emissary/internal/agent/controller/openwrt/uci_controller.go

118 lines
2.7 KiB
Go

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.NameUCI, 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("spec", uciSpec.SpecName()), 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{}