feat: passthrough additional env vars to provider

as some provider binaries probably need additional environment variables
set (e.g kubernetes as client-go depends on KUBERNETES_SERVICE_ vars) it
should be possible to define a list of environment variables which
should get bypassed into the provider binary execution
This commit is contained in:
Mario Constanti 2023-11-30 16:40:46 +01:00
parent 05e179604d
commit 215bd71855
4 changed files with 145 additions and 8 deletions

View file

@ -39,6 +39,9 @@ const (
MySQLBackend DBBackendType = "mysql"
// SQLiteBackend represents the SQLite3 DB backend
SQLiteBackend DBBackendType = "sqlite3"
// EnvironmentVariablePrefix is the prefix for all environment variables
// that can not be used to get overwritten via the external provider
EnvironmentVariablePrefix = "GARM"
)
// NewConfig returns a new Config

View file

@ -18,6 +18,7 @@ import (
"fmt"
"os"
"path/filepath"
"strings"
"github.com/cloudbase/garm-provider-common/util/exec"
@ -42,6 +43,25 @@ type External struct {
// the provider. If specified, it will take precedence over the "garm-external-provider"
// executable in the ProviderDir.
ProviderExecutable string `toml:"provider_executable" json:"provider-executable"`
// EnvironmentVariables is a list of environment variable names that will be
// passed to the external binary together with their values.
EnvironmentVariables []string `toml:"environment_variables" json:"environment-variables"`
}
func (e *External) GetEnvironmentVariables() []string {
envVars := []string{}
for _, configuredEnvVars := range e.EnvironmentVariables {
// discover environment variables
for _, k := range os.Environ() {
variable := strings.SplitN(k, "=", 2)
if strings.HasPrefix(variable[0], configuredEnvVars) &&
!strings.HasPrefix(variable[0], EnvironmentVariablePrefix) {
envVars = append(envVars, k)
}
}
}
return envVars
}
func (e *External) ExecutablePath() (string, error) {

View file

@ -18,6 +18,7 @@ import (
"fmt"
"os"
"path/filepath"
"slices"
"testing"
"github.com/stretchr/testify/require"
@ -121,3 +122,101 @@ func TestProviderExecutableIsExecutable(t *testing.T) {
require.NotNil(t, err)
require.EqualError(t, err, fmt.Sprintf("external provider binary %s is not executable", execPath))
}
func TestExternalEnvironmentVariables(t *testing.T) {
cfg := getDefaultExternalConfig(t)
tests := []struct {
name string
cfg External
expectedEnvironmentVariables []string
environmentVariables map[string]string
}{
{
name: "Provider with no additional environment variables",
cfg: cfg,
expectedEnvironmentVariables: []string{},
environmentVariables: map[string]string{
"PATH": "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
"PROVIDER_LOG_LEVEL": "debug",
"PROVIDER_TIMEOUT": "30",
"PROVIDER_RETRY_COUNT": "3",
"INFRA_REGION": "us-east-1",
},
},
{
name: "Provider with additional environment variables",
cfg: External{
ConfigFile: "",
ProviderDir: "../test",
EnvironmentVariables: []string{
"PROVIDER_",
"INFRA_REGION",
},
},
environmentVariables: map[string]string{
"PATH": "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
"PROVIDER_LOG_LEVEL": "debug",
"PROVIDER_TIMEOUT": "30",
"PROVIDER_RETRY_COUNT": "3",
"INFRA_REGION": "us-east-1",
"GARM_POOL_ID": "f3b21376-e189-43ae-a1bd-7a3ffee57a58",
},
expectedEnvironmentVariables: []string{
"PROVIDER_LOG_LEVEL=debug",
"PROVIDER_TIMEOUT=30",
"PROVIDER_RETRY_COUNT=3",
"INFRA_REGION=us-east-1",
},
},
{
name: "GARM variables are getting ignored",
cfg: External{
ConfigFile: "",
ProviderDir: "../test",
EnvironmentVariables: []string{
"PROVIDER_",
"INFRA_REGION",
"GARM_SERVER",
},
},
environmentVariables: map[string]string{
"PATH": "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
"PROVIDER_LOG_LEVEL": "debug",
"PROVIDER_TIMEOUT": "30",
"PROVIDER_RETRY_COUNT": "3",
"INFRA_REGION": "us-east-1",
"GARM_POOL_ID": "f3b21376-e189-43ae-a1bd-7a3ffee57a58",
"GARM_SERVER_SHUTDOWN": "true",
"GARM_SERVER_INSECURE": "true",
},
expectedEnvironmentVariables: []string{
"PROVIDER_LOG_LEVEL=debug",
"PROVIDER_TIMEOUT=30",
"PROVIDER_RETRY_COUNT=3",
"INFRA_REGION=us-east-1",
},
},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
// set environment variables
for k, v := range tc.environmentVariables {
err := os.Setenv(k, v)
if err != nil {
t.Fatalf("failed to set environment variable: %s", err)
}
}
envVars := tc.cfg.GetEnvironmentVariables()
// sort slices to make them comparable
slices.Sort(envVars)
slices.Sort(tc.expectedEnvironmentVariables)
// compare slices
require.Equal(t, tc.expectedEnvironmentVariables, envVars)
})
}
}