Files
kubesolo-os/cloud-init/kubesolo.go
Adolfo Delorenzo 1b44c9d621
Some checks failed
ARM64 Build / Build generic ARM64 disk image (push) Failing after 3s
CI / Go Tests (push) Successful in 1m27s
CI / Shellcheck (push) Failing after 50s
CI / Build Go Binaries (amd64, linux, linux-amd64) (push) Failing after 1m33s
CI / Build Go Binaries (arm64, linux, linux-arm64) (push) Failing after 1m15s
feat: bump KubeSolo to v1.1.5 + cross-arch CI workflow
Phase 4 of v0.3 — KubeSolo version bump and CI gating.

KubeSolo v1.1.0 → v1.1.5 brings:
- New flag --disable-ipv6 (v1.1.5)
- New flag --db-wal-repair (v1.1.5) — important for power-loss resilience
  on edge appliances; surfaced as kubesolo.db-wal-repair in cloud-init
- New flag --full (v1.1.4) — disables edge-optimised k8s overrides
- Pod egress connectivity fix after reboot (v1.1.4)
- Registry config persistence fix (v1.1.5)
- k8s 1.34.7, CoreDNS 1.14.3, Go 1.26.2

All three new flags wired into cloud-init: config.go fields, kubesolo.go
extra-flag emission, full-config.yaml example.

Supply-chain hygiene:
- Per-arch checksums: KUBESOLO_SHA256_AMD64 and KUBESOLO_SHA256_ARM64 in
  versions.env. Replaces the single shared KUBESOLO_SHA256 that couldn't
  meaningfully verify both binaries at once.
- Checksum now applied to the tarball (the immutable upstream artifact)
  rather than the post-extract binary.

CI:
- New .gitea/workflows/build-arm64.yaml routes the full kernel + rootfs +
  disk-image build to the Odroid arm64-linux runner. Triggers on push to
  main, tags, and manual workflow_dispatch. The boot smoke test is
  continue-on-error because KubeSolo's first-boot image import deadline
  fires under QEMU TCG on the Odroid.

VERSION bumped to 0.3.0-dev. CHANGELOG entry under [0.3.0-dev] captures all
Phase 1-4 work + the known limitations documented in arm64-status.md.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-14 16:26:20 -06:00

116 lines
3.0 KiB
Go

package cloudinit
import (
"fmt"
"log/slog"
"os"
"path/filepath"
"strings"
)
// ApplyKubeSolo writes KubeSolo configuration files based on cloud-init config.
// These files are read by init stage 90-kubesolo.sh when building the
// KubeSolo command line.
func ApplyKubeSolo(cfg *Config, configDir string) error {
if err := os.MkdirAll(configDir, 0o755); err != nil {
return fmt.Errorf("creating config dir %s: %w", configDir, err)
}
// Write extra flags file (consumed by 90-kubesolo.sh)
flags := buildExtraFlags(cfg)
if flags != "" {
flagsPath := filepath.Join(configDir, "extra-flags")
if err := os.WriteFile(flagsPath, []byte(flags+"\n"), 0o644); err != nil {
return fmt.Errorf("writing extra-flags: %w", err)
}
slog.Info("wrote KubeSolo extra flags", "path", flagsPath, "flags", flags)
}
// Write config.yaml for KubeSolo if we have settings beyond defaults
if err := writeKubeSoloConfig(cfg, configDir); err != nil {
return err
}
return nil
}
func buildExtraFlags(cfg *Config) string {
var parts []string
if cfg.KubeSolo.ExtraFlags != "" {
parts = append(parts, cfg.KubeSolo.ExtraFlags)
}
// Add extra SANs from cloud-init
for _, san := range cfg.KubeSolo.ExtraSANs {
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")
}
if cfg.KubeSolo.Full {
parts = append(parts, "--full")
}
if cfg.KubeSolo.DisableIPv6 {
parts = append(parts, "--disable-ipv6")
}
if cfg.KubeSolo.DBWALRepair {
parts = append(parts, "--db-wal-repair")
}
return strings.Join(parts, " ")
}
func writeKubeSoloConfig(cfg *Config, configDir string) error {
var lines []string
lines = append(lines, "# Generated by KubeSolo OS cloud-init")
lines = append(lines, "data-dir: /var/lib/kubesolo")
if cfg.KubeSolo.LocalStorage != nil {
if *cfg.KubeSolo.LocalStorage {
lines = append(lines, "local-storage: true")
} else {
lines = append(lines, "local-storage: false")
}
} else {
lines = append(lines, "local-storage: true")
}
lines = append(lines, "bind-address: 0.0.0.0")
lines = append(lines, "cluster-cidr: 10.42.0.0/16")
lines = append(lines, "service-cidr: 10.43.0.0/16")
dest := filepath.Join(configDir, "config.yaml")
content := strings.Join(lines, "\n") + "\n"
if err := os.WriteFile(dest, []byte(content), 0o644); err != nil {
return fmt.Errorf("writing config.yaml: %w", err)
}
slog.Info("wrote KubeSolo config", "path", dest)
return nil
}