Better documentation for the resource and status commands.

This commit is contained in:
Morten Torkildsen
2019-12-22 00:36:02 -08:00
parent 697a6e9759
commit 71ce46416e
14 changed files with 230 additions and 16 deletions

View File

@@ -17,8 +17,8 @@ fmt:
go fmt ./... go fmt ./...
generate: generate:
#(which $(GOBIN)/mdtogo || go get sigs.k8s.io/kustomize/cmd/resource) (which $(GOBIN)/mdtogo || go get sigs.k8s.io/kustomize/cmd/mdtogo)
#GOBIN=$(GOBIN) go generate ./... GOBIN=$(GOBIN) go generate ./...
license: license:
(which $(GOBIN)/addlicense || go get github.com/google/addlicense) (which $(GOBIN)/addlicense || go get github.com/google/addlicense)

View File

@@ -9,6 +9,7 @@ require (
k8s.io/apimachinery v0.0.0-20190913080033-27d36303b655 k8s.io/apimachinery v0.0.0-20190913080033-27d36303b655
k8s.io/client-go v0.0.0-20190918160344-1fbdaa4c8d90 k8s.io/client-go v0.0.0-20190918160344-1fbdaa4c8d90
sigs.k8s.io/controller-runtime v0.4.0 sigs.k8s.io/controller-runtime v0.4.0
sigs.k8s.io/kustomize/cmd/mdtogo v0.0.0-20191222005333-3900166fdf4f // indirect
sigs.k8s.io/kustomize/kstatus v0.0.0-20191204200457-7c1b477ff62d sigs.k8s.io/kustomize/kstatus v0.0.0-20191204200457-7c1b477ff62d
sigs.k8s.io/kustomize/kyaml v0.0.0-20191202204815-0a19a5dbd9b8 sigs.k8s.io/kustomize/kyaml v0.0.0-20191202204815-0a19a5dbd9b8
) )

View File

@@ -402,6 +402,9 @@ modernc.org/strutil v1.0.0/go.mod h1:lstksw84oURvj9y3tn8lGvRxyRC1S2+g5uuIzNfIOBs
modernc.org/xc v1.0.0/go.mod h1:mRNCo0bvLjGhHO9WsyuKVU4q0ceiDDDoEeWDJHrNx8I= modernc.org/xc v1.0.0/go.mod h1:mRNCo0bvLjGhHO9WsyuKVU4q0ceiDDDoEeWDJHrNx8I=
sigs.k8s.io/controller-runtime v0.4.0 h1:wATM6/m+3w8lj8FXNaO6Fs/rq/vqoOjO1Q116Z9NPsg= sigs.k8s.io/controller-runtime v0.4.0 h1:wATM6/m+3w8lj8FXNaO6Fs/rq/vqoOjO1Q116Z9NPsg=
sigs.k8s.io/controller-runtime v0.4.0/go.mod h1:ApC79lpY3PHW9xj/w9pj+lYkLgwAAUZwfXkME1Lajns= sigs.k8s.io/controller-runtime v0.4.0/go.mod h1:ApC79lpY3PHW9xj/w9pj+lYkLgwAAUZwfXkME1Lajns=
sigs.k8s.io/kustomize v2.0.3+incompatible h1:JUufWFNlI44MdtnjUqVnvh29rR37PQFzPbLXqhyOyX0=
sigs.k8s.io/kustomize/cmd/mdtogo v0.0.0-20191222005333-3900166fdf4f h1:Wdh26pJ0THtsuSB1DCkaLc1Ssv2NDddB7E7vCSdTHdg=
sigs.k8s.io/kustomize/cmd/mdtogo v0.0.0-20191222005333-3900166fdf4f/go.mod h1:arffnBwv6VTLUY3hxATxJ2fwNMWy92GSXm6UXEjFddQ=
sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI= sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI=
sigs.k8s.io/structured-merge-diff v0.0.0-20190817042607-6149e4549fca/go.mod h1:IIgPezJWb76P0hotTxzDbWsMYB8APh18qZnxkomBpxA= sigs.k8s.io/structured-merge-diff v0.0.0-20190817042607-6149e4549fca/go.mod h1:IIgPezJWb76P0hotTxzDbWsMYB8APh18qZnxkomBpxA=
sigs.k8s.io/testing_frameworks v0.1.2 h1:vK0+tvjF0BZ/RYFeZ1E6BYBwHJJXhjuZ3TdsEKH+UQM= sigs.k8s.io/testing_frameworks v0.1.2 h1:vK0+tvjF0BZ/RYFeZ1E6BYBwHJJXhjuZ3TdsEKH+UQM=

