package openwrt import ( "bytes" "context" "os" "os/exec" "forge.cadoles.com/Cadoles/emissary/internal/agent" "forge.cadoles.com/Cadoles/emissary/internal/openwrt/uci" ucispec "forge.cadoles.com/Cadoles/emissary/internal/spec/uci" "github.com/pkg/errors" "gitlab.com/wpetit/goweb/logger" ) type UCIController struct { binPath string currentSpecRevision int } // Name implements node.Controller. func (*UCIController) Name() string { return "uci-controller" } // Reconcile implements node.Controller. func (c *UCIController) Reconcile(ctx context.Context, state *agent.State) error { uciSpec := ucispec.NewSpec() if err := state.GetSpec(ucispec.Name, ucispec.Version, uciSpec); err != nil { if errors.Is(err, agent.ErrSpecNotFound) { logger.Info(ctx, "could not find uci spec, doing nothing") return nil } return errors.WithStack(err) } logger.Info(ctx, "retrieved spec", logger.F("name", uciSpec.SpecDefinitionName()), logger.F("version", uciSpec.SpecDefinitionVersion()), logger.F("revision", uciSpec.SpecRevision()), ) if c.currentSpecRevision == uciSpec.SpecRevision() { logger.Info(ctx, "spec revision did not change, doing nothing") return nil } if err := c.updateConfiguration(ctx, uciSpec); err != nil { err = errors.WithStack(err) logger.Error(ctx, "could not update configuration", logger.CapturedE(err)) return nil } c.currentSpecRevision = uciSpec.SpecRevision() logger.Info(ctx, "updating current spec revision", logger.F("revision", c.currentSpecRevision)) return nil } func (c *UCIController) updateConfiguration(ctx context.Context, spec *ucispec.Spec) error { logger.Info(ctx, "importing uci config") if err := c.importConfig(ctx, spec.Config); err != nil { return errors.WithStack(err) } if err := c.execPostImportCommands(ctx, spec.PostImportCommands); err != nil { return errors.WithStack(err) } return nil } func (c *UCIController) importConfig(ctx context.Context, uci *uci.UCI) error { cmd := exec.CommandContext(ctx, c.binPath, "import") var buf bytes.Buffer if _, err := buf.WriteString(uci.Export()); err != nil { return errors.WithStack(err) } cmd.Stdin = &buf cmd.Stderr = os.Stderr cmd.Stdout = os.Stdout if err := cmd.Run(); err != nil { return errors.WithStack(err) } return nil } func (c *UCIController) execPostImportCommands(ctx context.Context, commands []*ucispec.UCIPostImportCommand) error { for _, postImportCmd := range commands { cmd := exec.CommandContext(ctx, postImportCmd.Command, postImportCmd.Args...) cmd.Stderr = os.Stderr cmd.Stdout = os.Stdout if err := cmd.Run(); err != nil { return errors.WithStack(err) } } return nil } func NewUCIController(binPath string) *UCIController { return &UCIController{ binPath: binPath, currentSpecRevision: -1, } } var _ agent.Controller = &UCIController{}