diff --git a/pkg/config/config.go b/pkg/config/config.go index 6396647f4..473c8157f 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -29,21 +29,20 @@ import ( "sigs.k8s.io/yaml" "k8s.io/client-go/tools/clientcmd" - - kubeconfig "k8s.io/client-go/tools/clientcmd/api" + clientcmdapi "k8s.io/client-go/tools/clientcmd/api" "opendev.org/airship/airshipctl/pkg/util" ) -// Called from root to Load the initial configuration -func (c *Config) LoadConfig(configFileArg string, kPathOptions *clientcmd.PathOptions) error { - err := c.loadFromAirConfig(configFileArg) +// LoadConfig populates the Config object using the files found at +// airshipConfigPath and kubeConfigPath +func (c *Config) LoadConfig(airshipConfigPath, kubeConfigPath string) error { + err := c.loadFromAirConfig(airshipConfigPath) if err != nil { return err } - // Load or initialize the kubeconfig object from a file - err = c.loadKubeConfig(kPathOptions) + err = c.loadKubeConfig(kubeConfigPath) if err != nil { return err } @@ -52,36 +51,45 @@ func (c *Config) LoadConfig(configFileArg string, kPathOptions *clientcmd.PathOp return c.reconcileConfig() } -func (c *Config) loadFromAirConfig(configFileArg string) error { - // If it exists, Read the ConfigFile data - // Only care about the errors here, because there is a file - // And essentially I cannot use its data. - // airshipctl probable should stop - if configFileArg == "" { +// loadFromAirConfig populates the Config from the file found at airshipConfigPath. +// If there is no file at airshipConfigPath, this function does nothing. +// An error is returned if: +// * airshipConfigPath is the empty string +// * the file at airshipConfigPath is inaccessible +// * the file at airshipConfigPath cannot be marshaled into Config +func (c *Config) loadFromAirConfig(airshipConfigPath string) error { + if airshipConfigPath == "" { return errors.New("Configuration file location was not provided.") } + // Remember where I loaded the Config from - c.loadedConfigPath = configFileArg - // If I have a file to read, load from it + c.loadedConfigPath = airshipConfigPath - if _, err := os.Stat(configFileArg); os.IsNotExist(err) { + // If I can read from the file, load from it + if _, err := os.Stat(airshipConfigPath); os.IsNotExist(err) { return nil - } - return util.ReadYAMLFile(configFileArg, c) -} - -func (c *Config) loadKubeConfig(kPathOptions *clientcmd.PathOptions) error { - // Will need this for Persisting the changes - c.loadedPathOptions = kPathOptions - // Now at this point what I load might not reflect the associated kubeconfig yet - kConfig, err := kPathOptions.GetStartingConfig() - if err != nil { + } else if err != nil { return err } - // Store the kubeconfig object into an airship managed kubeconfig object - c.kubeConfig = kConfig - return nil + return util.ReadYAMLFile(airshipConfigPath, c) +} + +func (c *Config) loadKubeConfig(kubeConfigPath string) error { + // Will need this for persisting the changes + c.kubeConfigPath = kubeConfigPath + + // If I can read from the file, load from it + var err error + if _, err = os.Stat(kubeConfigPath); os.IsNotExist(err) { + c.kubeConfig = clientcmdapi.NewConfig() + return nil + } else if err != nil { + return err + } + + c.kubeConfig, err = clientcmd.LoadFromFile(kubeConfigPath) + return err } // reconcileConfig serves two functions: @@ -198,6 +206,7 @@ func (c *Config) rmConfigClusterStragglers(persistIt bool) bool { } return rccs } + func (c *Config) reconcileContexts(updatedClusterNames map[string]string) { for key, context := range c.kubeConfig.Contexts { // Check if the Cluster name referred to by the context @@ -254,7 +263,7 @@ func (c *Config) reconcileAuthInfos() { func (c *Config) reconcileCurrentContext() { // If the Airship current context is different that the current context in the kubeconfig // then - // - if the airship current context is valid, then updated kubeconfiug CC + // - if the airship current context is valid, then updated kubeconfig CC // - if the airship currentcontext is invalid, and the kubeconfig CC is valid, then create the reference // - otherwise , they are both empty. Make sure @@ -326,20 +335,17 @@ func (c *Config) EnsureComplete() error { return nil } -// This function is called to update the configuration in the file defined by the -// ConfigFile name -// It will completely overwrite the existing file, -// If the file specified by ConfigFile exists ts updates with the contents of the Config object -// If the file specified by ConfigFile does not exist it will create a new file. +// PersistConfig updates the airshipctl config and kubeconfig files to match +// the current Config and KubeConfig objects. +// If either file did not previously exist, the file will be created. +// Otherwise, the file will be overwritten func (c *Config) PersistConfig() error { - // Dont care if the file exists or not, will create if needed - // We are 100% overwriting the existing file - configyaml, err := c.ToYaml() + airshipConfigYaml, err := c.ToYaml() if err != nil { return err } - // WriteFile doesn't create the directory , create it if needed + // WriteFile doesn't create the directory, create it if needed configDir := filepath.Dir(c.loadedConfigPath) err = os.MkdirAll(configDir, 0755) if err != nil { @@ -347,19 +353,13 @@ func (c *Config) PersistConfig() error { } // Write the Airship Config file - err = ioutil.WriteFile(c.loadedConfigPath, configyaml, 0644) + err = ioutil.WriteFile(c.loadedConfigPath, airshipConfigYaml, 0644) if err != nil { return err } - // FIXME(howell): if this fails, then the results from the previous - // actions will persist, meaning that we might have overwritten our - // airshipconfig without updating our kubeconfig. A possible solution - // is to generate brand new config files and then write them at the - // end. That could still fail, but should be more robust - // Persist the kubeconfig file referenced - if err := clientcmd.ModifyConfig(c.loadedPathOptions, *c.kubeConfig, true); err != nil { + if err := clientcmd.WriteToFile(*c.kubeConfig, c.kubeConfigPath); err != nil { return err } @@ -386,14 +386,15 @@ func (c *Config) SetLoadedConfigPath(lcp string) { c.loadedConfigPath = lcp } -func (c *Config) LoadedPathOptions() *clientcmd.PathOptions { - return c.loadedPathOptions -} -func (c *Config) SetLoadedPathOptions(po *clientcmd.PathOptions) { - c.loadedPathOptions = po +func (c *Config) KubeConfigPath() string { + return c.kubeConfigPath } -func (c *Config) KubeConfig() *kubeconfig.Config { +func (c *Config) SetKubeConfigPath(kubeConfigPath string) { + c.kubeConfigPath = kubeConfigPath +} + +func (c *Config) KubeConfig() *clientcmdapi.Config { return c.kubeConfig } @@ -441,7 +442,7 @@ func (c *Config) AddCluster(theCluster *ClusterOptions) (*Cluster, error) { nCluster := NewCluster() c.Clusters[theCluster.Name].ClusterTypes[theCluster.ClusterType] = nCluster // Create a new Kubeconfig Cluster object as well - kcluster := kubeconfig.NewCluster() + kcluster := clientcmdapi.NewCluster() clusterName := NewClusterComplexName() clusterName.WithType(theCluster.Name, theCluster.ClusterType) nCluster.NameInKubeconf = clusterName.Name() @@ -541,7 +542,7 @@ func (c *Config) AddContext(theContext *ContextOptions) *Context { nContext := NewContext() c.Contexts[theContext.Name] = nContext // Create a new Kubeconfig Context object as well - kContext := kubeconfig.NewContext() + kContext := clientcmdapi.NewContext() nContext.NameInKubeconf = theContext.Name contextName := NewClusterComplexName() contextName.WithType(theContext.Name, theContext.ClusterType) @@ -645,7 +646,7 @@ func (c *Config) AddAuthInfo(theAuthInfo *AuthInfoOptions) *AuthInfo { nAuthInfo := NewAuthInfo() c.AuthInfos[theAuthInfo.Name] = nAuthInfo // Create a new Kubeconfig AuthInfo object as well - kAuthInfo := kubeconfig.NewAuthInfo() + kAuthInfo := clientcmdapi.NewAuthInfo() nAuthInfo.SetKubeAuthInfo(kAuthInfo) c.KubeConfig().AuthInfos[theAuthInfo.Name] = kAuthInfo @@ -739,10 +740,10 @@ func (c *Cluster) PrettyString() string { clusterName.ClusterName(), clusterName.ClusterType(), c) } -func (c *Cluster) KubeCluster() *kubeconfig.Cluster { +func (c *Cluster) KubeCluster() *clientcmdapi.Cluster { return c.kCluster } -func (c *Cluster) SetKubeCluster(kc *kubeconfig.Cluster) { +func (c *Cluster) SetKubeCluster(kc *clientcmdapi.Cluster) { c.kCluster = kc } @@ -777,11 +778,11 @@ func (c *Context) PrettyString() string { clusterName.ClusterName(), c.String()) } -func (c *Context) KubeContext() *kubeconfig.Context { +func (c *Context) KubeContext() *clientcmdapi.Context { return c.kContext } -func (c *Context) SetKubeContext(kc *kubeconfig.Context) { +func (c *Context) SetKubeContext(kc *clientcmdapi.Context) { c.kContext = kc } @@ -808,10 +809,10 @@ func (c *AuthInfo) String() string { return string(kyaml) } -func (c *AuthInfo) KubeAuthInfo() *kubeconfig.AuthInfo { +func (c *AuthInfo) KubeAuthInfo() *clientcmdapi.AuthInfo { return c.kAuthInfo } -func (c *AuthInfo) SetKubeAuthInfo(kc *kubeconfig.AuthInfo) { +func (c *AuthInfo) SetKubeAuthInfo(kc *clientcmdapi.AuthInfo) { c.kAuthInfo = kc } @@ -979,7 +980,7 @@ PLACEHOLDER UNTIL I IDENTIFY if CLIENTADM HAS SOMETHING LIKE THIS */ -func KClusterString(kCluster *kubeconfig.Cluster) string { +func KClusterString(kCluster *clientcmdapi.Cluster) string { yamlData, err := yaml.Marshal(&kCluster) if err != nil { return "" @@ -987,7 +988,8 @@ func KClusterString(kCluster *kubeconfig.Cluster) string { return string(yamlData) } -func KContextString(kContext *kubeconfig.Context) string { + +func KContextString(kContext *clientcmdapi.Context) string { yamlData, err := yaml.Marshal(&kContext) if err != nil { return "" @@ -995,7 +997,8 @@ func KContextString(kContext *kubeconfig.Context) string { return string(yamlData) } -func KAuthInfoString(kAuthInfo *kubeconfig.AuthInfo) string { + +func KAuthInfoString(kAuthInfo *clientcmdapi.AuthInfo) string { yamlData, err := yaml.Marshal(&kAuthInfo) if err != nil { return "" diff --git a/pkg/config/config_test.go b/pkg/config/config_test.go index cc0c5dd4d..0bb92ea4b 100644 --- a/pkg/config/config_test.go +++ b/pkg/config/config_test.go @@ -26,7 +26,6 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "k8s.io/client-go/tools/clientcmd" kubeconfig "k8s.io/client-go/tools/clientcmd/api" "opendev.org/airship/airshipctl/testutil" @@ -224,10 +223,11 @@ func TestPersistConfig(t *testing.T) { config := InitConfig(t) err := config.PersistConfig() - assert.NoErrorf(t, err, "Unable to persist configuration expected at %v", config.LoadedConfigPath()) + require.NoError(t, err) - kpo := config.LoadedPathOptions() - assert.NotNil(t, kpo) + // Check that the files were created + assert.FileExists(t, config.LoadedConfigPath()) + assert.FileExists(t, config.KubeConfigPath()) } func TestEnsureComplete(t *testing.T) { @@ -347,11 +347,10 @@ func TestPurge(t *testing.T) { require.NoError(t, err) airConfigFile := filepath.Join(tempDir, AirshipConfig) - kConfigFile := filepath.Join(tempDir, AirshipKubeConfig) config.SetLoadedConfigPath(airConfigFile) - kubePathOptions := clientcmd.NewDefaultPathOptions() - kubePathOptions.GlobalFile = kConfigFile - config.SetLoadedPathOptions(kubePathOptions) + + kConfigFile := filepath.Join(tempDir, AirshipKubeConfig) + config.kubeConfigPath = kConfigFile // Store it err = config.PersistConfig() diff --git a/pkg/config/constants.go b/pkg/config/constants.go index d06c73e8f..936c238d1 100644 --- a/pkg/config/constants.go +++ b/pkg/config/constants.go @@ -11,19 +11,22 @@ const ( AirshipClusterDefaultType = Target ) -//Sorted +// Sorted var AllClusterTypes = [2]string{Ephemeral, Target} // Constants defining default values const ( - AirshipConfigEnv = "airshipconf" - AirshipConfig = "config" - AirshipConfigDir = ".airship" - AirshipConfigKind = "Config" - AirshipConfigVersion = "v1alpha1" AirshipConfigGroup = "airshipit.org" + AirshipConfigVersion = "v1alpha1" AirshipConfigApiVersion = AirshipConfigGroup + "/" + AirshipConfigVersion - AirshipKubeConfig = "kubeconfig" + AirshipConfigKind = "Config" + + AirshipConfigDir = ".airship" + AirshipConfig = "config" + AirshipKubeConfig = "kubeconfig" + + AirshipConfigEnv = "AIRSHIPCONFIG" + AirshipKubeConfigEnv = "AIRSHIP_KUBECONFIG" ) // Constants defining CLI flags @@ -37,7 +40,7 @@ const ( FlagClusterType = "cluster-type" FlagContext = "context" FlagCurrentContext = "current-context" - FlagConfigFilePath = AirshipConfigEnv + FlagConfigFilePath = "airshipconf" FlagEmbedCerts = "embed-certs" FlagImpersonate = "as" FlagImpersonateGroup = "as-group" diff --git a/pkg/config/test_utils.go b/pkg/config/test_utils.go index a285d94e8..799e5b5f5 100644 --- a/pkg/config/test_utils.go +++ b/pkg/config/test_utils.go @@ -22,7 +22,6 @@ import ( "path/filepath" "testing" - "k8s.io/client-go/tools/clientcmd" kubeconfig "k8s.io/client-go/tools/clientcmd/api" "github.com/stretchr/testify/require" @@ -145,9 +144,7 @@ func InitConfig(t *testing.T) *Config { conf := NewConfig() - kubePathOptions := clientcmd.NewDefaultPathOptions() - kubePathOptions.GlobalFile = kubeConfigPath - err = conf.LoadConfig(configPath, kubePathOptions) + err = conf.LoadConfig(configPath, kubeConfigPath) require.NoError(t, err) return conf diff --git a/pkg/config/types.go b/pkg/config/types.go index 64d5a43d4..6750fd77e 100644 --- a/pkg/config/types.go +++ b/pkg/config/types.go @@ -19,7 +19,6 @@ package config import ( "net/url" - "k8s.io/client-go/tools/clientcmd" kubeconfig "k8s.io/client-go/tools/clientcmd/api" ) @@ -57,15 +56,15 @@ type Config struct { // Such as Bootstrap, Workflows, Document, etc ModulesConfig *Modules `json:"modules-config"` - // Private LoadedConfigPath is the full path to the the location of the config file - // from which these config was loaded + // loadedConfigPath is the full path to the the location of the config + // file from which this config was loaded // +not persisted in file loadedConfigPath string - // Private loadedPathOptions is the full path to the the location of the kubeconfig file - // associated with this airship config instance + // kubeConfigPath is the full path to the the location of the + // kubeconfig file associated with this airship config instance // +not persisted in file - loadedPathOptions *clientcmd.PathOptions + kubeConfigPath string // Private instance of Kube Config content as an object kubeConfig *kubeconfig.Config diff --git a/pkg/environment/settings.go b/pkg/environment/settings.go index e2f05be6c..8d1f14fba 100644 --- a/pkg/environment/settings.go +++ b/pkg/environment/settings.go @@ -3,7 +3,6 @@ package environment import ( "os" "path/filepath" - "strings" "github.com/spf13/cobra" @@ -64,67 +63,64 @@ func (a *AirshipCTLSettings) SetKubeConfigPath(kcp string) { func (a *AirshipCTLSettings) InitConfig() { a.SetConfig(config.NewConfig()) - a.setAirshipConfigPath() + a.initAirshipConfigPath() + a.initKubeConfigPath() - //Pass the airshipConfigPath and kubeConfig object - err := a.Config().LoadConfig(a.AirshipConfigPath(), a.setKubePathOptions()) + err := a.Config().LoadConfig(a.AirshipConfigPath(), a.KubeConfigPath()) if err != nil { // Should stop airshipctl log.Fatal(err) } } -func (a *AirshipCTLSettings) setAirshipConfigPath() { - // (1) If the airshipConfigPath was received as an argument its already set - if a.airshipConfigPath == "" { - // (2) If not , we can check if we got the Path via ENVIRONMENT variable, - // set the appropriate fields - a.setAirshipConfigPathFromEnv() +func (a *AirshipCTLSettings) initAirshipConfigPath() { + // The airshipConfigPath may already have been received as a command line argument + if a.airshipConfigPath != "" { + return } - // (3) Check if the a.airshipConfigPath is empty still at this point , use the defaults - acp, home := a.replaceHomePlaceholder(a.airshipConfigPath) - a.airshipConfigPath = acp - if a.airshipConfigPath == "" { - a.airshipConfigPath = filepath.Join(home, config.AirshipConfigDir, config.AirshipConfig) - } -} -// setAirshipConfigPathFromEnv Get AIRSHIP CONFIG from an environment variable if set -func (a *AirshipCTLSettings) setAirshipConfigPathFromEnv() { - // Check if AIRSHIPCONF env variable was set - // I have the path and name for the airship config file + // Otherwise, we can check if we got the path via ENVIRONMENT variable a.airshipConfigPath = os.Getenv(config.AirshipConfigEnv) -} - -func (a *AirshipCTLSettings) setKubePathOptions() *clientcmd.PathOptions { - // USe default expectations for Kubeconfig - kubePathOptions := clientcmd.NewDefaultPathOptions() - // No need to check the Environment , since we are relying on the kubeconfig defaults - // If we did not get an explicit kubeconfig definition on airshipctl - // as far as airshipctl is concerned will use the default expectations for the kubeconfig - // file location . This avoids messing up someones kubeconfig if they didnt explicitly want - // airshipctl to use it. - kcp, home := a.replaceHomePlaceholder(a.kubeConfigPath) - a.kubeConfigPath = kcp - if a.kubeConfigPath == "" { - a.kubeConfigPath = filepath.Join(home, config.AirshipConfigDir, config.AirshipKubeConfig) + if a.airshipConfigPath != "" { + return } - // We will always rely on tha airshipctl cli args or default for where to find kubeconfig - kubePathOptions.GlobalFile = a.kubeConfigPath - kubePathOptions.GlobalFileSubpath = a.kubeConfigPath - return kubePathOptions + // Otherwise, we'll try putting it in the home directory + homeDir := userHomeDir() + a.airshipConfigPath = filepath.Join(homeDir, config.AirshipConfigDir, config.AirshipConfig) } -func (a *AirshipCTLSettings) replaceHomePlaceholder(configPath string) (string, string) { - home, err := os.UserHomeDir() +func (a *AirshipCTLSettings) initKubeConfigPath() { + // NOTE(howell): This function will set the kubeConfigPath to the + // default location under the airship directory unless the user + // *explicitly* specifies a different location, either by setting the + // ENVIRONMENT variable or by passing a command line argument. + // This avoids messing up the user's kubeconfig if they didn't + // explicitly want airshipctl to use it. + + // The kubeConfigPath may already have been received as a command line argument + if a.kubeConfigPath != "" { + return + } + + // Otherwise, we can check if we got the path via ENVIRONMENT variable + a.kubeConfigPath = os.Getenv(config.AirshipKubeConfigEnv) + if a.kubeConfigPath != "" { + return + } + + // Otherwise, we'll try putting it in the home directory + homeDir := userHomeDir() + a.kubeConfigPath = filepath.Join(homeDir, config.AirshipConfigDir, config.AirshipKubeConfig) +} + +// userHomeDir is a utility function that wraps os.UserHomeDir and returns no +// errors. If the user has no home directory, the returned value will be the +// empty string +func userHomeDir() string { + homeDir, err := os.UserHomeDir() if err != nil { - // Use defaults under current directory - home = "" + homeDir = "" } - if configPath == "" { - return configPath, home - } - - return strings.Replace(configPath, HomePlaceholder, home, 1), home + return homeDir } diff --git a/pkg/environment/settings_test.go b/pkg/environment/settings_test.go index 4e31bf9f8..cb6a362d7 100644 --- a/pkg/environment/settings_test.go +++ b/pkg/environment/settings_test.go @@ -17,6 +17,7 @@ limitations under the License. package environment import ( + "io/ioutil" "os" "path/filepath" "testing" @@ -36,25 +37,107 @@ func TestInitFlags(t *testing.T) { assert.True(t, testCmd.HasPersistentFlags()) } -func TestSpecifyAirConfigFromEnv(t *testing.T) { - fakeConfig := "FakeConfigPath" - err := os.Setenv(config.AirshipConfigEnv, fakeConfig) +func TestInitConfig(t *testing.T) { + t.Run("DefaultToHomeDirectory", func(subTest *testing.T) { + // Set up a fake $HOME directory + testDir := makeTestDir(t) + defer deleteTestDir(t, testDir) + defer setHome(testDir)() + + var testSettings AirshipCTLSettings + expectedAirshipConfig := filepath.Join(testDir, config.AirshipConfigDir, config.AirshipConfig) + expectedKubeConfig := filepath.Join(testDir, config.AirshipConfigDir, config.AirshipKubeConfig) + + testSettings.InitConfig() + assert.Equal(t, expectedAirshipConfig, testSettings.AirshipConfigPath()) + assert.Equal(t, expectedKubeConfig, testSettings.KubeConfigPath()) + }) + + t.Run("PreferEnvToDefault", func(subTest *testing.T) { + // Set up a fake $HOME directory + testDir := makeTestDir(t) + defer deleteTestDir(t, testDir) + defer setHome(testDir)() + + var testSettings AirshipCTLSettings + expectedAirshipConfig := filepath.Join(testDir, "airshipEnv") + expectedKubeConfig := filepath.Join(testDir, "kubeEnv") + + os.Setenv(config.AirshipConfigEnv, expectedAirshipConfig) + os.Setenv(config.AirshipKubeConfigEnv, expectedKubeConfig) + defer os.Unsetenv(config.AirshipConfigEnv) + defer os.Unsetenv(config.AirshipKubeConfigEnv) + + testSettings.InitConfig() + assert.Equal(t, expectedAirshipConfig, testSettings.AirshipConfigPath()) + assert.Equal(t, expectedKubeConfig, testSettings.KubeConfigPath()) + }) + + t.Run("PreferCmdLineArgToDefault", func(subTest *testing.T) { + // Set up a fake $HOME directory + testDir := makeTestDir(t) + defer deleteTestDir(t, testDir) + defer setHome(testDir)() + + var testSettings AirshipCTLSettings + expectedAirshipConfig := filepath.Join(testDir, "airshipCmdLine") + expectedKubeConfig := filepath.Join(testDir, "kubeCmdLine") + + testSettings.SetAirshipConfigPath(expectedAirshipConfig) + testSettings.SetKubeConfigPath(expectedKubeConfig) + + // InitConfig should not change any values + testSettings.InitConfig() + assert.Equal(t, expectedAirshipConfig, testSettings.AirshipConfigPath()) + assert.Equal(t, expectedKubeConfig, testSettings.KubeConfigPath()) + }) + + t.Run("PreferCmdLineArgToEnv", func(subTest *testing.T) { + // Set up a fake $HOME directory + testDir := makeTestDir(t) + defer deleteTestDir(t, testDir) + defer setHome(testDir)() + + var testSettings AirshipCTLSettings + expectedAirshipConfig := filepath.Join(testDir, "airshipCmdLine") + expectedKubeConfig := filepath.Join(testDir, "kubeCmdLine") + + // set up "decoy" environment variables. These should be + // ignored, since we're simulating passing in command line + // arguments + wrongAirshipConfig := filepath.Join(testDir, "wrongAirshipConfig") + wrongKubeConfig := filepath.Join(testDir, "wrongKubeConfig") + os.Setenv(config.AirshipConfigEnv, wrongAirshipConfig) + os.Setenv(config.AirshipKubeConfigEnv, wrongKubeConfig) + defer os.Unsetenv(config.AirshipConfigEnv) + defer os.Unsetenv(config.AirshipKubeConfigEnv) + + testSettings.SetAirshipConfigPath(expectedAirshipConfig) + testSettings.SetKubeConfigPath(expectedKubeConfig) + + testSettings.InitConfig() + assert.Equal(t, expectedAirshipConfig, testSettings.AirshipConfigPath()) + assert.Equal(t, expectedKubeConfig, testSettings.KubeConfigPath()) + }) +} + +func makeTestDir(t *testing.T) string { + testDir, err := ioutil.TempDir("", "airship-test") require.NoError(t, err) - - settings := &AirshipCTLSettings{} - settings.InitConfig() - - assert.EqualValues(t, fakeConfig, settings.AirshipConfigPath()) + return testDir } -func TestGetSetPaths(t *testing.T) { - settings := &AirshipCTLSettings{} - settings.InitConfig() - airConfigFile := filepath.Join(config.AirshipConfigDir, config.AirshipConfig) - kConfigFile := filepath.Join(config.AirshipConfigDir, config.AirshipKubeConfig) - settings.SetAirshipConfigPath(airConfigFile) - assert.EqualValues(t, airConfigFile, settings.AirshipConfigPath()) - - settings.SetKubeConfigPath(kConfigFile) - assert.EqualValues(t, kConfigFile, settings.KubeConfigPath()) +func deleteTestDir(t *testing.T, path string) { + err := os.Remove(path) + require.NoError(t, err) +} + +// setHome sets the HOME environment variable to `path`, and returns a function +// that can be used to reset HOME to its original value +func setHome(path string) (resetHome func()) { + oldHome := os.Getenv("HOME") + os.Setenv("HOME", path) + return func() { + os.Setenv("HOME", oldHome) + } }