Link cluster get-kubeconfig cmd with appropriate functionality
This patch links cluster get-kubeconfig command call with appropriate clusterctl functionality, and also replaces old callbacks and removes outdated implementation. Change-Id: Ibd0d981985f94497db250c8df3f5675fdec1d2ca Signed-off-by: Ruslan Aliev <raliev@mirantis.com> Relates-To: #374
This commit is contained in:
parent
63421c8fcc
commit
899bbdbe07
@ -43,7 +43,7 @@ func NewClusterCommand(cfgFactory config.Factory) *cobra.Command {
|
|||||||
clusterRootCmd.AddCommand(NewStatusCommand(cfgFactory))
|
clusterRootCmd.AddCommand(NewStatusCommand(cfgFactory))
|
||||||
clusterRootCmd.AddCommand(resetsatoken.NewResetCommand(cfgFactory))
|
clusterRootCmd.AddCommand(resetsatoken.NewResetCommand(cfgFactory))
|
||||||
clusterRootCmd.AddCommand(checkexpiration.NewCheckCommand(cfgFactory))
|
clusterRootCmd.AddCommand(checkexpiration.NewCheckCommand(cfgFactory))
|
||||||
clusterRootCmd.AddCommand(NewGetKubeconfigCommand())
|
clusterRootCmd.AddCommand(NewGetKubeconfigCommand(cfgFactory))
|
||||||
|
|
||||||
return clusterRootCmd
|
return clusterRootCmd
|
||||||
}
|
}
|
||||||
|
@ -17,36 +17,66 @@ package cluster
|
|||||||
import (
|
import (
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
"opendev.org/airship/airshipctl/pkg/errors"
|
"opendev.org/airship/airshipctl/pkg/clusterctl/client"
|
||||||
|
clusterctlcmd "opendev.org/airship/airshipctl/pkg/clusterctl/cmd"
|
||||||
|
"opendev.org/airship/airshipctl/pkg/config"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
getKubeconfigLong = `
|
getKubeconfigLong = `
|
||||||
Retrieve cluster kubeconfig and save it to file or stdout.
|
Retrieve cluster kubeconfig and print it to stdout
|
||||||
`
|
`
|
||||||
getKubeconfigExample = `
|
getKubeconfigExample = `
|
||||||
# Retrieve target-cluster kubeconfig and print it to stdout
|
# Retrieve target-cluster kubeconfig
|
||||||
airshipctl cluster get-kubeconfig target-cluster
|
airshipctl cluster get-kubeconfig target-cluster --kubeconfig /tmp/kubeconfig
|
||||||
`
|
`
|
||||||
)
|
)
|
||||||
|
|
||||||
// NewGetKubeconfigCommand creates a command which retrieves cluster kubeconfig
|
// NewGetKubeconfigCommand creates a command which retrieves cluster kubeconfig
|
||||||
func NewGetKubeconfigCommand() *cobra.Command {
|
func NewGetKubeconfigCommand(cfgFactory config.Factory) *cobra.Command {
|
||||||
|
o := &client.GetKubeconfigOptions{}
|
||||||
cmd := &cobra.Command{
|
cmd := &cobra.Command{
|
||||||
Use: "get-kubeconfig [cluster_name]",
|
Use: "get-kubeconfig [cluster_name]",
|
||||||
Short: "Retrieve kubeconfig for a desired cluster",
|
Short: "Retrieve kubeconfig for a desired cluster",
|
||||||
Long: getKubeconfigLong[1:],
|
Long: getKubeconfigLong[1:],
|
||||||
Example: getKubeconfigExample[1:],
|
Example: getKubeconfigExample[1:],
|
||||||
Args: cobra.ExactArgs(1),
|
Args: cobra.ExactArgs(1),
|
||||||
RunE: getKubeconfigRunE(),
|
RunE: getKubeconfigRunE(cfgFactory, o),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
initFlags(o, cmd)
|
||||||
|
|
||||||
return cmd
|
return cmd
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func initFlags(o *client.GetKubeconfigOptions, cmd *cobra.Command) {
|
||||||
|
flags := cmd.Flags()
|
||||||
|
|
||||||
|
flags.StringVar(
|
||||||
|
&o.ParentKubeconfigPath,
|
||||||
|
"kubeconfig",
|
||||||
|
"",
|
||||||
|
"path to kubeconfig associated with parental cluster")
|
||||||
|
|
||||||
|
flags.StringVarP(
|
||||||
|
&o.ManagedClusterNamespace,
|
||||||
|
"namespace",
|
||||||
|
"n",
|
||||||
|
"default",
|
||||||
|
"namespace where cluster is located, if not specified default one will be used")
|
||||||
|
|
||||||
|
flags.StringVar(
|
||||||
|
&o.ParentKubeconfigContext,
|
||||||
|
"context",
|
||||||
|
"",
|
||||||
|
"specify context within the kubeconfig file")
|
||||||
|
}
|
||||||
|
|
||||||
// getKubeconfigRunE returns a function to cobra command to be executed in runtime
|
// getKubeconfigRunE returns a function to cobra command to be executed in runtime
|
||||||
func getKubeconfigRunE() func(cmd *cobra.Command, args []string) error {
|
func getKubeconfigRunE(cfgFactory config.Factory, o *client.GetKubeconfigOptions) func(
|
||||||
|
cmd *cobra.Command, args []string) error {
|
||||||
return func(cmd *cobra.Command, args []string) error {
|
return func(cmd *cobra.Command, args []string) error {
|
||||||
return errors.ErrNotImplemented{What: "cluster get-kubeconfig is not implemented yet"}
|
o.ManagedClusterName = args[0]
|
||||||
|
return clusterctlcmd.GetKubeconfig(cfgFactory, o, cmd.OutOrStdout())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -26,7 +26,7 @@ func TestNewKubeConfigCommandCmd(t *testing.T) {
|
|||||||
{
|
{
|
||||||
Name: "cluster-get-kubeconfig-cmd-with-help",
|
Name: "cluster-get-kubeconfig-cmd-with-help",
|
||||||
CmdLine: "--help",
|
CmdLine: "--help",
|
||||||
Cmd: cluster.NewGetKubeconfigCommand(),
|
Cmd: cluster.NewGetKubeconfigCommand(nil),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
for _, testcase := range tests {
|
for _, testcase := range tests {
|
||||||
|
@ -1,12 +1,15 @@
|
|||||||
Retrieve cluster kubeconfig and save it to file or stdout.
|
Retrieve cluster kubeconfig and print it to stdout
|
||||||
|
|
||||||
Usage:
|
Usage:
|
||||||
get-kubeconfig [cluster_name] [flags]
|
get-kubeconfig [cluster_name] [flags]
|
||||||
|
|
||||||
Examples:
|
Examples:
|
||||||
# Retrieve target-cluster kubeconfig and print it to stdout
|
# Retrieve target-cluster kubeconfig
|
||||||
airshipctl cluster get-kubeconfig target-cluster
|
airshipctl cluster get-kubeconfig target-cluster --kubeconfig /tmp/kubeconfig
|
||||||
|
|
||||||
|
|
||||||
Flags:
|
Flags:
|
||||||
-h, --help help for get-kubeconfig
|
--context string specify context within the kubeconfig file
|
||||||
|
-h, --help help for get-kubeconfig
|
||||||
|
--kubeconfig string path to kubeconfig associated with parental cluster
|
||||||
|
-n, --namespace string namespace where cluster is located, if not specified default one will be used (default "default")
|
||||||
|
@ -4,7 +4,7 @@ Retrieve kubeconfig for a desired cluster
|
|||||||
|
|
||||||
### Synopsis
|
### Synopsis
|
||||||
|
|
||||||
Retrieve cluster kubeconfig and save it to file or stdout.
|
Retrieve cluster kubeconfig and print it to stdout
|
||||||
|
|
||||||
|
|
||||||
```
|
```
|
||||||
@ -14,15 +14,18 @@ airshipctl cluster get-kubeconfig [cluster_name] [flags]
|
|||||||
### Examples
|
### Examples
|
||||||
|
|
||||||
```
|
```
|
||||||
# Retrieve target-cluster kubeconfig and print it to stdout
|
# Retrieve target-cluster kubeconfig
|
||||||
airshipctl cluster get-kubeconfig target-cluster
|
airshipctl cluster get-kubeconfig target-cluster --kubeconfig /tmp/kubeconfig
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### Options
|
### Options
|
||||||
|
|
||||||
```
|
```
|
||||||
-h, --help help for get-kubeconfig
|
--context string specify context within the kubeconfig file
|
||||||
|
-h, --help help for get-kubeconfig
|
||||||
|
--kubeconfig string path to kubeconfig associated with parental cluster
|
||||||
|
-n, --namespace string namespace where cluster is located, if not specified default one will be used (default "default")
|
||||||
```
|
```
|
||||||
|
|
||||||
### Options inherited from parent commands
|
### Options inherited from parent commands
|
||||||
|
@ -30,7 +30,7 @@ var _ Interface = &Client{}
|
|||||||
type Interface interface {
|
type Interface interface {
|
||||||
Init(kubeconfigPath, kubeconfigContext string) error
|
Init(kubeconfigPath, kubeconfigContext string) error
|
||||||
Move(fromKubeconfigPath, fromKubeconfigContext, toKubeconfigPath, toKubeconfigContext, namespace string) error
|
Move(fromKubeconfigPath, fromKubeconfigContext, toKubeconfigPath, toKubeconfigContext, namespace string) error
|
||||||
GetKubeconfig(options GetKubeconfigOptions) (string, error)
|
GetKubeconfig(options *GetKubeconfigOptions) (string, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Client Implements interface to Clusterctl
|
// Client Implements interface to Clusterctl
|
||||||
@ -124,7 +124,7 @@ func newClusterctlClient(root string, options *airshipv1.Clusterctl) (clusterctl
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetKubeconfig is a wrapper for related cluster-api function
|
// GetKubeconfig is a wrapper for related cluster-api function
|
||||||
func (c *Client) GetKubeconfig(options GetKubeconfigOptions) (string, error) {
|
func (c *Client) GetKubeconfig(options *GetKubeconfigOptions) (string, error) {
|
||||||
return c.clusterctlClient.GetKubeconfig(clusterctlclient.GetKubeconfigOptions{
|
return c.clusterctlClient.GetKubeconfig(clusterctlclient.GetKubeconfigOptions{
|
||||||
Kubeconfig: clusterctlclient.Kubeconfig{
|
Kubeconfig: clusterctlclient.Kubeconfig{
|
||||||
Path: options.ParentKubeconfigPath,
|
Path: options.ParentKubeconfigPath,
|
||||||
|
@ -15,10 +15,13 @@
|
|||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"io"
|
||||||
|
|
||||||
airshipv1 "opendev.org/airship/airshipctl/pkg/api/v1alpha1"
|
airshipv1 "opendev.org/airship/airshipctl/pkg/api/v1alpha1"
|
||||||
"opendev.org/airship/airshipctl/pkg/clusterctl/client"
|
"opendev.org/airship/airshipctl/pkg/clusterctl/client"
|
||||||
"opendev.org/airship/airshipctl/pkg/config"
|
"opendev.org/airship/airshipctl/pkg/config"
|
||||||
"opendev.org/airship/airshipctl/pkg/document"
|
"opendev.org/airship/airshipctl/pkg/document"
|
||||||
|
"opendev.org/airship/airshipctl/pkg/k8s/kubeconfig"
|
||||||
"opendev.org/airship/airshipctl/pkg/log"
|
"opendev.org/airship/airshipctl/pkg/log"
|
||||||
"opendev.org/airship/airshipctl/pkg/phase"
|
"opendev.org/airship/airshipctl/pkg/phase"
|
||||||
)
|
)
|
||||||
@ -105,3 +108,22 @@ func (c *Command) Move(toKubeconfigContext string) error {
|
|||||||
}
|
}
|
||||||
return c.client.Move(c.kubeconfigPath, c.kubeconfigContext, c.kubeconfigPath, toKubeconfigContext, "")
|
return c.client.Move(c.kubeconfigPath, c.kubeconfigContext, c.kubeconfigPath, toKubeconfigContext, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetKubeconfig creates new kubeconfig interface object from secret and prints its content to writer
|
||||||
|
func GetKubeconfig(cfgFactory config.Factory, options *client.GetKubeconfigOptions, writer io.Writer) error {
|
||||||
|
cfg, err := cfgFactory()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
targetPath, err := cfg.CurrentContextTargetPath()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
client, err := client.NewClient(targetPath, log.DebugEnabled(), &airshipv1.Clusterctl{})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return kubeconfig.NewKubeConfig(kubeconfig.FromSecret(client, options)).Write(writer)
|
||||||
|
}
|
||||||
|
@ -14,10 +14,6 @@
|
|||||||
|
|
||||||
package kubeconfig
|
package kubeconfig
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
)
|
|
||||||
|
|
||||||
// ErrKubeConfigPathEmpty returned when kubeconfig path is not specified
|
// ErrKubeConfigPathEmpty returned when kubeconfig path is not specified
|
||||||
type ErrKubeConfigPathEmpty struct {
|
type ErrKubeConfigPathEmpty struct {
|
||||||
}
|
}
|
||||||
@ -25,27 +21,3 @@ type ErrKubeConfigPathEmpty struct {
|
|||||||
func (e *ErrKubeConfigPathEmpty) Error() string {
|
func (e *ErrKubeConfigPathEmpty) Error() string {
|
||||||
return "kubeconfig path is not defined"
|
return "kubeconfig path is not defined"
|
||||||
}
|
}
|
||||||
|
|
||||||
// ErrClusterNameEmpty returned when cluster name is not provided
|
|
||||||
type ErrClusterNameEmpty struct {
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e ErrClusterNameEmpty) Error() string {
|
|
||||||
return "cluster name is not defined"
|
|
||||||
}
|
|
||||||
|
|
||||||
// ErrMalformedSecret error returned if secret data value is lost or empty
|
|
||||||
type ErrMalformedSecret struct {
|
|
||||||
ClusterName string
|
|
||||||
Namespace string
|
|
||||||
SecretName string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e ErrMalformedSecret) Error() string {
|
|
||||||
return fmt.Sprintf(
|
|
||||||
"can't retrieve data from secret %s in cluster %s(namespace: %s)",
|
|
||||||
e.SecretName,
|
|
||||||
e.ClusterName,
|
|
||||||
e.Namespace,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
@ -21,6 +21,7 @@ import (
|
|||||||
"sigs.k8s.io/yaml"
|
"sigs.k8s.io/yaml"
|
||||||
|
|
||||||
"opendev.org/airship/airshipctl/pkg/api/v1alpha1"
|
"opendev.org/airship/airshipctl/pkg/api/v1alpha1"
|
||||||
|
"opendev.org/airship/airshipctl/pkg/clusterctl/client"
|
||||||
"opendev.org/airship/airshipctl/pkg/document"
|
"opendev.org/airship/airshipctl/pkg/document"
|
||||||
"opendev.org/airship/airshipctl/pkg/fs"
|
"opendev.org/airship/airshipctl/pkg/fs"
|
||||||
)
|
)
|
||||||
@ -90,9 +91,13 @@ func FromAPIalphaV1(apiObj *v1alpha1.KubeConfig) KubeSourceFunc {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// FromSecret returns KubeSource type, uses client interface to kubernetes cluster
|
// FromSecret returns KubeSource type, uses client interface to kubernetes cluster
|
||||||
func FromSecret(kubeOpts *FromClusterOptions) KubeSourceFunc {
|
func FromSecret(c client.Interface, o *client.GetKubeconfigOptions) KubeSourceFunc {
|
||||||
return func() ([]byte, error) {
|
return func() ([]byte, error) {
|
||||||
return GetKubeconfigFromSecret(kubeOpts)
|
data, err := c.GetKubeconfig(o)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return []byte(data), nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -16,6 +16,7 @@ package kubeconfig_test
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
@ -23,22 +24,17 @@ import (
|
|||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
coreV1 "k8s.io/api/core/v1"
|
|
||||||
metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
||||||
v1 "k8s.io/client-go/tools/clientcmd/api/v1"
|
v1 "k8s.io/client-go/tools/clientcmd/api/v1"
|
||||||
kustfs "sigs.k8s.io/kustomize/api/filesys"
|
kustfs "sigs.k8s.io/kustomize/api/filesys"
|
||||||
|
|
||||||
"opendev.org/airship/airshipctl/pkg/api/v1alpha1"
|
"opendev.org/airship/airshipctl/pkg/api/v1alpha1"
|
||||||
|
"opendev.org/airship/airshipctl/pkg/clusterctl/client"
|
||||||
"opendev.org/airship/airshipctl/pkg/fs"
|
"opendev.org/airship/airshipctl/pkg/fs"
|
||||||
"opendev.org/airship/airshipctl/pkg/k8s/client/fake"
|
|
||||||
"opendev.org/airship/airshipctl/pkg/k8s/kubeconfig"
|
"opendev.org/airship/airshipctl/pkg/k8s/kubeconfig"
|
||||||
testfs "opendev.org/airship/airshipctl/testutil/fs"
|
testfs "opendev.org/airship/airshipctl/testutil/fs"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
testClusterName = "dummy_target_cluster"
|
|
||||||
testSecretName = testClusterName + "-kubeconfig"
|
|
||||||
testNamespace = "default"
|
|
||||||
testValidKubeconfig = `apiVersion: v1
|
testValidKubeconfig = `apiVersion: v1
|
||||||
clusters:
|
clusters:
|
||||||
- cluster:
|
- cluster:
|
||||||
@ -136,166 +132,46 @@ func TestKubeconfigContent(t *testing.T) {
|
|||||||
assert.Equal(t, expectedData, actualData)
|
assert.Equal(t, expectedData, actualData)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type MockClientInterface struct {
|
||||||
|
MockGetKubeconfig func(options *client.GetKubeconfigOptions) (string, error)
|
||||||
|
client.Interface
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c MockClientInterface) GetKubeconfig(o *client.GetKubeconfigOptions) (string, error) {
|
||||||
|
return c.MockGetKubeconfig(o)
|
||||||
|
}
|
||||||
|
|
||||||
func TestFromSecret(t *testing.T) {
|
func TestFromSecret(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
opts *kubeconfig.FromClusterOptions
|
expectedData string
|
||||||
acc fake.ResourceAccumulator
|
|
||||||
expectedData []byte
|
|
||||||
err error
|
err error
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "valid kubeconfig",
|
name: "valid kubeconfig",
|
||||||
opts: &kubeconfig.FromClusterOptions{
|
expectedData: testValidKubeconfig,
|
||||||
ClusterName: testClusterName,
|
|
||||||
Namespace: testNamespace,
|
|
||||||
},
|
|
||||||
acc: fake.WithTypedObjects(&coreV1.Secret{
|
|
||||||
TypeMeta: metaV1.TypeMeta{
|
|
||||||
Kind: "Secret",
|
|
||||||
APIVersion: "v1",
|
|
||||||
},
|
|
||||||
ObjectMeta: metaV1.ObjectMeta{
|
|
||||||
Name: testSecretName,
|
|
||||||
Namespace: testNamespace,
|
|
||||||
},
|
|
||||||
Data: map[string][]byte{
|
|
||||||
"value": []byte(testValidKubeconfig),
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
expectedData: []byte(testValidKubeconfig),
|
|
||||||
err: nil,
|
err: nil,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "no cluster name",
|
name: "failed to get kubeconfig",
|
||||||
opts: &kubeconfig.FromClusterOptions{
|
expectedData: "",
|
||||||
ClusterName: "",
|
err: errors.New("error"),
|
||||||
Namespace: testNamespace,
|
|
||||||
},
|
|
||||||
acc: fake.WithTypedObjects(&coreV1.Secret{
|
|
||||||
TypeMeta: metaV1.TypeMeta{
|
|
||||||
Kind: "Secret",
|
|
||||||
APIVersion: "v1",
|
|
||||||
},
|
|
||||||
ObjectMeta: metaV1.ObjectMeta{
|
|
||||||
Name: testSecretName,
|
|
||||||
Namespace: testNamespace,
|
|
||||||
},
|
|
||||||
Data: map[string][]byte{
|
|
||||||
"value": []byte(testValidKubeconfig),
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
expectedData: nil,
|
|
||||||
err: kubeconfig.ErrClusterNameEmpty{},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "default namespace",
|
|
||||||
opts: &kubeconfig.FromClusterOptions{
|
|
||||||
ClusterName: testClusterName,
|
|
||||||
Namespace: "",
|
|
||||||
},
|
|
||||||
acc: fake.WithTypedObjects(&coreV1.Secret{
|
|
||||||
TypeMeta: metaV1.TypeMeta{
|
|
||||||
Kind: "Secret",
|
|
||||||
APIVersion: "v1",
|
|
||||||
},
|
|
||||||
ObjectMeta: metaV1.ObjectMeta{
|
|
||||||
Name: testSecretName,
|
|
||||||
Namespace: testNamespace,
|
|
||||||
},
|
|
||||||
Data: map[string][]byte{
|
|
||||||
"value": []byte(testValidKubeconfig),
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
expectedData: []byte(testValidKubeconfig),
|
|
||||||
err: nil,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "no data in secret",
|
|
||||||
opts: &kubeconfig.FromClusterOptions{
|
|
||||||
ClusterName: testClusterName,
|
|
||||||
Namespace: testNamespace,
|
|
||||||
},
|
|
||||||
acc: fake.WithTypedObjects(&coreV1.Secret{
|
|
||||||
TypeMeta: metaV1.TypeMeta{
|
|
||||||
Kind: "Secret",
|
|
||||||
APIVersion: "v1",
|
|
||||||
},
|
|
||||||
ObjectMeta: metaV1.ObjectMeta{
|
|
||||||
Name: testSecretName,
|
|
||||||
Namespace: testNamespace,
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
expectedData: nil,
|
|
||||||
err: kubeconfig.ErrMalformedSecret{
|
|
||||||
ClusterName: testClusterName,
|
|
||||||
Namespace: testNamespace,
|
|
||||||
SecretName: testSecretName,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "empty data in secret",
|
|
||||||
opts: &kubeconfig.FromClusterOptions{
|
|
||||||
ClusterName: testClusterName,
|
|
||||||
Namespace: testNamespace,
|
|
||||||
},
|
|
||||||
acc: fake.WithTypedObjects(&coreV1.Secret{
|
|
||||||
TypeMeta: metaV1.TypeMeta{
|
|
||||||
Kind: "Secret",
|
|
||||||
APIVersion: "v1",
|
|
||||||
},
|
|
||||||
ObjectMeta: metaV1.ObjectMeta{
|
|
||||||
Name: testSecretName,
|
|
||||||
Namespace: testNamespace,
|
|
||||||
},
|
|
||||||
Data: map[string][]byte{},
|
|
||||||
}),
|
|
||||||
expectedData: nil,
|
|
||||||
err: kubeconfig.ErrMalformedSecret{
|
|
||||||
ClusterName: testClusterName,
|
|
||||||
Namespace: testNamespace,
|
|
||||||
SecretName: testSecretName,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "empty value in data in secret",
|
|
||||||
opts: &kubeconfig.FromClusterOptions{
|
|
||||||
ClusterName: testClusterName,
|
|
||||||
Namespace: testNamespace,
|
|
||||||
},
|
|
||||||
acc: fake.WithTypedObjects(&coreV1.Secret{
|
|
||||||
TypeMeta: metaV1.TypeMeta{
|
|
||||||
Kind: "Secret",
|
|
||||||
APIVersion: "v1",
|
|
||||||
},
|
|
||||||
ObjectMeta: metaV1.ObjectMeta{
|
|
||||||
Name: testSecretName,
|
|
||||||
Namespace: testNamespace,
|
|
||||||
},
|
|
||||||
Data: map[string][]byte{
|
|
||||||
"value": []byte(""),
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
expectedData: nil,
|
|
||||||
err: kubeconfig.ErrMalformedSecret{
|
|
||||||
ClusterName: testClusterName,
|
|
||||||
Namespace: testNamespace,
|
|
||||||
SecretName: testSecretName,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
tt := tt
|
tt := tt
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
tt.opts.Client = fake.NewClient(tt.acc)
|
cl := MockClientInterface{
|
||||||
kubeconf, err := kubeconfig.FromSecret(tt.opts)()
|
MockGetKubeconfig: func(_ *client.GetKubeconfigOptions) (string, error) { return tt.expectedData, tt.err },
|
||||||
|
}
|
||||||
|
kubeconf, err := kubeconfig.FromSecret(cl, nil)()
|
||||||
if tt.err != nil {
|
if tt.err != nil {
|
||||||
assert.Equal(t, tt.err, err)
|
require.Error(t, err)
|
||||||
assert.Nil(t, kubeconf)
|
assert.Nil(t, kubeconf)
|
||||||
} else {
|
} else {
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.Equal(t, tt.expectedData, kubeconf)
|
assert.Equal(t, []byte(tt.expectedData), kubeconf)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -1,72 +0,0 @@
|
|||||||
/*
|
|
||||||
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 kubeconfig
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
||||||
|
|
||||||
"opendev.org/airship/airshipctl/pkg/k8s/client"
|
|
||||||
"opendev.org/airship/airshipctl/pkg/log"
|
|
||||||
)
|
|
||||||
|
|
||||||
// FromClusterOptions holds all configurable options for kubeconfig extraction
|
|
||||||
type FromClusterOptions struct {
|
|
||||||
ClusterName string
|
|
||||||
Namespace string
|
|
||||||
Client client.Interface
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetKubeconfigFromSecret extracts kubeconfig from secret data structure
|
|
||||||
func GetKubeconfigFromSecret(o *FromClusterOptions) ([]byte, error) {
|
|
||||||
const defaultNamespace = "default"
|
|
||||||
|
|
||||||
if o.ClusterName == "" {
|
|
||||||
return nil, ErrClusterNameEmpty{}
|
|
||||||
}
|
|
||||||
if o.Namespace == "" {
|
|
||||||
log.Printf("Namespace is not provided, using default one")
|
|
||||||
o.Namespace = defaultNamespace
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Debugf("Extracting kubeconfig from secret in cluster %s(namespace: %s)", o.ClusterName, o.Namespace)
|
|
||||||
secretName := fmt.Sprintf("%s-kubeconfig", o.ClusterName)
|
|
||||||
kubeCore := o.Client.ClientSet().CoreV1()
|
|
||||||
|
|
||||||
secret, err := kubeCore.Secrets(o.Namespace).Get(secretName, metav1.GetOptions{})
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if secret.Data == nil {
|
|
||||||
return nil, ErrMalformedSecret{
|
|
||||||
ClusterName: o.ClusterName,
|
|
||||||
Namespace: o.Namespace,
|
|
||||||
SecretName: secretName,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val, exist := secret.Data["value"]
|
|
||||||
if !exist || len(val) == 0 {
|
|
||||||
return nil, ErrMalformedSecret{
|
|
||||||
ClusterName: o.ClusterName,
|
|
||||||
Namespace: o.Namespace,
|
|
||||||
SecretName: secretName,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return val, nil
|
|
||||||
}
|
|
Loading…
x
Reference in New Issue
Block a user