View File

@@ -1,3 +1,6 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package main package main
import ( import (

View File

@@ -1,3 +1,6 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package cmd package cmd
import ( import (
@@ -6,23 +9,27 @@ import (
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"sigs.k8s.io/kustomize/cmd/resource/status/generateddocs/commands"
"sigs.k8s.io/kustomize/kstatus/wait" "sigs.k8s.io/kustomize/kstatus/wait"
"sigs.k8s.io/kustomize/kyaml/kio" "sigs.k8s.io/kustomize/kyaml/kio"
) )
// GetEventsRunner returns a command EventsRunner.
func GetEventsRunner() *EventsRunner { func GetEventsRunner() *EventsRunner {
r := &EventsRunner{} r := &EventsRunner{}
c := &cobra.Command{ c := &cobra.Command{
Use: "events", Use: "events DIR...",
Short: "Events", Short: commands.EventsShort,
RunE: r.runE, Long: commands.EventsLong,
Example: commands.EventsExamples,
RunE: r.runE,
} }
c.Flags().BoolVar(&r.IncludeSubpackages, "include-subpackages", true, c.Flags().BoolVar(&r.IncludeSubpackages, "include-subpackages", true,
"also print resources from subpackages.") "also print resources from subpackages.")
c.Flags().DurationVar(&r.Interval, "interval", 2*time.Second, c.Flags().DurationVar(&r.Interval, "interval", 2*time.Second,
"check every n seconds. Default is every 2 seconds.") "check every n seconds.")
c.Flags().DurationVar(&r.Timeout, "timeout", 60*time.Second, c.Flags().DurationVar(&r.Timeout, "timeout", 60*time.Second,
"give up after n seconds. Default is 60 seconds.") "give up after n seconds.")
r.Command = c r.Command = c
return r return r
@@ -32,6 +39,8 @@ func EventsCommand() *cobra.Command {
return GetEventsRunner().Command return GetEventsRunner().Command
} }
// EventsRunner captures the parameters for the command
// and contains the run function.
type EventsRunner struct { type EventsRunner struct {
IncludeSubpackages bool IncludeSubpackages bool
Interval time.Duration Interval time.Duration
@@ -41,13 +50,17 @@ type EventsRunner struct {
func (r *EventsRunner) runE(c *cobra.Command, args []string) error { func (r *EventsRunner) runE(c *cobra.Command, args []string) error {
ctx := context.Background() ctx := context.Background()
// Create a client and use it to set up a new resolver.
client, err := getClient() client, err := getClient()
if err != nil { if err != nil {
return errors.Wrap(err, "error creating client") return errors.Wrap(err, "error creating client")
} }
resolver := wait.NewResolver(client, r.Interval) resolver := wait.NewResolver(client, r.Interval)
// Set up a CaptureIdentifierFilter and run all inputs through the
// filter with the pipeline to capture the inventory of resources
// which we are interested in.
captureFilter := &CaptureIdentifiersFilter{} captureFilter := &CaptureIdentifiersFilter{}
filters := []kio.Filter{captureFilter} filters := []kio.Filter{captureFilter}
@@ -70,12 +83,17 @@ func (r *EventsRunner) runE(c *cobra.Command, args []string) error {
return errors.Wrap(err, "error reading manifests") return errors.Wrap(err, "error reading manifests")
} }
// Create a new printer that knows how to print updates about
// resourdes and their aggregate status in the events format.
printer := newEventPrinter(c.OutOrStdout(), c.OutOrStderr()) printer := newEventPrinter(c.OutOrStdout(), c.OutOrStderr())
ctx, cancel := context.WithTimeout(ctx, r.Timeout) ctx, cancel := context.WithTimeout(ctx, r.Timeout)
defer cancel() defer cancel()
resChannel := resolver.WaitForStatus(ctx, captureFilter.Identifiers) resChannel := resolver.WaitForStatus(ctx, captureFilter.Identifiers)
// Print events until the channel is closed. This will happen
// either because all resources has reached the Current status
// or it has timed out.
for msg := range resChannel { for msg := range resChannel {
printer.printEvent(msg) printer.printEvent(msg)
} }

View File

@@ -1,3 +1,6 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package cmd package cmd
import ( import (
@@ -6,17 +9,21 @@ import (
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"sigs.k8s.io/kustomize/cmd/resource/status/generateddocs/commands"
"sigs.k8s.io/kustomize/kstatus/status" "sigs.k8s.io/kustomize/kstatus/status"
"sigs.k8s.io/kustomize/kstatus/wait" "sigs.k8s.io/kustomize/kstatus/wait"
"sigs.k8s.io/kustomize/kyaml/kio" "sigs.k8s.io/kustomize/kyaml/kio"
) )
// GetFetchRunner returns a command FetchRunner.
func GetFetchRunner() *FetchRunner { func GetFetchRunner() *FetchRunner {
r := &FetchRunner{} r := &FetchRunner{}
c := &cobra.Command{ c := &cobra.Command{
Use: "fetch", Use: "fetch DIR...",
Short: "Fetch", Short: commands.FetchShort,
RunE: r.runE, Long: commands.FetchLong,
Example: commands.FetchExamples,
RunE: r.runE,
} }
c.Flags().BoolVar(&r.IncludeSubpackages, "include-subpackages", true, c.Flags().BoolVar(&r.IncludeSubpackages, "include-subpackages", true,
"also print resources from subpackages.") "also print resources from subpackages.")
@@ -29,6 +36,8 @@ func FetchCommand() *cobra.Command {
return GetFetchRunner().Command return GetFetchRunner().Command
} }
// FetchRunner captures the parameters for the command and contains
// the run function.
type FetchRunner struct { type FetchRunner struct {
IncludeSubpackages bool IncludeSubpackages bool
Command *cobra.Command Command *cobra.Command
@@ -36,6 +45,8 @@ type FetchRunner struct {
func (r *FetchRunner) runE(c *cobra.Command, args []string) error { func (r *FetchRunner) runE(c *cobra.Command, args []string) error {
ctx := context.Background() ctx := context.Background()
// Create a new client and use it to set up a resolver.
client, err := getClient() client, err := getClient()
if err != nil { if err != nil {
return errors.Wrap(err, "error creating client") return errors.Wrap(err, "error creating client")
@@ -43,6 +54,9 @@ func (r *FetchRunner) runE(c *cobra.Command, args []string) error {
resolver := wait.NewResolver(client, time.Minute) resolver := wait.NewResolver(client, time.Minute)
// Set up a CaptureIdentifierFilter and run all inputs through the
// filter with the pipeline to capture the inventory of resources
// which we are interested in.
captureFilter := &CaptureIdentifiersFilter{} captureFilter := &CaptureIdentifiersFilter{}
filters := []kio.Filter{captureFilter} filters := []kio.Filter{captureFilter}
@@ -65,16 +79,27 @@ func (r *FetchRunner) runE(c *cobra.Command, args []string) error {
return errors.Wrap(err, "error reading manifests") return errors.Wrap(err, "error reading manifests")
} }
// Pass in the inventory of resources to the FetchAndResolve function
// on the resolver. It will return the status (or an error) for each
// resource in the inventory.
results := resolver.FetchAndResolve(ctx, captureFilter.Identifiers) results := resolver.FetchAndResolve(ctx, captureFilter.Identifiers)
// Create new printer that knows how to print resource statuses
// in a table format and ask it to print the results.
newTablePrinter(FetchStatusInfo{results}, c.OutOrStdout(), c.OutOrStderr(), false).Print() newTablePrinter(FetchStatusInfo{results}, c.OutOrStdout(), c.OutOrStderr(), false).Print()
return nil return nil
} }
// FetchStatusInfo wraps the results from the FetchAndResolve function
// to the format expected in the TablePrinter.
type FetchStatusInfo struct { type FetchStatusInfo struct {
Results []wait.ResourceResult Results []wait.ResourceResult
} }
// CurrentStatus returns the latest information known about the
// status of each of the resources. For FetchStatusInfo, the result
// is never updated, so it just returns the information provided
// by the slice of wait.ResourceResult at creation.
func (f FetchStatusInfo) CurrentStatus() StatusData { func (f FetchStatusInfo) CurrentStatus() StatusData {
var resourceData []ResourceStatusData var resourceData []ResourceStatusData
for _, res := range f.Results { for _, res := range f.Results {

View File

@@ -1,3 +1,6 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package cmd package cmd
import ( import (

View File

@@ -1,3 +1,6 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package cmd package cmd
import ( import (
@@ -19,6 +22,8 @@ func init() {
_ = clientgoscheme.AddToScheme(scheme) _ = clientgoscheme.AddToScheme(scheme)
} }
// getClient returns a client for talking to a Kubernetes cluster. The client
// is from controller-runtime.
func getClient() (client.Client, error) { func getClient() (client.Client, error) {
config := ctrl.GetConfigOrDie() config := ctrl.GetConfigOrDie()
mapper, err := apiutil.NewDiscoveryRESTMapper(config) mapper, err := apiutil.NewDiscoveryRESTMapper(config)
@@ -28,6 +33,8 @@ func getClient() (client.Client, error) {
return client.New(config, client.Options{Scheme: scheme, Mapper: mapper}) return client.New(config, client.Options{Scheme: scheme, Mapper: mapper})
} }
// CaptureIdentifiersFilter implements the Filter interface in the kio package. It
// captures the identifiers for all resources passed through the pipeline.
type CaptureIdentifiersFilter struct { type CaptureIdentifiersFilter struct {
Identifiers []wait.ResourceIdentifier Identifiers []wait.ResourceIdentifier
} }

View File

@@ -1,3 +1,6 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package cmd package cmd
import ( import (
@@ -7,18 +10,21 @@ import (
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"sigs.k8s.io/kustomize/cmd/resource/status/generateddocs/commands"
"sigs.k8s.io/kustomize/kstatus/status" "sigs.k8s.io/kustomize/kstatus/status"
"sigs.k8s.io/kustomize/kstatus/wait" "sigs.k8s.io/kustomize/kstatus/wait"
"sigs.k8s.io/kustomize/kyaml/kio" "sigs.k8s.io/kustomize/kyaml/kio"
) )
// GetWaitRunner return a command WaitRunner.
func GetWaitRunner() *WaitRunner { func GetWaitRunner() *WaitRunner {
r := &WaitRunner{} r := &WaitRunner{}
c := &cobra.Command{ c := &cobra.Command{
Use: "wait", Use: "wait DIR...",
Short: "Wait", Short: commands.WaitShort,
RunE: r.runE, Long: commands.WaitLong,
Example: commands.WaitExamples,
RunE: r.runE,
} }
c.Flags().BoolVar(&r.IncludeSubpackages, "include-subpackages", true, c.Flags().BoolVar(&r.IncludeSubpackages, "include-subpackages", true,
"also print resources from subpackages.") "also print resources from subpackages.")
@@ -35,6 +41,8 @@ func WaitCommand() *cobra.Command {
return GetWaitRunner().Command return GetWaitRunner().Command
} }
// WaitRunner captures the parameters for the command and contains
// the run function.
type WaitRunner struct { type WaitRunner struct {
IncludeSubpackages bool IncludeSubpackages bool
Interval time.Duration Interval time.Duration
@@ -42,6 +50,9 @@ type WaitRunner struct {
Command *cobra.Command Command *cobra.Command
} }
// runE implements the logic of the command and will call the Wait command in the wait
// package, use a ResourceStatusCollector to capture the events from the channel, and the
// TablePrinter to display the information.
func (r *WaitRunner) runE(c *cobra.Command, args []string) error { func (r *WaitRunner) runE(c *cobra.Command, args []string) error {
ctx := context.Background() ctx := context.Background()
client, err := getClient() client, err := getClient()
@@ -98,6 +109,9 @@ func (r *WaitRunner) runE(c *cobra.Command, args []string) error {
return nil return nil
} }
// ResourceStatusCollector captures the latest state seen for all resources
// based on the events from the Wait channel. This is used by the TablePrinter
// to display status for all resources.
type ResourceStatusCollector struct { type ResourceStatusCollector struct {
mux sync.RWMutex mux sync.RWMutex
@@ -105,6 +119,8 @@ type ResourceStatusCollector struct {
ResourceStatuses []*ResourceStatus ResourceStatuses []*ResourceStatus
} }
// updateResourceStatus takes the given event and update the status info
// in the ResourceStatusCollector.
func (r *ResourceStatusCollector) updateResourceStatus(msg wait.Event) { func (r *ResourceStatusCollector) updateResourceStatus(msg wait.Event) {
r.mux.Lock() r.mux.Lock()
defer r.mux.Unlock() defer r.mux.Unlock()
@@ -121,18 +137,23 @@ func (r *ResourceStatusCollector) updateResourceStatus(msg wait.Event) {
} }
} }
// updateAggregateStatus sets the aggregate status of the ResourceStatusCollector to the
// given value.
func (r *ResourceStatusCollector) updateAggregateStatus(aggregateStatus status.Status) { func (r *ResourceStatusCollector) updateAggregateStatus(aggregateStatus status.Status) {
r.mux.Lock() r.mux.Lock()
defer r.mux.Unlock() defer r.mux.Unlock()
r.AggregateStatus = aggregateStatus r.AggregateStatus = aggregateStatus
} }
// ResourceStatus contains the status information for a single resource.
type ResourceStatus struct { type ResourceStatus struct {
Identifier wait.ResourceIdentifier Identifier wait.ResourceIdentifier
Status status.Status Status status.Status
Message string Message string
} }
// newResourceStatusCollector creates a new ResourceStatusCollector with the given
// resources and sets the status for all of them to Unknown.
func newResourceStatusCollector(identifiers []wait.ResourceIdentifier) *ResourceStatusCollector { func newResourceStatusCollector(identifiers []wait.ResourceIdentifier) *ResourceStatusCollector {
var statuses []*ResourceStatus var statuses []*ResourceStatus
@@ -150,10 +171,16 @@ func newResourceStatusCollector(identifiers []wait.ResourceIdentifier) *Resource
} }
} }
// CollectorStatusInfo is a wrapper around the ResourceStatusCollector
// to make it adhere to the interface of the TableWriter.
type CollectorStatusInfo struct { type CollectorStatusInfo struct {
Collector *ResourceStatusCollector Collector *ResourceStatusCollector
} }
// CurrentStatus implements the interface for the TableWriter and
// returns a copy of the current status of the resources in the
// ResourceStatusCollector. This is done to make sure the TableWriter
// does not have to deal with synchronization when accessing the data.
func (f CollectorStatusInfo) CurrentStatus() StatusData { func (f CollectorStatusInfo) CurrentStatus() StatusData {
f.Collector.mux.RLock() f.Collector.mux.RLock()
defer f.Collector.mux.RUnlock() defer f.Collector.mux.RUnlock()

View File

@@ -0,0 +1,22 @@
## events
[Alpha] Poll the cluster until all provided resources have become Current and list the status change events.
### Synopsis
[Alpha] Poll the cluster for the state of all the provided resources until either they have all become
Current or the timeout is reached. The output will be status change events.
The list of resources which should be polled are provided as manifests either on the filesystem or
on StdIn.
DIR:
Path to local directory. If not provided, input is expected on StdIn.
### Examples
# Read resources from the filesystem and wait up to 1 minute for all of them to become Current
resource status events my-dir/
# Fetch all resources in the cluster and wait up to 5 minutes for all of them to become Current
kubectl get all --all-namespaces -oyaml | resource status events --timeout=5m

View File

@@ -0,0 +1,21 @@
## fetch
[Alpha] Fetch the state of the provided resources from the cluster and display status in a table.
### Synopsis
[Alpha] Fetches the state of all provided resources from the cluster and displays the status in
a table.
The list of resources are provided as manifests either on the filesystem or on StdIn.
DIR:
Path to local directory.
### Examples
# Read resources from the filesystem and wait up to 1 minute for all of them to become Current
resource status fetch my-dir/
# Fetch all resources in the cluster and wait up to 5 minutes for all of them to become Current
kubectl get all --all-namespaces -oyaml | resource status fetch

View File

@@ -0,0 +1,22 @@
## wait
[Alpha] Poll the cluster until all provided resources have become Current and display progress in a table.
### Synopsis
[Alpha] Poll the cluster for the state of all the provided resources until either they have all become
Current or the timeout is reached. The output will be presented as a table.
The list of resources which should be polled are provided as manifests either on the filesystem or
on StdIn.
DIR:
Path to local directory. If not provided, input is expected on StdIn.
### Examples
# Read resources from the filesystem and wait up to 1 minute for all of them to become Current
resource status wait my-dir/
# Fetch all resources in the cluster and wait up to 5 minutes for all of them to become Current
kubectl get all --all-namespaces -oyaml | resource status wait --timeout=5m

View File

@@ -0,0 +1,57 @@
// Code generated by "mdtogo"; DO NOT EDIT.
package commands
var EventsShort=`[Alpha] Poll the cluster until all provided resources have become Current and list the status change events.`
var EventsLong=`
[Alpha] Poll the cluster for the state of all the provided resources until either they have all become
Current or the timeout is reached. The output will be status change events.
The list of resources which should be polled are provided as manifests either on the filesystem or
on StdIn.
DIR:
Path to local directory. If not provided, input is expected on StdIn.
`
var EventsExamples=`
# Read resources from the filesystem and wait up to 1 minute for all of them to become Current
resource status events my-dir/
# Fetch all resources in the cluster and wait up to 5 minutes for all of them to become Current
kubectl get all --all-namespaces -oyaml | resource status events --timeout=5m`
var FetchShort=`[Alpha] Fetch the state of the provided resources from the cluster and display status in a table.`
var FetchLong=`
[Alpha] Fetches the state of all provided resources from the cluster and displays the status in
a table.
The list of resources are provided as manifests either on the filesystem or on StdIn.
DIR:
Path to local directory.
`
var FetchExamples=`
# Read resources from the filesystem and wait up to 1 minute for all of them to become Current
resource status fetch my-dir/
# Fetch all resources in the cluster and wait up to 5 minutes for all of them to become Current
kubectl get all --all-namespaces -oyaml | resource status fetch`
var WaitShort=`[Alpha] Poll the cluster until all provided resources have become Current and display progress in a table. `
var WaitLong=`
[Alpha] Poll the cluster for the state of all the provided resources until either they have all become
Current or the timeout is reached. The output will be presented as a table.
The list of resources which should be polled are provided as manifests either on the filesystem or
on StdIn.
DIR:
Path to local directory. If not provided, input is expected on StdIn.
`
var WaitExamples=`
# Read resources from the filesystem and wait up to 1 minute for all of them to become Current
resource status wait my-dir/
# Fetch all resources in the cluster and wait up to 5 minutes for all of them to become Current
kubectl get all --all-namespaces -oyaml | resource status wait --timeout=5m`

View File

@@ -1,3 +1,8 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
//go:generate $GOBIN/mdtogo docs/commands generateddocs/commands --license=none
package status package status
import ( import (
@@ -8,7 +13,7 @@ import (
func StatusCommand() *cobra.Command { func StatusCommand() *cobra.Command {
var status = &cobra.Command{ var status = &cobra.Command{
Use: "status", Use: "status",
Short: "status reference command", Short: "[Alpha] Commands for working with resource status.",
} }
status.AddCommand(cmd.FetchCommand()) status.AddCommand(cmd.FetchCommand())