Compare commits

..

3 commits

14 changed files with 435 additions and 324 deletions

5
.gitignore vendored
View file

@ -24,3 +24,8 @@ vagrant/dev/ubuntu/ansible/ansible_inventory.ini
*.cast
vagrant/dev/ubuntu/certs/
vagrant/dev/ubuntu/config-dev
.terraform*
registry*.json*
terraform.tfstate**
*history*.txt
*.tfvars

360
README.md
View file

@ -1,289 +1,7 @@
# INFCTL CLI
A command-line tool for automated deployment and management of an [MVK (Minimal Viable Kubernetes) infrastructure](https://mvk.headshed.dev/). The CLI orchestrates Kubernetes deployments by executing shell scripts and applying Kubernetes manifests through a JSON-defined pipeline approach.
# infctl-cli
## Table of Contents
- [Overview](#overview)
- [Features](#features)
- [Design Philosophy](#design-philosophy)
- [Prerequisites](#prerequisites)
- [Installation](#installation)
- [Configuration](#configuration)
- [Usage](#usage)
- [Pipeline Execution](#pipeline-execution)
- [Scripts Directory](#scripts-directory)
- [Kubernetes Manifests](#kubernetes-manifests)
- [Project Structure](#project-structure)
- [Development](#development)
- [Contributing](#contributing)
- [License](#license)
## Overview
INFCTL CLI is a Go-based deployment orchestrator that automates the setup and deployment of INFCTL applications in Kubernetes environments. The tool executes a series of predefined scripts from the `scripts/` directory and applies Kubernetes manifests from the `k8s-manifests/` directory using kubectl and kustomize, all defined in a JSON pipeline file.
## Features
- **JSON-Defined Pipeline Execution**: Runs deployment scripts and manifests in an order specified in a JSON pipeline file
- **Script Orchestration**: Executes shell scripts from the `scripts/` directory for various setup tasks
- **Kustomize Integration**: Applies Kubernetes manifests using kubectl kustomize
- **Namespace Management**: Automatically creates required Kubernetes namespaces
- **Secret Management**: Automated creation of secrets for databases, SMTP, AWS, etc.
- **ConfigMap Management**: Creates and manages application configuration maps
- **Infrastructure Setup**: Installs and configures cert-manager, Traefik, Longhorn, and PostgreSQL operators
- **Retry Logic**: Built-in retry mechanism for failed operations
- **Structured Logging**: JSON-based logging with debug support
- **Integrated Testing**: Includes smoke tests using k3d for validation
## Design Philosophy
infctl-cli is built on principles derived from over 20 years of experience tackling deployment and orchestration challenges. The design is inspired by a "plugin" mentality, where each plugin is essentially a script. This approach emphasizes simplicity and modularity, allowing each script to act as an independent unit of execution.
Key principles include:
- **Script-Based Orchestration**: Each script or program, when executed, returns an exit code that indicates success or failure. This exit code is used to determine the next steps in the pipeline, enabling robust and predictable orchestration.
- **Structured Logging**: Scripts produce structured logs that can be consumed by web interfaces or stored in a database. This ensures transparency and traceability, making it easier to debug and monitor deployments.
- **Modularity and Reusability**: By treating scripts as plugins, the system encourages reusability and flexibility. New functionality can be added by simply introducing new scripts without altering the core logic.
- **UNIX Philosophy**: The design adheres to the UNIX philosophy of building small, composable tools that do one thing well. Each script is a self-contained unit that performs a specific task.
This philosophy underpins infctl's ability to orchestrate complex deployments while remaining simple and extensible.
## Prerequisites
- Go 1.23.3 or later
- Bash shell environment
- For running tests: k3d installed
- Kubernetes cluster with kubectl configured (for actual deployments)
- Required Kubernetes operators (installed by the tool for K8s deployments):
- cert-manager
- Traefik ingress controller
- PostgreSQL operator (Crunchy Data)
- Longhorn storage
## Installation
### Option 1: Download Pre-built Binary
You can run an install from `curl -L https://codeberg.org/headshed/infctl-cli/raw/branch/chore/code-refactor/install.sh | bash `
.or.
manually download the pre-built binary for your platform from the [releases page](https://codeberg.org/headshed/infctl-cli/releases).
1. Download the binary for your platform:
- **Linux**:
```bash
wget https://codeberg.org/headshed/infctl-cli/releases/download/v0.0.2/infctl-linux-amd64 -O /usr/local/bin/infctl
```
- **Windows**:
Download the `.exe` file from the [releases page](https://codeberg.org/headshed/infctl-cli/releases) and place it in a directory included in your `PATH`.
- **macOS (Intel)**:
```bash
wget https://codeberg.org/headshed/infctl-cli/releases/download/v0.0.2/infctl-darwin-amd64 -O /usr/local/bin/infctl
```
- **macOS (Apple Silicon)**:
```bash
wget https://codeberg.org/headshed/infctl-cli/releases/download/v0.0.2/infctl-darwin-arm64 -O /usr/local/bin/infctl
```
2. Make the binary executable:
```bash
chmod +x /usr/local/bin/infctl
```
### Option 2: Clone Repository and Build Binary
1. Clone the repository:
```bash
git clone <repository-url>
cd infctl-cli
```
2. Build the application:
```bash
go mod download
go build -o infctl-cli .
```
### Quick start example
```bash
# Copy configuration examples
cp base.json.example base.json
cp config.json.example config.json
cp pipeline.json.example pipeline.json
# Edit* configuration files as needed
# vim base.json
# vim config.json
# vim pipeline.json
# - where vim may be your default or chosen editor be it nano, vi, ed, emacs, etc ...
# Run with pipeline file
./infctl-cli --deployment-file pipeline.json
# or using the short format
./infctl-cli -f pipeline.json--deployment-file
```
## Configuration
The infctl-cli requires three configuration files:
### Base Configuration (`base.json`)
Copy and customize the base configuration:
```bash
cp base.json.example base.json
```
Key configuration options:
- `projects_directory`: Base directory for project deployments
- `app_image`: Docker image for the INFCTL application
- `webserver_image`: Docker image for the web server
- `env`: Environment file path
- `preview_path`: Path for preview functionality
### Project Configuration (`config.json`)
Copy and customize the project-specific configuration:
```bash
cp config.json.example config.json
```
Key configuration options:
- `project`: Project name/identifier (used as Kubernetes namespace)
- `project_directory`: Project-specific directory
- `ui_url`: UI service URL
- `static_url`: Static content URL
- `port`: Service port
### Pipeline Configuration (`pipeline.json`)
Copy and customize the pipeline definition:
```bash
cp pipeline.json.example pipeline.json
```
This file defines the sequence of operations to be executed, including:
- Scripts to run
- Kubernetes manifests to apply
- Order of operations
- Specific deployment type configuration
## Usage
Run the CLI by providing a path to your pipeline JSON file:
```bash
./infctl-cli --deployment-file /path/to/pipeline.json
# or using the short format
./infctl-cli -f /path/to/pipeline.json
```
The tool will automatically:
1. Load base and project configurations
2. Initialize SQLite database for state management
3. Execute the deployment pipeline defined in your JSON file
4. Run scripts from the `scripts/` directory
5. Apply Kubernetes manifests using kustomize (for K8s deployments)
--deployment-file
### Command Line Options
- `--deployment-file <path>` or `-f <path>`: Path to the pipeline JSON configuration file
- `--help`: Show help message and usage information
### Running from Source
You can also run directly with Go:
```bash
go run main.go --deployment-file /path/to/pipeline.json
# or using the short format
go run main.go -f /path/to/pipeline.json
```
### Running Tests
The project includes smoke tests using k3d for validation:
```bash
# Run all tests
go test ./... -v
# Run specific test
go test ./app -run TestRunPipeline
```
## Pipeline Execution
The CLI executes deployment tasks defined in your pipeline.json file, which typically includes:
### Infrastructure Setup Pipeline
Sets up the Kubernetes cluster infrastructure:
- Creates required namespaces (project, redis, traefik, metallb-system, longhorn-system, cert-manager)
- Installs Traefik ingress controller
- Sets up PostgreSQL operator (Crunchy Data)
- Configures cert-manager and Redis
### Application Deployment Pipeline
Deploys the INFCTL application:
- Creates database secrets and configurations
- Sets up SMTP secrets
- Creates application secrets and config maps
- Applies INFCTL Kubernetes manifests
- Configures ingress and networking
## Scripts Directory
The `scripts/` directory contains shell scripts executed by the CLI:
### Infrastructure Scripts
- `install_traefik.sh` - Installs Traefik ingress controller
- `install_cert-manager.sh` - Installs cert-manager
- `install_longhorn.sh` - Installs Longhorn storage
- `install_cloudnative_pg.sh` - Installs PostgreSQL operator
### Database Scripts
- `create_crunchy_operator.sh` - Sets up PostgreSQL operator
- `check_crunchy_operator.sh` - Verifies operator installation
- `create_crunchy_db.sh` - Creates PostgreSQL database
- `create_crunchy_inf_secrets.sh` - Creates database secrets
### Application Scripts
- `create_app_secret_inf.sh` - Creates application secrets
- `create_smtp_inf_secrets.sh` - Creates SMTP configuration
- `create_init_configmap_inf.sh` - Creates initialization config maps
- `create_nginx_configmap_inf.sh` - Creates Nginx configuration
- `create_php_configmap_inf.sh` - Creates PHP configuration
### Cloud Provider Scripts
- `create_aws_secrets.sh` - Creates AWS secrets
- `create_cloudflare_secret.sh` - Creates Cloudflare secrets
- `create_redis_secret.sh` - Creates Redis secrets
## Kubernetes Manifests
The `k8s-manifests/` directory contains Kubernetes resources applied via kustomize:
### INFCTL Application (`k8s-manifests/inf/`)
- `deployment.yaml` - Main application deployment
- `pvc.yaml` - Persistent volume claims
- `kustomization.yaml` - Kustomize configuration
### INFCTL Ingress (`k8s-manifests/inf-ingress/`)
- `ingress.yaml` - Ingress rules
- `service.yaml` - Service definitions
- `issuer.yaml` - Certificate issuer
- `kustomization.yaml` - Kustomize configuration
`infctl-cli` is a Go command-line tool for orchestrating deployment pipelines using shell scripts and Kubernetes manifests. The tool is configured via JSON files and executes tasks as defined in a pipeline configuration.
## Project Structure
@ -291,61 +9,73 @@ The `k8s-manifests/` directory contains Kubernetes resources applied via kustomi
infctl-cli/
├── main.go # Application entry point
├── go.mod # Go module definition
├── base.json.example # Base configuration template
├── config.json.example # Project configuration template
├── app/ # Core application logic
│ ├── app.go # Pipeline orchestration and state management
│ └── k8s.go # Kubernetes operations (kubectl, kustomize)
│ └── k8s.go # Kubernetes operations
├── config/ # Configuration management
│ ├── base.go # Base configuration handling
│ └── project.go # Project configuration handling
├── database/ # SQLite database operations
├── docs/ # Documentation
│ ├── API_REFERENCE.md # API reference
│ └── CONFIG_SCHEMA.md # Config schema
├── scripts/ # Shell scripts executed by the CLI
│ ├── install_*.sh # Infrastructure installation scripts
│ ├── create_*_secrets.sh # Secret creation scripts
│ └── create_*_configmap_*.sh # ConfigMap creation scripts
├── k8s-manifests/ # Kubernetes manifests applied via kustomize
│ ├── ctl/ # INFCTL application manifests
│ └── ctl-ingress/ # INFCTL ingress configuration
├── templates/ # Template files for configuration generation
├── k8s-manifests/ # Kubernetes manifests
├── templates/ # Template files
└── files/ # Static configuration files
```
## Development
## Configuration Files
### Building from Source
Three JSON files are used:
- `base.json`: Base configuration (e.g., `projects_directory`, images, environment file path)
- `config.json`: Project-specific configuration (e.g., project name, directory, URLs, port)
- `pipeline.json`: Defines the sequence of scripts and manifests to execute
Example configuration files are provided as `.example` files in the repository.
## Usage
Build the CLI:
```bash
go mod download
go build -o infctl-cli .
```
### Running with Debug Logging
Run the CLI with a pipeline file:
The CLI uses structured JSON logging. Debug logs are enabled by default and include detailed information about script execution and kustomize operations.
```bash
./infctl-cli --deployment-file pipeline.json
# or
./infctl-cli -f pipeline.json
```
### Adding New Scripts
The CLI will:
1. Place shell scripts / executables in the `scripts/` directory
2. Add confiiguration as appropriate into `pipeline.json`
3. Re-run `infctl-cli --deployment-file pipeline.json` or `infctl-cli -f pipeline.json`
1. Load base and project configuration
2. Initialize SQLite database for state management
3. Execute the pipeline defined in the JSON file
4. Run scripts from the `scripts/` directory
5. Apply Kubernetes manifests from the `k8s-manifests/` directory
### Adding New Manifests
## Scripts
1. Create Kubernetes YAML files in the appropriate `k8s-manifests/` subdirectory
2. Include a `kustomization.yaml` file for kustomize processing
3. Add confiiguration as appropriate into `pipeline.json`
4. Re-run `infctl-cli --deployment-file pipeline.json` or `infctl-cli -f pipeline.json`
Shell scripts in `scripts/` are executed as defined in the pipeline configuration. Scripts are responsible for infrastructure setup, secret creation, configmap generation, and other deployment tasks. The pipeline JSON determines the order and parameters for each script.
## Contributing
## Kubernetes Manifests
1. Fork the repository
2. Create a feature branch (`git checkout -b feature/amazing-feature`)
3. Commit your changes (`git commit -m 'Add some amazing feature'`)
4. Push to the branch (`git push origin feature/amazing-feature`)
5. Open a Pull Request
Manifests in `k8s-manifests/` are applied using kubectl and kustomize. The pipeline configuration specifies which manifests to apply and in what order.
## Testing
Run tests with:
```bash
go test ./... -v
```
## License
This project is licensed under the GNU General Public License v3.0. See the [LICENSE](./LICENSE) file for details.
This project is licensed under the GNU General Public License v3.0. See `LICENSE` for details.

50
docs/API_REFERENCE.md Normal file
View file

@ -0,0 +1,50 @@
# API Reference
This document describes the API and pipeline functions available in `infctl-cli`.
## PipelineStep Structure
Each pipeline step is defined as:
- `name`: Step name (string)
- `function`: Function to call (string)
- `params`: List of parameters (array of strings)
- `retryCount`: Number of retries (integer)
- `shouldAbort`: Whether to abort on failure (boolean)
## Available Functions
### k8sNamespaceExists
Checks if a Kubernetes namespace exists.
- Params: `[namespace]` (string)
- Returns: error if namespace does not exist
### RunCommand
Runs a shell command.
- Params: `[command]` (string)
- Returns: error if command fails
## Example Pipeline JSON
```
[
{
"name": "ensure inf namespace exists",
"function": "k8sNamespaceExists",
"params": ["infctl"],
"retryCount": 0,
"shouldAbort": true
},
{
"name": "create php configmap",
"function": "RunCommand",
"params": ["./scripts/create_php_configmap_ctl.sh"],
"retryCount": 0,
"shouldAbort": true
}
]
```
## Notes
- Only functions defined in the codebase are available for use in pipelines.
- The API does not expose any HTTP endpoints; all orchestration is via CLI and pipeline JSON.

51
docs/CONFIG_SCHEMA.md Normal file
View file

@ -0,0 +1,51 @@
# Configuration Schema
This document describes the configuration schema for `infctl-cli`.
## Base Configuration (`base.json`)
Example:
```json
{
"retry_delay_seconds": 3
}
```
- `retry_delay_seconds` (integer): Delay in seconds before retrying failed steps.
## Project Configuration (`config.json`)
Project configuration fields are defined in the code and may include:
- Project name
- Directory paths
- URLs
- Port numbers
- Log format
Refer to the code for exact field names and types.
## Pipeline Configuration (`pipeline.json`)
Pipeline configuration is an array of steps. Each step:
- `name`: Step name (string)
- `function`: Function to call (string)
- `params`: List of parameters (array of strings)
- `retryCount`: Number of retries (integer)
- `shouldAbort`: Whether to abort on failure (boolean)
Example:
```json
[
{
"name": "ensure inf namespace exists",
"function": "k8sNamespaceExists",
"params": ["infctl"],
"retryCount": 0,
"shouldAbort": true
}
]
```
## Notes
- Example configuration files are provided as `.example` files in the repository.
- All configuration fields must match those defined in the codebase; do not add undocumented fields.

10
gcloud/tf/Dockerfile Normal file
View file

@ -0,0 +1,10 @@
FROM python:3.12-slim
# Install dependencies
RUN pip install --no-cache-dir gunicorn httpbin
# Expose the application port
EXPOSE 80
# Launch the application
CMD ["gunicorn", "-b", "0.0.0.0:80", "httpbin:app"]

0
gcloud/tf/doit.tf Normal file
View file

17
gcloud/tf/firewall.tf Normal file
View file

@ -0,0 +1,17 @@
// Firewall
// ----------------------------------
resource "google_compute_firewall" "allow_http" {
name = "allow-http"
network = "default"
allow {
protocol = "tcp"
ports = [
"80", "443", // http/https
"30080" // ports opened to access the python API via NodePort
]
}
source_ranges = ["0.0.0.0/0"]
target_tags = ["web"]
}

98
gcloud/tf/main.tf Normal file
View file

@ -0,0 +1,98 @@
// Compute
// ----------------------------------
// The instance for K3S
resource "google_compute_instance" "k3s" {
name = "k3s-vm-1"
machine_type = "e2-small" # This instance will have 2 Gb of RAM
zone = var.zone
tags = ["web"]
// Set the boot disk and the image (10 Gb)
boot_disk {
initialize_params {
image = "debian-cloud/debian-12"
size = 10
}
}
// Configuration to be a Spot Instance, to reduce costs
scheduling {
automatic_restart = true
}
# scheduling {
# preemptible = false
# automatic_restart = true
# provisioning_model = "SPOT"
# instance_termination_action = "STOP"
# }
// attach a disk for K3S
attached_disk {
source = google_compute_disk.k3s_disk.id
device_name = "k3s-disk"
}
// attach a disk for app data
attached_disk {
source = google_compute_disk.app_data_disk.id
device_name = "app-data-disk"
}
network_interface {
network = "default"
// enable ephemeral ip
access_config {}
}
labels = {
env = var.env
region = var.region
app = var.app_name
sensitive = "false"
}
metadata_startup_script = file("scripts/k3s-vm-startup.sh")
allow_stopping_for_update = true
}
// Storage
// ----------------------------------
// The disk attached to the instance (15 Gb)
resource "google_compute_disk" "k3s_disk" {
name = "k3s-disk"
size = 15
type = "pd-standard"
zone = var.zone
}
// The disk for app data (20 Gb)
resource "google_compute_disk" "app_data_disk" {
name = "app-data-disk"
size = 20
type = "pd-standard"
zone = var.zone
}
data "google_project" "project" {
project_id = var.project_name # Use variable from tfvars
}
output "project_number" {
value = data.google_project.project.number
}
output "k3s_vm_public_ip" {
value = google_compute_instance.k3s.network_interface[0].access_config[0].nat_ip
description = "Ephemeral public IP of the k3s VM"
}

24
gcloud/tf/provider.tf Normal file
View file

@ -0,0 +1,24 @@
terraform {
required_providers {
google = {
source = "hashicorp/google"
version = "~> 4.0"
}
}
}
// Provider
// ----------------------------------
// Connect to the GCP project
provider "google" {
# Configuration options
project = var.project_name # Use variable from tfvars
region = "us-central1" # Replace with your desired region
}
# provider "google" {
# credentials = file("<my-gcp-creds>.json")
# project = var.project_name
# region = var.region
# zone = var.zone
# }

14
gcloud/tf/registry.tf Normal file
View file

@ -0,0 +1,14 @@
// Registry
// ----------------------------------
// The Artifact Registry repository for our app
resource "google_artifact_registry_repository" "app-repo" {
location = var.region
repository_id = "app-repo"
description = "App Docker repository"
format = "DOCKER"
docker_config {
immutable_tags = true
}
}

37
gcloud/tf/remote_state.tf Normal file
View file

@ -0,0 +1,37 @@
// Remote state
// ----------------------------------
# variable "bucket_name" {
# type = string
# default = "your-project-name-k3s-bucket"
# description = "your-project-name k3s Bucket"
# }
# terraform {
# # Use a shared bucket (wich allows collaborative work)
# backend "gcs" {
# bucket = "<my-bucket-for-states>"
# prefix = "k3s-infra"
# }
# // Set versions
# required_version = ">=1.8.0"
# required_providers {
# google = {
# source = "hashicorp/google"
# version = ">=4.0.0"
# }
# }
# }
// The bucket where you can store other data
# resource "google_storage_bucket" "k3s-storage" {
# name = var.bucket_name
# location = var.region
# labels = {
# env = var.env
# region = var.region
# app = var.app_name
# sensitive = "false"
# }
# }

View file

@ -0,0 +1,33 @@
#!/bin/bash
# Format the disk if not already formatted
if ! lsblk | grep -q "/mnt/disks/k3s"; then
mkfs.ext4 -m 0 -F -E lazy_itable_init=0,lazy_journal_init=0,discard /dev/disk/by-id/google-k3s-disk
mkdir -p /mnt/disks/k3s
mount -o discard,defaults /dev/disk/by-id/google-k3s-disk /mnt/disks/k3s
chmod a+w /mnt/disks/k3s
fi
# ensure only run once
if [[ -f /etc/startup_was_launched ]]; then exit 0; fi
touch /etc/startup_was_launched
# apt install
apt update
apt install -y ncdu htop
# helm install
curl -fsSL -o get_helm.sh https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3
chmod 700 get_helm.sh
/bin/bash get_helm.sh
# bashrc config
rc=/root/.bashrc
echo "alias l='ls -lah'" >> $rc
echo "alias ll='ls -lh'" >> $rc
echo "alias k=kubectl" >> $rc
echo "export dry='--dry-run=client'" >> $rc
echo "export o='-oyaml'" >> $rc
# Install k3s and configure it to use the persistent disk for data storage
curl -sfL https://get.k3s.io | INSTALL_K3S_EXEC="--data-dir /mnt/disks/k3s" sh -

View file

@ -0,0 +1,14 @@
// Your GCP project name
// it will be refererred as the project id
// in Google Cloud
// ----------------------------------
project_name = "<your gpc project name>"
// application name
app_name = "your-projects-k3s-cluster"
// where to deploy to
// region
region = "us-central1"
zone = "us-central1-a"

28
gcloud/tf/vars.tf Normal file
View file

@ -0,0 +1,28 @@
// Env vars
// ----------------------------------
variable "project_name" {
type = string
}
variable "env" {
type = string
default = "dev"
description = "Environment"
}
variable "region" {
type = string
description = "GCP Region"
}
variable "zone" {
type = string
description = "GCP Zone"
}
variable "app_name" {
type = string
description = "Application name"
}