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"
|
"sigs.k8s.io/yaml"
|
||||||
|
|
||||||
"k8s.io/client-go/tools/clientcmd"
|
"k8s.io/client-go/tools/clientcmd"
|
||||||
|
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
|
||||||
kubeconfig "k8s.io/client-go/tools/clientcmd/api"
|
|
||||||
|
|
||||||
"opendev.org/airship/airshipctl/pkg/util"
|
"opendev.org/airship/airshipctl/pkg/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Called from root to Load the initial configuration
|
// LoadConfig populates the Config object using the files found at
|
||||||
func (c *Config) LoadConfig(configFileArg string, kPathOptions *clientcmd.PathOptions) error {
|
// airshipConfigPath and kubeConfigPath
|
||||||
err := c.loadFromAirConfig(configFileArg)
|
func (c *Config) LoadConfig(airshipConfigPath, kubeConfigPath string) error {
|
||||||
|
err := c.loadFromAirConfig(airshipConfigPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load or initialize the kubeconfig object from a file
|
err = c.loadKubeConfig(kubeConfigPath)
|
||||||
err = c.loadKubeConfig(kPathOptions)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -52,36 +51,45 @@ func (c *Config) LoadConfig(configFileArg string, kPathOptions *clientcmd.PathOp
|
|||||||
return c.reconcileConfig()
|
return c.reconcileConfig()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Config) loadFromAirConfig(configFileArg string) error {
|
// loadFromAirConfig populates the Config from the file found at airshipConfigPath.
|
||||||
// If it exists, Read the ConfigFile data
|
// If there is no file at airshipConfigPath, this function does nothing.
|
||||||
// Only care about the errors here, because there is a file
|
// An error is returned if:
|
||||||
// And essentially I cannot use its data.
|
// * airshipConfigPath is the empty string
|
||||||
// airshipctl probable should stop
|
// * the file at airshipConfigPath is inaccessible
|
||||||
if configFileArg == "" {
|
// * 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.")
|
return errors.New("Configuration file location was not provided.")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remember where I loaded the Config from
|
// Remember where I loaded the Config from
|
||||||
c.loadedConfigPath = configFileArg
|
c.loadedConfigPath = airshipConfigPath
|
||||||
// If I have a file to read, load from it
|
|
||||||
|
|
||||||
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 nil
|
||||||
}
|
} else if err != 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 {
|
|
||||||
return err
|
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:
|
// reconcileConfig serves two functions:
|
||||||
@ -198,6 +206,7 @@ func (c *Config) rmConfigClusterStragglers(persistIt bool) bool {
|
|||||||
}
|
}
|
||||||
return rccs
|
return rccs
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Config) reconcileContexts(updatedClusterNames map[string]string) {
|
func (c *Config) reconcileContexts(updatedClusterNames map[string]string) {
|
||||||
for key, context := range c.kubeConfig.Contexts {
|
for key, context := range c.kubeConfig.Contexts {
|
||||||
// Check if the Cluster name referred to by the context
|
// Check if the Cluster name referred to by the context
|
||||||
@ -254,7 +263,7 @@ func (c *Config) reconcileAuthInfos() {
|
|||||||
func (c *Config) reconcileCurrentContext() {
|
func (c *Config) reconcileCurrentContext() {
|
||||||
// If the Airship current context is different that the current context in the kubeconfig
|
// If the Airship current context is different that the current context in the kubeconfig
|
||||||
// then
|
// 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
|
// - if the airship currentcontext is invalid, and the kubeconfig CC is valid, then create the reference
|
||||||
// - otherwise , they are both empty. Make sure
|
// - otherwise , they are both empty. Make sure
|
||||||
|
|
||||||
@ -326,20 +335,17 @@ func (c *Config) EnsureComplete() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// This function is called to update the configuration in the file defined by the
|
// PersistConfig updates the airshipctl config and kubeconfig files to match
|
||||||
// ConfigFile name
|
// the current Config and KubeConfig objects.
|
||||||
// It will completely overwrite the existing file,
|
// If either file did not previously exist, the file will be created.
|
||||||
// If the file specified by ConfigFile exists ts updates with the contents of the Config object
|
// Otherwise, the file will be overwritten
|
||||||
// If the file specified by ConfigFile does not exist it will create a new file.
|
|
||||||
func (c *Config) PersistConfig() error {
|
func (c *Config) PersistConfig() error {
|
||||||
// Dont care if the file exists or not, will create if needed
|
airshipConfigYaml, err := c.ToYaml()
|
||||||
// We are 100% overwriting the existing file
|
|
||||||
configyaml, err := c.ToYaml()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
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)
|
configDir := filepath.Dir(c.loadedConfigPath)
|
||||||
err = os.MkdirAll(configDir, 0755)
|
err = os.MkdirAll(configDir, 0755)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -347,19 +353,13 @@ func (c *Config) PersistConfig() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Write the Airship Config file
|
// Write the Airship Config file
|
||||||
err = ioutil.WriteFile(c.loadedConfigPath, configyaml, 0644)
|
err = ioutil.WriteFile(c.loadedConfigPath, airshipConfigYaml, 0644)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
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
|
// 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
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -386,14 +386,15 @@ func (c *Config) SetLoadedConfigPath(lcp string) {
|
|||||||
c.loadedConfigPath = lcp
|
c.loadedConfigPath = lcp
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Config) LoadedPathOptions() *clientcmd.PathOptions {
|
func (c *Config) KubeConfigPath() string {
|
||||||
return c.loadedPathOptions
|
return c.kubeConfigPath
|
||||||
}
|
|
||||||
func (c *Config) SetLoadedPathOptions(po *clientcmd.PathOptions) {
|
|
||||||
c.loadedPathOptions = po
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Config) KubeConfig() *kubeconfig.Config {
|
func (c *Config) SetKubeConfigPath(kubeConfigPath string) {
|
||||||
|
c.kubeConfigPath = kubeConfigPath
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Config) KubeConfig() *clientcmdapi.Config {
|
||||||
return c.kubeConfig
|
return c.kubeConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -441,7 +442,7 @@ func (c *Config) AddCluster(theCluster *ClusterOptions) (*Cluster, error) {
|
|||||||
nCluster := NewCluster()
|
nCluster := NewCluster()
|
||||||
c.Clusters[theCluster.Name].ClusterTypes[theCluster.ClusterType] = nCluster
|
c.Clusters[theCluster.Name].ClusterTypes[theCluster.ClusterType] = nCluster
|
||||||
// Create a new Kubeconfig Cluster object as well
|
// Create a new Kubeconfig Cluster object as well
|
||||||
kcluster := kubeconfig.NewCluster()
|
kcluster := clientcmdapi.NewCluster()
|
||||||
clusterName := NewClusterComplexName()
|
clusterName := NewClusterComplexName()
|
||||||
clusterName.WithType(theCluster.Name, theCluster.ClusterType)
|
clusterName.WithType(theCluster.Name, theCluster.ClusterType)
|
||||||
nCluster.NameInKubeconf = clusterName.Name()
|
nCluster.NameInKubeconf = clusterName.Name()
|
||||||
@ -541,7 +542,7 @@ func (c *Config) AddContext(theContext *ContextOptions) *Context {
|
|||||||
nContext := NewContext()
|
nContext := NewContext()
|
||||||
c.Contexts[theContext.Name] = nContext
|
c.Contexts[theContext.Name] = nContext
|
||||||
// Create a new Kubeconfig Context object as well
|
// Create a new Kubeconfig Context object as well
|
||||||
kContext := kubeconfig.NewContext()
|
kContext := clientcmdapi.NewContext()
|
||||||
nContext.NameInKubeconf = theContext.Name
|
nContext.NameInKubeconf = theContext.Name
|
||||||
contextName := NewClusterComplexName()
|
contextName := NewClusterComplexName()
|
||||||
contextName.WithType(theContext.Name, theContext.ClusterType)
|
contextName.WithType(theContext.Name, theContext.ClusterType)
|
||||||
@ -645,7 +646,7 @@ func (c *Config) AddAuthInfo(theAuthInfo *AuthInfoOptions) *AuthInfo {
|
|||||||
nAuthInfo := NewAuthInfo()
|
nAuthInfo := NewAuthInfo()
|
||||||
c.AuthInfos[theAuthInfo.Name] = nAuthInfo
|
c.AuthInfos[theAuthInfo.Name] = nAuthInfo
|
||||||
// Create a new Kubeconfig AuthInfo object as well
|
// Create a new Kubeconfig AuthInfo object as well
|
||||||
kAuthInfo := kubeconfig.NewAuthInfo()
|
kAuthInfo := clientcmdapi.NewAuthInfo()
|
||||||
nAuthInfo.SetKubeAuthInfo(kAuthInfo)
|
nAuthInfo.SetKubeAuthInfo(kAuthInfo)
|
||||||
c.KubeConfig().AuthInfos[theAuthInfo.Name] = kAuthInfo
|
c.KubeConfig().AuthInfos[theAuthInfo.Name] = kAuthInfo
|
||||||
|
|
||||||
@ -739,10 +740,10 @@ func (c *Cluster) PrettyString() string {
|
|||||||
clusterName.ClusterName(), clusterName.ClusterType(), c)
|
clusterName.ClusterName(), clusterName.ClusterType(), c)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Cluster) KubeCluster() *kubeconfig.Cluster {
|
func (c *Cluster) KubeCluster() *clientcmdapi.Cluster {
|
||||||
return c.kCluster
|
return c.kCluster
|
||||||
}
|
}
|
||||||
func (c *Cluster) SetKubeCluster(kc *kubeconfig.Cluster) {
|
func (c *Cluster) SetKubeCluster(kc *clientcmdapi.Cluster) {
|
||||||
c.kCluster = kc
|
c.kCluster = kc
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -777,11 +778,11 @@ func (c *Context) PrettyString() string {
|
|||||||
clusterName.ClusterName(), c.String())
|
clusterName.ClusterName(), c.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Context) KubeContext() *kubeconfig.Context {
|
func (c *Context) KubeContext() *clientcmdapi.Context {
|
||||||
return c.kContext
|
return c.kContext
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Context) SetKubeContext(kc *kubeconfig.Context) {
|
func (c *Context) SetKubeContext(kc *clientcmdapi.Context) {
|
||||||
c.kContext = kc
|
c.kContext = kc
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -808,10 +809,10 @@ func (c *AuthInfo) String() string {
|
|||||||
return string(kyaml)
|
return string(kyaml)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *AuthInfo) KubeAuthInfo() *kubeconfig.AuthInfo {
|
func (c *AuthInfo) KubeAuthInfo() *clientcmdapi.AuthInfo {
|
||||||
return c.kAuthInfo
|
return c.kAuthInfo
|
||||||
}
|
}
|
||||||
func (c *AuthInfo) SetKubeAuthInfo(kc *kubeconfig.AuthInfo) {
|
func (c *AuthInfo) SetKubeAuthInfo(kc *clientcmdapi.AuthInfo) {
|
||||||
c.kAuthInfo = kc
|
c.kAuthInfo = kc
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -979,7 +980,7 @@ PLACEHOLDER UNTIL I IDENTIFY if CLIENTADM
|
|||||||
HAS SOMETHING LIKE THIS
|
HAS SOMETHING LIKE THIS
|
||||||
*/
|
*/
|
||||||
|
|
||||||
func KClusterString(kCluster *kubeconfig.Cluster) string {
|
func KClusterString(kCluster *clientcmdapi.Cluster) string {
|
||||||
yamlData, err := yaml.Marshal(&kCluster)
|
yamlData, err := yaml.Marshal(&kCluster)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ""
|
return ""
|
||||||
@ -987,7 +988,8 @@ func KClusterString(kCluster *kubeconfig.Cluster) string {
|
|||||||
|
|
||||||
return string(yamlData)
|
return string(yamlData)
|
||||||
}
|
}
|
||||||
func KContextString(kContext *kubeconfig.Context) string {
|
|
||||||
|
func KContextString(kContext *clientcmdapi.Context) string {
|
||||||
yamlData, err := yaml.Marshal(&kContext)
|
yamlData, err := yaml.Marshal(&kContext)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ""
|
return ""
|
||||||
@ -995,7 +997,8 @@ func KContextString(kContext *kubeconfig.Context) string {
|
|||||||
|
|
||||||
return string(yamlData)
|
return string(yamlData)
|
||||||
}
|
}
|
||||||
func KAuthInfoString(kAuthInfo *kubeconfig.AuthInfo) string {
|
|
||||||
|
func KAuthInfoString(kAuthInfo *clientcmdapi.AuthInfo) string {
|
||||||
yamlData, err := yaml.Marshal(&kAuthInfo)
|
yamlData, err := yaml.Marshal(&kAuthInfo)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ""
|
return ""
|
||||||
|
@ -26,7 +26,6 @@ import (
|
|||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
"k8s.io/client-go/tools/clientcmd"
|
|
||||||
kubeconfig "k8s.io/client-go/tools/clientcmd/api"
|
kubeconfig "k8s.io/client-go/tools/clientcmd/api"
|
||||||
|
|
||||||
"opendev.org/airship/airshipctl/testutil"
|
"opendev.org/airship/airshipctl/testutil"
|
||||||
@ -224,10 +223,11 @@ func TestPersistConfig(t *testing.T) {
|
|||||||
config := InitConfig(t)
|
config := InitConfig(t)
|
||||||
|
|
||||||
err := config.PersistConfig()
|
err := config.PersistConfig()
|
||||||
assert.NoErrorf(t, err, "Unable to persist configuration expected at %v", config.LoadedConfigPath())
|
require.NoError(t, err)
|
||||||
|
|
||||||
kpo := config.LoadedPathOptions()
|
// Check that the files were created
|
||||||
assert.NotNil(t, kpo)
|
assert.FileExists(t, config.LoadedConfigPath())
|
||||||
|
assert.FileExists(t, config.KubeConfigPath())
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestEnsureComplete(t *testing.T) {
|
func TestEnsureComplete(t *testing.T) {
|
||||||
@ -347,11 +347,10 @@ func TestPurge(t *testing.T) {
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
airConfigFile := filepath.Join(tempDir, AirshipConfig)
|
airConfigFile := filepath.Join(tempDir, AirshipConfig)
|
||||||
kConfigFile := filepath.Join(tempDir, AirshipKubeConfig)
|
|
||||||
config.SetLoadedConfigPath(airConfigFile)
|
config.SetLoadedConfigPath(airConfigFile)
|
||||||
kubePathOptions := clientcmd.NewDefaultPathOptions()
|
|
||||||
kubePathOptions.GlobalFile = kConfigFile
|
kConfigFile := filepath.Join(tempDir, AirshipKubeConfig)
|
||||||
config.SetLoadedPathOptions(kubePathOptions)
|
config.kubeConfigPath = kConfigFile
|
||||||
|
|
||||||
// Store it
|
// Store it
|
||||||
err = config.PersistConfig()
|
err = config.PersistConfig()
|
||||||
|
@ -11,19 +11,22 @@ const (
|
|||||||
AirshipClusterDefaultType = Target
|
AirshipClusterDefaultType = Target
|
||||||
)
|
)
|
||||||
|
|
||||||
//Sorted
|
// Sorted
|
||||||
var AllClusterTypes = [2]string{Ephemeral, Target}
|
var AllClusterTypes = [2]string{Ephemeral, Target}
|
||||||
|
|
||||||
// Constants defining default values
|
// Constants defining default values
|
||||||
const (
|
const (
|
||||||
AirshipConfigEnv = "airshipconf"
|
|
||||||
AirshipConfig = "config"
|
|
||||||
AirshipConfigDir = ".airship"
|
|
||||||
AirshipConfigKind = "Config"
|
|
||||||
AirshipConfigVersion = "v1alpha1"
|
|
||||||
AirshipConfigGroup = "airshipit.org"
|
AirshipConfigGroup = "airshipit.org"
|
||||||
|
AirshipConfigVersion = "v1alpha1"
|
||||||
AirshipConfigApiVersion = AirshipConfigGroup + "/" + AirshipConfigVersion
|
AirshipConfigApiVersion = AirshipConfigGroup + "/" + AirshipConfigVersion
|
||||||
AirshipKubeConfig = "kubeconfig"
|
AirshipConfigKind = "Config"
|
||||||
|
|
||||||
|
AirshipConfigDir = ".airship"
|
||||||
|
AirshipConfig = "config"
|
||||||
|
AirshipKubeConfig = "kubeconfig"
|
||||||
|
|
||||||
|
AirshipConfigEnv = "AIRSHIPCONFIG"
|
||||||
|
AirshipKubeConfigEnv = "AIRSHIP_KUBECONFIG"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Constants defining CLI flags
|
// Constants defining CLI flags
|
||||||
@ -37,7 +40,7 @@ const (
|
|||||||
FlagClusterType = "cluster-type"
|
FlagClusterType = "cluster-type"
|
||||||
FlagContext = "context"
|
FlagContext = "context"
|
||||||
FlagCurrentContext = "current-context"
|
FlagCurrentContext = "current-context"
|
||||||
FlagConfigFilePath = AirshipConfigEnv
|
FlagConfigFilePath = "airshipconf"
|
||||||
FlagEmbedCerts = "embed-certs"
|
FlagEmbedCerts = "embed-certs"
|
||||||
FlagImpersonate = "as"
|
FlagImpersonate = "as"
|
||||||
FlagImpersonateGroup = "as-group"
|
FlagImpersonateGroup = "as-group"
|
||||||
|
@ -22,7 +22,6 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"k8s.io/client-go/tools/clientcmd"
|
|
||||||
kubeconfig "k8s.io/client-go/tools/clientcmd/api"
|
kubeconfig "k8s.io/client-go/tools/clientcmd/api"
|
||||||
|
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
@ -145,9 +144,7 @@ func InitConfig(t *testing.T) *Config {
|
|||||||
|
|
||||||
conf := NewConfig()
|
conf := NewConfig()
|
||||||
|
|
||||||
kubePathOptions := clientcmd.NewDefaultPathOptions()
|
err = conf.LoadConfig(configPath, kubeConfigPath)
|
||||||
kubePathOptions.GlobalFile = kubeConfigPath
|
|
||||||
err = conf.LoadConfig(configPath, kubePathOptions)
|
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
return conf
|
return conf
|
||||||
|
@ -19,7 +19,6 @@ package config
|
|||||||
import (
|
import (
|
||||||
"net/url"
|
"net/url"
|
||||||
|
|
||||||
"k8s.io/client-go/tools/clientcmd"
|
|
||||||
kubeconfig "k8s.io/client-go/tools/clientcmd/api"
|
kubeconfig "k8s.io/client-go/tools/clientcmd/api"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -57,15 +56,15 @@ type Config struct {
|
|||||||
// Such as Bootstrap, Workflows, Document, etc
|
// Such as Bootstrap, Workflows, Document, etc
|
||||||
ModulesConfig *Modules `json:"modules-config"`
|
ModulesConfig *Modules `json:"modules-config"`
|
||||||
|
|
||||||
// Private LoadedConfigPath is the full path to the the location of the config file
|
// loadedConfigPath is the full path to the the location of the config
|
||||||
// from which these config was loaded
|
// file from which this config was loaded
|
||||||
// +not persisted in file
|
// +not persisted in file
|
||||||
loadedConfigPath string
|
loadedConfigPath string
|
||||||
|
|
||||||
// Private loadedPathOptions is the full path to the the location of the kubeconfig file
|
// kubeConfigPath is the full path to the the location of the
|
||||||
// associated with this airship config instance
|
// kubeconfig file associated with this airship config instance
|
||||||
// +not persisted in file
|
// +not persisted in file
|
||||||
loadedPathOptions *clientcmd.PathOptions
|
kubeConfigPath string
|
||||||
|
|
||||||
// Private instance of Kube Config content as an object
|
// Private instance of Kube Config content as an object
|
||||||
kubeConfig *kubeconfig.Config
|
kubeConfig *kubeconfig.Config
|
||||||
|
@ -3,7 +3,6 @@ package environment
|
|||||||
import (
|
import (
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
@ -64,67 +63,64 @@ func (a *AirshipCTLSettings) SetKubeConfigPath(kcp string) {
|
|||||||
func (a *AirshipCTLSettings) InitConfig() {
|
func (a *AirshipCTLSettings) InitConfig() {
|
||||||
a.SetConfig(config.NewConfig())
|
a.SetConfig(config.NewConfig())
|
||||||
|
|
||||||
a.setAirshipConfigPath()
|
a.initAirshipConfigPath()
|
||||||
|
a.initKubeConfigPath()
|
||||||
|
|
||||||
//Pass the airshipConfigPath and kubeConfig object
|
err := a.Config().LoadConfig(a.AirshipConfigPath(), a.KubeConfigPath())
|
||||||
err := a.Config().LoadConfig(a.AirshipConfigPath(), a.setKubePathOptions())
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// Should stop airshipctl
|
// Should stop airshipctl
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *AirshipCTLSettings) setAirshipConfigPath() {
|
func (a *AirshipCTLSettings) initAirshipConfigPath() {
|
||||||
// (1) If the airshipConfigPath was received as an argument its already set
|
// The airshipConfigPath may already have been received as a command line argument
|
||||||
if a.airshipConfigPath == "" {
|
if a.airshipConfigPath != "" {
|
||||||
// (2) If not , we can check if we got the Path via ENVIRONMENT variable,
|
return
|
||||||
// set the appropriate fields
|
|
||||||
a.setAirshipConfigPathFromEnv()
|
|
||||||
}
|
}
|
||||||
// (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
|
// Otherwise, we can check if we got the path via ENVIRONMENT variable
|
||||||
func (a *AirshipCTLSettings) setAirshipConfigPathFromEnv() {
|
|
||||||
// Check if AIRSHIPCONF env variable was set
|
|
||||||
// I have the path and name for the airship config file
|
|
||||||
a.airshipConfigPath = os.Getenv(config.AirshipConfigEnv)
|
a.airshipConfigPath = os.Getenv(config.AirshipConfigEnv)
|
||||||
}
|
if a.airshipConfigPath != "" {
|
||||||
|
return
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
// 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) {
|
func (a *AirshipCTLSettings) initKubeConfigPath() {
|
||||||
home, err := os.UserHomeDir()
|
// 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 {
|
if err != nil {
|
||||||
// Use defaults under current directory
|
homeDir = ""
|
||||||
home = ""
|
|
||||||
}
|
}
|
||||||
if configPath == "" {
|
return homeDir
|
||||||
return configPath, home
|
|
||||||
}
|
|
||||||
|
|
||||||
return strings.Replace(configPath, HomePlaceholder, home, 1), home
|
|
||||||
}
|
}
|
||||||
|
@ -17,6 +17,7 @@ limitations under the License.
|
|||||||
package environment
|
package environment
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"testing"
|
"testing"
|
||||||
@ -36,25 +37,107 @@ func TestInitFlags(t *testing.T) {
|
|||||||
assert.True(t, testCmd.HasPersistentFlags())
|
assert.True(t, testCmd.HasPersistentFlags())
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSpecifyAirConfigFromEnv(t *testing.T) {
|
func TestInitConfig(t *testing.T) {
|
||||||
fakeConfig := "FakeConfigPath"
|
t.Run("DefaultToHomeDirectory", func(subTest *testing.T) {
|
||||||
err := os.Setenv(config.AirshipConfigEnv, fakeConfig)
|
// 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)
|
require.NoError(t, err)
|
||||||
|
return testDir
|
||||||
settings := &AirshipCTLSettings{}
|
|
||||||
settings.InitConfig()
|
|
||||||
|
|
||||||
assert.EqualValues(t, fakeConfig, settings.AirshipConfigPath())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGetSetPaths(t *testing.T) {
|
func deleteTestDir(t *testing.T, path string) {
|
||||||
settings := &AirshipCTLSettings{}
|
err := os.Remove(path)
|
||||||
settings.InitConfig()
|
require.NoError(t, err)
|
||||||
airConfigFile := filepath.Join(config.AirshipConfigDir, config.AirshipConfig)
|
}
|
||||||
kConfigFile := filepath.Join(config.AirshipConfigDir, config.AirshipKubeConfig)
|
|
||||||
settings.SetAirshipConfigPath(airConfigFile)
|
// setHome sets the HOME environment variable to `path`, and returns a function
|
||||||
assert.EqualValues(t, airConfigFile, settings.AirshipConfigPath())
|
// that can be used to reset HOME to its original value
|
||||||
|
func setHome(path string) (resetHome func()) {
|
||||||
settings.SetKubeConfigPath(kConfigFile)
|
oldHome := os.Getenv("HOME")
|
||||||
assert.EqualValues(t, kConfigFile, settings.KubeConfigPath())
|
os.Setenv("HOME", path)
|
||||||
|
return func() {
|
||||||
|
os.Setenv("HOME", oldHome)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user