mirror of
https://github.com/Buriburizaem0n/nezha_domains.git
synced 2026-09-19 09:40:12 +00:00
200 lines
6.8 KiB
Go
200 lines
6.8 KiB
Go
//go:build linux && agentcompat
|
|
|
|
package agent
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"syscall"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func TestPreparedBinary_EightIndependentAgentsShareBinaryAndCleanUp(t *testing.T) {
|
|
prepared, err := PrepareBinary(t.Context(), testAgentSourceDir(t))
|
|
require.NoError(t, err)
|
|
preparedRoot := prepared.WorkspaceRoot()
|
|
binaryPath := prepared.BinaryPath()
|
|
require.FileExists(t, binaryPath)
|
|
requireDirectoryMode(t, preparedRoot, 0o711)
|
|
requireDirectoryMode(t, filepath.Dir(binaryPath), 0o711)
|
|
initialBinaryInfo, err := os.Stat(binaryPath)
|
|
require.NoError(t, err)
|
|
initialBinaryStat, ok := initialBinaryInfo.Sys().(*syscall.Stat_t)
|
|
require.True(t, ok)
|
|
instances := make([]*Agent, 0, 8)
|
|
t.Cleanup(func() {
|
|
for _, instance := range instances {
|
|
cleanupContext, cancel := context.WithTimeout(context.Background(), 15*time.Second)
|
|
_ = instance.Stop(cleanupContext)
|
|
cancel()
|
|
}
|
|
_ = prepared.Close()
|
|
})
|
|
dashboardInstance := startTestDashboard(t, true)
|
|
configPaths := make(map[string]struct{}, 8)
|
|
logPaths := make(map[string]struct{}, 8)
|
|
for index := range 8 {
|
|
config := AgentStartConfig{
|
|
PreparedBinary: prepared,
|
|
Endpoint: dashboardInstance.TLSEndpoint(),
|
|
Secret: dashboardInstance.AgentSecret(),
|
|
UUID: preparedBinaryUUID(index),
|
|
TLS: true,
|
|
CAFilePath: dashboardInstance.TLSCACertificatePath(),
|
|
Debug: index%2 == 0,
|
|
}
|
|
if index == 6 {
|
|
config.FMObserverRunID = "prepared-binary-observer"
|
|
}
|
|
if index == 7 {
|
|
config.Credential = &syscall.Credential{Uid: 65534, Gid: 65534}
|
|
}
|
|
instance, startErr := Start(t.Context(), config)
|
|
require.NoError(t, startErr)
|
|
require.Equal(t, binaryPath, instance.BinaryPath())
|
|
processBinaryInfo, statErr := os.Stat(fmt.Sprintf("/proc/%d/exe", instance.PID()))
|
|
if errors.Is(statErr, syscall.EACCES) || errors.Is(statErr, syscall.EPERM) {
|
|
t.Logf("kernel denied /proc/%d/exe metadata for agent %d", instance.PID(), index)
|
|
} else {
|
|
require.NoError(t, statErr)
|
|
processBinaryStat, statOK := processBinaryInfo.Sys().(*syscall.Stat_t)
|
|
require.True(t, statOK)
|
|
require.Equal(t, initialBinaryStat.Dev, processBinaryStat.Dev)
|
|
require.Equal(t, initialBinaryStat.Ino, processBinaryStat.Ino)
|
|
}
|
|
require.NotEqual(t, preparedRoot, instance.WorkspaceRoot())
|
|
require.NoFileExists(t, filepath.Join(instance.WorkspaceRoot(), "bin", "agent"))
|
|
configPaths[instance.ConfigPath()] = struct{}{}
|
|
logPaths[instance.LogPath()] = struct{}{}
|
|
require.NoError(t, waitForAgentReady(t, instance, dashboardInstance))
|
|
if index == 6 {
|
|
require.NotNil(t, instance.FMProducerObserver())
|
|
require.FileExists(t, instance.fmObserverPath)
|
|
}
|
|
if index == 7 {
|
|
status, statusErr := os.ReadFile(fmt.Sprintf("/proc/%d/status", instance.PID()))
|
|
require.NoError(t, statusErr)
|
|
require.Contains(t, string(status), "Uid:\t65534\t65534\t65534\t65534")
|
|
}
|
|
instances = append(instances, instance)
|
|
}
|
|
finalBinaryInfo, err := os.Stat(binaryPath)
|
|
require.NoError(t, err)
|
|
require.True(t, os.SameFile(initialBinaryInfo, finalBinaryInfo))
|
|
require.Len(t, configPaths, 8)
|
|
require.Len(t, logPaths, 8)
|
|
closeErr := prepared.Close()
|
|
var usageErr *PreparedBinaryUsageError
|
|
require.ErrorAs(t, closeErr, &usageErr)
|
|
require.Equal(t, "close", usageErr.Operation)
|
|
require.Equal(t, "has active consumers", usageErr.Reason)
|
|
for index, instance := range instances {
|
|
stopContext, cancel := context.WithTimeout(context.Background(), 15*time.Second)
|
|
require.NoError(t, instance.Stop(stopContext), "agent %d", index)
|
|
cancel()
|
|
require.True(t, instance.CleanupReceipt().Passed)
|
|
require.False(t, instance.CleanupReceipt().Forced)
|
|
if index < len(instances)-1 {
|
|
require.DirExists(t, preparedRoot)
|
|
require.FileExists(t, binaryPath)
|
|
}
|
|
}
|
|
require.NoError(t, prepared.Close())
|
|
require.NoError(t, prepared.Close())
|
|
_, statErr := os.Stat(preparedRoot)
|
|
require.ErrorIs(t, statErr, os.ErrNotExist)
|
|
}
|
|
|
|
func requireDirectoryMode(t *testing.T, path string, want os.FileMode) {
|
|
t.Helper()
|
|
info, err := os.Stat(path)
|
|
require.NoError(t, err)
|
|
require.Equal(t, want, info.Mode().Perm())
|
|
}
|
|
|
|
func TestAgent_StartBuildsAndOwnsItsBinary(t *testing.T) {
|
|
instance, err := Start(t.Context(), AgentStartConfig{
|
|
SourceDir: testAgentSourceDir(t),
|
|
Endpoint: "127.0.0.1:1",
|
|
UUID: "00000000-0000-0000-0000-000000000197",
|
|
})
|
|
require.NoError(t, err)
|
|
workspaceRoot := instance.WorkspaceRoot()
|
|
require.Equal(t, filepath.Join(workspaceRoot, "bin", "agent"), instance.BinaryPath())
|
|
require.FileExists(t, instance.BinaryPath())
|
|
stopContext, cancel := context.WithTimeout(context.Background(), 15*time.Second)
|
|
defer cancel()
|
|
require.NoError(t, instance.Stop(stopContext))
|
|
_, statErr := os.Stat(workspaceRoot)
|
|
require.ErrorIs(t, statErr, os.ErrNotExist)
|
|
}
|
|
|
|
func TestPreparedBinary_RejectsConsumerAfterClose(t *testing.T) {
|
|
prepared, err := PrepareBinary(t.Context(), testAgentSourceDir(t))
|
|
require.NoError(t, err)
|
|
require.NoError(t, prepared.Close())
|
|
_, err = Start(t.Context(), AgentStartConfig{
|
|
PreparedBinary: prepared,
|
|
Endpoint: "127.0.0.1:1",
|
|
UUID: "00000000-0000-0000-0000-000000000198",
|
|
})
|
|
var usageErr *PreparedBinaryUsageError
|
|
require.ErrorAs(t, err, &usageErr)
|
|
require.Equal(t, "start", usageErr.Operation)
|
|
require.Equal(t, "is closed", usageErr.Reason)
|
|
}
|
|
|
|
func TestPreparedBinary_RejectsInvalidSourceDirectory(t *testing.T) {
|
|
_, err := PrepareBinary(t.Context(), "relative")
|
|
var usageErr *PreparedBinaryUsageError
|
|
require.ErrorAs(t, err, &usageErr)
|
|
require.Equal(t, "prepare", usageErr.Operation)
|
|
require.True(t, strings.Contains(usageErr.Reason, "must be absolute"))
|
|
}
|
|
|
|
func TestPreparedBinary_CancelledBuildRemovesWorkspace(t *testing.T) {
|
|
temporaryRoot := t.TempDir()
|
|
t.Setenv("TMPDIR", temporaryRoot)
|
|
ctx, cancel := context.WithCancel(t.Context())
|
|
cancel()
|
|
|
|
_, err := PrepareBinary(ctx, testAgentSourceDir(t))
|
|
require.Error(t, err)
|
|
entries, readErr := os.ReadDir(temporaryRoot)
|
|
require.NoError(t, readErr)
|
|
require.Empty(t, entries)
|
|
}
|
|
|
|
func TestPreparedBinary_RejectsUninitializedValue(t *testing.T) {
|
|
_, err := Start(t.Context(), AgentStartConfig{
|
|
PreparedBinary: &PreparedBinary{},
|
|
Endpoint: "127.0.0.1:1",
|
|
UUID: "00000000-0000-0000-0000-000000000199",
|
|
})
|
|
var usageErr *PreparedBinaryUsageError
|
|
require.ErrorAs(t, err, &usageErr)
|
|
require.Equal(t, "start", usageErr.Operation)
|
|
require.Equal(t, "is uninitialized", usageErr.Reason)
|
|
}
|
|
|
|
func TestPreparedBinary_CloseRejectsNilAndUninitializedValues(t *testing.T) {
|
|
var nilPrepared *PreparedBinary
|
|
for _, prepared := range []*PreparedBinary{nilPrepared, &PreparedBinary{}} {
|
|
err := prepared.Close()
|
|
var usageErr *PreparedBinaryUsageError
|
|
require.ErrorAs(t, err, &usageErr)
|
|
require.Equal(t, "close", usageErr.Operation)
|
|
}
|
|
}
|
|
|
|
func preparedBinaryUUID(index int) string {
|
|
return fmt.Sprintf("00000000-0000-0000-0000-%012d", 190+index)
|
|
}
|