Allow to specify multiple clusters per get-kubeconfig request
Change-Id: I1b736a4b9cae4e6e47ddb7909a8fd619518e975c Signed-off-by: Ruslan Aliev <raliev@mirantis.com> Closes: #567
This commit is contained in:
parent
f74a935df6
commit
b8ddc1fe43
@ -23,11 +23,14 @@ import (
|
|||||||
|
|
||||||
const (
|
const (
|
||||||
getKubeconfigLong = `
|
getKubeconfigLong = `
|
||||||
Retrieves kubeconfig of the cluster and prints it to stdout.
|
Retrieves kubeconfig of the cluster(s) and prints it to stdout.
|
||||||
|
|
||||||
If you specify CLUSTER_NAME, kubeconfig will have a CurrentContext set to CLUSTER_NAME and
|
If you specify single CLUSTER_NAME, kubeconfig will have a CurrentContext set to CLUSTER_NAME and
|
||||||
will have its context defined.
|
will have its context defined.
|
||||||
|
|
||||||
|
If you specify multiple CLUSTER_NAME args, kubeconfig will contain contexts for all of them, but current one
|
||||||
|
won't be specified.
|
||||||
|
|
||||||
If you don't specify CLUSTER_NAME, kubeconfig will have multiple contexts for every cluster
|
If you don't specify CLUSTER_NAME, kubeconfig will have multiple contexts for every cluster
|
||||||
in the airship site. Context names will correspond to cluster names. CurrentContext will be empty.
|
in the airship site. Context names will correspond to cluster names. CurrentContext will be empty.
|
||||||
`
|
`
|
||||||
@ -52,8 +55,8 @@ Airshipctl will overwrite the contents of the file, if you want merge with exist
|
|||||||
func NewGetKubeconfigCommand(cfgFactory config.Factory) *cobra.Command {
|
func NewGetKubeconfigCommand(cfgFactory config.Factory) *cobra.Command {
|
||||||
opts := &cluster.GetKubeconfigCommand{}
|
opts := &cluster.GetKubeconfigCommand{}
|
||||||
cmd := &cobra.Command{
|
cmd := &cobra.Command{
|
||||||
Use: "get-kubeconfig CLUSTER_NAME",
|
Use: "get-kubeconfig [CLUSTER_NAME...]",
|
||||||
Short: "Airshipctl command to retrieve kubeconfig for a desired cluster",
|
Short: "Airshipctl command to retrieve kubeconfig for a desired cluster(s)",
|
||||||
Long: getKubeconfigLong[1:],
|
Long: getKubeconfigLong[1:],
|
||||||
Args: GetKubeconfArgs(opts),
|
Args: GetKubeconfArgs(opts),
|
||||||
Example: getKubeconfigExample,
|
Example: getKubeconfigExample,
|
||||||
@ -82,9 +85,10 @@ func NewGetKubeconfigCommand(cfgFactory config.Factory) *cobra.Command {
|
|||||||
// GetKubeconfArgs extracts one or less arguments from command line, and saves it as name
|
// GetKubeconfArgs extracts one or less arguments from command line, and saves it as name
|
||||||
func GetKubeconfArgs(opts *cluster.GetKubeconfigCommand) cobra.PositionalArgs {
|
func GetKubeconfArgs(opts *cluster.GetKubeconfigCommand) cobra.PositionalArgs {
|
||||||
return func(cmd *cobra.Command, args []string) error {
|
return func(cmd *cobra.Command, args []string) error {
|
||||||
if len(args) == 1 {
|
for _, arg := range args {
|
||||||
opts.ClusterName = args[0]
|
opts.ClusterNames = append(opts.ClusterNames, arg)
|
||||||
}
|
}
|
||||||
return cobra.MaximumNArgs(1)(cmd, args)
|
|
||||||
|
return cobra.MinimumNArgs(0)(cmd, args)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -40,23 +40,23 @@ func TestNewKubeConfigCommandCmd(t *testing.T) {
|
|||||||
|
|
||||||
func TestGetKubeconfArgs(t *testing.T) {
|
func TestGetKubeconfArgs(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
args []string
|
args []string
|
||||||
expectedErrStr string
|
expectedErrStr string
|
||||||
expectedClusterName string
|
expectedClusterNames []string
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "success one cluster specified",
|
name: "success one cluster specified",
|
||||||
args: []string{"cluster01"},
|
args: []string{"cluster01"},
|
||||||
expectedClusterName: "cluster01",
|
expectedClusterNames: []string{"cluster01"},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "success no cluster specified",
|
name: "success no cluster specified",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "error two cluster specified",
|
name: "success two cluster specified",
|
||||||
expectedErrStr: "accepts at most 1 arg(s)",
|
args: []string{"cluster01", "cluster02"},
|
||||||
args: []string{"cluster01", "cluster02"},
|
expectedClusterNames: []string{"cluster01", "cluster02"},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
@ -70,7 +70,7 @@ func TestGetKubeconfArgs(t *testing.T) {
|
|||||||
assert.Contains(t, err.Error(), tt.expectedErrStr)
|
assert.Contains(t, err.Error(), tt.expectedErrStr)
|
||||||
} else {
|
} else {
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, tt.expectedClusterName, cmd.ClusterName)
|
assert.Equal(t, tt.expectedClusterNames, cmd.ClusterNames)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,7 @@ Usage:
|
|||||||
cluster [command]
|
cluster [command]
|
||||||
|
|
||||||
Available Commands:
|
Available Commands:
|
||||||
get-kubeconfig Airshipctl command to retrieve kubeconfig for a desired cluster
|
get-kubeconfig Airshipctl command to retrieve kubeconfig for a desired cluster(s)
|
||||||
help Help about any command
|
help Help about any command
|
||||||
list Airshipctl command to get and list defined clusters
|
list Airshipctl command to get and list defined clusters
|
||||||
status Retrieve statuses of deployed cluster components
|
status Retrieve statuses of deployed cluster components
|
||||||
|
@ -1,13 +1,16 @@
|
|||||||
Retrieves kubeconfig of the cluster and prints it to stdout.
|
Retrieves kubeconfig of the cluster(s) and prints it to stdout.
|
||||||
|
|
||||||
If you specify CLUSTER_NAME, kubeconfig will have a CurrentContext set to CLUSTER_NAME and
|
If you specify single CLUSTER_NAME, kubeconfig will have a CurrentContext set to CLUSTER_NAME and
|
||||||
will have its context defined.
|
will have its context defined.
|
||||||
|
|
||||||
|
If you specify multiple CLUSTER_NAME args, kubeconfig will contain contexts for all of them, but current one
|
||||||
|
won't be specified.
|
||||||
|
|
||||||
If you don't specify CLUSTER_NAME, kubeconfig will have multiple contexts for every cluster
|
If you don't specify CLUSTER_NAME, kubeconfig will have multiple contexts for every cluster
|
||||||
in the airship site. Context names will correspond to cluster names. CurrentContext will be empty.
|
in the airship site. Context names will correspond to cluster names. CurrentContext will be empty.
|
||||||
|
|
||||||
Usage:
|
Usage:
|
||||||
get-kubeconfig CLUSTER_NAME [flags]
|
get-kubeconfig [CLUSTER_NAME...] [flags]
|
||||||
|
|
||||||
Examples:
|
Examples:
|
||||||
|
|
||||||
|
@ -32,7 +32,7 @@ SEE ALSO
|
|||||||
~~~~~~~~
|
~~~~~~~~
|
||||||
|
|
||||||
* :ref:`airshipctl <airshipctl>` - A unified command line tool for management of end-to-end kubernetes cluster deployment on cloud infrastructure environments.
|
* :ref:`airshipctl <airshipctl>` - A unified command line tool for management of end-to-end kubernetes cluster deployment on cloud infrastructure environments.
|
||||||
* :ref:`airshipctl cluster get-kubeconfig <airshipctl_cluster_get-kubeconfig>` - Airshipctl command to retrieve kubeconfig for a desired cluster
|
* :ref:`airshipctl cluster get-kubeconfig <airshipctl_cluster_get-kubeconfig>` - Airshipctl command to retrieve kubeconfig for a desired cluster(s)
|
||||||
* :ref:`airshipctl cluster list <airshipctl_cluster_list>` - Airshipctl command to get and list defined clusters
|
* :ref:`airshipctl cluster list <airshipctl_cluster_list>` - Airshipctl command to get and list defined clusters
|
||||||
* :ref:`airshipctl cluster status <airshipctl_cluster_status>` - Retrieve statuses of deployed cluster components
|
* :ref:`airshipctl cluster status <airshipctl_cluster_status>` - Retrieve statuses of deployed cluster components
|
||||||
|
|
||||||
|
@ -3,24 +3,27 @@
|
|||||||
airshipctl cluster get-kubeconfig
|
airshipctl cluster get-kubeconfig
|
||||||
---------------------------------
|
---------------------------------
|
||||||
|
|
||||||
Airshipctl command to retrieve kubeconfig for a desired cluster
|
Airshipctl command to retrieve kubeconfig for a desired cluster(s)
|
||||||
|
|
||||||
Synopsis
|
Synopsis
|
||||||
~~~~~~~~
|
~~~~~~~~
|
||||||
|
|
||||||
|
|
||||||
Retrieves kubeconfig of the cluster and prints it to stdout.
|
Retrieves kubeconfig of the cluster(s) and prints it to stdout.
|
||||||
|
|
||||||
If you specify CLUSTER_NAME, kubeconfig will have a CurrentContext set to CLUSTER_NAME and
|
If you specify single CLUSTER_NAME, kubeconfig will have a CurrentContext set to CLUSTER_NAME and
|
||||||
will have its context defined.
|
will have its context defined.
|
||||||
|
|
||||||
|
If you specify multiple CLUSTER_NAME args, kubeconfig will contain contexts for all of them, but current one
|
||||||
|
won't be specified.
|
||||||
|
|
||||||
If you don't specify CLUSTER_NAME, kubeconfig will have multiple contexts for every cluster
|
If you don't specify CLUSTER_NAME, kubeconfig will have multiple contexts for every cluster
|
||||||
in the airship site. Context names will correspond to cluster names. CurrentContext will be empty.
|
in the airship site. Context names will correspond to cluster names. CurrentContext will be empty.
|
||||||
|
|
||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
airshipctl cluster get-kubeconfig CLUSTER_NAME [flags]
|
airshipctl cluster get-kubeconfig [CLUSTER_NAME...] [flags]
|
||||||
|
|
||||||
Examples
|
Examples
|
||||||
~~~~~~~~
|
~~~~~~~~
|
||||||
|
@ -56,9 +56,9 @@ func StatusRunner(o StatusOptions, w io.Writer) error {
|
|||||||
|
|
||||||
// GetKubeconfigCommand holds options for get kubeconfig command
|
// GetKubeconfigCommand holds options for get kubeconfig command
|
||||||
type GetKubeconfigCommand struct {
|
type GetKubeconfigCommand struct {
|
||||||
ClusterName string
|
ClusterNames []string
|
||||||
File string
|
File string
|
||||||
Merge bool
|
Merge bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// RunE creates new kubeconfig interface object from secret, options hold the writer and merge(bool)
|
// RunE creates new kubeconfig interface object from secret, options hold the writer and merge(bool)
|
||||||
@ -80,14 +80,14 @@ func (cmd *GetKubeconfigCommand) RunE(cfgFactory config.Factory, writer io.Write
|
|||||||
}
|
}
|
||||||
|
|
||||||
var siteWide bool
|
var siteWide bool
|
||||||
if cmd.ClusterName == "" {
|
if len(cmd.ClusterNames) == 0 {
|
||||||
siteWide = true
|
siteWide = true
|
||||||
}
|
}
|
||||||
|
|
||||||
kubeconf := kubeconfig.NewBuilder().
|
kubeconf := kubeconfig.NewBuilder().
|
||||||
WithBundle(helper.PhaseConfigBundle()).
|
WithBundle(helper.PhaseConfigBundle()).
|
||||||
WithClusterMap(cMap).
|
WithClusterMap(cMap).
|
||||||
WithClusterName(cmd.ClusterName).
|
WithClusterNames(cmd.ClusterNames...).
|
||||||
WithTempRoot(helper.WorkDir()).
|
WithTempRoot(helper.WorkDir()).
|
||||||
SiteWide(siteWide).
|
SiteWide(siteWide).
|
||||||
Build()
|
Build()
|
||||||
|
@ -39,9 +39,9 @@ func NewBuilder() *Builder {
|
|||||||
// Builder is an object that allows to build a kubeconfig based on various provided sources
|
// Builder is an object that allows to build a kubeconfig based on various provided sources
|
||||||
// such as path to kubeconfig, path to bundle that should contain kubeconfig and parent cluster
|
// such as path to kubeconfig, path to bundle that should contain kubeconfig and parent cluster
|
||||||
type Builder struct {
|
type Builder struct {
|
||||||
siteWide bool
|
siteWide bool
|
||||||
clusterName string
|
clusterNames []string
|
||||||
root string
|
root string
|
||||||
|
|
||||||
bundle document.Bundle
|
bundle document.Bundle
|
||||||
client corev1.CoreV1Interface
|
client corev1.CoreV1Interface
|
||||||
@ -62,9 +62,9 @@ func (b *Builder) WithClusterMap(cMap clustermap.ClusterMap) *Builder {
|
|||||||
return b
|
return b
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithClusterName allows to reach to a cluster to download kubeconfig from there
|
// WithClusterNames allows to reach to a cluster to download kubeconfig from there
|
||||||
func (b *Builder) WithClusterName(clusterName string) *Builder {
|
func (b *Builder) WithClusterNames(clusterNames ...string) *Builder {
|
||||||
b.clusterName = clusterName
|
b.clusterNames = clusterNames
|
||||||
return b
|
return b
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -106,40 +106,31 @@ func (b *Builder) Build() Interface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (b *Builder) build() ([]byte, error) {
|
func (b *Builder) build() ([]byte, error) {
|
||||||
// Set current context to clustername if it was provided
|
if err := b.buildKubeconfig(); err != nil {
|
||||||
var result *api.Config
|
return nil, err
|
||||||
var err error
|
|
||||||
if !b.siteWide {
|
|
||||||
var kubeContext string
|
|
||||||
kubeContext, result, err = b.buildOne(b.clusterName)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
b.siteKubeconf.CurrentContext = kubeContext
|
|
||||||
} else {
|
|
||||||
result, err = b.builtSiteKubeconf()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return clientcmd.Write(*result)
|
|
||||||
|
return clientcmd.Write(*b.siteKubeconf)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *Builder) builtSiteKubeconf() (*api.Config, error) {
|
func (b *Builder) buildKubeconfig() error {
|
||||||
log.Debugf("Getting site kubeconfig")
|
log.Debugf("Getting requested kubeconfig")
|
||||||
for _, clusterID := range b.clusterMap.AllClusters() {
|
for _, clusterID := range (map[bool][]string{true: b.clusterMap.AllClusters(), false: b.clusterNames})[b.siteWide] {
|
||||||
log.Debugf("Getting kubeconfig for cluster '%s' to build site kubeconfig", clusterID)
|
log.Debugf("Getting kubeconfig for cluster '%s'", clusterID)
|
||||||
// buildOne merges context into site kubeconfig
|
// buildOne merges context into site kubeconfig
|
||||||
_, _, err := b.buildOne(clusterID)
|
ctx, _, err := b.buildOne(clusterID)
|
||||||
|
if !b.siteWide && len(b.clusterNames) == 1 {
|
||||||
|
b.siteKubeconf.CurrentContext = ctx
|
||||||
|
}
|
||||||
if IsErrAllSourcesFailedErr(err) {
|
if IsErrAllSourcesFailedErr(err) {
|
||||||
log.Debugf("All kubeconfig sources failed for cluster '%s', error '%v', skipping it",
|
log.Debugf("All kubeconfig sources failed for cluster '%s', error '%v', skipping it",
|
||||||
clusterID, err)
|
clusterID, err)
|
||||||
continue
|
continue
|
||||||
} else if err != nil {
|
} else if err != nil {
|
||||||
return nil, err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return b.siteKubeconf, nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *Builder) buildOne(clusterID string) (string, *api.Config, error) {
|
func (b *Builder) buildOne(clusterID string) (string, *api.Config, error) {
|
||||||
|
@ -260,7 +260,7 @@ func TestBuilderClusterctl(t *testing.T) {
|
|||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
kube := kubeconfig.NewBuilder().
|
kube := kubeconfig.NewBuilder().
|
||||||
WithClusterMap(tt.clusterMap).
|
WithClusterMap(tt.clusterMap).
|
||||||
WithClusterName(tt.requestedClusterName).
|
WithClusterNames(tt.requestedClusterName).
|
||||||
WithBundle(testBundle).
|
WithBundle(testBundle).
|
||||||
WithTempRoot(tt.tempRoot).
|
WithTempRoot(tt.tempRoot).
|
||||||
WithCoreV1Client(tt.client).
|
WithCoreV1Client(tt.client).
|
||||||
|
@ -105,7 +105,7 @@ func (p *phase) executor(docFactory document.DocFactoryFunc,
|
|||||||
WithBundle(p.helper.PhaseConfigBundle()).
|
WithBundle(p.helper.PhaseConfigBundle()).
|
||||||
WithClusterMap(cMap).
|
WithClusterMap(cMap).
|
||||||
WithTempRoot(p.helper.WorkDir()).
|
WithTempRoot(p.helper.WorkDir()).
|
||||||
WithClusterName(p.apiObj.ClusterName).
|
WithClusterNames(p.apiObj.ClusterName).
|
||||||
SiteWide(p.apiObj.Config.SiteWideKubeconfig).
|
SiteWide(p.apiObj.Config.SiteWideKubeconfig).
|
||||||
Build()
|
Build()
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user