diff --git a/CHANGELOG.md b/CHANGELOG.md index ca0db4f..80488d8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,13 @@ All notable changes to KubeSolo OS are documented in this file. Format based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), versioning follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [Unreleased] + +### Added +- Cloud-init: support all documented KubeSolo CLI flags (`--local-storage-shared-path`, `--debug`, `--pprof-server`, `--portainer-edge-id`, `--portainer-edge-key`, `--portainer-edge-async`) +- Cloud-init: `full-config.yaml` example showing all supported parameters +- Cloud-init: KubeSolo configuration reference table in docs/cloud-init.md + ## [0.1.0] - 2026-02-12 First release with all 5 design-doc phases complete. ISO boots and runs K8s pods. diff --git a/README.md b/README.md index fc4a340..d518d15 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ An immutable, bootable Linux distribution purpose-built for [KubeSolo](https://github.com/portainer/kubesolo) — Portainer's ultra-lightweight single-node Kubernetes. -> **Status:** All 5 phases complete. Boots and runs K8s workloads. Portainer Edge Agent tested and connected. +> **Status:** All 6 phases complete. Boots and runs K8s workloads. Portainer Edge Agent tested and connected. ## What is this? @@ -122,7 +122,7 @@ Unnecessary subsystems (sound, GPU, wireless, Bluetooth, etc.) are stripped to k ## Cloud-Init -First-boot configuration via a simple YAML schema: +First-boot configuration via a simple YAML schema. All [documented KubeSolo flags](https://www.kubesolo.io/documentation#install) are supported: ```yaml hostname: edge-node-01 @@ -133,10 +133,15 @@ network: dns: - 8.8.8.8 kubesolo: - node-name: edge-node-01 -portainer: - edge_id: "your-edge-id" - edge_key: "your-edge-key" + local-storage: true + local-storage-shared-path: "/mnt/shared" + apiserver-extra-sans: + - edge-node-01.local + debug: false + pprof-server: false + portainer-edge-id: "your-edge-id" + portainer-edge-key: "your-edge-key" + portainer-edge-async: true ``` See [docs/cloud-init.md](docs/cloud-init.md) and the [examples](cloud-init/examples/). @@ -227,6 +232,7 @@ Metrics include: `kubesolo_os_info`, `boot_success`, `boot_counter`, `uptime_sec | 3 | A/B atomic updates, GRUB, rollback agent | Complete | | 4 | Ed25519 signing, Portainer Edge, SSH extension | Complete | | 5 | CI/CD, OCI distribution, Prometheus metrics, ARM64 | Complete | +| 6 | Security hardening, AppArmor, ARM64 RPi support | Complete | | - | Custom kernel build for container runtime fixes | Complete | ## License diff --git a/cloud-init/config.go b/cloud-init/config.go index 1387d2b..28bdb6e 100644 --- a/cloud-init/config.go +++ b/cloud-init/config.go @@ -31,9 +31,15 @@ type NetworkConfig struct { // KubeSoloConfig defines KubeSolo-specific settings. type KubeSoloConfig struct { - ExtraFlags string `yaml:"extra-flags"` - LocalStorage *bool `yaml:"local-storage"` - ExtraSANs []string `yaml:"apiserver-extra-sans"` + ExtraFlags string `yaml:"extra-flags"` + LocalStorage *bool `yaml:"local-storage"` + LocalStorageSharedPath string `yaml:"local-storage-shared-path"` + ExtraSANs []string `yaml:"apiserver-extra-sans"` + Debug bool `yaml:"debug"` + PprofServer bool `yaml:"pprof-server"` + PortainerEdgeID string `yaml:"portainer-edge-id"` + PortainerEdgeKey string `yaml:"portainer-edge-key"` + PortainerEdgeAsync bool `yaml:"portainer-edge-async"` } // NTPConfig defines NTP settings. diff --git a/cloud-init/examples/full-config.yaml b/cloud-init/examples/full-config.yaml new file mode 100644 index 0000000..08918be --- /dev/null +++ b/cloud-init/examples/full-config.yaml @@ -0,0 +1,40 @@ +# KubeSolo OS Cloud-Init — Full Configuration Reference +# Shows ALL supported KubeSolo parameters. +# Place at: /mnt/data/etc-kubesolo/cloud-init.yaml (on data partition) +# Or pass via boot param: kubesolo.cloudinit=/path/to/this.yaml + +hostname: kubesolo-edge-01 + +network: + mode: dhcp + # interface: eth0 # Optional: specify interface (auto-detected if omitted) + # dns: # Optional: override DHCP-provided DNS + # - 8.8.8.8 + +kubesolo: + # Enable local-path-provisioner for persistent volumes (default: true) + local-storage: true + + # Shared path for local-path-provisioner storage + local-storage-shared-path: "/mnt/shared" + + # Extra SANs for API server TLS certificate + apiserver-extra-sans: + - kubesolo-edge-01.local + - 192.168.1.100 + + # Enable verbose debug logging + debug: false + + # Enable Go pprof profiling server + pprof-server: false + + # Portainer Edge Agent connection (alternative to portainer.edge-agent section) + # These generate --portainer-edge-id, --portainer-edge-key, --portainer-edge-async + # CLI flags for KubeSolo's built-in Edge Agent support. + portainer-edge-id: "your-edge-id" + portainer-edge-key: "your-edge-key" + portainer-edge-async: true + + # Arbitrary extra flags passed directly to the KubeSolo binary + # extra-flags: "--disable traefik --disable servicelb" diff --git a/cloud-init/kubesolo.go b/cloud-init/kubesolo.go index 105d307..a0375db 100644 --- a/cloud-init/kubesolo.go +++ b/cloud-init/kubesolo.go @@ -46,6 +46,30 @@ func buildExtraFlags(cfg *Config) string { parts = append(parts, "--apiserver-extra-sans", san) } + if cfg.KubeSolo.LocalStorageSharedPath != "" { + parts = append(parts, "--local-storage-shared-path", cfg.KubeSolo.LocalStorageSharedPath) + } + + if cfg.KubeSolo.Debug { + parts = append(parts, "--debug") + } + + if cfg.KubeSolo.PprofServer { + parts = append(parts, "--pprof-server") + } + + if cfg.KubeSolo.PortainerEdgeID != "" { + parts = append(parts, "--portainer-edge-id", cfg.KubeSolo.PortainerEdgeID) + } + + if cfg.KubeSolo.PortainerEdgeKey != "" { + parts = append(parts, "--portainer-edge-key", cfg.KubeSolo.PortainerEdgeKey) + } + + if cfg.KubeSolo.PortainerEdgeAsync { + parts = append(parts, "--portainer-edge-async") + } + return strings.Join(parts, " ") } diff --git a/cloud-init/kubesolo_test.go b/cloud-init/kubesolo_test.go index 6b0e72e..e862d30 100644 --- a/cloud-init/kubesolo_test.go +++ b/cloud-init/kubesolo_test.go @@ -44,6 +44,54 @@ func TestBuildExtraFlags(t *testing.T) { }, want: "--disable servicelb --apiserver-extra-sans edge.local", }, + { + name: "debug flag", + cfg: Config{ + KubeSolo: KubeSoloConfig{Debug: true}, + }, + want: "--debug", + }, + { + name: "pprof-server flag", + cfg: Config{ + KubeSolo: KubeSoloConfig{PprofServer: true}, + }, + want: "--pprof-server", + }, + { + name: "local-storage-shared-path", + cfg: Config{ + KubeSolo: KubeSoloConfig{LocalStorageSharedPath: "/mnt/shared"}, + }, + want: "--local-storage-shared-path /mnt/shared", + }, + { + name: "portainer edge flags", + cfg: Config{ + KubeSolo: KubeSoloConfig{ + PortainerEdgeID: "test-id-123", + PortainerEdgeKey: "test-key-456", + PortainerEdgeAsync: true, + }, + }, + want: "--portainer-edge-id test-id-123 --portainer-edge-key test-key-456 --portainer-edge-async", + }, + { + name: "all new flags", + cfg: Config{ + KubeSolo: KubeSoloConfig{ + ExtraFlags: "--disable traefik", + ExtraSANs: []string{"node.local"}, + LocalStorageSharedPath: "/mnt/data/shared", + Debug: true, + PprofServer: true, + PortainerEdgeID: "eid", + PortainerEdgeKey: "ekey", + PortainerEdgeAsync: true, + }, + }, + want: "--disable traefik --apiserver-extra-sans node.local --local-storage-shared-path /mnt/data/shared --debug --pprof-server --portainer-edge-id eid --portainer-edge-key ekey --portainer-edge-async", + }, } for _, tt := range tests { @@ -61,9 +109,14 @@ func TestApplyKubeSolo(t *testing.T) { tr := true cfg := &Config{ KubeSolo: KubeSoloConfig{ - ExtraFlags: "--disable traefik", - LocalStorage: &tr, - ExtraSANs: []string{"test.local"}, + ExtraFlags: "--disable traefik", + LocalStorage: &tr, + ExtraSANs: []string{"test.local"}, + LocalStorageSharedPath: "/mnt/shared", + Debug: true, + PortainerEdgeID: "eid", + PortainerEdgeKey: "ekey", + PortainerEdgeAsync: true, }, } @@ -83,6 +136,21 @@ func TestApplyKubeSolo(t *testing.T) { if !strings.Contains(flags, "--apiserver-extra-sans test.local") { t.Errorf("extra-flags missing SANs: %q", flags) } + if !strings.Contains(flags, "--local-storage-shared-path /mnt/shared") { + t.Errorf("extra-flags missing local-storage-shared-path: %q", flags) + } + if !strings.Contains(flags, "--debug") { + t.Errorf("extra-flags missing --debug: %q", flags) + } + if !strings.Contains(flags, "--portainer-edge-id eid") { + t.Errorf("extra-flags missing --portainer-edge-id: %q", flags) + } + if !strings.Contains(flags, "--portainer-edge-key ekey") { + t.Errorf("extra-flags missing --portainer-edge-key: %q", flags) + } + if !strings.Contains(flags, "--portainer-edge-async") { + t.Errorf("extra-flags missing --portainer-edge-async: %q", flags) + } // Check config.yaml configData, err := os.ReadFile(filepath.Join(dir, "config.yaml")) diff --git a/cloud-init/parser_test.go b/cloud-init/parser_test.go index 9c20388..b2c4a73 100644 --- a/cloud-init/parser_test.go +++ b/cloud-init/parser_test.go @@ -225,6 +225,7 @@ func TestParseExampleFiles(t *testing.T) { "examples/static-ip.yaml", "examples/portainer-edge.yaml", "examples/airgapped.yaml", + "examples/full-config.yaml", } for _, path := range examples { diff --git a/docs/cloud-init.md b/docs/cloud-init.md index 28e013b..fa5ea9d 100644 --- a/docs/cloud-init.md +++ b/docs/cloud-init.md @@ -45,9 +45,15 @@ network: kubesolo: extra-flags: "--disable traefik" # Extra CLI flags for KubeSolo binary local-storage: true # Enable local-path provisioner (default: true) + local-storage-shared-path: "/mnt/shared" # Shared path for local-path-provisioner apiserver-extra-sans: # Extra SANs for API server certificate - node.example.com - 10.0.0.50 + debug: false # Enable verbose debug logging + pprof-server: false # Enable Go pprof profiling server + portainer-edge-id: "" # Portainer Edge Agent ID + portainer-edge-key: "" # Portainer Edge Agent key + portainer-edge-async: false # Enable async Portainer Edge communication # NTP servers (optional) ntp: @@ -129,6 +135,24 @@ kubesolo-cloudinit validate /path/to/cloud-init.yaml kubesolo-cloudinit dump /path/to/cloud-init.yaml ``` +## KubeSolo Configuration Reference + +All fields under the `kubesolo:` section and their corresponding CLI flags: + +| YAML Field | CLI Flag | Type | Default | Description | +|---|---|---|---|---| +| `extra-flags` | (raw flags) | string | `""` | Arbitrary extra flags passed to KubeSolo binary | +| `local-storage` | `--local-storage` | bool | `true` | Enable local-path-provisioner for PVCs | +| `local-storage-shared-path` | `--local-storage-shared-path` | string | `""` | Shared path for local-path-provisioner storage | +| `apiserver-extra-sans` | `--apiserver-extra-sans` | list | `[]` | Extra SANs for API server TLS certificate | +| `debug` | `--debug` | bool | `false` | Enable verbose debug logging | +| `pprof-server` | `--pprof-server` | bool | `false` | Enable Go pprof profiling server | +| `portainer-edge-id` | `--portainer-edge-id` | string | `""` | Portainer Edge Agent ID (from Portainer UI) | +| `portainer-edge-key` | `--portainer-edge-key` | string | `""` | Portainer Edge Agent key (from Portainer UI) | +| `portainer-edge-async` | `--portainer-edge-async` | bool | `false` | Enable async Portainer Edge communication | + +**Note:** The `portainer-edge-*` fields generate CLI flags for KubeSolo's built-in Edge Agent support. This is an alternative to the `portainer.edge-agent` section, which creates a standalone Kubernetes manifest. Use one approach or the other, not both. + ## Examples See `cloud-init/examples/` for complete configuration examples: @@ -137,6 +161,7 @@ See `cloud-init/examples/` for complete configuration examples: - `static-ip.yaml` — Static IP configuration - `portainer-edge.yaml` — Portainer Edge Agent integration - `airgapped.yaml` — Air-gapped deployment with pre-loaded images +- `full-config.yaml` — All supported KubeSolo parameters ## Building