Extend kubeconfig builder

Change-Id: I6724112826a4cc59815b7524bd05af01a9a1d01b
This commit is contained in:
Kostiantyn Kalynovskyi 2021-02-01 22:50:36 +00:00
parent 77268cd1d0
commit d9f54b4171
9 changed files with 502 additions and 111 deletions

View File

@ -116,7 +116,7 @@ func (cm clusterMap) ClusterKubeconfigContext(clusterName string) (string, error
kubeContext := cluster.KubeconfigContext
// if kubeContext is still empty, set it to clusterName
if kubeContext == "" {
if cluster.KubeconfigContext == "" {
kubeContext = clusterName
}

View File

@ -15,12 +15,18 @@
package kubeconfig
import (
"bytes"
"path/filepath"
"k8s.io/client-go/tools/clientcmd"
"k8s.io/client-go/tools/clientcmd/api"
"opendev.org/airship/airshipctl/pkg/api/v1alpha1"
"opendev.org/airship/airshipctl/pkg/cluster/clustermap"
"opendev.org/airship/airshipctl/pkg/clusterctl/client"
"opendev.org/airship/airshipctl/pkg/config"
"opendev.org/airship/airshipctl/pkg/errors"
"opendev.org/airship/airshipctl/pkg/fs"
"opendev.org/airship/airshipctl/pkg/log"
"opendev.org/airship/airshipctl/pkg/util"
)
@ -40,7 +46,9 @@ type Builder struct {
clusterName string
root string
clusterMap clustermap.ClusterMap
clusterMap clustermap.ClusterMap
clusterctlClient client.Interface
fs fs.FileSystem
}
// WithPath allows to set path to prexisting kubeconfig
@ -73,25 +81,35 @@ func (b *Builder) WithTempRoot(root string) *Builder {
return b
}
// WithClusterctClient this is used if u want to inject your own clusterctl
// mostly needed for tests
func (b *Builder) WithClusterctClient(c client.Interface) *Builder {
b.clusterctlClient = c
return b
}
// WithFilesytem allows to set filesystem
func (b *Builder) WithFilesytem(fs fs.FileSystem) *Builder {
b.fs = fs
return b
}
// Build builds a kubeconfig interface to be used
func (b *Builder) Build() Interface {
switch {
case b.path != "":
fSys := fs.NewDocumentFs()
return NewKubeConfig(FromFile(b.path, fSys), InjectFilePath(b.path, fSys), InjectTempRoot(b.root))
return NewKubeConfig(FromFile(b.path, b.fs), InjectFilePath(b.path, b.fs), InjectTempRoot(b.root))
case b.fromParent():
// TODO add method that would get kubeconfig from parent cluster and glue it together
// with parent kubeconfig if needed
return NewKubeConfig(func() ([]byte, error) {
return nil, errors.ErrNotImplemented{}
})
// TODO consider adding various drivers to source kubeconfig from
// Also consider accumulating different kubeconfigs, and returning one single
// large file, so that every executor has access to all parent clusters.
return NewKubeConfig(b.buildClusterctlFromParent, InjectTempRoot(b.root), InjectFileSystem(b.fs))
case b.bundlePath != "":
return NewKubeConfig(FromBundle(b.bundlePath), InjectTempRoot(b.root))
return NewKubeConfig(FromBundle(b.bundlePath), InjectTempRoot(b.root), InjectFileSystem(b.fs))
default:
fSys := fs.NewDocumentFs()
// return default path to kubeconfig file in airship workdir
path := filepath.Join(util.UserHomeDir(), config.AirshipConfigDir, KubeconfigDefaultFileName)
return NewKubeConfig(FromFile(path, fSys), InjectFilePath(path, fSys), InjectTempRoot(b.root))
return NewKubeConfig(FromFile(path, b.fs), InjectFilePath(path, b.fs), InjectTempRoot(b.root))
}
}
@ -102,3 +120,103 @@ func (b *Builder) fromParent() bool {
}
return b.clusterMap.DynamicKubeConfig(b.clusterName)
}
func (b *Builder) buildClusterctlFromParent() ([]byte, error) {
currentCluster := b.clusterName
log.Printf("current cluster name is '%s'",
currentCluster)
parentCluster, err := b.clusterMap.ParentCluster(currentCluster)
if err != nil {
return nil, err
}
parentKubeconfig := b.WithClusterName(parentCluster).Build()
f, cleanup, err := parentKubeconfig.GetFile()
if err != nil {
return nil, err
}
defer cleanup()
parentCtx, err := b.clusterMap.ClusterKubeconfigContext(parentCluster)
if err != nil {
return nil, err
}
clusterAPIRef, err := b.clusterMap.ClusterAPIRef(currentCluster)
if err != nil {
return nil, err
}
if b.clusterctlClient == nil {
b.clusterctlClient, err = client.NewClient("", log.DebugEnabled(), v1alpha1.DefaultClusterctl())
if err != nil {
return nil, err
}
}
log.Printf("Getting child kubeconfig from parent, parent context '%s', parent kubeconfing '%s'",
parentCtx, f)
stringChild, err := b.clusterctlClient.GetKubeconfig(&client.GetKubeconfigOptions{
ParentKubeconfigPath: f,
ParentKubeconfigContext: parentCtx,
ManagedClusterNamespace: clusterAPIRef.Namespace,
ManagedClusterName: clusterAPIRef.Name,
})
if err != nil {
return nil, err
}
buf := bytes.NewBuffer([]byte{})
err = parentKubeconfig.Write(buf)
if err != nil {
return nil, err
}
parentObj, err := clientcmd.Load(buf.Bytes())
if err != nil {
return nil, err
}
childObj, err := clientcmd.Load([]byte(stringChild))
if err != nil {
return nil, err
}
childCtx, err := b.clusterMap.ClusterKubeconfigContext(currentCluster)
if err != nil {
return nil, err
}
log.Printf("Merging '%s' cluster kubeconfig into '%s' cluster kubeconfig",
currentCluster, parentCluster)
return b.mergeOneContext(childCtx, parentObj, childObj)
}
// merges two kubeconfigs,
func (b *Builder) mergeOneContext(contextOverride string, dst, src *api.Config) ([]byte, error) {
for key, content := range src.AuthInfos {
dst.AuthInfos[key] = content
}
for key, content := range src.Clusters {
dst.Clusters[key] = content
}
if len(src.Contexts) != 1 {
return nil, &ErrClusterctlKubeconfigWrongContextsCount{
ContextCount: len(src.Contexts),
}
}
for key, content := range src.Contexts {
if contextOverride == "" {
contextOverride = key
}
dst.Contexts[contextOverride] = content
}
return clientcmd.Write(*dst)
}

View File

@ -16,17 +16,65 @@ package kubeconfig_test
import (
"bytes"
"fmt"
"io/ioutil"
"path/filepath"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require"
"opendev.org/airship/airshipctl/pkg/api/v1alpha1"
"opendev.org/airship/airshipctl/pkg/cluster/clustermap"
"opendev.org/airship/airshipctl/pkg/clusterctl/client"
"opendev.org/airship/airshipctl/pkg/config"
"opendev.org/airship/airshipctl/pkg/fs"
"opendev.org/airship/airshipctl/pkg/k8s/kubeconfig"
"opendev.org/airship/airshipctl/pkg/util"
"opendev.org/airship/airshipctl/testutil/clusterctl"
testfs "opendev.org/airship/airshipctl/testutil/fs"
)
const (
testKubeconfigString = `apiVersion: v1
kind: Config
clusters:
- cluster:
certificate-authority-data: c29tZWNlcnQK
server: https://10.23.25.101:6443
name: child_cluster
contexts:
- context:
cluster: child_cluster
user: child_user
name: child
current-context: dummy_cluster
preferences: {}
users:
- name: child_user
user:
client-certificate-data: c29tZWNlcnQK
client-key-data: c29tZWNlcnQK`
testKubeconfigStringSecond = `apiVersion: v1
kind: Config
clusters:
- cluster:
certificate-authority-data: c29tZWNlcnQK
server: https://10.23.25.101:6443
name: parent_cluster
contexts:
- context:
cluster: parent_cluster
user: parent_admin
name: parent-context
current-context: dummy_cluster
preferences: {}
users:
- name: parent_admin
user:
client-certificate-data: c29tZWNlcnQK
client-key-data: c29tZWNlcnQK`
)
func TestBuilder(t *testing.T) {
@ -38,7 +86,7 @@ func TestBuilder(t *testing.T) {
err := kube.Write(buf)
require.NoError(t, err)
// check that kubeconfig contains expected cluster string
assert.Contains(t, buf.String(), "dummycluster_ephemeral")
assert.Contains(t, buf.String(), "parent_parent_context")
})
t.Run("Only filepath", func(t *testing.T) {
@ -52,33 +100,6 @@ func TestBuilder(t *testing.T) {
assert.Contains(t, buf.String(), "dummycluster_ephemeral")
})
t.Run("Only cluster map", func(t *testing.T) {
childCluster := "child"
parentCluster := "parent"
clusterMap := &v1alpha1.ClusterMap{
Map: map[string]*v1alpha1.Cluster{
childCluster: {
Parent: parentCluster,
DynamicKubeConfig: true,
},
parentCluster: {
DynamicKubeConfig: false,
},
},
}
builder := kubeconfig.NewBuilder().
WithClusterMap(clustermap.NewClusterMap(clusterMap)).
WithClusterName(childCluster)
kube := builder.Build()
// This should not be implemented yet, and we need to check that we are getting there
require.NotNil(t, kube)
filePath, cleanup, err := kube.GetFile()
require.Error(t, err)
require.Contains(t, err.Error(), "not implemented")
assert.Equal(t, "", filePath)
require.Nil(t, cleanup)
})
t.Run("No current cluster, fall to default", func(t *testing.T) {
clusterMap := &v1alpha1.ClusterMap{}
builder := kubeconfig.NewBuilder().
@ -93,27 +114,6 @@ func TestBuilder(t *testing.T) {
assert.Equal(t, path, actualPath)
})
t.Run("Dynamic, but no parent", func(t *testing.T) {
childCluster := "child"
clusterMap := &v1alpha1.ClusterMap{
Map: map[string]*v1alpha1.Cluster{
childCluster: {
DynamicKubeConfig: true,
},
},
}
builder := kubeconfig.NewBuilder().
WithClusterMap(clustermap.NewClusterMap(clusterMap)).
WithClusterName(childCluster)
kube := builder.Build()
// We should get a default value for cluster, as we can't find parent cluster
filePath, cleanup, err := kube.GetFile()
require.Error(t, err)
require.Contains(t, err.Error(), "not implemented")
assert.Equal(t, "", filePath)
require.Nil(t, cleanup)
})
t.Run("Default source", func(t *testing.T) {
builder := kubeconfig.NewBuilder()
kube := builder.Build()
@ -126,3 +126,158 @@ func TestBuilder(t *testing.T) {
assert.Equal(t, path, actualPath)
})
}
func TestBuilderClusterctl(t *testing.T) {
childCluster := "child"
parentCluster := "parent"
testBundlePath := "testdata"
kubeconfigPath := filepath.Join(testBundlePath, "kubeconfig-12341234")
tests := []struct {
name string
errString string
requestedClusterName string
parentClusterName string
tempRoot string
clusterMap clustermap.ClusterMap
clusterctlClient client.Interface
fs fs.FileSystem
}{
{
name: "error no parent context",
errString: fmt.Sprintf("context \"%s\" does not exist", parentCluster),
parentClusterName: parentCluster,
requestedClusterName: childCluster,
clusterMap: clustermap.NewClusterMap(&v1alpha1.ClusterMap{
Map: map[string]*v1alpha1.Cluster{
childCluster: {
Parent: parentCluster,
DynamicKubeConfig: true,
},
parentCluster: {
DynamicKubeConfig: false,
},
},
}),
},
{
name: "error dynamic but no parrent",
parentClusterName: parentCluster,
requestedClusterName: childCluster,
errString: "failed to find a parent",
clusterMap: clustermap.NewClusterMap(&v1alpha1.ClusterMap{
Map: map[string]*v1alpha1.Cluster{
childCluster: {
DynamicKubeConfig: true,
},
},
}),
},
{
name: "error write temp parent",
parentClusterName: parentCluster,
requestedClusterName: childCluster,
tempRoot: "does not exist anywhere",
errString: "no such file or directory",
clusterMap: clustermap.NewClusterMap(&v1alpha1.ClusterMap{
Map: map[string]*v1alpha1.Cluster{
childCluster: {
Parent: parentCluster,
DynamicKubeConfig: true,
},
parentCluster: {
DynamicKubeConfig: false,
KubeconfigContext: "dummy_cluster",
},
},
}),
},
{
name: "success",
parentClusterName: parentCluster,
requestedClusterName: childCluster,
clusterMap: clustermap.NewClusterMap(&v1alpha1.ClusterMap{
Map: map[string]*v1alpha1.Cluster{
childCluster: {
Parent: parentCluster,
DynamicKubeConfig: true,
},
parentCluster: {
DynamicKubeConfig: true,
KubeconfigContext: "parent-custom",
Parent: "parent-parent",
},
"parent-parent": {
DynamicKubeConfig: false,
KubeconfigContext: "parent_parent_context",
},
},
}),
tempRoot: "testdata",
fs: testfs.MockFileSystem{
MockRemoveAll: func() error { return nil },
MockTempFile: func(s1, s2 string) (fs.File, error) {
return testfs.TestFile{
MockName: func() string { return kubeconfigPath },
MockWrite: func() (int, error) { return 0, nil },
MockClose: func() error { return nil },
}, nil
},
},
clusterctlClient: func() client.Interface {
c := &clusterctl.MockInterface{
Mock: mock.Mock{},
}
c.On("GetKubeconfig", &client.GetKubeconfigOptions{
ParentKubeconfigPath: kubeconfigPath,
ParentKubeconfigContext: "parent-custom",
ManagedClusterNamespace: clustermap.DefaultClusterAPIObjNamespace,
ManagedClusterName: childCluster,
}).Once().Return(testKubeconfigString, nil)
c.On("GetKubeconfig", &client.GetKubeconfigOptions{
ParentKubeconfigPath: kubeconfigPath,
ParentKubeconfigContext: "parent_parent_context",
ManagedClusterNamespace: clustermap.DefaultClusterAPIObjNamespace,
ManagedClusterName: parentCluster,
}).Once().Return(testKubeconfigStringSecond, nil)
return c
}(),
},
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
kube := kubeconfig.NewBuilder().
WithClusterMap(tt.clusterMap).
WithClusterName(tt.requestedClusterName).
WithBundle(testBundlePath).
WithTempRoot(tt.tempRoot).
WithClusterctClient(tt.clusterctlClient).
WithFilesytem(tt.fs).
Build()
require.NotNil(t, kube)
filePath, cleanup, err := kube.GetFile()
// This is needed to avoid leftovers on test failures
if cleanup != nil {
defer cleanup()
}
if tt.errString != "" {
require.Error(t, err)
assert.Contains(t, err.Error(), tt.errString)
assert.Equal(t, "", filePath)
} else {
assert.NoError(t, err)
assert.NotEqual(t, "", filePath)
assert.NotNil(t, cleanup)
buf := bytes.NewBuffer([]byte{})
err := kube.Write(buf)
require.NoError(t, err)
result, err := ioutil.ReadFile("testdata/result-kubeconf")
require.NoError(t, err)
assert.Equal(t, result, buf.Bytes())
}
})
}
}

View File

@ -14,6 +14,8 @@
package kubeconfig
import "fmt"
// ErrKubeConfigPathEmpty returned when kubeconfig path is not specified
type ErrKubeConfigPathEmpty struct {
}
@ -21,3 +23,14 @@ type ErrKubeConfigPathEmpty struct {
func (e *ErrKubeConfigPathEmpty) Error() string {
return "kubeconfig path is not defined"
}
// ErrClusterctlKubeconfigWrongContextsCount is returned when clusterctl client returns
// multiple or no contexts in kubeconfig for the child cluster
type ErrClusterctlKubeconfigWrongContextsCount struct {
ContextCount int
}
func (e *ErrClusterctlKubeconfigWrongContextsCount) Error() string {
return fmt.Sprintf("clusterctl client returned '%d' contexts in kubeconfig "+
"context count must exactly one", e.ContextCount)
}

View File

@ -26,6 +26,11 @@ import (
"opendev.org/airship/airshipctl/pkg/fs"
)
const (
// KubeconfigPrefix is a prefix that is added when writing temporary kubeconfig files
KubeconfigPrefix = "kubeconfig-"
)
// Interface provides a uniform way to interact with kubeconfig file
type Interface interface {
// GetFile returns path to kubeconfig file and a function to remove it
@ -43,8 +48,9 @@ type Interface interface {
var _ Interface = &kubeConfig{}
type kubeConfig struct {
path string
dumpRoot string
path string
dumpRoot string
savedByes []byte
fileSystem fs.FileSystem
sourceFunc KubeSourceFunc
@ -104,6 +110,9 @@ func FromSecret(c client.Interface, o *client.GetKubeconfigOptions) KubeSourceFu
// FromFile returns KubeSource type, uses path to kubeconfig on FS as source to construct kubeconfig object
func FromFile(path string, fSys fs.FileSystem) KubeSourceFunc {
return func() ([]byte, error) {
if fSys == nil {
fSys = fs.NewDocumentFs()
}
return fSys.ReadFile(path)
}
}
@ -160,7 +169,7 @@ func InjectFilePath(path string, fSys fs.FileSystem) Option {
}
func (k *kubeConfig) WriteFile(path string) (err error) {
data, err := k.sourceFunc()
data, err := k.bytes()
if err != nil {
return err
}
@ -168,7 +177,7 @@ func (k *kubeConfig) WriteFile(path string) (err error) {
}
func (k *kubeConfig) Write(w io.Writer) (err error) {
data, err := k.sourceFunc()
data, err := k.bytes()
if err != nil {
return err
}
@ -178,11 +187,11 @@ func (k *kubeConfig) Write(w io.Writer) (err error) {
// WriteTempFile implements kubeconfig Interface
func (k *kubeConfig) WriteTempFile(root string) (string, Cleanup, error) {
data, err := k.sourceFunc()
data, err := k.bytes()
if err != nil {
return "", nil, err
}
file, err := k.fileSystem.TempFile(root, "kubeconfig-")
file, err := k.fileSystem.TempFile(root, KubeconfigPrefix)
if err != nil {
return "", nil, err
}
@ -197,6 +206,14 @@ func (k *kubeConfig) WriteTempFile(root string) (string, Cleanup, error) {
return fName, cleanup(fName, k.fileSystem), nil
}
func (k *kubeConfig) bytes() ([]byte, error) {
var err error
if len(k.savedByes) == 0 {
k.savedByes, err = k.sourceFunc()
}
return k.savedByes, err
}
// GetFile checks if path to kubeconfig is already set and returns it no cleanup is necessary,
// and Cleanup() method will do nothing.
// If path is not set kubeconfig will be written to temporary file system, returned path will

View File

@ -54,27 +54,6 @@ users:
user:
client-certificate-data: cert-data
client-key-data: client-keydata
`
//nolint: lll
testFullValidKubeconfig = `apiVersion: v1
clusters:
- cluster:
certificate-authority-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUN5RENDQWJDZ0F3SUJBZ0lCQURBTkJna3Foa2lHOXcwQkFRc0ZBREFWTVJNd0VRWURWUVFERXdwcmRXSmwKY201bGRHVnpNQjRYRFRFNU1USXlOakE0TWpneU5Gb1hEVEk1TVRJeU16QTRNamd5TkZvd0ZURVRNQkVHQTFVRQpBeE1LYTNWaVpYSnVaWFJsY3pDQ0FTSXdEUVlKS29aSWh2Y05BUUVCQlFBRGdnRVBBRENDQVFvQ2dnRUJBTTFSClM0d3lnajNpU0JBZjlCR0JUS1p5VTFwYmdDaGQ2WTdJektaZWRoakM2K3k1ZEJpWm81ZUx6Z2tEc2gzOC9YQ1MKenFPS2V5cE5RcDN5QVlLdmJKSHg3ODZxSFZZNjg1ZDVYVDNaOHNyVVRzVDR5WmNzZHAzV3lHdDM0eXYzNi9BSQoxK1NlUFErdU5JemN6bzNEdWhXR0ZoQjk3VjZwRitFUTBlVWN5bk05c2hkL3AwWVFzWDR1ZlhxaENENVpzZnZUCnBka3UvTWkyWnVGUldUUUtNeGpqczV3Z2RBWnBsNnN0L2ZkbmZwd1Q5cC9WTjRuaXJnMEsxOURTSFFJTHVrU2MKb013bXNBeDJrZmxITWhPazg5S3FpMEloL2cyczRFYTRvWURZemt0Y2JRZ24wd0lqZ2dmdnVzM3pRbEczN2lwYQo4cVRzS2VmVGdkUjhnZkJDNUZNQ0F3RUFBYU1qTUNFd0RnWURWUjBQQVFIL0JBUURBZ0trTUE4R0ExVWRFd0VCCi93UUZNQU1CQWY4d0RRWUpLb1pJaHZjTkFRRUxCUUFEZ2dFQkFJek9BL00xWmRGUElzd2VoWjFuemJ0VFNURG4KRHMyVnhSV0VnclFFYzNSYmV3a1NkbTlBS3MwVGR0ZHdEbnBEL2tRYkNyS2xEeFF3RWg3NFZNSFZYYkFadDdsVwpCSm90T21xdXgxYThKYklDRTljR0FHRzFvS0g5R29jWERZY0JzOTA3ckxIdStpVzFnL0xVdG5hN1dSampqZnBLCnFGelFmOGdJUHZIM09BZ3B1RVVncUx5QU8ya0VnelZwTjZwQVJxSnZVRks2TUQ0YzFmMnlxWGxwNXhrN2dFSnIKUzQ4WmF6d0RmWUVmV3Jrdld1YWdvZ1M2SktvbjVEZ0Z1ZHhINXM2Snl6R3lPVnZ0eG1TY2FvOHNxaCs3UXkybgoyLzFVcU5ZK0hlN0x4d04rYkhwYkIxNUtIMTU5ZHNuS3BRbjRORG1jSTZrVnJ3MDVJMUg5ZGRBbGF0bz0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo=
server: https://10.23.25.101:6443
name: dummycluster_ephemeral
contexts:
- context:
cluster: dummycluster_ephemeral
user: kubernetes-admin
name: dummy_cluster
current-context: dummy_cluster
kind: Config
preferences: {}
users:
- name: kubernetes-admin
user:
client-certificate-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUQwRENDQXJnQ0ZFdFBveEZYSjVrVFNWTXQ0OVlqcHBQL3hCYnlNQTBHQ1NxR1NJYjNEUUVCQ3dVQU1CVXgKRXpBUkJnTlZCQU1UQ210MVltVnlibVYwWlhNd0hoY05NakF3TVRJME1Ua3hOVEV3V2hjTk1qa3hNakF5TVRreApOVEV3V2pBME1Sa3dGd1lEVlFRRERCQnJkV0psY201bGRHVnpMV0ZrYldsdU1SY3dGUVlEVlFRS0RBNXplWE4wClpXMDZiV0Z6ZEdWeWN6Q0NBaUl3RFFZSktvWklodmNOQVFFQkJRQURnZ0lQQURDQ0Fnb0NnZ0lCQU1iaFhUUmsKVjZiZXdsUjBhZlpBdTBGYWVsOXRtRThaSFEvaGtaSHhuTjc2bDZUUFltcGJvaDRvRjNGMFFqbzROS1o5NVRuWgo0OWNoV240eFJiZVlPU25EcDBpV0Qzd0pXUlZ5aVFvVUFyYTlNcHVPNkVFU1FpbFVGNXNxc0VXUVdVMjBETStBCkdxK1k0Z2c3eDJ1Q0hTdk1GUmkrNEw5RWlXR2xnRDIvb1hXUm5NWEswNExQajZPb3Vkb2Zid2RmT3J6dTBPVkUKUzR0eGtuS1BCY1BUU3YxMWVaWVhja0JEVjNPbExENEZ3dTB3NTcwcnczNzAraEpYdlZxd3Zjb2RjZjZEL1BXWQowamlnd2ppeUJuZ2dXYW04UVFjd1Nud3o0d05sV3hKOVMyWUJFb1ptdWxVUlFaWVk5ZXRBcEpBdFMzTjlUNlQ2ClovSlJRdEdhZDJmTldTYkxEck5qdU1OTGhBYWRMQnhJUHpBNXZWWk5aalJkdEMwU25pMlFUMTVpSFp4d1RxcjQKakRQQ0pYRXU3KytxcWpQVldUaUZLK3JqcVNhS1pqVWZVaUpHQkJWcm5RZkJENHNtRnNkTjB5cm9tYTZOYzRMNQpKS21RV1NHdmd1aG0zbW5sYjFRaVRZanVyZFJQRFNmdmwrQ0NHbnA1QkkvZ1pwMkF1SHMvNUpKVTJlc1ZvL0xsCkVPdHdSOXdXd3dXcTAvZjhXS3R4bVRrMTUyOUp2dFBGQXQweW1CVjhQbHZlYnVwYmJqeW5pL2xWbTJOYmV6dWUKeCtlMEpNbGtWWnFmYkRSS243SjZZSnJHWW1CUFV0QldoSVkzb1pJVTFEUXI4SUlIbkdmYlZoWlR5ME1IMkFCQQp1dlVQcUtSVk80UGkxRTF4OEE2eWVPeVRDcnB4L0pBazVyR2RBZ01CQUFFd0RRWUpLb1pJaHZjTkFRRUxCUUFECmdnRUJBSWNFM1BxZHZDTVBIMnJzMXJESk9ESHY3QWk4S01PVXZPRi90RjlqR2EvSFBJbkh3RlVFNEltbldQeDYKVUdBMlE1bjFsRDFGQlU0T0M4eElZc3VvS1VQVHk1T0t6SVNMNEZnL0lEcG54STlrTXlmNStMR043aG8rblJmawpCZkpJblVYb0tERW1neHZzSWFGd1h6bGtSTDJzL1lKYUZRRzE1Uis1YzFyckJmd2dJOFA5Tkd6aEM1cXhnSmovCm04K3hPMGhXUmJIYklrQ21NekRib2pCSWhaL00rb3VYR1doei9TakpodXhZTVBnek5MZkFGcy9PMTVaSjd3YXcKZ3ZoSGc3L2E5UzRvUCtEYytPa3VrMkV1MUZjL0E5WHpWMzc5aWhNWW5ub3RQMldWeFZ3b0ZZQUg0NUdQcDZsUApCQmwyNnkxc2JMbjl6aGZYUUJIMVpFN0EwZVE9Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K
client-key-data: LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlFcFFJQkFBS0NBUUVBdXpHSll2UFo2RG9pNDIxREs4V0phWkNuTkFkMnF6NXAvMDQyb0Z6UVBickFnekUyCnFZVmt6T0w4eEFWZVI3U041d1dvVFdFeUY4RVY3cnIvNCtIaEhHaXE1UG1xdUlGeXp6bjYvSVpjOGpVOXhFZnoKdmlrY2lyTGZVNHZSWEpRd1Z3Z0FTTmwyQVdBSWgyZERkVHJqQkNmaUtXTUh5ajBSYkhhbHNCek9wZ1QvSFR2MwpHUXpuVVF6Rkt2MmRqNVYxTmtTL0RIanlSUkorRUw2UUJZbUdzZWc1UDROYkM5ZWJ1aXBtTVRBcS9KdW1Pb29kCitGakwyblpxTDZmSTZmQnRGNU9HbHBDQjFZSjhmekN0R1FRWjdIVEliZGIydHA0M0ZWT2h5UWJWY0hxVEEwNFAKSjE1KzBXQXltVUpVejhYQTU0NHIvYnY3NEpjSlVSRmhhclppUXdJREFRQUJBb0lCQVFDU0pycjlaeVpiQ2dqegpSL3VKMFZEWCt2aVF4c01BTUZyUjJsOE1GV3NBeHk1SFA4Vk4xYmc5djN0YUVGYnI1U3hsa3lVMFJRNjNQU25DCm1uM3ZqZ3dVQWlScllnTEl5MGk0UXF5VFBOU1V4cnpTNHRxTFBjM3EvSDBnM2FrNGZ2cSsrS0JBUUlqQnloamUKbnVFc1JpMjRzT3NESlM2UDE5NGlzUC9yNEpIM1M5bFZGbkVuOGxUR2c0M1kvMFZoMXl0cnkvdDljWjR5ZUNpNwpjMHFEaTZZcXJZaFZhSW9RRW1VQjdsbHRFZkZzb3l4VDR6RTE5U3pVbkRoMmxjYTF1TzhqcmI4d2xHTzBoQ2JyClB1R1l2WFFQa3Q0VlNmalhvdGJ3d2lBNFRCVERCRzU1bHp6MmNKeS9zSS8zSHlYbEMxcTdXUmRuQVhhZ1F0VzkKOE9DZGRkb0JBb0dCQU5NcUNtSW94REtyckhZZFRxT1M1ZFN4cVMxL0NUN3ZYZ0pScXBqd2Y4WHA2WHo0KzIvTAozVXFaVDBEL3dGTkZkc1Z4eFYxMnNYMUdwMHFWZVlKRld5OVlCaHVSWGpTZ0ZEWldSY1Z1Y01sNVpPTmJsbmZGCjVKQ0xnNXFMZ1g5VTNSRnJrR3A0R241UDQxamg4TnhKVlhzZG5xWE9xNTFUK1RRT1UzdkpGQjc1QW9HQkFPTHcKalp1cnZtVkZyTHdaVGgvRDNpWll5SVV0ZUljZ2NKLzlzbTh6L0pPRmRIbFd4dGRHUFVzYVd1MnBTNEhvckFtbgpqTm4vSTluUXd3enZ3MWUzVVFPbUhMRjVBczk4VU5hbk5TQ0xNMW1yaXZHRXJ1VHFnTDM1bU41eFZPdTUxQU5JCm4yNkFtODBJT2JDeEtLa0R0ZXJSaFhHd3g5c1pONVJCbG9VRThZNGJBb0dBQ3ZsdVhMZWRxcng5VkE0bDNoNXUKVDJXRVUxYjgxZ1orcmtRc1I1S0lNWEw4cllBTElUNUpHKzFuendyN3BkaEFXZmFWdVV2SDRhamdYT0h6MUs5aQpFODNSVTNGMG9ldUg0V01PY1RwU0prWm0xZUlXcWRiaEVCb1FGdUlWTXRib1BsV0d4ZUhFRHJoOEtreGp4aThSCmdEcUQyajRwY1IzQ0g5QjJ5a0lqQjVFQ2dZRUExc0xXLys2enE1c1lNSm14K1JXZThhTXJmL3pjQnVTSU1LQWgKY0dNK0wwMG9RSHdDaUU4TVNqcVN1ajV3R214YUFuanhMb3ZwSFlRV1VmUEVaUW95UE1YQ2VhRVBLOU4xbk8xMwp0V2lHRytIZkIxaU5PazFCc0lhNFNDbndOM1FRVTFzeXBaeEgxT3hueS9LYmkvYmEvWEZ5VzNqMGFUK2YvVWxrCmJGV1ZVdWtDZ1lFQTBaMmRTTFlmTjV5eFNtYk5xMWVqZXdWd1BjRzQxR2hQclNUZEJxdHFac1doWGE3aDdLTWEKeHdvamh5SXpnTXNyK2tXODdlajhDQ2h0d21sQ1p5QU92QmdOZytncnJ1cEZLM3FOSkpKeU9YREdHckdpbzZmTQp5aXB3Q2tZVGVxRThpZ1J6UkI5QkdFUGY4eVpjMUtwdmZhUDVhM0lRZmxiV0czbGpUemNNZVZjPQotLS0tLUVORCBSU0EgUFJJVkFURSBLRVktLS0tLQo=
`
)
@ -179,28 +158,26 @@ func TestFromSecret(t *testing.T) {
func TestFromBundle(t *testing.T) {
tests := []struct {
name string
rootPath string
shouldFail bool
expectedData []byte
name string
rootPath string
expectedContains string
shouldFail bool
}{
{
name: "valid kubeconfig",
rootPath: "testdata",
shouldFail: false,
expectedData: []byte(testFullValidKubeconfig),
name: "valid kubeconfig",
rootPath: "testdata",
shouldFail: false,
expectedContains: "parent_parent_context",
},
{
name: "wrong path",
rootPath: "wrong/path",
shouldFail: true,
expectedData: nil,
name: "wrong path",
rootPath: "wrong/path",
shouldFail: true,
},
{
name: "kubeconfig not found",
rootPath: "testdata_fail",
shouldFail: true,
expectedData: nil,
name: "kubeconfig not found",
rootPath: "testdata_fail",
shouldFail: true,
},
}
@ -213,7 +190,7 @@ func TestFromBundle(t *testing.T) {
assert.Nil(t, kubeconf)
} else {
require.NoError(t, err)
assert.Equal(t, tt.expectedData, kubeconf)
assert.Contains(t, string(kubeconf), tt.expectedContains)
}
})
}

View File

@ -9,16 +9,16 @@ config:
- cluster:
certificate-authority-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUN5RENDQWJDZ0F3SUJBZ0lCQURBTkJna3Foa2lHOXcwQkFRc0ZBREFWTVJNd0VRWURWUVFERXdwcmRXSmwKY201bGRHVnpNQjRYRFRFNU1USXlOakE0TWpneU5Gb1hEVEk1TVRJeU16QTRNamd5TkZvd0ZURVRNQkVHQTFVRQpBeE1LYTNWaVpYSnVaWFJsY3pDQ0FTSXdEUVlKS29aSWh2Y05BUUVCQlFBRGdnRVBBRENDQVFvQ2dnRUJBTTFSClM0d3lnajNpU0JBZjlCR0JUS1p5VTFwYmdDaGQ2WTdJektaZWRoakM2K3k1ZEJpWm81ZUx6Z2tEc2gzOC9YQ1MKenFPS2V5cE5RcDN5QVlLdmJKSHg3ODZxSFZZNjg1ZDVYVDNaOHNyVVRzVDR5WmNzZHAzV3lHdDM0eXYzNi9BSQoxK1NlUFErdU5JemN6bzNEdWhXR0ZoQjk3VjZwRitFUTBlVWN5bk05c2hkL3AwWVFzWDR1ZlhxaENENVpzZnZUCnBka3UvTWkyWnVGUldUUUtNeGpqczV3Z2RBWnBsNnN0L2ZkbmZwd1Q5cC9WTjRuaXJnMEsxOURTSFFJTHVrU2MKb013bXNBeDJrZmxITWhPazg5S3FpMEloL2cyczRFYTRvWURZemt0Y2JRZ24wd0lqZ2dmdnVzM3pRbEczN2lwYQo4cVRzS2VmVGdkUjhnZkJDNUZNQ0F3RUFBYU1qTUNFd0RnWURWUjBQQVFIL0JBUURBZ0trTUE4R0ExVWRFd0VCCi93UUZNQU1CQWY4d0RRWUpLb1pJaHZjTkFRRUxCUUFEZ2dFQkFJek9BL00xWmRGUElzd2VoWjFuemJ0VFNURG4KRHMyVnhSV0VnclFFYzNSYmV3a1NkbTlBS3MwVGR0ZHdEbnBEL2tRYkNyS2xEeFF3RWg3NFZNSFZYYkFadDdsVwpCSm90T21xdXgxYThKYklDRTljR0FHRzFvS0g5R29jWERZY0JzOTA3ckxIdStpVzFnL0xVdG5hN1dSampqZnBLCnFGelFmOGdJUHZIM09BZ3B1RVVncUx5QU8ya0VnelZwTjZwQVJxSnZVRks2TUQ0YzFmMnlxWGxwNXhrN2dFSnIKUzQ4WmF6d0RmWUVmV3Jrdld1YWdvZ1M2SktvbjVEZ0Z1ZHhINXM2Snl6R3lPVnZ0eG1TY2FvOHNxaCs3UXkybgoyLzFVcU5ZK0hlN0x4d04rYkhwYkIxNUtIMTU5ZHNuS3BRbjRORG1jSTZrVnJ3MDVJMUg5ZGRBbGF0bz0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo=
server: https://10.23.25.101:6443
name: dummycluster_ephemeral
name: parent_parent_cluster
contexts:
- context:
cluster: dummycluster_ephemeral
user: kubernetes-admin
name: dummy_cluster
cluster: parent_parent_cluster
user: parent_parent_admin
name: parent_parent_context
current-context: dummy_cluster
preferences: {}
users:
- name: kubernetes-admin
- name: parent_parent_admin
user:
client-certificate-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUQwRENDQXJnQ0ZFdFBveEZYSjVrVFNWTXQ0OVlqcHBQL3hCYnlNQTBHQ1NxR1NJYjNEUUVCQ3dVQU1CVXgKRXpBUkJnTlZCQU1UQ210MVltVnlibVYwWlhNd0hoY05NakF3TVRJME1Ua3hOVEV3V2hjTk1qa3hNakF5TVRreApOVEV3V2pBME1Sa3dGd1lEVlFRRERCQnJkV0psY201bGRHVnpMV0ZrYldsdU1SY3dGUVlEVlFRS0RBNXplWE4wClpXMDZiV0Z6ZEdWeWN6Q0NBaUl3RFFZSktvWklodmNOQVFFQkJRQURnZ0lQQURDQ0Fnb0NnZ0lCQU1iaFhUUmsKVjZiZXdsUjBhZlpBdTBGYWVsOXRtRThaSFEvaGtaSHhuTjc2bDZUUFltcGJvaDRvRjNGMFFqbzROS1o5NVRuWgo0OWNoV240eFJiZVlPU25EcDBpV0Qzd0pXUlZ5aVFvVUFyYTlNcHVPNkVFU1FpbFVGNXNxc0VXUVdVMjBETStBCkdxK1k0Z2c3eDJ1Q0hTdk1GUmkrNEw5RWlXR2xnRDIvb1hXUm5NWEswNExQajZPb3Vkb2Zid2RmT3J6dTBPVkUKUzR0eGtuS1BCY1BUU3YxMWVaWVhja0JEVjNPbExENEZ3dTB3NTcwcnczNzAraEpYdlZxd3Zjb2RjZjZEL1BXWQowamlnd2ppeUJuZ2dXYW04UVFjd1Nud3o0d05sV3hKOVMyWUJFb1ptdWxVUlFaWVk5ZXRBcEpBdFMzTjlUNlQ2ClovSlJRdEdhZDJmTldTYkxEck5qdU1OTGhBYWRMQnhJUHpBNXZWWk5aalJkdEMwU25pMlFUMTVpSFp4d1RxcjQKakRQQ0pYRXU3KytxcWpQVldUaUZLK3JqcVNhS1pqVWZVaUpHQkJWcm5RZkJENHNtRnNkTjB5cm9tYTZOYzRMNQpKS21RV1NHdmd1aG0zbW5sYjFRaVRZanVyZFJQRFNmdmwrQ0NHbnA1QkkvZ1pwMkF1SHMvNUpKVTJlc1ZvL0xsCkVPdHdSOXdXd3dXcTAvZjhXS3R4bVRrMTUyOUp2dFBGQXQweW1CVjhQbHZlYnVwYmJqeW5pL2xWbTJOYmV6dWUKeCtlMEpNbGtWWnFmYkRSS243SjZZSnJHWW1CUFV0QldoSVkzb1pJVTFEUXI4SUlIbkdmYlZoWlR5ME1IMkFCQQp1dlVQcUtSVk80UGkxRTF4OEE2eWVPeVRDcnB4L0pBazVyR2RBZ01CQUFFd0RRWUpLb1pJaHZjTkFRRUxCUUFECmdnRUJBSWNFM1BxZHZDTVBIMnJzMXJESk9ESHY3QWk4S01PVXZPRi90RjlqR2EvSFBJbkh3RlVFNEltbldQeDYKVUdBMlE1bjFsRDFGQlU0T0M4eElZc3VvS1VQVHk1T0t6SVNMNEZnL0lEcG54STlrTXlmNStMR043aG8rblJmawpCZkpJblVYb0tERW1neHZzSWFGd1h6bGtSTDJzL1lKYUZRRzE1Uis1YzFyckJmd2dJOFA5Tkd6aEM1cXhnSmovCm04K3hPMGhXUmJIYklrQ21NekRib2pCSWhaL00rb3VYR1doei9TakpodXhZTVBnek5MZkFGcy9PMTVaSjd3YXcKZ3ZoSGc3L2E5UzRvUCtEYytPa3VrMkV1MUZjL0E5WHpWMzc5aWhNWW5ub3RQMldWeFZ3b0ZZQUg0NUdQcDZsUApCQmwyNnkxc2JMbjl6aGZYUUJIMVpFN0EwZVE9Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K
client-key-data: LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlFcFFJQkFBS0NBUUVBdXpHSll2UFo2RG9pNDIxREs4V0phWkNuTkFkMnF6NXAvMDQyb0Z6UVBickFnekUyCnFZVmt6T0w4eEFWZVI3U041d1dvVFdFeUY4RVY3cnIvNCtIaEhHaXE1UG1xdUlGeXp6bjYvSVpjOGpVOXhFZnoKdmlrY2lyTGZVNHZSWEpRd1Z3Z0FTTmwyQVdBSWgyZERkVHJqQkNmaUtXTUh5ajBSYkhhbHNCek9wZ1QvSFR2MwpHUXpuVVF6Rkt2MmRqNVYxTmtTL0RIanlSUkorRUw2UUJZbUdzZWc1UDROYkM5ZWJ1aXBtTVRBcS9KdW1Pb29kCitGakwyblpxTDZmSTZmQnRGNU9HbHBDQjFZSjhmekN0R1FRWjdIVEliZGIydHA0M0ZWT2h5UWJWY0hxVEEwNFAKSjE1KzBXQXltVUpVejhYQTU0NHIvYnY3NEpjSlVSRmhhclppUXdJREFRQUJBb0lCQVFDU0pycjlaeVpiQ2dqegpSL3VKMFZEWCt2aVF4c01BTUZyUjJsOE1GV3NBeHk1SFA4Vk4xYmc5djN0YUVGYnI1U3hsa3lVMFJRNjNQU25DCm1uM3ZqZ3dVQWlScllnTEl5MGk0UXF5VFBOU1V4cnpTNHRxTFBjM3EvSDBnM2FrNGZ2cSsrS0JBUUlqQnloamUKbnVFc1JpMjRzT3NESlM2UDE5NGlzUC9yNEpIM1M5bFZGbkVuOGxUR2c0M1kvMFZoMXl0cnkvdDljWjR5ZUNpNwpjMHFEaTZZcXJZaFZhSW9RRW1VQjdsbHRFZkZzb3l4VDR6RTE5U3pVbkRoMmxjYTF1TzhqcmI4d2xHTzBoQ2JyClB1R1l2WFFQa3Q0VlNmalhvdGJ3d2lBNFRCVERCRzU1bHp6MmNKeS9zSS8zSHlYbEMxcTdXUmRuQVhhZ1F0VzkKOE9DZGRkb0JBb0dCQU5NcUNtSW94REtyckhZZFRxT1M1ZFN4cVMxL0NUN3ZYZ0pScXBqd2Y4WHA2WHo0KzIvTAozVXFaVDBEL3dGTkZkc1Z4eFYxMnNYMUdwMHFWZVlKRld5OVlCaHVSWGpTZ0ZEWldSY1Z1Y01sNVpPTmJsbmZGCjVKQ0xnNXFMZ1g5VTNSRnJrR3A0R241UDQxamg4TnhKVlhzZG5xWE9xNTFUK1RRT1UzdkpGQjc1QW9HQkFPTHcKalp1cnZtVkZyTHdaVGgvRDNpWll5SVV0ZUljZ2NKLzlzbTh6L0pPRmRIbFd4dGRHUFVzYVd1MnBTNEhvckFtbgpqTm4vSTluUXd3enZ3MWUzVVFPbUhMRjVBczk4VU5hbk5TQ0xNMW1yaXZHRXJ1VHFnTDM1bU41eFZPdTUxQU5JCm4yNkFtODBJT2JDeEtLa0R0ZXJSaFhHd3g5c1pONVJCbG9VRThZNGJBb0dBQ3ZsdVhMZWRxcng5VkE0bDNoNXUKVDJXRVUxYjgxZ1orcmtRc1I1S0lNWEw4cllBTElUNUpHKzFuendyN3BkaEFXZmFWdVV2SDRhamdYT0h6MUs5aQpFODNSVTNGMG9ldUg0V01PY1RwU0prWm0xZUlXcWRiaEVCb1FGdUlWTXRib1BsV0d4ZUhFRHJoOEtreGp4aThSCmdEcUQyajRwY1IzQ0g5QjJ5a0lqQjVFQ2dZRUExc0xXLys2enE1c1lNSm14K1JXZThhTXJmL3pjQnVTSU1LQWgKY0dNK0wwMG9RSHdDaUU4TVNqcVN1ajV3R214YUFuanhMb3ZwSFlRV1VmUEVaUW95UE1YQ2VhRVBLOU4xbk8xMwp0V2lHRytIZkIxaU5PazFCc0lhNFNDbndOM1FRVTFzeXBaeEgxT3hueS9LYmkvYmEvWEZ5VzNqMGFUK2YvVWxrCmJGV1ZVdWtDZ1lFQTBaMmRTTFlmTjV5eFNtYk5xMWVqZXdWd1BjRzQxR2hQclNUZEJxdHFac1doWGE3aDdLTWEKeHdvamh5SXpnTXNyK2tXODdlajhDQ2h0d21sQ1p5QU92QmdOZytncnJ1cEZLM3FOSkpKeU9YREdHckdpbzZmTQp5aXB3Q2tZVGVxRThpZ1J6UkI5QkdFUGY4eVpjMUtwdmZhUDVhM0lRZmxiV0czbGpUemNNZVZjPQotLS0tLUVORCBSU0EgUFJJVkFURSBLRVktLS0tLQo=

View File

@ -0,0 +1,43 @@
apiVersion: v1
clusters:
- cluster:
certificate-authority-data: c29tZWNlcnQK
server: https://10.23.25.101:6443
name: child_cluster
- cluster:
certificate-authority-data: c29tZWNlcnQK
server: https://10.23.25.101:6443
name: parent_cluster
- cluster:
certificate-authority-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUN5RENDQWJDZ0F3SUJBZ0lCQURBTkJna3Foa2lHOXcwQkFRc0ZBREFWTVJNd0VRWURWUVFERXdwcmRXSmwKY201bGRHVnpNQjRYRFRFNU1USXlOakE0TWpneU5Gb1hEVEk1TVRJeU16QTRNamd5TkZvd0ZURVRNQkVHQTFVRQpBeE1LYTNWaVpYSnVaWFJsY3pDQ0FTSXdEUVlKS29aSWh2Y05BUUVCQlFBRGdnRVBBRENDQVFvQ2dnRUJBTTFSClM0d3lnajNpU0JBZjlCR0JUS1p5VTFwYmdDaGQ2WTdJektaZWRoakM2K3k1ZEJpWm81ZUx6Z2tEc2gzOC9YQ1MKenFPS2V5cE5RcDN5QVlLdmJKSHg3ODZxSFZZNjg1ZDVYVDNaOHNyVVRzVDR5WmNzZHAzV3lHdDM0eXYzNi9BSQoxK1NlUFErdU5JemN6bzNEdWhXR0ZoQjk3VjZwRitFUTBlVWN5bk05c2hkL3AwWVFzWDR1ZlhxaENENVpzZnZUCnBka3UvTWkyWnVGUldUUUtNeGpqczV3Z2RBWnBsNnN0L2ZkbmZwd1Q5cC9WTjRuaXJnMEsxOURTSFFJTHVrU2MKb013bXNBeDJrZmxITWhPazg5S3FpMEloL2cyczRFYTRvWURZemt0Y2JRZ24wd0lqZ2dmdnVzM3pRbEczN2lwYQo4cVRzS2VmVGdkUjhnZkJDNUZNQ0F3RUFBYU1qTUNFd0RnWURWUjBQQVFIL0JBUURBZ0trTUE4R0ExVWRFd0VCCi93UUZNQU1CQWY4d0RRWUpLb1pJaHZjTkFRRUxCUUFEZ2dFQkFJek9BL00xWmRGUElzd2VoWjFuemJ0VFNURG4KRHMyVnhSV0VnclFFYzNSYmV3a1NkbTlBS3MwVGR0ZHdEbnBEL2tRYkNyS2xEeFF3RWg3NFZNSFZYYkFadDdsVwpCSm90T21xdXgxYThKYklDRTljR0FHRzFvS0g5R29jWERZY0JzOTA3ckxIdStpVzFnL0xVdG5hN1dSampqZnBLCnFGelFmOGdJUHZIM09BZ3B1RVVncUx5QU8ya0VnelZwTjZwQVJxSnZVRks2TUQ0YzFmMnlxWGxwNXhrN2dFSnIKUzQ4WmF6d0RmWUVmV3Jrdld1YWdvZ1M2SktvbjVEZ0Z1ZHhINXM2Snl6R3lPVnZ0eG1TY2FvOHNxaCs3UXkybgoyLzFVcU5ZK0hlN0x4d04rYkhwYkIxNUtIMTU5ZHNuS3BRbjRORG1jSTZrVnJ3MDVJMUg5ZGRBbGF0bz0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo=
server: https://10.23.25.101:6443
name: parent_parent_cluster
contexts:
- context:
cluster: child_cluster
user: child_user
name: child
- context:
cluster: parent_cluster
user: parent_admin
name: parent-custom
- context:
cluster: parent_parent_cluster
user: parent_parent_admin
name: parent_parent_context
current-context: dummy_cluster
kind: Config
preferences: {}
users:
- name: child_user
user:
client-certificate-data: c29tZWNlcnQK
client-key-data: c29tZWNlcnQK
- name: parent_admin
user:
client-certificate-data: c29tZWNlcnQK
client-key-data: c29tZWNlcnQK
- name: parent_parent_admin
user:
client-certificate-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUQwRENDQXJnQ0ZFdFBveEZYSjVrVFNWTXQ0OVlqcHBQL3hCYnlNQTBHQ1NxR1NJYjNEUUVCQ3dVQU1CVXgKRXpBUkJnTlZCQU1UQ210MVltVnlibVYwWlhNd0hoY05NakF3TVRJME1Ua3hOVEV3V2hjTk1qa3hNakF5TVRreApOVEV3V2pBME1Sa3dGd1lEVlFRRERCQnJkV0psY201bGRHVnpMV0ZrYldsdU1SY3dGUVlEVlFRS0RBNXplWE4wClpXMDZiV0Z6ZEdWeWN6Q0NBaUl3RFFZSktvWklodmNOQVFFQkJRQURnZ0lQQURDQ0Fnb0NnZ0lCQU1iaFhUUmsKVjZiZXdsUjBhZlpBdTBGYWVsOXRtRThaSFEvaGtaSHhuTjc2bDZUUFltcGJvaDRvRjNGMFFqbzROS1o5NVRuWgo0OWNoV240eFJiZVlPU25EcDBpV0Qzd0pXUlZ5aVFvVUFyYTlNcHVPNkVFU1FpbFVGNXNxc0VXUVdVMjBETStBCkdxK1k0Z2c3eDJ1Q0hTdk1GUmkrNEw5RWlXR2xnRDIvb1hXUm5NWEswNExQajZPb3Vkb2Zid2RmT3J6dTBPVkUKUzR0eGtuS1BCY1BUU3YxMWVaWVhja0JEVjNPbExENEZ3dTB3NTcwcnczNzAraEpYdlZxd3Zjb2RjZjZEL1BXWQowamlnd2ppeUJuZ2dXYW04UVFjd1Nud3o0d05sV3hKOVMyWUJFb1ptdWxVUlFaWVk5ZXRBcEpBdFMzTjlUNlQ2ClovSlJRdEdhZDJmTldTYkxEck5qdU1OTGhBYWRMQnhJUHpBNXZWWk5aalJkdEMwU25pMlFUMTVpSFp4d1RxcjQKakRQQ0pYRXU3KytxcWpQVldUaUZLK3JqcVNhS1pqVWZVaUpHQkJWcm5RZkJENHNtRnNkTjB5cm9tYTZOYzRMNQpKS21RV1NHdmd1aG0zbW5sYjFRaVRZanVyZFJQRFNmdmwrQ0NHbnA1QkkvZ1pwMkF1SHMvNUpKVTJlc1ZvL0xsCkVPdHdSOXdXd3dXcTAvZjhXS3R4bVRrMTUyOUp2dFBGQXQweW1CVjhQbHZlYnVwYmJqeW5pL2xWbTJOYmV6dWUKeCtlMEpNbGtWWnFmYkRSS243SjZZSnJHWW1CUFV0QldoSVkzb1pJVTFEUXI4SUlIbkdmYlZoWlR5ME1IMkFCQQp1dlVQcUtSVk80UGkxRTF4OEE2eWVPeVRDcnB4L0pBazVyR2RBZ01CQUFFd0RRWUpLb1pJaHZjTkFRRUxCUUFECmdnRUJBSWNFM1BxZHZDTVBIMnJzMXJESk9ESHY3QWk4S01PVXZPRi90RjlqR2EvSFBJbkh3RlVFNEltbldQeDYKVUdBMlE1bjFsRDFGQlU0T0M4eElZc3VvS1VQVHk1T0t6SVNMNEZnL0lEcG54STlrTXlmNStMR043aG8rblJmawpCZkpJblVYb0tERW1neHZzSWFGd1h6bGtSTDJzL1lKYUZRRzE1Uis1YzFyckJmd2dJOFA5Tkd6aEM1cXhnSmovCm04K3hPMGhXUmJIYklrQ21NekRib2pCSWhaL00rb3VYR1doei9TakpodXhZTVBnek5MZkFGcy9PMTVaSjd3YXcKZ3ZoSGc3L2E5UzRvUCtEYytPa3VrMkV1MUZjL0E5WHpWMzc5aWhNWW5ub3RQMldWeFZ3b0ZZQUg0NUdQcDZsUApCQmwyNnkxc2JMbjl6aGZYUUJIMVpFN0EwZVE9Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K
client-key-data: LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlFcFFJQkFBS0NBUUVBdXpHSll2UFo2RG9pNDIxREs4V0phWkNuTkFkMnF6NXAvMDQyb0Z6UVBickFnekUyCnFZVmt6T0w4eEFWZVI3U041d1dvVFdFeUY4RVY3cnIvNCtIaEhHaXE1UG1xdUlGeXp6bjYvSVpjOGpVOXhFZnoKdmlrY2lyTGZVNHZSWEpRd1Z3Z0FTTmwyQVdBSWgyZERkVHJqQkNmaUtXTUh5ajBSYkhhbHNCek9wZ1QvSFR2MwpHUXpuVVF6Rkt2MmRqNVYxTmtTL0RIanlSUkorRUw2UUJZbUdzZWc1UDROYkM5ZWJ1aXBtTVRBcS9KdW1Pb29kCitGakwyblpxTDZmSTZmQnRGNU9HbHBDQjFZSjhmekN0R1FRWjdIVEliZGIydHA0M0ZWT2h5UWJWY0hxVEEwNFAKSjE1KzBXQXltVUpVejhYQTU0NHIvYnY3NEpjSlVSRmhhclppUXdJREFRQUJBb0lCQVFDU0pycjlaeVpiQ2dqegpSL3VKMFZEWCt2aVF4c01BTUZyUjJsOE1GV3NBeHk1SFA4Vk4xYmc5djN0YUVGYnI1U3hsa3lVMFJRNjNQU25DCm1uM3ZqZ3dVQWlScllnTEl5MGk0UXF5VFBOU1V4cnpTNHRxTFBjM3EvSDBnM2FrNGZ2cSsrS0JBUUlqQnloamUKbnVFc1JpMjRzT3NESlM2UDE5NGlzUC9yNEpIM1M5bFZGbkVuOGxUR2c0M1kvMFZoMXl0cnkvdDljWjR5ZUNpNwpjMHFEaTZZcXJZaFZhSW9RRW1VQjdsbHRFZkZzb3l4VDR6RTE5U3pVbkRoMmxjYTF1TzhqcmI4d2xHTzBoQ2JyClB1R1l2WFFQa3Q0VlNmalhvdGJ3d2lBNFRCVERCRzU1bHp6MmNKeS9zSS8zSHlYbEMxcTdXUmRuQVhhZ1F0VzkKOE9DZGRkb0JBb0dCQU5NcUNtSW94REtyckhZZFRxT1M1ZFN4cVMxL0NUN3ZYZ0pScXBqd2Y4WHA2WHo0KzIvTAozVXFaVDBEL3dGTkZkc1Z4eFYxMnNYMUdwMHFWZVlKRld5OVlCaHVSWGpTZ0ZEWldSY1Z1Y01sNVpPTmJsbmZGCjVKQ0xnNXFMZ1g5VTNSRnJrR3A0R241UDQxamg4TnhKVlhzZG5xWE9xNTFUK1RRT1UzdkpGQjc1QW9HQkFPTHcKalp1cnZtVkZyTHdaVGgvRDNpWll5SVV0ZUljZ2NKLzlzbTh6L0pPRmRIbFd4dGRHUFVzYVd1MnBTNEhvckFtbgpqTm4vSTluUXd3enZ3MWUzVVFPbUhMRjVBczk4VU5hbk5TQ0xNMW1yaXZHRXJ1VHFnTDM1bU41eFZPdTUxQU5JCm4yNkFtODBJT2JDeEtLa0R0ZXJSaFhHd3g5c1pONVJCbG9VRThZNGJBb0dBQ3ZsdVhMZWRxcng5VkE0bDNoNXUKVDJXRVUxYjgxZ1orcmtRc1I1S0lNWEw4cllBTElUNUpHKzFuendyN3BkaEFXZmFWdVV2SDRhamdYT0h6MUs5aQpFODNSVTNGMG9ldUg0V01PY1RwU0prWm0xZUlXcWRiaEVCb1FGdUlWTXRib1BsV0d4ZUhFRHJoOEtreGp4aThSCmdEcUQyajRwY1IzQ0g5QjJ5a0lqQjVFQ2dZRUExc0xXLys2enE1c1lNSm14K1JXZThhTXJmL3pjQnVTSU1LQWgKY0dNK0wwMG9RSHdDaUU4TVNqcVN1ajV3R214YUFuanhMb3ZwSFlRV1VmUEVaUW95UE1YQ2VhRVBLOU4xbk8xMwp0V2lHRytIZkIxaU5PazFCc0lhNFNDbndOM1FRVTFzeXBaeEgxT3hueS9LYmkvYmEvWEZ5VzNqMGFUK2YvVWxrCmJGV1ZVdWtDZ1lFQTBaMmRTTFlmTjV5eFNtYk5xMWVqZXdWd1BjRzQxR2hQclNUZEJxdHFac1doWGE3aDdLTWEKeHdvamh5SXpnTXNyK2tXODdlajhDQ2h0d21sQ1p5QU92QmdOZytncnJ1cEZLM3FOSkpKeU9YREdHckdpbzZmTQp5aXB3Q2tZVGVxRThpZ1J6UkI5QkdFUGY4eVpjMUtwdmZhUDVhM0lRZmxiV0czbGpUemNNZVZjPQotLS0tLUVORCBSU0EgUFJJVkFURSBLRVktLS0tLQo=

View File

@ -0,0 +1,68 @@
/*
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
https://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package clusterctl
import (
"fmt"
"github.com/stretchr/testify/mock"
"opendev.org/airship/airshipctl/pkg/clusterctl/client"
)
var _ client.Interface = &MockInterface{}
// MockInterface provides mock interface for clusterctl
type MockInterface struct {
mock.Mock
}
// Init to be implemented
func (m *MockInterface) Init(kubeconfigPath, kubeconfigContext string) error {
return nil
}
// Move to be implemented
func (m *MockInterface) Move(fkp, fkc, tkp, tkc, namespace string) error {
return nil
}
// Render to be implemented
func (m *MockInterface) Render(client.RenderOptions) ([]byte, error) {
return nil, nil
}
// GetKubeconfig allows to control exepected input to the function and check expected output
// example usage:
// c := &clusterctl.MockInterface{
// Mock: mock.Mock{},
// }
// c.On("GetKubeconfig").Once().Return(&client.GetKubeconfigOptions{
// ParentKubeconfigPath: filepath.Join("testdata", kubeconfig.KubeconfigPrefix),
// ParentKubeconfigContext: "dummy_cluster",
// ManagedClusterNamespace: clustermap.DefaultClusterAPIObjNamespace,
// ManagedClusterName: childCluster,
// }, "kubeconfig data", nil)
// first argument in return function is what you expect as input
// second argument is resulting expected string
// third is resulting error
func (m *MockInterface) GetKubeconfig(options *client.GetKubeconfigOptions) (string, error) {
args := m.Called(options)
expectedResult, ok := args.Get(0).(string)
if !ok {
return "", fmt.Errorf("wrong input")
}
return expectedResult, args.Error(1)
}