Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions agent/lifecycle/keymanagementagent/launcher/launcher_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"testing"
"time"

"github.com/eclipse-cfm/cfm/common/fixtures"
"github.com/eclipse-cfm/cfm/common/natsclient"
"github.com/eclipse-cfm/cfm/common/natsfixtures"
"github.com/nats-io/nats.go/jetstream"
Expand All @@ -43,6 +44,8 @@ func TestKeyManagementAgent_Integration(t *testing.T) {
require.NoError(t, err)
defer natsfixtures.TeardownNatsContainer(ctx, nt)

fixtures.IsolateConfig(t)

t.Setenv("KMAGENT_URI", nt.URI)
t.Setenv("KMAGENT_BUCKET", bucket)
t.Setenv("KMAGENT_STREAM", streamName)
Expand Down
8 changes: 8 additions & 0 deletions common/fixtures/fixtures.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,16 @@ package fixtures
import (
"net"
"testing"

"github.com/eclipse-cfm/cfm/common/system"
)

// IsolateConfig points the configuration file search path at an empty temp directory so that configuration files on
// the developer's machine (e.g. $HOME/.appname) cannot leak into the test. The override is undone when the test ends.
func IsolateConfig(t *testing.T) {
t.Setenv(system.ConfigDirEnvVar, t.TempDir())
}

// GetRandomPort returns a random available port or fails the test
func GetRandomPort(t *testing.T) int {
listener, err := net.Listen("tcp", ":0")
Expand Down
3 changes: 3 additions & 0 deletions common/lifecycleagent/agent_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"testing"
"time"

"github.com/eclipse-cfm/cfm/common/fixtures"
"github.com/eclipse-cfm/cfm/common/lifecycleagent"
"github.com/eclipse-cfm/cfm/common/natsclient"
"github.com/eclipse-cfm/cfm/common/natsfixtures"
Expand Down Expand Up @@ -73,6 +74,8 @@ func provisionStream(t *testing.T, ctx context.Context, nt *natsfixtures.NatsTes
}

func setupAgentEnv(t *testing.T, prefix, uri string) {
fixtures.IsolateConfig(t)

// viper uppercases the env prefix and key, so the environment variables must be uppercase.
p := strings.ToUpper(prefix)
t.Setenv(p+"_URI", uri)
Expand Down
16 changes: 13 additions & 3 deletions common/system/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,19 +14,29 @@ package system

import (
"fmt"
"os"
"strings"

"github.com/spf13/viper"
)

// ConfigDirEnvVar overrides the configuration file search path. When set, only the given directory is searched for
// configuration files, replacing the default locations entirely. This pins the config location in deployments and
// isolates tests from configuration files on the developer's machine.
const ConfigDirEnvVar = "CFM_CONFIG_DIR"

// LoadConfigOrPanic initializes a Config instance with the specified configuration name.
// Configuration will be read from a file (if it exists) and can be overridden using environment variables.
func LoadConfigOrPanic(name string) *viper.Viper {
v := viper.New()
v.SetConfigName(name)
v.AddConfigPath("/etc/appname/")
v.AddConfigPath("$HOME/.appname")
v.AddConfigPath(".")
if dir := os.Getenv(ConfigDirEnvVar); dir != "" {
v.AddConfigPath(dir)
} else {
v.AddConfigPath("/etc/appname/")
v.AddConfigPath("$HOME/.appname")
v.AddConfigPath(".")
}
v.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
v.AutomaticEnv()
v.SetEnvPrefix(name)
Expand Down
38 changes: 25 additions & 13 deletions common/system/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,10 @@ import (

const appName = "testapp"

//nolint:errcheck
func TestLoadConfigFromEnvironment(t *testing.T) {
// setup
os.Setenv("TESTAPP_FOO", "foo_value")
os.Setenv("TESTAPP_BAR", "bar_value")
defer func() {
os.Unsetenv("FOO")
os.Unsetenv("BAR")
}()
t.Setenv("TESTAPP_FOO", "foo_value")
t.Setenv("TESTAPP_BAR", "bar_value")

v := LoadConfigOrPanic(appName)

Expand Down Expand Up @@ -50,6 +45,27 @@ bar: "bar_value"
assert.Equal(t, "bar_value", v.GetString("bar"))
}

func TestLoadConfigDirOverride(t *testing.T) {
// a config file in the current directory, which must be ignored when the override is set
cwdContent := []byte(`foo: "cwd_value"`)
require.NoError(t, os.WriteFile("./testapp.yaml", cwdContent, 0644))
defer func() { _ = os.Remove("./testapp.yaml") }()

// a config file in the override directory
overrideDir := t.TempDir()
overrideContent := []byte(`foo: "override_value"`)
require.NoError(t, os.WriteFile(filepath.Join(overrideDir, "testapp.yaml"), overrideContent, 0644))

t.Setenv(ConfigDirEnvVar, overrideDir)
v := LoadConfigOrPanic(appName)
assert.Equal(t, "override_value", v.GetString("foo"), "only the override directory must be searched")

// pointing the override at an empty directory must yield no file-based configuration at all
t.Setenv(ConfigDirEnvVar, t.TempDir())
v = LoadConfigOrPanic(appName)
assert.Empty(t, v.GetString("foo"), "config files outside the override directory must be ignored")
}

//nolint:errcheck
func TestLoadConfigEnvPriorityOverFile(t *testing.T) {
// create the config file
Expand All @@ -68,12 +84,8 @@ bar: "bar_value_file"
defer os.Remove("./testapp.yaml")

// Set environment variables
os.Setenv("TESTAPP_FOO", "foo_value_file")
os.Setenv("TESTAPP_BAR", "bar_value_env")
defer func() {
os.Unsetenv("TESTAPP_FOO")
os.Unsetenv("TESTAPP_BAR")
}()
t.Setenv("TESTAPP_FOO", "foo_value_file")
t.Setenv("TESTAPP_BAR", "bar_value_env")

v := LoadConfigOrPanic(appName)

Expand Down
2 changes: 2 additions & 0 deletions e2e/e2etests/fixtures.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ func launchPlatformWithAgent(t *testing.T, natsURI string, pgDsn string) *e2efix
}

func launchPlatform(t *testing.T, natsURI string, pgDsn string) (chan struct{}, *e2efixtures.ApiClient) {
fixtures.IsolateConfig(t)

_ = os.Setenv("TM_POSTGRES", "true")
_ = os.Setenv("TM_DSN", pgDsn)
_ = os.Setenv("TM_URI", natsURI)
Expand Down
3 changes: 3 additions & 0 deletions e2e/e2etests/lifecycleagent_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (
"testing"
"time"

"github.com/eclipse-cfm/cfm/common/fixtures"
"github.com/eclipse-cfm/cfm/common/lifecycleagent"
"github.com/eclipse-cfm/cfm/common/natsclient"
"github.com/eclipse-cfm/cfm/common/natsfixtures"
Expand Down Expand Up @@ -74,6 +75,8 @@ func Test_VerifyLifecycleAgentDispatchesCloudEvent(t *testing.T) {
})
require.NoError(t, err)

fixtures.IsolateConfig(t)

// viper uppercases the env prefix and key, so the environment variables must be uppercase.
p := strings.ToUpper(agentName)
t.Setenv(p+"_URI", nt.URI)
Expand Down
9 changes: 5 additions & 4 deletions e2e/testagent/launcher/launcher_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,10 @@ package launcher

import (
"context"
"os"
"testing"
"time"

"github.com/eclipse-cfm/cfm/common/fixtures"
"github.com/eclipse-cfm/cfm/common/natsclient"
"github.com/eclipse-cfm/cfm/common/natsfixtures"
"github.com/eclipse-cfm/cfm/common/runtime"
Expand Down Expand Up @@ -65,9 +65,10 @@ func TestTestAgent_Integration(t *testing.T) {
}

// Required agent config
_ = os.Setenv("TESTAGENT_URI", nt.URI)
_ = os.Setenv("TESTAGENT_BUCKET", "cfm-bucket")
_ = os.Setenv("TESTAGENT_STREAM", streamName)
fixtures.IsolateConfig(t)
t.Setenv("TESTAGENT_URI", nt.URI)
t.Setenv("TESTAGENT_BUCKET", "cfm-bucket")
t.Setenv("TESTAGENT_STREAM", streamName)

// Create and start the test agent
shutdownChannel := make(chan struct{})
Expand Down
13 changes: 7 additions & 6 deletions pmanager/cmd/server/launcher/launcher_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ package launcher

import (
"context"
"os"
"strconv"
"testing"
"time"
Expand All @@ -30,6 +29,8 @@ const (
)

func TestTestAgent_Integration(t *testing.T) {
fixtures.IsolateConfig(t)

ctx, cancel := context.WithTimeout(context.Background(), testTimeout)
defer cancel()

Expand All @@ -43,11 +44,11 @@ func TestTestAgent_Integration(t *testing.T) {
natsfixtures.SetupTestStream(t, ctx, nt.Client, streamName)

// Required agent config
_ = os.Setenv("PM_URI", nt.URI)
_ = os.Setenv("PM_BUCKET", "cfm-bucket")
_ = os.Setenv("PM_STREAM", streamName)
_ = os.Setenv("PM_HTTPPORT", strconv.Itoa(fixtures.GetRandomPort(t)))
_ = os.Setenv("PM_AUTH_ENABLED", "false")
t.Setenv("PM_URI", nt.URI)
t.Setenv("PM_BUCKET", "cfm-bucket")
t.Setenv("PM_STREAM", streamName)
t.Setenv("PM_HTTPPORT", strconv.Itoa(fixtures.GetRandomPort(t)))
t.Setenv("PM_AUTH_ENABLED", "false")

// Create and start the test agent
shutdownChannel := make(chan struct{})
Expand Down
40 changes: 13 additions & 27 deletions pmanager/natsagent/launcher_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,25 +13,19 @@
package natsagent

import (
"os"
"testing"

"github.com/eclipse-cfm/cfm/common/fixtures"
"github.com/eclipse-cfm/cfm/common/system"
"github.com/eclipse-cfm/cfm/pmanager/api"
"github.com/stretchr/testify/require"
)

func Test_LaunchAgent_WithAssemblyProvider(t *testing.T) {
// Setup environment variables
_ = os.Setenv("TESTAGENT_URI", "nats://localhost:4222")
_ = os.Setenv("TESTAGENT_BUCKET", "test-bucket")
_ = os.Setenv("TESTAGENT_STREAM", "test-stream")

t.Cleanup(func() {
_ = os.Unsetenv("TESTAGENT_URI")
_ = os.Unsetenv("TESTAGENT_BUCKET")
_ = os.Unsetenv("TESTAGENT_STREAM")
})
t.Setenv("TESTAGENT_URI", "nats://localhost:4222")
t.Setenv("TESTAGENT_BUCKET", "test-bucket")
t.Setenv("TESTAGENT_STREAM", "test-stream")

// Create a flag to track if AssemblyProvider was called
var assemblyProviderCalled bool
Expand Down Expand Up @@ -90,16 +84,12 @@ func Test_LaunchAgent_WithAssemblyProvider(t *testing.T) {
}

func Test_LaunchAgent_WithoutAssemblyProvider(t *testing.T) {
// Setup environment variables
_ = os.Setenv("TESTAGENT_URI", "nats://localhost:4222")
_ = os.Setenv("TESTAGENT_BUCKET", "test-bucket")
_ = os.Setenv("TESTAGENT_STREAM", "test-stream")
fixtures.IsolateConfig(t)

t.Cleanup(func() {
_ = os.Unsetenv("TESTAGENT_URI")
_ = os.Unsetenv("TESTAGENT_BUCKET")
_ = os.Unsetenv("TESTAGENT_STREAM")
})
// Setup environment variables
t.Setenv("TESTAGENT_URI", "nats://localhost:4222")
t.Setenv("TESTAGENT_BUCKET", "test-bucket")
t.Setenv("TESTAGENT_STREAM", "test-stream")

// Create launcher config without AssemblyProvider
config := LauncherConfig{
Expand All @@ -125,15 +115,11 @@ func Test_LaunchAgent_WithoutAssemblyProvider(t *testing.T) {
}

func Test_loadAgentConfig_MissingRequiredParams(t *testing.T) {
fixtures.IsolateConfig(t)

// Test with missing URI
_ = os.Setenv("TESTAGENT_BUCKET", "test-bucket")
_ = os.Setenv("TESTAGENT_STREAM", "test-stream")

t.Cleanup(func() {
_ = os.Unsetenv("TESTAGENT_URI")
_ = os.Unsetenv("TESTAGENT_BUCKET")
_ = os.Unsetenv("TESTAGENT_STREAM")
})
t.Setenv("TESTAGENT_BUCKET", "test-bucket")
t.Setenv("TESTAGENT_STREAM", "test-stream")

// Verify that missing required params causes panic
require.Panics(t, func() {
Expand Down