a73x

internal/agent/statelock/statelock_test.go

Ref:   Size: 2.7 KiB   History

package statelock

import (
	"fmt"
	"os"
	"path/filepath"
	"testing"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

// flock ownership belongs to the open file description, not the process, so two
// Acquires in this one test binary conflict exactly as two agents would.

func TestAcquireClaimsTheDirectoryAndNamesTheHolder(t *testing.T) {
	dir := t.TempDir()

	lk, err := Acquire(dir)
	require.NoError(t, err)
	t.Cleanup(func() { lk.Release() }) //nolint:errcheck

	raw, err := os.ReadFile(filepath.Join(dir, lockName))
	require.NoError(t, err)
	assert.Equal(t, fmt.Sprintf("%d\n", os.Getpid()), string(raw),
		"the lock file names the process holding it")
}

func TestASecondAgentIsRefusedAndToldWhichProcessHasIt(t *testing.T) {
	dir := t.TempDir()
	lk, err := Acquire(dir)
	require.NoError(t, err)
	t.Cleanup(func() { lk.Release() }) //nolint:errcheck

	second, err := Acquire(dir)
	assert.Nil(t, second)
	require.Error(t, err)
	assert.Equal(t, fmt.Sprintf("an agent is already running with this identity (pid %d)", os.Getpid()), err.Error())
}

func TestARefusalWithNoReadablePidStillRefuses(t *testing.T) {
	dir := t.TempDir()
	lk, err := Acquire(dir)
	require.NoError(t, err)
	t.Cleanup(func() { lk.Release() }) //nolint:errcheck

	// An agent old enough to predate the pid, or one killed between taking the
	// lock and writing it. The kernel still says the lock is held, which is the
	// answer that counts; the message simply names nobody.
	require.NoError(t, os.WriteFile(filepath.Join(dir, lockName), []byte("not a pid\n"), mode))

	_, err = Acquire(dir)
	require.Error(t, err)
	assert.Equal(t, "an agent is already running with this identity", err.Error())
}

func TestReleasingLetsTheNextAgentIn(t *testing.T) {
	dir := t.TempDir()
	lk, err := Acquire(dir)
	require.NoError(t, err)
	require.NoError(t, lk.Release())

	// An agent that has exited leaves nothing behind but a file: a restart, or
	// the re-exec of a self-upgrade, must not be refused by its own predecessor.
	next, err := Acquire(dir)
	require.NoError(t, err)
	assert.NoError(t, next.Release())
}

func TestTwoStateDirsAreTwoIdentities(t *testing.T) {
	// One machine may legitimately run several agents, each enrolled separately.
	// The lock is per state directory precisely so that stays possible.
	a, err := Acquire(t.TempDir())
	require.NoError(t, err)
	t.Cleanup(func() { a.Release() }) //nolint:errcheck

	b, err := Acquire(t.TempDir())
	require.NoError(t, err)
	assert.NoError(t, b.Release())
}

func TestAcquireFailsWhenTheDirectoryIsNotThere(t *testing.T) {
	_, err := Acquire(filepath.Join(t.TempDir(), "no-such-dir"))
	require.Error(t, err)
	assert.Contains(t, err.Error(), "open agent lock")
}