Some checks failed
ARM64 Build / Build generic ARM64 disk image (push) Failing after 3s
CI / Go Tests (push) Successful in 1m23s
CI / Shellcheck (push) Successful in 46s
CI / Build Go Binaries (amd64, linux, linux-amd64) (push) Successful in 1m32s
CI / Build Go Binaries (arm64, linux, linux-arm64) (push) Successful in 1m15s
Phase 6 of v0.3. The update agent now refuses to apply artifacts whose
channel doesn't match local policy, whose architecture differs from the
running host, or whose min_compatible_version is above the current
version. It also refuses to apply outside a configured maintenance window
unless --force is given.
New package update/pkg/config:
- config.Load parses /etc/kubesolo/update.conf (key=value, # comments,
unknown keys ignored). Missing file is fine — fresh systems before
cloud-init has run.
- ParseWindow handles "HH:MM-HH:MM" plus the wrapping midnight case
(e.g. "23:00-01:00"). Empty input -> AlwaysOpen (no constraint).
Degenerate zero-length windows never match.
- CompareVersions does a simple 3-component semver compare with the 'v'
prefix optional and pre-release suffix ignored.
- 14 unit tests total.
update/pkg/image/image.UpdateMetadata gains three optional fields:
- channel ("stable", "beta", ...)
- min_compatible_version (refuse upgrade if current < this)
- architecture ("amd64", "arm64", ...)
update/cmd/opts.go reads update.conf and merges it into opts; explicit
--server / --channel / --pubkey / --maintenance-window CLI flags override
the file. New --force, --conf, --channel, --maintenance-window flags.
Precedence: CLI > config file > package defaults.
update/cmd/apply.go gains four gates in order:
1. Maintenance window — checked locally before any HTTP work; skipped
with --force.
2. Channel — refused if metadata.channel doesn't match opts.Channel.
3. Architecture — refused if metadata.architecture != runtime.GOARCH.
4. Min compatible version — refused if FromVersion < min_compatible.
All gate failures transition state to Failed with a clear LastError.
cloud-init gains a top-level updates: block (Server, Channel,
MaintenanceWindow, PubKey). cloud-init.ApplyUpdates writes
/etc/kubesolo/update.conf from those fields on first boot. Empty block
leaves any existing file alone (so hand-edited update.conf survives a
reboot without cloud-init re-applying). 4 new tests cover empty / all /
partial / parent-dir-creation cases. full-config.yaml example updated.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
95 lines
3.6 KiB
Go
95 lines
3.6 KiB
Go
// Package cloudinit implements a lightweight cloud-init parser for KubeSolo OS.
|
|
//
|
|
// It reads a simplified cloud-init YAML config and applies:
|
|
// - hostname
|
|
// - network configuration (static IP or DHCP)
|
|
// - KubeSolo extra flags and settings
|
|
// - NTP servers
|
|
//
|
|
// The config file is typically at /mnt/data/etc-kubesolo/cloud-init.yaml
|
|
// or specified via kubesolo.cloudinit= boot parameter.
|
|
package cloudinit
|
|
|
|
// Config is the top-level cloud-init configuration.
|
|
type Config struct {
|
|
Hostname string `yaml:"hostname"`
|
|
Network NetworkConfig `yaml:"network"`
|
|
KubeSolo KubeSoloConfig `yaml:"kubesolo"`
|
|
NTP NTPConfig `yaml:"ntp"`
|
|
Airgap AirgapConfig `yaml:"airgap"`
|
|
Portainer PortainerConfig `yaml:"portainer"`
|
|
Updates UpdatesConfig `yaml:"updates"`
|
|
}
|
|
|
|
// UpdatesConfig configures the kubesolo-update agent. Written to
|
|
// /etc/kubesolo/update.conf on first boot. See update/pkg/config.
|
|
type UpdatesConfig struct {
|
|
// Server is the update server URL (HTTP or OCI registry).
|
|
Server string `yaml:"server"`
|
|
// Channel selects which channel to track ("stable", "beta", "edge").
|
|
// Empty = "stable".
|
|
Channel string `yaml:"channel"`
|
|
// MaintenanceWindow restricts apply to the given local time range,
|
|
// e.g. "03:00-05:00". Wrapping windows like "23:00-01:00" supported.
|
|
// Empty = no restriction.
|
|
MaintenanceWindow string `yaml:"maintenance_window"`
|
|
// PubKey is the path to the Ed25519 public key file used to verify
|
|
// signed update artifacts. Empty = signature verification disabled.
|
|
PubKey string `yaml:"pubkey"`
|
|
}
|
|
|
|
// NetworkConfig defines network settings.
|
|
type NetworkConfig struct {
|
|
Mode string `yaml:"mode"` // "dhcp" or "static"
|
|
Interface string `yaml:"interface"` // e.g. "eth0" (auto-detected if empty)
|
|
Address string `yaml:"address"` // CIDR notation, e.g. "192.168.1.100/24"
|
|
Gateway string `yaml:"gateway"` // e.g. "192.168.1.1"
|
|
DNS []string `yaml:"dns"` // nameservers
|
|
}
|
|
|
|
// KubeSoloConfig defines KubeSolo-specific settings.
|
|
type KubeSoloConfig struct {
|
|
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"`
|
|
// v1.1.4+: skip edge-optimised overrides, use upstream k8s defaults
|
|
// (useful for CI and powerful machines, disabled by default).
|
|
Full bool `yaml:"full"`
|
|
// v1.1.5+: disable IPv6 in the cluster.
|
|
DisableIPv6 bool `yaml:"disable-ipv6"`
|
|
// v1.1.5+: detect SQLite WAL corruption on startup and recover from
|
|
// unclean shutdowns (e.g. power loss). Recommended ON for edge devices.
|
|
DBWALRepair bool `yaml:"db-wal-repair"`
|
|
}
|
|
|
|
// NTPConfig defines NTP settings.
|
|
type NTPConfig struct {
|
|
Servers []string `yaml:"servers"`
|
|
}
|
|
|
|
// AirgapConfig defines air-gapped deployment settings.
|
|
type AirgapConfig struct {
|
|
ImportImages bool `yaml:"import-images"`
|
|
ImagesDir string `yaml:"images-dir"`
|
|
}
|
|
|
|
// PortainerConfig defines Portainer Edge Agent settings.
|
|
type PortainerConfig struct {
|
|
EdgeAgent EdgeAgentConfig `yaml:"edge-agent"`
|
|
}
|
|
|
|
// EdgeAgentConfig holds Portainer Edge Agent connection details.
|
|
type EdgeAgentConfig struct {
|
|
Enabled bool `yaml:"enabled"`
|
|
EdgeID string `yaml:"edge-id"`
|
|
EdgeKey string `yaml:"edge-key"`
|
|
PortainerURL string `yaml:"portainer-url"`
|
|
Image string `yaml:"image"`
|
|
}
|