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)}, {"boot_disk", fmt.Sprintf("%d", cfg.BootDisk)}, {"rootfs_a_partnum", fmt.Sprintf("%d", cfg.RootfsAPartNum)}, {"rootfs_b_partnum", fmt.Sprintf("%d", cfg.RootfsBPartNum)}, {"data_partnum", fmt.Sprintf("%d", cfg.DataPartNum)}, {"linux_root_prefix", cfg.LinuxRootPrefix}, } 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 }