changed app to use json config for pipeline steps
readme command line usage - to specify pipeline file name readme updated to include reasoning behind the project use native golang sqlite RunScriptCommand named in functionMap removed unused functions removed unused functions run script and pipeline example renamed functions to drop the word script and add pipeline verb
This commit is contained in:
parent
bd7cee720a
commit
924954d0ff
49 changed files with 2059 additions and 101 deletions
16
.gitignore
vendored
Normal file
16
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
config.json
|
||||
app.db
|
||||
base.json
|
||||
infctl-cli
|
||||
config.json.1
|
||||
my_log.txt
|
||||
.aws_creds.yaml
|
||||
*.suspect
|
||||
*.deleted
|
||||
*.orig
|
||||
scripts/galene/groups/
|
||||
scripts/galene/data/
|
||||
.envrc
|
||||
.vscode
|
||||
*.json
|
||||
bin
|
||||
219
README.md
219
README.md
|
|
@ -1,11 +1,12 @@
|
|||
# INFCTL CLI
|
||||
|
||||
A command-line tool for automated deployment and management of an MVK (Minimal Viable Kubernetes) infrastrucure. The CLI orchestrates Kubernetes deployments by executing shell scripts and applying Kubernetes manifests through a pipeline-based approach.
|
||||
A command-line tool for automated deployment and management of an MVK (Minimal Viable Kubernetes) infrastrucure. The CLI orchestrates Kubernetes deployments by executing shell scripts and applying Kubernetes manifests through a JSON-defined pipeline approach.
|
||||
|
||||
## Table of Contents
|
||||
|
||||
- [Overview](#overview)
|
||||
- [Features](#features)
|
||||
- [Design Philosophy](#design-philosophy)
|
||||
- [Prerequisites](#prerequisites)
|
||||
- [Installation](#installation)
|
||||
- [Configuration](#configuration)
|
||||
|
|
@ -20,27 +21,40 @@ A command-line tool for automated deployment and management of an MVK (Minimal V
|
|||
|
||||
## 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.
|
||||
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
|
||||
|
||||
- **Automated Pipeline Execution**: Runs deployment scripts and manifests in a specific order
|
||||
- **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
|
||||
- **Multi-Component Deployment**: Supports INFCTL, Galene, and SL application deployments
|
||||
- **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
|
||||
- `DEPLOYMENT_TYPE` environment variable set to one of: `customer_k8s_setup`, `infctl_k8s_setup`, or `sl_k8s_setup`
|
||||
- Kubernetes cluster with kubectl configured (for K8s deployment types)
|
||||
- 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
|
||||
|
|
@ -49,6 +63,37 @@ INFCTL CLI is a Go-based deployment orchestrator that automates the setup and de
|
|||
|
||||
## 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.1/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.1/infctl-darwin-amd64 -O /usr/local/bin/infctl
|
||||
```
|
||||
- **macOS (Apple Silicon)**:
|
||||
```bash
|
||||
wget https://codeberg.org/headshed/infctl-cli/releases/download/v0.0.1/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>
|
||||
|
|
@ -61,28 +106,27 @@ go mod download
|
|||
go build -o infctl-cli .
|
||||
```
|
||||
|
||||
3. (Optional) Install globally:
|
||||
```bash
|
||||
sudo mv infctl-cli /usr/local/bin/
|
||||
```
|
||||
### Quick start example
|
||||
|
||||
4. 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
|
||||
# nano base.json
|
||||
# nano config.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 desired deployment type
|
||||
DEPLOYMENT_TYPE=customer_k8s_setup ./infctl-cli
|
||||
# Run with pipeline file
|
||||
./infctl-cli --deployment-file pipeline.json
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
The infctl-cli requires two configuration files:
|
||||
The infctl-cli requires three configuration files:
|
||||
|
||||
### Base Configuration (`base.json`)
|
||||
|
||||
|
|
@ -114,77 +158,59 @@ Key configuration options:
|
|||
- `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
|
||||
|
||||
The infctl-cli requires the `DEPLOYMENT_TYPE` environment variable to be set to specify the type of deployment. Run the CLI from a directory containing your configuration files.
|
||||
|
||||
### Available Deployment Types
|
||||
|
||||
#### 1. Customer Kubernetes Setup
|
||||
For customer-specific Kubernetes deployments:
|
||||
Run the CLI by providing a path to your pipeline JSON file:
|
||||
|
||||
```bash
|
||||
DEPLOYMENT_TYPE=customer_k8s_setup ./infctl-cli
|
||||
```
|
||||
|
||||
This deployment type:
|
||||
- Creates customer-specific Kubernetes resources
|
||||
- Sets up namespaces, secrets, and config maps
|
||||
- Deploys customer applications with custom configurations
|
||||
|
||||
#### 2. INFCTL Kubernetes Setup
|
||||
For INFCTL infrastructure and application deployment:
|
||||
|
||||
```bash
|
||||
DEPLOYMENT_TYPE=inf_k8s_setup ./infctl-cli
|
||||
```
|
||||
|
||||
This deployment type:
|
||||
- Sets up core INFCTL infrastructure
|
||||
- Installs required operators (cert-manager, Traefik, PostgreSQL)
|
||||
- Deploys INFCTL applications and services
|
||||
|
||||
#### 3. SL Kubernetes Setup
|
||||
For SL (Share Lite) application deployment:
|
||||
|
||||
```bash
|
||||
DEPLOYMENT_TYPE=sl_k8s_setup ./infctl-cli
|
||||
```
|
||||
|
||||
This deployment type:
|
||||
- Deploys SL applications using Helm charts
|
||||
- Configures SL-specific resources and networking
|
||||
- Sets up SL instance management
|
||||
|
||||
### Running from Source
|
||||
|
||||
You can also run directly with Go:
|
||||
|
||||
```bash
|
||||
# For Docker Compose setup
|
||||
DEPLOYMENT_TYPE=customer_compose_setup go run main.go
|
||||
|
||||
# For customer Kubernetes setup
|
||||
DEPLOYMENT_TYPE=customer_k8s_setup go run main.go
|
||||
|
||||
# For INFCTL Kubernetes setup
|
||||
DEPLOYMENT_TYPE=inf_k8s_setup go run main.go
|
||||
|
||||
# For SL Kubernetes setup
|
||||
DEPLOYMENT_TYPE=sl_k8s_setup go run main.go
|
||||
./infctl-cli --deployment-file /path/to/pipeline.json
|
||||
```
|
||||
|
||||
The tool will automatically:
|
||||
|
||||
1. Load base and customer configurations
|
||||
2. Initialize SQLite database for state management
|
||||
3. Execute the appropriate deployment pipeline based on `DEPLOYMENT_TYPE`
|
||||
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)
|
||||
|
||||
### Running from Source
|
||||
|
||||
You can also run directly with Go:
|
||||
|
||||
```bash
|
||||
go run main.go -pipeline /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 in two main pipelines:
|
||||
The CLI executes deployment tasks defined in your pipeline.json file, which typically includes:
|
||||
|
||||
### Infrastructure Setup Pipeline
|
||||
|
||||
|
|
@ -248,15 +274,6 @@ The `k8s-manifests/` directory contains Kubernetes resources applied via kustomi
|
|||
- `issuer.yaml` - Certificate issuer
|
||||
- `kustomization.yaml` - Kustomize configuration
|
||||
|
||||
### Galene (`k8s-manifests/galene/`)
|
||||
- WebRTC conferencing application manifests
|
||||
- TCP/UDP ingress configurations
|
||||
- Certificate management
|
||||
|
||||
### SL Applications (`k8s-manifests/sl/`)
|
||||
- Helm chart templates for SL instances
|
||||
- `Chart.yaml` and `values.yaml` for Helm deployments
|
||||
|
||||
## Project Structure
|
||||
|
||||
```
|
||||
|
|
@ -266,25 +283,21 @@ infctl-cli/
|
|||
├── base.json.example # Base configuration template
|
||||
├── config.json.example # Customer configuration template
|
||||
├── app/ # Core application logic
|
||||
│ ├── app.go # Pipeline orchestration and state management
|
||||
│ └── k8s.go # Kubernetes operations (kubectl, kustomize)
|
||||
│ ├── app.go # Pipeline orchestration and state management
|
||||
│ └── k8s.go # Kubernetes operations (kubectl, kustomize)
|
||||
├── config/ # Configuration management
|
||||
│ ├── base.go # Base configuration handling
|
||||
│ └── customer.go # Customer configuration handling
|
||||
│ ├── base.go # Base configuration handling
|
||||
│ └── customer.go # Customer configuration handling
|
||||
├── database/ # SQLite database operations
|
||||
├── scripts/ # Shell scripts executed by the CLI
|
||||
│ ├── install_*.sh # Infrastructure installation scripts
|
||||
│ ├── create_*_secrets.sh # Secret creation scripts
|
||||
│ ├── create_*_configmap_*.sh # ConfigMap creation scripts
|
||||
│ └── galene/ # Galene-specific scripts
|
||||
├── k8s-manifests/ # Kubernetes manifests applied via kustomize
|
||||
│ ├── inf/ # INFCTL application manifests
|
||||
│ ├── inf-ingress/ # INFCTL ingress configuration
|
||||
│ ├── galene/ # Galene application manifests
|
||||
│ ├── sl/ # SL application Helm charts
|
||||
│ └── sl-certificate/ # SSL certificate management
|
||||
│ ├── 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
|
||||
└── files/ # Static configuration files
|
||||
└── files/ # Static configuration files
|
||||
```
|
||||
|
||||
## Development
|
||||
|
|
@ -302,15 +315,16 @@ The CLI uses structured JSON logging. Debug logs are enabled by default and incl
|
|||
|
||||
### Adding New Scripts
|
||||
|
||||
1. Place shell scripts in the `scripts/` directory
|
||||
2. Add script execution to the appropriate pipeline in `app/app.go`
|
||||
3. Use the `k8sRunSriptCommand()` function to execute scripts
|
||||
1. Place shell scripts / executables in the `scripts/` directory
|
||||
2. Add confiiguration as appropriate into `pipeline.json`
|
||||
3. Re-run `cnfctl --deployment-file pipeline.json`
|
||||
|
||||
### Adding New Manifests
|
||||
|
||||
1. Create Kubernetes YAML files in the appropriate `k8s-manifests/` subdirectory
|
||||
2. Include a `kustomization.yaml` file for kustomize processing
|
||||
3. Add manifest application to the pipeline using `k8sRunKustomizeCommand()`
|
||||
3. Add confiiguration as appropriate into `pipeline.json`
|
||||
4. Re-run `cnfctl --deployment-file pipeline.json`
|
||||
|
||||
## Contributing
|
||||
|
||||
|
|
@ -323,3 +337,6 @@ The CLI uses structured JSON logging. Debug logs are enabled by default and incl
|
|||
## License
|
||||
|
||||
This project is licensed under the GNU General Public License v3.0. See the [LICENSE](./LICENSE) file for details.
|
||||
## License
|
||||
|
||||
This project is licensed under the GNU General Public License v3.0. See the [LICENSE](./LICENSE) file for details.
|
||||
|
|
|
|||
225
app/app.go
Normal file
225
app/app.go
Normal file
|
|
@ -0,0 +1,225 @@
|
|||
package app
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"log/slog"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"headshed/infctl-cli/config"
|
||||
"headshed/infctl-cli/database"
|
||||
)
|
||||
|
||||
type AppState struct {
|
||||
Config config.BaseConfig
|
||||
Customer config.CustomerConfig
|
||||
DB *sql.DB
|
||||
}
|
||||
|
||||
type PipelineStep struct {
|
||||
Name string
|
||||
Function string // Name of the function to call
|
||||
Params []string // Parameters for the function
|
||||
RetryCount int
|
||||
ShouldAbort bool
|
||||
}
|
||||
|
||||
var functionMap = map[string]func([]string) error{
|
||||
"k8sNamespaceExists": func(params []string) error {
|
||||
if len(params) != 1 {
|
||||
return fmt.Errorf("invalid parameters for k8sNamespaceExists")
|
||||
}
|
||||
return k8sNamespaceExists(params[0])
|
||||
},
|
||||
"RunCommand": func(params []string) error {
|
||||
if len(params) != 1 {
|
||||
return fmt.Errorf("invalid parameters for RunCommand")
|
||||
}
|
||||
return RunCommand(params[0])
|
||||
},
|
||||
// Add more functions as needed
|
||||
}
|
||||
|
||||
func parseStepsFromJSON(filePath string) ([]PipelineStep, error) {
|
||||
data, err := os.ReadFile(filePath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to read JSON file: %w", err)
|
||||
}
|
||||
|
||||
var steps []PipelineStep
|
||||
if err := json.Unmarshal(data, &steps); err != nil {
|
||||
return nil, fmt.Errorf("failed to unmarshal JSON: %w", err)
|
||||
}
|
||||
|
||||
return steps, nil
|
||||
}
|
||||
|
||||
func (app *AppState) ToDoDeployment() []PipelineStep {
|
||||
slog.Info("ToDo deployment is not implemented yet")
|
||||
return []PipelineStep{}
|
||||
}
|
||||
|
||||
func (app *AppState) RunJsonDeployment() []PipelineStep {
|
||||
|
||||
jsonFile := app.Config.DeploymentFile
|
||||
if jsonFile == "" {
|
||||
log.Fatal("no config specified with --deployment-file=<path_to_config_file>")
|
||||
}
|
||||
|
||||
file, err := os.Open(jsonFile)
|
||||
if err != nil {
|
||||
slog.Error(fmt.Sprintf("Failed to open JSON file: %s", err))
|
||||
}
|
||||
|
||||
defer file.Close()
|
||||
|
||||
steps, err := parseStepsFromJSON(jsonFile)
|
||||
if err != nil {
|
||||
slog.Error(fmt.Sprintf("Failed to parse JSON file: %s", err))
|
||||
}
|
||||
|
||||
for _, step := range steps {
|
||||
slog.Info(fmt.Sprintf("🔄 Running step: %s", step.Name))
|
||||
function, exists := functionMap[step.Function]
|
||||
if !exists {
|
||||
slog.Error(fmt.Sprintf("Unknown function: %s", step.Function))
|
||||
continue
|
||||
}
|
||||
|
||||
err := function(step.Params)
|
||||
if err != nil {
|
||||
slog.Error(fmt.Sprintf("❌ Step failed: %s, error: %v", step.Name, err))
|
||||
if step.ShouldAbort {
|
||||
log.Fatalf("🚨Critical failure at step: %s", step.Name)
|
||||
}
|
||||
} else {
|
||||
slog.Info(fmt.Sprintf("✅ Step completed: %s", step.Name))
|
||||
}
|
||||
}
|
||||
return steps
|
||||
}
|
||||
|
||||
func (app *AppState) getPipeline() []PipelineStep {
|
||||
|
||||
switch app.Config.DeploymentType {
|
||||
|
||||
case "api":
|
||||
return app.ToDoDeployment()
|
||||
|
||||
case "json":
|
||||
return app.RunJsonDeployment()
|
||||
|
||||
default:
|
||||
return app.RunJsonDeployment()
|
||||
}
|
||||
}
|
||||
|
||||
func NewAppState(cust config.CustomerConfig, config config.BaseConfig, dbPath string) (*AppState, error) {
|
||||
db, err := database.NewDatabase(dbPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &AppState{
|
||||
Config: config,
|
||||
Customer: cust,
|
||||
DB: db,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (app *AppState) runPipeline(steps []PipelineStep) error {
|
||||
for _, step := range steps {
|
||||
slog.Info(fmt.Sprintf("🔄 Running step: %s\n", step.Name))
|
||||
|
||||
// Look up the function in the functionMap
|
||||
function, exists := functionMap[step.Function]
|
||||
if !exists {
|
||||
slog.Error(fmt.Sprintf("❌ Unknown function: %s", step.Function))
|
||||
if step.ShouldAbort {
|
||||
return fmt.Errorf("🚨critical failure: unknown function %s", step.Function)
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
// Execute the function with the provided parameters
|
||||
err := function(step.Params)
|
||||
if err != nil {
|
||||
slog.Error(fmt.Sprintf("❌ Step failed: %s, error: %v", step.Name, err))
|
||||
|
||||
// Retry logic
|
||||
if step.RetryCount > 0 {
|
||||
for i := 0; i < step.RetryCount; i++ {
|
||||
slog.Info("Waiting for 20 seconds before retrying...")
|
||||
time.Sleep(20 * time.Second)
|
||||
if innerErr := function(step.Params); innerErr == nil {
|
||||
slog.Info(fmt.Sprintf("✅ Step completed: %s\n", step.Name))
|
||||
err = nil
|
||||
break
|
||||
} else {
|
||||
err = innerErr
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Handle failure after retries
|
||||
if err != nil {
|
||||
if step.ShouldAbort {
|
||||
return fmt.Errorf("🚨critical failure at step: %s", step.Name)
|
||||
}
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
slog.Info(fmt.Sprintf("✅ Step completed: %s\n", step.Name))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (app *AppState) SetUpNewCustomer() error {
|
||||
|
||||
/*
|
||||
| --------------------------
|
||||
| main pipeline
|
||||
| --------------------------
|
||||
*/
|
||||
|
||||
steps := app.getPipeline()
|
||||
app.runPipeline(steps)
|
||||
slog.Info(fmt.Sprintln("🎉 Customer setup complete!"))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (app *AppState) CreatePipeline() error {
|
||||
isNew, err := database.CheckProjectName(app.DB, app.Customer.Project)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to check project name: %w", err)
|
||||
}
|
||||
|
||||
if isNew {
|
||||
|
||||
port, err := database.GetNextPortNumber(app.DB)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get next port number: %w", err)
|
||||
}
|
||||
err = database.AddProjectName(app.DB, app.Customer.Project, port)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to add project name: %w", err)
|
||||
}
|
||||
slog.Info(fmt.Sprintln("Project name added:", app.Customer.Project))
|
||||
fmt.Printf("Port number assigned: %d\n", port)
|
||||
app.Config.Port = port
|
||||
} else {
|
||||
slog.Info(fmt.Sprintln("Project name already exists:", app.Customer.Project))
|
||||
}
|
||||
|
||||
err = app.SetUpNewCustomer()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to set up new customer: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
158
app/app_test.go
Normal file
158
app/app_test.go
Normal file
|
|
@ -0,0 +1,158 @@
|
|||
package app
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"math/rand"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"headshed/infctl-cli/config"
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
// Setup: Set TEST_ENV=true for all tests
|
||||
err := os.Setenv("TEST_ENV", "true")
|
||||
if err != nil {
|
||||
panic("Failed to set TEST_ENV")
|
||||
}
|
||||
|
||||
// Run all tests
|
||||
code := m.Run()
|
||||
|
||||
// Teardown: Unset TEST_ENV after all tests
|
||||
os.Unsetenv("TEST_ENV")
|
||||
|
||||
// Exit with the test result code
|
||||
os.Exit(code)
|
||||
}
|
||||
|
||||
func TestRunPipeline(t *testing.T) {
|
||||
// Create a temporary directory for test assets
|
||||
tempDir, err := os.MkdirTemp("", "smoke-test")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create temp directory: %v", err)
|
||||
}
|
||||
defer os.RemoveAll(tempDir) // Cleanup after test
|
||||
|
||||
// Create test scripts
|
||||
scripts := map[string]string{
|
||||
"good.sh": "#!/bin/bash\necho 'Good script executed'\nexit 0",
|
||||
"warning.sh": "#!/bin/bash\necho 'Warning script executed'\nexit 0",
|
||||
"error.sh": "#!/bin/bash\necho 'Error script executed'\nexit 1",
|
||||
}
|
||||
|
||||
for name, content := range scripts {
|
||||
scriptPath := filepath.Join(tempDir, name)
|
||||
if err := os.WriteFile(scriptPath, []byte(content), 0755); err != nil {
|
||||
t.Fatalf("Failed to create script %s: %v", name, err)
|
||||
}
|
||||
}
|
||||
|
||||
// Create a test JSON pipeline file
|
||||
pipeline := []PipelineStep{
|
||||
{Name: "Good Step", Function: "RunCommand", Params: []string{filepath.Join(tempDir, "good.sh")}, RetryCount: 0, ShouldAbort: false},
|
||||
{Name: "Warning Step", Function: "RunCommand", Params: []string{filepath.Join(tempDir, "warning.sh")}, RetryCount: 0, ShouldAbort: false},
|
||||
{Name: "Error Step", Function: "RunCommand", Params: []string{filepath.Join(tempDir, "error.sh")}, RetryCount: 0, ShouldAbort: true},
|
||||
}
|
||||
|
||||
pipelineFile := filepath.Join(tempDir, "pipeline.json")
|
||||
pipelineData, err := json.Marshal(pipeline)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to marshal pipeline: %v", err)
|
||||
}
|
||||
if err := os.WriteFile(pipelineFile, pipelineData, 0644); err != nil {
|
||||
t.Fatalf("Failed to write pipeline file: %v", err)
|
||||
}
|
||||
|
||||
// Set up AppState
|
||||
app := &AppState{
|
||||
Config: config.BaseConfig{
|
||||
DeploymentFile: pipelineFile,
|
||||
},
|
||||
}
|
||||
|
||||
// Run the pipeline
|
||||
err = app.runPipeline(pipeline)
|
||||
if err == nil {
|
||||
t.Errorf("Expected error due to 'Error Step', but got none")
|
||||
}
|
||||
}
|
||||
|
||||
func randomString(length int) string {
|
||||
const charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
|
||||
b := make([]byte, length)
|
||||
for i := range b {
|
||||
b[i] = charset[rand.Intn(len(charset))]
|
||||
}
|
||||
return string(b)
|
||||
}
|
||||
|
||||
func TestK3DNamespaceCreation(t *testing.T) {
|
||||
// Check if k3d is installed
|
||||
_, err := exec.LookPath("k3d")
|
||||
if err != nil {
|
||||
t.Fatal("k3d is not installed. Please install k3d to run this test.")
|
||||
}
|
||||
|
||||
// Create a test cluster
|
||||
clusterName := "test-" + randomString(6)
|
||||
|
||||
cmd := exec.Command("k3d", "cluster", "create", clusterName, "--servers", "1")
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
if err := cmd.Run(); err != nil {
|
||||
t.Fatalf("Failed to create k3d cluster: %v", err)
|
||||
}
|
||||
defer func() {
|
||||
// Clean up the test cluster
|
||||
cmd := exec.Command("k3d", "cluster", "delete", clusterName)
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
if err := cmd.Run(); err != nil {
|
||||
t.Errorf("Failed to delete k3d cluster: %v", err)
|
||||
}
|
||||
}()
|
||||
|
||||
// Create a temporary directory for the pipeline config
|
||||
tempDir, err := os.MkdirTemp("", "k3d-test")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create temp directory: %v", err)
|
||||
}
|
||||
defer os.RemoveAll(tempDir)
|
||||
|
||||
// Create a test JSON pipeline file
|
||||
pipeline := []PipelineStep{
|
||||
{Name: "Ensure Namespace Exists", Function: "k8sNamespaceExists", Params: []string{"test-namespace"}, RetryCount: 0, ShouldAbort: true},
|
||||
}
|
||||
pipelineFile := filepath.Join(tempDir, "pipeline.json")
|
||||
pipelineData, err := json.Marshal(pipeline)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to marshal pipeline: %v", err)
|
||||
}
|
||||
if err := os.WriteFile(pipelineFile, pipelineData, 0644); err != nil {
|
||||
t.Fatalf("Failed to write pipeline file: %v", err)
|
||||
}
|
||||
|
||||
// Set up AppState
|
||||
app := &AppState{
|
||||
Config: config.BaseConfig{
|
||||
DeploymentFile: pipelineFile,
|
||||
},
|
||||
}
|
||||
|
||||
// Run the pipeline
|
||||
err = app.runPipeline(pipeline)
|
||||
if err != nil {
|
||||
t.Fatalf("Pipeline execution failed: %v", err)
|
||||
}
|
||||
|
||||
// Verify the namespace exists
|
||||
cmd = exec.Command("kubectl", "get", "ns", "test-namespace")
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
if err := cmd.Run(); err != nil {
|
||||
t.Fatalf("Namespace 'test-namespace' was not created: %v", err)
|
||||
}
|
||||
}
|
||||
85
app/k8s.go
Normal file
85
app/k8s.go
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
package app
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"os/exec"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func k8sNamespaceExists(project string) error {
|
||||
|
||||
commandString := "kubectl get ns " + project
|
||||
|
||||
cmd := exec.Command("sh", "-c", commandString)
|
||||
var stdout, stderr bytes.Buffer
|
||||
cmd.Stdout = &stdout
|
||||
cmd.Stderr = &stderr
|
||||
err := cmd.Run()
|
||||
if err != nil {
|
||||
|
||||
if strings.Contains(stderr.String(), "not found") {
|
||||
err := k8sCreateNamespace(project)
|
||||
if err != nil {
|
||||
slog.Error(fmt.Sprintf("Failed to create namespace: %s", project))
|
||||
return fmt.Errorf("failed to create namespace: %w", err)
|
||||
}
|
||||
return nil
|
||||
} else {
|
||||
slog.Error(fmt.Sprintf("❌ Command failed with error: %v\n", err))
|
||||
slog.Debug(fmt.Sprintf("🐞 Stdout: %s\n", stdout.String()))
|
||||
slog.Debug(fmt.Sprintf("🐞 Stderr: %s\n", stderr.String()))
|
||||
return fmt.Errorf("failed to run kubectl command: %w", err)
|
||||
}
|
||||
}
|
||||
output := stdout.String()
|
||||
if !strings.Contains(output, project) {
|
||||
return fmt.Errorf("namespace %s does not exist", project)
|
||||
}
|
||||
slog.Info(fmt.Sprintf("k8sNamespaceExists nothing to do - project: %s eists ...", project))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func k8sCreateNamespace(project string) error {
|
||||
slog.Info(fmt.Sprintf("in k8sCreateNamespace with project: %s", project))
|
||||
commandString := "kubectl create ns " + project
|
||||
cmd := exec.Command("sh", "-c", commandString)
|
||||
var stdout, stderr bytes.Buffer
|
||||
cmd.Stdout = &stdout
|
||||
cmd.Stderr = &stderr
|
||||
err := cmd.Run()
|
||||
if err != nil {
|
||||
slog.Error(fmt.Sprintf("❌ Command failed with error: %v\n", err))
|
||||
slog.Debug(fmt.Sprintf("🐞 Stdout: %s\n", stdout.String()))
|
||||
slog.Debug(fmt.Sprintf("🐞 Stderr: %s\n", stderr.String()))
|
||||
return fmt.Errorf("failed to run kubectl command: %w", err)
|
||||
}
|
||||
output := stdout.String()
|
||||
if !strings.Contains(output, project) {
|
||||
return fmt.Errorf("failed to create namespace %s", project)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func RunCommand(command string) error {
|
||||
slog.Debug(fmt.Sprintf("🐞 Running script command: %s", command))
|
||||
cmd := exec.Command("sh", "-c", command)
|
||||
var stdout, stderr bytes.Buffer
|
||||
cmd.Stdout = &stdout
|
||||
cmd.Stderr = &stderr
|
||||
err := cmd.Run()
|
||||
if err != nil {
|
||||
slog.Error(fmt.Sprintf("❌ Command failed with error: %v\n", err))
|
||||
slog.Debug(fmt.Sprintf("🐞 Stdout: %s\n", stdout.String()))
|
||||
slog.Debug(fmt.Sprintf("🐞 Stderr: %s\n", stderr.String()))
|
||||
return fmt.Errorf("failed to run script command: %w", err)
|
||||
}
|
||||
output := stdout.String()
|
||||
slog.Debug(fmt.Sprintf("RunCommand command executed successfully: %s", command))
|
||||
slog.Debug(fmt.Sprintf("RunCommand command output: %s", output))
|
||||
|
||||
return nil
|
||||
}
|
||||
17
base.json.example
Normal file
17
base.json.example
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
{
|
||||
"projects_directory": "/home/user/docker/",
|
||||
"app_image": "headshead/some-app:0.0.1",
|
||||
"webserver_image": "headsheddev/some-webserver_image:0.0.1",
|
||||
"db": "database/database.sqlite",
|
||||
"env": ".env",
|
||||
"preview_path": "prv",
|
||||
"data_www": "data_www",
|
||||
"static_images": "data_images/",
|
||||
"public_images": "images",
|
||||
"php_conf": "php/local.ini",
|
||||
"exports": "exports",
|
||||
"logs": "logs/laravel.log",
|
||||
"admin_url": ".headshed.dev",
|
||||
"preview_url": "-prv.headshed.dev",
|
||||
"nginx_conf": "nginx/conf.d"
|
||||
}
|
||||
17
build.sh
Executable file
17
build.sh
Executable file
|
|
@ -0,0 +1,17 @@
|
|||
#!/bin/bash
|
||||
|
||||
mkdir -p bin
|
||||
|
||||
echo "Building for Linux..."
|
||||
GOOS=linux GOARCH=amd64 go build -ldflags="-s -w" -o bin/infctl-linux-amd64
|
||||
|
||||
echo "Building for Windows..."
|
||||
GOOS=windows GOARCH=amd64 go build -ldflags="-s -w" -o bin/infctl-windows-amd64.exe
|
||||
|
||||
echo "Building for macOS (Intel)..."
|
||||
GOOS=darwin GOARCH=amd64 go build -ldflags="-s -w" -o bin/infctl-darwin-amd64
|
||||
|
||||
echo "Building for macOS (Apple Silicon)..."
|
||||
GOOS=darwin GOARCH=arm64 go build -ldflags="-s -w" -o bin/infctl-darwin-arm64
|
||||
|
||||
echo "Build complete. Binaries are in the 'bin' directory."
|
||||
19
config.json.example
Normal file
19
config.json.example
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
{
|
||||
"project": "hdshd",
|
||||
"project_data": "path_to/project_data",
|
||||
"app_image": "headsheddev/some-app:0.0.1",
|
||||
"env": "path_to/.env",
|
||||
"images": "path_to_images",
|
||||
"php_conf": "path_to/local.ini",
|
||||
"exports": "path_to/exports",
|
||||
"db": "path_to/db",
|
||||
"logs": "path_to/logs",
|
||||
"preview_path": "path_to_preview",
|
||||
"webserver_image": "headsheddev/my-nginx:0.0.1",
|
||||
"public_images": "path_to/images",
|
||||
"data_www": "path_to/www",
|
||||
"nginx_conf": "path_to/conf.d",
|
||||
"admin_url": "admin_url.headshed.dev",
|
||||
"preview_url": "app-prv.headshed.dev",
|
||||
"ui_url": "ww2.headshed.dev",
|
||||
}
|
||||
63
config/base.go
Normal file
63
config/base.go
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
package config
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
)
|
||||
|
||||
type BaseConfig struct {
|
||||
ProjectsDirectory string `json:"projects_directory"`
|
||||
Env string `json:"env"`
|
||||
StaticImages string `json:"static_images"`
|
||||
PublicImages string `json:"public_images"`
|
||||
PhpConf string `json:"php_conf"`
|
||||
Exports string `json:"exports"`
|
||||
Logs string `json:"logs"`
|
||||
PreviewPath string `json:"preview_path"`
|
||||
DataWww string `json:"data_www"`
|
||||
NginxConf string `json:"nginx_conf"`
|
||||
AdminURL string `json:"admin_url"`
|
||||
PreviewURL string `json:"preview_url"`
|
||||
AppImage string `json:"app_image"`
|
||||
WebserverImage string `json:"webserver_image"`
|
||||
EmptyDB string `json:"empty_db"`
|
||||
DB string `json:"db"`
|
||||
EmptyImages string `json:"empty_imaages"`
|
||||
DeploymentType string `json:"deployment_type"`
|
||||
DeploymentFile string `json:"deployment_file"`
|
||||
Port int `json:"port"`
|
||||
}
|
||||
|
||||
func ReadBaseConfig(path string) (BaseConfig, error) {
|
||||
|
||||
deploymentType := os.Getenv("DEPLOYMENT_TYPE")
|
||||
|
||||
deploymentFile := flag.String("deployment-file", "", "path to config file")
|
||||
helpFlag := flag.Bool("help", false, "show help")
|
||||
flag.Parse()
|
||||
|
||||
if *helpFlag {
|
||||
fmt.Println("Usage: infctl-cli --deployment-file=<path_to_config_file>")
|
||||
fmt.Println("DEPLOYMENT_TYPE environment variable must be set to 'json' or 'api (api is not implemented yet)'")
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
var config BaseConfig
|
||||
if *deploymentFile != "" {
|
||||
config.DeploymentFile = *deploymentFile
|
||||
}
|
||||
|
||||
data, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
return BaseConfig{}, fmt.Errorf("failed to read file: %w", err)
|
||||
}
|
||||
|
||||
if err := json.Unmarshal(data, &config); err != nil {
|
||||
return BaseConfig{}, fmt.Errorf("failed to unmarshal JSON: %w", err)
|
||||
}
|
||||
config.DeploymentType = deploymentType
|
||||
|
||||
return config, nil
|
||||
}
|
||||
29
config/customer.go
Normal file
29
config/customer.go
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
package config
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
)
|
||||
|
||||
type CustomerConfig struct {
|
||||
Project string `json:"project"`
|
||||
CustomerDirectory string `json:"customer_directory"`
|
||||
UIURL string `json:"ui_url"`
|
||||
StaticURL string `json:"static_url"`
|
||||
Port int `json:"port"`
|
||||
}
|
||||
|
||||
func ReadCustomerConfig(path string) (CustomerConfig, error) {
|
||||
data, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
return CustomerConfig{}, fmt.Errorf("failed to read file: %w", err)
|
||||
}
|
||||
|
||||
var cust CustomerConfig
|
||||
if err := json.Unmarshal(data, &cust); err != nil {
|
||||
return CustomerConfig{}, fmt.Errorf("failed to unmarshal JSON: %w", err)
|
||||
}
|
||||
|
||||
return cust, nil
|
||||
}
|
||||
74
database/database.go
Normal file
74
database/database.go
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
package database
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"log"
|
||||
"log/slog"
|
||||
"os"
|
||||
|
||||
_ "modernc.org/sqlite"
|
||||
)
|
||||
|
||||
func NewDatabase(dbPath string) (*sql.DB, error) {
|
||||
|
||||
// Check if the application is running in a test environment
|
||||
if os.Getenv("TEST_ENV") == "true" {
|
||||
dbPath = ":memory:" // Use in-memory database for tests
|
||||
slog.Info("🧪 Running in test environment, using in-memory database")
|
||||
log.Fatal("🧪 Running in test environment, using in-memory database ")
|
||||
}
|
||||
|
||||
db, err := sql.Open("sqlite", dbPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
createTableSQL := `
|
||||
CREATE TABLE IF NOT EXISTS project_name (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
project_name TEXT NOT NULL,
|
||||
port INTEGER NOT NULL,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
|
||||
);`
|
||||
_, err = db.Exec(createTableSQL)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return db, nil
|
||||
}
|
||||
|
||||
func CheckProjectName(db *sql.DB, projectName string) (bool, error) {
|
||||
var exists bool
|
||||
query := `SELECT EXISTS(SELECT 1 FROM project_name WHERE project_name = ? LIMIT 1);`
|
||||
err := db.QueryRow(query, projectName).Scan(&exists)
|
||||
if err != nil && err != sql.ErrNoRows {
|
||||
return false, err
|
||||
}
|
||||
return !exists, nil
|
||||
}
|
||||
|
||||
func AddProjectName(db *sql.DB, projectName string, port int) error {
|
||||
query := `INSERT INTO project_name (project_name, port) VALUES (?, ?);`
|
||||
_, err := db.Exec(query, projectName, port)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func GetNextPortNumber(db *sql.DB) (int, error) {
|
||||
var maxPortNumber sql.NullInt64
|
||||
query := `SELECT MAX(port) FROM project_name;`
|
||||
err := db.QueryRow(query).Scan(&maxPortNumber)
|
||||
if err != nil && err != sql.ErrNoRows {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
if !maxPortNumber.Valid {
|
||||
// No rows in the table, return a default port number
|
||||
return 10000, nil
|
||||
}
|
||||
return int(maxPortNumber.Int64 + 1), nil
|
||||
}
|
||||
30
files/ctl/nginx/default.conf
Normal file
30
files/ctl/nginx/default.conf
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
server {
|
||||
listen 80;
|
||||
|
||||
# Redirect all HTTP requests to HTTPS
|
||||
# return 301 https://$host$request_uri;
|
||||
|
||||
index index.php index.html;
|
||||
error_log /var/log/nginx/error.log;
|
||||
access_log /var/log/nginx/access.log;
|
||||
root /var/www/public;
|
||||
location ~ \.php$ {
|
||||
try_files $uri =404;
|
||||
fastcgi_split_path_info ^(.+\.php)(/.+)$;
|
||||
fastcgi_pass localhost:9000;
|
||||
fastcgi_index index.php;
|
||||
include fastcgi_params;
|
||||
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
|
||||
fastcgi_param PATH_INFO $fastcgi_path_info;
|
||||
fastcgi_param HTTPS on;
|
||||
fastcgi_param HTTP_X_FORWARDED_PROTO https;
|
||||
}
|
||||
location / {
|
||||
try_files $uri $uri/ /index.php?$query_string;
|
||||
gzip_static on;
|
||||
}
|
||||
location /storage/ {
|
||||
alias /var/www/storage/app/public/;
|
||||
autoindex on;
|
||||
}
|
||||
}
|
||||
8
files/ctl/php/local.ini
Normal file
8
files/ctl/php/local.ini
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
; PHP Configuration
|
||||
; extension=pdo_pgsql.so
|
||||
; extension=pgsql.so
|
||||
upload_max_filesize = 100M
|
||||
post_max_size = 100M
|
||||
max_execution_time = 300
|
||||
memory_limit = 256M
|
||||
; Add any other PHP configurations you need
|
||||
18
go.mod
Normal file
18
go.mod
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
module headshed/infctl-cli
|
||||
|
||||
go 1.23.3
|
||||
|
||||
require modernc.org/sqlite v1.38.0
|
||||
|
||||
require (
|
||||
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/ncruces/go-strftime v0.1.9 // indirect
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
||||
golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0 // indirect
|
||||
golang.org/x/sys v0.33.0 // indirect
|
||||
modernc.org/libc v1.65.10 // indirect
|
||||
modernc.org/mathutil v1.7.1 // indirect
|
||||
modernc.org/memory v1.11.0 // indirect
|
||||
)
|
||||
47
go.sum
Normal file
47
go.sum
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
|
||||
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
||||
github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e h1:ijClszYn+mADRFY17kjQEVQ1XRhq2/JR1M3sGqeJoxs=
|
||||
github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4=
|
||||
github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
||||
golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0 h1:R84qjqJb5nVJMxqWYb3np9L5ZsaDtB+a39EqjV0JSUM=
|
||||
golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0/go.mod h1:S9Xr4PYopiDyqSyp5NjCrhFrqg6A5zA2E/iPHPhqnS8=
|
||||
golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU=
|
||||
golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
|
||||
golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ=
|
||||
golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
|
||||
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/tools v0.33.0 h1:4qz2S3zmRxbGIhDIAgjxvFutSvH5EfnsYrRBj0UI0bc=
|
||||
golang.org/x/tools v0.33.0/go.mod h1:CIJMaWEY88juyUfo7UbgPqbC8rU2OqfAV1h2Qp0oMYI=
|
||||
modernc.org/cc/v4 v4.26.1 h1:+X5NtzVBn0KgsBCBe+xkDC7twLb/jNVj9FPgiwSQO3s=
|
||||
modernc.org/cc/v4 v4.26.1/go.mod h1:uVtb5OGqUKpoLWhqwNQo/8LwvoiEBLvZXIQ/SmO6mL0=
|
||||
modernc.org/ccgo/v4 v4.28.0 h1:rjznn6WWehKq7dG4JtLRKxb52Ecv8OUGah8+Z/SfpNU=
|
||||
modernc.org/ccgo/v4 v4.28.0/go.mod h1:JygV3+9AV6SmPhDasu4JgquwU81XAKLd3OKTUDNOiKE=
|
||||
modernc.org/fileutil v1.3.3 h1:3qaU+7f7xxTUmvU1pJTZiDLAIoJVdUSSauJNHg9yXoA=
|
||||
modernc.org/fileutil v1.3.3/go.mod h1:HxmghZSZVAz/LXcMNwZPA/DRrQZEVP9VX0V4LQGQFOc=
|
||||
modernc.org/gc/v2 v2.6.5 h1:nyqdV8q46KvTpZlsw66kWqwXRHdjIlJOhG6kxiV/9xI=
|
||||
modernc.org/gc/v2 v2.6.5/go.mod h1:YgIahr1ypgfe7chRuJi2gD7DBQiKSLMPgBQe9oIiito=
|
||||
modernc.org/libc v1.65.10 h1:ZwEk8+jhW7qBjHIT+wd0d9VjitRyQef9BnzlzGwMODc=
|
||||
modernc.org/libc v1.65.10/go.mod h1:StFvYpx7i/mXtBAfVOjaU0PWZOvIRoZSgXhrwXzr8Po=
|
||||
modernc.org/mathutil v1.7.1 h1:GCZVGXdaN8gTqB1Mf/usp1Y/hSqgI2vAGGP4jZMCxOU=
|
||||
modernc.org/mathutil v1.7.1/go.mod h1:4p5IwJITfppl0G4sUEDtCr4DthTaT47/N3aT6MhfgJg=
|
||||
modernc.org/memory v1.11.0 h1:o4QC8aMQzmcwCK3t3Ux/ZHmwFPzE6hf2Y5LbkRs+hbI=
|
||||
modernc.org/memory v1.11.0/go.mod h1:/JP4VbVC+K5sU2wZi9bHoq2MAkCnrt2r98UGeSK7Mjw=
|
||||
modernc.org/opt v0.1.4 h1:2kNGMRiUjrp4LcaPuLY2PzUfqM/w9N23quVwhKt5Qm8=
|
||||
modernc.org/opt v0.1.4/go.mod h1:03fq9lsNfvkYSfxrfUhZCWPk1lm4cq4N+Bh//bEtgns=
|
||||
modernc.org/sortutil v1.2.1 h1:+xyoGf15mM3NMlPDnFqrteY07klSFxLElE2PVuWIJ7w=
|
||||
modernc.org/sortutil v1.2.1/go.mod h1:7ZI3a3REbai7gzCLcotuw9AC4VZVpYMjDzETGsSMqJE=
|
||||
modernc.org/sqlite v1.38.0 h1:+4OrfPQ8pxHKuWG4md1JpR/EYAh3Md7TdejuuzE7EUI=
|
||||
modernc.org/sqlite v1.38.0/go.mod h1:1Bj+yES4SVvBZ4cBOpVZ6QgesMCKpJZDq0nxYzOpmNE=
|
||||
modernc.org/strutil v1.2.1 h1:UneZBkQA+DX2Rp35KcM69cSsNES9ly8mQWD71HKlOA0=
|
||||
modernc.org/strutil v1.2.1/go.mod h1:EHkiggD70koQxjVdSBM3JKM7k6L0FbGE5eymy9i3B9A=
|
||||
modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y=
|
||||
modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
|
||||
37
install.sh
Executable file
37
install.sh
Executable file
|
|
@ -0,0 +1,37 @@
|
|||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
# Determine the platform
|
||||
OS=$(uname -s)
|
||||
ARCH=$(uname -m)
|
||||
|
||||
# Map architecture
|
||||
if [ "$ARCH" == "x86_64" ]; then
|
||||
ARCH="amd64"
|
||||
elif [[ "$ARCH" == "arm64" || "$ARCH" == "aarch64" ]]; then
|
||||
ARCH="arm64"
|
||||
else
|
||||
echo "Unsupported architecture: $ARCH"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Map OS
|
||||
case "$OS" in
|
||||
Linux) OS="linux" ;;
|
||||
Darwin) OS="darwin" ;;
|
||||
*) echo "Unsupported OS: $OS"; exit 1 ;;
|
||||
esac
|
||||
|
||||
# Construct the download URL
|
||||
VERSION="v0.0.1"
|
||||
BINARY_URL="https://codeberg.org/headshed/infctl-cli/releases/download/$VERSION/infctl-$OS-$ARCH"
|
||||
|
||||
# Download the binary
|
||||
echo "Downloading infctl binary for $OS-$ARCH..."
|
||||
sudo curl -s -L "$BINARY_URL" -o /usr/local/bin/infctl
|
||||
|
||||
# Make it executable
|
||||
sudo chmod +x /usr/local/bin/infctl
|
||||
|
||||
echo "infctl installed successfully!"
|
||||
24
k8s-manifests/ctl-ingress/ingress.yaml
Normal file
24
k8s-manifests/ctl-ingress/ingress.yaml
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: tls-infctl-ingress-http
|
||||
namespace: infctl
|
||||
annotations:
|
||||
cert-manager.io/issuer: "le-cluster-issuer-http"
|
||||
kubernetes.io/ingress.class: "traefik"
|
||||
spec:
|
||||
tls:
|
||||
- hosts:
|
||||
- ctl.headshed.dev
|
||||
secretName: tls-infctl-ingress-http
|
||||
rules:
|
||||
- host: ctl.headshed.dev
|
||||
http:
|
||||
paths:
|
||||
- path: /
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
name: infctl-service
|
||||
port:
|
||||
name: web
|
||||
22
k8s-manifests/ctl-ingress/issuer.yaml
Normal file
22
k8s-manifests/ctl-ingress/issuer.yaml
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
apiVersion: cert-manager.io/v1
|
||||
kind: Issuer
|
||||
metadata:
|
||||
name: le-cluster-issuer-http
|
||||
namespace: infctl
|
||||
spec:
|
||||
acme:
|
||||
email: marshyon@gmail.com
|
||||
# We use the staging server here for testing to avoid hitting
|
||||
# server: https://acme-staging-v02.api.letsencrypt.org/directory
|
||||
server: https://acme-v02.api.letsencrypt.org/directory
|
||||
privateKeySecretRef:
|
||||
name: http-issuer-account-key
|
||||
# solvers:
|
||||
# - http01:
|
||||
# # The ingressClass used to create the necessary ingress routes
|
||||
# ingress:
|
||||
# class: traefik
|
||||
solvers:
|
||||
- http01:
|
||||
ingress:
|
||||
class: traefik
|
||||
6
k8s-manifests/ctl-ingress/kustomization.yaml
Normal file
6
k8s-manifests/ctl-ingress/kustomization.yaml
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
resources:
|
||||
- issuer.yaml
|
||||
- service.yaml
|
||||
- ingress.yaml
|
||||
13
k8s-manifests/ctl-ingress/service.yaml
Normal file
13
k8s-manifests/ctl-ingress/service.yaml
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: infctl-service
|
||||
namespace: infctl
|
||||
spec:
|
||||
selector:
|
||||
app: nginx-sl
|
||||
ports:
|
||||
- name: web
|
||||
protocol: TCP
|
||||
port: 80
|
||||
targetPort: 80
|
||||
298
k8s-manifests/ctl/deployment.yaml
Normal file
298
k8s-manifests/ctl/deployment.yaml
Normal file
|
|
@ -0,0 +1,298 @@
|
|||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: infctl-deployment
|
||||
namespace: infctl
|
||||
labels:
|
||||
app: nginx-sl
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: nginx-sl
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: nginx-sl
|
||||
spec:
|
||||
|
||||
imagePullSecrets:
|
||||
- name: registry-credentials
|
||||
|
||||
|
||||
initContainers:
|
||||
- name: init-data-s3
|
||||
image: amazon/aws-cli:latest
|
||||
imagePullPolicy: IfNotPresent
|
||||
# command: ["/bin/bash", "-c", "trap : TERM INT; sleep infinity & wait"]
|
||||
# command: ["/bin/bash", "-c", "/scripts/init-data.sh && echo 'Script completed, sleeping for debugging' && sleep 3600"]
|
||||
command: ["/bin/bash", "/scripts/init-data.sh"]
|
||||
|
||||
volumeMounts:
|
||||
- name: init-script
|
||||
mountPath: /scripts
|
||||
- name: infctl-public-data
|
||||
mountPath: /var/www/public
|
||||
- name: infctl-storage-data
|
||||
mountPath: /var/www/storage
|
||||
- name: infctl-database-data
|
||||
mountPath: /var/www/database
|
||||
|
||||
env:
|
||||
- name: AWS_ACCESS_KEY_ID
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: aws-credentials
|
||||
key: access-key
|
||||
- name: AWS_SECRET_ACCESS_KEY
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: aws-credentials
|
||||
key: secret-key
|
||||
- name: AWS_DEFAULT_REGION
|
||||
value: "eu-west-2"
|
||||
- name: merge-data
|
||||
image: busybox
|
||||
imagePullPolicy: IfNotPresent
|
||||
# command: ["/bin/sh", "-c", "trap : TERM INT; sleep infinity & wait"]
|
||||
command: ["/bin/sh", "/scripts/merge_data_inf.sh"]
|
||||
|
||||
volumeMounts:
|
||||
- name: merge-script
|
||||
mountPath: /scripts
|
||||
- name: infctl-public-data
|
||||
mountPath: /var/www/public
|
||||
- name: infctl-storage-data
|
||||
mountPath: /var/www/storage
|
||||
- name: infctl-database-data
|
||||
mountPath: /var/www/database
|
||||
|
||||
env:
|
||||
- name: AWS_ACCESS_KEY_ID
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: aws-credentials
|
||||
key: access-key
|
||||
- name: AWS_SECRET_ACCESS_KEY
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: aws-credentials
|
||||
key: secret-key
|
||||
- name: AWS_DEFAULT_REGION
|
||||
value: "eu-west-2"
|
||||
|
||||
|
||||
|
||||
|
||||
containers:
|
||||
|
||||
|
||||
- name: php-fpm
|
||||
image: $APP_CONTAINER
|
||||
command: ["/bin/sh", "-c"]
|
||||
args:
|
||||
- |
|
||||
cd /var/www
|
||||
php artisan config:clear
|
||||
npm install && npm run build
|
||||
php-fpm
|
||||
resources:
|
||||
requests:
|
||||
memory: "128Mi"
|
||||
cpu: "100m"
|
||||
limits:
|
||||
memory: "256Mi"
|
||||
cpu: "500m"
|
||||
env:
|
||||
- name: APP_NAME
|
||||
value: "infctl"
|
||||
- name: APP_ENV
|
||||
value: production
|
||||
- name: APP_KEY
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: app-key-secret
|
||||
key: app_key
|
||||
- name: APP_DEBUG
|
||||
value: "false"
|
||||
- name: APP_URL
|
||||
value: "https://ctl.headshed.dev/"
|
||||
- name: APP_LOCAL
|
||||
value: "en"
|
||||
- name: APP_FALLBACK_LOCALE
|
||||
value: "en"
|
||||
- name: APP_FAKER_LOCALE
|
||||
value: "en_US"
|
||||
- name: APP_MAINTENANCE_DRIVER
|
||||
value: "file"
|
||||
- name: PHP_CLI_SERVER_WORKERS
|
||||
value: "4"
|
||||
- name: BCRYPT_ROUNDS
|
||||
value: "12"
|
||||
- name: LOG_CHANNEL
|
||||
value: "stack"
|
||||
- name: LOG_STACK
|
||||
value: "single"
|
||||
- name: LOG_DEPRECATIONS_CHANNEL
|
||||
value: ""
|
||||
- name: LOG_LEVEL
|
||||
value: "debug"
|
||||
- name: DB_CONNECTION
|
||||
value: pgsql
|
||||
- name: DB_HOST
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: pg-credentials
|
||||
key: host
|
||||
- name: DB_PORT
|
||||
value: "5432"
|
||||
- name: DB_DATABASE
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: pg-credentials
|
||||
key: dbname
|
||||
- name: DB_USERNAME
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: pg-credentials
|
||||
key: username
|
||||
- name: DB_PASSWORD
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: pg-credentials
|
||||
key: password
|
||||
- name: SESSION_DRIVER
|
||||
value: "database"
|
||||
- name: SESSION_LIFETIME
|
||||
value: "120"
|
||||
- name: SESSION_ENCRYPT
|
||||
value: "false"
|
||||
- name: SESSION_PATH
|
||||
value: "/"
|
||||
- name: SESSION_DOMAIN
|
||||
value: ""
|
||||
- name: BROADCAST_CONNECTION
|
||||
value: "log"
|
||||
- name: FILESYSTEM_DISK
|
||||
value: "s3"
|
||||
- name: FILAMENT_FILESYSTEM_DISK
|
||||
value: "s3"
|
||||
- name: QUEUE_CONNECTION
|
||||
value: "redis"
|
||||
- name: CACHE_STORE
|
||||
value: "database"
|
||||
- name: MEMCACHED_HOST
|
||||
value: "127.0.0.1"
|
||||
- name: REDIS_CLIENT
|
||||
value: "phpredis"
|
||||
- name: REDIS_HOST
|
||||
value: "redis.redis.svc.cluster.local"
|
||||
- name: REDIS_PASSWORD
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: redis-auth
|
||||
key: password
|
||||
- name: REDIS_PORT
|
||||
value: "6379"
|
||||
- name: MAIL_MAILER
|
||||
value: "smtp"
|
||||
- name: MAIL_HOST
|
||||
value: "smtp.fastmail.com"
|
||||
- name: MAIL_PORT
|
||||
value: "465"
|
||||
- name: MAIL_USERNAME
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: smtp-credentials
|
||||
key: user
|
||||
- name: MAIL_PASSWORD
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: smtp-credentials
|
||||
key: password
|
||||
- name: MAIL_ENCRYPTION
|
||||
value: "ssl"
|
||||
- name: MAIL_FROM_ADDRESS
|
||||
value: "info@headshed.dev"
|
||||
- name: MAIL_FROM_NAME
|
||||
value: "Headshed"
|
||||
- name: AWS_ACCESS_KEY_ID
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: aws-credentials
|
||||
key: access-key
|
||||
- name: AWS_SECRET_ACCESS_KEY
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: aws-credentials
|
||||
key: secret-key
|
||||
- name: AWS_DEFAULT_REGION
|
||||
value: "eu-west-2"
|
||||
- name: AWS_BUCKET
|
||||
value: "headsheddev-sharelt-cust-data"
|
||||
- name: AWS_USE_PATH_STYLE_ENDPOINT
|
||||
value: "false"
|
||||
- name: VITE_APP_NAME
|
||||
value: "infctl"
|
||||
tty: true
|
||||
workingDir: /var/www
|
||||
volumeMounts:
|
||||
- name: infctl-public-data
|
||||
mountPath: /var/www/public
|
||||
- name: infctl-storage-data
|
||||
mountPath: /var/www/storage
|
||||
- name: infctl-database-data
|
||||
mountPath: /var/www/database
|
||||
- mountPath: /usr/local/etc/php/conf.d/local.ini
|
||||
name: php-config
|
||||
subPath: local.ini
|
||||
|
||||
|
||||
- name: nginx
|
||||
image: nginx:1.28
|
||||
imagePullPolicy: IfNotPresent
|
||||
resources:
|
||||
requests:
|
||||
memory: "64Mi"
|
||||
cpu: "50m"
|
||||
limits:
|
||||
memory: "128Mi"
|
||||
cpu: "100m"
|
||||
ports:
|
||||
- containerPort: 80
|
||||
volumeMounts:
|
||||
- name: infctl-public-data
|
||||
mountPath: /var/www/public
|
||||
- name: infctl-storage-data
|
||||
mountPath: /var/www/storage
|
||||
- name: infctl-database-data
|
||||
mountPath: /var/www/database
|
||||
- name: nginx-config
|
||||
mountPath: /etc/nginx/conf.d
|
||||
|
||||
|
||||
volumes:
|
||||
- name: init-script
|
||||
configMap:
|
||||
name: init-data-script
|
||||
- name: merge-script
|
||||
configMap:
|
||||
name: merge-data-script
|
||||
- name: nginx-config
|
||||
configMap:
|
||||
name: nginx-config
|
||||
# - name: nginx-content
|
||||
# configMap:
|
||||
# name: nginx-content
|
||||
- name: infctl-public-data
|
||||
persistentVolumeClaim:
|
||||
claimName: infctl-public-data-pvc
|
||||
- name: infctl-storage-data
|
||||
persistentVolumeClaim:
|
||||
claimName: infctl-storage-data-pvc
|
||||
- name: infctl-database-data
|
||||
persistentVolumeClaim:
|
||||
claimName: infctl-database-data-pvc
|
||||
- name: php-config
|
||||
configMap:
|
||||
name: php-config
|
||||
5
k8s-manifests/ctl/kustomization.yaml
Normal file
5
k8s-manifests/ctl/kustomization.yaml
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
resources:
|
||||
- pvc.yaml
|
||||
- deployment.yaml
|
||||
35
k8s-manifests/ctl/pvc.yaml
Normal file
35
k8s-manifests/ctl/pvc.yaml
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
apiVersion: v1
|
||||
kind: PersistentVolumeClaim
|
||||
metadata:
|
||||
name: infctl-public-data-pvc
|
||||
namespace: infctl
|
||||
spec:
|
||||
accessModes:
|
||||
- ReadWriteOnce
|
||||
resources:
|
||||
requests:
|
||||
storage: 1Gi
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: PersistentVolumeClaim
|
||||
metadata:
|
||||
name: infctl-storage-data-pvc
|
||||
namespace: infctl
|
||||
spec:
|
||||
accessModes:
|
||||
- ReadWriteOnce
|
||||
resources:
|
||||
requests:
|
||||
storage: 1Gi
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: PersistentVolumeClaim
|
||||
metadata:
|
||||
name: infctl-database-data-pvc
|
||||
namespace: infctl
|
||||
spec:
|
||||
accessModes:
|
||||
- ReadWriteOnce
|
||||
resources:
|
||||
requests:
|
||||
storage: 1Gi
|
||||
71
main.go
Normal file
71
main.go
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"log/slog"
|
||||
"os"
|
||||
|
||||
"headshed/infctl-cli/app"
|
||||
"headshed/infctl-cli/config"
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
||||
var levelVar slog.LevelVar
|
||||
levelVar.Set(slog.LevelDebug)
|
||||
logger := slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{Level: &levelVar}))
|
||||
|
||||
slog.SetDefault(logger)
|
||||
|
||||
if err := run(); err != nil {
|
||||
log.Fatalf("Application error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func run() error {
|
||||
|
||||
wd, err := os.Getwd()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get current directory: %w", err)
|
||||
}
|
||||
baseConfigPath := wd + string(os.PathSeparator) + "base.json"
|
||||
configPath := wd + string(os.PathSeparator) + "config.json"
|
||||
|
||||
baseConfig, err := config.ReadBaseConfig(baseConfigPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error reading base config file: %w", err)
|
||||
}
|
||||
customerConfig, err := config.ReadCustomerConfig(configPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error reading customer config file: %w", err)
|
||||
}
|
||||
appState, err := app.NewAppState(customerConfig, baseConfig, "app.db")
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to initialize app state: %w", err)
|
||||
}
|
||||
defer func() {
|
||||
if err := appState.DB.Close(); err != nil {
|
||||
log.Printf("Error closing database: %v", err)
|
||||
}
|
||||
}()
|
||||
|
||||
if err := appState.CreatePipeline(); err != nil {
|
||||
return fmt.Errorf("failed to create customer project: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
16
pipeline.json.example
Normal file
16
pipeline.json.example
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
[
|
||||
{
|
||||
"name": "ensure inf namespace exists",
|
||||
"function": "k8sNamespaceExists",
|
||||
"params": ["infctl"],
|
||||
"retryCount": 0,
|
||||
"shouldAbort": true
|
||||
},
|
||||
{
|
||||
"name": "run inf redis secret",
|
||||
"function": "RunCommand",
|
||||
"params": ["./scripts/create_php_configmap_ctl.sh"],
|
||||
"retryCount": 0,
|
||||
"shouldAbort": true
|
||||
}
|
||||
]
|
||||
9
scripts/check_crunchy_operator.sh
Executable file
9
scripts/check_crunchy_operator.sh
Executable file
|
|
@ -0,0 +1,9 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
output=$(kubectl -n postgres-operator get pods --selector=postgres-operator.crunchydata.com/control-plane=postgres-operator --field-selector=status.phase=Running 2>&1)
|
||||
if echo "$output" | grep -iq 'running'; then
|
||||
echo "At least one pod is running."
|
||||
else
|
||||
echo "No running pods found."
|
||||
exit 1
|
||||
fi
|
||||
28
scripts/create_app_secret_ctl.sh
Executable file
28
scripts/create_app_secret_ctl.sh
Executable file
|
|
@ -0,0 +1,28 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
|
||||
if kubectl get secret app-key-secret -n infctl >/dev/null 2>&1; then
|
||||
echo "Secret app-key-secret already exists in namespace infctl. Exiting."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
generate_app_key() {
|
||||
APP_KEY=$(docker run --rm \
|
||||
--entrypoint /bin/sh \
|
||||
$APP_CONTAINER \
|
||||
-c "cd /var/www && \
|
||||
cp .env.example .env && \
|
||||
php artisan key:generate --force > /dev/null 2>&1 && \
|
||||
grep 'APP_KEY' .env | sed 's/APP_KEY=//'")
|
||||
|
||||
APP_KEY=$(echo "$APP_KEY" | tr -d '\r\n')
|
||||
|
||||
}
|
||||
|
||||
generate_app_key
|
||||
|
||||
echo "Extracted APP_KEY: $APP_KEY"
|
||||
|
||||
kubectl create secret generic app-key-secret \
|
||||
--from-literal=app_key="$APP_KEY" \
|
||||
-n infctl --dry-run=client -o yaml | kubectl apply -f -
|
||||
16
scripts/create_aws_secrets.sh
Executable file
16
scripts/create_aws_secrets.sh
Executable file
|
|
@ -0,0 +1,16 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
echo ""
|
||||
echo ""
|
||||
|
||||
temp_file=$(mktemp)
|
||||
|
||||
kubectl -n infctl create secret generic aws-credentials -o yaml --dry-run=client \
|
||||
--from-literal access-key=$AWS_ACCESS_KEY_ID \
|
||||
--from-literal secret-key=$AWS_SECRET_ACCESS_KEY > "$temp_file"
|
||||
|
||||
|
||||
kubectl apply -f $temp_file
|
||||
rm $temp_file
|
||||
|
||||
|
||||
9
scripts/create_cloudflare_secret.sh
Executable file
9
scripts/create_cloudflare_secret.sh
Executable file
|
|
@ -0,0 +1,9 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
if kubectl -n cert-manager get secret cloudflare-api-token-secret &>/dev/null; then
|
||||
echo "Secret 'cloudflare-api-token-secret' already exists in 'cert-manager' namespace. Skipping."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
kubectl create secret generic cloudflare-api-token-secret --from-literal=api-token=$API_TOKEN --namespace='cert-manager'
|
||||
|
||||
15
scripts/create_crunchy_ctl_secrets.sh
Executable file
15
scripts/create_crunchy_ctl_secrets.sh
Executable file
|
|
@ -0,0 +1,15 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
NS="postgres-operator"
|
||||
|
||||
USER=$(kubectl -n $NS get secrets ctl-pguser-ctl-controller -o jsonpath='{.data.user}' | base64 -d)
|
||||
PASSWORD=$(kubectl -n $NS get secrets ctl-pguser-ctl-controller -o jsonpath='{.data.password}' | base64 -d)
|
||||
HOST=$(kubectl -n $NS get secrets ctl-pguser-ctl-controller -o jsonpath='{.data.host}' | base64 -d)
|
||||
PORT=$(kubectl -n $NS get secrets ctl-pguser-ctl-controller -o jsonpath='{.data.port}' | base64 -d)
|
||||
DBNAME=$(kubectl -n $NS get secrets ctl-pguser-ctl-controller -o jsonpath='{.data.dbname}' | base64 -d)
|
||||
PG_URI=$(kubectl -n $NS get secrets ctl-pguser-ctl-controller -o jsonpath='{.data.uri}' | base64 -d)
|
||||
|
||||
SECRET_YAML=$(kubectl -n infctl create secret generic pg-credentials -o yaml --dry-run=client --from-literal=username="$USER" --from-literal=password="$PASSWORD" --from-literal=host="$HOST" --from-literal=dbname="$DBNAME")
|
||||
|
||||
echo "$SECRET_YAML" | kubectl apply -f -
|
||||
|
||||
8
scripts/create_crunchy_db.sh
Executable file
8
scripts/create_crunchy_db.sh
Executable file
|
|
@ -0,0 +1,8 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
EXAMOPLES_DIR=/home/user/projects/crunchy/postgres-operator-examples
|
||||
|
||||
cd $EXAMOPLES_DIR # || echo "Directory $EXAMOPLES_DIR does not exist" && exit 1
|
||||
|
||||
kubectl apply -k kustomize/postgres
|
||||
|
||||
12
scripts/create_crunchy_operator.sh
Executable file
12
scripts/create_crunchy_operator.sh
Executable file
|
|
@ -0,0 +1,12 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
EXAMOPLES_DIR=/home/user/projects/crunchy/postgres-operator-examples
|
||||
|
||||
cd $EXAMOPLES_DIR # || echo "Directory $EXAMOPLES_DIR does not exist" && exit 1
|
||||
|
||||
pwd
|
||||
|
||||
# exit 1
|
||||
|
||||
kubectl apply -k kustomize/install/namespace
|
||||
kubectl apply --server-side -k kustomize/install/default
|
||||
7
scripts/create_init_configmap_ctl.sh
Executable file
7
scripts/create_init_configmap_ctl.sh
Executable file
|
|
@ -0,0 +1,7 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
SCRIPT=scripts/init-data-ctl.sh
|
||||
|
||||
CONFIGMAP=$(kubectl -n infctl create configmap init-data-script --from-file=init-data.sh=$SCRIPT --dry-run=client -o yaml)
|
||||
|
||||
echo "$CONFIGMAP" | kubectl apply -f -
|
||||
13
scripts/create_merge_data_configmap_ctl.sh
Executable file
13
scripts/create_merge_data_configmap_ctl.sh
Executable file
|
|
@ -0,0 +1,13 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
CREATE_CONFIGMAP=$(kubectl -n infctl create configmap merge-data-script --from-file=scripts/merge_data_ctl.sh --dry-run=client -o yaml)
|
||||
|
||||
echo $CREATE_CONFIGMAP
|
||||
|
||||
echo "$CREATE_CONFIGMAP" | kubectl -n infctl apply -f -
|
||||
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "Failed to create or update the configmap."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
15
scripts/create_nginx_configmap_ctl.sh
Executable file
15
scripts/create_nginx_configmap_ctl.sh
Executable file
|
|
@ -0,0 +1,15 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
NGINX_CONFIGMAP=$(kubectl -n infctl create configmap nginx-config --from-file files/ctl/nginx/default.conf --dry-run=client -oyaml)
|
||||
|
||||
if [ -z "$NGINX_CONFIGMAP" ]; then
|
||||
echo "Failed to create NGINX configmap."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "$NGINX_CONFIGMAP" | kubectl apply -f -
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "Failed to apply NGINX configmap."
|
||||
exit 1
|
||||
fi
|
||||
echo "NGINX configmap created successfully."
|
||||
14
scripts/create_pg_ctl.sh
Normal file
14
scripts/create_pg_ctl.sh
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
cat <<EOF | kubectl apply -f -
|
||||
apiVersion: postgresql.cnpg.io/v1
|
||||
kind: Cluster
|
||||
metadata:
|
||||
name: pg-cluster
|
||||
namespace: pg-cluster
|
||||
|
||||
spec:
|
||||
instances: 2
|
||||
storage:
|
||||
size: 2Gi
|
||||
EOF
|
||||
21
scripts/create_pg_secrets.sh
Executable file
21
scripts/create_pg_secrets.sh
Executable file
|
|
@ -0,0 +1,21 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
NS="pg-cluster"
|
||||
|
||||
USERNAME=$(kubectl -n $NS get secrets pg-cluster-app -o jsonpath='{.data.username}' | base64 -d)
|
||||
PASSWORD=$(kubectl -n $NS get secrets pg-cluster-app -o jsonpath='{.data.password}' | base64 -d)
|
||||
HOST=$(kubectl -n $NS get secrets pg-cluster-app -o jsonpath='{.data.host}' | base64 -d)
|
||||
PORT=$(kubectl -n $NS get secrets pg-cluster-app -o jsonpath='{.data.port}' | base64 -d)
|
||||
DBNAME=$(kubectl -n $NS get secrets pg-cluster-app -o jsonpath='{.data.dbname}' | base64 -d)
|
||||
PG_URI=$(kubectl -n $NS get secrets pg-cluster-app -o jsonpath='{.data.uri}' | base64 -d)
|
||||
postgres_fqdn="${HOST}.${NS}.svc.cluster.local"
|
||||
|
||||
echo ""
|
||||
|
||||
echo "this script needs to be sourced"
|
||||
|
||||
echo "then run a command to use it like "
|
||||
|
||||
echo ""
|
||||
|
||||
echo 'kubectl -n infctl create secret generic pg-credentials -o yaml --dry-run=client --from-literal username=$USERNAME --from-literal password=$PASSWORD --from-literal host=$postgres_fqdn --from-literal dbname=$DBNAME'
|
||||
16
scripts/create_php_configmap_ctl.sh
Executable file
16
scripts/create_php_configmap_ctl.sh
Executable file
|
|
@ -0,0 +1,16 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
|
||||
|
||||
NGINX_CONFIGMAP=$(kubectl -n infctl create configmap php-config --from-file files/ctl/php/local.ini --dry-run=client -oyaml)
|
||||
|
||||
if [ -z "$NGINX_CONFIGMAP" ]; then
|
||||
echo "Failed to create NGINX configmap."
|
||||
exit 1
|
||||
fi
|
||||
echo "$NGINX_CONFIGMAP" | kubectl apply -f -
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "Failed to apply NGINX configmap."
|
||||
exit 1
|
||||
fi
|
||||
echo "NGINX configmap created successfully."
|
||||
5
scripts/create_redis_secret.sh
Executable file
5
scripts/create_redis_secret.sh
Executable file
|
|
@ -0,0 +1,5 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
kubectl -n infctl delete secrets redis-auth
|
||||
|
||||
kubectl get secret redis-auth -n redis -o yaml | sed "s/namespace: redis/namespace: infctl/" | kubectl apply -n infctl -f -
|
||||
24
scripts/create_registry_secret.sh
Executable file
24
scripts/create_registry_secret.sh
Executable file
|
|
@ -0,0 +1,24 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
if kubectl get secret registry-credentials -n infctl >/dev/null 2>&1; then
|
||||
echo "Secret 'registry-credentials' already exists in namespace 'infctl'. Skipping."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
|
||||
echo "Container Registry Server: $SERVER"
|
||||
|
||||
kubectl create secret docker-registry registry-credentials \
|
||||
--docker-server=$SERVER \
|
||||
--docker-username=$USER \
|
||||
--docker-password=$PASSWORD \
|
||||
--docker-email=$EMAIL \
|
||||
-n infctl
|
||||
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "Error: Failed to create the docker-registry secret."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Docker registry secret created successfully."
|
||||
|
||||
6
scripts/create_smtp_ctl_secrets.sh
Executable file
6
scripts/create_smtp_ctl_secrets.sh
Executable file
|
|
@ -0,0 +1,6 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
CREATE_SMTP_CREDS=$(kubectl -n infctl create secret generic smtp-credentials -o yaml --dry-run=client --from-literal user=$SMTP_USER --from-literal password=$SMTP_PASS)
|
||||
|
||||
echo "$CREATE_SMTP_CREDS" | kubectl apply -f -
|
||||
|
||||
42
scripts/init-data-ctl.sh
Normal file
42
scripts/init-data-ctl.sh
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
LOG_FILE="/var/log/init-data.log"
|
||||
mkdir -p /var/log
|
||||
|
||||
|
||||
echo "env variables" | tee -a "$LOG_FILE"
|
||||
env | tee -a "$LOG_FILE"
|
||||
ls -lirt /var/www/public | tee -a "$LOG_FILE"
|
||||
|
||||
mkdir -p /var/www/{public,storage,database}
|
||||
|
||||
# Function to log errors and continue
|
||||
log_error() {
|
||||
echo "[ERROR] $1" | tee -a "$LOG_FILE"
|
||||
}
|
||||
|
||||
# Check if public directory is empty
|
||||
if [ -z "$(find /var/www/public -type f -o -type d -not -name "lost+found" -not -path "/var/www/public" 2>/dev/null)" ]; then
|
||||
echo "Public directory is empty, copying data from S3..." | tee -a "$LOG_FILE"
|
||||
aws s3 cp $S3_BUCKET/assets/public.tar /var/www/public/ 2>>"$LOG_FILE" || log_error "Failed to copy public data from S3"
|
||||
else
|
||||
echo "Public directory already has data, skipping S3 copy..." | tee -a "$LOG_FILE"
|
||||
fi
|
||||
|
||||
# Check if storage directory is empty
|
||||
if [ -z "$(find /var/www/storage -type f -o -type d -not -name "lost+found" -not -path "/var/www/storage" 2>/dev/null)" ]; then
|
||||
echo "Storage directory is empty, copying data from S3..." | tee -a "$LOG_FILE"
|
||||
aws s3 cp $S3_BUCKET/assets/storage.tar /var/www/storage/ 2>>"$LOG_FILE" || log_error "Failed to copy storage data from S3"
|
||||
else
|
||||
echo "Storage directory already has data, skipping S3 copy..." | tee -a "$LOG_FILE"
|
||||
fi
|
||||
|
||||
# Check if database directory is empty
|
||||
if [ -z "$(ls -A /var/www/database 2>/dev/null)" ]; then
|
||||
echo "Database directory is empty, copying data from S3..." | tee -a "$LOG_FILE"
|
||||
aws s3 cp $S3_BUCKET/assets/database.tar /var/www/database/ 2>>"$LOG_FILE" || log_error "Failed to copy database data from S3"
|
||||
else
|
||||
echo "Database directory already has data, skipping S3 copy..." | tee -a "$LOG_FILE"
|
||||
fi
|
||||
|
||||
|
||||
|
||||
echo "Script completed. Check $LOG_FILE for details."
|
||||
11
scripts/install_cert-manager.sh
Executable file
11
scripts/install_cert-manager.sh
Executable file
|
|
@ -0,0 +1,11 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
if kubectl -n cert-manager get pods 2>/dev/null | grep -q 'Running'; then
|
||||
echo "cert-manager pods already running. Skipping installation."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
|
||||
|
||||
|
||||
kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.17.2/cert-manager.yaml
|
||||
37
scripts/install_cloudnative_pg.sh
Executable file
37
scripts/install_cloudnative_pg.sh
Executable file
|
|
@ -0,0 +1,37 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
if kubectl -n cnpg-system get pods | grep cnpg &>/dev/null; then
|
||||
echo "CloudNativePG pods already running. Skipping installation."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
echo "Installing CloudNativePG..."
|
||||
|
||||
helm repo add cnpg https://cloudnative-pg.github.io/charts
|
||||
helm upgrade --install cnpg \
|
||||
--namespace cnpg-system \
|
||||
--set config.clusterWide=true \
|
||||
--skip-crds \
|
||||
--force \
|
||||
cnpg/cloudnative-pg
|
||||
|
||||
|
||||
# had to do this
|
||||
|
||||
# kubectl get mutatingwebhookconfiguration,validatingwebhookconfiguration,crd -A | grep cnpg
|
||||
# Delete Conflicting Resources (if safe to do so):
|
||||
|
||||
# kubtctl delete <anyting in the above list>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# kubectl apply -f https://raw.githubusercontent.com/cloudnative-pg/cloudnative-pg/release-1.17/releases/cnpg-1.17.5.yaml
|
||||
|
||||
# kubectl patch configmap cnpg-config -n cnpg-system --type merge -p '{"data":{"config":"clusterWide: true"}}'
|
||||
9
scripts/install_longhorn.sh
Executable file
9
scripts/install_longhorn.sh
Executable file
|
|
@ -0,0 +1,9 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
# Check if there are any pods in the longhorn-system namespace
|
||||
if kubectl -n longhorn-system get pods --no-headers 2>/dev/null | grep -q '^[^ ]'; then
|
||||
echo "Pods already exist in the longhorn-system namespace. Skipping installation."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
kubectl apply -f https://raw.githubusercontent.com/longhorn/longhorn/v1.8.1/deploy/longhorn.yaml
|
||||
168
scripts/install_traefik.sh
Executable file
168
scripts/install_traefik.sh
Executable file
|
|
@ -0,0 +1,168 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
if kubectl -n traefik get pods --no-headers 2>/dev/null | grep -q 'Running'; then
|
||||
echo "Traefik is already running in the 'traefik' namespace. Upgrading instead."
|
||||
|
||||
# Create a temporary values file for more complex configuration
|
||||
cat > /tmp/traefik-values.yaml <<EOF
|
||||
ports:
|
||||
web:
|
||||
port: 80
|
||||
websecure:
|
||||
port: 443
|
||||
traefik:
|
||||
port: 9000
|
||||
turn-tcp:
|
||||
port: 1194
|
||||
exposedPort: 1194
|
||||
protocol: TCP
|
||||
turn-udp:
|
||||
port: 1194
|
||||
exposedPort: 1194
|
||||
protocol: UDP
|
||||
entryPoints:
|
||||
turn-tcp:
|
||||
address: ":1194/tcp"
|
||||
turn-udp:
|
||||
address: ":1194/udp"
|
||||
api:
|
||||
dashboard: true
|
||||
insecure: true
|
||||
ingressRoute:
|
||||
dashboard:
|
||||
enabled: true
|
||||
ping: true
|
||||
log:
|
||||
level: INFO
|
||||
# Add this service section to expose the ports properly
|
||||
service:
|
||||
enabled: true
|
||||
type: LoadBalancer
|
||||
annotations: {}
|
||||
ports:
|
||||
web:
|
||||
port: 80
|
||||
protocol: TCP
|
||||
targetPort: web
|
||||
websecure:
|
||||
port: 443
|
||||
protocol: TCP
|
||||
targetPort: websecure
|
||||
turn-tcp:
|
||||
port: 1194
|
||||
protocol: TCP
|
||||
targetPort: turn-tcp
|
||||
turn-udp:
|
||||
port: 1194
|
||||
protocol: UDP
|
||||
targetPort: turn-udp
|
||||
EOF
|
||||
|
||||
helm upgrade traefik traefik/traefik --namespace traefik -f /tmp/traefik-values.yaml
|
||||
|
||||
else
|
||||
echo "Installing Traefik..."
|
||||
|
||||
helm repo add traefik https://traefik.github.io/charts
|
||||
helm repo update
|
||||
|
||||
# Create a temporary values file for more complex configuration
|
||||
cat > /tmp/traefik-values.yaml <<EOF
|
||||
ports:
|
||||
web:
|
||||
port: 80
|
||||
websecure:
|
||||
port: 443
|
||||
traefik:
|
||||
port: 9000
|
||||
turn-tcp:
|
||||
port: 1194
|
||||
exposedPort: 1194
|
||||
protocol: TCP
|
||||
turn-udp:
|
||||
port: 1194
|
||||
exposedPort: 1194
|
||||
protocol: UDP
|
||||
entryPoints:
|
||||
turn-tcp:
|
||||
address: ":1194/tcp"
|
||||
turn-udp:
|
||||
address: ":1194/udp"
|
||||
api:
|
||||
dashboard: true
|
||||
insecure: true
|
||||
ingressRoute:
|
||||
dashboard:
|
||||
enabled: true
|
||||
ping: true
|
||||
log:
|
||||
level: INFO
|
||||
# Add the service section here too for new installations
|
||||
service:
|
||||
enabled: true
|
||||
type: LoadBalancer
|
||||
annotations: {}
|
||||
ports:
|
||||
web:
|
||||
port: 80
|
||||
protocol: TCP
|
||||
targetPort: web
|
||||
websecure:
|
||||
port: 443
|
||||
protocol: TCP
|
||||
targetPort: websecure
|
||||
turn-tcp:
|
||||
port: 1194
|
||||
protocol: TCP
|
||||
targetPort: turn-tcp
|
||||
turn-udp:
|
||||
port: 1194
|
||||
protocol: UDP
|
||||
targetPort: turn-udp
|
||||
EOF
|
||||
|
||||
helm install traefik traefik/traefik --namespace traefik --create-namespace -f /tmp/traefik-values.yaml
|
||||
fi
|
||||
|
||||
|
||||
|
||||
cat > traefik-turn-service.yaml << EOF
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: traefik-turn
|
||||
namespace: traefik
|
||||
labels:
|
||||
app.kubernetes.io/instance: traefik-traefik
|
||||
app.kubernetes.io/name: traefik
|
||||
spec:
|
||||
type: LoadBalancer
|
||||
ports:
|
||||
- name: turn-tcp
|
||||
port: 1194
|
||||
protocol: TCP
|
||||
targetPort: turn-tcp
|
||||
- name: turn-udp
|
||||
port: 1194
|
||||
protocol: UDP
|
||||
targetPort: turn-udp
|
||||
selector:
|
||||
app.kubernetes.io/instance: traefik-traefik
|
||||
app.kubernetes.io/name: traefik
|
||||
EOF
|
||||
|
||||
kubectl apply -f traefik-turn-service.yaml
|
||||
|
||||
rm -f traefik-turn-service.yaml
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
echo "Don't forget to create TCP and UDP ingress routes for the TURN server with:"
|
||||
echo "kubectl apply -f k8s-manifests/galene/ingressroute-tcp.yaml"
|
||||
echo "kubectl apply -f k8s-manifests/galene/ingressroute-udp.yaml"
|
||||
echo ""
|
||||
echo "To access the dashboard:"
|
||||
echo "kubectl port-forward -n traefik \$(kubectl get pods -n traefik -l \"app.kubernetes.io/name=traefik\" -o name) 9000:9000"
|
||||
echo "Then visit http://localhost:9000/dashboard/ in your browser"
|
||||
30
scripts/redis_secret.sh
Executable file
30
scripts/redis_secret.sh
Executable file
|
|
@ -0,0 +1,30 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
NAMESPACE=redis
|
||||
REDIS_SECRET=redis-auth
|
||||
|
||||
generate_password() {
|
||||
pwgen 32 1
|
||||
}
|
||||
|
||||
REDIS_PASSWORD=$(generate_password)
|
||||
|
||||
NAMESPACE_EXISTS=$(kubectl get namespace $NAMESPACE --ignore-not-found)
|
||||
if [ -z "$NAMESPACE_EXISTS" ]; then
|
||||
echo "Creating namespace $NAMESPACE"
|
||||
kubectl create namespace $NAMESPACE
|
||||
else
|
||||
echo "Namespace $NAMESPACE already exists"
|
||||
fi
|
||||
|
||||
REDIS_SECRET_EXISTS=$(kubectl get secret $REDIS_SECRET -n $NAMESPACE --ignore-not-found)
|
||||
if [ -z "$REDIS_SECRET_EXISTS" ]; then
|
||||
echo "Creating secret $REDIS_SECRET in namespace $NAMESPACE"
|
||||
kubectl create secret generic $REDIS_SECRET -n $NAMESPACE \
|
||||
--from-literal=password=$REDIS_PASSWORD \
|
||||
--dry-run=client -o yaml | kubectl apply -f -
|
||||
else
|
||||
echo "Secret $REDIS_SECRET already exists in namespace $NAMESPACE"
|
||||
fi
|
||||
|
||||
|
||||
63
templates/env.tmpl
Normal file
63
templates/env.tmpl
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
APP_NAME=Laravel
|
||||
APP_ENV=local
|
||||
APP_DEBUG=false
|
||||
APP_URL=http://{{- .AppURL }}
|
||||
|
||||
BEANSTALKD_API="api:8080/api/v1/job"
|
||||
BEANSTALKD_PREVIEW_PAYLOAD="PostPreviewJob-{{- .Project }}"
|
||||
BEANSTALKD_PUBLISH_PAYLOAD="PostPublishJob-{{- .Project }}"
|
||||
|
||||
APP_PREVIEW_URL=https://{{- .Preview }}
|
||||
STATIC_URL=https://{{- .Static }}
|
||||
APP_USER_INTERFACE_URL={{- .UI }}
|
||||
|
||||
LOG_CHANNEL=stack
|
||||
LOG_DEPRECATIONS_CHANNEL=null
|
||||
LOG_LEVEL=debug
|
||||
|
||||
DB_CONNECTION=sqlite
|
||||
|
||||
BROADCAST_DRIVER=log
|
||||
CACHE_DRIVER=file
|
||||
FILESYSTEM_DISK=local
|
||||
QUEUE_CONNECTION=sync
|
||||
SESSION_DRIVER=database
|
||||
SESSION_LIFETIME=120
|
||||
|
||||
MEMCACHED_HOST=127.0.0.1
|
||||
|
||||
REDIS_HOST=127.0.0.1
|
||||
REDIS_PASSWORD=null
|
||||
REDIS_PORT=6379
|
||||
|
||||
MAIL_MAILER=smtp
|
||||
MAIL_HOST=mailpit
|
||||
MAIL_PORT=1025
|
||||
MAIL_USERNAME=null
|
||||
MAIL_PASSWORD=null
|
||||
MAIL_ENCRYPTION=null
|
||||
MAIL_FROM_ADDRESS="hello@example.com"
|
||||
MAIL_FROM_NAME="${APP_NAME}"
|
||||
|
||||
AWS_ACCESS_KEY_ID=
|
||||
AWS_SECRET_ACCESS_KEY=
|
||||
AWS_DEFAULT_REGION=us-east-1
|
||||
AWS_BUCKET=
|
||||
AWS_USE_PATH_STYLE_ENDPOINT=false
|
||||
|
||||
PUSHER_APP_ID=
|
||||
PUSHER_APP_KEY=
|
||||
PUSHER_APP_SECRET=
|
||||
PUSHER_HOST=
|
||||
PUSHER_PORT=443
|
||||
PUSHER_SCHEME=https
|
||||
PUSHER_APP_CLUSTER=mt1
|
||||
|
||||
VITE_APP_NAME="${APP_NAME}"
|
||||
VITE_PUSHER_APP_KEY="${PUSHER_APP_KEY}"
|
||||
VITE_PUSHER_HOST="${PUSHER_HOST}"
|
||||
VITE_PUSHER_PORT="${PUSHER_PORT}"
|
||||
VITE_PUSHER_SCHEME="${PUSHER_SCHEME}"
|
||||
VITE_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}"
|
||||
|
||||
OCTANE_SERVER=frankenphp
|
||||
30
templates/nginx_app.conf
Normal file
30
templates/nginx_app.conf
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
server {
|
||||
listen {{ .Port }};
|
||||
|
||||
# Redirect all HTTP requests to HTTPS
|
||||
# return 301 https://$host$request_uri;
|
||||
|
||||
index index.php index.html;
|
||||
error_log /var/log/nginx/error.log;
|
||||
access_log /var/log/nginx/access.log;
|
||||
root /var/www/public;
|
||||
location ~ \.php$ {
|
||||
try_files $uri =404;
|
||||
fastcgi_split_path_info ^(.+\.php)(/.+)$;
|
||||
fastcgi_pass app:9000;
|
||||
fastcgi_index index.php;
|
||||
include fastcgi_params;
|
||||
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
|
||||
fastcgi_param PATH_INFO $fastcgi_path_info;
|
||||
fastcgi_param HTTPS on;
|
||||
fastcgi_param HTTP_X_FORWARDED_PROTO https;
|
||||
}
|
||||
location / {
|
||||
try_files $uri $uri/ /index.php?$query_string;
|
||||
gzip_static on;
|
||||
}
|
||||
location /storage/ {
|
||||
alias /var/www/storage/app/public/;
|
||||
autoindex on;
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue