Refactor management of config file paths
This change replaces the usage of the clientcmd's PathOptions with a simpler filepath approach. The handling of the kube config file associated with airshipctl is now entirely managed by airshipctl. This also introduces the environment variable AIRSHIP_KUBECONFIG, which can be used to override the default location for airship's kube config. It can in turn be overridden by the command line argument. Prior to this change, the kube config object was created by creating a kubernetes client, then stripping off that client's config object. As a side effect, this change removes the middleman, and creates the kube config object directly from file. Change-Id: I99ba88d50a0f45c40597a58fe4b3fdfeb7d1467d
This commit is contained in:
parent
c7c1011a5c
commit
32ef58435d
@ -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 ""
|
||||
|
@ -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()
|
||||
|
@ -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"
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user