control agent can now uboot commands
This commit is contained in:
@@ -7,6 +7,8 @@ ENV VERSION=${VERSION}
|
||||
WORKDIR /
|
||||
|
||||
COPY bin/ctl-linux-aarch64-${VERSION} ./ctl
|
||||
COPY out/fw_printenv ./
|
||||
COPY out/fw_setenv ./
|
||||
COPY --from=cacerts /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
|
||||
|
||||
ENTRYPOINT ["/ctl"]
|
||||
41
clitools/docker/uboot-tools.Dockerfile
Normal file
41
clitools/docker/uboot-tools.Dockerfile
Normal file
@@ -0,0 +1,41 @@
|
||||
FROM alpine:3.22 AS build
|
||||
|
||||
RUN apk add --no-cache \
|
||||
build-base \
|
||||
bison \
|
||||
flex \
|
||||
linux-headers \
|
||||
file \
|
||||
binutils \
|
||||
tar
|
||||
|
||||
WORKDIR /src
|
||||
|
||||
ARG UBOOT_TAR
|
||||
ARG UBOOT_VERSION
|
||||
|
||||
COPY ${UBOOT_TAR} /tmp/
|
||||
RUN tar zxf "/tmp/$(basename "${UBOOT_TAR}")"
|
||||
|
||||
RUN make tools-only_defconfig
|
||||
|
||||
# Build the env tools using the supported target.
|
||||
RUN make -j"$(nproc)" \
|
||||
HOSTCC=gcc \
|
||||
HOSTLD=gcc \
|
||||
HOSTCFLAGS='-O2' \
|
||||
HOSTLDFLAGS='-static' \
|
||||
envtools
|
||||
|
||||
# fw_setenv is the same program; create the link ourselves.
|
||||
RUN ln -sf fw_printenv tools/env/fw_setenv
|
||||
|
||||
RUN file tools/env/fw_printenv tools/env/fw_setenv
|
||||
|
||||
RUN readelf -d tools/env/fw_printenv || true
|
||||
|
||||
RUN ! readelf -d tools/env/fw_printenv | grep -q '(NEEDED)'
|
||||
|
||||
FROM scratch AS export
|
||||
COPY --from=build /src/tools/env/fw_printenv /fw_printenv
|
||||
COPY --from=build /src/tools/env/fw_setenv /fw_setenv
|
||||
@@ -1,17 +1,24 @@
|
||||
# Should be the same as upstream version in production
|
||||
VERSION ?= dev
|
||||
UBOOT_VERSION=v2026.01
|
||||
|
||||
# Target kube version
|
||||
KUBE_VERSION ?= v1.35.0
|
||||
|
||||
GIT_REV := $(shell git rev-parse HEAD)
|
||||
|
||||
BIN_DIR := bin
|
||||
OUT_DIR := out
|
||||
PACKAGES_DIR := packages
|
||||
BIN_DIR := bin
|
||||
OUT_DIR := out
|
||||
|
||||
UBOOT_TAR := $(PACKAGES_DIR)/uboot-$(UBOOT_VERSION).tar.gz
|
||||
|
||||
BUILDINFO_FILE := pkg/buildinfo/buildinfo_gen.go
|
||||
CRD_PATHS := ./pkg/apis/...
|
||||
|
||||
$(PACKAGES_DIR):
|
||||
mkdir -p $@
|
||||
|
||||
# Never cache this
|
||||
.buildinfo:
|
||||
@mkdir -p $(dir $(BUILDINFO_FILE))
|
||||
@@ -26,13 +33,27 @@ CRD_PATHS := ./pkg/apis/...
|
||||
')' \
|
||||
> $(BUILDINFO_FILE)
|
||||
|
||||
$(UBOOT_TAR): | $(PACKAGES_DIR)
|
||||
git clone --depth 1 --branch v2026.01 --filter=blob:none https://github.com/u-boot/u-boot.git $(OUT_DIR)/u-boot-$(UBOOT_VERSION)
|
||||
tar -C "$(OUT_DIR)/u-boot-$(UBOOT_VERSION)" -zcf "$@" .
|
||||
rm -rf $(OUT_DIR)/u-boot-$(UBOOT_VERSION)
|
||||
test -f $@
|
||||
|
||||
uboot-tools: $(UBOOT_TAR)
|
||||
docker buildx build --platform linux/arm64 \
|
||||
-f docker/uboot-tools.Dockerfile \
|
||||
--build-arg UBOOT_VERSION=$(UBOOT_VERSION) \
|
||||
--build-arg UBOOT_TAR=$(UBOOT_TAR) \
|
||||
--output type=local,dest=./$(OUT_DIR) .
|
||||
|
||||
build: .buildinfo
|
||||
mkdir -p $(BIN_DIR) $(OUT_DIR)/crds
|
||||
controller-gen crd paths=$(CRD_PATHS) output:crd:dir=$(OUT_DIR)/crds
|
||||
GOOS=linux GOARCH=arm64 go build -o $(BIN_DIR)/ctl-linux-aarch64-$(VERSION) ./cmd/ctl
|
||||
|
||||
build-agent: build
|
||||
build-agent: build uboot-tools
|
||||
docker build \
|
||||
-f docker/ctl-agent.Dockerfile \
|
||||
--platform=linux/arm64 \
|
||||
--build-arg VERSION=$(VERSION) \
|
||||
-t localhost/monok8s/control-agent:$(VERSION) .
|
||||
@@ -52,4 +73,4 @@ clean:
|
||||
|
||||
all: build build-agent build-local
|
||||
|
||||
.PHONY: clean all run .buildinfo build build-local build-agent
|
||||
.PHONY: clean all run .buildinfo build build-local build-agent uboot-tools
|
||||
|
||||
@@ -4,10 +4,10 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"k8s.io/klog/v2"
|
||||
monov1alpha1 "example.com/monok8s/pkg/apis/monok8s/v1alpha1"
|
||||
"example.com/monok8s/pkg/node"
|
||||
"example.com/monok8s/pkg/system"
|
||||
"k8s.io/klog/v2"
|
||||
)
|
||||
|
||||
type Runner struct {
|
||||
@@ -132,6 +132,11 @@ func NewRunner(cfg *monov1alpha1.MonoKSConfig) *Runner {
|
||||
Name: "Apply node metadata",
|
||||
Desc: "Apply labels/annotations to the local node if API server is reachable",
|
||||
},
|
||||
{
|
||||
RegKey: "ConfigureUBootCommands",
|
||||
Name: "Ensure fw_env config and u-boot-tools availablilty",
|
||||
Desc: "Install or generate /etc/fw_env.config for U-Boot environment access",
|
||||
},
|
||||
{
|
||||
RegKey: "ApplyControlAgentDaemonSetResources",
|
||||
Name: "Apply daemonset for control agent",
|
||||
|
||||
89
clitools/pkg/cmd/internal/fwprintenv.go
Normal file
89
clitools/pkg/cmd/internal/fwprintenv.go
Normal file
@@ -0,0 +1,89 @@
|
||||
package internal
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"k8s.io/klog/v2"
|
||||
|
||||
"example.com/monok8s/pkg/system"
|
||||
)
|
||||
|
||||
func newInternalFWPrintEnvCmd() *cobra.Command {
|
||||
var key string
|
||||
var configPath string
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "fw-printenv",
|
||||
Short: "Run fw_printenv",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
ctx := cmd.Context()
|
||||
|
||||
key = strings.TrimSpace(key)
|
||||
configPath = strings.TrimSpace(configPath)
|
||||
|
||||
if configPath == "" {
|
||||
configPath = defaultFWEnvConfigPath
|
||||
}
|
||||
|
||||
if _, err := os.Stat(configPath); err != nil {
|
||||
return fmt.Errorf("stat fw env config %q: %w", configPath, err)
|
||||
}
|
||||
|
||||
runner := system.NewRunner(system.RunnerConfig{
|
||||
DefaultTimeout: 15 * time.Second,
|
||||
StreamOutput: false,
|
||||
Logger: &system.StdLogger{},
|
||||
})
|
||||
|
||||
runArgs := []string{"-c", configPath}
|
||||
if key != "" {
|
||||
runArgs = append(runArgs, key)
|
||||
}
|
||||
|
||||
res, err := runner.RunWithOptions(
|
||||
ctx,
|
||||
"/fw_printenv",
|
||||
runArgs,
|
||||
system.RunOptions{
|
||||
Quiet: true,
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
if res != nil {
|
||||
klog.ErrorS(err, "fw_printenv failed",
|
||||
"key", key,
|
||||
"stdout", strings.TrimSpace(res.Stdout),
|
||||
"stderr", strings.TrimSpace(res.Stderr),
|
||||
)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
stdout := strings.TrimSpace(res.Stdout)
|
||||
stderr := strings.TrimSpace(res.Stderr)
|
||||
|
||||
if stdout != "" {
|
||||
fmt.Println(stdout)
|
||||
}
|
||||
if stderr != "" {
|
||||
klog.InfoS("fw_printenv stderr", "output", stderr)
|
||||
}
|
||||
|
||||
klog.InfoS("fw_printenv succeeded",
|
||||
"key", key,
|
||||
"configPath", configPath,
|
||||
)
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
cmd.Flags().StringVar(&key, "key", "", "U-Boot environment variable name to print")
|
||||
cmd.Flags().StringVar(&configPath, "config", defaultFWEnvConfigPath, "Path to fw_env.config")
|
||||
|
||||
return cmd
|
||||
}
|
||||
115
clitools/pkg/cmd/internal/fwsetenv.go
Normal file
115
clitools/pkg/cmd/internal/fwsetenv.go
Normal file
@@ -0,0 +1,115 @@
|
||||
package internal
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"k8s.io/klog/v2"
|
||||
|
||||
"example.com/monok8s/pkg/system"
|
||||
)
|
||||
|
||||
const defaultFWEnvConfigPath = "/host/etc/fw_env.config"
|
||||
|
||||
func newInternalFWSetEnvCmd() *cobra.Command {
|
||||
var key string
|
||||
var value string
|
||||
var configPath string
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "fw-setenv",
|
||||
Short: "Run fw_setenv",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
ctx := cmd.Context()
|
||||
|
||||
key = strings.TrimSpace(key)
|
||||
value = strings.TrimSpace(value)
|
||||
configPath = strings.TrimSpace(configPath)
|
||||
|
||||
if key == "" {
|
||||
return fmt.Errorf("--key is required")
|
||||
}
|
||||
if value == "" {
|
||||
return fmt.Errorf("--value is required")
|
||||
}
|
||||
if configPath == "" {
|
||||
configPath = defaultFWEnvConfigPath
|
||||
}
|
||||
|
||||
if _, err := os.Stat(configPath); err != nil {
|
||||
return fmt.Errorf("stat fw env config %q: %w", configPath, err)
|
||||
}
|
||||
|
||||
runner := system.NewRunner(system.RunnerConfig{
|
||||
DefaultTimeout: 15 * time.Second,
|
||||
StreamOutput: false,
|
||||
Logger: &system.StdLogger{},
|
||||
})
|
||||
|
||||
// Preflight first so failure is clearer than blindly writing.
|
||||
preflightRes, err := runner.RunWithOptions(
|
||||
ctx,
|
||||
"/fw_printenv",
|
||||
[]string{"-c", configPath},
|
||||
system.RunOptions{
|
||||
Quiet: true,
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
if preflightRes != nil {
|
||||
klog.ErrorS(err, "fw_printenv preflight failed",
|
||||
"stdout", strings.TrimSpace(preflightRes.Stdout),
|
||||
"stderr", strings.TrimSpace(preflightRes.Stderr),
|
||||
)
|
||||
}
|
||||
return fmt.Errorf("fw_printenv preflight: %w", err)
|
||||
}
|
||||
|
||||
res, err := runner.RunWithOptions(
|
||||
ctx,
|
||||
"/fw_setenv",
|
||||
[]string{
|
||||
"-c", configPath,
|
||||
key, value,
|
||||
},
|
||||
system.RunOptions{
|
||||
Quiet: true,
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
if res != nil {
|
||||
klog.ErrorS(err, "fw_setenv failed",
|
||||
"key", key,
|
||||
"value", value,
|
||||
"stdout", strings.TrimSpace(res.Stdout),
|
||||
"stderr", strings.TrimSpace(res.Stderr),
|
||||
)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
if strings.TrimSpace(res.Stdout) != "" {
|
||||
klog.InfoS("fw_setenv stdout", "output", strings.TrimSpace(res.Stdout))
|
||||
}
|
||||
if strings.TrimSpace(res.Stderr) != "" {
|
||||
klog.InfoS("fw_setenv stderr", "output", strings.TrimSpace(res.Stderr))
|
||||
}
|
||||
|
||||
klog.InfoS("fw_setenv succeeded",
|
||||
"key", key,
|
||||
"value", value,
|
||||
"configPath", configPath,
|
||||
)
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
cmd.Flags().StringVar(&key, "key", "", "U-Boot environment variable name")
|
||||
cmd.Flags().StringVar(&value, "value", "", "U-Boot environment variable value")
|
||||
cmd.Flags().StringVar(&configPath, "config", defaultFWEnvConfigPath, "Path to fw_env.config")
|
||||
|
||||
return cmd
|
||||
}
|
||||
@@ -1,9 +1,9 @@
|
||||
package internal
|
||||
|
||||
import (
|
||||
"github.com/spf13/cobra"
|
||||
"example.com/monok8s/pkg/bootstrap"
|
||||
"example.com/monok8s/pkg/config"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func NewCmdInternal() *cobra.Command {
|
||||
@@ -33,6 +33,9 @@ func NewCmdInternal() *cobra.Command {
|
||||
return nil
|
||||
},
|
||||
})
|
||||
cmd.AddCommand(newInternalFWSetEnvCmd())
|
||||
cmd.AddCommand(newInternalFWPrintEnvCmd())
|
||||
|
||||
cmd.PersistentFlags().StringVarP(&configPath, "config", "c", "", "path to MonoKSConfig yaml")
|
||||
return cmd
|
||||
}
|
||||
|
||||
37
clitools/pkg/controller/osupgrade/bootenv.go
Normal file
37
clitools/pkg/controller/osupgrade/bootenv.go
Normal file
@@ -0,0 +1,37 @@
|
||||
package osupgrade
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os/exec"
|
||||
)
|
||||
|
||||
type NextBootConfig struct {
|
||||
Key string
|
||||
Value string
|
||||
}
|
||||
|
||||
func SetNextBootEnv(ctx context.Context, cfg NextBootConfig) error {
|
||||
if cfg.Key == "" {
|
||||
return fmt.Errorf("boot env key is empty")
|
||||
}
|
||||
if cfg.Value == "" {
|
||||
return fmt.Errorf("boot env value is empty")
|
||||
}
|
||||
|
||||
cmd := exec.CommandContext(
|
||||
ctx,
|
||||
"/proc/self/exe",
|
||||
"internal",
|
||||
"fw-setenv",
|
||||
"--key", cfg.Key,
|
||||
"--value", cfg.Value,
|
||||
)
|
||||
|
||||
out, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
return fmt.Errorf("invoke internal fw-setenv: %w: %s", err, string(out))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -128,7 +128,22 @@ func HandleOSUpgrade(ctx context.Context, clients *kube.Clients,
|
||||
}
|
||||
|
||||
klog.Info(result)
|
||||
// TODO: fw_setenv
|
||||
if err := SetNextBootEnv(ctx, NextBootConfig{
|
||||
Key: "boot_part",
|
||||
Value: "B",
|
||||
}); err != nil {
|
||||
return failProgress(ctx, clients, osup, "set boot env", err)
|
||||
}
|
||||
|
||||
now = metav1.Now()
|
||||
osup.Status.LastUpdatedAt = &now
|
||||
osup.Status.Message = "image applied, verified, and next boot environment updated"
|
||||
osup.Status.Phase = monov1alpha1.OSUpgradeProgressPhaseRebooting
|
||||
|
||||
osup, err = updateProgressStatus(ctx, clients, osup_gvr, osup)
|
||||
if err != nil {
|
||||
return fmt.Errorf("update progress status: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -273,20 +273,8 @@ func applyControlAgentDaemonSet(ctx context.Context, kubeClient kubernetes.Inter
|
||||
Value: "/host/opt/monok8s/config/cluster.env",
|
||||
},
|
||||
{
|
||||
Name: "HOST_MOUNT_ROOT",
|
||||
Value: "/host/mnt/control-agent",
|
||||
},
|
||||
{
|
||||
Name: "HOST_DEV_DIR",
|
||||
Value: "/host/dev",
|
||||
},
|
||||
{
|
||||
Name: "HOST_PROC_DIR",
|
||||
Value: "/host/proc",
|
||||
},
|
||||
{
|
||||
Name: "HOST_RUN_DIR",
|
||||
Value: "/host/run",
|
||||
Name: "FW_ENV_CONFIG_FILE",
|
||||
Value: "/host/etc/fw_env.config",
|
||||
},
|
||||
},
|
||||
SecurityContext: &corev1.SecurityContext{
|
||||
@@ -295,22 +283,18 @@ func applyControlAgentDaemonSet(ctx context.Context, kubeClient kubernetes.Inter
|
||||
VolumeMounts: []corev1.VolumeMount{
|
||||
{
|
||||
Name: "host-dev",
|
||||
MountPath: "/host/dev",
|
||||
MountPath: "/dev",
|
||||
},
|
||||
{
|
||||
Name: "host-etc",
|
||||
MountPath: "/host/etc",
|
||||
ReadOnly: true,
|
||||
},
|
||||
{
|
||||
Name: "host-config",
|
||||
MountPath: "/host/opt/monok8s/config",
|
||||
ReadOnly: true,
|
||||
},
|
||||
{
|
||||
Name: "host-run",
|
||||
MountPath: "/host/run",
|
||||
},
|
||||
{
|
||||
Name: "host-proc",
|
||||
MountPath: "/host/proc",
|
||||
ReadOnly: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -324,6 +308,15 @@ func applyControlAgentDaemonSet(ctx context.Context, kubeClient kubernetes.Inter
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "host-etc",
|
||||
VolumeSource: corev1.VolumeSource{
|
||||
HostPath: &corev1.HostPathVolumeSource{
|
||||
Path: "/etc",
|
||||
Type: hostPathType(corev1.HostPathDirectory),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "host-config",
|
||||
VolumeSource: corev1.VolumeSource{
|
||||
@@ -333,24 +326,6 @@ func applyControlAgentDaemonSet(ctx context.Context, kubeClient kubernetes.Inter
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "host-run",
|
||||
VolumeSource: corev1.VolumeSource{
|
||||
HostPath: &corev1.HostPathVolumeSource{
|
||||
Path: "/run",
|
||||
Type: hostPathType(corev1.HostPathDirectory),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "host-proc",
|
||||
VolumeSource: corev1.VolumeSource{
|
||||
HostPath: &corev1.HostPathVolumeSource{
|
||||
Path: "/proc",
|
||||
Type: hostPathType(corev1.HostPathDirectory),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -7,8 +7,8 @@ import (
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"k8s.io/klog/v2"
|
||||
system "example.com/monok8s/pkg/system"
|
||||
"k8s.io/klog/v2"
|
||||
)
|
||||
|
||||
type NetworkConfig struct {
|
||||
|
||||
Reference in New Issue
Block a user