mirror of
https://github.com/juanfont/headscale.git
synced 2026-04-25 01:59:07 +02:00
integration: add run ID isolation for concurrent test execution
Add run ID-based isolation to container naming and network setup to
enable multiple integration tests to run concurrently on the same
Docker daemon without conflicts.
Changes:
- hsic: Add run ID prefix to headscale container names and use dynamic
port allocation for metrics endpoint (port 0 lets kernel assign)
- tsic: Add run ID prefix to tailscale container names
- dsic: Add run ID prefix to DERP container names
- scenario: Use run ID-aware test suite container name for network setup
Container naming now follows: {type}-{runIDShort}-{identifier}-{hash}
Example: ts-mdjtzx-1-74-fgdyls, hs-mdjtzx-pingallbyip-abc123
The run ID is obtained from HEADSCALE_INTEGRATION_RUN_ID environment
variable via dockertestutil.GetIntegrationRunID().
This commit is contained in:
@@ -147,7 +147,18 @@ func New(
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
hostname := fmt.Sprintf("derp-%s-%s", strings.ReplaceAll(version, ".", "-"), hash)
|
// Include run ID in hostname for easier identification of which test run owns this container
|
||||||
|
runID := dockertestutil.GetIntegrationRunID()
|
||||||
|
|
||||||
|
var hostname string
|
||||||
|
|
||||||
|
if runID != "" {
|
||||||
|
// Use last 6 chars of run ID (the random hash part) for brevity
|
||||||
|
runIDShort := runID[len(runID)-6:]
|
||||||
|
hostname = fmt.Sprintf("derp-%s-%s-%s", runIDShort, strings.ReplaceAll(version, ".", "-"), hash)
|
||||||
|
} else {
|
||||||
|
hostname = fmt.Sprintf("derp-%s-%s", strings.ReplaceAll(version, ".", "-"), hash)
|
||||||
|
}
|
||||||
tlsCert, tlsKey, err := integrationutil.CreateCertificate(hostname)
|
tlsCert, tlsKey, err := integrationutil.CreateCertificate(hostname)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to create certificates for headscale test: %w", err)
|
return nil, fmt.Errorf("failed to create certificates for headscale test: %w", err)
|
||||||
|
|||||||
@@ -74,6 +74,7 @@ type HeadscaleInContainer struct {
|
|||||||
// optional config
|
// optional config
|
||||||
port int
|
port int
|
||||||
extraPorts []string
|
extraPorts []string
|
||||||
|
hostMetricsPort string // Dynamically assigned host port for metrics/pprof access
|
||||||
caCerts [][]byte
|
caCerts [][]byte
|
||||||
hostPortBindings map[string][]string
|
hostPortBindings map[string][]string
|
||||||
aclPolicy *policyv2.Policy
|
aclPolicy *policyv2.Policy
|
||||||
@@ -330,7 +331,18 @@ func New(
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
hostname := "hs-" + hash
|
// Include run ID in hostname for easier identification of which test run owns this container
|
||||||
|
runID := dockertestutil.GetIntegrationRunID()
|
||||||
|
|
||||||
|
var hostname string
|
||||||
|
|
||||||
|
if runID != "" {
|
||||||
|
// Use last 6 chars of run ID (the random hash part) for brevity
|
||||||
|
runIDShort := runID[len(runID)-6:]
|
||||||
|
hostname = fmt.Sprintf("hs-%s-%s", runIDShort, hash)
|
||||||
|
} else {
|
||||||
|
hostname = "hs-" + hash
|
||||||
|
}
|
||||||
|
|
||||||
hsic := &HeadscaleInContainer{
|
hsic := &HeadscaleInContainer{
|
||||||
hostname: hostname,
|
hostname: hostname,
|
||||||
@@ -438,13 +450,13 @@ func New(
|
|||||||
Env: env,
|
Env: env,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Bind metrics port to predictable host port
|
// Bind metrics port to dynamic host port (kernel assigns free port)
|
||||||
if runOptions.PortBindings == nil {
|
if runOptions.PortBindings == nil {
|
||||||
runOptions.PortBindings = map[docker.Port][]docker.PortBinding{}
|
runOptions.PortBindings = map[docker.Port][]docker.PortBinding{}
|
||||||
}
|
}
|
||||||
|
|
||||||
runOptions.PortBindings["9090/tcp"] = []docker.PortBinding{
|
runOptions.PortBindings["9090/tcp"] = []docker.PortBinding{
|
||||||
{HostPort: "49090"},
|
{HostPort: "0"}, // Let kernel assign a free port
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(hsic.hostPortBindings) > 0 {
|
if len(hsic.hostPortBindings) > 0 {
|
||||||
@@ -540,9 +552,14 @@ func New(
|
|||||||
|
|
||||||
hsic.container = container
|
hsic.container = container
|
||||||
|
|
||||||
|
// Get the dynamically assigned host port for metrics/pprof
|
||||||
|
hsic.hostMetricsPort = container.GetHostPort("9090/tcp")
|
||||||
|
|
||||||
log.Printf(
|
log.Printf(
|
||||||
"Ports for %s: metrics/pprof=49090\n",
|
"Headscale %s metrics available at http://localhost:%s/metrics (debug at http://localhost:%s/debug/)\n",
|
||||||
hsic.hostname,
|
hsic.hostname,
|
||||||
|
hsic.hostMetricsPort,
|
||||||
|
hsic.hostMetricsPort,
|
||||||
)
|
)
|
||||||
|
|
||||||
// Write the CA certificates to the container
|
// Write the CA certificates to the container
|
||||||
@@ -932,6 +949,13 @@ func (t *HeadscaleInContainer) GetPort() string {
|
|||||||
return strconv.Itoa(t.port)
|
return strconv.Itoa(t.port)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetHostMetricsPort returns the dynamically assigned host port for metrics/pprof access.
|
||||||
|
// This port can be used by operators to access metrics at http://localhost:{port}/metrics
|
||||||
|
// and debug endpoints at http://localhost:{port}/debug/ while tests are running.
|
||||||
|
func (t *HeadscaleInContainer) GetHostMetricsPort() string {
|
||||||
|
return t.hostMetricsPort
|
||||||
|
}
|
||||||
|
|
||||||
// GetHealthEndpoint returns a health endpoint for the HeadscaleInContainer
|
// GetHealthEndpoint returns a health endpoint for the HeadscaleInContainer
|
||||||
// instance.
|
// instance.
|
||||||
func (t *HeadscaleInContainer) GetHealthEndpoint() string {
|
func (t *HeadscaleInContainer) GetHealthEndpoint() string {
|
||||||
|
|||||||
@@ -247,9 +247,14 @@ func (s *Scenario) AddNetwork(name string) (*dockertest.Network, error) {
|
|||||||
|
|
||||||
// We run the test suite in a docker container that calls a couple of endpoints for
|
// We run the test suite in a docker container that calls a couple of endpoints for
|
||||||
// readiness checks, this ensures that we can run the tests with individual networks
|
// readiness checks, this ensures that we can run the tests with individual networks
|
||||||
// and have the client reach the different containers
|
// and have the client reach the different containers.
|
||||||
// TODO(kradalby): Can the test-suite be renamed so we can have multiple?
|
// The container name includes the run ID to support multiple concurrent test runs.
|
||||||
err = dockertestutil.AddContainerToNetwork(s.pool, network, "headscale-test-suite")
|
testSuiteName := "headscale-test-suite"
|
||||||
|
if runID := dockertestutil.GetIntegrationRunID(); runID != "" {
|
||||||
|
testSuiteName = "headscale-test-suite-" + runID
|
||||||
|
}
|
||||||
|
|
||||||
|
err = dockertestutil.AddContainerToNetwork(s.pool, network, testSuiteName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to add test suite container to network: %w", err)
|
return nil, fmt.Errorf("failed to add test suite container to network: %w", err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -307,7 +307,18 @@ func New(
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
hostname := fmt.Sprintf("ts-%s-%s", strings.ReplaceAll(version, ".", "-"), hash)
|
// Include run ID in hostname for easier identification of which test run owns this container
|
||||||
|
runID := dockertestutil.GetIntegrationRunID()
|
||||||
|
|
||||||
|
var hostname string
|
||||||
|
|
||||||
|
if runID != "" {
|
||||||
|
// Use last 6 chars of run ID (the random hash part) for brevity
|
||||||
|
runIDShort := runID[len(runID)-6:]
|
||||||
|
hostname = fmt.Sprintf("ts-%s-%s-%s", runIDShort, strings.ReplaceAll(version, ".", "-"), hash)
|
||||||
|
} else {
|
||||||
|
hostname = fmt.Sprintf("ts-%s-%s", strings.ReplaceAll(version, ".", "-"), hash)
|
||||||
|
}
|
||||||
|
|
||||||
tsic := &TailscaleInContainer{
|
tsic := &TailscaleInContainer{
|
||||||
version: version,
|
version: version,
|
||||||
|
|||||||
Reference in New Issue
Block a user