diff --git a/cmd/kops/create_cluster.go b/cmd/kops/create_cluster.go index 5ee7209aea..1404f62a8b 100644 --- a/cmd/kops/create_cluster.go +++ b/cmd/kops/create_cluster.go @@ -720,7 +720,7 @@ func RunCreateCluster(ctx context.Context, f *util.Factory, out io.Writer, c *Cr } return nil case OutputJSON: - if err := fullOutputJSON(out, obj...); err != nil { + if err := fullOutputJSON(out, true, obj...); err != nil { return fmt.Errorf("error writing cluster json to stdout: %v", err) } return nil diff --git a/cmd/kops/create_instancegroup.go b/cmd/kops/create_instancegroup.go index 1bff5a6ecd..82e2f92fdd 100644 --- a/cmd/kops/create_instancegroup.go +++ b/cmd/kops/create_instancegroup.go @@ -221,7 +221,7 @@ func RunCreateInstanceGroup(ctx context.Context, f *util.Factory, out io.Writer, } return nil case OutputJSON: - if err := fullOutputJSON(out, ig); err != nil { + if err := fullOutputJSON(out, true, ig); err != nil { return fmt.Errorf("error writing cluster json to stdout: %v", err) } return nil diff --git a/cmd/kops/get.go b/cmd/kops/get.go index 63b89ec573..30aec302fb 100644 --- a/cmd/kops/get.go +++ b/cmd/kops/get.go @@ -158,7 +158,7 @@ func RunGet(ctx context.Context, f commandutils.Factory, out io.Writer, options return nil case OutputJSON: - if err := fullOutputJSON(out, allObjects...); err != nil { + if err := fullOutputJSON(out, false, allObjects...); err != nil { return fmt.Errorf("error writing json to stdout: %v", err) } return nil diff --git a/cmd/kops/get_cluster.go b/cmd/kops/get_cluster.go index 7578ec3194..65784caaee 100644 --- a/cmd/kops/get_cluster.go +++ b/cmd/kops/get_cluster.go @@ -123,17 +123,11 @@ func RunGetClusters(ctx context.Context, f commandutils.Factory, out io.Writer, return err } + singleClusterSelected := false var clusterList []*kopsapi.Cluster - if len(options.ClusterNames) != 1 { - list, err := client.ListClusters(ctx, metav1.ListOptions{}) - if err != nil { - return err - } - for i := range list.Items { - clusterList = append(clusterList, &list.Items[i]) - } - } else { + if len(options.ClusterNames) == 1 { // Optimization - avoid fetching all clusters if we're only querying one + singleClusterSelected = true cluster, err := client.GetCluster(ctx, options.ClusterNames[0]) if err != nil { if !apierrors.IsNotFound(err) { @@ -142,6 +136,14 @@ func RunGetClusters(ctx context.Context, f commandutils.Factory, out io.Writer, } else { clusterList = append(clusterList, cluster) } + } else { + list, err := client.ListClusters(ctx, metav1.ListOptions{}) + if err != nil { + return err + } + for i := range list.Items { + clusterList = append(clusterList, &list.Items[i]) + } } clusters, err := filterClustersByName(options.ClusterNames, clusterList) @@ -176,7 +178,11 @@ func RunGetClusters(ctx context.Context, f commandutils.Factory, out io.Writer, case OutputYaml: return fullOutputYAML(out, obj...) case OutputJSON: - return fullOutputJSON(out, obj...) + // if singleClusterSelected is true, only a single object is returned + // otherwise to keep it consistent, always returns an array. + // Ex: kops get clusters -ojson should will always return an array (even if 1 cluster is available) + // kops get cluster test.example.com -o json will return a single object (since a specific cluster is selected) + return fullOutputJSON(out, singleClusterSelected, obj...) default: return fmt.Errorf("Unknown output format: %q", options.Output) } @@ -227,12 +233,10 @@ func clusterOutputTable(clusters []*kopsapi.Cluster, out io.Writer) error { return t.Render(clusters, out, "NAME", "CLOUD", "ZONES") } -// fullOutputJson outputs the marshalled JSON of a list of clusters and instance groups. It will handle +// fullOutputJSON outputs the marshalled JSON of a list of clusters and instance groups. It will handle // nils for clusters and instanceGroups slices. -func fullOutputJSON(out io.Writer, args ...runtime.Object) error { - argsLen := len(args) - - if argsLen > 1 { +func fullOutputJSON(out io.Writer, singleObject bool, args ...runtime.Object) error { + if !singleObject { if _, err := fmt.Fprint(out, "["); err != nil { return err } @@ -249,7 +253,7 @@ func fullOutputJSON(out io.Writer, args ...runtime.Object) error { } } - if argsLen > 1 { + if !singleObject { if _, err := fmt.Fprint(out, "]"); err != nil { return err } @@ -258,7 +262,7 @@ func fullOutputJSON(out io.Writer, args ...runtime.Object) error { return nil } -// fullOutputJson outputs the marshalled JSON of a list of clusters and instance groups. It will handle +// fullOutputYAML outputs the marshalled JSON of a list of clusters and instance groups. It will handle // nils for clusters and instanceGroups slices. func fullOutputYAML(out io.Writer, args ...runtime.Object) error { for i, obj := range args { diff --git a/cmd/kops/get_instancegroups.go b/cmd/kops/get_instancegroups.go index f7e8c215ec..18a43e752e 100644 --- a/cmd/kops/get_instancegroups.go +++ b/cmd/kops/get_instancegroups.go @@ -113,8 +113,12 @@ func RunGetInstanceGroups(ctx context.Context, f commandutils.Factory, out io.Wr return err } + singleObject := false + if len(instancegroups) == 0 { return fmt.Errorf("no InstanceGroup objects found") + } else if len(instancegroups) == 1 { + singleObject = true } var obj []runtime.Object @@ -130,7 +134,7 @@ func RunGetInstanceGroups(ctx context.Context, f commandutils.Factory, out io.Wr case OutputYaml: return fullOutputYAML(out, obj...) case OutputJSON: - return fullOutputJSON(out, obj...) + return fullOutputJSON(out, singleObject, obj...) default: return fmt.Errorf("unknown output format: %q", options.Output) } diff --git a/cmd/kops/toolbox_instance-selector.go b/cmd/kops/toolbox_instance-selector.go index 97ed24513a..31edcc4877 100644 --- a/cmd/kops/toolbox_instance-selector.go +++ b/cmd/kops/toolbox_instance-selector.go @@ -332,7 +332,7 @@ func RunToolboxInstanceSelector(ctx context.Context, f *util.Factory, out io.Wri return fmt.Errorf("error writing cluster yaml to stdout: %v", err) } case OutputJSON: - if err := fullOutputJSON(out, ig); err != nil { + if err := fullOutputJSON(out, true, ig); err != nil { return fmt.Errorf("error writing cluster json to stdout: %v", err) } default: diff --git a/docs/releases/1.23-NOTES.md b/docs/releases/1.23-NOTES.md index 4208ceb13e..e38217a33c 100644 --- a/docs/releases/1.23-NOTES.md +++ b/docs/releases/1.23-NOTES.md @@ -64,6 +64,8 @@ It is recommended to keep using the `v1alpha2` API version. * The `kops rolling-update cluster` command has a new `--drain-timeout` flag for specifying the maximum amount of time to wait when attempting to drain a node. Previously, rolling-updates would attempt to drain a node for an indefinite amount of time. If `--drain-timeout` is not specified, a default of 15 minutes is applied. +* Fix inconsistent output of `kops get clusters -ojson`. This will now always return a list (irrespective of a single or multiple clusters) to keep the format consistent. However, note that `kops get cluster dev.example.com -ojson` will continue to work as previously, and will return a single object. + # Full change list since 1.22.0 release