Files
kubesolo-os/update/cmd/check.go
Adolfo Delorenzo efc7f80b65
Some checks failed
CI / Go Tests (push) Has been cancelled
CI / Build Go Binaries (amd64, linux, linux-amd64) (push) Has been cancelled
CI / Build Go Binaries (arm64, linux, linux-arm64) (push) Has been cancelled
CI / Shellcheck (push) Has been cancelled
feat: add security hardening, AppArmor, and ARM64 Raspberry Pi support (Phase 6)
Security hardening: bind kubeconfig server to localhost, mount hardening
(noexec/nosuid/nodev on tmpfs), sysctl network hardening, kernel module
loading lock after boot, SHA256 checksum verification for downloads,
kernel AppArmor + Audit support, complain-mode AppArmor profiles for
containerd and kubelet, and security integration test.

ARM64 Raspberry Pi support: piCore64 base extraction, RPi kernel build
from raspberrypi/linux fork, RPi firmware fetch, SD card image with 4-
partition GPT and tryboot A/B mechanism, BootEnv Go interface abstracting
GRUB vs RPi boot environments, architecture-aware build scripts, QEMU
aarch64 dev VM and boot test.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-12 13:08:17 -06:00

65 lines
1.7 KiB
Go

package cmd
import (
"fmt"
"log/slog"
"github.com/portainer/kubesolo-os/update/pkg/image"
"github.com/portainer/kubesolo-os/update/pkg/partition"
)
// Check queries the update server for available updates and compares
// against the currently running version.
func Check(args []string) error {
opts := parseOpts(args)
if opts.ServerURL == "" {
return fmt.Errorf("--server is required (no default update server configured)")
}
// Get current version from active partition
env := opts.NewBootEnv()
activeSlot, err := env.ActiveSlot()
if err != nil {
return fmt.Errorf("reading active slot: %w", err)
}
partInfo, err := partition.GetSlotPartition(activeSlot)
if err != nil {
return fmt.Errorf("finding active partition: %w", err)
}
mountPoint := "/tmp/kubesolo-check-" + activeSlot
if err := partition.MountReadOnly(partInfo.Device, mountPoint); err != nil {
return fmt.Errorf("mounting active partition: %w", err)
}
defer partition.Unmount(mountPoint)
currentVersion, err := partition.ReadVersion(mountPoint)
if err != nil {
slog.Warn("could not read current version", "error", err)
currentVersion = "unknown"
}
// Check update server
client := image.NewClient(opts.ServerURL, "")
meta, err := client.CheckForUpdate()
if err != nil {
return fmt.Errorf("checking for update: %w", err)
}
fmt.Printf("Current version: %s (slot %s)\n", currentVersion, activeSlot)
fmt.Printf("Latest version: %s\n", meta.Version)
if meta.Version == currentVersion {
fmt.Println("Status: up to date")
} else {
fmt.Println("Status: update available")
if meta.ReleaseNotes != "" {
fmt.Printf("Release notes: %s\n", meta.ReleaseNotes)
}
}
return nil
}