update project structure

- move agent to /agent
- change /beszel to /src
- update workflows and docker builds
This commit is contained in:
henrygd
2025-09-07 16:42:15 -04:00
parent 4e26defdca
commit 6f5d95031c
212 changed files with 258 additions and 216 deletions

43
agent/health/health.go Normal file
View File

@@ -0,0 +1,43 @@
// Package health provides functions to check and update the health of the agent.
// It uses a file in the temp directory to store the timestamp of the last connection attempt.
// If the timestamp is older than 90 seconds, the agent is considered unhealthy.
// NB: The agent must be started with the Start() method to be considered healthy.
package health
import (
"errors"
"log"
"os"
"path/filepath"
"time"
)
// healthFile is the path to the health file
var healthFile = filepath.Join(os.TempDir(), "beszel_health")
// Check checks if the agent is connected by checking the modification time of the health file
func Check() error {
fileInfo, err := os.Stat(healthFile)
if err != nil {
return err
}
if time.Since(fileInfo.ModTime()) > 91*time.Second {
log.Println("over 90 seconds since last connection")
return errors.New("unhealthy")
}
return nil
}
// Update updates the modification time of the health file
func Update() error {
file, err := os.Create(healthFile)
if err != nil {
return err
}
return file.Close()
}
// CleanUp removes the health file
func CleanUp() error {
return os.Remove(healthFile)
}

View File

@@ -0,0 +1,67 @@
//go:build testing
// +build testing
package health
import (
"os"
"path/filepath"
"testing"
"time"
"testing/synctest"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestHealth(t *testing.T) {
// Override healthFile to use a temporary directory for this test.
originalHealthFile := healthFile
tmpDir := t.TempDir()
healthFile = filepath.Join(tmpDir, "beszel_health_test")
defer func() { healthFile = originalHealthFile }()
t.Run("check with no health file", func(t *testing.T) {
err := Check()
require.Error(t, err)
assert.True(t, os.IsNotExist(err), "expected a file-not-exist error, but got: %v", err)
})
t.Run("update and check", func(t *testing.T) {
err := Update()
require.NoError(t, err, "Update() failed")
err = Check()
assert.NoError(t, err, "Check() failed immediately after Update()")
})
// This test uses synctest to simulate time passing.
// NOTE: This test requires GOEXPERIMENT=synctest to run.
t.Run("check with simulated time", func(t *testing.T) {
synctest.Test(t, func(t *testing.T) {
// Update the file to set the initial timestamp.
require.NoError(t, Update(), "Update() failed inside synctest")
// Set the mtime to the current fake time to align the file's timestamp with the simulated clock.
now := time.Now()
require.NoError(t, os.Chtimes(healthFile, now, now), "Chtimes failed")
// Wait a duration less than the threshold.
time.Sleep(89 * time.Second)
synctest.Wait()
// The check should still pass.
assert.NoError(t, Check(), "Check() failed after 89s")
// Wait for the total duration to exceed the threshold.
time.Sleep(5 * time.Second)
synctest.Wait()
// The check should now fail as unhealthy.
err := Check()
require.Error(t, err, "Check() should have failed after 91s")
assert.Equal(t, "unhealthy", err.Error(), "Check() returned wrong error")
})
})
}