mirror of https://github.com/k3s-io/k3s
resolve relative paths in kubeconfig
parent
ac2f435aca
commit
aba73493de
|
@ -19,6 +19,7 @@ package clientcmd
|
||||||
import (
|
import (
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
"github.com/ghodss/yaml"
|
"github.com/ghodss/yaml"
|
||||||
"github.com/imdario/mergo"
|
"github.com/imdario/mergo"
|
||||||
|
@ -59,13 +60,22 @@ func NewClientConfigLoadingRules() *ClientConfigLoadingRules {
|
||||||
// This means that the first file to set CurrentContext will have its context preserved. It also means
|
// This means that the first file to set CurrentContext will have its context preserved. It also means
|
||||||
// that if two files specify a "red-user", only values from the first file's red-user are used. Even
|
// that if two files specify a "red-user", only values from the first file's red-user are used. Even
|
||||||
// non-conflicting entries from the second file's "red-user" are discarded.
|
// non-conflicting entries from the second file's "red-user" are discarded.
|
||||||
|
// Relative paths inside of the .kubeconfig files are resolved against the .kubeconfig file's parent folder
|
||||||
|
// and only absolute file paths are returned.
|
||||||
func (rules *ClientConfigLoadingRules) Load() (*clientcmdapi.Config, error) {
|
func (rules *ClientConfigLoadingRules) Load() (*clientcmdapi.Config, error) {
|
||||||
config := clientcmdapi.NewConfig()
|
config := clientcmdapi.NewConfig()
|
||||||
|
|
||||||
mergeConfigWithFile(config, rules.CommandLinePath)
|
mergeConfigWithFile(config, rules.CommandLinePath)
|
||||||
|
resolveLocalPaths(rules.CommandLinePath, config)
|
||||||
|
|
||||||
mergeConfigWithFile(config, rules.EnvVarPath)
|
mergeConfigWithFile(config, rules.EnvVarPath)
|
||||||
|
resolveLocalPaths(rules.EnvVarPath, config)
|
||||||
|
|
||||||
mergeConfigWithFile(config, rules.CurrentDirectoryPath)
|
mergeConfigWithFile(config, rules.CurrentDirectoryPath)
|
||||||
|
resolveLocalPaths(rules.CurrentDirectoryPath, config)
|
||||||
|
|
||||||
mergeConfigWithFile(config, rules.HomeDirectoryPath)
|
mergeConfigWithFile(config, rules.HomeDirectoryPath)
|
||||||
|
resolveLocalPaths(rules.HomeDirectoryPath, config)
|
||||||
|
|
||||||
return config, nil
|
return config, nil
|
||||||
}
|
}
|
||||||
|
@ -86,6 +96,50 @@ func mergeConfigWithFile(startingConfig *clientcmdapi.Config, filename string) e
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// resolveLocalPaths resolves all relative paths in the config object with respect to the parent directory of the filename
|
||||||
|
// this cannot be done directly inside of LoadFromFile because doing so there would make it impossible to load a file without
|
||||||
|
// modification of its contents.
|
||||||
|
func resolveLocalPaths(filename string, config *clientcmdapi.Config) error {
|
||||||
|
if len(filename) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
configDir, err := filepath.Abs(filepath.Dir(filename))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
resolvedClusters := make(map[string]clientcmdapi.Cluster)
|
||||||
|
for key, cluster := range config.Clusters {
|
||||||
|
cluster.CertificateAuthority = resolveLocalPath(configDir, cluster.CertificateAuthority)
|
||||||
|
resolvedClusters[key] = cluster
|
||||||
|
}
|
||||||
|
config.Clusters = resolvedClusters
|
||||||
|
|
||||||
|
resolvedAuthInfos := make(map[string]clientcmdapi.AuthInfo)
|
||||||
|
for key, authInfo := range config.AuthInfos {
|
||||||
|
authInfo.AuthPath = resolveLocalPath(configDir, authInfo.AuthPath)
|
||||||
|
authInfo.ClientCertificate = resolveLocalPath(configDir, authInfo.ClientCertificate)
|
||||||
|
authInfo.ClientKey = resolveLocalPath(configDir, authInfo.ClientKey)
|
||||||
|
resolvedAuthInfos[key] = authInfo
|
||||||
|
}
|
||||||
|
config.AuthInfos = resolvedAuthInfos
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// resolveLocalPath makes the path absolute with respect to the startingDir
|
||||||
|
func resolveLocalPath(startingDir, path string) string {
|
||||||
|
if len(path) == 0 {
|
||||||
|
return path
|
||||||
|
}
|
||||||
|
if filepath.IsAbs(path) {
|
||||||
|
return path
|
||||||
|
}
|
||||||
|
|
||||||
|
return filepath.Join(startingDir, path)
|
||||||
|
}
|
||||||
|
|
||||||
// LoadFromFile takes a filename and deserializes the contents into Config object
|
// LoadFromFile takes a filename and deserializes the contents into Config object
|
||||||
func LoadFromFile(filename string) (*clientcmdapi.Config, error) {
|
func LoadFromFile(filename string) (*clientcmdapi.Config, error) {
|
||||||
config := &clientcmdapi.Config{}
|
config := &clientcmdapi.Config{}
|
||||||
|
|
|
@ -20,6 +20,9 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
|
"path"
|
||||||
|
"path/filepath"
|
||||||
|
"testing"
|
||||||
|
|
||||||
"github.com/ghodss/yaml"
|
"github.com/ghodss/yaml"
|
||||||
|
|
||||||
|
@ -71,6 +74,107 @@ var (
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func TestResolveRelativePaths(t *testing.T) {
|
||||||
|
pathResolutionConfig1 := clientcmdapi.Config{
|
||||||
|
AuthInfos: map[string]clientcmdapi.AuthInfo{
|
||||||
|
"relative-user-1": {ClientCertificate: "relative/client/cert", ClientKey: "../relative/client/key", AuthPath: "../../relative/auth/path"},
|
||||||
|
"absolute-user-1": {ClientCertificate: "/absolute/client/cert", ClientKey: "/absolute/client/key", AuthPath: "/absolute/auth/path"},
|
||||||
|
},
|
||||||
|
Clusters: map[string]clientcmdapi.Cluster{
|
||||||
|
"relative-server-1": {CertificateAuthority: "../relative/ca"},
|
||||||
|
"absolute-server-1": {CertificateAuthority: "/absolute/ca"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
pathResolutionConfig2 := clientcmdapi.Config{
|
||||||
|
AuthInfos: map[string]clientcmdapi.AuthInfo{
|
||||||
|
"relative-user-2": {ClientCertificate: "relative/client/cert2", ClientKey: "../relative/client/key2", AuthPath: "../../relative/auth/path2"},
|
||||||
|
"absolute-user-2": {ClientCertificate: "/absolute/client/cert2", ClientKey: "/absolute/client/key2", AuthPath: "/absolute/auth/path2"},
|
||||||
|
},
|
||||||
|
Clusters: map[string]clientcmdapi.Cluster{
|
||||||
|
"relative-server-2": {CertificateAuthority: "../relative/ca2"},
|
||||||
|
"absolute-server-2": {CertificateAuthority: "/absolute/ca2"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
configDir1, _ := ioutil.TempDir("", "")
|
||||||
|
configFile1 := path.Join(configDir1, ".kubeconfig")
|
||||||
|
configDir1, _ = filepath.Abs(configDir1)
|
||||||
|
defer os.Remove(configFile1)
|
||||||
|
configDir2, _ := ioutil.TempDir("", "")
|
||||||
|
configDir2, _ = ioutil.TempDir(configDir2, "")
|
||||||
|
configFile2 := path.Join(configDir2, ".kubeconfig")
|
||||||
|
configDir2, _ = filepath.Abs(configDir2)
|
||||||
|
defer os.Remove(configFile2)
|
||||||
|
|
||||||
|
WriteToFile(pathResolutionConfig1, configFile1)
|
||||||
|
WriteToFile(pathResolutionConfig2, configFile2)
|
||||||
|
|
||||||
|
loadingRules := ClientConfigLoadingRules{
|
||||||
|
CommandLinePath: configFile1,
|
||||||
|
EnvVarPath: configFile2,
|
||||||
|
}
|
||||||
|
|
||||||
|
mergedConfig, err := loadingRules.Load()
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
foundClusterCount := 0
|
||||||
|
for key, cluster := range mergedConfig.Clusters {
|
||||||
|
if key == "relative-server-1" {
|
||||||
|
foundClusterCount++
|
||||||
|
matchStringArg(path.Join(configDir1, pathResolutionConfig1.Clusters["relative-server-1"].CertificateAuthority), cluster.CertificateAuthority, t)
|
||||||
|
}
|
||||||
|
if key == "relative-server-2" {
|
||||||
|
foundClusterCount++
|
||||||
|
matchStringArg(path.Join(configDir2, pathResolutionConfig2.Clusters["relative-server-2"].CertificateAuthority), cluster.CertificateAuthority, t)
|
||||||
|
}
|
||||||
|
if key == "absolute-server-1" {
|
||||||
|
foundClusterCount++
|
||||||
|
matchStringArg(pathResolutionConfig1.Clusters["absolute-server-1"].CertificateAuthority, cluster.CertificateAuthority, t)
|
||||||
|
}
|
||||||
|
if key == "absolute-server-2" {
|
||||||
|
foundClusterCount++
|
||||||
|
matchStringArg(pathResolutionConfig2.Clusters["absolute-server-2"].CertificateAuthority, cluster.CertificateAuthority, t)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if foundClusterCount != 4 {
|
||||||
|
t.Errorf("Expected 4 clusters, found %v: %v", foundClusterCount, mergedConfig.Clusters)
|
||||||
|
}
|
||||||
|
|
||||||
|
foundAuthInfoCount := 0
|
||||||
|
for key, authInfo := range mergedConfig.AuthInfos {
|
||||||
|
if key == "relative-user-1" {
|
||||||
|
foundAuthInfoCount++
|
||||||
|
matchStringArg(path.Join(configDir1, pathResolutionConfig1.AuthInfos["relative-user-1"].ClientCertificate), authInfo.ClientCertificate, t)
|
||||||
|
matchStringArg(path.Join(configDir1, pathResolutionConfig1.AuthInfos["relative-user-1"].ClientKey), authInfo.ClientKey, t)
|
||||||
|
matchStringArg(path.Join(configDir1, pathResolutionConfig1.AuthInfos["relative-user-1"].AuthPath), authInfo.AuthPath, t)
|
||||||
|
}
|
||||||
|
if key == "relative-user-2" {
|
||||||
|
foundAuthInfoCount++
|
||||||
|
matchStringArg(path.Join(configDir2, pathResolutionConfig2.AuthInfos["relative-user-2"].ClientCertificate), authInfo.ClientCertificate, t)
|
||||||
|
matchStringArg(path.Join(configDir2, pathResolutionConfig2.AuthInfos["relative-user-2"].ClientKey), authInfo.ClientKey, t)
|
||||||
|
matchStringArg(path.Join(configDir2, pathResolutionConfig2.AuthInfos["relative-user-2"].AuthPath), authInfo.AuthPath, t)
|
||||||
|
}
|
||||||
|
if key == "absolute-user-1" {
|
||||||
|
foundAuthInfoCount++
|
||||||
|
matchStringArg(pathResolutionConfig1.AuthInfos["absolute-user-1"].ClientCertificate, authInfo.ClientCertificate, t)
|
||||||
|
matchStringArg(pathResolutionConfig1.AuthInfos["absolute-user-1"].ClientKey, authInfo.ClientKey, t)
|
||||||
|
matchStringArg(pathResolutionConfig1.AuthInfos["absolute-user-1"].AuthPath, authInfo.AuthPath, t)
|
||||||
|
}
|
||||||
|
if key == "absolute-user-2" {
|
||||||
|
foundAuthInfoCount++
|
||||||
|
matchStringArg(pathResolutionConfig2.AuthInfos["absolute-user-2"].ClientCertificate, authInfo.ClientCertificate, t)
|
||||||
|
matchStringArg(pathResolutionConfig2.AuthInfos["absolute-user-2"].ClientKey, authInfo.ClientKey, t)
|
||||||
|
matchStringArg(pathResolutionConfig2.AuthInfos["absolute-user-2"].AuthPath, authInfo.AuthPath, t)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if foundAuthInfoCount != 4 {
|
||||||
|
t.Errorf("Expected 4 users, found %v: %v", foundAuthInfoCount, mergedConfig.AuthInfos)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
func ExampleMergingSomeWithConflict() {
|
func ExampleMergingSomeWithConflict() {
|
||||||
commandLineFile, _ := ioutil.TempFile("", "")
|
commandLineFile, _ := ioutil.TempFile("", "")
|
||||||
defer os.Remove(commandLineFile.Name())
|
defer os.Remove(commandLineFile.Name())
|
||||||
|
|
Loading…
Reference in New Issue