mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-09-08 17:14:34 +00:00
513 lines
17 KiB
Go
513 lines
17 KiB
Go
//go:build linux
|
|
|
|
package dockeragent
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"net"
|
|
"os"
|
|
"path/filepath"
|
|
"strconv"
|
|
"strings"
|
|
"testing"
|
|
|
|
containertypes "github.com/moby/moby/api/types/container"
|
|
systemtypes "github.com/moby/moby/api/types/system"
|
|
"github.com/moby/moby/client"
|
|
"github.com/rcourtman/pulse-go-rewrite/internal/agenthelper"
|
|
"github.com/rcourtman/pulse-go-rewrite/internal/hostmetrics"
|
|
agentsdocker "github.com/rcourtman/pulse-go-rewrite/pkg/agents/docker"
|
|
"github.com/rs/zerolog"
|
|
)
|
|
|
|
func TestCollectorOwnsRootlessEndpointRequiresOwnedRuntimeSocket(t *testing.T) {
|
|
originalRoot := rootlessRuntimeRoot
|
|
originalUID := effectiveUID
|
|
t.Cleanup(func() {
|
|
rootlessRuntimeRoot = originalRoot
|
|
effectiveUID = originalUID
|
|
})
|
|
|
|
rootlessRuntimeRoot = t.TempDir()
|
|
effectiveUID = os.Geteuid
|
|
runtimeDir := filepath.Join(rootlessRuntimeRoot, strconv.Itoa(os.Geteuid()))
|
|
if err := os.MkdirAll(runtimeDir, 0o700); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
socketPath := filepath.Join(runtimeDir, "docker.sock")
|
|
listener, err := net.Listen("unix", socketPath)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer listener.Close()
|
|
|
|
if !collectorOwnsRootlessEndpoint("unix://" + socketPath) {
|
|
t.Fatal("collector-owned rootless Unix socket was rejected")
|
|
}
|
|
if collectorOwnsRootlessEndpoint("unix:///var/run/docker.sock") {
|
|
t.Fatal("rootful system socket was accepted")
|
|
}
|
|
if collectorOwnsRootlessEndpoint("tcp://127.0.0.1:2375") {
|
|
t.Fatal("non-Unix runtime endpoint was accepted")
|
|
}
|
|
|
|
symlink := filepath.Join(runtimeDir, "linked.sock")
|
|
if err := os.Symlink(socketPath, symlink); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if collectorOwnsRootlessEndpoint("unix://" + symlink) {
|
|
t.Fatal("symlinked runtime endpoint was accepted")
|
|
}
|
|
}
|
|
|
|
func TestCollectorRootlessRuntimeCandidatesRequireOneExactOwnedSocket(t *testing.T) {
|
|
originalRoot := rootlessRuntimeRoot
|
|
originalUID := effectiveUID
|
|
t.Cleanup(func() {
|
|
rootlessRuntimeRoot = originalRoot
|
|
effectiveUID = originalUID
|
|
})
|
|
t.Setenv("DOCKER_HOST", "")
|
|
t.Setenv("CONTAINER_HOST", "")
|
|
t.Setenv("PODMAN_HOST", "")
|
|
|
|
rootlessRuntimeRoot = t.TempDir()
|
|
effectiveUID = os.Geteuid
|
|
runtimeDir := filepath.Join(rootlessRuntimeRoot, strconv.Itoa(os.Geteuid()))
|
|
if err := os.MkdirAll(filepath.Join(runtimeDir, "podman"), 0o700); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
dockerPath := filepath.Join(runtimeDir, "docker.sock")
|
|
dockerListener, err := net.Listen("unix", dockerPath)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer dockerListener.Close()
|
|
|
|
candidates, err := collectorRootlessRuntimeCandidates(RuntimeAuto)
|
|
if err != nil {
|
|
t.Fatalf("single Docker candidate: %v", err)
|
|
}
|
|
if len(candidates) != 1 || candidates[0].host != "unix://"+dockerPath {
|
|
t.Fatalf("Docker candidates = %+v", candidates)
|
|
}
|
|
|
|
podmanPath := filepath.Join(runtimeDir, "podman", "podman.sock")
|
|
podmanListener, err := net.Listen("unix", podmanPath)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer podmanListener.Close()
|
|
|
|
if _, err := collectorRootlessRuntimeCandidates(RuntimeAuto); err == nil || !strings.Contains(err.Error(), "ambiguous") {
|
|
t.Fatalf("auto discovery with Docker and Podman = %v, want ambiguity", err)
|
|
}
|
|
|
|
candidates, err = collectorRootlessRuntimeCandidates(RuntimeDocker)
|
|
if err != nil {
|
|
t.Fatalf("explicit Docker preference: %v", err)
|
|
}
|
|
if len(candidates) != 1 || candidates[0].host != "unix://"+dockerPath {
|
|
t.Fatalf("Docker preference candidates = %+v", candidates)
|
|
}
|
|
|
|
t.Setenv("DOCKER_HOST", "unix://"+dockerPath)
|
|
candidates, err = collectorRootlessRuntimeCandidates(RuntimeAuto)
|
|
if err != nil {
|
|
t.Fatalf("explicit endpoint pin: %v", err)
|
|
}
|
|
if len(candidates) != 1 || candidates[0].label != "DOCKER_HOST" {
|
|
t.Fatalf("explicit candidates = %+v", candidates)
|
|
}
|
|
}
|
|
|
|
func TestCollectorRootlessRuntimeCandidatesRejectCrossUserAndConflictingPins(t *testing.T) {
|
|
originalRoot := rootlessRuntimeRoot
|
|
originalUID := effectiveUID
|
|
t.Cleanup(func() {
|
|
rootlessRuntimeRoot = originalRoot
|
|
effectiveUID = originalUID
|
|
})
|
|
|
|
rootlessRuntimeRoot = t.TempDir()
|
|
effectiveUID = os.Geteuid
|
|
runtimeDir := filepath.Join(rootlessRuntimeRoot, strconv.Itoa(os.Geteuid()))
|
|
if err := os.MkdirAll(filepath.Join(runtimeDir, "podman"), 0o700); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
dockerPath := filepath.Join(runtimeDir, "docker.sock")
|
|
dockerListener, err := net.Listen("unix", dockerPath)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer dockerListener.Close()
|
|
podmanPath := filepath.Join(runtimeDir, "podman", "podman.sock")
|
|
podmanListener, err := net.Listen("unix", podmanPath)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer podmanListener.Close()
|
|
|
|
t.Setenv("DOCKER_HOST", "unix://"+dockerPath)
|
|
t.Setenv("PODMAN_HOST", "unix://"+podmanPath)
|
|
t.Setenv("CONTAINER_HOST", "")
|
|
if _, err := collectorRootlessRuntimeCandidates(RuntimeAuto); err == nil || !strings.Contains(err.Error(), "conflicting") {
|
|
t.Fatalf("conflicting live pins = %v, want conflict rejection", err)
|
|
}
|
|
|
|
t.Setenv("DOCKER_HOST", "unix://"+filepath.Join(runtimeDir, "custom.sock"))
|
|
t.Setenv("PODMAN_HOST", "")
|
|
if _, err := collectorRootlessRuntimeCandidates(RuntimeAuto); err == nil || !strings.Contains(err.Error(), "exact collector-owned endpoint") {
|
|
t.Fatalf("custom same-UID pin = %v, want exact-path rejection", err)
|
|
}
|
|
|
|
t.Setenv("DOCKER_HOST", "")
|
|
t.Setenv("PODMAN_HOST", "")
|
|
effectiveUID = func() int { return os.Geteuid() + 1 }
|
|
if _, err := collectorRootlessRuntimeCandidates(RuntimeAuto); err == nil || !strings.Contains(err.Error(), "no live collector-owned") {
|
|
t.Fatalf("cross-user sockets = %v, want fail-closed rejection", err)
|
|
}
|
|
}
|
|
|
|
func TestCollectorRootlessRuntimeAmbiguityIsRejectedBeforeDaemonProbe(t *testing.T) {
|
|
originalRoot := rootlessRuntimeRoot
|
|
originalUID := effectiveUID
|
|
originalNewClient := newDockerClientFn
|
|
t.Cleanup(func() {
|
|
rootlessRuntimeRoot = originalRoot
|
|
effectiveUID = originalUID
|
|
newDockerClientFn = originalNewClient
|
|
})
|
|
t.Setenv("DOCKER_HOST", "")
|
|
t.Setenv("CONTAINER_HOST", "")
|
|
t.Setenv("PODMAN_HOST", "")
|
|
|
|
rootlessRuntimeRoot = t.TempDir()
|
|
effectiveUID = os.Geteuid
|
|
runtimeDir := filepath.Join(rootlessRuntimeRoot, strconv.Itoa(os.Geteuid()))
|
|
if err := os.MkdirAll(filepath.Join(runtimeDir, "podman"), 0o700); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
listeners := make([]net.Listener, 0, 2)
|
|
for _, path := range []string{
|
|
filepath.Join(runtimeDir, "docker.sock"),
|
|
filepath.Join(runtimeDir, "podman", "podman.sock"),
|
|
} {
|
|
listener, err := net.Listen("unix", path)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
listeners = append(listeners, listener)
|
|
}
|
|
defer func() {
|
|
for _, listener := range listeners {
|
|
_ = listener.Close()
|
|
}
|
|
}()
|
|
|
|
probes := 0
|
|
newDockerClientFn = func(...client.Opt) (dockerClient, error) {
|
|
probes++
|
|
return nil, nil
|
|
}
|
|
if _, _, _, err := connectCollectorOwnedRootlessRuntime(RuntimeAuto, nil); err == nil || !strings.Contains(err.Error(), "ambiguous") {
|
|
t.Fatalf("ambiguous connection = %v", err)
|
|
}
|
|
if probes != 0 {
|
|
t.Fatalf("ambiguous endpoints reached daemon client construction %d times", probes)
|
|
}
|
|
}
|
|
|
|
func TestCollectorRootlessRuntimeRejectsSymlinkAndUnreadableSocket(t *testing.T) {
|
|
originalRoot := rootlessRuntimeRoot
|
|
originalUID := effectiveUID
|
|
t.Cleanup(func() {
|
|
rootlessRuntimeRoot = originalRoot
|
|
effectiveUID = originalUID
|
|
})
|
|
t.Setenv("DOCKER_HOST", "")
|
|
t.Setenv("CONTAINER_HOST", "")
|
|
t.Setenv("PODMAN_HOST", "")
|
|
|
|
rootlessRuntimeRoot = t.TempDir()
|
|
effectiveUID = os.Geteuid
|
|
runtimeDir := filepath.Join(rootlessRuntimeRoot, strconv.Itoa(os.Geteuid()))
|
|
if err := os.MkdirAll(runtimeDir, 0o700); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
realPath := filepath.Join(runtimeDir, "real.sock")
|
|
listener, err := net.Listen("unix", realPath)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer listener.Close()
|
|
standardPath := filepath.Join(runtimeDir, "docker.sock")
|
|
if err := os.Symlink(realPath, standardPath); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if _, err := collectorRootlessRuntimeCandidates(RuntimeAuto); err == nil {
|
|
t.Fatal("symlinked rootless runtime endpoint was accepted")
|
|
}
|
|
if err := os.Remove(standardPath); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := os.Rename(realPath, standardPath); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := os.Chmod(standardPath, 0); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if _, err := collectorRootlessRuntimeCandidates(RuntimeAuto); err == nil {
|
|
t.Fatal("collector-unreadable rootless runtime endpoint was accepted")
|
|
}
|
|
}
|
|
|
|
func TestCollectorRootlessRuntimeRejectsOwnedSocketBackedByRootfulDaemon(t *testing.T) {
|
|
originalRoot := rootlessRuntimeRoot
|
|
originalUID := effectiveUID
|
|
originalNewClient := newDockerClientFn
|
|
t.Cleanup(func() {
|
|
rootlessRuntimeRoot = originalRoot
|
|
effectiveUID = originalUID
|
|
newDockerClientFn = originalNewClient
|
|
})
|
|
t.Setenv("DOCKER_HOST", "")
|
|
t.Setenv("CONTAINER_HOST", "")
|
|
t.Setenv("PODMAN_HOST", "")
|
|
|
|
rootlessRuntimeRoot = t.TempDir()
|
|
effectiveUID = os.Geteuid
|
|
runtimeDir := filepath.Join(rootlessRuntimeRoot, strconv.Itoa(os.Geteuid()))
|
|
if err := os.MkdirAll(runtimeDir, 0o700); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
socketPath := filepath.Join(runtimeDir, "docker.sock")
|
|
listener, err := net.Listen("unix", socketPath)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer listener.Close()
|
|
|
|
closed := false
|
|
newDockerClientFn = func(...client.Opt) (dockerClient, error) {
|
|
return &fakeDockerClient{
|
|
daemonHost: "unix://" + socketPath,
|
|
infoFunc: func(context.Context) (systemtypes.Info, error) {
|
|
return systemtypes.Info{SecurityOptions: []string{"name=seccomp,profile=default"}}, nil
|
|
},
|
|
closeFn: func() error { closed = true; return nil },
|
|
}, nil
|
|
}
|
|
if _, _, _, err := connectCollectorOwnedRootlessRuntime(RuntimeDocker, nil); err == nil || !strings.Contains(err.Error(), "did not attest rootless mode") {
|
|
t.Fatalf("owned-path rootful daemon = %v", err)
|
|
}
|
|
if !closed {
|
|
t.Fatal("owned-path rootful daemon client was not closed")
|
|
}
|
|
|
|
newDockerClientFn = func(...client.Opt) (dockerClient, error) {
|
|
return &fakeDockerClient{
|
|
daemonHost: "unix://" + socketPath,
|
|
infoFunc: func(context.Context) (systemtypes.Info, error) {
|
|
return systemtypes.Info{SecurityOptions: []string{"name=rootless", "name=seccomp,profile=default"}}, nil
|
|
},
|
|
}, nil
|
|
}
|
|
cli, _, runtimeKind, err := connectCollectorOwnedRootlessRuntime(RuntimeDocker, nil)
|
|
if err != nil {
|
|
t.Fatalf("rootless daemon: %v", err)
|
|
}
|
|
if cli == nil || runtimeKind != RuntimeDocker {
|
|
t.Fatalf("rootless daemon connection = cli:%T runtime:%s", cli, runtimeKind)
|
|
}
|
|
}
|
|
|
|
func TestTypedHelperInventoryPromotesToAttestedRootlessRuntime(t *testing.T) {
|
|
originalRoot := rootlessRuntimeRoot
|
|
originalUID := effectiveUID
|
|
originalConnect := connectCollectorRuntimeFn
|
|
t.Cleanup(func() {
|
|
rootlessRuntimeRoot = originalRoot
|
|
effectiveUID = originalUID
|
|
connectCollectorRuntimeFn = originalConnect
|
|
})
|
|
|
|
rootlessRuntimeRoot = t.TempDir()
|
|
effectiveUID = os.Geteuid
|
|
runtimeDir := filepath.Join(rootlessRuntimeRoot, strconv.Itoa(os.Geteuid()))
|
|
if err := os.MkdirAll(runtimeDir, 0o700); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
socketPath := filepath.Join(runtimeDir, "docker.sock")
|
|
listener, err := net.Listen("unix", socketPath)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer listener.Close()
|
|
|
|
rootlessClient := &fakeDockerClient{daemonHost: "unix://" + socketPath}
|
|
connectCollectorRuntimeFn = func(RuntimeKind, *zerolog.Logger) (dockerClient, systemtypes.Info, RuntimeKind, error) {
|
|
return rootlessClient, systemtypes.Info{
|
|
ID: "rootless-daemon", ServerVersion: "28.0.0", SecurityOptions: []string{"name=rootless"},
|
|
}, RuntimeDocker, nil
|
|
}
|
|
helper := &helperInventoryStub{}
|
|
agent := &Agent{
|
|
cfg: Config{
|
|
HelperInventory: helper, DisableUpdateChecks: true,
|
|
},
|
|
helperInventory: helper,
|
|
directDisableChecks: false,
|
|
directServices: true,
|
|
directTasks: true,
|
|
runtimePref: RuntimeAuto,
|
|
runtime: RuntimeDocker,
|
|
registryChecker: newRegistryCheckerWithConfig(zerolog.Nop(), false),
|
|
logger: zerolog.Nop(),
|
|
}
|
|
|
|
if !agent.maybePromoteCollectorRootlessRuntime() {
|
|
t.Fatal("attested rootless runtime was not promoted")
|
|
}
|
|
if agent.helperInventory != nil || agent.docker == nil || agent.daemonID != "rootless-daemon" {
|
|
t.Fatalf("promoted state = helper:%T docker:%T daemon:%q", agent.helperInventory, agent.docker, agent.daemonID)
|
|
}
|
|
if agent.cfg.DisableUpdateChecks || !agent.cfg.IncludeServices || !agent.cfg.IncludeTasks || !agent.registryChecker.Enabled() {
|
|
t.Fatalf("direct configuration was not restored: %+v", agent.cfg)
|
|
}
|
|
if agent.ContainerActionsAvailable() {
|
|
t.Fatal("safe collector promotion exposed container action authority")
|
|
}
|
|
}
|
|
|
|
func TestTypedHelperInventoryTransitionsThroughRootlessPodman(t *testing.T) {
|
|
originalRoot := rootlessRuntimeRoot
|
|
originalUID := effectiveUID
|
|
originalConnect := connectCollectorRuntimeFn
|
|
t.Cleanup(func() {
|
|
rootlessRuntimeRoot = originalRoot
|
|
effectiveUID = originalUID
|
|
connectCollectorRuntimeFn = originalConnect
|
|
})
|
|
|
|
rootlessRuntimeRoot = t.TempDir()
|
|
effectiveUID = os.Geteuid
|
|
runtimeDir := filepath.Join(rootlessRuntimeRoot, strconv.Itoa(os.Geteuid()), "podman")
|
|
if err := os.MkdirAll(runtimeDir, 0o700); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
socketPath := filepath.Join(runtimeDir, "podman.sock")
|
|
listener, err := net.Listen("unix", socketPath)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer listener.Close()
|
|
|
|
connectCollectorRuntimeFn = func(RuntimeKind, *zerolog.Logger) (dockerClient, systemtypes.Info, RuntimeKind, error) {
|
|
return &fakeDockerClient{daemonHost: "unix://" + socketPath}, systemtypes.Info{
|
|
ID: "rootless-podman", ServerVersion: "5.5.0 podman", SecurityOptions: []string{"name=rootless"},
|
|
}, RuntimePodman, nil
|
|
}
|
|
helper := &helperInventoryStub{result: agenthelper.ContainerInventoryResult{
|
|
Runtimes: []agenthelper.ContainerRuntimeSnapshot{{Runtime: "podman", Available: true}},
|
|
}}
|
|
agent := &Agent{
|
|
cfg: Config{HelperInventory: helper, DisableUpdateChecks: true},
|
|
helperInventory: helper,
|
|
directDisableChecks: false,
|
|
directServices: true,
|
|
directTasks: true,
|
|
runtimePref: RuntimePodman,
|
|
runtime: RuntimePodman,
|
|
registryChecker: newRegistryCheckerWithConfig(zerolog.Nop(), false),
|
|
logger: zerolog.Nop(),
|
|
}
|
|
if !agent.maybePromoteCollectorRootlessRuntime() {
|
|
t.Fatal("attested rootless Podman runtime was not promoted")
|
|
}
|
|
if agent.runtime != RuntimePodman || agent.cfg.IncludeServices || agent.cfg.IncludeTasks || agent.helperInventory != nil {
|
|
t.Fatalf("promoted Podman state = runtime:%s services:%t tasks:%t helper:%T", agent.runtime, agent.cfg.IncludeServices, agent.cfg.IncludeTasks, agent.helperInventory)
|
|
}
|
|
agent.runtimeGoneStreak = runtimeReconnectFailureThreshold
|
|
if !agent.maybeFallbackToHelperInventory(context.Background()) {
|
|
t.Fatal("rootless Podman loss did not fall back to typed helper summary")
|
|
}
|
|
if agent.helperInventory != helper || agent.docker != nil || agent.runtime != RuntimePodman {
|
|
t.Fatalf("Podman fallback state = helper:%T docker:%T runtime:%s", agent.helperInventory, agent.docker, agent.runtime)
|
|
}
|
|
}
|
|
|
|
func TestCycleBoundaryChangeStopsDirectCallsAndFallsBackToHelper(t *testing.T) {
|
|
originalRoot := rootlessRuntimeRoot
|
|
originalUID := effectiveUID
|
|
originalConnect := connectCollectorRuntimeFn
|
|
originalMetrics := hostmetricsCollectWithDiskFilters
|
|
t.Cleanup(func() {
|
|
rootlessRuntimeRoot = originalRoot
|
|
effectiveUID = originalUID
|
|
connectCollectorRuntimeFn = originalConnect
|
|
hostmetricsCollectWithDiskFilters = originalMetrics
|
|
})
|
|
|
|
rootlessRuntimeRoot = t.TempDir()
|
|
effectiveUID = os.Geteuid
|
|
runtimeDir := filepath.Join(rootlessRuntimeRoot, strconv.Itoa(os.Geteuid()))
|
|
if err := os.MkdirAll(runtimeDir, 0o700); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
socketPath := filepath.Join(runtimeDir, "docker.sock")
|
|
listener, err := net.Listen("unix", socketPath)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer listener.Close()
|
|
|
|
postInfoCalls := 0
|
|
direct := &fakeDockerClient{
|
|
daemonHost: "unix://" + socketPath,
|
|
infoFunc: func(context.Context) (systemtypes.Info, error) {
|
|
return systemtypes.Info{SecurityOptions: []string{"name=seccomp,profile=default"}}, nil
|
|
},
|
|
containerListFunc: func(context.Context, dockerContainerListOptions) ([]containertypes.Summary, error) {
|
|
postInfoCalls++
|
|
return nil, nil
|
|
},
|
|
}
|
|
connectCollectorRuntimeFn = func(RuntimeKind, *zerolog.Logger) (dockerClient, systemtypes.Info, RuntimeKind, error) {
|
|
return nil, systemtypes.Info{}, RuntimeAuto, errors.New("replacement failed rootless admission")
|
|
}
|
|
hostmetricsCollectWithDiskFilters = func(context.Context, []string, []string) (hostmetrics.Snapshot, error) {
|
|
return hostmetrics.Snapshot{}, nil
|
|
}
|
|
helper := &helperInventoryStub{result: agenthelper.ContainerInventoryResult{
|
|
Runtimes: []agenthelper.ContainerRuntimeSnapshot{{Runtime: "docker", Available: true}},
|
|
}}
|
|
agent := &Agent{
|
|
cfg: Config{HelperInventory: helper, IncludeContainers: true},
|
|
docker: newSwappableDockerClient(direct),
|
|
directServices: true,
|
|
directTasks: true,
|
|
runtime: RuntimeDocker,
|
|
runtimePref: RuntimeDocker,
|
|
runtimeGoneStreak: runtimeReconnectFailureThreshold - 1,
|
|
registryChecker: newRegistryCheckerWithConfig(zerolog.Nop(), false),
|
|
allowedStates: map[string]struct{}{},
|
|
logger: zerolog.Nop(),
|
|
reportStreamID: "boundary-change",
|
|
}
|
|
|
|
report, err := agent.collectOnceWithReport(context.Background())
|
|
if err != nil {
|
|
t.Fatalf("boundary fallback collection: %v", err)
|
|
}
|
|
if postInfoCalls != 0 {
|
|
t.Fatalf("changed runtime boundary received %d post-Info daemon calls", postInfoCalls)
|
|
}
|
|
if agent.helperInventory != helper || agent.docker != nil || report.Host.CollectionMode != agentsdocker.CollectionModeTypedHelperSummary {
|
|
t.Fatalf("boundary fallback state = helper:%T docker:%T mode:%q", agent.helperInventory, agent.docker, report.Host.CollectionMode)
|
|
}
|
|
}
|