feat(controller,sysupgrade): openwrt upgrade controller
This commit is contained in:
parent
97a60e2617
commit
0b783c374a
@ -6,6 +6,8 @@ import (
|
|||||||
"forge.cadoles.com/Cadoles/emissary/internal/command"
|
"forge.cadoles.com/Cadoles/emissary/internal/command"
|
||||||
"forge.cadoles.com/Cadoles/emissary/internal/command/agent"
|
"forge.cadoles.com/Cadoles/emissary/internal/command/agent"
|
||||||
"forge.cadoles.com/Cadoles/emissary/internal/command/api"
|
"forge.cadoles.com/Cadoles/emissary/internal/command/api"
|
||||||
|
|
||||||
|
_ "forge.cadoles.com/Cadoles/emissary/internal/imports/spec"
|
||||||
)
|
)
|
||||||
|
|
||||||
// nolint: gochecknoglobals
|
// nolint: gochecknoglobals
|
||||||
|
@ -7,8 +7,9 @@ import (
|
|||||||
"forge.cadoles.com/Cadoles/emissary/internal/command/api"
|
"forge.cadoles.com/Cadoles/emissary/internal/command/api"
|
||||||
"forge.cadoles.com/Cadoles/emissary/internal/command/server"
|
"forge.cadoles.com/Cadoles/emissary/internal/command/server"
|
||||||
|
|
||||||
_ "github.com/jackc/pgx/v5/stdlib"
|
_ "forge.cadoles.com/Cadoles/emissary/internal/imports/format"
|
||||||
_ "modernc.org/sqlite"
|
_ "forge.cadoles.com/Cadoles/emissary/internal/imports/spec"
|
||||||
|
_ "forge.cadoles.com/Cadoles/emissary/internal/imports/sql"
|
||||||
)
|
)
|
||||||
|
|
||||||
// nolint: gochecknoglobals
|
// nolint: gochecknoglobals
|
||||||
|
1
go.mod
1
go.mod
@ -22,7 +22,6 @@ require (
|
|||||||
github.com/mitchellh/mapstructure v1.5.0
|
github.com/mitchellh/mapstructure v1.5.0
|
||||||
github.com/pkg/errors v0.9.1
|
github.com/pkg/errors v0.9.1
|
||||||
github.com/qri-io/jsonschema v0.2.1
|
github.com/qri-io/jsonschema v0.2.1
|
||||||
github.com/santhosh-tekuri/jsonschema/v5 v5.1.1
|
|
||||||
github.com/urfave/cli/v2 v2.24.4
|
github.com/urfave/cli/v2 v2.24.4
|
||||||
gitlab.com/wpetit/goweb v0.0.0-20230227162855-a1f09bafccb3
|
gitlab.com/wpetit/goweb v0.0.0-20230227162855-a1f09bafccb3
|
||||||
gopkg.in/yaml.v3 v3.0.1
|
gopkg.in/yaml.v3 v3.0.1
|
||||||
|
2
go.sum
2
go.sum
@ -1169,8 +1169,6 @@ github.com/rwtodd/Go.Sed v0.0.0-20210816025313-55464686f9ef/go.mod h1:8AEUvGVi2u
|
|||||||
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
|
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
|
||||||
github.com/safchain/ethtool v0.0.0-20190326074333-42ed695e3de8/go.mod h1:Z0q5wiBQGYcxhMZ6gUqHn6pYNLypFAvaL3UvgZLR0U4=
|
github.com/safchain/ethtool v0.0.0-20190326074333-42ed695e3de8/go.mod h1:Z0q5wiBQGYcxhMZ6gUqHn6pYNLypFAvaL3UvgZLR0U4=
|
||||||
github.com/safchain/ethtool v0.0.0-20210803160452-9aa261dae9b1/go.mod h1:Z0q5wiBQGYcxhMZ6gUqHn6pYNLypFAvaL3UvgZLR0U4=
|
github.com/safchain/ethtool v0.0.0-20210803160452-9aa261dae9b1/go.mod h1:Z0q5wiBQGYcxhMZ6gUqHn6pYNLypFAvaL3UvgZLR0U4=
|
||||||
github.com/santhosh-tekuri/jsonschema/v5 v5.1.1 h1:lEOLY2vyGIqKWUI9nzsOJRV3mb3WC9dXYORsLEUcoeY=
|
|
||||||
github.com/santhosh-tekuri/jsonschema/v5 v5.1.1/go.mod h1:FKdcjfQW6rpZSnxxUvEA5H/cDPdvJ/SZJQLWWXWGrZ0=
|
|
||||||
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
|
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
|
||||||
github.com/sclevine/agouti v3.0.0+incompatible/go.mod h1:b4WX9W9L1sfQKXeJf1mUTLZKJ48R1S7H23Ji7oFO5Bw=
|
github.com/sclevine/agouti v3.0.0+incompatible/go.mod h1:b4WX9W9L1sfQKXeJf1mUTLZKJ48R1S7H23Ji7oFO5Bw=
|
||||||
github.com/sclevine/spec v1.2.0/go.mod h1:W4J29eT/Kzv7/b9IWLB055Z+qvVC9vt0Arko24q7p+U=
|
github.com/sclevine/spec v1.2.0/go.mod h1:W4J29eT/Kzv7/b9IWLB055Z+qvVC9vt0Arko24q7p+U=
|
||||||
|
@ -28,8 +28,7 @@ import (
|
|||||||
"github.com/lestrrat-go/jwx/v2/jwk"
|
"github.com/lestrrat-go/jwx/v2/jwk"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
|
|
||||||
_ "forge.cadoles.com/arcad/edge/pkg/module/auth/http/passwd/argon2id"
|
_ "forge.cadoles.com/Cadoles/emissary/internal/imports/passwd"
|
||||||
_ "forge.cadoles.com/arcad/edge/pkg/module/auth/http/passwd/plain"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type Server struct {
|
type Server struct {
|
||||||
|
31
internal/agent/controller/openwrt/hash.go
Normal file
31
internal/agent/controller/openwrt/hash.go
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
package openwrt
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/sha256"
|
||||||
|
"encoding/hex"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
func hash(path string) (string, error) {
|
||||||
|
file, err := os.Open(path)
|
||||||
|
if err != nil {
|
||||||
|
return "", errors.WithStack(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
hasher := sha256.New()
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
if err := file.Close(); err != nil {
|
||||||
|
panic(errors.WithStack(err))
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
if _, err := io.Copy(hasher, file); err != nil {
|
||||||
|
return "", errors.WithStack(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return hex.EncodeToString(hasher.Sum(nil)), nil
|
||||||
|
}
|
17
internal/agent/controller/openwrt/spec/sysupgrade/init.go
Normal file
17
internal/agent/controller/openwrt/spec/sysupgrade/init.go
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
package sysupgrade
|
||||||
|
|
||||||
|
import (
|
||||||
|
_ "embed"
|
||||||
|
|
||||||
|
"forge.cadoles.com/Cadoles/emissary/internal/spec"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
//go:embed schema.json
|
||||||
|
var schema []byte
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
if err := spec.Register(Name, schema); err != nil {
|
||||||
|
panic(errors.WithStack(err))
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||||
|
"$id": "https://sysupgrade.openwrt.emissary.cadoles.com/spec.json",
|
||||||
|
"title": "SysUpgradeSpec",
|
||||||
|
"description": "Emissary 'SysUpgrade' specification",
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"url": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"sha256sum": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"version": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["url", "sha256sum", "version"],
|
||||||
|
"additionalProperties": false
|
||||||
|
}
|
38
internal/agent/controller/openwrt/spec/sysupgrade/spec.go
Normal file
38
internal/agent/controller/openwrt/spec/sysupgrade/spec.go
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
package sysupgrade
|
||||||
|
|
||||||
|
import (
|
||||||
|
"forge.cadoles.com/Cadoles/emissary/internal/spec"
|
||||||
|
)
|
||||||
|
|
||||||
|
const Name spec.Name = "sysupgrade.openwrt.emissary.cadoles.com"
|
||||||
|
|
||||||
|
type Spec struct {
|
||||||
|
Revision int `json:"revision"`
|
||||||
|
URL string `json:"url"`
|
||||||
|
SHA256Sum string `json:"sha256sum"`
|
||||||
|
Version string `json:"version"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Spec) SpecName() spec.Name {
|
||||||
|
return Name
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Spec) SpecRevision() int {
|
||||||
|
return s.Revision
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Spec) SpecData() map[string]any {
|
||||||
|
return map[string]any{
|
||||||
|
"url": s.URL,
|
||||||
|
"version": s.Version,
|
||||||
|
"sha256sum": s.SHA256Sum,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewSpec() *Spec {
|
||||||
|
return &Spec{
|
||||||
|
Revision: -1,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ spec.Spec = &Spec{}
|
9
internal/agent/controller/openwrt/spec/sysupgrade/testdata/spec-ok.json
vendored
Normal file
9
internal/agent/controller/openwrt/spec/sysupgrade/testdata/spec-ok.json
vendored
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"name": "sysupgrade.openwrt.emissary.cadoles.com",
|
||||||
|
"data": {
|
||||||
|
"url": "http://example.com/firmware.img",
|
||||||
|
"sha256sum": "58019192dacdae17755707719707db007e26dac856102280583fbd18427dd352",
|
||||||
|
"version": "0.0.0"
|
||||||
|
},
|
||||||
|
"revision": 0
|
||||||
|
}
|
@ -0,0 +1,65 @@
|
|||||||
|
package sysupgrade
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"io/ioutil"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"forge.cadoles.com/Cadoles/emissary/internal/spec"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
type validatorTestCase struct {
|
||||||
|
Name string
|
||||||
|
Source string
|
||||||
|
ShouldFail bool
|
||||||
|
}
|
||||||
|
|
||||||
|
var validatorTestCases = []validatorTestCase{
|
||||||
|
{
|
||||||
|
Name: "SpecOK",
|
||||||
|
Source: "testdata/spec-ok.json",
|
||||||
|
ShouldFail: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestValidator(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
validator := spec.NewValidator()
|
||||||
|
if err := validator.Register(Name, schema); err != nil {
|
||||||
|
t.Fatalf("+%v", errors.WithStack(err))
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range validatorTestCases {
|
||||||
|
func(tc validatorTestCase) {
|
||||||
|
t.Run(tc.Name, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
rawSpec, err := ioutil.ReadFile(tc.Source)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("+%v", errors.WithStack(err))
|
||||||
|
}
|
||||||
|
|
||||||
|
var spec spec.RawSpec
|
||||||
|
|
||||||
|
if err := json.Unmarshal(rawSpec, &spec); err != nil {
|
||||||
|
t.Fatalf("+%v", errors.WithStack(err))
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
err = validator.Validate(ctx, &spec)
|
||||||
|
|
||||||
|
if !tc.ShouldFail && err != nil {
|
||||||
|
t.Errorf("+%v", errors.WithStack(err))
|
||||||
|
}
|
||||||
|
|
||||||
|
if tc.ShouldFail && err == nil {
|
||||||
|
t.Error("validation should have failed")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}(tc)
|
||||||
|
}
|
||||||
|
}
|
177
internal/agent/controller/openwrt/sysupgrade_controller.go
Normal file
177
internal/agent/controller/openwrt/sysupgrade_controller.go
Normal file
@ -0,0 +1,177 @@
|
|||||||
|
package openwrt
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"forge.cadoles.com/Cadoles/emissary/internal/agent"
|
||||||
|
"forge.cadoles.com/Cadoles/emissary/internal/agent/controller/openwrt/spec/sysupgrade"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"gitlab.com/wpetit/goweb/logger"
|
||||||
|
)
|
||||||
|
|
||||||
|
type SysUpgradeController struct {
|
||||||
|
client *http.Client
|
||||||
|
command string
|
||||||
|
args []string
|
||||||
|
firmwareVersion FirmwareVersion
|
||||||
|
}
|
||||||
|
|
||||||
|
// Name implements agent.Controller
|
||||||
|
func (*SysUpgradeController) Name() string {
|
||||||
|
return "sysupgrade-controller"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reconcile implements agent.Controller
|
||||||
|
func (c *SysUpgradeController) Reconcile(ctx context.Context, state *agent.State) error {
|
||||||
|
sysSpec := sysupgrade.NewSpec()
|
||||||
|
|
||||||
|
if err := state.GetSpec(sysupgrade.Name, sysSpec); err != nil {
|
||||||
|
if errors.Is(err, agent.ErrSpecNotFound) {
|
||||||
|
logger.Info(ctx, "could not find sysupgrade spec, doing nothing")
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return errors.WithStack(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
firmwareVersion, err := c.firmwareVersion.FirmwareVersion(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return errors.WithStack(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx = logger.With(ctx,
|
||||||
|
logger.F("currentFirmwareVersion", firmwareVersion),
|
||||||
|
logger.F("newFirmwareVersion", sysSpec.Version),
|
||||||
|
)
|
||||||
|
|
||||||
|
if firmwareVersion == sysSpec.Version {
|
||||||
|
logger.Info(ctx, "firmware version did not change, doing nothing")
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
downloadDir, err := os.MkdirTemp(os.TempDir(), "emissary_sysupgrade_*")
|
||||||
|
if err != nil {
|
||||||
|
return errors.WithStack(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
if err := os.RemoveAll(downloadDir); err != nil {
|
||||||
|
logger.Error(
|
||||||
|
ctx, "could not remove download direction",
|
||||||
|
logger.E(errors.WithStack(err)),
|
||||||
|
logger.F("downloadDir", downloadDir),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
firmwareFile := filepath.Join(downloadDir, "firmware.bin")
|
||||||
|
|
||||||
|
logger.Info(
|
||||||
|
ctx, "downloading firmware",
|
||||||
|
logger.F("url", sysSpec.URL), logger.F("sha256sum", sysSpec.SHA256Sum),
|
||||||
|
)
|
||||||
|
|
||||||
|
if err := c.downloadFile(ctx, sysSpec.URL, sysSpec.SHA256Sum, firmwareFile); err != nil {
|
||||||
|
return errors.WithStack(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.Info(ctx, "upgrading firmware")
|
||||||
|
|
||||||
|
if err := c.upgradeFirmware(ctx, firmwareFile); err != nil {
|
||||||
|
return errors.WithStack(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *SysUpgradeController) downloadFile(ctx context.Context, url string, sha256sum string, dest string) error {
|
||||||
|
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
|
||||||
|
if err != nil {
|
||||||
|
return errors.WithStack(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := c.client.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return errors.WithStack(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
if err := res.Body.Close(); err != nil && !errors.Is(err, os.ErrClosed) {
|
||||||
|
panic(errors.WithStack(err))
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
tmp, err := os.CreateTemp(filepath.Dir(dest), "download_")
|
||||||
|
if err != nil {
|
||||||
|
return errors.WithStack(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
if err := os.Remove(tmp.Name()); err != nil && !os.IsNotExist(err) {
|
||||||
|
panic(errors.WithStack(err))
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
if _, err := io.Copy(tmp, res.Body); err != nil {
|
||||||
|
return errors.WithStack(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
tmpFileHash, err := hash(tmp.Name())
|
||||||
|
if err != nil {
|
||||||
|
return errors.WithStack(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if tmpFileHash != sha256sum {
|
||||||
|
return errors.Errorf("sha256 sum mismatch: expected '%s', got '%s'", sha256sum, tmpFileHash)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := os.Rename(tmp.Name(), dest); err != nil {
|
||||||
|
return errors.WithStack(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *SysUpgradeController) upgradeFirmware(ctx context.Context, firmwareFile string) error {
|
||||||
|
templatizedArgs := make([]string, len(c.args))
|
||||||
|
for i, a := range c.args {
|
||||||
|
templatizedArgs[i] = strings.Replace(a, FirmwareFileTemplate, firmwareFile, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
command := exec.CommandContext(ctx, c.command, templatizedArgs...)
|
||||||
|
|
||||||
|
command.Stdout = os.Stdout
|
||||||
|
command.Stderr = os.Stderr
|
||||||
|
|
||||||
|
logger.Debug(ctx, "executing command", logger.F("command", c.command), logger.F("args", templatizedArgs))
|
||||||
|
|
||||||
|
if err := command.Run(); err != nil {
|
||||||
|
return errors.WithStack(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewSysUpgradeController(funcs ...SysUpgradeOptionFunc) *SysUpgradeController {
|
||||||
|
opts := defaultSysUpgradeOptions()
|
||||||
|
for _, fn := range funcs {
|
||||||
|
fn(opts)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &SysUpgradeController{
|
||||||
|
command: opts.Command,
|
||||||
|
args: opts.Args,
|
||||||
|
client: opts.Client,
|
||||||
|
firmwareVersion: opts.FirmwareVersion,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ agent.Controller = &SysUpgradeController{}
|
@ -0,0 +1,46 @@
|
|||||||
|
package openwrt
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"gitlab.com/wpetit/goweb/logger"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ShellFirmwareVersion struct {
|
||||||
|
command string
|
||||||
|
args []string
|
||||||
|
}
|
||||||
|
|
||||||
|
// FirmwareVersion implements FirmwareVersion
|
||||||
|
func (fv *ShellFirmwareVersion) FirmwareVersion(ctx context.Context) (string, error) {
|
||||||
|
command := exec.CommandContext(ctx, fv.command, fv.args...)
|
||||||
|
|
||||||
|
var buf bytes.Buffer
|
||||||
|
|
||||||
|
command.Stdout = &buf
|
||||||
|
command.Stderr = os.Stderr
|
||||||
|
|
||||||
|
logger.Debug(ctx, "executing command", logger.F("command", fv.command), logger.F("args", fv.args))
|
||||||
|
|
||||||
|
if err := command.Run(); err != nil {
|
||||||
|
return "", errors.WithStack(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
version := strings.TrimSpace(buf.String())
|
||||||
|
|
||||||
|
return version, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewShellFirmwareVersion(command string, args ...string) *ShellFirmwareVersion {
|
||||||
|
return &ShellFirmwareVersion{
|
||||||
|
command: command,
|
||||||
|
args: args,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ FirmwareVersion = &ShellFirmwareVersion{}
|
58
internal/agent/controller/openwrt/sysupgrade_options.go
Normal file
58
internal/agent/controller/openwrt/sysupgrade_options.go
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
package openwrt
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"net/http"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
const FirmwareFileTemplate = "%FIRMWARE%"
|
||||||
|
|
||||||
|
type FirmwareVersion interface {
|
||||||
|
FirmwareVersion(context.Context) (string, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type SysUpgradeOptions struct {
|
||||||
|
Command string
|
||||||
|
Args []string
|
||||||
|
FirmwareVersion FirmwareVersion
|
||||||
|
Client *http.Client
|
||||||
|
}
|
||||||
|
|
||||||
|
func defaultSysUpgradeOptions() *SysUpgradeOptions {
|
||||||
|
return &SysUpgradeOptions{
|
||||||
|
Command: `echo`,
|
||||||
|
Args: []string{`[DUMMY UPGRADE]`, FirmwareFileTemplate},
|
||||||
|
Client: &http.Client{
|
||||||
|
Timeout: 30 * time.Second,
|
||||||
|
},
|
||||||
|
FirmwareVersion: NewShellFirmwareVersion(`echo`, "0.0.0-dummy"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type SysUpgradeOptionFunc func(*SysUpgradeOptions)
|
||||||
|
|
||||||
|
func WithSysUpgradeCommand(command string, args ...string) SysUpgradeOptionFunc {
|
||||||
|
return func(opts *SysUpgradeOptions) {
|
||||||
|
opts.Command = command
|
||||||
|
opts.Args = args
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func WithSysUpgradeFirmwareVersion(firmwareVersion FirmwareVersion) SysUpgradeOptionFunc {
|
||||||
|
return func(opts *SysUpgradeOptions) {
|
||||||
|
opts.FirmwareVersion = firmwareVersion
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func WithSysUpgradeShellFirmwareVersion(command string, args ...string) SysUpgradeOptionFunc {
|
||||||
|
return func(opts *SysUpgradeOptions) {
|
||||||
|
opts.FirmwareVersion = NewShellFirmwareVersion(command, args...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func WithSysUpgradeClient(client *http.Client) SysUpgradeOptionFunc {
|
||||||
|
return func(opts *SysUpgradeOptions) {
|
||||||
|
opts.Client = client
|
||||||
|
}
|
||||||
|
}
|
@ -10,7 +10,6 @@ import (
|
|||||||
"forge.cadoles.com/Cadoles/emissary/internal/command/common"
|
"forge.cadoles.com/Cadoles/emissary/internal/command/common"
|
||||||
"forge.cadoles.com/Cadoles/emissary/internal/openwrt/uci"
|
"forge.cadoles.com/Cadoles/emissary/internal/openwrt/uci"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
_ "github.com/santhosh-tekuri/jsonschema/v5/httploader"
|
|
||||||
"github.com/urfave/cli/v2"
|
"github.com/urfave/cli/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -66,6 +66,29 @@ func RunCommand() *cli.Command {
|
|||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ctrlConf.SysUpgrade.Enabled {
|
||||||
|
sysUpgradeArgs := make([]string, 0)
|
||||||
|
if len(ctrlConf.SysUpgrade.SysUpgradeCommand) > 1 {
|
||||||
|
sysUpgradeArgs = ctrlConf.SysUpgrade.SysUpgradeCommand[1:]
|
||||||
|
}
|
||||||
|
|
||||||
|
firmwareVersionArgs := make([]string, 0)
|
||||||
|
if len(ctrlConf.SysUpgrade.FirmwareVersionCommand) > 1 {
|
||||||
|
firmwareVersionArgs = ctrlConf.SysUpgrade.FirmwareVersionCommand[1:]
|
||||||
|
}
|
||||||
|
|
||||||
|
controllers = append(controllers, openwrt.NewSysUpgradeController(
|
||||||
|
openwrt.WithSysUpgradeCommand(
|
||||||
|
ctrlConf.SysUpgrade.SysUpgradeCommand[0],
|
||||||
|
sysUpgradeArgs...,
|
||||||
|
),
|
||||||
|
openwrt.WithSysUpgradeShellFirmwareVersion(
|
||||||
|
ctrlConf.SysUpgrade.FirmwareVersionCommand[0],
|
||||||
|
firmwareVersionArgs...,
|
||||||
|
),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
key, err := jwk.LoadOrGenerate(string(conf.Agent.PrivateKeyPath), jwk.DefaultKeySize)
|
key, err := jwk.LoadOrGenerate(string(conf.Agent.PrivateKeyPath), jwk.DefaultKeySize)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.WithStack(err)
|
return errors.WithStack(err)
|
||||||
|
@ -13,11 +13,6 @@ import (
|
|||||||
jsonpatch "github.com/evanphx/json-patch/v5"
|
jsonpatch "github.com/evanphx/json-patch/v5"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/urfave/cli/v2"
|
"github.com/urfave/cli/v2"
|
||||||
|
|
||||||
// Import specs
|
|
||||||
_ "forge.cadoles.com/Cadoles/emissary/internal/spec/app"
|
|
||||||
_ "forge.cadoles.com/Cadoles/emissary/internal/spec/proxy"
|
|
||||||
_ "forge.cadoles.com/Cadoles/emissary/internal/spec/uci"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func UpdateCommand() *cli.Command {
|
func UpdateCommand() *cli.Command {
|
||||||
|
@ -3,10 +3,6 @@ package api
|
|||||||
import (
|
import (
|
||||||
"forge.cadoles.com/Cadoles/emissary/internal/command/api/agent"
|
"forge.cadoles.com/Cadoles/emissary/internal/command/api/agent"
|
||||||
"github.com/urfave/cli/v2"
|
"github.com/urfave/cli/v2"
|
||||||
|
|
||||||
// Output format
|
|
||||||
_ "forge.cadoles.com/Cadoles/emissary/internal/format/json"
|
|
||||||
_ "forge.cadoles.com/Cadoles/emissary/internal/format/table"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func Root() *cli.Command {
|
func Root() *cli.Command {
|
||||||
|
@ -6,7 +6,6 @@ import (
|
|||||||
"forge.cadoles.com/Cadoles/emissary/internal/command/common"
|
"forge.cadoles.com/Cadoles/emissary/internal/command/common"
|
||||||
"forge.cadoles.com/Cadoles/emissary/internal/config"
|
"forge.cadoles.com/Cadoles/emissary/internal/config"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
_ "github.com/santhosh-tekuri/jsonschema/v5/httploader"
|
|
||||||
"github.com/urfave/cli/v2"
|
"github.com/urfave/cli/v2"
|
||||||
"gitlab.com/wpetit/goweb/logger"
|
"gitlab.com/wpetit/goweb/logger"
|
||||||
)
|
)
|
||||||
|
@ -9,10 +9,6 @@ import (
|
|||||||
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/urfave/cli/v2"
|
"github.com/urfave/cli/v2"
|
||||||
|
|
||||||
// Spec validation
|
|
||||||
_ "forge.cadoles.com/Cadoles/emissary/internal/spec/proxy"
|
|
||||||
_ "forge.cadoles.com/Cadoles/emissary/internal/spec/uci"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func Main(buildDate, projectVersion, gitRef, defaultConfigPath string, commands ...*cli.Command) {
|
func Main(buildDate, projectVersion, gitRef, defaultConfigPath string, commands ...*cli.Command) {
|
||||||
|
@ -7,7 +7,6 @@ import (
|
|||||||
"forge.cadoles.com/Cadoles/emissary/internal/command/common"
|
"forge.cadoles.com/Cadoles/emissary/internal/command/common"
|
||||||
"forge.cadoles.com/Cadoles/emissary/internal/server"
|
"forge.cadoles.com/Cadoles/emissary/internal/server"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
_ "github.com/santhosh-tekuri/jsonschema/v5/httploader"
|
|
||||||
"github.com/urfave/cli/v2"
|
"github.com/urfave/cli/v2"
|
||||||
"gitlab.com/wpetit/goweb/logger"
|
"gitlab.com/wpetit/goweb/logger"
|
||||||
)
|
)
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
package config
|
package config
|
||||||
|
|
||||||
|
import "forge.cadoles.com/Cadoles/emissary/internal/agent/controller/openwrt"
|
||||||
|
|
||||||
type AgentConfig struct {
|
type AgentConfig struct {
|
||||||
ServerURL InterpolatedString `yaml:"serverUrl"`
|
ServerURL InterpolatedString `yaml:"serverUrl"`
|
||||||
PrivateKeyPath InterpolatedString `yaml:"privateKeyPath"`
|
PrivateKeyPath InterpolatedString `yaml:"privateKeyPath"`
|
||||||
@ -20,6 +22,7 @@ type ControllersConfig struct {
|
|||||||
Proxy ProxyControllerConfig `yaml:"proxy"`
|
Proxy ProxyControllerConfig `yaml:"proxy"`
|
||||||
UCI UCIControllerConfig `yaml:"uci"`
|
UCI UCIControllerConfig `yaml:"uci"`
|
||||||
App AppControllerConfig `yaml:"app"`
|
App AppControllerConfig `yaml:"app"`
|
||||||
|
SysUpgrade SysUpgradeControllerConfig `yaml:"sysupgrade"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type PersistenceControllerConfig struct {
|
type PersistenceControllerConfig struct {
|
||||||
@ -46,6 +49,12 @@ type AppControllerConfig struct {
|
|||||||
DownloadDir InterpolatedString `yaml:"downloadDir"`
|
DownloadDir InterpolatedString `yaml:"downloadDir"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type SysUpgradeControllerConfig struct {
|
||||||
|
Enabled InterpolatedBool `yaml:"enabled"`
|
||||||
|
SysUpgradeCommand InterpolatedStringSlice `yaml:"sysupgradeCommand"`
|
||||||
|
FirmwareVersionCommand InterpolatedStringSlice `yaml:"firmwareVersionCommand"`
|
||||||
|
}
|
||||||
|
|
||||||
func NewDefaultAgentConfig() AgentConfig {
|
func NewDefaultAgentConfig() AgentConfig {
|
||||||
return AgentConfig{
|
return AgentConfig{
|
||||||
ServerURL: "http://127.0.0.1:3000",
|
ServerURL: "http://127.0.0.1:3000",
|
||||||
@ -72,6 +81,11 @@ func NewDefaultAgentConfig() AgentConfig {
|
|||||||
DataDir: "apps/data",
|
DataDir: "apps/data",
|
||||||
DownloadDir: "apps/bundles",
|
DownloadDir: "apps/bundles",
|
||||||
},
|
},
|
||||||
|
SysUpgrade: SysUpgradeControllerConfig{
|
||||||
|
Enabled: true,
|
||||||
|
SysUpgradeCommand: InterpolatedStringSlice{"sysupgrade", "--force", "-u", "-v", openwrt.FirmwareFileTemplate},
|
||||||
|
FirmwareVersionCommand: InterpolatedStringSlice{"sh", "-c", `source /etc/openwrt_release && echo "$DISTRIB_ID-$DISTRIB_RELEASE-$DISTRIB_REVISION"`},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
Collectors: []ShellCollectorConfig{
|
Collectors: []ShellCollectorConfig{
|
||||||
{
|
{
|
||||||
|
6
internal/imports/format/format_import.go
Normal file
6
internal/imports/format/format_import.go
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
package format
|
||||||
|
|
||||||
|
import (
|
||||||
|
_ "forge.cadoles.com/Cadoles/emissary/internal/format/json"
|
||||||
|
_ "forge.cadoles.com/Cadoles/emissary/internal/format/table"
|
||||||
|
)
|
6
internal/imports/passwd/passwd_import.go
Normal file
6
internal/imports/passwd/passwd_import.go
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
package passwd
|
||||||
|
|
||||||
|
import (
|
||||||
|
_ "forge.cadoles.com/arcad/edge/pkg/module/auth/http/passwd/argon2id"
|
||||||
|
_ "forge.cadoles.com/arcad/edge/pkg/module/auth/http/passwd/plain"
|
||||||
|
)
|
8
internal/imports/spec/spec_import.go
Normal file
8
internal/imports/spec/spec_import.go
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
package spec
|
||||||
|
|
||||||
|
import (
|
||||||
|
_ "forge.cadoles.com/Cadoles/emissary/internal/agent/controller/openwrt/spec/sysupgrade"
|
||||||
|
_ "forge.cadoles.com/Cadoles/emissary/internal/spec/app"
|
||||||
|
_ "forge.cadoles.com/Cadoles/emissary/internal/spec/proxy"
|
||||||
|
_ "forge.cadoles.com/Cadoles/emissary/internal/spec/uci"
|
||||||
|
)
|
6
internal/imports/sql/sql_import.go
Normal file
6
internal/imports/sql/sql_import.go
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
package sql
|
||||||
|
|
||||||
|
import (
|
||||||
|
_ "github.com/jackc/pgx/v5/stdlib"
|
||||||
|
_ "modernc.org/sqlite"
|
||||||
|
)
|
@ -10,11 +10,6 @@ import (
|
|||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"gitlab.com/wpetit/goweb/api"
|
"gitlab.com/wpetit/goweb/api"
|
||||||
"gitlab.com/wpetit/goweb/logger"
|
"gitlab.com/wpetit/goweb/logger"
|
||||||
|
|
||||||
// Import specs
|
|
||||||
_ "forge.cadoles.com/Cadoles/emissary/internal/spec/app"
|
|
||||||
_ "forge.cadoles.com/Cadoles/emissary/internal/spec/proxy"
|
|
||||||
_ "forge.cadoles.com/Cadoles/emissary/internal/spec/uci"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
Loading…
Reference in New Issue
Block a user