Merge "[#323] Cluster list command to output in table/clusternames."
This commit is contained in:
commit
6a62e11fea
@ -26,6 +26,8 @@ const (
|
|||||||
listExample = `
|
listExample = `
|
||||||
# Retrieve cluster list
|
# Retrieve cluster list
|
||||||
airshipctl cluster list --airshipconf /tmp/airconfig
|
airshipctl cluster list --airshipconf /tmp/airconfig
|
||||||
|
airshipctl cluster list -o table
|
||||||
|
airshipctl cluster list -o name
|
||||||
`
|
`
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -38,6 +40,11 @@ func NewListCommand(cfgFactory config.Factory) *cobra.Command {
|
|||||||
Example: listExample[1:],
|
Example: listExample[1:],
|
||||||
RunE: listRunE(o),
|
RunE: listRunE(o),
|
||||||
}
|
}
|
||||||
|
flags := cmd.Flags()
|
||||||
|
flags.StringVarP(&o.Format,
|
||||||
|
"output", "o", "name", "'table' "+
|
||||||
|
"and 'name' are available "+
|
||||||
|
"output formats")
|
||||||
|
|
||||||
return cmd
|
return cmd
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,10 @@ Usage:
|
|||||||
Examples:
|
Examples:
|
||||||
# Retrieve cluster list
|
# Retrieve cluster list
|
||||||
airshipctl cluster list --airshipconf /tmp/airconfig
|
airshipctl cluster list --airshipconf /tmp/airconfig
|
||||||
|
airshipctl cluster list -o table
|
||||||
|
airshipctl cluster list -o name
|
||||||
|
|
||||||
|
|
||||||
Flags:
|
Flags:
|
||||||
-h, --help help for list
|
-h, --help help for list
|
||||||
|
-o, --output string 'table' and 'name' are available output formats (default "name")
|
||||||
|
@ -15,13 +15,16 @@ airshipctl cluster list [flags]
|
|||||||
```
|
```
|
||||||
# Retrieve cluster list
|
# Retrieve cluster list
|
||||||
airshipctl cluster list --airshipconf /tmp/airconfig
|
airshipctl cluster list --airshipconf /tmp/airconfig
|
||||||
|
airshipctl cluster list -o table
|
||||||
|
airshipctl cluster list -o name
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### Options
|
### Options
|
||||||
|
|
||||||
```
|
```
|
||||||
-h, --help help for list
|
-h, --help help for list
|
||||||
|
-o, --output string 'table' and 'name' are available output formats (default "name")
|
||||||
```
|
```
|
||||||
|
|
||||||
### Options inherited from parent commands
|
### Options inherited from parent commands
|
||||||
|
@ -15,12 +15,22 @@
|
|||||||
package clustermap
|
package clustermap
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"text/tabwriter"
|
||||||
|
|
||||||
"opendev.org/airship/airshipctl/pkg/api/v1alpha1"
|
"opendev.org/airship/airshipctl/pkg/api/v1alpha1"
|
||||||
)
|
)
|
||||||
|
|
||||||
// DefaultClusterAPIObjNamespace is a default namespace used for cluster-api cluster object
|
// DefaultClusterAPIObjNamespace is a default namespace used for cluster-api cluster object
|
||||||
const DefaultClusterAPIObjNamespace = "default"
|
const DefaultClusterAPIObjNamespace = "default"
|
||||||
|
|
||||||
|
// WriteOptions has format in which we want to print the output(table/yaml/cluster name)
|
||||||
|
type WriteOptions struct {
|
||||||
|
Format string
|
||||||
|
}
|
||||||
|
|
||||||
// ClusterMap interface that allows to list all clusters, find its parent, namespace,
|
// ClusterMap interface that allows to list all clusters, find its parent, namespace,
|
||||||
// check if dynamic kubeconfig is enabled.
|
// check if dynamic kubeconfig is enabled.
|
||||||
// TODO use typed cluster names
|
// TODO use typed cluster names
|
||||||
@ -29,6 +39,7 @@ type ClusterMap interface {
|
|||||||
AllClusters() []string
|
AllClusters() []string
|
||||||
ClusterKubeconfigContext(string) (string, error)
|
ClusterKubeconfigContext(string) (string, error)
|
||||||
Sources(string) ([]v1alpha1.KubeconfigSource, error)
|
Sources(string) ([]v1alpha1.KubeconfigSource, error)
|
||||||
|
Write(io.Writer, WriteOptions) error
|
||||||
}
|
}
|
||||||
|
|
||||||
// clusterMap allows to view clusters and relationship between them
|
// clusterMap allows to view clusters and relationship between them
|
||||||
@ -82,3 +93,28 @@ func (cm clusterMap) Sources(clusterName string) ([]v1alpha1.KubeconfigSource, e
|
|||||||
}
|
}
|
||||||
return cluster.Sources, nil
|
return cluster.Sources, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Write prints the cluster list in table/name output format
|
||||||
|
func (cm clusterMap) Write(writer io.Writer, wo WriteOptions) error {
|
||||||
|
if wo.Format == "table" {
|
||||||
|
w := tabwriter.NewWriter(os.Stdout, 20, 8, 1, ' ', 0)
|
||||||
|
fmt.Fprintf(w, "NAME\tKUBECONFIG CONTEXT\tPARENT CLUSTER\n")
|
||||||
|
for clustername, cluster := range cm.apiMap.Map {
|
||||||
|
kubeconfig, err := cm.ClusterKubeconfigContext(clustername)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
fmt.Fprintf(w, "%s\t%s\t%s\n",
|
||||||
|
clustername, kubeconfig, cluster.Parent)
|
||||||
|
}
|
||||||
|
w.Flush()
|
||||||
|
} else if wo.Format == "name" {
|
||||||
|
clusterList := cm.AllClusters()
|
||||||
|
for _, clusterName := range clusterList {
|
||||||
|
if _, err := writer.Write([]byte(clusterName + "\n")); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
@ -15,6 +15,11 @@
|
|||||||
package clustermap_test
|
package clustermap_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bufio"
|
||||||
|
"bytes"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
@ -65,7 +70,6 @@ func TestClusterMap(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
cMap := clustermap.NewClusterMap(apiMap)
|
cMap := clustermap.NewClusterMap(apiMap)
|
||||||
require.NotNil(t, cMap)
|
require.NotNil(t, cMap)
|
||||||
|
|
||||||
@ -121,3 +125,64 @@ func TestClusterMap(t *testing.T) {
|
|||||||
assert.Error(t, err)
|
assert.Error(t, err)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Test_clusterMap_Write(t *testing.T) {
|
||||||
|
var b bytes.Buffer
|
||||||
|
wr := bufio.NewWriter(&b)
|
||||||
|
targetCluster := "target"
|
||||||
|
ephemeraCluster := "ephemeral"
|
||||||
|
apiMap := &v1alpha1.ClusterMap{
|
||||||
|
Map: map[string]*v1alpha1.Cluster{
|
||||||
|
targetCluster: {
|
||||||
|
Parent: ephemeraCluster,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
wo clustermap.WriteOptions
|
||||||
|
wantWriter string
|
||||||
|
expectedOut string
|
||||||
|
expectedErr string
|
||||||
|
writer io.Writer
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "success table",
|
||||||
|
wo: clustermap.WriteOptions{Format: "table"},
|
||||||
|
expectedOut: "NAME KUBECONFIG CONTEXT PARENT CLUSTER" +
|
||||||
|
"\ntarget target ephemeral\n",
|
||||||
|
writer: wr,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "writer nil",
|
||||||
|
wo: clustermap.WriteOptions{Format: "table"},
|
||||||
|
writer: nil,
|
||||||
|
expectedOut: "",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
rStdout := os.Stdout
|
||||||
|
r, w, err := os.Pipe()
|
||||||
|
if err != nil {
|
||||||
|
require.Error(t, err)
|
||||||
|
}
|
||||||
|
os.Stdout = w
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
cMap := clustermap.NewClusterMap(apiMap)
|
||||||
|
err := cMap.Write(tt.writer, tt.wo)
|
||||||
|
w.Close()
|
||||||
|
if tt.expectedErr != "" {
|
||||||
|
require.Error(t, err)
|
||||||
|
assert.Contains(t, err.Error(), tt.expectedErr)
|
||||||
|
} else {
|
||||||
|
assert.NoError(t, err)
|
||||||
|
}
|
||||||
|
out, err := ioutil.ReadAll(r)
|
||||||
|
if err != nil {
|
||||||
|
require.Error(t, err)
|
||||||
|
}
|
||||||
|
os.Stdout = rStdout
|
||||||
|
assert.Equal(t, tt.expectedOut, string(out))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -26,6 +26,7 @@ import (
|
|||||||
"sigs.k8s.io/cli-utils/pkg/print/table"
|
"sigs.k8s.io/cli-utils/pkg/print/table"
|
||||||
|
|
||||||
"opendev.org/airship/airshipctl/pkg/api/v1alpha1"
|
"opendev.org/airship/airshipctl/pkg/api/v1alpha1"
|
||||||
|
"opendev.org/airship/airshipctl/pkg/cluster/clustermap"
|
||||||
"opendev.org/airship/airshipctl/pkg/config"
|
"opendev.org/airship/airshipctl/pkg/config"
|
||||||
"opendev.org/airship/airshipctl/pkg/document"
|
"opendev.org/airship/airshipctl/pkg/document"
|
||||||
phaseerrors "opendev.org/airship/airshipctl/pkg/phase/errors"
|
phaseerrors "opendev.org/airship/airshipctl/pkg/phase/errors"
|
||||||
@ -247,10 +248,14 @@ func (c *PlanRunCommand) RunE() error {
|
|||||||
type ClusterListCommand struct {
|
type ClusterListCommand struct {
|
||||||
Factory config.Factory
|
Factory config.Factory
|
||||||
Writer io.Writer
|
Writer io.Writer
|
||||||
|
Format string
|
||||||
}
|
}
|
||||||
|
|
||||||
// RunE executes cluster list command
|
// RunE executes cluster list command
|
||||||
func (c *ClusterListCommand) RunE() error {
|
func (c *ClusterListCommand) RunE() error {
|
||||||
|
if c.Format != "table" && c.Format != "name" {
|
||||||
|
return phaseerrors.ErrInvalidOutputFormat{RequestedFormat: c.Format}
|
||||||
|
}
|
||||||
cfg, err := c.Factory()
|
cfg, err := c.Factory()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -263,12 +268,9 @@ func (c *ClusterListCommand) RunE() error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
err = clusterMap.Write(c.Writer, clustermap.WriteOptions{Format: c.Format})
|
||||||
clusterList := clusterMap.AllClusters()
|
if err != nil {
|
||||||
for _, clusterName := range clusterList {
|
return err
|
||||||
if _, err := c.Writer.Write([]byte(clusterName + "\n")); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -456,6 +456,7 @@ func TestClusterListCommand_RunE(t *testing.T) {
|
|||||||
name string
|
name string
|
||||||
factory config.Factory
|
factory config.Factory
|
||||||
expectedErr string
|
expectedErr string
|
||||||
|
Format string
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "Error config factory",
|
name: "Error config factory",
|
||||||
@ -463,6 +464,7 @@ func TestClusterListCommand_RunE(t *testing.T) {
|
|||||||
return nil, testErr
|
return nil, testErr
|
||||||
},
|
},
|
||||||
expectedErr: testFactoryErr,
|
expectedErr: testFactoryErr,
|
||||||
|
Format: "name",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Error new helper",
|
name: "Error new helper",
|
||||||
@ -473,9 +475,11 @@ func TestClusterListCommand_RunE(t *testing.T) {
|
|||||||
}, nil
|
}, nil
|
||||||
},
|
},
|
||||||
expectedErr: "missing configuration: context with name 'does not exist'",
|
expectedErr: "missing configuration: context with name 'does not exist'",
|
||||||
|
Format: "name",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "No error",
|
name: "No error",
|
||||||
|
Format: "name",
|
||||||
factory: func() (*config.Config, error) {
|
factory: func() (*config.Config, error) {
|
||||||
conf := config.NewConfig()
|
conf := config.NewConfig()
|
||||||
conf.Manifests = map[string]*config.Manifest{
|
conf.Manifests = map[string]*config.Manifest{
|
||||||
@ -506,6 +510,7 @@ func TestClusterListCommand_RunE(t *testing.T) {
|
|||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
cmd := phase.ClusterListCommand{
|
cmd := phase.ClusterListCommand{
|
||||||
Factory: tt.factory,
|
Factory: tt.factory,
|
||||||
|
Format: tt.Format,
|
||||||
Writer: bytes.NewBuffer(nil),
|
Writer: bytes.NewBuffer(nil),
|
||||||
}
|
}
|
||||||
err := cmd.RunE()
|
err := cmd.RunE()
|
||||||
|
@ -69,3 +69,12 @@ type ErrInvalidPhase struct {
|
|||||||
func (e ErrInvalidPhase) Error() string {
|
func (e ErrInvalidPhase) Error() string {
|
||||||
return fmt.Sprintf("invalid phase: %s", e.Reason)
|
return fmt.Sprintf("invalid phase: %s", e.Reason)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ErrInvalidOutputFormat is called when the user provides format other than name/table
|
||||||
|
type ErrInvalidOutputFormat struct {
|
||||||
|
RequestedFormat string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e ErrInvalidOutputFormat) Error() string {
|
||||||
|
return fmt.Sprintf("invalid output format specified %s. Allowed values are table|name", e.RequestedFormat)
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user