From 127a0c7d063fdeb60dc6afe66b187d074d958ed9 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Fri, 18 Sep 2026 00:57:46 +0000 Subject: [PATCH 1/9] =?UTF-8?q?docs:=20Initialize=20=F0=9F=A6=A9=20Flaming?= =?UTF-8?q?o=20Code=20Documentation=20run=20[skip=20ci]?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .flamingo-ai-technical-writer-status.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .flamingo-ai-technical-writer-status.md diff --git a/.flamingo-ai-technical-writer-status.md b/.flamingo-ai-technical-writer-status.md new file mode 100644 index 00000000..fd8ba1d5 --- /dev/null +++ b/.flamingo-ai-technical-writer-status.md @@ -0,0 +1,5 @@ +# ๐Ÿฆฉ Flamingo Code Documentation: Started + +Run ID: doc-orchestrator-1789693032407 +Status: In Progress +Started: 2026-09-18 00:57:46 UTC From abc5989fddab8f0e8f86ae135309de3a2ecf2b6d Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Fri, 18 Sep 2026 00:57:52 +0000 Subject: [PATCH 2/9] chore(docs): Clean slate - remove all documentation (28 files) [skip ci] --- docs/README.md | 53 -- docs/architecture/decisions.md | 183 ------- docs/development/.gitignore | 7 - docs/development/README.md | 104 ---- docs/development/architecture/README.md | 280 ----------- docs/development/contributing/guidelines.md | 308 ------------ docs/development/release-signing.md | 94 ---- docs/development/releasing.md | 57 --- docs/development/security/README.md | 250 --------- docs/development/setup/environment.md | 224 --------- docs/development/setup/local-development.md | 216 -------- docs/development/testing/README.md | 358 ------------- docs/diagrams/architecture/.gitignore | 8 - docs/diagrams/architecture/README.md | 18 - .../app-install-upgrade-data-flow.mmd | 28 -- .../bootstrap-sequence-diagram.mmd | 47 -- .../architecture/dependency-flowchart.mmd | 62 --- .../architecture/high-level-system-design.mmd | 73 --- docs/getting-started/.gitignore | 7 - docs/getting-started/cloud-clusters.md | 175 ------- docs/getting-started/first-steps.md | 224 --------- docs/getting-started/gke-workflow.md | 173 ------- docs/getting-started/introduction.md | 141 ------ docs/getting-started/prerequisites.md | 186 ------- docs/getting-started/quick-start.md | 204 -------- docs/reference/architecture/.gitignore | 8 - docs/reference/architecture/overview.md | 473 ------------------ docs/reference/terminal-output.md | 107 ---- 28 files changed, 4068 deletions(-) delete mode 100644 docs/README.md delete mode 100644 docs/architecture/decisions.md delete mode 100644 docs/development/.gitignore delete mode 100644 docs/development/README.md delete mode 100644 docs/development/architecture/README.md delete mode 100644 docs/development/contributing/guidelines.md delete mode 100644 docs/development/release-signing.md delete mode 100644 docs/development/releasing.md delete mode 100644 docs/development/security/README.md delete mode 100644 docs/development/setup/environment.md delete mode 100644 docs/development/setup/local-development.md delete mode 100644 docs/development/testing/README.md delete mode 100644 docs/diagrams/architecture/.gitignore delete mode 100644 docs/diagrams/architecture/README.md delete mode 100644 docs/diagrams/architecture/app-install-upgrade-data-flow.mmd delete mode 100644 docs/diagrams/architecture/bootstrap-sequence-diagram.mmd delete mode 100644 docs/diagrams/architecture/dependency-flowchart.mmd delete mode 100644 docs/diagrams/architecture/high-level-system-design.mmd delete mode 100644 docs/getting-started/.gitignore delete mode 100644 docs/getting-started/cloud-clusters.md delete mode 100644 docs/getting-started/first-steps.md delete mode 100644 docs/getting-started/gke-workflow.md delete mode 100644 docs/getting-started/introduction.md delete mode 100644 docs/getting-started/prerequisites.md delete mode 100644 docs/getting-started/quick-start.md delete mode 100644 docs/reference/architecture/.gitignore delete mode 100644 docs/reference/architecture/overview.md delete mode 100644 docs/reference/terminal-output.md diff --git a/docs/README.md b/docs/README.md deleted file mode 100644 index eef5a6d0..00000000 --- a/docs/README.md +++ /dev/null @@ -1,53 +0,0 @@ -# OpenFrame CLI Documentation - -`openframe` is an interactive command-line tool for standing up and managing OpenFrame Kubernetes environments. It provisions local k3d clusters, deploys the OpenFrame platform via an ArgoCD app-of-apps GitOps workflow, and keeps itself up to date. - -This repository (`flamingo-stack/openframe-cli`) is the CLI. The platform and application manifests it deploys live in [`flamingo-stack/openframe-oss-tenant`](https://github.com/flamingo-stack/openframe-oss-tenant). - -## Getting Started - -- [Introduction](./getting-started/introduction.md) โ€” Overview and key concepts -- [Prerequisites](./getting-started/prerequisites.md) โ€” System requirements and dependencies -- [Quick Start](./getting-started/quick-start.md) โ€” Install and bootstrap in a few minutes -- [First Steps](./getting-started/first-steps.md) โ€” Core commands and workflows -- [Cloud Clusters](./getting-started/cloud-clusters.md) โ€” Provision EKS/GKE clusters with Terraform (reference) -- [GKE Workflow](./getting-started/gke-workflow.md) โ€” Step-by-step: from zero to a running GKE cluster - -## Reference - -- [Terminal Output](./reference/terminal-output.md) โ€” Live dashboards, sequential/CI mode, `--plain`/`--silent`/`--verbose`, color and glyph controls, GitHub Actions integration - -## Commands - -- `openframe bootstrap` โ€” Create a cluster and install the platform in one step -- `openframe cluster {create,delete,list,status,cleanup}` โ€” Manage k3d and cloud (EKS/GKE) clusters -- `openframe app {install,upgrade,status,access,uninstall}` โ€” Manage the OpenFrame app-of-apps deployment (`status` also has `--watch` and `--interactive` live views) -- `openframe prerequisites {check,install}` โ€” Check and install required tools -- `openframe update` (`check`, `rollback`, `update `) โ€” Self-update the CLI -- `openframe completion` โ€” Generate shell completion scripts - -## System Requirements - -A full local platform is demanding. Recommended host: - -| Resource | Recommended | -|----------|-------------| -| RAM | 24 GB | -| CPU | 6 cores | -| Disk | 50 GB free | - -## Dependencies - -**Docker is the only tool you install and run yourself.** The CLI auto-installs pinned, verified copies of `kubectl`, `k3d`, and `helm` into `~/.openframe/bin`. `mkcert` is used to issue a locally-trusted certificate for the HTTPS ingress. See [Prerequisites](./getting-started/prerequisites.md). - -## Community and Support - -- **Slack**: [OpenMSP community](https://join.slack.com/t/openmsp/shared_invite/zt-36bl7mx0h-3~U2nFH6nqHqoTPXMaHEHA) (primary support channel) -- **Website**: [https://flamingo.run](https://flamingo.run) -- **Platform**: [https://openframe.ai](https://openframe.ai) - -We don't monitor GitHub Issues for support โ€” use Slack. - -## License - -See [LICENSE.md](../LICENSE.md). diff --git a/docs/architecture/decisions.md b/docs/architecture/decisions.md deleted file mode 100644 index 01ade042..00000000 --- a/docs/architecture/decisions.md +++ /dev/null @@ -1,183 +0,0 @@ -# Architecture Decisions - -This document records the key design decisions for the OpenFrame CLI restructure. -It is the authoritative reference for how the CLI is organized and why. - -Status: **accepted** ยท Last updated: 2026-06-24 - ---- - -## Context - -OpenFrame CLI is used by operators and semi-technical users to stand up OpenFrame -on Kubernetes. The primary supported path is **OSS** (a local cluster, no -credentials). SaaS modes come later. The CLI is being restructured into three -clearly isolated abstractions so each can be used on its own. - ---- - -## D1 โ€” Three isolated abstractions: cluster, app, prerequisites - -The CLI is organized around three independent concerns: - -- **cluster** โ€” make a Kubernetes cluster (local now; cloud later). -- **app** โ€” deploy the OpenFrame application (Helm chart โ†’ ArgoCD โ†’ apps) onto a - cluster that already exists and is online. -- **prerequisites** โ€” check and install the tools each of the above needs. - -**Rule:** the `app` subsystem must not import cluster-creation code. It only -talks to a cluster through a small Kubernetes access API (list contexts, check -health, check resources). This lets a user who already has a cluster (their own, -or one made outside OpenFrame) install the app into it, and lets a user create a -cluster without installing anything. - ---- - -## D2 โ€” OSS-tenant is the only deployment - -The CLI supports a single deployment: **oss-tenant**. The app is always installed -from the public `openframe-oss-tenant` chart repository, which requires no -credentials. There is no `--deployment-mode` flag; `--non-interactive` simply -reuses the existing `openframe-helm-values.yaml`. - -| deployment | chart repository | credentials | -|--------------|---------------------------------|-------------| -| `oss-tenant` | `openframe-oss-tenant` (public) | none | - -The cluster is always a local k3d cluster. - ---- - -## D3 โ€” Commands: `cluster` and `app` are the two primitives - -- `openframe cluster create|delete|list|status|cleanup` โ€” cluster lifecycle. - `create` **only creates the cluster**; it never installs the app. (Verb is - `create`; there is no `apply`.) `cleanup` only prunes unused container images - on the nodes; removing the platform is `app uninstall`'s job. -- `openframe app install|upgrade|status|access|uninstall` โ€” installs and operates - the OpenFrame app on an existing, online cluster. `upgrade` re-deploys the - app-of-apps at a new git ref (`--ref`) or forces an ArgoCD hard refresh + sync - (`--sync`); `access` prints the ArgoCD admin credentials and how to open the - UI. (`app` was previously named `chart`.) -- `openframe prerequisites check|install [cluster|app]` โ€” the prerequisite - checks/installs as first-class commands. -- `openframe update` โ€” self-update of the CLI binary (checksum + cosign verified, - with `check` and `rollback`); see D6-adjacent tooling in - `internal/shared/selfupdate`. - ---- - -## D4 โ€” `bootstrap` is a thin orchestrator - -`openframe bootstrap [name] [--non-interactive] [--verbose]` stays as a single, -beginner-friendly command. Internally it only orchestrates: - -```text -prerequisites โ†’ cluster create โ†’ app install -``` - -It contains no business logic of its own โ€” everything lives in the primitives. -`openframe bootstrap --non-interactive` reuses the existing `openframe-helm-values.yaml` -for the OSS tenant deployment. - ---- - -## D5 โ€” Cluster providers behind a unified interface - -Cluster creation goes through a `Provider` interface with three backends: -**k3d** (local), **EKS**, and **GKE** (cloud). Backends are selected via the -`provider.New(type)` factory, keyed on `ClusterConfig.Type`; the rest of the -CLI never knows which backend runs. Cloud providers additionally implement -`Planner` (`--dry-run` renders a real `terraform plan` footprint). - -The cloud backends share one terraform engine (D7/D8): each generates a -pinned, self-contained root module on the public `terraform-aws-modules` / -`terraform-google-modules` modules and drives `terraform` via terraform-exec. -Kubeconfig entries carry no static credentials โ€” auth runs through the -provider CLI exec plugins (`aws eks get-token`, `gke-gcloud-auth-plugin`), -with the context named after the cluster so exact-match context resolution -works unchanged. - -For OSS the default remains **local** (k3d); cloud clusters are an explicit -`--type eks|gke` opt-in with a cost warning and a typed-name confirmation on -delete. - ---- - -## D6 โ€” No dependency on the ArgoCD Go module (use the dynamic client) - -ArgoCD is **not importable as a Go library** (its `go.mod` uses a local -`replace => ./gitops-engine`), which previously pinned the entire Kubernetes -*server* tree (`k8s.io/kubernetes`) into this CLI. - -The CLI reads ArgoCD `Application` resources through the Kubernetes **dynamic -client** (unstructured, GVR `argoproj.io/v1alpha1 applications`) instead of the -typed argo-cd clientset. Benefits: - -- **version-agnostic** โ€” compatible with whatever ArgoCD version is deployed, - including the latest; -- removes the largest supply-chain dependency; -- unblocks keeping `k8s.io/*` on the latest stable release. - ---- - -## D7 โ€” Terraform (BUSL) as the provisioning engine, installed verified - -Cloud clusters are provisioned with **HashiCorp Terraform**, not OpenTofu. -BUSL 1.1 only restricts "hosted or embedded" offerings **competitive with -HashiCorp's products**; this CLI uses terraform as an internal tool to -provision the user's own infrastructure, which is not a competitive offering. -The binary is installed like every other prerequisite: a pinned version with -SHA256 verification into `~/.openframe/bin` (no curl-pipe-bash, no sudo). An -already-installed `terraform` on PATH in `~/.openframe/bin` is preferred. - -If a server-side scenario ever provisions clusters *as a service* with -terraform, that is a different BUSL use profile and needs its own review. - -## D8 โ€” Local terraform state in per-cluster workspaces - -Each cloud cluster owns a workspace under `~/.openframe/clusters//`: -the generated root module, `terraform.tfvars.json`, local state, a -`terraform.log` every apply/destroy appends its output stream to, and a -`cluster.json` registry record (type, status, endpoint/CA). The registry is -what makes cloud clusters visible to `list`/`status`/`delete` without cloud -API calls, and the state file is the only pointer to billed resources โ€” so a -workspace is **never deleted on a failed apply**, only after a successful -destroy. Re-running `create` resumes an interrupted apply. - -Remote state is opt-in via `--backend-config s3://bucket/prefix` (EKS) or -`gcs://bucket/prefix` (GKE) for users who need the state to survive the -machine that created the cluster. - -## Platform support - -- **macOS / Linux** โ€” full support; prerequisites are checked and auto-installed. -- **Windows** โ€” prerequisites are not auto-installed; the CLI prints a link to - the documentation describing what to install and how (WSL2, Docker, etc.). - -The primary audience is non-technical and semi-technical users, so every -interactive flow uses plain-language prompts, safe defaults, and confirmations -rather than raw errors. - ---- - -## Target layout - -```text -cmd/ - cluster/ create, delete, list, status, cleanup - app/ install, upgrade, status, access, uninstall - prerequisites/ check, install - bootstrap/ orchestrator (prerequisites โ†’ cluster create โ†’ app install) - update/ self-update: (update), check, rollback -internal/ - cluster/provider/ Provider interface + Target(local|cloud) + k3d impl - cluster/ cluster lifecycle (service + k3d provider) - chart/ helm/argocd/git providers + app-of-apps install - k8s/ cluster-access API: contexts, rest.Config, health, resources - prerequisites/ OS-aware checker/installer framework - platform/ OS detection + Windows/WSL2 doc hints - shared/ executor, errors, ui, redact, files, config, flags, - download (pinned tools), selfupdate, wsllauncher -docs/ all documentation -``` diff --git a/docs/development/.gitignore b/docs/development/.gitignore deleted file mode 100644 index a5d01be5..00000000 --- a/docs/development/.gitignore +++ /dev/null @@ -1,7 +0,0 @@ -# VoltAgent temp files -temp/ - -# JSON intermediate files (except schema/config) -*.json -!*-schema.json -!*-config.json diff --git a/docs/development/README.md b/docs/development/README.md deleted file mode 100644 index 40286b7c..00000000 --- a/docs/development/README.md +++ /dev/null @@ -1,104 +0,0 @@ -# Development Documentation - -Welcome to the OpenFrame CLI development documentation. This section covers everything you need to contribute to, extend, and understand the internals of the `openframe` CLI. - -OpenFrame CLI is written in **Go** and uses [Cobra](https://github.com/spf13/cobra) for command-line parsing. It orchestrates K3D clusters, ArgoCD GitOps deployments, and Helm chart management through a layered service/provider architecture. - ---- - -## Documentation Index - -| Document | Description | -|---|---| -| [Environment Setup](setup/environment.md) | IDE configuration, Go toolchain, editor extensions | -| [Local Development](setup/local-development.md) | Clone, build, run, and debug the CLI locally | -| [Architecture Overview](architecture/README.md) | High-level design, component breakdown, data flows | -| [Security Guidelines](security/README.md) | Auth patterns, secret handling, vulnerability mitigations | -| [Testing Guide](testing/README.md) | Unit tests, integration tests, test utilities | -| [Contributing Guidelines](contributing/guidelines.md) | Code style, PR process, commit messages | - ---- - -## Quick Navigation - -### I want to... - -**Build and run the CLI locally** -โ†’ See [Local Development](setup/local-development.md) - -**Understand how the codebase is structured** -โ†’ See [Architecture Overview](architecture/README.md) - -**Add a new command or feature** -โ†’ Start with [Architecture Overview](architecture/README.md), then [Contributing Guidelines](contributing/guidelines.md) - -**Write or run tests** -โ†’ See [Testing Guide](testing/README.md) - -**Handle secrets or security concerns** -โ†’ See [Security Guidelines](security/README.md) - -**Set up my development environment** -โ†’ See [Environment Setup](setup/environment.md) - ---- - -## Repository Structure - -```text -openframe-cli/ -โ”œโ”€โ”€ cmd/ # Cobra command definitions (entry points) -โ”‚ โ”œโ”€โ”€ root.go # Root command, wires all subcommands -โ”‚ โ”œโ”€โ”€ bootstrap/ # openframe bootstrap -โ”‚ โ”œโ”€โ”€ cluster/ # openframe cluster (create/delete/list/status/cleanup) -โ”‚ โ”œโ”€โ”€ app/ # openframe app (install/upgrade/status/access/uninstall) -โ”‚ โ”œโ”€โ”€ prerequisites/ # openframe prerequisites (check/install) -โ”‚ โ””โ”€โ”€ update/ # openframe update (self-update/rollback) -โ”œโ”€โ”€ internal/ # All internal business logic -โ”‚ โ”œโ”€โ”€ bootstrap/ # Bootstrap service (cluster + chart orchestration) -โ”‚ โ”œโ”€โ”€ cluster/ # Cluster service + K3D provider -โ”‚ โ”œโ”€โ”€ chart/ # Chart services, ArgoCD/Helm/Git providers -โ”‚ โ”œโ”€โ”€ app/ # App status and uninstall services -โ”‚ โ”œโ”€โ”€ k8s/ # Kubernetes client utilities -โ”‚ โ”œโ”€โ”€ platform/ # OS detection and platform hints -โ”‚ โ”œโ”€โ”€ prerequisites/ # Prerequisite framework -โ”‚ โ””โ”€โ”€ shared/ # Cross-cutting: executor, UI, errors, config, selfupdate -โ”œโ”€โ”€ tests/ -โ”‚ โ”œโ”€โ”€ integration/ # Integration tests (requires running cluster) -โ”‚ โ””โ”€โ”€ testutil/ # Shared test utilities and patterns -โ”œโ”€โ”€ scripts/ -โ”‚ โ””โ”€โ”€ sign-binary.sh # Binary signing helper -โ””โ”€โ”€ main.go # Entry point -``` - ---- - -## Tech Stack - -| Technology | Role | -|---|---| -| **Go** | Primary language | -| **Cobra** | CLI framework (command/flag parsing) | -| **K3D** | Local Kubernetes cluster provider | -| **ArgoCD** | GitOps deployment engine (via client-go dynamic client) | -| **Helm** | Kubernetes package manager (CLI wrapper) | -| **go-git** | Git operations (no `git` binary dependency) | -| **client-go** | Kubernetes API client | -| **pterm** | Terminal UI rendering (spinners, prompts, colors) | -| **Sigstore/cosign** | Binary signature verification for self-updates | - ---- - -## External Dependencies - -The OpenFrame platform chart lives in a separate repository: - -- **openframe-oss-tenant:** [https://github.com/flamingo-stack/openframe-oss-tenant](https://github.com/flamingo-stack/openframe-oss-tenant) -- Documentation: [https://github.com/flamingo-stack/openframe-oss-tenant/tree/main/docs](https://github.com/flamingo-stack/openframe-oss-tenant/tree/main/docs) - ---- - -## Getting Help - -- **OpenMSP Slack:** [https://www.openmsp.ai/](https://www.openmsp.ai/) -- **CLI Source:** [https://github.com/flamingo-stack/openframe-cli](https://github.com/flamingo-stack/openframe-cli) diff --git a/docs/development/architecture/README.md b/docs/development/architecture/README.md deleted file mode 100644 index 24e3aaba..00000000 --- a/docs/development/architecture/README.md +++ /dev/null @@ -1,280 +0,0 @@ -# Architecture Overview - -OpenFrame CLI is a Go-based command-line tool with a layered architecture that cleanly separates command definitions, business logic, provider integrations, and shared infrastructure. - -For the full generated reference, see the [architecture reference documentation](../../reference/architecture/overview.md). - ---- - -## High-Level Design - -```mermaid -graph TB - subgraph Entry["Entry Point"] - main["main.go"] - root["cmd/root.go (Cobra)"] - end - - subgraph Commands["Command Layer (cmd/)"] - bootstrap["bootstrap"] - cluster["cluster/*"] - app["app/*"] - prereq["prerequisites"] - update["update"] - end - - subgraph Services["Service Layer (internal/)"] - bsvc["bootstrap.Service"] - csvc["cluster.ClusterService"] - chsvc["chart/services.ChartService"] - appsvc["app/status + uninstall"] - prefw["prerequisites.Runner"] - supdater["selfupdate.Updater"] - end - - subgraph Providers["Provider Layer"] - k3dp["K3D Provider"] - argop["ArgoCD Manager"] - helmp["Helm Manager"] - gitp["Git Repository"] - end - - subgraph Shared["Shared Infrastructure"] - exec["executor.CommandExecutor"] - k8spkg["k8s (rest.Config, Accessor)"] - uipkg["shared/ui (pterm)"] - errpkg["shared/errors"] - redact["shared/redact"] - dl["download.Downloader"] - end - - main --> root - root --> Commands - bootstrap --> bsvc - cluster --> csvc - app --> chsvc - app --> appsvc - prereq --> prefw - update --> supdater - - bsvc --> csvc - bsvc --> chsvc - csvc --> k3dp - chsvc --> argop - chsvc --> helmp - chsvc --> gitp - appsvc --> argop - - k3dp --> exec - helmp --> exec - argop --> k8spkg - helmp --> k8spkg - prefw --> dl - supdater --> dl - exec --> redact -``` - ---- - -## Core Components - -| Package | Path | Responsibility | -|---|---|---| -| **Root Command** | `cmd/root.go` | Cobra root; wires subcommands, global flags (`--verbose`, `--silent`), version info, WSL launcher | -| **Bootstrap Command** | `cmd/bootstrap/` | Orchestrates `cluster create` + `app install` as a single user-facing workflow | -| **Cluster Commands** | `cmd/cluster/` | Cobra subcommands: create, delete, list, status, cleanup | -| **App Commands** | `cmd/app/` | Cobra subcommands: install, upgrade, status, access, uninstall | -| **Prerequisites Command** | `cmd/prerequisites/` | Exposes `check` / `install` for Docker, k3d, Helm | -| **Update Command** | `cmd/update/` | Self-update, rollback, update-check with cosign signature verification | -| **Bootstrap Service** | `internal/bootstrap/` | Coordinates cluster creation then chart installation end-to-end | -| **Cluster Service** | `internal/cluster/service.go` | Lifecycle operations (create, delete, list, status, cleanup) via the provider interface | -| **K3D Provider** | `internal/cluster/providers/k3d/` | K3D-specific cluster creation and management | -| **Cluster Provider Interface** | `internal/cluster/provider/` | Unified `Provider` interface; K3D satisfies it today | -| **Chart Services** | `internal/chart/services/` | High-level install workflow: prerequisites โ†’ ArgoCD โ†’ app-of-apps โ†’ wait | -| **ArgoCD Provider** | `internal/chart/providers/argocd/` | Install, wait, refresh/sync, application management via native client-go dynamic client | -| **Helm Provider** | `internal/chart/providers/helm/` | Helm CLI wrapper; ArgoCD and app-of-apps installation | -| **Git Provider** | `internal/chart/providers/git/` | Shallow clone of chart repository using go-git (no `git` binary) | -| **App Status Service** | `internal/app/status/` | Aggregates cluster health + ArgoCD app status into a unified Report | -| **App Uninstall Service** | `internal/app/uninstall/` | Removes ArgoCD applications and Helm releases safely | -| **k8s Package** | `internal/k8s/` | Kubeconfig context loading, `rest.Config` construction, cluster health/resource checks | -| **Prerequisites Framework** | `internal/prerequisites/` | OS-aware check + auto-install runner (macOS/Linux auto-installs, Windows shows docs) | -| **Executor** | `internal/shared/executor/` | Command execution abstraction (real + mock); records argv for security testing | -| **Self-Update** | `internal/shared/selfupdate/` | GitHub release fetch, cosign signature verification, binary swap, rollback | -| **Download** | `internal/shared/download/` | Verified binary downloads (SHA256 + pinned versions) for k3d, mkcert, Helm | -| **Redact** | `internal/shared/redact/` | Secret redaction from log/debug output | -| **WSL Launcher** | `internal/shared/wsllauncher/` | Re-runs the CLI inside WSL2 on Windows; auto-installs the Linux binary | -| **Platform** | `internal/platform/` | Host OS detection, per-tool install hints, WSL guidance errors | -| **Shared UI** | `internal/shared/ui/` | Logo, prompts, silent mode, status colors, selection menus (pterm) | -| **Shared Config** | `internal/shared/config/` | `EnvBool`, TLS config for local clusters, system service | -| **Shared Errors** | `internal/shared/errors/` | Error types, friendly hints, retry policies, `AlreadyHandledError` sentinel | - ---- - -## Data Flow: Bootstrap Sequence - -The `openframe bootstrap` command is the primary user workflow. This sequence diagram shows all the moving parts: - -```mermaid -sequenceDiagram - participant User - participant CLI as "openframe bootstrap" - participant BSvc as "bootstrap.Service" - participant CSvc as "cluster.Service" - participant K3D as "K3D Provider" - participant ChSvc as "chart/services" - participant Helm as "HelmManager" - participant Git as "git.Repository" - participant ArgoCD as "argocd.Manager" - participant K8s as "Kubernetes API" - - User->>CLI: openframe bootstrap [name] - CLI->>BSvc: Execute(cmd, args) - BSvc->>ChSvc: ValidateHelmValuesFile() - ChSvc-->>BSvc: OK - - BSvc->>CSvc: CreateClusterWithPrerequisites(ctx, name) - CSvc->>K3D: CreateCluster(ctx, config) - K3D-->>CSvc: rest.Config - CSvc-->>BSvc: rest.Config - - BSvc->>ChSvc: InstallChartsWithConfigContext(ctx, req) - ChSvc->>Helm: InstallArgoCDWithProgress(ctx, cfg) - Helm->>K8s: helm upgrade --install argo-cd - K8s-->>Helm: OK - - ChSvc->>Git: CloneChartRepository(ctx, appConfig) - Git-->>ChSvc: CloneResult{tempDir, chartPath} - - ChSvc->>Helm: InstallAppOfAppsFromLocal(ctx, cfg) - Helm->>K8s: helm upgrade --install app-of-apps - K8s-->>Helm: OK - - ChSvc->>ArgoCD: WaitForApplications(ctx, cfg) - loop Every 2s until ready or timeout - ArgoCD->>K8s: List Applications - K8s-->>ArgoCD: Application list - ArgoCD->>ArgoCD: assessApplications() - end - ArgoCD-->>ChSvc: All Healthy+Synced - - ChSvc-->>BSvc: OK - BSvc-->>User: Bootstrap complete -``` - ---- - -## Data Flow: App Install / Upgrade - -```mermaid -sequenceDiagram - participant User - participant AppCmd as "cmd/app/install" - participant Target as "app/target.Selector" - participant K8sPkg as "k8s package" - participant ChSvc as "chart/services" - participant ArgoProv as "argocd.Manager" - participant HelmProv as "helm.HelmManager" - - User->>AppCmd: openframe app install - AppCmd->>Target: Select(ctx) - Target->>K8sPkg: LoadContexts(kubeconfigPath) - K8sPkg-->>Target: ContextInfo list - Target->>User: Prompt: select context - User-->>Target: k3d-openframe-dev - Target->>K8sPkg: CheckResources(ctx, requirements) - K8sPkg-->>Target: Resources sufficient - Target-->>AppCmd: SelectResult{Config, Context} - - AppCmd->>ChSvc: InstallChartsWithConfigContext(ctx, req) - ChSvc->>ArgoProv: Install ArgoCD - ArgoProv-->>ChSvc: ArgoCD installed - ChSvc->>HelmProv: InstallAppOfAppsFromLocal(ctx, cfg) - HelmProv-->>ChSvc: app-of-apps installed - ChSvc->>ArgoProv: WaitForApplications(ctx, cfg) - ArgoProv-->>ChSvc: All apps Healthy+Synced - ChSvc-->>AppCmd: OK - AppCmd-->>User: SUCCESS -``` - ---- - -## Key Design Decisions - -### 1. Provider Interface Pattern - -The `cluster.Provider` interface allows the CLI to support multiple cluster backends (K3D today, potentially Kind or cloud providers in the future): - -```go -// internal/cluster/provider/provider.go -type Provider interface { - CreateCluster(ctx context.Context, cfg models.ClusterConfig) (*rest.Config, error) - DeleteCluster(ctx context.Context, name string, clusterType models.ClusterType, force bool) error - ListClusters(ctx context.Context) ([]models.ClusterInfo, error) - GetClusterStatus(ctx context.Context, name string) (*models.ClusterStatus, error) -} -``` - -### 2. CommandExecutor Abstraction - -All external binary invocations (k3d, helm) go through the `CommandExecutor` interface, enabling complete mock substitution in unit tests: - -```go -// Real execution -exec := executor.NewRealCommandExecutor(false, true) -result, err := exec.Execute(ctx, "k3d", "cluster", "list") - -// Test mock -mock := executor.MockCommandExecutor{} -mock.SetResponse("k3d cluster list", &executor.CommandResult{Stdout: `[]`}) -``` - -### 3. GitOps via ArgoCD App-of-Apps - -Platform deployment uses the ArgoCD [App of Apps pattern](https://argo-cd.readthedocs.io/en/stable/operator-manual/cluster-bootstrapping/). The CLI installs a single "app-of-apps" Helm chart that ArgoCD then uses to deploy and manage all child applications from the `openframe-oss-tenant` repository. - -### 4. Secret Redaction at the Executor Layer - -All output from external commands passes through `redact.Redact()` before being displayed or logged. Secrets registered via `redact.RegisterSecret()` and URL-embedded credentials are automatically scrubbed with `***`. - -### 5. AlreadyHandledError Sentinel - -To avoid double-printing errors, the `AlreadyHandledError` sentinel is used throughout the codebase. When a command has already displayed its error to the user, it wraps the error as `AlreadyHandledError` โ€” the main entry point then silently exits with the appropriate code. - -### 6. Interactive + Non-Interactive Modes - -Every wizard checks `ui.IsNonInteractive()` before prompting. Non-interactive mode is triggered by `--non-interactive`, piped stdin, or `--output json/yaml`. This makes every command safe for CI/CD pipelines without special handling. - ---- - -## Configuration File: openframe-helm-values.yaml - -The bootstrap wizard generates a `openframe-helm-values.yaml` configuration file. Before any cluster creation, the CLI validates this file via a "preflight" check โ€” the cheapest possible gate to catch errors before expensive cluster operations begin. - -```mermaid -graph LR - A["User runs bootstrap"] --> B["Validate openframe-helm-values.yaml"] - B --> C{"Valid?"} - C -->|Yes| D["Create K3D cluster"] - C -->|No| E["Error: fix your values file"] - D --> F["Install ArgoCD"] - F --> G["Deploy app-of-apps"] - G --> H["Wait for healthy"] -``` - ---- - -## Upgrade Modes - -The `openframe app upgrade` command has two distinct modes: - -| Mode | Flag | Description | -|---|---|---| -| **Change-Ref (Mode 1)** | `--ref ` | Updates the git ref in ArgoCD, triggers re-sync to new version | -| **Force-Sync (Mode 2)** | `--force-sync` | Forces ArgoCD to re-sync the current ref without changing the version | - ---- - -## Further Reading - -- [Reference Architecture Documentation](../../reference/architecture/overview.md) โ€” Full generated documentation with all component details -- [openframe-oss-tenant](https://github.com/flamingo-stack/openframe-oss-tenant) โ€” The external OpenFrame platform chart repository diff --git a/docs/development/contributing/guidelines.md b/docs/development/contributing/guidelines.md deleted file mode 100644 index d127fd96..00000000 --- a/docs/development/contributing/guidelines.md +++ /dev/null @@ -1,308 +0,0 @@ -# Contributing Guidelines - -Thank you for contributing to OpenFrame CLI! This guide covers everything you need to know to submit high-quality contributions. - ---- - -## Before You Start - -- Join the [OpenMSP Slack community](https://www.openmsp.ai/) to discuss your ideas before starting large features -- Read the [Architecture Overview](../architecture/README.md) to understand the codebase -- Set up your [development environment](../setup/environment.md) and verify you can [build and run locally](../setup/local-development.md) - ---- - -## Code Style and Conventions - -### Go Style - -OpenFrame CLI follows standard Go conventions: - -- **`gofmt` / `goimports`** formatting is required โ€” no unformatted code will be merged -- **`go vet`** must pass with no warnings -- Follow [Effective Go](https://go.dev/doc/effective_go) and the [Go Code Review Comments](https://github.com/golang/go/wiki/CodeReviewComments) - -```bash -# Format and organize imports -goimports -w . - -# Run vet -go vet ./... - -# Run linter (if golangci-lint is configured) -golangci-lint run -``` - -### Naming Conventions - -| Element | Convention | Example | -|---|---|---| -| Package names | Lowercase, single word | `cluster`, `executor`, `redact` | -| Exported types | PascalCase | `ClusterService`, `CommandExecutor` | -| Unexported types | camelCase | `clusterManager`, `mockExecutor` | -| Constants | PascalCase (exported), camelCase (unexported) | `DefaultClusterName`, `maxRetries` | -| Test files | `_test.go` suffix | `service_test.go` | -| Test functions | `Test` prefix + PascalCase | `TestCreateClusterSuccess` | - -### Error Handling - -- Wrap errors with context using `fmt.Errorf("context: %w", err)` -- Use `shared/errors` types for structured errors (`CommandError`, `AlreadyHandledError`) -- Use `friendlyHint` patterns for user-facing errors -- Never swallow errors silently โ€” return them or log them - -```go -// GOOD: Wrap with context -if err := mgr.CreateCluster(ctx, cfg); err != nil { - return fmt.Errorf("creating cluster %q: %w", cfg.Name, err) -} - -// BAD: Lost context -if err := mgr.CreateCluster(ctx, cfg); err != nil { - return err -} -``` - -### Command Structure - -When adding a new Cobra command, follow the established pattern: - -```go -func getMyCmd() *cobra.Command { - cmd := &cobra.Command{ - Use: "mycommand [name]", - Short: "One-line description", - Long: `Multi-line detailed description. - -The long description should explain what the command does, -when to use it, and any important caveats.`, - Args: cobra.MaximumNArgs(1), - RunE: func(cmd *cobra.Command, args []string) error { - // Validate input - // Delegate to service layer - // Handle errors via sharedErrors.HandleGlobalError - return nil - }, - } - - // Add flags - cmd.Flags().StringVar(&flagVar, "flag-name", "default", "Flag description") - - return cmd -} -``` - -### Service Layer Conventions - -- Services must accept interfaces (not concrete types) for all dependencies -- Always accept `context.Context` as the first argument for cancellable operations -- Return descriptive errors, not boolean success flags -- Use the `CommandExecutor` interface for all external binary invocations โ€” never `os/exec` directly - ---- - -## Branch Naming - -| Type | Pattern | Example | -|---|---|---| -| Feature | `feature/` | `feature/add-kind-provider` | -| Bug fix | `fix/` | `fix/cluster-delete-timeout` | -| Documentation | `docs/` | `docs/update-contributing-guide` | -| Refactor | `refactor/` | `refactor/extract-helm-manager` | -| Test | `test/` | `test/add-bootstrap-integration` | -| Chore | `chore/` | `chore/update-go-dependencies` | - -**Rules:** -- Use lowercase and hyphens only (no underscores, no uppercase) -- Keep descriptions short and meaningful -- Branch from `main` unless working on a specific release branch - ---- - -## Commit Message Format - -OpenFrame CLI uses [Conventional Commits](https://www.conventionalcommits.org/): - -```text -(): - -[optional body] - -[optional footer(s)] -``` - -### Types - -| Type | When to Use | -|---|---| -| `feat` | A new feature | -| `fix` | A bug fix | -| `docs` | Documentation changes only | -| `refactor` | Code change that neither fixes a bug nor adds a feature | -| `test` | Adding or modifying tests | -| `chore` | Build process, dependency updates, tooling | -| `perf` | Performance improvements | -| `ci` | CI/CD configuration changes | - -### Scopes (Optional but Recommended) - -| Scope | Area | -|---|---| -| `cluster` | Cluster commands and services | -| `app` | App commands and chart services | -| `bootstrap` | Bootstrap command and service | -| `prereq` | Prerequisites system | -| `update` | Self-update mechanism | -| `executor` | Command executor | -| `k8s` | Kubernetes client package | -| `argocd` | ArgoCD provider | -| `helm` | Helm provider | -| `ui` | Terminal UI and wizards | -| `errors` | Error handling | -| `redact` | Secret redaction | - -### Examples - -```text -feat(cluster): add --wait flag to cluster create command - -Adds a --wait flag that blocks until all cluster nodes are Ready. -Useful for CI pipelines that need the cluster immediately after creation. - -fix(argocd): handle stalled sync after ref change - -When ArgoCD silently fails to adopt a new ref (ArgoCD v3 regression), -the stall detector now surfaces a diagnostic message after 90s. - -docs(contributing): add commit message guidelines - -test(bootstrap): add integration test for non-interactive mode - -chore: upgrade go-git to v5.12.0 -``` - ---- - -## Pull Request Process - -### Before Opening a PR - -```bash -# 1. Ensure all tests pass -go test -race ./... - -# 2. Format code -goimports -w . - -# 3. Run vet -go vet ./... - -# 4. Build successfully -go build -o openframe . - -# 5. Test your changes manually -./openframe --help -``` - -### PR Description Template - -```text -## Summary - - -## Changes - -- -- - -## Testing - -- [ ] Unit tests added/updated -- [ ] Integration tests added/updated (if applicable) -- [ ] Manual testing performed - -## Checklist -- [ ] Code follows the style guidelines -- [ ] Self-review completed -- [ ] Tests pass (`go test -race ./...`) -- [ ] `go vet ./...` passes -- [ ] `goimports` formatting applied -- [ ] No secrets or credentials in code -- [ ] Security guidelines followed (secrets registered with redact, no shell injection) -``` - -### PR Size Guidelines - -| Size | Lines Changed | Guidance | -|---|---|---| -| Small | < 100 lines | Preferred โ€” fast review | -| Medium | 100โ€“500 lines | Include detailed description | -| Large | 500+ lines | Split into smaller PRs if possible | - ---- - -## Review Checklist - -When reviewing a PR, check: - -**Correctness:** -- [ ] Logic is correct and handles edge cases -- [ ] Error paths are handled and tested -- [ ] Context cancellation is propagated correctly - -**Security:** -- [ ] No secrets hardcoded or logged -- [ ] New credentials registered with `redact.RegisterSecret()` -- [ ] User input validated before reaching shell-outs -- [ ] External commands use argv arrays, not shell strings - -**Architecture:** -- [ ] Business logic is in the service layer, not command layer -- [ ] Dependencies are injected via interfaces (testable) -- [ ] New command follows the established Cobra pattern - -**Tests:** -- [ ] Unit tests cover new code paths -- [ ] Error cases are tested -- [ ] Mock executor used instead of real subprocess calls in unit tests - -**Documentation:** -- [ ] Public API has Go doc comments -- [ ] Complex logic has inline comments -- [ ] `--help` text is accurate and helpful - ---- - -## Adding a New Command - -Follow these steps when adding a new CLI command: - -1. **Create the command file** in `cmd//.go` -2. **Define a `getCmd()` function** returning `*cobra.Command` -3. **Register it** in the parent command group (e.g., `cmd/cluster/cluster.go`) -4. **Create a service** in `internal//` with injected dependencies -5. **Write unit tests** using `testutil.TestClusterCommand` -6. **Add integration tests** if the command interacts with external systems -7. **Verify `--help` output** is accurate and descriptive - ---- - -## Adding a New Provider - -To add a new cluster provider (e.g., Kind): - -1. **Implement the `Provider` interface** in `internal/cluster/providers//manager.go` -2. **Add prerequisite definitions** in `internal/cluster/prerequisites/` -3. **Register the provider** in the cluster service provider resolution -4. **Add the cluster type** to `internal/cluster/models/cluster.go` -5. **Write unit and integration tests** - ---- - -## Community - -All contribution discussions happen in the [OpenMSP Slack](https://www.openmsp.ai/). There are no GitHub Issues or Discussions for this project โ€” bring your questions, feature ideas, and bug reports to Slack. - -- **Slack invite:** [https://join.slack.com/t/openmsp/shared_invite/zt-36bl7mx0h-3~U2nFH6nqHqoTPXMaHEHA](https://join.slack.com/t/openmsp/shared_invite/zt-36bl7mx0h-3~U2nFH6nqHqoTPXMaHEHA) -- **OpenFrame platform repo:** [https://github.com/flamingo-stack/openframe-oss-tenant](https://github.com/flamingo-stack/openframe-oss-tenant) -- **Releases:** [https://github.com/flamingo-stack/openframe-cli/releases](https://github.com/flamingo-stack/openframe-cli/releases) diff --git a/docs/development/release-signing.md b/docs/development/release-signing.md deleted file mode 100644 index 6cd39990..00000000 --- a/docs/development/release-signing.md +++ /dev/null @@ -1,94 +0,0 @@ -# Release Signing - -Release binaries are code-signed during `goreleaser release` (see -[release.yml](../../.github/workflows/release.yml)), **before archiving** โ€” so -the published archives, `checksums.txt`, and the cosign bundle all cover the -signed binaries. - -| Platform | Mechanism | -|----------|-----------| -| macOS | `codesign` (Developer ID Application, hardened runtime, timestamp) + `notarytool` notarization | -| Windows | Authenticode via Azure Trusted Signing (SHA-256, RFC3161 timestamp from `timestamp.acs.microsoft.com`) | -| Linux | Unsigned; integrity via `checksums.txt` + cosign bundle | - -The flow mirrors the `sign-macos-package` / `sign-windows-package` composite -steps in [openframe-oss-tenant](https://github.com/flamingo-stack/openframe-oss-tenant) -and uses the same certificates and secrets. One deviation: the release job runs -on a single macOS runner (signing must happen before GoReleaser packs the -archives), so Windows signing uses [jsign](https://ebourg.github.io/jsign/) -instead of `azure/trusted-signing-action` (signtool is Windows-only) โ€” same -Azure Trusted Signing account, endpoint and certificate profile. - -## How it's wired - -- A GoReleaser build post-hook calls `scripts/sign-binary.sh ` - for every built binary. `linux` is a pass-through. -- The script is a no-op unless `OPENFRAME_SIGN=1`, which only the release - workflow sets โ€” local builds and CI compile checks never attempt to sign. -- [.github/steps/setup-macos-signing](../../.github/steps/setup-macos-signing/action.yml) - imports the Developer ID certificate into a throwaway keychain and exports - `KEYCHAIN_PATH` / `SIGNING_IDENTITY`; the keychain is deleted in an - `if: always()` cleanup step. -- The workflow downloads a version-pinned, checksum-verified jsign jar and - exports `JSIGN_JAR`. The script fetches a fresh AAD client-credentials token - per Windows binary (notarization waits can outlive a token fetched up-front). - -## Required secrets - -Same names as `openframe-oss-tenant`, so org-level secrets cover both repos. - -| Secret | Used for | -|--------|----------| -| `APPLE_CERTIFICATE_P12` | Base64-encoded Developer ID Application certificate (.p12) | -| `APPLE_CERTIFICATE_PASSWORD` | Password for the .p12 | -| `APPLE_ID_USERNAME` / `APPLE_ID_PASSWORD` | Notarization (app-specific password) | -| `APPLE_TEAM_ID` | Apple Developer Team ID | -| `AZURE_TENANT_ID` / `AZURE_CLIENT_ID` / `AZURE_CLIENT_SECRET` | AAD token for Trusted Signing | -| `AZURE_SIGNING_ENDPOINT` | e.g. `https://eus.codesigning.azure.net` | -| `AZURE_CODE_SIGNING_ACCOUNT_NAME` | Trusted Signing account | -| `AZURE_CERTIFICATE_PROFILE_NAME` | Certificate profile | - -## Testing - -Two layers, neither needing certificates locally: - -- **Unit tests** โ€” `tests/scripts/sign_binary_test.go` (part of - `make test-unit`) runs `scripts/sign-binary.sh` with PATH stubs for - `codesign`/`xcrun`/`java`/`curl`/`jq` that record their argv. They pin the - `OPENFRAME_SIGN` gate, the per-OS dispatch, fail-fast on missing env, and the - exact flags passed to codesign/notarytool/jsign (identity, hardened runtime, - endpoint scheme-stripping, alias, timestamp URL, call ordering). -- **Post-publish verification** โ€” the `verify-windows-signature` / - `verify-macos-signature` jobs in the release workflow download the published - assets on real Windows/macOS runners and verify them against the OS trust - stores (`Get-AuthenticodeSignature` incl. timestamp; `codesign --verify` + - Developer ID authority check, best-effort `spctl` notarization assessment). - Both checks pin the signer identity โ€” Authenticode subject - `Flamingo AI, Inc.`, Apple `TeamIdentifier=F7LDSU8JPJ` โ€” so a binary signed - by *some* trusted-but-wrong publisher still fails. On verification failure - the `cleanup-on-failed-verification` job yanks the release and tag. - -## Verifying a released binary - -macOS: - -```bash -codesign --verify --strict --verbose=2 openframe -spctl --assess --type open --context context:primary-signature -v openframe -``` - -Windows (PowerShell): - -```powershell -Get-AuthenticodeSignature .\openframe.exe -``` - -Any platform โ€” release provenance (covers Linux too), see the `signs` block in -[.goreleaser.yml](../../.goreleaser.yml): - -```bash -cosign verify-blob --bundle checksums.txt.bundle \ - --certificate-oidc-issuer https://token.actions.githubusercontent.com \ - --certificate-identity-regexp '^https://github.com/flamingo-stack/openframe-cli/\.github/workflows/release\.yml@.*$' \ - checksums.txt -``` diff --git a/docs/development/releasing.md b/docs/development/releasing.md deleted file mode 100644 index a84e1c9c..00000000 --- a/docs/development/releasing.md +++ /dev/null @@ -1,57 +0,0 @@ -# Releasing - -Versioning is fully automated by -[semantic-release](https://semantic-release.gitbook.io/) โ€” there is no manual -version input anywhere. The version is computed from the **Conventional -Commits** on `main` since the last release tag: - -| Commit subject | Effect | -|---|---| -| `fix: โ€ฆ`, `fix(scope): โ€ฆ` | patch bump | -| `feat: โ€ฆ` | minor bump | -| `feat!: โ€ฆ`, or a `BREAKING CHANGE:` footer | major bump | -| `chore:`, `docs:`, `refactor:`, `ci:`, `test:`, free-form | no release | - -This repo squash-merges PRs, so the analyzed commit subjects are **PR titles**. -[pr-title.yml](../../.github/workflows/pr-title.yml) lints every PR title into -the conventional format โ€” a free-form title would otherwise silently contribute -nothing to any release. - -## Flow - -Releases are **manual**: merging to `main` never ships anything by itself. -When you decide to ship, dispatch the workflow from `main`: - -```bash -gh workflow run release.yml --ref main -``` - -1. [release.yml](../../.github/workflows/release.yml) then runs: - - **plan** โ€” `semantic-release --dry-run` resolves the next version from - the commits since the last tag. No release-worthy commits โ†’ the run ends - here as a no-op. - - **release** (macOS runner) โ€” lint + unit-test gates, signing setup, then - `semantic-release` creates and pushes the bare `x.y.z` tag, and - **GoReleaser** builds, signs and publishes the GitHub Release against it. - - **verify** โ€” the published Windows/macOS assets are downloaded on real - runners and their signatures verified; a failure yanks the release + tag. -2. Re-dispatching after a rolled-back failure is safe (same rules re-apply). - Dispatching from any other branch fails the guard step by design. - -## Invariants (do not break) - -- **Tags are bare `x.y.z`** (`tagFormat` in [.releaserc.yml](../../.releaserc.yml)): - the self-updater and wsllauncher download - `releases/download//โ€ฆ` URLs with no `v` prefix. -- **The workflow file must stay `.github/workflows/release.yml` and run from - `main`**: the self-updater pins the cosign signing identity to - `release.yml@refs/heads/main` (`internal/shared/selfupdate/cosign.go`). -- **GoReleaser owns the GitHub Release** โ€” notes are label-based via - [.github/release.yml](../../.github/release.yml) (`changelog.use: - github-native`); semantic-release deliberately has no github/notes plugin. - -Release *notes* (categorized, label-based, per PR) and release *versions* -(conventional-commit types) are thus decoupled: labels shape the changelog, -types shape the version. - -Binary signing is documented in [release-signing.md](release-signing.md). diff --git a/docs/development/security/README.md b/docs/development/security/README.md deleted file mode 100644 index ef317b71..00000000 --- a/docs/development/security/README.md +++ /dev/null @@ -1,250 +0,0 @@ -# Security Guidelines - -This document describes the security patterns, practices, and mitigations built into the OpenFrame CLI, along with guidelines for contributors to maintain these standards. - ---- - -## Authentication and Authorization - -### Kubernetes Authentication - -The CLI authenticates to Kubernetes clusters using standard kubeconfig files. The `internal/k8s` package handles context loading and `rest.Config` construction: - -```go -// Contexts are loaded from the standard kubeconfig path (~/.kube/config) -// or from the KUBECONFIG environment variable. -// rest.Config is constructed per-operation, not stored globally. -``` - -**Guidelines:** -- Never hardcode kubeconfig paths โ€” always resolve via `clientcmd.BuildConfigFromFlags` -- Use the `Accessor` type for cluster health checks rather than raw API calls -- Always pass `rest.Config` through function arguments, not global variables - -### GitHub API Authentication - -The self-update and download subsystems authenticate with GitHub using tokens: - -| Variable | Priority | Description | -|---|---|---| -| `OPENFRAME_GITHUB_TOKEN` | High | OpenFrame-specific token (takes precedence) | -| `GITHUB_TOKEN` | Standard | Standard GitHub Actions token | - -**Guidelines:** -- Never log tokens, even at debug level โ€” they are registered with `redact.RegisterSecret()` at startup -- Always pass tokens through environment variables, never as command-line arguments (visible in `ps` output) - -### ArgoCD Authentication - -ArgoCD is managed via the native Kubernetes dynamic client (client-go) rather than the ArgoCD HTTP API. This means: -- No ArgoCD API tokens are ever stored or transmitted -- All operations go through Kubernetes RBAC via the kubeconfig credentials -- The CLI never calls ArgoCD's REST API directly - ---- - -## Secret Redaction - -All potentially sensitive values must be registered with the `redact` package before any logging or command execution: - -```go -import "github.com/flamingo-stack/openframe-cli/internal/shared/redact" - -// Register a secret for automatic scrubbing -redact.RegisterSecret(githubToken) -redact.RegisterSecret(registryPassword) - -// All log output and command strings are automatically scrubbed -// redact.Redact("helm upgrade --set auth.token=mysecret") -// โ†’ "helm upgrade --set auth.token=***" -``` - -**Key behaviors:** -- Longer secrets are replaced before shorter ones to prevent partial unmasking -- URL-embedded credentials (`user:pass@host`) are scrubbed unconditionally without explicit registration -- The redaction is thread-safe via `sync.RWMutex` -- In tests, call `redact.ClearSecrets()` in teardown to prevent cross-test contamination - -**Contribution rule:** Any value read from environment variables, configuration files, or user prompts that could be a credential **must** be passed through `redact.RegisterSecret()` before being used in any executor call or log statement. - ---- - -## Input Validation and Sanitization - -### Cluster Name Validation - -Cluster names are validated against RFC1123 rules at the command boundary before reaching any shell-out: - -```go -// Validation is enforced in cmd/bootstrap/bootstrap.go and cmd/cluster/create.go -// before any subprocess execution โ€” prevents injection via cluster names -if err := clustermodels.ValidateClusterName(name); err != nil { - return err -} -``` - -This ensures that a cluster name like `; rm -rf /` cannot reach the k3d subprocess. - -### Helm Values Validation - -The `openframe-helm-values.yaml` file is validated via a "preflight" check **before** cluster creation โ€” the cheapest gate in the pipeline: - -```go -// internal/chart/services/preflight.go -if err := services.ValidateHelmValuesFile(); err != nil { - // Fails fast before any expensive cluster operations - return err -} -``` - -**Guidelines:** -- All user-supplied YAML/flag values must be validated before being passed to external processes -- Use structured types with validation tags rather than raw string interpolation into shell commands -- Never construct shell commands via string concatenation โ€” use argv arrays via the `CommandExecutor` interface - -### Command Injection Prevention - -The `CommandExecutor` interface uses `os/exec` with argv arrays (not shell invocation): - -```go -// SAFE: argv array โ€” no shell injection possible -result, err := exec.Execute(ctx, "k3d", "cluster", "list", "--output", "json") - -// NEVER do this โ€” shell injection risk: -// exec.Execute(ctx, "sh", "-c", "k3d cluster list --output " + userInput) -``` - -**Contribution rule:** Never pass user input to `sh -c` or any shell interpreter. Always use direct `os/exec` with separate argument lists. - ---- - -## Self-Update Security - -The self-update mechanism uses [Sigstore/cosign](https://docs.sigstore.dev/cosign/overview/) for supply chain security: - -```mermaid -graph LR - A["openframe update"] --> B["Fetch latest release from GitHub"] - B --> C["Download checksums.txt + bundle.json"] - C --> D["Verify cosign signature"] - D --> E{"Signature valid?"} - E -->|Yes| F["Download binary archive"] - E -->|No| G["REJECT โ€” abort update"] - F --> H["Verify SHA256 checksum"] - H --> I["Smoke-test new binary"] - I --> J["Atomic binary swap"] - J --> K[".bak rollback saved"] -``` - -**Pinned identity checks:** -- OIDC Issuer: `https://token.actions.githubusercontent.com` (GitHub Actions only) -- SAN Regex: Matches only `flamingo-stack/openframe-cli`'s `release.yml` workflow on `main` or tag refs -- Signatures from any other repository, workflow, or issuer are **rejected** - -**Emergency escape hatch** (for testing/development only โ€” never in production): - -```bash -export OPENFRAME_UPDATE_INSECURE_SKIP_VERIFY=1 -``` - -> **Warning:** Setting `OPENFRAME_UPDATE_INSECURE_SKIP_VERIFY=1` disables all cryptographic verification. Only use this in isolated development environments. - ---- - -## Binary Download Security - -All binary downloads (k3d, mkcert, Helm) use pinned versions and SHA256 checksum verification: - -```go -// internal/shared/download/pins.go -// Each tool has a pinned version and expected SHA256 checksum -// Downloads are rejected if the checksum doesn't match -``` - -**Guidelines:** -- Never download binaries without checksum verification -- Pin versions explicitly โ€” never download "latest" without verification -- Use HTTPS for all downloads - ---- - -## WSL Security Considerations - -On Windows, the CLI forwards execution into WSL2: - -```go -// Only forward if ShouldForward() returns true -// ShouldForward() returns false if: -// - running on Linux (prevents infinite recursion) -// - OPENFRAME_NO_WSL_FORWARD=1 is set -if wsllauncher.ShouldForward() { - code, err := wsllauncher.Forward(version, os.Args[1:]) - os.Exit(code) -} -``` - -Environment variables `GITHUB_TOKEN` and `OPENFRAME_GITHUB_TOKEN` are forwarded into WSL via `WSLENV` โ€” ensure these are not set to high-privilege tokens in shared environments. - ---- - -## Environment Variables and Secrets Management - -### Principles - -1. **Never log secrets** โ€” Register all credentials with `redact.RegisterSecret()` immediately on ingestion -2. **Never pass secrets as CLI flags** โ€” Flags appear in process lists (`ps aux`). Use environment variables -3. **Never embed secrets in source code** โ€” Use environment variables or external secret managers -4. **Rotate regularly** โ€” GitHub tokens used for `OPENFRAME_GITHUB_TOKEN` should be scoped to the minimum required permissions - -### Recommended Token Scopes - -For `OPENFRAME_GITHUB_TOKEN` / `GITHUB_TOKEN`: - -| Scope | Required? | Reason | -|---|---|---| -| `read:packages` | Optional | Accessing private container images | -| `repo` (public read) | No | Public repos are accessible without auth | -| No special scopes | Sufficient | For rate-limit bypass only (public repos) | - ---- - -## Common Vulnerabilities and Mitigations - -| Vulnerability | Mitigation | -|---|---| -| **Command injection** | `os/exec` with argv arrays; cluster name RFC1123 validation | -| **Secret leakage in logs** | `redact` package with automatic URL credential scrubbing | -| **Malicious update binary** | Cosign signature verification against pinned GitHub Actions identity | -| **Checksum bypass** | SHA256 verification before any binary execution | -| **Stale kubeconfig** | Context validated before use; `Accessor.Reachable()` check | -| **YAML injection** | Structured Helm values parsing, not raw string interpolation | -| **Token exposure in env** | Tokens forwarded via `WSLENV` mechanism, not command arguments | - ---- - -## Security Testing - -The `MockCommandExecutor` records all argv arrays for security assertions: - -```go -mock := executor.MockCommandExecutor{} -// After execution: -calls := mock.RecordedCalls() -for _, call := range calls { - // Assert no user input leaked into command args without validation - assert.NotContains(t, call.Args, userInput) -} -``` - -**Security test checklist for new commands:** -- [ ] User-supplied cluster names are validated via `ValidateClusterName` -- [ ] Any new credential/token is registered with `redact.RegisterSecret()` -- [ ] External commands use argv arrays, not shell strings -- [ ] New YAML/JSON input is validated via structured types before use -- [ ] Sensitive flags are not printed in error messages - ---- - -## Reporting Security Issues - -Please report security vulnerabilities via the [OpenMSP Slack community](https://www.openmsp.ai/) using a direct message to the maintainers rather than public channels. Do not open public GitHub issues for security vulnerabilities. diff --git a/docs/development/setup/environment.md b/docs/development/setup/environment.md deleted file mode 100644 index bc3dd5d4..00000000 --- a/docs/development/setup/environment.md +++ /dev/null @@ -1,224 +0,0 @@ -# Development Environment Setup - -This guide covers setting up a development environment for contributing to the OpenFrame CLI. - ---- - -## Required Tools - -| Tool | Version | Purpose | -|---|---|---| -| **Go** | 1.21+ | Primary language runtime and toolchain | -| **Git** | 2.30+ | Version control | -| **Docker** | 24.x+ | Container runtime (required for integration tests) | -| **k3d** | 5.x+ | Local Kubernetes clusters (integration tests) | -| **Helm** | 3.x+ | Kubernetes package manager (integration tests) | -| **Make** | Any | Build automation (if Makefile is present) | - ---- - -## Installing Go - -### macOS - -```bash -# Using Homebrew -brew install go - -# Verify -go version -``` - -### Linux - -```bash -# Download the latest Go release -curl -OL https://go.dev/dl/go1.22.0.linux-amd64.tar.gz -sudo tar -C /usr/local -xzf go1.22.0.linux-amd64.tar.gz - -# Add to PATH (add to ~/.bashrc or ~/.zshrc) -export PATH=$PATH:/usr/local/go/bin - -# Verify -go version -``` - -### Windows (WSL2) - -Follow the Linux instructions inside your WSL2 terminal. - ---- - -## IDE Recommendations - -### Visual Studio Code (Recommended) - -VS Code with the Go extension provides the best development experience for this project. - -**Install the Go extension:** - -```bash -code --install-extension golang.go -``` - -**Recommended VS Code extensions:** - -| Extension | ID | Purpose | -|---|---|---| -| Go | `golang.go` | Go language support, debugging, testing | -| GitLens | `eamodio.gitlens` | Enhanced Git integration | -| YAML | `redhat.vscode-yaml` | YAML editing for Helm values | -| Docker | `ms-azuretools.vscode-docker` | Docker integration | -| Markdown All in One | `yzhang.markdown-all-in-one` | Documentation editing | - -**Recommended `settings.json` for Go development:** - -```json -{ - "go.useLanguageServer": true, - "go.lintTool": "golangci-lint", - "go.lintOnSave": "package", - "go.formatTool": "goimports", - "go.testFlags": ["-v", "-race"], - "[go]": { - "editor.formatOnSave": true, - "editor.codeActionsOnSave": { - "source.organizeImports": "explicit" - } - } -} -``` - -### GoLand (JetBrains) - -GoLand offers excellent Go support with built-in refactoring tools. No additional plugins required โ€” all Go features are built in. - -### Neovim / Vim - -Use `gopls` (Go language server) via `nvim-lspconfig`: - -```bash -go install golang.org/x/tools/gopls@latest -``` - ---- - -## Go Environment Configuration - -### Verify GOPATH and module mode - -```bash -go env GOPATH -go env GOMODCACHE -go env GOFLAGS -``` - -The project uses Go modules (`go.mod`), so `GOFLAGS` should not set `-mod=vendor` unless you're working with a vendor directory. - -### Configure GOPRIVATE (if needed) - -If your environment restricts access to the Flamingo private modules, configure: - -```bash -go env -w GOPRIVATE=github.com/flamingo-stack -``` - ---- - -## Linting and Code Quality Tools - -Install the Go linting toolchain used in the project: - -```bash -# golangci-lint (recommended) -curl -sSfL https://raw.githubusercontent.com/golangci-lint/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin - -# goimports (import management + formatting) -go install golang.org/x/tools/cmd/goimports@latest - -# staticcheck (additional static analysis) -go install honnef.co/go/tools/cmd/staticcheck@latest -``` - -Verify: - -```bash -golangci-lint --version -goimports -h -staticcheck -version -``` - ---- - -## Environment Variables for Development - -| Variable | Description | Example | -|---|---|---| -| `OPENFRAME_GITHUB_TOKEN` | GitHub token for API calls (avoids rate limits) | Your personal access token | -| `GOFLAGS` | Go build flags | `-v` for verbose builds | -| `OPENFRAME_UPDATE_INSECURE_SKIP_VERIFY` | Skip cosign verification (dev/testing only) | `1` | - -Set these in your shell profile (`~/.bashrc`, `~/.zshrc`, etc.): - -```bash -export OPENFRAME_GITHUB_TOKEN="your-github-token-here" -``` - ---- - -## Pre-Commit Hooks (Optional) - -Setting up pre-commit hooks ensures code quality before every commit: - -```bash -# Install pre-commit -pip install pre-commit -# or: brew install pre-commit - -# Install hooks (from repo root) -pre-commit install -``` - -Alternatively, add a manual hook to `.git/hooks/pre-commit`: - -```bash -#!/bin/sh -set -e -go vet ./... -goimports -l . -``` - ---- - -## Verifying Your Setup - -Run these commands from the repository root to confirm your environment is ready: - -```bash -# Verify Go version -go version - -# Download dependencies -go mod download - -# Verify all dependencies resolve -go mod verify - -# Build the binary -go build -o openframe . - -# Run unit tests -go test ./... - -# Run vet -go vet ./... -``` - -A successful run of all the above indicates a correctly configured development environment. - ---- - -## Next Steps - -- Follow the [Local Development Guide](local-development.md) to clone, build, and run the CLI -- Review the [Architecture Overview](../architecture/README.md) to understand the codebase diff --git a/docs/development/setup/local-development.md b/docs/development/setup/local-development.md deleted file mode 100644 index 4fb3b714..00000000 --- a/docs/development/setup/local-development.md +++ /dev/null @@ -1,216 +0,0 @@ -# Local Development Guide - -Clone, build, run, test, and debug OpenFrame CLI locally. - -## Prerequisites - -- **[Environment Setup](environment.md)** - Go toolchain, editor, and Kubernetes tools - -## Clone the Repository - -Fork on GitHub (recommended for contributors), then: - -```bash -git clone https://github.com/YOUR-USERNAME/openframe-cli.git -cd openframe-cli -git remote add upstream https://github.com/flamingo-stack/openframe-cli.git -``` - -Or clone directly for read-only use: - -```bash -git clone https://github.com/flamingo-stack/openframe-cli.git -cd openframe-cli -``` - -## Project Structure - -```text -openframe-cli/ -โ”œโ”€โ”€ main.go # Entry point -โ”œโ”€โ”€ Makefile # build / test / lint targets -โ”œโ”€โ”€ cmd/ # Command definitions: bootstrap, cluster, app, prerequisites, update, root.go -โ”œโ”€โ”€ internal/ # Private packages: bootstrap, cluster, chart, app, k8s, platform, prerequisites, shared -โ”œโ”€โ”€ tests/ # integration/ and testutil/ -โ””โ”€โ”€ docs/ # Documentation -``` - -Unit tests are colocated as `*_test.go` next to the code they cover. - -## Build and Run - -```bash -# Build for your current platform (produces openframe--) -make build - -# Cross-compile all six release platforms (matches .goreleaser.yml) -make build-all - -# Or build directly -go build -o openframe . -./openframe --version -``` - -Run without building during development: - -```bash -go run . --help -go run . cluster status -go run . app status -``` - -## Run Tests - -```bash -make test # unit + integration -make test-unit # ./cmd/... ./internal/... -make test-race # unit tests with the race detector (needs CGO) -make test-integration # ./tests/integration/... - -# Or with go directly -go test ./... -go test -run TestClusterCreate ./internal/cluster/... -go test -cover ./... -``` - -Integration tests may require a running cluster: - -```bash -k3d cluster create openframe-test -go test ./tests/integration/... -k3d cluster delete openframe-test -``` - -## Lint and Format - -```bash -make fmt # gofmt -w over the tree -make vet # go vet ./... -make lint # golangci-lint run ./... -make tidy # fail if `go mod tidy` would change go.mod/go.sum -``` - -These mirror the CI gates โ€” run them before pushing. - -## Development Workflow - -```bash -# Sync with upstream -git fetch upstream && git checkout main && git merge upstream/main - -# Create a branch, make changes, then before committing: -make fmt vet tidy -make test -make lint - -# Commit (conventional commits) and push -git commit -m "feat(cluster): add support for custom node labels" -git push origin feature/your-feature-name -``` - -## Debugging - -### VS Code - -Use the launch configurations from [Environment Setup](environment.md), set breakpoints, and press F5. - -### Delve - -```bash -go install github.com/go-delve/delve/cmd/dlv@latest - -dlv debug . -- bootstrap --verbose --non-interactive -dlv test ./internal/bootstrap/ -``` - -## Manually Testing Your Changes - -The CLI's top-level commands are `bootstrap`, `cluster`, `app`, `prerequisites`, and `update`. - -```bash -# Prerequisites -go run . prerequisites check - -# Cluster lifecycle -go run . cluster create test-cluster -go run . cluster status test-cluster -go run . cluster list -go run . cluster delete test-cluster - -# App-of-apps: clones openframe-oss-tenant and installs ArgoCD + the app-of-apps chart. -# --non-interactive reuses the existing openframe-helm-values.yaml. -go run . app install --non-interactive -go run . app status -go run . app access - -# Full bootstrap (cluster + app-of-apps) -go run . bootstrap --non-interactive -``` - -Verify against a real cluster: - -```bash -kubectl get pods --all-namespaces -kubectl get applications -n argocd -``` - -### Overriding ArgoCD chart values - -The CLI installs ArgoCD from a built-in baseline (embedded -`internal/chart/providers/argocd/argocd-values.yaml`), which is separate from -the app-of-apps values. To change an ArgoCD chart value without rebuilding the -CLI, add a top-level `argocd:` section to `openframe-helm-values.yaml`: - -```yaml -# openframe-helm-values.yaml -repository: - branch: main # (app-of-apps settings, as before) - -argocd: # deep-merged over the built-in ArgoCD baseline - dex: - enabled: true # e.g. re-enable dex (disabled by default) - server: - replicas: 2 -``` - -Only the `argocd:` subtree is applied to the ArgoCD install โ€” the rest of the -file targets the app-of-apps chart, and keeping them separate stops secrets -(e.g. the docker registry password) from leaking into the ArgoCD release. The -merge follows Helm semantics (maps merge, scalars/lists replace), and the CLI -prints a warning listing the keys you overrode, since a bad override can break -the ArgoCD install. Without an `argocd:` section the baseline is used unchanged. - -## Cross-platform Builds - -`make build` puts the current-platform binary into `build/`; `make build-all` -cross-compiles every release platform there. By hand: - -```bash -GOOS=linux GOARCH=amd64 go build -o build/openframe-linux-amd64 . -GOOS=darwin GOARCH=arm64 go build -o build/openframe-darwin-arm64 . -GOOS=windows GOARCH=amd64 go build -o build/openframe-windows-amd64.exe . -``` - -On Windows the CLI forwards into WSL2 and runs the Linux binary; that launch is handled by `internal/shared/wsllauncher`. - -## Troubleshooting - -```bash -# Module issues -go clean -modcache && go mod tidy - -# Build cache -go clean -cache - -# Kubernetes context -kubectl config current-context -kubectl config use-context k3d-openframe-local -``` - -## Next Steps - -- **[Architecture Overview](../architecture/README.md)** - Understand the system design - -## Getting Help - -Search existing GitHub issues, or ask in the [OpenMSP community](https://join.slack.com/t/openmsp/shared_invite/zt-36bl7mx0h-3~U2nFH6nqHqoTPXMaHEHA). diff --git a/docs/development/testing/README.md b/docs/development/testing/README.md deleted file mode 100644 index bc9b7290..00000000 --- a/docs/development/testing/README.md +++ /dev/null @@ -1,358 +0,0 @@ -# Testing Guide - -OpenFrame CLI uses a layered testing approach: fast unit tests with mock executors, integration tests against real CLI binaries, and shared test utilities that eliminate boilerplate. - ---- - -## Test Structure and Organization - -```text -tests/ -โ”œโ”€โ”€ integration/ -โ”‚ โ”œโ”€โ”€ common/ -โ”‚ โ”‚ โ”œโ”€โ”€ cli_runner.go # Build + execute CLI binary, capture output -โ”‚ โ”‚ โ”œโ”€โ”€ cluster_management.go # Helpers for cluster lifecycle in tests -โ”‚ โ”‚ โ””โ”€โ”€ dependencies.go # Dependency setup utilities -โ”‚ โ””โ”€โ”€ ... # Integration test files -โ””โ”€โ”€ testutil/ - โ”œโ”€โ”€ setup.go # Test mode init, mock executor, flag containers - โ”œโ”€โ”€ patterns.go # Standard command test patterns (Structure/Flags/CLI/Execution) - โ”œโ”€โ”€ assertions.go # Custom assertion helpers - โ”œโ”€โ”€ cluster.go # Cluster-specific test helpers - โ”œโ”€โ”€ command_assertions.go # CLI output assertion utilities - โ”œโ”€โ”€ flag_contract.go # Flag contract validation helpers - โ””โ”€โ”€ utilities.go # General test utilities -``` - -Unit tests live alongside the source code they test (e.g., `internal/cluster/service_test.go`). - ---- - -## Running Tests - -### Unit Tests - -```bash -# Run all unit tests -go test ./... - -# Run with verbose output -go test -v ./... - -# Run with race detector (recommended) -go test -race ./... - -# Run tests for a specific package -go test ./internal/cluster/... -go test ./cmd/bootstrap/... - -# Run a specific test by name -go test -run TestBootstrapService ./internal/bootstrap/... -``` - -### Integration Tests - -Integration tests require Docker, k3d, and Helm to be installed and running: - -```bash -# Run all integration tests (longer timeout required) -go test ./tests/integration/... -v -timeout 30m - -# Run a specific integration test -go test ./tests/integration/... -run TestClusterCreate -v -timeout 10m -``` - -> **Resource requirement:** Integration tests provision real K3D clusters. Ensure at least 24 GB RAM and 50 GB disk space are available. - -### Coverage - -```bash -# Generate coverage profile -go test -coverprofile=coverage.out ./... - -# View coverage in browser -go tool cover -html=coverage.out - -# View coverage in terminal -go tool cover -func=coverage.out | tail -1 -``` - ---- - -## Test Utilities - -### Initializing Test Mode - -Always call `testutil.InitializeTestMode()` in test setups to enable safe UI rendering (prevents pterm from trying to write to a non-TTY): - -```go -func TestMain(m *testing.M) { - testutil.InitializeTestMode() - os.Exit(m.Run()) -} -``` - -### Mock Command Executor - -The `MockCommandExecutor` replaces real shell-outs with configurable stubs, enabling fully isolated unit tests: - -```go -func TestCreateCluster(t *testing.T) { - testutil.InitializeTestMode() - - mock := testutil.NewTestMockExecutor() - mock.SetResponse("k3d cluster create", &executor.CommandResult{ - ExitCode: 0, - Stdout: `{"name": "test-cluster"}`, - }) - - // Inject mock into the service under test - svc := cluster.NewClusterService(mock) - err := svc.CreateCluster(context.Background(), "test-cluster") - assert.NoError(t, err) - - // Verify the right command was called - calls := mock.RecordedCalls() - assert.Contains(t, calls[0].Args, "create") -} -``` - -### Standard Flag Containers - -Use `CreateStandardTestFlags()` for unit tests (mock dependencies) and `CreateIntegrationTestFlags()` for integration tests (real dependencies): - -```go -// Unit test โ€” mock executor, no live cluster needed -flags := testutil.CreateStandardTestFlags() - -// Integration test โ€” real executor, requires k3d -flags := testutil.CreateIntegrationTestFlags() -``` - -`CreateStandardTestFlags()` pre-configures common mock responses: -- `k3d cluster list` โ†’ empty array `[]` -- `k3d cluster get` โ†’ not found - ---- - -## Writing Unit Tests - -### Testing a Cobra Command - -Use `testutil.TestClusterCommand` to run the four standard sub-tests for any cluster command: - -```go -package create_test - -import ( - "testing" - "github.com/flamingo-stack/openframe-cli/tests/testutil" -) - -func TestCreateCommand(t *testing.T) { - testutil.TestClusterCommand( - t, - "create", // command name - NewCreateCommand, // func() *cobra.Command - func() { // setup - testutil.InitializeTestMode() - }, - func() {}, // teardown - ) -} -``` - -This runs four sub-tests automatically: - -| Sub-test | What it checks | -|---|---| -| `Structure` | Name, short/long descriptions, `RunE` presence | -| `Flags` | `--help` succeeds, unknown flags return error | -| `CLI` | Argument count validation via `cmd.Args` | -| `Execution` | `--dry-run` behavior (if registered), `--help` always succeeds | - -### Testing Business Logic (Service Layer) - -```go -func TestChartServiceInstall(t *testing.T) { - testutil.InitializeTestMode() - - mock := testutil.NewTestMockExecutor() - // Configure mock responses for helm commands - mock.SetResponse("helm upgrade --install argo-cd", &executor.CommandResult{ - ExitCode: 0, - Stdout: "Release \"argo-cd\" has been upgraded.", - }) - - svc := chart.NewChartService(mock, fakeK8sClient) - err := svc.InstallArgoCD(context.Background(), cfg) - assert.NoError(t, err) -} -``` - -### Testing Error Paths - -```go -func TestClusterCreateFailure(t *testing.T) { - testutil.InitializeTestMode() - mock := testutil.NewTestMockExecutor() - - // Simulate k3d failure - mock.SetResponse("k3d cluster create", &executor.CommandResult{ - ExitCode: 1, - Stderr: "cluster already exists", - }) - - svc := cluster.NewClusterService(mock) - err := svc.CreateCluster(context.Background(), "existing-cluster") - - assert.Error(t, err) - assert.Contains(t, err.Error(), "already exists") -} -``` - ---- - -## Writing Integration Tests - -Integration tests use the `common.CLIRunner` to build and execute the real binary: - -```go -package integration_test - -import ( - "log" - "os" - "strings" - "testing" - "github.com/flamingo-stack/openframe-cli/tests/integration/common" -) - -func TestMain(m *testing.M) { - if err := common.InitializeCLI(); err != nil { - log.Fatalf("CLI build failed: %v", err) - } - defer common.CleanupCLI() - os.Exit(m.Run()) -} - -func TestClusterList(t *testing.T) { - result := common.RunCLI("cluster", "list", "--output", "json") - - if result.Failed() { - t.Fatalf("cluster list failed: %s", result.ErrorMessage()) - } - - // Verify JSON output - if !strings.HasPrefix(strings.TrimSpace(result.Stdout), "[") { - t.Errorf("expected JSON array output, got: %s", result.Stdout) - } -} -``` - -### CLIResult Methods - -| Method | Description | -|---|---| -| `result.Success()` | `true` when exit code is 0 and no error | -| `result.Failed()` | Inverse of `Success()` | -| `result.Output()` | Concatenates stdout + stderr | -| `result.ErrorMessage()` | Extracts first `Error: ...` line from stderr | -| `result.Stdout` | Raw stdout string | -| `result.Stderr` | Raw stderr string | -| `result.ExitCode` | Integer exit code | - -### CLI Binary Caching - -`InitializeCLI()` builds the binary to `build/openframe` and caches it by comparing mod times against `main.go`. Subsequent test runs skip the rebuild if the binary is newer than the source โ€” significantly speeding up iterative testing. - ---- - -## Test Patterns and Conventions - -### Table-Driven Tests - -Prefer table-driven tests for commands with multiple argument/flag combinations: - -```go -func TestClusterNameValidation(t *testing.T) { - tests := []struct { - name string - clusterName string - wantError bool - }{ - {"valid name", "openframe-dev", false}, - {"too short", "ab", true}, - {"uppercase", "MyCluster", true}, - {"injection attempt", "test;rm -rf /", true}, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - err := models.ValidateClusterName(tt.clusterName) - if tt.wantError { - assert.Error(t, err) - } else { - assert.NoError(t, err) - } - }) - } -} -``` - -### Redaction Cleanup in Tests - -When testing code that uses `redact.RegisterSecret()`, always clean up in teardown: - -```go -func TestWithSecret(t *testing.T) { - defer redact.ClearSecrets() - - redact.RegisterSecret("test-token") - // ... test code -} -``` - -### Non-Interactive Mode in Tests - -Set non-interactive mode to prevent tests from blocking on prompts: - -```go -func TestNonInteractiveBehavior(t *testing.T) { - // Option 1: Use the --non-interactive flag in integration tests - result := common.RunCLI("bootstrap", "--non-interactive") - - // Option 2: Set UI test mode (unit tests) - testutil.InitializeTestMode() // sets ui.TestMode = true -} -``` - ---- - -## Coverage Requirements - -| Package Type | Target Coverage | -|---|---| -| Core services (`internal/`) | โ‰ฅ 80% | -| Command layer (`cmd/`) | โ‰ฅ 70% | -| Provider implementations | โ‰ฅ 75% | -| Shared utilities | โ‰ฅ 85% | - -Run coverage and check targets: - -```bash -go test -coverprofile=coverage.out ./... -go tool cover -func=coverage.out | grep -v "100.0%" -``` - ---- - -## CI Test Environment - -The GitHub Actions CI pipeline runs: - -1. **Unit tests** with race detector on every push -2. **Integration tests** on PRs targeting `main` -3. **Coverage reporting** on every PR - -Integration tests in CI use a matrix of Go versions and operating systems. Ensure your tests pass on both Linux and macOS. diff --git a/docs/diagrams/architecture/.gitignore b/docs/diagrams/architecture/.gitignore deleted file mode 100644 index b4f4c7a6..00000000 --- a/docs/diagrams/architecture/.gitignore +++ /dev/null @@ -1,8 +0,0 @@ -# CodeWiki temp files (dependency graphs can be 7GB+) -temp/ -dependency_graphs/ - -# JSON intermediate files (except schema/config) -*.json -!*-schema.json -!*-config.json diff --git a/docs/diagrams/architecture/README.md b/docs/diagrams/architecture/README.md deleted file mode 100644 index d2887df1..00000000 --- a/docs/diagrams/architecture/README.md +++ /dev/null @@ -1,18 +0,0 @@ -# Architecture Diagrams - -This directory contains Mermaid diagrams generated from architecture analysis. - -## Diagrams - -- **[High-Level System Design](./high-level-system-design.mmd)** - `.mmd` file -- **[Dependency Flowchart](./dependency-flowchart.mmd)** - `.mmd` file -- **[Bootstrap Sequence Diagram](./bootstrap-sequence-diagram.mmd)** - `.mmd` file -- **[App Install / Upgrade Data Flow](./app-install-upgrade-data-flow.mmd)** - `.mmd` file - -## Viewing Diagrams - -These `.mmd` files can be viewed: -- In VS Code with Mermaid extension -- On GitHub (renders inline) -- Via [Mermaid Live Editor](https://mermaid.live) -- In documentation tools that support Mermaid diff --git a/docs/diagrams/architecture/app-install-upgrade-data-flow.mmd b/docs/diagrams/architecture/app-install-upgrade-data-flow.mmd deleted file mode 100644 index 2f26c570..00000000 --- a/docs/diagrams/architecture/app-install-upgrade-data-flow.mmd +++ /dev/null @@ -1,28 +0,0 @@ -sequenceDiagram - participant User - participant AppCmd as "cmd/app/install" - participant Target as "app/target.Selector" - participant K8sPkg as "k8s package" - participant ChSvc as "chart/services" - participant ArgoProv as "argocd.Manager" - participant HelmProv as "helm.HelmManager" - - User->>AppCmd: openframe app install [--context k3d-dev] - AppCmd->>Target: Select(ctx) [if no --context] - Target->>K8sPkg: LoadContexts(kubeconfigPath) - K8sPkg-->>Target: []ContextInfo - Target->>User: Prompt: select context - User-->>Target: k3d-openframe-dev - Target->>K8sPkg: CheckResources(ctx, requirements) - K8sPkg-->>Target: Resources, sufficient=true - Target-->>AppCmd: SelectResult{Config, Context} - - AppCmd->>ChSvc: InstallChartsWithConfigContext(ctx, req) - ChSvc->>ArgoProv: Install(ctx, cfg) - ArgoProv-->>ChSvc: ArgoCD installed - ChSvc->>HelmProv: InstallAppOfAppsFromLocal(ctx, cfg) - HelmProv-->>ChSvc: app-of-apps installed - ChSvc->>ArgoProv: WaitForApplications(ctx, cfg) - ArgoProv-->>ChSvc: All apps Healthy+Synced - ChSvc-->>AppCmd: OK - AppCmd-->>User: SUCCESS diff --git a/docs/diagrams/architecture/bootstrap-sequence-diagram.mmd b/docs/diagrams/architecture/bootstrap-sequence-diagram.mmd deleted file mode 100644 index 5fda484b..00000000 --- a/docs/diagrams/architecture/bootstrap-sequence-diagram.mmd +++ /dev/null @@ -1,47 +0,0 @@ -sequenceDiagram - participant User - participant CLI as "openframe bootstrap" - participant BSvc as "bootstrap.Service" - participant CSvc as "cluster.Service" - participant K3D as "K3D Provider" - participant ChSvc as "chart/services" - participant Helm as "HelmManager" - participant Git as "git.Repository" - participant ArgoCD as "argocd.Manager" - participant K8s as "Kubernetes API" - - User->>CLI: openframe bootstrap [name] - CLI->>BSvc: Execute(cmd, args) - BSvc->>ChSvc: ValidateHelmValuesFile() - ChSvc-->>BSvc: OK / error - - BSvc->>CSvc: CreateClusterWithPrerequisites(ctx, name) - CSvc->>K3D: CreateCluster(ctx, config) - K3D-->>CSvc: rest.Config - CSvc-->>BSvc: rest.Config - - BSvc->>ChSvc: InstallChartsWithConfigContext(ctx, req) - ChSvc->>ChSvc: CheckAndInstallPrerequisites() - ChSvc->>Helm: InstallArgoCDWithProgress(ctx, cfg) - Helm->>K8s: helm upgrade --install argo-cd - K8s-->>Helm: OK - Helm->>K8s: waitForArgoCDDeployments() - K8s-->>Helm: Deployments ready - - ChSvc->>Git: CloneChartRepository(ctx, appConfig) - Git-->>ChSvc: CloneResult{tempDir, chartPath} - - ChSvc->>Helm: InstallAppOfAppsFromLocal(ctx, cfg) - Helm->>K8s: helm upgrade --install app-of-apps - K8s-->>Helm: OK - - ChSvc->>ArgoCD: WaitForApplications(ctx, cfg) - loop Every 2s until ready or timeout - ArgoCD->>K8s: List Applications (dynamic client) - K8s-->>ArgoCD: Application list - ArgoCD->>ArgoCD: assessApplications() - end - ArgoCD-->>ChSvc: All Healthy+Synced - - ChSvc-->>BSvc: OK - BSvc-->>User: Bootstrap complete diff --git a/docs/diagrams/architecture/dependency-flowchart.mmd b/docs/diagrams/architecture/dependency-flowchart.mmd deleted file mode 100644 index 714b7f72..00000000 --- a/docs/diagrams/architecture/dependency-flowchart.mmd +++ /dev/null @@ -1,62 +0,0 @@ -graph LR - subgraph Commands["cmd/"] - bootstrap["bootstrap"] - cluster_cmd["cluster/*"] - app_cmd["app/*"] - prereq_cmd["prerequisites"] - update_cmd["update"] - end - - subgraph Services["internal/"] - bsvc["bootstrap.Service"] - csvc["cluster.ClusterService"] - chsvc["chart/services.ChartService"] - appsvc["app/status + uninstall"] - prefw["prerequisites.Runner"] - supdater["selfupdate.Updater"] - end - - subgraph Providers["Providers"] - k3dp["cluster/providers/k3d"] - argop["chart/providers/argocd.Manager"] - helmp["chart/providers/helm.HelmManager"] - gitp["chart/providers/git.Repository"] - end - - subgraph Infra["Shared Infrastructure"] - exec["executor.CommandExecutor"] - k8spkg["k8s (rest.Config, Accessor)"] - dlpkg["download.Downloader"] - uipkg["shared/ui"] - errpkg["shared/errors"] - redactpkg["shared/redact"] - end - - bootstrap --> bsvc - cluster_cmd --> csvc - app_cmd --> chsvc - app_cmd --> appsvc - prereq_cmd --> prefw - update_cmd --> supdater - - bsvc --> csvc - bsvc --> chsvc - - csvc --> k3dp - chsvc --> argop - chsvc --> helmp - chsvc --> gitp - appsvc --> argop - - k3dp --> exec - helmp --> exec - argop --> k8spkg - helmp --> k8spkg - - prefw --> dlpkg - supdater --> dlpkg - - exec --> redactpkg - errpkg --> uipkg - chsvc --> errpkg - csvc --> errpkg diff --git a/docs/diagrams/architecture/high-level-system-design.mmd b/docs/diagrams/architecture/high-level-system-design.mmd deleted file mode 100644 index 46e33a66..00000000 --- a/docs/diagrams/architecture/high-level-system-design.mmd +++ /dev/null @@ -1,73 +0,0 @@ -graph TB - subgraph CLI["CLI Entry Point"] - main["main.go"] - root["cmd/root.go"] - end - - subgraph Commands["Command Layer"] - bootstrap["cmd/bootstrap"] - cluster_cmd["cmd/cluster"] - app_cmd["cmd/app"] - prereq_cmd["cmd/prerequisites"] - update_cmd["cmd/update"] - end - - subgraph Core["Core Services"] - bootstrap_svc["internal/bootstrap"] - cluster_svc["internal/cluster"] - chart_svc["internal/chart/services"] - prereq_fw["internal/prerequisites"] - selfupdate["internal/shared/selfupdate"] - end - - subgraph Providers["Providers"] - k3d_prov["cluster/providers/k3d"] - argocd_prov["chart/providers/argocd"] - helm_prov["chart/providers/helm"] - git_prov["chart/providers/git"] - end - - subgraph Shared["Shared Infrastructure"] - executor["internal/shared/executor"] - k8s["internal/k8s"] - download["internal/shared/download"] - ui["internal/shared/ui"] - redact["internal/shared/redact"] - errors["internal/shared/errors"] - end - - subgraph External["External Tools & APIs"] - k3d_tool["K3D CLI"] - helm_tool["Helm CLI"] - argocd_cr["ArgoCD CRDs"] - github["GitHub API"] - git_repo["Git Repositories"] - end - - main --> root - root --> Commands - bootstrap_cmd --> bootstrap_svc - cluster_cmd --> cluster_svc - app_cmd --> chart_svc - prereq_cmd --> prereq_fw - update_cmd --> selfupdate - - bootstrap_svc --> cluster_svc - bootstrap_svc --> chart_svc - - cluster_svc --> k3d_prov - chart_svc --> argocd_prov - chart_svc --> helm_prov - chart_svc --> git_prov - - k3d_prov --> executor - helm_prov --> executor - argocd_prov --> k8s - helm_prov --> k8s - - executor --> k3d_tool - executor --> helm_tool - argocd_prov --> argocd_cr - git_prov --> git_repo - selfupdate --> github - download --> github diff --git a/docs/getting-started/.gitignore b/docs/getting-started/.gitignore deleted file mode 100644 index a5d01be5..00000000 --- a/docs/getting-started/.gitignore +++ /dev/null @@ -1,7 +0,0 @@ -# VoltAgent temp files -temp/ - -# JSON intermediate files (except schema/config) -*.json -!*-schema.json -!*-config.json diff --git a/docs/getting-started/cloud-clusters.md b/docs/getting-started/cloud-clusters.md deleted file mode 100644 index 8d2cf12a..00000000 --- a/docs/getting-started/cloud-clusters.md +++ /dev/null @@ -1,175 +0,0 @@ -# Cloud Clusters (EKS / GKE) - -> Looking for a step-by-step walkthrough? See the -> [GKE Workflow](./gke-workflow.md) โ€” this page is the reference. - -Besides local k3d clusters, `openframe cluster create` can provision managed -Kubernetes clusters in AWS (EKS) or Google Cloud (GKE) using Terraform under -the hood. The CLI installs its own verified Terraform binary and generates the -infrastructure code for you โ€” no Terraform knowledge required. - -> **Cost warning.** Cloud clusters create billed resources: a managed control -> plane, VM nodes, and NAT/networking. The CLI shows a warning with the -> provider's pricing page before creating โ€” and, in an interactive -> `--dry-run` it offers to install [infracost](https://www.infracost.io) -> (verified pinned download; the one-time free `infracost auth login` is also offered in-CLI) and shows -> a monthly estimate โ€” and requires you to re-type the cluster name -> before deleting. Pricing: [GKE](https://cloud.google.com/kubernetes-engine/pricing) -> ยท [EKS](https://aws.amazon.com/eks/pricing/). - -## Prerequisites - -Checked and installed automatically on `cluster create`: - -| Type | Tools | You provide | -|------|-------|-------------| -| eks | terraform (pinned, verified), AWS CLI | working AWS credentials (`aws configure` or `--profile`) | -| gke | terraform (pinned, verified), gcloud, gke-gcloud-auth-plugin | `gcloud auth login` + a GCP project | - -**You do not need to log in beforehand.** When a command needs Google Cloud -access (`create --type gke`, `list --all`, `use`), the CLI checks your gcloud -auth state and, in an interactive session, offers to run `gcloud auth login` -(and `gcloud auth application-default login` for Terraform) right there โ€” one -flow, no manual steps. Non-interactive sessions (CI) never prompt and fail -with the exact command to run instead. - -**AWS identity is vetted before anything runs.** An EKS create (or its -`--dry-run`) first resolves which identity it is about to use โ€” your -`--profile`, or the default credential chain โ€” and shows the actual account -and ARN: - -- *Interactive*: you must confirm it (`Use AWS profile 'staging' โ€” account - 123456789012, arn:โ€ฆ for this EKS operation?`); declining aborts and lists - your other configured profiles. The wizard additionally offers a picker of - the profiles found in your AWS config. -- *Non-interactive (CI)*: never prompts โ€” passing `--profile` (or having a - working default chain) is the consent, and the account in use is printed so - CI logs show whose account was billed. -- *Nothing configured at all* (no named profiles and the default chain cannot - authenticate): the command fails with a pointer to the official AWS guide โ€” - [Configuration and credential file settings](https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-files.html). - Set up credentials, then re-run (optionally with `--profile `). - -Credentials are additionally preflighted before anything is created (`aws sts -get-caller-identity` / `gcloud auth print-access-token`), so a broken login -fails in seconds, not mid-provisioning. - -## Creating a cluster - -Interactive (wizard asks for type, region, instance type): - -```bash -openframe cluster create -``` - -Non-interactive: - -```bash -# AWS EKS -openframe cluster create my-eks --type eks --region us-east-1 --skip-wizard - -# Google GKE -openframe cluster create my-gke --type gke --project my-project --region us-central1 --skip-wizard -``` - -Useful flags: `--machine-type`, `--min-nodes` / `--max-nodes` (autoscaler -bounds; defaults 1 / 4, must be at least 1 โ€” an explicit 0 is rejected), -`--spot` (spot-capacity nodes, typically 60โ€“90% off the node cost โ€” the cost -warning suggests it for test clusters), `--profile` (AWS), `--nodes` (initial -size), `--version` (`.`, e.g. `1.33`), `--ha` (GKE: regional -control plane and nodes; the node count is then **per zone**, and every -summary shows the `N per zone ร— 3 zones` math). - -In interactive sessions the CLI first shows the full Terraform plan and asks -for approval (the `terraform apply` shape; what you approve is exactly what -runs โ€” non-interactive sessions auto-approve). Provisioning then takes ~10โ€“20 -minutes; the CLI streams per-resource progress. GKE nodes are private (no -external IPs, egress via Cloud NAT) with a public control-plane endpoint, so -the flow works in organizations enforcing `restrict_vm_external_ips`. EKS -clusters get a dedicated VPC (2 AZs, nodes in private subnets behind a single -NAT gateway), the core addons (`vpc-cni`, `kube-proxy`, `coredns`) and the -`aws-ebs-csi-driver` addon with a default gp3 StorageClass, so networking and -PersistentVolumeClaims work out of the box. The generated Terraform pins the -upstream EKS/VPC modules to exact versions โ€” upstream default changes arrive -only with a deliberate CLI release, never mid-`create`. The default node type -is `m7i-flex.large`, which is Free-Tier-eligible: a brand-new AWS account (its -Free plan refuses non-eligible instance types) can run the documented flow -unchanged. -When it finishes, your kubeconfig gets a context named after the cluster and -it becomes the current context โ€” `kubectl get nodes` just works -(authentication runs through short-lived tokens via `aws eks get-token` / -`gke-gcloud-auth-plugin`; no static credentials are stored). - -## Previewing without creating - -`--dry-run` runs a real `terraform plan` and prints the resource footprint -without creating anything (and without registering the cluster): - -```bash -openframe cluster create my-eks --type eks --region us-east-1 --skip-wizard --dry-run -# Plan: 47 to add, 0 to change, 0 to destroy -``` - -The preview authenticates like a real create (including the AWS identity -vetting above), but has classic `terraform plan` semantics: nothing is -written โ€” not to the cloud, and not to the workspace. Over an existing -(failed/interrupted) workspace the preview shows what a **resume** would -actually apply: the module and variables are regenerated from the current CLI -and your current flags, planned against the workspace's saved state in a -throwaway directory. - -## Where the state lives - -Each cloud cluster owns a workspace in `~/.openframe/clusters//`: the -generated Terraform module, the state file, and a `terraform.log` that every -apply/destroy appends its full output stream to (so a long operation leaves a -record beyond the terminal). The state is the only pointer to your billed -cloud resources โ€” the workspace is never deleted on a failed create, only -after a successful delete. - -- **A create failed or was interrupted?** Re-run the same `cluster create` โ€” - it resumes where it stopped. -- **Want the state to survive your machine?** Pass a remote backend at - create time: `--backend-config s3://bucket/prefix` (EKS) or - `--backend-config gcs://bucket/prefix` (GKE). - -## Day-2 commands - -```bash -openframe cluster list # local + cloud clusters -openframe cluster list --all # + external clusters discovered in your GCP projects / AWS profiles -openframe cluster use my-gke # switch kubectl context (and gcloud configuration) -openframe cluster status my-eks -openframe cluster delete my-eks # terraform destroy; asks to re-type the name -openframe app install # install OpenFrame onto the current context -``` - -`cluster use` works for external (discovered) GKE clusters too: it fetches -credentials via gcloud when the kubeconfig has no entry yet, and activates -the gcloud configuration matching the cluster's project. - -`cluster delete` tears down more than the terraform state: application -namespaces are removed first so PVC-backed disks/volumes are reclaimed while -the nodes still run, and anything that survives the destroy is swept up -afterwards โ€” listed and deleted with your consent. `--force` skips the typed -confirmation and consents to that sweep (for CI). `cluster cleanup` does not -apply to cloud clusters โ€” use `delete`. - -## Troubleshooting - -- **"no usable AWS configuration found"** โ€” the default credential chain - cannot authenticate and no named profiles exist. Set up credentials first - (official guide: [Configuration and credential file settings](https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-files.html)), - then re-run โ€” optionally with `--profile `. -- **"AWS ... cannot authenticate" / "gcloud is not authenticated"** โ€” fix - credentials (`aws configure`, `gcloud auth login`) and re-run; nothing was - created. For AWS the error lists your other configured profiles when the - selected one is broken. -- **Create failed mid-way** โ€” the error names the workspace directory. Re-run - `cluster create ` to resume, or `cluster delete ` to tear down - what was partially created. -- **Verbose Terraform output** โ€” add `--verbose` to stream Terraform's own - logs during create/delete. Either way, the full stream of every - apply/destroy is appended to - `~/.openframe/clusters//terraform/terraform.log`, and a failed - operation names that path. diff --git a/docs/getting-started/first-steps.md b/docs/getting-started/first-steps.md deleted file mode 100644 index 00b41ee5..00000000 --- a/docs/getting-started/first-steps.md +++ /dev/null @@ -1,224 +0,0 @@ -# First Steps - -You've successfully bootstrapped an OpenFrame environment. Here are the first 5 things to explore and configure to get the most out of your installation. - ---- - -## 1. Verify Your Environment - -Start by confirming the state of your cluster and platform: - -```bash -# Check cluster status -openframe cluster status - -# Check application status -openframe app status -``` - -### Cluster Status Output - -```text -NAME STATUS NODES VERSION -openframe-dev running 3 v1.29.x -``` - -### App Status Output - -The app status command aggregates both Kubernetes cluster health and ArgoCD application sync state into a single unified report. You'll see each deployed application with its sync and health status. - -```bash -# For machine-readable output (e.g., in scripts) -openframe cluster list --output json -openframe cluster status --output yaml -``` - ---- - -## 2. Access the OpenFrame Platform - -Get access information for the deployed OpenFrame services: - -```bash -openframe app access -``` - -This command displays the URLs and connection details for the OpenFrame platform running in your local cluster. - -> **Tip:** Bookmark the displayed URLs for quick access to the OpenFrame web interface and ArgoCD dashboard. - ---- - -## 3. Explore the Cluster Commands - -The `cluster` command group (also aliased as `k`) manages your Kubernetes cluster lifecycle: - -```bash -# List all managed clusters -openframe cluster list - -# Get detailed status of a cluster -openframe cluster status - -# Create an additional cluster with a custom name -openframe cluster create my-second-cluster - -# Delete a cluster -openframe cluster delete my-second-cluster - -# Reclaim disk space by pruning unused container images on cluster nodes -openframe cluster cleanup -``` - -> **Shorthand:** `openframe k list` is equivalent to `openframe cluster list`. - ---- - -## 4. Explore the App Commands - -The `app` command group manages the OpenFrame platform deployment: - -```bash -# Install OpenFrame on an existing cluster -openframe app install - -# Check application status -openframe app status - -# Upgrade to a different OpenFrame version/branch -openframe app upgrade - -# Uninstall OpenFrame from a cluster -openframe app uninstall - -# Show access details -openframe app access -``` - -### Upgrading OpenFrame - -There are two upgrade modes: - -```bash -# Mode 1: Switch to a different git ref (branch, tag, or commit) -openframe app upgrade --ref v2.0.0 - -# Mode 2: Force re-sync of the current ref -openframe app upgrade --force-sync -``` - ---- - -## 5. Keep the CLI Up to Date - -OpenFrame CLI includes a built-in self-update mechanism with cryptographic verification: - -```bash -# Check if an update is available -openframe update --check - -# Apply the latest update -openframe update - -# Roll back to the previous version if needed -openframe update --rollback -``` - -> **Security note:** All updates are verified using [Sigstore/cosign](https://docs.sigstore.dev/cosign/overview/) against the official GitHub Actions release workflow. Only binaries produced by the `flamingo-stack/openframe-cli` release pipeline are accepted. - ---- - -## Initial Configuration: The Helm Values File - -When you ran `openframe bootstrap`, a configuration file called `openframe-helm-values.yaml` was created in your working directory. This file controls the OpenFrame platform deployment: - -```bash -# View the generated configuration -cat openframe-helm-values.yaml -``` - -Key configurable areas include: - -| Section | Description | -|---|---| -| `branch` | The OpenFrame git ref (branch, tag) to deploy | -| `docker` | Container registry settings | -| `ingress` | Ingress hostname and TLS configuration | -| `argocd` | ArgoCD Helm value overrides | - -To apply changes to an existing deployment: - -```bash -openframe app upgrade -``` - ---- - -## Verbose and Silent Modes - -Control the CLI's output verbosity: - -```bash -# Show detailed debug output (ArgoCD sync events, Helm operations, etc.) -openframe bootstrap --verbose - -# Suppress all non-error output (perfect for scripts) -openframe bootstrap --silent - -# Machine-readable output for cluster commands -openframe cluster list --output json -``` - ---- - -## Running in CI/CD - -For automated pipelines, use `--non-interactive` to skip all prompts: - -```bash -# Full non-interactive bootstrap -openframe bootstrap --non-interactive - -# With a specific cluster name -openframe bootstrap --non-interactive my-ci-cluster -``` - -The CLI reads from an existing `openframe-helm-values.yaml` file in the current directory when running non-interactively. - ---- - -## Getting Help - -Every command has built-in help: - -```bash -# General help -openframe --help - -# Help for a specific command -openframe bootstrap --help -openframe cluster create --help -openframe app install --help -openframe update --help -``` - ---- - -## Community & Support - -- **OpenMSP Slack:** [https://www.openmsp.ai/](https://www.openmsp.ai/) โ€” Join for help, discussions, and announcements -- **Slack invite:** [https://join.slack.com/t/openmsp/shared_invite/zt-36bl7mx0h-3~U2nFH6nqHqoTPXMaHEHA](https://join.slack.com/t/openmsp/shared_invite/zt-36bl7mx0h-3~U2nFH6nqHqoTPXMaHEHA) -- **OpenFrame platform repo:** [https://github.com/flamingo-stack/openframe-oss-tenant](https://github.com/flamingo-stack/openframe-oss-tenant) -- **CLI releases:** [https://github.com/flamingo-stack/openframe-cli/releases](https://github.com/flamingo-stack/openframe-cli/releases) - ---- - -## Summary: First Steps Checklist - -- [ ] Verified cluster status with `openframe cluster status` -- [ ] Checked app status with `openframe app status` -- [ ] Accessed the platform with `openframe app access` -- [ ] Explored `openframe cluster --help` and `openframe app --help` -- [ ] Reviewed `openframe-helm-values.yaml` configuration file -- [ ] Ran `openframe update --check` to see if a newer version is available -- [ ] Joined the [OpenMSP Slack](https://www.openmsp.ai/) community diff --git a/docs/getting-started/gke-workflow.md b/docs/getting-started/gke-workflow.md deleted file mode 100644 index d97687c7..00000000 --- a/docs/getting-started/gke-workflow.md +++ /dev/null @@ -1,173 +0,0 @@ -# GKE Workflow โ€” from zero to a running cluster - -The complete, in-order flow for working with Google Kubernetes Engine through -the OpenFrame CLI. You need: the `openframe` binary, a Google account with -access to a GCP project, and a browser for the login. Everything else โ€” -Terraform, gcloud, the auth plugin, credentials โ€” the CLI sets up itself. - -> **Costs.** A GKE cluster bills real money: a cluster management fee, VM -> nodes, and networking โ€” see the -> [GKE pricing page](https://cloud.google.com/kubernetes-engine/pricing). -> In an interactive `--dry-run`, the CLI offers to install -> [infracost](https://www.infracost.io) (verified pinned download) and then -> shows a monthly estimate โ€” even the one-time free -> `infracost auth login` is offered right inside the CLI. The CLI warns before creating and requires re-typing the -> cluster name before deleting. - -## 0. (Optional) Preview what would be created - -A dry-run computes a real `terraform plan` without creating anything and -without registering the cluster: - -```bash -openframe cluster create my-gke --type gke \ - --project my-project --region us-central1 --skip-wizard --dry-run -# + module.network.module.vpc.google_compute_network.network -# + module.network.module.subnets.google_compute_subnetwork.subnetwork["us-central1/my-gke-subnet"] -# + module.gke.google_container_cluster.primary -# + module.gke.google_container_node_pool.pools["default"] -# ... -# Plan: 11 to add, 0 to change, 0 to destroy -``` - -If Terraform is not installed yet, the preview is skipped with a note โ€” it -installs automatically on a real create. - -## 1. Create the cluster - -One command; interactive wizard or flags. - -**Wizard** (prompts: name โ†’ type `gke` โ†’ project โ†’ region โ†’ instance type โ†’ -node count โ†’ confirmation with a cost warning): - -```bash -openframe cluster create -``` - -**Flags** (defaults: `e2-standard-4`, 3 nodes, latest GKE version): - -```bash -openframe cluster create my-gke --type gke \ - --project my-project --region us-central1 --skip-wizard -``` - -Useful extras: `--machine-type e2-standard-8`, `--min-nodes 1 --max-nodes 6`, -`--spot` (spot-capacity nodes, typically 60โ€“90% off the node cost โ€” good for -test clusters), `--version 1.33`, `--nodes 4`, `--ha` (regional control plane -and nodes โ€” note that a regional node count is **per zone**: `--nodes 3 --ha` -provisions 3 ร— 3 zones = 9 nodes, and the CLI displays exactly that math). - -What happens, in order โ€” no manual steps in between: - -1. **Tools**: terraform (pinned, checksum-verified, into `~/.openframe/bin`), - gcloud, and `gke-gcloud-auth-plugin` are checked and installed if missing. -2. **Login**: if gcloud is not authenticated, the CLI offers to run - `gcloud auth login` right there (browser opens); then, because Terraform - uses Application Default Credentials, it offers - `gcloud auth application-default login` too. CI/non-interactive sessions - never get prompts โ€” they fail with the exact command to run. -3. **Preflight**: project access is verified; the required project APIs - (Compute Engine, Kubernetes Engine) are enabled with one idempotent - `gcloud services enable` โ€” deliberately outside Terraform, so no cluster's - state owns project-level toggles in a shared project; and the CLI refuses - to proceed if a cluster with this name already exists in the project but - was not created by openframe (it will never touch clusters it does not - own). -4. **Plan & confirm** (interactive sessions): the full Terraform plan is - shown โ€” every resource to be created and the summary line โ€” and you are - asked to approve it before anything is applied, exactly like - `terraform apply`. What you approve is what runs (the saved plan is - applied, not a re-plan). Declining a brand-new create leaves no trace. - Non-interactive sessions auto-approve, as before. -5. **Provision** (~10โ€“15 min): a dedicated VPC with pod/service ranges, a - Cloud NAT for egress, and a **zonal** GKE cluster (regional with `--ha`) - with **private nodes** (no external IPs โ€” compatible with orgs enforcing - `restrict_vm_external_ips`) behind a public control-plane endpoint, - streamed as per-resource progress lines. Add `--verbose` for raw - Terraform output; the full stream is also appended to - `~/.openframe/clusters/my-gke/terraform/terraform.log` either way. -6. **Kubeconfig**: a context named exactly `my-gke` is merged into your - kubeconfig (existing contexts are never overwritten) and made current. - -## 2. Verify - -```bash -kubectl get nodes # exec-auth via gke-gcloud-auth-plugin -openframe cluster status my-gke -openframe cluster list -``` - -## 3. Install OpenFrame onto it - -```bash -openframe app install # targets the current kubectl context -openframe app status -openframe app access # ArgoCD URL + credentials -``` - -## 4. Day-2 operations - -```bash -# See everything: local k3d + openframe-managed + EXTERNAL clusters -# discovered in the GCP projects of your gcloud configurations -openframe cluster list --all -# NAME TYPE SOURCE STATUS NODES CONTEXT PROJECT CREATED -# my-gke gke openframe Ready 3 my-gke my-project 2026-07-21 14:02 -# tenant-cluster-1 gke external Running 3 connectgateway_..._tenant-... tenant-runners-db9z โ€” - -# Switch kubectl (and the matching gcloud configuration) to any of them: -openframe cluster use my-gke -openframe cluster use tenant-cluster-1 # external: credentials are fetched - # via gcloud if not present yet -``` - -External clusters are strictly **read-only** for openframe: they show up in -`list --all`/`status`/`use`, but `delete` and `cleanup` refuse them. - -## 5. If a create fails or is interrupted - -The workspace and Terraform state under `~/.openframe/clusters/my-gke/` are -kept โ€” they are the only pointer to the billed resources. Two options: - -```bash -openframe cluster create my-gke --type gke \ - --project my-project --region us-central1 --skip-wizard # resumes -openframe cluster delete my-gke # tears down -``` - -Want the state to survive your machine? Create with -`--backend-config gcs://my-bucket/clusters/my-gke` (remote state in GCS). - -## 6. Delete - -```bash -openframe cluster delete my-gke -# โ†’ asks you to re-type "my-gke", then: -# 1. app namespaces are deleted first, so the CSI driver reclaims the -# PVC-backed Persistent Disks while the nodes still run (those disks -# live outside the terraform state and would otherwise survive as -# billable orphans) -# 2. terraform destroy removes the cluster, node pool, and VPC -# 3. any disk that still survived is swept up: listed, and deleted with -# your consent (interactive prompt, or automatically with --force) -# 4. the workspace and kubeconfig context are cleaned up -``` - -`--force` skips the typed confirmation and consents to the orphan-disk -cleanup (CI). `cluster cleanup` does not apply to cloud clusters โ€” use -`delete`. - -## Troubleshooting - -| Symptom | What to do | -| --- | --- | -| "gcloud is not authenticated" (CI) | run `gcloud auth login` and `gcloud auth application-default login` in an interactive session | -| "project ... is not accessible" | check the project ID and your IAM role (`gcloud projects describe `) | -| "already exists ... not managed by openframe" | the name is taken by a cluster openframe does not own โ€” pick another name | -| "kubeconfig context ... refusing to overwrite" | a same-named context points elsewhere โ€” rename it or pick another cluster name | -| create failed mid-way | re-run the same create to resume, or delete to tear down (state is never lost) | -| want Terraform's own logs | add `--verbose`, or read `~/.openframe/clusters//terraform/terraform.log` โ€” every apply/destroy appends its full stream there | - -See [Cloud Clusters](./cloud-clusters.md) for the reference (flags, state -model, EKS status) and `docs/architecture/decisions.md` (D5, D7, D8) for the -design rationale. diff --git a/docs/getting-started/introduction.md b/docs/getting-started/introduction.md deleted file mode 100644 index bd9d1db2..00000000 --- a/docs/getting-started/introduction.md +++ /dev/null @@ -1,141 +0,0 @@ -# Introduction to OpenFrame CLI - -**OpenFrame CLI** is a modern, interactive command-line tool written in Go that bootstraps and manages OpenFrame Kubernetes environments. With a single `openframe` binary, you can provision local K3D clusters, install the full OpenFrame platform stack via ArgoCD GitOps, and manage the entire lifecycle of your deployment โ€” all through both guided interactive wizards and fully scriptable non-interactive modes. - -> **OpenFrame** is the unified platform from [Flamingo](https://flamingo.run) that integrates multiple MSP tools into a single AI-driven interface, automating IT support operations across the stack. Learn more at [openframe.ai](https://openframe.ai). - ---- - -## What is OpenFrame CLI? - -OpenFrame CLI is your single entry point for: - -- **Bootstrapping** a fully functional OpenFrame environment from scratch in minutes -- **Managing Kubernetes clusters** (K3D) โ€” create, delete, list, inspect, and clean up -- **Deploying and upgrading** the OpenFrame platform chart via ArgoCD GitOps -- **Checking and installing prerequisites** automatically (Docker, k3d, Helm) -- **Self-updating** to the latest version with cryptographic signature verification - -It replaces manual shell scripts and disparate tooling with a cohesive, type-safe Go binary that provides real-time progress feedback, friendly error messages, and deep automation support. - ---- - -## Key Features - -| Feature | Description | -|---|---| -| **One-command bootstrap** | `openframe bootstrap` provisions a cluster and deploys the full platform in one step | -| **Interactive wizards** | Step-by-step guided prompts for new users โ€” no YAML editing required | -| **Non-interactive / CI mode** | `--non-interactive` flag makes every command scriptable for pipelines | -| **ArgoCD GitOps integration** | Platform deployment is fully GitOps-driven using the `openframe-oss-tenant` chart | -| **Auto-prerequisite management** | Detects and installs Docker, k3d, and Helm automatically on macOS/Linux | -| **Cosign signature verification** | All self-updates are cryptographically verified against the official release workflow | -| **WSL2 support on Windows** | Transparently re-executes inside WSL2 โ€” no manual Linux setup needed | -| **Secret redaction** | Credentials and tokens are automatically scrubbed from all debug output | -| **Machine-readable output** | `--output json/yaml` for clean scripted consumption | - ---- - -## Target Audience - -OpenFrame CLI is designed for: - -- **MSP technicians and operators** setting up OpenFrame environments -- **DevOps engineers** automating OpenFrame deployment in CI/CD pipelines -- **Developers** contributing to or extending the OpenFrame platform -- **System administrators** managing the lifecycle of OpenFrame Kubernetes clusters - ---- - -## High-Level Architecture - -```mermaid -graph TB - subgraph User["User Interface"] - cli["openframe binary"] - wizard["Interactive Wizard"] - flags["--flag automation"] - end - - subgraph Commands["Command Layer"] - bootstrap["bootstrap"] - cluster["cluster (create/delete/list/status)"] - app["app (install/upgrade/status/uninstall)"] - prereq["prerequisites (check/install)"] - update["update (self-update/rollback)"] - end - - subgraph Platform["OpenFrame Platform"] - k3d["K3D Kubernetes Cluster"] - argocd["ArgoCD GitOps Engine"] - openframe["OpenFrame OSS Tenant Chart"] - end - - cli --> Commands - wizard --> Commands - flags --> Commands - bootstrap --> k3d - bootstrap --> argocd - argocd --> openframe - cluster --> k3d - app --> argocd -``` - ---- - -## How It Works - -The CLI follows a layered architecture: - -1. **Command Layer** (`cmd/`) โ€” Cobra-based subcommands with flag parsing and interactive wizards -2. **Service Layer** (`internal/*/service.go`) โ€” Business logic orchestration -3. **Provider Layer** (`internal/*/providers/`) โ€” K3D, ArgoCD, Helm, and Git integrations -4. **Shared Infrastructure** โ€” Executor, k8s client, UI rendering, error handling, and secret redaction - -The **bootstrap** workflow ties it all together: - -```mermaid -sequenceDiagram - participant User - participant CLI as "openframe bootstrap" - participant K3D as "K3D Cluster" - participant ArgoCD as "ArgoCD" - participant OpenFrame as "OpenFrame Platform" - - User->>CLI: openframe bootstrap - CLI->>CLI: Validate prerequisites - CLI->>K3D: Create local cluster - K3D-->>CLI: Cluster ready - CLI->>ArgoCD: Install via Helm - ArgoCD-->>CLI: ArgoCD ready - CLI->>OpenFrame: Deploy app-of-apps chart - OpenFrame-->>CLI: All apps Healthy + Synced - CLI-->>User: Bootstrap complete! -``` - ---- - -## External Repository - -The OpenFrame platform configuration (Helm charts, values) lives in a separate repository: - -- **openframe-oss-tenant**: [https://github.com/flamingo-stack/openframe-oss-tenant](https://github.com/flamingo-stack/openframe-oss-tenant) -- Documentation: [https://github.com/flamingo-stack/openframe-oss-tenant/tree/main/docs](https://github.com/flamingo-stack/openframe-oss-tenant/tree/main/docs) - ---- - -## Community & Support - -Join the OpenMSP Slack community for questions, discussions, and support: - -https://www.openmsp.ai/ - -[![OpenMSP Slack](https://img.shields.io/badge/Slack-OpenMSP-blue)](https://join.slack.com/t/openmsp/shared_invite/zt-36bl7mx0h-3~U2nFH6nqHqoTPXMaHEHA) - ---- - -## Next Steps - -- Follow the [Prerequisites Guide](prerequisites.md) to prepare your environment -- Jump straight to the [Quick Start Guide](quick-start.md) for a 5-minute setup -- Read the [First Steps Guide](first-steps.md) to explore key features after installation diff --git a/docs/getting-started/prerequisites.md b/docs/getting-started/prerequisites.md deleted file mode 100644 index ab431ccc..00000000 --- a/docs/getting-started/prerequisites.md +++ /dev/null @@ -1,186 +0,0 @@ -# Prerequisites - -Before installing and using the OpenFrame CLI, ensure your environment meets the following requirements. The CLI can automatically check and install most prerequisites on macOS and Linux โ€” on Windows, you will be guided to the relevant documentation. - ---- - -## System Requirements - -| Resource | Minimum | Recommended | -|---|---|---| -| **RAM** | 24 GB | 32 GB | -| **CPU Cores** | 6 cores | 12 cores | -| **Disk Space** | 50 GB free | 100 GB free | -| **Operating System** | macOS, Linux, Windows (WSL2) | macOS or Linux | - -> **Windows users:** The OpenFrame CLI runs natively on Windows but automatically forwards all operations into WSL2 (Windows Subsystem for Linux). You must have WSL2 installed and a Linux distro configured. The CLI will auto-install itself inside WSL when first run. - ---- - -## Required Software - -| Tool | Minimum Version | Purpose | Auto-Installed? | -|---|---|---|---| -| **Docker** | 24.x or newer | Container runtime for K3D clusters | โœ… macOS/Linux | -| **k3d** | 5.x or newer | Lightweight K3D cluster manager | โœ… macOS/Linux | -| **Helm** | 3.x or newer | Kubernetes package manager | โœ… macOS/Linux | -| **kubectl** | 1.28+ | Kubernetes CLI (optional โ€” CLI uses client-go directly) | โŒ Manual | -| **WSL2** *(Windows only)* | Windows 10/11 | Linux environment on Windows | โŒ Manual | - -> **Note:** Docker, k3d, and Helm can be installed automatically by running `openframe prerequisites install`. On Windows, the CLI will display documentation links for each missing tool. - ---- - -## Operating System Details - -### macOS - -- macOS 12 (Monterey) or newer recommended -- [Docker Desktop for Mac](https://docs.docker.com/desktop/mac/install/) or [OrbStack](https://orbstack.dev/) required -- Homebrew is recommended for manual tool management - -### Linux - -- Ubuntu 20.04+, Debian 11+, Fedora 36+, or any modern distribution -- Docker Engine (not just the CLI) must be running -- User must be in the `docker` group or have `sudo` access - -### Windows (via WSL2) - -- Windows 10 version 2004+ or Windows 11 -- WSL2 enabled: run `wsl --install` in PowerShell as Administrator -- A Linux distro installed (Ubuntu recommended): `wsl --install -d Ubuntu` -- Docker Desktop for Windows with WSL2 backend enabled - ---- - -## Account & Access Requirements - -| Requirement | Details | -|---|---| -| **GitHub Access** | Required for downloading the `openframe-oss-tenant` chart and for self-updates | -| **GitHub Token** *(optional)* | Set `OPENFRAME_GITHUB_TOKEN` or `GITHUB_TOKEN` to avoid rate limiting | -| **Internet Access** | Required for downloading charts, container images, and updates | - ---- - -## Environment Variables - -The following environment variables are recognized by the CLI: - -| Variable | Required | Description | -|---|---|---| -| `OPENFRAME_GITHUB_TOKEN` | Optional | GitHub personal access token (avoids API rate limits) | -| `GITHUB_TOKEN` | Optional | Standard GitHub token (also accepted) | -| `OPENFRAME_WSL_DISTRO` | Windows only | Target WSL distro name (default: WSL default distro) | -| `OPENFRAME_NO_WSL_FORWARD` | Windows only | Disable WSL forwarding (unsupported; use at your own risk) | -| `OPENFRAME_UPDATE_INSECURE_SKIP_VERIFY` | Emergency only | Skip cosign signature verification during updates | -| `KUBECONFIG` | Optional | Path to kubeconfig file (default: `~/.kube/config`) | - ---- - -## Verification Commands - -Run these commands to verify your environment is ready before installing OpenFrame CLI: - -### Check Docker - -```bash -docker --version -docker ps -``` - -Expected output: Docker version and an empty container list (confirms Docker is running). - -### Check k3d - -```bash -k3d version -``` - -Expected output: `k3d version vX.Y.Z` - -### Check Helm - -```bash -helm version -``` - -Expected output: `version.BuildInfo{Version:"vX.Y.Z", ...}` - -### Check available memory - -```bash -# macOS -sysctl -n hw.memsize | awk '{print $1/1024/1024/1024 " GB"}' - -# Linux -free -h -``` - -Ensure at least 24 GB RAM is available. - -### Check disk space - -```bash -df -h . -``` - -Ensure at least 50 GB free on the relevant partition. - -### Run CLI prerequisite check (after installing OpenFrame CLI) - -```bash -openframe prerequisites check -``` - -This is the most comprehensive check โ€” the CLI will display exactly what is missing and how to fix it. - ---- - -## Windows-Specific Setup - -
-Expand Windows WSL2 Setup Steps - -**Step 1: Enable WSL2** - -Open PowerShell as Administrator and run: - -```bash -wsl --install -``` - -**Step 2: Install Ubuntu distro** - -```bash -wsl --install -d Ubuntu -``` - -**Step 3: Install Docker Desktop** - -Download and install [Docker Desktop for Windows](https://docs.docker.com/desktop/windows/install/). In Docker Desktop settings, enable: -- **WSL2 backend** (Settings โ†’ General โ†’ Use WSL2 based engine) -- **Ubuntu integration** (Settings โ†’ Resources โ†’ WSL Integration โ†’ Ubuntu) - -**Step 4: Set WSL2 as default (if needed)** - -```bash -wsl --set-default-version 2 -wsl --set-default Ubuntu -``` - -**Step 5: Download the Windows CLI binary** - -Download from: https://github.com/flamingo-stack/openframe-cli/releases/latest/download/openframe-cli_windows_amd64.zip - -Extract and run the `.exe` โ€” the CLI will automatically forward into WSL2. - -
- ---- - -## Next Steps - -- Proceed to the [Quick Start Guide](quick-start.md) to install and run OpenFrame CLI -- Return to the [Introduction](introduction.md) for a feature overview diff --git a/docs/getting-started/quick-start.md b/docs/getting-started/quick-start.md deleted file mode 100644 index 0c778e41..00000000 --- a/docs/getting-started/quick-start.md +++ /dev/null @@ -1,204 +0,0 @@ -# Quick Start Guide - -Get OpenFrame up and running in under 5 minutes. This guide covers the fastest path to a working OpenFrame environment. - ---- - -## TL;DR โ€” 5-Minute Setup - -```bash -# 1. Download the CLI for your platform (see below) -# 2. Check prerequisites -openframe prerequisites check - -# 3. Bootstrap a full OpenFrame environment -openframe bootstrap -``` - -That's it. The `bootstrap` command creates a local K3D cluster, installs ArgoCD, deploys the full OpenFrame platform, and waits for everything to become healthy. - ---- - -## Step 1: Download the OpenFrame CLI - -Choose your platform: - -### macOS (Apple Silicon / Intel) - -```bash -# Apple Silicon (M1/M2/M3) -curl -L https://github.com/flamingo-stack/openframe-cli/releases/latest/download/openframe-cli_darwin_arm64.tar.gz | tar xz -sudo mv openframe /usr/local/bin/ - -# Intel -curl -L https://github.com/flamingo-stack/openframe-cli/releases/latest/download/openframe-cli_darwin_amd64.tar.gz | tar xz -sudo mv openframe /usr/local/bin/ -``` - -### Linux (amd64) - -```bash -curl -L https://github.com/flamingo-stack/openframe-cli/releases/latest/download/openframe-cli_linux_amd64.tar.gz | tar xz -sudo mv openframe /usr/local/bin/ -``` - -### Windows (amd64) - -Download: https://github.com/flamingo-stack/openframe-cli/releases/latest/download/openframe-cli_windows_amd64.zip - -Extract the ZIP archive and run the `openframe.exe` โ€” it will automatically forward commands into WSL2. - -### Browse All Releases - -Visit [https://github.com/flamingo-stack/openframe-cli/releases](https://github.com/flamingo-stack/openframe-cli/releases) for all available platform binaries. - ---- - -## Step 2: Verify Installation - -```bash -openframe --version -``` - -Expected output: - -```text -openframe version v1.x.x (abc1234) built on 2024-xx-xx -``` - ---- - -## Step 3: Check Prerequisites - -```bash -openframe prerequisites check -``` - -The CLI will inspect your environment and report the status of required tools: - -```text -โœ“ Docker - running -โœ“ k3d - v5.x.x -โœ“ Helm - v3.x.x -``` - -If any prerequisites are missing, install them automatically: - -```bash -openframe prerequisites install -``` - -> **Windows users:** Auto-install is not supported on native Windows. The CLI will display documentation links for each missing tool instead. - ---- - -## Step 4: Bootstrap OpenFrame - -Run the interactive bootstrap wizard: - -```bash -openframe bootstrap -``` - -The wizard will guide you through: - -1. **Cluster name** โ€” the name for your local K3D cluster (default: `openframe-dev`) -2. **Configuration mode** โ€” default settings or interactive customization -3. **Branch/version** โ€” which OpenFrame release to deploy - -To use all defaults without prompts (e.g. in CI): - -```bash -openframe bootstrap --non-interactive -``` - -### Expected Output - -```text - ___ ___ - / _ \ _ __ ___ _ __| _|_ __ __ _ _ __ ___ ___ -| | | | '_ \ / _ \ '_ \ |_| '__/ _` | '_ ` _ \ / _ \ -| |_| | |_) | __/ | | | _| | | (_| | | | | | | __/ - \___/| .__/ \___|_| |_|_| |_| \__,_|_| |_| |_|\___| - |_| - -โœ“ Prerequisites validated -โœ“ Creating cluster: openframe-dev -โœ“ Cluster ready -โœ“ Installing ArgoCD -โœ“ ArgoCD ready -โœ“ Deploying OpenFrame platform -โœ“ Waiting for applications... -โœ“ All applications Healthy + Synced - -Bootstrap complete! ๐ŸŽ‰ -``` - ---- - -## Step 5: Check Status - -After bootstrapping, verify everything is running: - -```bash -openframe app status -``` - -```bash -openframe cluster status -``` - ---- - -## What Was Installed? - -After a successful `openframe bootstrap`, you have: - -| Component | Description | -|---|---| -| **K3D cluster** | A local lightweight Kubernetes cluster named `openframe-dev` | -| **ArgoCD** | GitOps continuous delivery engine managing your platform | -| **OpenFrame platform** | The full OSS tenant chart from [openframe-oss-tenant](https://github.com/flamingo-stack/openframe-oss-tenant) | - ---- - -## Common Next Actions - -After bootstrap completes, you may want to: - -```bash -# View all available commands -openframe --help - -# Check cluster list -openframe cluster list - -# Get access information -openframe app access - -# Upgrade to a new OpenFrame version -openframe app upgrade - -# Keep the CLI itself up to date -openframe update -``` - ---- - -## Troubleshooting Quick Fixes - -| Problem | Solution | -|---|---| -| `docker: command not found` | Install Docker: `openframe prerequisites install` | -| `connection refused` | Check `openframe cluster status` โ€” the cluster may not be running | -| `context deadline exceeded` | Network/resource issue; wait and retry, or check system resources | -| Missing `openframe-helm-values.yaml` | The bootstrap wizard will create it for you in non-interactive mode | -| Permission denied on binary | `chmod +x /usr/local/bin/openframe` | - ---- - -## Next Steps - -- Read the [First Steps Guide](first-steps.md) for what to explore after your first bootstrap -- Review the [Prerequisites Guide](prerequisites.md) if you encounter environment issues -- Visit the [OpenMSP community](https://www.openmsp.ai/) for help and discussion diff --git a/docs/reference/architecture/.gitignore b/docs/reference/architecture/.gitignore deleted file mode 100644 index b4f4c7a6..00000000 --- a/docs/reference/architecture/.gitignore +++ /dev/null @@ -1,8 +0,0 @@ -# CodeWiki temp files (dependency graphs can be 7GB+) -temp/ -dependency_graphs/ - -# JSON intermediate files (except schema/config) -*.json -!*-schema.json -!*-config.json diff --git a/docs/reference/architecture/overview.md b/docs/reference/architecture/overview.md deleted file mode 100644 index 47484830..00000000 --- a/docs/reference/architecture/overview.md +++ /dev/null @@ -1,473 +0,0 @@ -# openframe-cli Module Documentation - -# OpenFrame CLI โ€” Architecture Documentation - -## Overview - -OpenFrame CLI is a modern, interactive command-line tool written in Go that bootstraps and manages OpenFrame Kubernetes environments. It orchestrates the full lifecycle of local K3D clusters, installs the OpenFrame platform via ArgoCD GitOps (using the public `openframe-oss-tenant` chart), and provides developer utilities โ€” all from a single `openframe` binary with both interactive wizards and fully scriptable non-interactive modes. - ---- - -## Architecture - -### High-Level System Design - -```mermaid -graph TB - subgraph CLI["CLI Entry Point"] - main["main.go"] - root["cmd/root.go"] - end - - subgraph Commands["Command Layer"] - bootstrap["cmd/bootstrap"] - cluster_cmd["cmd/cluster"] - app_cmd["cmd/app"] - prereq_cmd["cmd/prerequisites"] - update_cmd["cmd/update"] - end - - subgraph Core["Core Services"] - bootstrap_svc["internal/bootstrap"] - cluster_svc["internal/cluster"] - chart_svc["internal/chart/services"] - prereq_fw["internal/prerequisites"] - selfupdate["internal/shared/selfupdate"] - end - - subgraph Providers["Providers"] - k3d_prov["cluster/providers/k3d"] - argocd_prov["chart/providers/argocd"] - helm_prov["chart/providers/helm"] - git_prov["chart/providers/git"] - end - - subgraph Shared["Shared Infrastructure"] - executor["internal/shared/executor"] - k8s["internal/k8s"] - download["internal/shared/download"] - ui["internal/shared/ui"] - redact["internal/shared/redact"] - errors["internal/shared/errors"] - end - - subgraph External["External Tools & APIs"] - k3d_tool["K3D CLI"] - helm_tool["Helm CLI"] - argocd_cr["ArgoCD CRDs"] - github["GitHub API"] - git_repo["Git Repositories"] - end - - main --> root - root --> Commands - bootstrap_cmd --> bootstrap_svc - cluster_cmd --> cluster_svc - app_cmd --> chart_svc - prereq_cmd --> prereq_fw - update_cmd --> selfupdate - - bootstrap_svc --> cluster_svc - bootstrap_svc --> chart_svc - - cluster_svc --> k3d_prov - chart_svc --> argocd_prov - chart_svc --> helm_prov - chart_svc --> git_prov - - k3d_prov --> executor - helm_prov --> executor - argocd_prov --> k8s - helm_prov --> k8s - - executor --> k3d_tool - executor --> helm_tool - argocd_prov --> argocd_cr - git_prov --> git_repo - selfupdate --> github - download --> github -``` - ---- - -## Core Components - -| Package | Path | Responsibility | -|---|---|---| -| **Root Command** | `cmd/root.go` | Cobra root; wires subcommands, global flags (`--verbose`, `--silent`), version info, WSL launcher | -| **Bootstrap Command** | `cmd/bootstrap/` | Orchestrates `cluster create` + `app install` as a single user-facing workflow | -| **Cluster Commands** | `cmd/cluster/` | Cobra subcommands: create, delete, list, status, cleanup | -| **App Commands** | `cmd/app/` | Cobra subcommands: install, upgrade, status, access, uninstall | -| **Prerequisites Command** | `cmd/prerequisites/` | Exposes `check` / `install` for Docker, k3d, helm | -| **Update Command** | `cmd/update/` | Self-update, rollback, update-check with cosign signature verification | -| **Bootstrap Service** | `internal/bootstrap/` | Coordinates cluster creation then chart installation end-to-end | -| **Cluster Service** | `internal/cluster/service.go` | Lifecycle operations (create, delete, list, status, cleanup) via the provider interface | -| **K3D Provider** | `internal/cluster/providers/k3d/` | K3D-specific cluster creation and management | -| **Cluster Provider Interface** | `internal/cluster/provider/` | Unified `Provider` interface; K3D satisfies it today | -| **Chart Services** | `internal/chart/services/` | High-level install workflow: prerequisites โ†’ ArgoCD โ†’ app-of-apps โ†’ wait | -| **ArgoCD Provider** | `internal/chart/providers/argocd/` | Install, wait, refresh/sync, application management via native client-go dynamic client | -| **Helm Provider** | `internal/chart/providers/helm/` | Helm CLI wrapper; ArgoCD and app-of-apps installation | -| **Git Provider** | `internal/chart/providers/git/` | Shallow clone of chart repository using go-git (no `git` binary) | -| **App Status Service** | `internal/app/status/` | Aggregates cluster health + ArgoCD app status into a unified Report | -| **App Uninstall Service** | `internal/app/uninstall/` | Removes ArgoCD applications and Helm releases safely | -| **App Target Selector** | `internal/app/target/` | Interactive/non-interactive kube-context selection with resource check | -| **k8s Package** | `internal/k8s/` | Kubeconfig context loading, `rest.Config` construction, cluster health/resource checks | -| **Prerequisites Framework** | `internal/prerequisites/` | OS-aware check + auto-install runner (macOS/Linux auto-installs, Windows shows docs) | -| **Cluster Prerequisites** | `internal/cluster/prerequisites/` | Docker, k3d, helm prerequisite definitions and installer | -| **Chart Prerequisites** | `internal/chart/prerequisites/` | Helm, mkcert/certificates, memory prerequisite definitions | -| **Executor** | `internal/shared/executor/` | Command execution abstraction (real + mock); records argv for security testing | -| **Self-Update** | `internal/shared/selfupdate/` | GitHub release fetch, cosign signature verification, binary swap, rollback | -| **Download** | `internal/shared/download/` | Verified binary downloads (SHA256 + pinned versions) for k3d, mkcert, helm | -| **Redact** | `internal/shared/redact/` | Secret redaction from log/debug output | -| **WSL Launcher** | `internal/shared/wsllauncher/` | Re-runs the CLI inside WSL on Windows; auto-installs the Linux binary | -| **Platform** | `internal/platform/` | Host OS detection, per-tool install hints, WSL guidance errors | -| **Shared UI** | `internal/shared/ui/` | Logo, prompts, silent mode, status colors, selection menus | -| **Shared Config** | `internal/shared/config/` | `EnvBool`, TLS config for local clusters, system service | -| **Shared Errors** | `internal/shared/errors/` | Error types, friendly hints, retry policies, `AlreadyHandledError` sentinel | - ---- - -## Component Relationships - -### Dependency Flowchart - -```mermaid -graph LR - subgraph Commands["cmd/"] - bootstrap["bootstrap"] - cluster_cmd["cluster/*"] - app_cmd["app/*"] - prereq_cmd["prerequisites"] - update_cmd["update"] - end - - subgraph Services["internal/"] - bsvc["bootstrap.Service"] - csvc["cluster.ClusterService"] - chsvc["chart/services.ChartService"] - appsvc["app/status + uninstall"] - prefw["prerequisites.Runner"] - supdater["selfupdate.Updater"] - end - - subgraph Providers["Providers"] - k3dp["cluster/providers/k3d"] - argop["chart/providers/argocd.Manager"] - helmp["chart/providers/helm.HelmManager"] - gitp["chart/providers/git.Repository"] - end - - subgraph Infra["Shared Infrastructure"] - exec["executor.CommandExecutor"] - k8spkg["k8s (rest.Config, Accessor)"] - dlpkg["download.Downloader"] - uipkg["shared/ui"] - errpkg["shared/errors"] - redactpkg["shared/redact"] - end - - bootstrap --> bsvc - cluster_cmd --> csvc - app_cmd --> chsvc - app_cmd --> appsvc - prereq_cmd --> prefw - update_cmd --> supdater - - bsvc --> csvc - bsvc --> chsvc - - csvc --> k3dp - chsvc --> argop - chsvc --> helmp - chsvc --> gitp - appsvc --> argop - - k3dp --> exec - helmp --> exec - argop --> k8spkg - helmp --> k8spkg - - prefw --> dlpkg - supdater --> dlpkg - - exec --> redactpkg - errpkg --> uipkg - chsvc --> errpkg - csvc --> errpkg -``` - ---- - -## Data Flow - -### Bootstrap Sequence Diagram - -```mermaid -sequenceDiagram - participant User - participant CLI as "openframe bootstrap" - participant BSvc as "bootstrap.Service" - participant CSvc as "cluster.Service" - participant K3D as "K3D Provider" - participant ChSvc as "chart/services" - participant Helm as "HelmManager" - participant Git as "git.Repository" - participant ArgoCD as "argocd.Manager" - participant K8s as "Kubernetes API" - - User->>CLI: openframe bootstrap [name] - CLI->>BSvc: Execute(cmd, args) - BSvc->>ChSvc: ValidateHelmValuesFile() - ChSvc-->>BSvc: OK / error - - BSvc->>CSvc: CreateClusterWithPrerequisites(ctx, name) - CSvc->>K3D: CreateCluster(ctx, config) - K3D-->>CSvc: rest.Config - CSvc-->>BSvc: rest.Config - - BSvc->>ChSvc: InstallChartsWithConfigContext(ctx, req) - ChSvc->>ChSvc: CheckAndInstallPrerequisites() - ChSvc->>Helm: InstallArgoCDWithProgress(ctx, cfg) - Helm->>K8s: helm upgrade --install argo-cd - K8s-->>Helm: OK - Helm->>K8s: waitForArgoCDDeployments() - K8s-->>Helm: Deployments ready - - ChSvc->>Git: CloneChartRepository(ctx, appConfig) - Git-->>ChSvc: CloneResult{tempDir, chartPath} - - ChSvc->>Helm: InstallAppOfAppsFromLocal(ctx, cfg) - Helm->>K8s: helm upgrade --install app-of-apps - K8s-->>Helm: OK - - ChSvc->>ArgoCD: WaitForApplications(ctx, cfg) - loop Every 2s until ready or timeout - ArgoCD->>K8s: List Applications (dynamic client) - K8s-->>ArgoCD: Application list - ArgoCD->>ArgoCD: assessApplications() - end - ArgoCD-->>ChSvc: All Healthy+Synced - - ChSvc-->>BSvc: OK - BSvc-->>User: Bootstrap complete -``` - -### App Install / Upgrade Data Flow - -```mermaid -sequenceDiagram - participant User - participant AppCmd as "cmd/app/install" - participant Target as "app/target.Selector" - participant K8sPkg as "k8s package" - participant ChSvc as "chart/services" - participant ArgoProv as "argocd.Manager" - participant HelmProv as "helm.HelmManager" - - User->>AppCmd: openframe app install [--context k3d-dev] - AppCmd->>Target: Select(ctx) [if no --context] - Target->>K8sPkg: LoadContexts(kubeconfigPath) - K8sPkg-->>Target: []ContextInfo - Target->>User: Prompt: select context - User-->>Target: k3d-openframe-dev - Target->>K8sPkg: CheckResources(ctx, requirements) - K8sPkg-->>Target: Resources, sufficient=true - Target-->>AppCmd: SelectResult{Config, Context} - - AppCmd->>ChSvc: InstallChartsWithConfigContext(ctx, req) - ChSvc->>ArgoProv: Install(ctx, cfg) - ArgoProv-->>ChSvc: ArgoCD installed - ChSvc->>HelmProv: InstallAppOfAppsFromLocal(ctx, cfg) - HelmProv-->>ChSvc: app-of-apps installed - ChSvc->>ArgoProv: WaitForApplications(ctx, cfg) - ArgoProv-->>ChSvc: All apps Healthy+Synced - ChSvc-->>AppCmd: OK - AppCmd-->>User: SUCCESS -``` - ---- - -## Key Files - -| File | Purpose | -|---|---| -| [`main.go`](https://github.com/flamingo-stack/openframe-cli/blob/main/main.go) | Entry point; exits with child process exit code for automation fidelity | -| [`cmd/root.go`](https://github.com/flamingo-stack/openframe-cli/blob/main/cmd/root.go) | Root Cobra command; wires all subcommands, persistent flags, version info, WSL launcher | -| [`cmd/bootstrap/bootstrap.go`](https://github.com/flamingo-stack/openframe-cli/blob/main/cmd/bootstrap/bootstrap.go) | `openframe bootstrap` command: validates cluster name, delegates to bootstrap service | -| [`cmd/app/install.go`](https://github.com/flamingo-stack/openframe-cli/blob/main/cmd/app/install.go) | `openframe app install`: flag parsing, context/target selection, request assembly | -| [`cmd/app/upgrade.go`](https://github.com/flamingo-stack/openframe-cli/blob/main/cmd/app/upgrade.go) | `openframe app upgrade`: two modes (change-ref Mode 1, force-sync Mode 2) | -| [`cmd/update/update.go`](https://github.com/flamingo-stack/openframe-cli/blob/main/cmd/update/update.go) | `openframe update`: self-update with cosign verification, rollback, update-check | -| [`internal/bootstrap/service.go`](https://github.com/flamingo-stack/openframe-cli/blob/main/internal/bootstrap/service.go) | Orchestrates pre-flight โ†’ cluster create โ†’ chart install end-to-end | -| [`internal/cluster/service.go`](https://github.com/flamingo-stack/openframe-cli/blob/main/internal/cluster/service.go) | `ClusterService`: lifecycle operations, `ApplicationCleaner` interface injection | -| [`internal/cluster/provider/provider.go`](https://github.com/flamingo-stack/openframe-cli/blob/main/internal/cluster/provider/provider.go) | `Provider` interface; compile-time assertion that K3D satisfies it | -| [`internal/chart/services/chart_service.go`](https://github.com/flamingo-stack/openframe-cli/blob/main/internal/chart/services/chart_service.go) | `ChartService`: top-level install orchestration, HelmManager wiring | -| [`internal/chart/services/preflight.go`](https://github.com/flamingo-stack/openframe-cli/blob/main/internal/chart/services/preflight.go) | Pre-flights `openframe-helm-values.yaml` before any cluster work begins | -| [`internal/chart/providers/argocd/applications.go`](https://github.com/flamingo-stack/openframe-cli/blob/main/internal/chart/providers/argocd/applications.go) | `Manager`: native client-go dynamic client for ArgoCD Application CRDs | -| [`internal/chart/providers/argocd/wait.go`](https://github.com/flamingo-stack/openframe-cli/blob/main/internal/chart/providers/argocd/wait.go) | `WaitForApplications`: stabilization loop, stall detection, repo-server recovery | -| [`internal/chart/providers/argocd/sync.go`](https://github.com/flamingo-stack/openframe-cli/blob/main/internal/chart/providers/argocd/sync.go) | `RefreshAndSync`: hard refresh + sync patches via dynamic client; group-ordered child sync | -| [`internal/chart/providers/argocd/values.go`](https://github.com/flamingo-stack/openframe-cli/blob/main/internal/chart/providers/argocd/values.go) | Embedded ArgoCD Helm values; deep-merge with user `argocd:` overrides; pre-flight validation | -| [`internal/chart/providers/argocd/stall.go`](https://github.com/flamingo-stack/openframe-cli/blob/main/internal/chart/providers/argocd/stall.go) | Per-application stall tracker; detects OutOfSync stragglers after ref changes | -| [`internal/chart/providers/argocd/fatalmanifest.go`](https://github.com/flamingo-stack/openframe-cli/blob/main/internal/chart/providers/argocd/fatalmanifest.go) | Fail-fast for deterministic manifest errors (missing chart path) | -| [`internal/chart/providers/argocd/refassert.go`](https://github.com/flamingo-stack/openframe-cli/blob/main/internal/chart/providers/argocd/refassert.go) | Verifies deployed git ref matches the requested ref; catches silent V3 failures | -| [`internal/chart/providers/helm/manager.go`](https://github.com/flamingo-stack/openframe-cli/blob/main/internal/chart/providers/helm/manager.go) | `HelmManager`: helm CLI execution, Kubernetes client for workload verification | -| [`internal/chart/providers/git/repository.go`](https://github.com/flamingo-stack/openframe-cli/blob/main/internal/chart/providers/git/repository.go) | go-git shallow clone; branchโ†’tag fallback; credential isolation | -| [`internal/k8s/accessor.go`](https://github.com/flamingo-stack/openframe-cli/blob/main/internal/k8s/accessor.go) | `Accessor`: cluster health (reachable, nodes ready) and resource sufficiency checks | -| [`internal/k8s/contexts.go`](https://github.com/flamingo-stack/openframe-cli/blob/main/internal/k8s/contexts.go) | Kubeconfig context loading, `ResolveContextForCluster` for k3d naming convention | -| [`internal/prerequisites/runner.go`](https://github.com/flamingo-stack/openframe-cli/blob/main/internal/prerequisites/runner.go) | OS-aware `Runner`: auto-installs on macOS/Linux, shows docs on Windows | -| [`internal/shared/executor/executor.go`](https://github.com/flamingo-stack/openframe-cli/blob/main/internal/shared/executor/executor.go) | `RealCommandExecutor`: runs external binaries; captures stderr for error enrichment | -| [`internal/shared/executor/mock.go`](https://github.com/flamingo-stack/openframe-cli/blob/main/internal/shared/executor/mock.go) | `MockCommandExecutor`: structured argv recording for security tests | -| [`internal/shared/selfupdate/update.go`](https://github.com/flamingo-stack/openframe-cli/blob/main/internal/shared/selfupdate/update.go) | Core update logic: GitHub release fetch, version comparison, binary swap | -| [`internal/shared/selfupdate/cosign.go`](https://github.com/flamingo-stack/openframe-cli/blob/main/internal/shared/selfupdate/cosign.go) | Sigstore/cosign signature verification against pinned GitHub Actions identity | -| [`internal/shared/download/pins.go`](https://github.com/flamingo-stack/openframe-cli/blob/main/internal/shared/download/pins.go) | Pinned tool versions + SHA256 for k3d, mkcert, helm; verified download infra | -| [`internal/shared/config/transport.go`](https://github.com/flamingo-stack/openframe-cli/blob/main/internal/shared/config/transport.go) | `ApplyInsecureTLSConfig`: bypasses TLS only for local/loopback clusters | -| [`internal/shared/errors/errors.go`](https://github.com/flamingo-stack/openframe-cli/blob/main/internal/shared/errors/errors.go) | `HandleGlobalError`, `AlreadyHandledError` sentinel, typed error handlers | -| [`internal/shared/errors/friendly.go`](https://github.com/flamingo-stack/openframe-cli/blob/main/internal/shared/errors/friendly.go) | `friendlyHint`: maps low-level errors to actionable user guidance | -| [`internal/shared/ui/silent.go`](https://github.com/flamingo-stack/openframe-cli/blob/main/internal/shared/ui/silent.go) | `SetSilent`: routes all non-error pterm printers to `io.Discard` | -| [`internal/shared/redact/redact.go`](https://github.com/flamingo-stack/openframe-cli/blob/main/internal/shared/redact/redact.go) | `Redact`: removes registered secrets and URL-embedded credentials from output | -| [`internal/shared/wsllauncher/launcher.go`](https://github.com/flamingo-stack/openframe-cli/blob/main/internal/shared/wsllauncher/launcher.go) | `Forward`: re-runs the whole CLI inside WSL on Windows; installs Linux binary if missing | -| [`internal/app/status/status.go`](https://github.com/flamingo-stack/openframe-cli/blob/main/internal/app/status/status.go) | `Report`: aggregates cluster health, ArgoCD app sync/health, admin password | -| [`internal/app/uninstall/uninstall.go`](https://github.com/flamingo-stack/openframe-cli/blob/main/internal/app/uninstall/uninstall.go) | Removes Applications, Helm releases, and optionally the `argocd` namespace safely | -| [`tests/testutil/command_assertions.go`](https://github.com/flamingo-stack/openframe-cli/blob/main/tests/testutil/command_assertions.go) | Security-spec assertions on `RecordedCommand` (no secrets in argv, no shell injection) | - ---- - -## Dependencies - -The project uses these key Go library dependencies: - -| Library | How It Is Used | -|---|---| -| **github.com/spf13/cobra** | CLI framework for all commands, flags, help generation, and completion | -| **github.com/pterm/pterm** | Rich terminal UI: spinners, tables, boxes, interactive prompts, color output | -| **github.com/charmbracelet/huh** | Interactive selection menus (with `/` filtering) and text input prompts in wizards | -| **github.com/charmbracelet/bubbletea** | TUI runtime: powers huh prompts and the interactive `app status` view | -| **k8s.io/client-go** | Native Kubernetes API access: kubeconfig loading, rest.Config, typed clients | -| **k8s.io/apimachinery** | Kubernetes API types, GVR definitions for ArgoCD Application CRDs | -| **k8s.io/apiextensions-apiserver** | CRD client for checking/managing ArgoCD CRD installation | -| **sigs.k8s.io/yaml** | YAML marshaling/unmarshaling (round-trips through JSON for consistent field names) | -| **github.com/go-git/go-git/v5** | Pure-Go git clone for the app-of-apps chart repository (no `git` binary required) | -| **github.com/sigstore/sigstore-go** | Cosign bundle parsing and Sigstore trust-root verification for self-update | -| **golang.org/x/mod/semver** | Semantic version comparison for self-update logic | -| **golang.org/x/term** | Terminal detection (`IsTerminal`) for non-interactive mode detection | -| **github.com/elastic/go-sysinfo** | Cross-platform total RAM query (no shell-outs to `sysctl`/`/proc`) | -| **k8s.io/apimachinery/pkg/util/wait** | `PollUntilContextTimeout` for resilient workload readiness polling | - ---- - -## CLI Commands - -### Global Flags - -| Flag | Description | -|---|---| -| `--verbose`, `-v` | Enable verbose/debug output | -| `--silent` | Suppress all output except errors | -| `--version` | Print version, commit, and build date | - -### Command Reference - -#### `openframe bootstrap` - -Creates a K3D cluster and installs the OpenFrame platform in a single step. - -```bash -openframe bootstrap # Interactive mode -openframe bootstrap my-cluster # Named cluster -openframe bootstrap --non-interactive # CI/CD mode (uses existing openframe-helm-values.yaml) -openframe bootstrap --verbose # Show detailed ArgoCD sync progress -``` - -#### `openframe cluster` - -| Subcommand | Description | Example | -|---|---|---| -| `create [NAME]` | Create a K3D cluster (wizard or flags) | `openframe cluster create dev --skip-wizard --nodes 1` | -| `delete [NAME]` | Delete a cluster and its resources | `openframe cluster delete dev --force` | -| `list` | List all managed clusters | `openframe cluster list -o json` | -| `status [NAME]` | Show detailed cluster status | `openframe cluster status dev -o yaml` | -| `cleanup [NAME]` | Prune unused container images from cluster nodes | `openframe cluster cleanup dev --force` | - -**`cluster create` flags:** - -```bash -openframe cluster create # Interactive wizard -openframe cluster create my-cluster # Named with wizard -openframe cluster create --skip-wizard # Defaults (k3d, 3 nodes) -openframe cluster create --type k3d --nodes 1 --skip-wizard -``` - -#### `openframe app` - -| Subcommand | Description | Example | -|---|---|---| -| `install [cluster]` | Install ArgoCD + app-of-apps | `openframe app install -c k3d-dev` | -| `upgrade [cluster]` | Re-sync or change git ref | `openframe app upgrade --ref v1.3.0` | -| `status` | Show platform readiness | `openframe app status -c k3d-dev -o json` | -| `access` | Print ArgoCD credentials | `openframe app access -c k3d-dev` | -| `uninstall` | Remove app (keep cluster) | `openframe app uninstall -c k3d-dev --yes` | - -**`app install` flags:** - -```bash -openframe app install # Interactive context picker -openframe app install -c k3d-openframe-dev # Explicit context -openframe app install --non-interactive # CI (reuse existing values file) -openframe app install --ref 1.0.48 # Deploy specific tag -openframe app install --dry-run # Preview only -``` - -**`app upgrade` modes:** - -```bash -openframe app upgrade # Force re-sync current ref (Mode 2) -openframe app upgrade --sync --prune # Re-sync + delete removed resources -openframe app upgrade --ref v1.4.0 # Change to new release tag (Mode 1) -openframe app upgrade -c k3d-dev --ref main # Target explicit context + ref -``` - -#### `openframe prerequisites` - -```bash -openframe prerequisites check # Report status, no changes -openframe prerequisites install # Install missing tools (macOS/Linux) -``` - -#### `openframe update` - -```bash -openframe update # Update to latest release -openframe update v1.4.0 # Switch to specific version (up or down) -openframe update check # Report availability only -openframe update check -o json # Machine-readable check -openframe update rollback # Revert to previous version (offline) -``` - -### Non-Interactive / CI Usage - -Every destructive or interactive command supports scripted operation: - -```bash -# Fully non-interactive bootstrap -openframe bootstrap my-cluster --non-interactive - -# Force-delete without confirmation -openframe cluster delete my-cluster --force - -# Install without prompts, output JSON for parsing -openframe app install -c k3d-dev --non-interactive -openframe app status -c k3d-dev -o json - -# Uninstall without confirmation -openframe app uninstall -c k3d-dev --yes -``` - -Non-interactive mode is also engaged automatically when `CI`, `GITHUB_ACTIONS`, `GITLAB_CI`, or `CIRCLECI` environment variables are set, or when `stdin` is not a terminal. - ---- - -## Community and Support - -- **OpenMSP Slack**: [Join the community](https://join.slack.com/t/openmsp/shared_invite/zt-36bl7mx0h-3~U2nFH6nqHqoTPXMaHEHA) โ€” primary support channel -- **Releases**: [https://github.com/flamingo-stack/openframe-cli/releases](https://github.com/flamingo-stack/openframe-cli/releases) -- **OpenFrame Platform**: [https://openframe.ai](https://openframe.ai) -- **Flamingo**: [https://flamingo.run](https://flamingo.run) diff --git a/docs/reference/terminal-output.md b/docs/reference/terminal-output.md deleted file mode 100644 index 93caeaa4..00000000 --- a/docs/reference/terminal-output.md +++ /dev/null @@ -1,107 +0,0 @@ -# Terminal output reference - -The CLI picks an output mode per command from the terminal it runs in and the -flags it was given. Exactly one "live" surface is ever active at a time; every -other consumer gets sequential, log-friendly lines carrying the same -information. - -## Output modes - -| Mode | When | What you see | -|------|------|--------------| -| Live | interactive terminal, no `--verbose`/`--plain`/`--silent` | animated spinners, the in-place application dashboard, download progress bars | -| Sequential | redirected output, CI, `--plain`, `--verbose` | timestamped log lines: heartbeats with ready-deltas, stage lines, download begin/done announces | -| Silent | `--silent` | errors only | -| Machine | `-o json` / `-o yaml` | data on stdout, human warnings on stderr | - -## Live surfaces - -- **Stage checklist** (`bootstrap`) โ€” each stage prints `โ—‰ [2/3] Create - cluster`, closes with `โœ”`/`โœ–` and its duration, and the run ends with a - summary card: cluster + kube-context, per-stage timings, next commands. -- **Application dashboard** (install/bootstrap wait) โ€” an in-place block with - an animated header, elapsed time, a progress bar (`14/17 ready`), and the - not-ready applications colored by health (red `Degraded`, yellow - `Progressing`), capped at 8 with `+N more`. One-off events (stall hints, - repo-server recovery notices) pin under the block as notes. The success - line reports which applications took longest to become ready. -- **`app status --watch`** โ€” the platform status re-rendered in place every - 3 s; a failing poll shows its error inside the view and keeps watching. -- **`app status --interactive`** โ€” a k9s-style TUI over the ArgoCD - applications: arrows/`j`/`k` navigate, `enter` opens the app detail (repo, - path, target ref, revision, conditions, operation state), `s` triggers a - per-app sync, `r` refreshes, `q` quits. Auto-refreshes every 3 s. -- **Download progress** โ€” a self-rewriting line with a bar, percentage, and - speed for verified tool downloads. -- **Desktop notification** โ€” long operations (bootstrap, install) emit an - OSC 9 notification plus a terminal bell on completion or failure, for the - user who switched to another window. - -`--watch` and `--interactive` require an interactive terminal and reject -machine output and `--plain`. - -## Sequential mode - -Where the live surfaces cannot run (redirected output, CI, `--plain`, -`--verbose`), the same information arrives as self-sufficient log lines: - -- **Wait heartbeat** (every 30 s, 10 s under `--verbose`): - `[12:34:05] apps 14/17 ready (+2 since last check) ยท elapsed 12m30s` with a - `pending: tenant(Progressing), gateway(Degraded)` detail line. A `+0` delta - makes a stall visible without diffing counts. -- **Interruption state** โ€” Ctrl+C or a cancelled CI job during the wait - records `interrupted at 14/17 applications ready ยท pending: โ€ฆ` before the - cancellation error. -- **Download announces** โ€” `Downloading helm-v3.16.2.tar.gz (52 MB)...` and - `Downloaded โ€ฆ in 4.2s` replace the live bar. -- **Phase heartbeats** โ€” output-less blocking operations (`helm --wait`) - emit `