penguin/monok8s

k8s image for Mono Gateway Dev Kit

clitools/pkg/node/uboot/env_writer.go

raw ยท 3178 bytes

package uboot

import (
	"context"
	"fmt"
	"strings"
	"time"

	"k8s.io/klog/v2"

	"example.com/monok8s/pkg/system"
)

func NewFWEnvWriter(configPath string, ctlPath string) *FWEnvWriter {
	return &FWEnvWriter{
		Runner: system.NewRunner(system.RunnerConfig{
			DefaultTimeout: 15 * time.Second,
			StreamOutput:   false,
			Logger:         &system.StdLogger{},
		}),
		ConfigPath: configPath,
		CtlPath:    ctlPath,
	}
}

func (w *FWEnvWriter) GetEnv(ctx context.Context, key string) (string, error) {
	key = strings.TrimSpace(key)
	if key == "" {
		return "", fmt.Errorf("key is required")
	}

	args := []string{
		"internal", "fw-printenv",
		"--key", key,
		"--config", w.ConfigPath,
	}

	res, err := w.Runner.RunWithOptions(ctx, w.CtlPath, args, system.RunOptions{Quiet: true})
	if err != nil {
		if res != nil {
			return "", fmt.Errorf("fw-printenv %q: %w (stdout=%q stderr=%q)",
				key, err, strings.TrimSpace(res.Stdout), strings.TrimSpace(res.Stderr))
		}
		return "", fmt.Errorf("fw-printenv %q: %w", key, err)
	}

	out := strings.TrimSpace(res.Stdout)
	if out == "" {
		return "", fmt.Errorf("empty output for key %q", key)
	}

	parts := strings.SplitN(out, "=", 2)
	if len(parts) != 2 {
		return "", fmt.Errorf("unexpected fw_printenv output for %q: %q", key, out)
	}
	if parts[0] != key {
		return "", fmt.Errorf("unexpected fw_printenv key: got %q want %q", parts[0], key)
	}

	return parts[1], nil
}

func (w *FWEnvWriter) SetEnv(ctx context.Context, key, value string) error {
	key = strings.TrimSpace(key)
	value = strings.TrimSpace(value)

	if key == "" {
		return fmt.Errorf("key is required")
	}
	if value == "" {
		return fmt.Errorf("value is required")
	}

	args := []string{
		"internal", "fw-setenv",
		"--key", key,
		"--value", value,
		"--config", w.ConfigPath,
	}

	res, err := w.Runner.RunWithOptions(ctx, w.CtlPath, args, system.RunOptions{Quiet: true})
	if err != nil {
		if res != nil {
			return fmt.Errorf("fw-setenv %q: %w (stdout=%q stderr=%q)",
				key, err, strings.TrimSpace(res.Stdout), strings.TrimSpace(res.Stderr))
		}
		return fmt.Errorf("fw-setenv %q: %w", key, err)
	}

	return nil
}

func (w *FWEnvWriter) SetEnvIfDifferent(ctx context.Context, key, desired string) error {
	current, err := w.GetEnv(ctx, key)
	if err == nil && current == desired {
		klog.V(1).InfoS("fw env already matches", "key", key, "value", desired)
		return nil
	}

	if err != nil {
		klog.InfoS("fw env key missing or unreadable, will set",
			"key", key,
			"desired", desired,
			"error", err,
		)
	} else {
		klog.InfoS("fw env drift detected, updating",
			"key", key,
			"current", current,
			"desired", desired,
		)
	}

	return w.SetEnv(ctx, key, desired)
}

func (w *FWEnvWriter) EnsureBootEnv(ctx context.Context, cfg BootEnvConfig) error {
	if err := cfg.Validate(); err != nil {
		return err
	}

	envs := []struct {
		Key   string
		Value string
	}{
		{"bootcmd", cfg.bootCmdOrDefault()},
		{"boot_source", string(cfg.BootSource)},
		{"boot_part", string(cfg.BootPart)},
	}

	for _, kv := range envs {
		if err := w.SetEnvIfDifferent(ctx, kv.Key, kv.Value); err != nil {
			return fmt.Errorf("ensure %s: %w", kv.Key, err)
		}
	}

	return nil
}