From c5e24ddc533ddc904b4a923dcd35805ac6be6aa9 Mon Sep 17 00:00:00 2001 From: Fabian Muscariello Date: Wed, 14 Oct 2026 15:18:20 +0200 Subject: [PATCH] test: set each environment name once, so a running command never reads a blank API key The concurrent tfstate execs failed now and then with "an API key needs MESHSTACK_API_KEY and MESHSTACK_API_SECRET together". Each start blanked the key and set it again a moment later, while the execs started before it were already reading their settings. Before: 2 of 49 runs of the concurrent subtest failed this way in meshfed-release CI since 2026-10-07 (jobs 115331305948 and 115838981323). A throwaway test that started 4 commands at once through start(), against internal/testutil/fakemeshstack, 2000 times, saw 1291 of 8000 commands read a blank API key or secret, and 274 a blank MESHSTACK_WORKSPACE. With an os.CopyFS before each start, as the subtest does, the counts were 1454 and 560. After: the same test saw 0 of 8000 commands read a blank value in both variants, and the whole TestAccTfstate passes against the local stack. Co-Authored-By: Claude Opus 5.5 --- cmd/internal/testacc/testacc_test.go | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/cmd/internal/testacc/testacc_test.go b/cmd/internal/testacc/testacc_test.go index 026b1d7f..be316324 100644 --- a/cmd/internal/testacc/testacc_test.go +++ b/cmd/internal/testacc/testacc_test.go @@ -109,8 +109,12 @@ func (c *cli) setEnv(key, value string) { // environ blanks every MESHSTACK_* name this suite does not set on purpose, and the names that // force color, so that a developer's .env or a CI cannot decide what a test proves. +// +// It holds each name once, with the value set last. applyEnv sets the names one at a time while +// earlier commands of c may still run and read them, so a name it set twice would show such a +// command the first value for a moment, such as a blank API key. func (c *cli) environ() []string { - return append([]string{ + variables := append([]string{ envConfigDir + "=" + c.configDir, envNoBrowser + "=" + testAccOn, envEndpoint + "=" + c.endpoint, @@ -124,6 +128,18 @@ func (c *cli) environ() []string { "CLICOLOR_FORCE=", "TTY_FORCE=", }, c.extraEnv...) + var environ []string + indexOf := map[string]int{} + for _, variable := range variables { + key, _, _ := strings.Cut(variable, "=") + if index, seen := indexOf[key]; seen { + environ[index] = variable + continue + } + indexOf[key] = len(environ) + environ = append(environ, variable) + } + return environ } // newRootCommand holds the commands this suite runs. The binary's own root is in package main, which @@ -153,7 +169,9 @@ func (c *cli) applyEnv() { } } -// start sets the environment with t.Setenv, so no two commands of a test can run at the same time. +// start sets the environment with t.Setenv, which every command in the process shares. Commands of +// one cli may run at the same time, since each start sets the values the others already read; +// commands of two clis may not. func (c *cli) start(stdin string, args ...string) *cliRun { c.t.Helper() c.applyEnv()