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 (
|
||||
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.
|
||||
|
||||
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
|
||||
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 {
|
||||
opts := &cluster.GetKubeconfigCommand{}
|
||||
cmd := &cobra.Command{
|
||||
Use: "get-kubeconfig CLUSTER_NAME",
|
||||
Short: "Airshipctl command to retrieve kubeconfig for a desired cluster",
|
||||
Use: "get-kubeconfig [CLUSTER_NAME...]",
|
||||
Short: "Airshipctl command to retrieve kubeconfig for a desired cluster(s)",
|
||||
Long: getKubeconfigLong[1:],
|
||||
Args: GetKubeconfArgs(opts),
|
||||
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
|
||||
func GetKubeconfArgs(opts *cluster.GetKubeconfigCommand) cobra.PositionalArgs {
|
||||
return func(cmd *cobra.Command, args []string) error {
|
||||
if len(args) == 1 {
|
||||
opts.ClusterName = args[0]
|
||||
for _, arg := range args {
|
||||
opts.ClusterNames = append(opts.ClusterNames, arg)
|
||||
}
|
||||
return cobra.MaximumNArgs(1)(cmd, args)
|
||||
|
||||
return cobra.MinimumNArgs(0)(cmd, args)
|
||||
}
|
||||
}
|
||||
|
@ -43,20 +43,20 @@ func TestGetKubeconfArgs(t *testing.T) {
|
||||
name string
|
||||
args []string
|
||||
expectedErrStr string
|
||||
expectedClusterName string
|
||||
expectedClusterNames []string
|
||||
}{
|
||||
{
|
||||
name: "success one cluster specified",
|
||||
args: []string{"cluster01"},
|
||||
expectedClusterName: "cluster01",
|
||||
expectedClusterNames: []string{"cluster01"},
|
||||
},
|
||||
{
|
||||
name: "success no cluster specified",
|
||||
},
|
||||
{
|
||||
name: "error two cluster specified",
|
||||
expectedErrStr: "accepts at most 1 arg(s)",
|
||||
name: "success two cluster specified",
|
||||
args: []string{"cluster01", "cluster02"},
|
||||
expectedClusterNames: []string{"cluster01", "cluster02"},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
@ -70,7 +70,7 @@ func TestGetKubeconfArgs(t *testing.T) {
|
||||
assert.Contains(t, err.Error(), tt.expectedErrStr)
|
||||
} else {
|
||||
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]
|
||||
|
||||
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
|
||||
list Airshipctl command to get and list defined clusters
|
||||
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.
|
||||
|
||||
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
|
||||
in the airship site. Context names will correspond to cluster names. CurrentContext will be empty.
|
||||
|
||||
Usage:
|
||||
get-kubeconfig CLUSTER_NAME [flags]
|
||||
get-kubeconfig [CLUSTER_NAME...] [flags]
|
||||
|
||||
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 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 status <airshipctl_cluster_status>` - Retrieve statuses of deployed cluster components
|
||||
|
||||
|
@ -3,24 +3,27 @@
|
||||
airshipctl cluster get-kubeconfig
|
||||
---------------------------------
|
||||
|
||||
Airshipctl command to retrieve kubeconfig for a desired cluster
|
||||
Airshipctl command to retrieve kubeconfig for a desired cluster(s)
|
||||
|
||||
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.
|
||||
|
||||
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
|
||||
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
|
||||
~~~~~~~~
|
||||
|
@ -56,7 +56,7 @@ func StatusRunner(o StatusOptions, w io.Writer) error {
|
||||
|
||||
// GetKubeconfigCommand holds options for get kubeconfig command
|
||||
type GetKubeconfigCommand struct {
|
||||
ClusterName string
|
||||
ClusterNames []string
|
||||
File string
|
||||
Merge bool
|
||||
}
|
||||
@ -80,14 +80,14 @@ func (cmd *GetKubeconfigCommand) RunE(cfgFactory config.Factory, writer io.Write
|
||||
}
|
||||
|
||||
var siteWide bool
|
||||
if cmd.ClusterName == "" {
|
||||
if len(cmd.ClusterNames) == 0 {
|
||||
siteWide = true
|
||||
}
|
||||
|
||||
kubeconf := kubeconfig.NewBuilder().
|
||||
WithBundle(helper.PhaseConfigBundle()).
|
||||
WithClusterMap(cMap).
|
||||
WithClusterName(cmd.ClusterName).
|
||||
WithClusterNames(cmd.ClusterNames...).
|
||||
WithTempRoot(helper.WorkDir()).
|
||||
SiteWide(siteWide).
|
||||
Build()
|
||||
|
@ -40,7 +40,7 @@ func NewBuilder() *Builder {
|
||||
// such as path to kubeconfig, path to bundle that should contain kubeconfig and parent cluster
|
||||
type Builder struct {
|
||||
siteWide bool
|
||||
clusterName string
|
||||
clusterNames []string
|
||||
root string
|
||||
|
||||
bundle document.Bundle
|
||||
@ -62,9 +62,9 @@ func (b *Builder) WithClusterMap(cMap clustermap.ClusterMap) *Builder {
|
||||
return b
|
||||
}
|
||||
|
||||
// WithClusterName allows to reach to a cluster to download kubeconfig from there
|
||||
func (b *Builder) WithClusterName(clusterName string) *Builder {
|
||||
b.clusterName = clusterName
|
||||
// WithClusterNames allows to reach to a cluster to download kubeconfig from there
|
||||
func (b *Builder) WithClusterNames(clusterNames ...string) *Builder {
|
||||
b.clusterNames = clusterNames
|
||||
return b
|
||||
}
|
||||
|
||||
@ -106,40 +106,31 @@ func (b *Builder) Build() Interface {
|
||||
}
|
||||
|
||||
func (b *Builder) build() ([]byte, error) {
|
||||
// Set current context to clustername if it was provided
|
||||
var result *api.Config
|
||||
var err error
|
||||
if !b.siteWide {
|
||||
var kubeContext string
|
||||
kubeContext, result, err = b.buildOne(b.clusterName)
|
||||
if err != nil {
|
||||
if err := b.buildKubeconfig(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
b.siteKubeconf.CurrentContext = kubeContext
|
||||
} else {
|
||||
result, err = b.builtSiteKubeconf()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return clientcmd.Write(*result)
|
||||
}
|
||||
|
||||
func (b *Builder) builtSiteKubeconf() (*api.Config, error) {
|
||||
log.Debugf("Getting site kubeconfig")
|
||||
for _, clusterID := range b.clusterMap.AllClusters() {
|
||||
log.Debugf("Getting kubeconfig for cluster '%s' to build site kubeconfig", clusterID)
|
||||
return clientcmd.Write(*b.siteKubeconf)
|
||||
}
|
||||
|
||||
func (b *Builder) buildKubeconfig() error {
|
||||
log.Debugf("Getting requested kubeconfig")
|
||||
for _, clusterID := range (map[bool][]string{true: b.clusterMap.AllClusters(), false: b.clusterNames})[b.siteWide] {
|
||||
log.Debugf("Getting kubeconfig for cluster '%s'", clusterID)
|
||||
// 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) {
|
||||
log.Debugf("All kubeconfig sources failed for cluster '%s', error '%v', skipping it",
|
||||
clusterID, err)
|
||||
continue
|
||||
} 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) {
|
||||
|
@ -260,7 +260,7 @@ func TestBuilderClusterctl(t *testing.T) {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
kube := kubeconfig.NewBuilder().
|
||||
WithClusterMap(tt.clusterMap).
|
||||
WithClusterName(tt.requestedClusterName).
|
||||
WithClusterNames(tt.requestedClusterName).
|
||||
WithBundle(testBundle).
|
||||
WithTempRoot(tt.tempRoot).
|
||||
WithCoreV1Client(tt.client).
|
||||
|
@ -105,7 +105,7 @@ func (p *phase) executor(docFactory document.DocFactoryFunc,
|
||||
WithBundle(p.helper.PhaseConfigBundle()).
|
||||
WithClusterMap(cMap).
|
||||
WithTempRoot(p.helper.WorkDir()).
|
||||
WithClusterName(p.apiObj.ClusterName).
|
||||
WithClusterNames(p.apiObj.ClusterName).
|
||||
SiteWide(p.apiObj.Config.SiteWideKubeconfig).
|
||||
Build()
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user