feat(agent): add updater-agent binary and ship it in the image

This commit is contained in:
2026-10-12 14:41:05 +02:00
parent ad6ff1e066
commit 9b74286c9b
4 changed files with 199 additions and 7 deletions
+51
View File
@@ -0,0 +1,51 @@
// Package config loads the agent's configuration from environment variables.
package config
import (
"errors"
"os"
"regexp"
"strings"
)
var masterKeyPattern = regexp.MustCompile(`^sha256:[0-9a-f]{64}$`)
// Config holds the agent's runtime configuration.
type Config struct {
MasterURL string // MASTER_URL, wss://host:port/agent
MasterKey string // MASTER_KEY, fingerprint shown in the master UI
Token string // AGENT_TOKEN, shown once when the agent was added
SelfID string // SELF_ID, defaults to the hostname (= container ID prefix)
LogLevel string // LOG_LEVEL, default info
}
// Load reads and validates the agent configuration. It fails fast so a
// misconfigured agent never starts dialling.
func Load() (*Config, error) {
cfg := &Config{
MasterURL: os.Getenv("MASTER_URL"),
MasterKey: os.Getenv("MASTER_KEY"),
Token: os.Getenv("AGENT_TOKEN"),
SelfID: os.Getenv("SELF_ID"),
LogLevel: os.Getenv("LOG_LEVEL"),
}
switch {
case cfg.MasterURL == "":
return nil, errors.New("MASTER_URL is required")
case !strings.HasPrefix(cfg.MasterURL, "wss://"):
return nil, errors.New("MASTER_URL must start with wss://")
case cfg.MasterKey == "":
return nil, errors.New("MASTER_KEY is required")
case !masterKeyPattern.MatchString(cfg.MasterKey):
return nil, errors.New("MASTER_KEY must look like sha256:<64 hex>")
case cfg.Token == "":
return nil, errors.New("AGENT_TOKEN is required")
}
if cfg.SelfID == "" {
cfg.SelfID, _ = os.Hostname()
}
if cfg.LogLevel == "" {
cfg.LogLevel = "info"
}
return cfg, nil
}
+50
View File
@@ -0,0 +1,50 @@
package config
import (
"os"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
const validKey = "sha256:0000000000000000000000000000000000000000000000000000000000000000"
func setValid(t *testing.T) {
t.Setenv("MASTER_URL", "wss://10.0.0.5:8090/agent")
t.Setenv("MASTER_KEY", validKey)
t.Setenv("AGENT_TOKEN", "pua_abc")
t.Setenv("SELF_ID", "")
t.Setenv("LOG_LEVEL", "")
}
func TestLoad_Valid(t *testing.T) {
setValid(t)
cfg, err := Load()
require.NoError(t, err)
host, _ := os.Hostname()
assert.Equal(t, &Config{
MasterURL: "wss://10.0.0.5:8090/agent", MasterKey: validKey, Token: "pua_abc",
SelfID: host, LogLevel: "info",
}, cfg)
}
func TestLoad_Invalid(t *testing.T) {
cases := []struct {
name, key, value, wantErr string
}{
{"missing url", "MASTER_URL", "", "MASTER_URL is required"},
{"plain ws", "MASTER_URL", "ws://10.0.0.5:8090/agent", "MASTER_URL must start with wss://"},
{"missing key", "MASTER_KEY", "", "MASTER_KEY is required"},
{"malformed key", "MASTER_KEY", "abc", "MASTER_KEY must look like sha256:<64 hex>"},
{"missing token", "AGENT_TOKEN", "", "AGENT_TOKEN is required"},
}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
setValid(t)
t.Setenv(c.key, c.value)
_, err := Load()
assert.ErrorContains(t, err, c.wantErr)
})
}
}