Image signing: - Ed25519 sign/verify package (pure Go stdlib, zero deps) - genkey and sign CLI subcommands for build system - Optional --pubkey flag for verifying updates on apply - Signature URLs in update metadata (latest.json) Portainer Edge Agent: - cloud-init portainer.go module writes K8s manifest - Auto-deploys Edge Agent when portainer.edge-agent.enabled - Full RBAC (ServiceAccount, ClusterRoleBinding, Deployment) - 5 Portainer tests in portainer_test.go Production tooling: - SSH debug extension builder (hack/build-ssh-extension.sh) - Boot performance benchmark (test/benchmark/bench-boot.sh) - Resource usage benchmark (test/benchmark/bench-resources.sh) - Deployment guide (docs/deployment-guide.md) Test results: 50 update agent tests + 22 cloud-init tests passing. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
137 lines
2.8 KiB
Go
137 lines
2.8 KiB
Go
package cloudinit
|
|
|
|
import (
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
func TestApplyPortainerDisabled(t *testing.T) {
|
|
cfg := &Config{}
|
|
dir := t.TempDir()
|
|
|
|
if err := ApplyPortainer(cfg, dir); err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
|
|
// No manifest should be written
|
|
entries, err := os.ReadDir(dir)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(entries) != 0 {
|
|
t.Errorf("expected no files, got %d", len(entries))
|
|
}
|
|
}
|
|
|
|
func TestApplyPortainerMissingFields(t *testing.T) {
|
|
cfg := &Config{
|
|
Portainer: PortainerConfig{
|
|
EdgeAgent: EdgeAgentConfig{
|
|
Enabled: true,
|
|
},
|
|
},
|
|
}
|
|
dir := t.TempDir()
|
|
|
|
err := ApplyPortainer(cfg, dir)
|
|
if err == nil {
|
|
t.Fatal("expected error for missing edge-id and edge-key")
|
|
}
|
|
}
|
|
|
|
func TestApplyPortainerMissingURL(t *testing.T) {
|
|
cfg := &Config{
|
|
Portainer: PortainerConfig{
|
|
EdgeAgent: EdgeAgentConfig{
|
|
Enabled: true,
|
|
EdgeID: "test-id",
|
|
EdgeKey: "test-key",
|
|
},
|
|
},
|
|
}
|
|
dir := t.TempDir()
|
|
|
|
err := ApplyPortainer(cfg, dir)
|
|
if err == nil {
|
|
t.Fatal("expected error for missing portainer-url")
|
|
}
|
|
}
|
|
|
|
func TestApplyPortainerEnabled(t *testing.T) {
|
|
cfg := &Config{
|
|
Portainer: PortainerConfig{
|
|
EdgeAgent: EdgeAgentConfig{
|
|
Enabled: true,
|
|
EdgeID: "test-edge-id",
|
|
EdgeKey: "test-edge-key-abc123",
|
|
PortainerURL: "https://portainer.example.com",
|
|
},
|
|
},
|
|
}
|
|
dir := t.TempDir()
|
|
|
|
if err := ApplyPortainer(cfg, dir); err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
|
|
// Check manifest was created
|
|
manifestPath := filepath.Join(dir, "portainer-edge-agent.yaml")
|
|
data, err := os.ReadFile(manifestPath)
|
|
if err != nil {
|
|
t.Fatalf("manifest not created: %v", err)
|
|
}
|
|
|
|
content := string(data)
|
|
|
|
// Check key elements are present
|
|
checks := []string{
|
|
"kind: Namespace",
|
|
"name: portainer",
|
|
"kind: Deployment",
|
|
"name: portainer-agent",
|
|
"EDGE_ID",
|
|
"test-edge-id",
|
|
"EDGE_KEY",
|
|
"test-edge-key-abc123",
|
|
"image: portainer/agent:latest",
|
|
"kind: ClusterRoleBinding",
|
|
"kind: ServiceAccount",
|
|
}
|
|
|
|
for _, check := range checks {
|
|
if !strings.Contains(content, check) {
|
|
t.Errorf("manifest missing expected content: %q", check)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestApplyPortainerCustomImage(t *testing.T) {
|
|
cfg := &Config{
|
|
Portainer: PortainerConfig{
|
|
EdgeAgent: EdgeAgentConfig{
|
|
Enabled: true,
|
|
EdgeID: "test-id",
|
|
EdgeKey: "test-key",
|
|
PortainerURL: "https://portainer.example.com",
|
|
Image: "portainer/agent:2.20.0",
|
|
},
|
|
},
|
|
}
|
|
dir := t.TempDir()
|
|
|
|
if err := ApplyPortainer(cfg, dir); err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
|
|
data, err := os.ReadFile(filepath.Join(dir, "portainer-edge-agent.yaml"))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if !strings.Contains(string(data), "image: portainer/agent:2.20.0") {
|
|
t.Error("expected custom image in manifest")
|
|
}
|
|
}
|