Merge "Refactor management of config file paths"
This commit is contained in:
@@ -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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user