Move workflow listing to its own package
* adds the AGE, DURATION, and PRIORITY columns * adds the --all-namespaces flag
This commit is contained in:
parent
3b4a9fb82c
commit
a8e649eb67
@ -1 +1 @@
|
|||||||
NAME PHASE
|
NAME STATUS AGE DURATION PRIORITY
|
||||||
|
@ -1,2 +1,2 @@
|
|||||||
NAME PHASE
|
NAME STATUS AGE DURATION PRIORITY
|
||||||
fake-wf completed
|
fake-wf completed 5m 3m 0
|
||||||
|
@ -2,13 +2,17 @@ package workflow
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
|
|
||||||
|
"github.com/argoproj/pkg/humanize"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
||||||
|
|
||||||
|
"github.com/ian-howell/airshipctl/pkg/apis/workflow/v1alpha1"
|
||||||
"github.com/ian-howell/airshipctl/pkg/environment"
|
"github.com/ian-howell/airshipctl/pkg/environment"
|
||||||
"github.com/ian-howell/airshipctl/pkg/util"
|
"github.com/ian-howell/airshipctl/pkg/util"
|
||||||
|
"github.com/ian-howell/airshipctl/pkg/workflow"
|
||||||
wfenv "github.com/ian-howell/airshipctl/pkg/workflow/environment"
|
wfenv "github.com/ian-howell/airshipctl/pkg/workflow/environment"
|
||||||
|
wfutil "github.com/ian-howell/airshipctl/pkg/workflow/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
// NewWorkflowListCommand is a command for listing argo workflows
|
// NewWorkflowListCommand is a command for listing argo workflows
|
||||||
@ -24,19 +28,60 @@ func NewWorkflowListCommand(rootSettings *environment.AirshipCTLSettings) *cobra
|
|||||||
fmt.Fprintf(out, "settings for %s were not registered\n", PluginSettingsID)
|
fmt.Fprintf(out, "settings for %s were not registered\n", PluginSettingsID)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
clientSet := wfSettings.ArgoClient.ArgoprojV1alpha1()
|
wflist, err := workflow.ListWorkflows(wfSettings)
|
||||||
wflist, err := clientSet.Workflows(wfSettings.Namespace).List(v1.ListOptions{})
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err.Error())
|
fmt.Fprintf(out, "Could not list workflows: %s\n", err.Error())
|
||||||
}
|
return
|
||||||
w := util.NewTabWriter(out)
|
|
||||||
defer w.Flush()
|
|
||||||
fmt.Fprintf(w, "%s\t%s\n", "NAME", "PHASE")
|
|
||||||
for _, wf := range wflist.Items {
|
|
||||||
fmt.Fprintf(w, "%s\t%s\n", wf.Name, wf.Status.Phase)
|
|
||||||
}
|
}
|
||||||
|
printTable(out, wflist, wfSettings)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
return workflowListCmd
|
return workflowListCmd
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// printTable pretty prints the list of workflows to out
|
||||||
|
func printTable(out io.Writer, wfList []v1alpha1.Workflow, wfSettings *wfenv.Settings) {
|
||||||
|
w := util.NewTabWriter(out)
|
||||||
|
defer w.Flush()
|
||||||
|
if wfSettings.AllNamespaces {
|
||||||
|
fmt.Fprint(w, "NAMESPACE\t")
|
||||||
|
}
|
||||||
|
fmt.Fprint(w, "NAME\tSTATUS\tAGE\tDURATION\tPRIORITY")
|
||||||
|
fmt.Fprint(w, "\n")
|
||||||
|
for _, wf := range wfList {
|
||||||
|
ageStr := humanize.RelativeDurationShort(wf.ObjectMeta.CreationTimestamp.Time, util.Now())
|
||||||
|
durationStr := humanize.RelativeDurationShort(wf.Status.StartedAt.Time, wf.Status.FinishedAt.Time)
|
||||||
|
if wfSettings.AllNamespaces {
|
||||||
|
fmt.Fprintf(w, "%s\t", wf.ObjectMeta.Namespace)
|
||||||
|
}
|
||||||
|
var priority int
|
||||||
|
if wf.Spec.Priority != nil {
|
||||||
|
priority = int(*wf.Spec.Priority)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%d\n", wf.ObjectMeta.Name, workflowStatus(&wf), ageStr, durationStr, priority)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// workflowStatus returns a human readable inferred workflow status based on workflow phase and conditions
|
||||||
|
func workflowStatus(wf *v1alpha1.Workflow) v1alpha1.NodePhase {
|
||||||
|
switch wf.Status.Phase {
|
||||||
|
case v1alpha1.NodeRunning:
|
||||||
|
if wfutil.IsWorkflowSuspended(wf) {
|
||||||
|
return "Running (Suspended)"
|
||||||
|
}
|
||||||
|
return wf.Status.Phase
|
||||||
|
case v1alpha1.NodeFailed:
|
||||||
|
if wfutil.IsWorkflowTerminated(wf) {
|
||||||
|
return "Failed (Terminated)"
|
||||||
|
}
|
||||||
|
return wf.Status.Phase
|
||||||
|
case "", v1alpha1.NodePending:
|
||||||
|
if !wf.ObjectMeta.CreationTimestamp.IsZero() {
|
||||||
|
return v1alpha1.NodePending
|
||||||
|
}
|
||||||
|
return "Unknown"
|
||||||
|
default:
|
||||||
|
return wf.Status.Phase
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -2,6 +2,7 @@ package workflow_test
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/apimachinery/pkg/runtime"
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
@ -10,6 +11,7 @@ import (
|
|||||||
"github.com/ian-howell/airshipctl/cmd/workflow"
|
"github.com/ian-howell/airshipctl/cmd/workflow"
|
||||||
"github.com/ian-howell/airshipctl/pkg/apis/workflow/v1alpha1"
|
"github.com/ian-howell/airshipctl/pkg/apis/workflow/v1alpha1"
|
||||||
argofake "github.com/ian-howell/airshipctl/pkg/client/clientset/versioned/fake"
|
argofake "github.com/ian-howell/airshipctl/pkg/client/clientset/versioned/fake"
|
||||||
|
"github.com/ian-howell/airshipctl/pkg/util"
|
||||||
wfenv "github.com/ian-howell/airshipctl/pkg/workflow/environment"
|
wfenv "github.com/ian-howell/airshipctl/pkg/workflow/environment"
|
||||||
"github.com/ian-howell/airshipctl/test"
|
"github.com/ian-howell/airshipctl/test"
|
||||||
)
|
)
|
||||||
@ -41,9 +43,18 @@ func TestWorkflowList(t *testing.T) {
|
|||||||
&v1alpha1.Workflow{
|
&v1alpha1.Workflow{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: "fake-wf",
|
Name: "fake-wf",
|
||||||
|
CreationTimestamp: metav1.Time{
|
||||||
|
Time: util.Clock().Add(5 * time.Minute),
|
||||||
|
},
|
||||||
},
|
},
|
||||||
Status: v1alpha1.WorkflowStatus{
|
Status: v1alpha1.WorkflowStatus{
|
||||||
Phase: "completed",
|
Phase: "completed",
|
||||||
|
StartedAt: metav1.Time{
|
||||||
|
Time: util.Clock().Add(5 * time.Minute),
|
||||||
|
},
|
||||||
|
FinishedAt: metav1.Time{
|
||||||
|
Time: util.Clock().Add(8 * time.Minute),
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
2
go.mod
2
go.mod
@ -3,6 +3,8 @@ module github.com/ian-howell/airshipctl
|
|||||||
go 1.12
|
go 1.12
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
github.com/argoproj/pkg v0.0.0-20190409001913-7e3ef65c8d44
|
||||||
|
github.com/dustin/go-humanize v1.0.0 // indirect
|
||||||
github.com/go-critic/go-critic v0.0.0-20181204210945-ee9bf5809ead // indirect
|
github.com/go-critic/go-critic v0.0.0-20181204210945-ee9bf5809ead // indirect
|
||||||
github.com/go-toolsmith/pkgload v0.0.0-20181120203407-5122569a890b // indirect
|
github.com/go-toolsmith/pkgload v0.0.0-20181120203407-5122569a890b // indirect
|
||||||
github.com/gogo/protobuf v1.2.1 // indirect
|
github.com/gogo/protobuf v1.2.1 // indirect
|
||||||
|
4
go.sum
4
go.sum
@ -14,6 +14,8 @@ github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV
|
|||||||
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
|
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
|
||||||
github.com/Quasilyte/go-consistent v0.0.0-20181230194409-8f8379e70f99/go.mod h1:ds1OLa3HF2x4OGKCx0pNTVL1s9Ii/2mT0Bg/8PtW6AM=
|
github.com/Quasilyte/go-consistent v0.0.0-20181230194409-8f8379e70f99/go.mod h1:ds1OLa3HF2x4OGKCx0pNTVL1s9Ii/2mT0Bg/8PtW6AM=
|
||||||
github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg=
|
github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg=
|
||||||
|
github.com/argoproj/pkg v0.0.0-20190409001913-7e3ef65c8d44 h1:fqYoz7qu4K8/mBdm/N1p7qKtdPhlwOSHlTQoAu4rATs=
|
||||||
|
github.com/argoproj/pkg v0.0.0-20190409001913-7e3ef65c8d44/go.mod h1:2EZ44RG/CcgtPTwrRR0apOc7oU6UIw8GjCUJWZ8X3bM=
|
||||||
github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
|
github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
|
||||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||||
github.com/blang/semver v3.5.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
|
github.com/blang/semver v3.5.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
|
||||||
@ -29,6 +31,8 @@ github.com/dgrijalva/jwt-go v0.0.0-20160705203006-01aeca54ebda/go.mod h1:E3ru+11
|
|||||||
github.com/docker/docker v0.7.3-0.20190327010347-be7ac8be2ae0/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
github.com/docker/docker v0.7.3-0.20190327010347-be7ac8be2ae0/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||||
github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
|
github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
|
||||||
github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM=
|
github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM=
|
||||||
|
github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo=
|
||||||
|
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
||||||
github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc=
|
github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc=
|
||||||
github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
|
github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
|
||||||
github.com/evanphx/json-patch v0.0.0-20190203023257-5858425f7550 h1:mV9jbLoSW/8m4VK16ZkHTozJa8sesK5u5kTMFysTYac=
|
github.com/evanphx/json-patch v0.0.0-20190203023257-5858425f7550 h1:mV9jbLoSW/8m4VK16ZkHTozJa8sesK5u5kTMFysTYac=
|
||||||
|
@ -2,6 +2,7 @@ package util
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"os"
|
"os"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
// IsReadable returns nil if the file at path is readable. An error is returned otherwise.
|
// IsReadable returns nil if the file at path is readable. An error is returned otherwise.
|
||||||
@ -12,3 +13,29 @@ func IsReadable(path string) error {
|
|||||||
}
|
}
|
||||||
return f.Close()
|
return f.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Clock is mostly useful for unit testing, allowing for mocking out of time
|
||||||
|
var clock *time.Time
|
||||||
|
|
||||||
|
// Now returns time.Now() if the Clock is nil. If the Clock is not nil, it is
|
||||||
|
// returned instead. This is useful for testing
|
||||||
|
func Now() time.Time {
|
||||||
|
if clock == nil {
|
||||||
|
return time.Now()
|
||||||
|
}
|
||||||
|
return *clock
|
||||||
|
}
|
||||||
|
|
||||||
|
// InitClock creates a clock for unit testing
|
||||||
|
func InitClock() {
|
||||||
|
mockClock := time.Date(2000, time.January, 0, 0, 0, 0, 0, time.UTC)
|
||||||
|
clock = &mockClock
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clock gives access to the mocked clock
|
||||||
|
func Clock() time.Time {
|
||||||
|
if clock == nil {
|
||||||
|
InitClock()
|
||||||
|
}
|
||||||
|
return *clock
|
||||||
|
}
|
||||||
|
@ -14,6 +14,9 @@ type Settings struct {
|
|||||||
// Namespace is the kubernetes namespace to be used during the context of this action
|
// Namespace is the kubernetes namespace to be used during the context of this action
|
||||||
Namespace string
|
Namespace string
|
||||||
|
|
||||||
|
// AllNamespaces denotes whether or not to use all namespaces. It will override the Namespace string
|
||||||
|
AllNamespaces bool
|
||||||
|
|
||||||
// KubeConfigFilePath is the path to the kubernetes configuration file.
|
// KubeConfigFilePath is the path to the kubernetes configuration file.
|
||||||
// This flag is only needed when airshipctl is being used
|
// This flag is only needed when airshipctl is being used
|
||||||
// out-of-cluster
|
// out-of-cluster
|
||||||
@ -37,6 +40,7 @@ func (s *Settings) InitFlags(cmd *cobra.Command) {
|
|||||||
flags := cmd.PersistentFlags()
|
flags := cmd.PersistentFlags()
|
||||||
flags.StringVar(&s.KubeConfigFilePath, "kubeconfig", "", "path to kubeconfig")
|
flags.StringVar(&s.KubeConfigFilePath, "kubeconfig", "", "path to kubeconfig")
|
||||||
flags.StringVar(&s.Namespace, "namespace", "default", "kubernetes namespace to use for the context of this command")
|
flags.StringVar(&s.Namespace, "namespace", "default", "kubernetes namespace to use for the context of this command")
|
||||||
|
flags.BoolVar(&s.AllNamespaces, "all-namespaces", false, "use all kubernetes namespaces for the context of this command")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Init assigns default values
|
// Init assigns default values
|
||||||
|
51
pkg/workflow/list.go
Normal file
51
pkg/workflow/list.go
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
package workflow
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sort"
|
||||||
|
|
||||||
|
apiv1 "k8s.io/api/core/v1"
|
||||||
|
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
|
||||||
|
"github.com/ian-howell/airshipctl/pkg/apis/workflow/v1alpha1"
|
||||||
|
v1alpha1client "github.com/ian-howell/airshipctl/pkg/client/clientset/versioned/typed/workflow/v1alpha1"
|
||||||
|
"github.com/ian-howell/airshipctl/pkg/workflow/environment"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ListWorkflows returns a list of Workflows
|
||||||
|
func ListWorkflows(settings *environment.Settings) ([]v1alpha1.Workflow, error) {
|
||||||
|
var clientSet v1alpha1client.WorkflowInterface
|
||||||
|
if settings.AllNamespaces {
|
||||||
|
clientSet = settings.ArgoClient.ArgoprojV1alpha1().Workflows(apiv1.NamespaceAll)
|
||||||
|
} else {
|
||||||
|
clientSet = settings.ArgoClient.ArgoprojV1alpha1().Workflows(settings.Namespace)
|
||||||
|
}
|
||||||
|
wflist, err := clientSet.List(v1.ListOptions{})
|
||||||
|
if err != nil {
|
||||||
|
return []v1alpha1.Workflow{}, err
|
||||||
|
}
|
||||||
|
workflows := wflist.Items
|
||||||
|
sort.Sort(ByFinishedAt(workflows))
|
||||||
|
return workflows, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ByFinishedAt is a sort interface which sorts running jobs earlier before considering FinishedAt
|
||||||
|
type ByFinishedAt []v1alpha1.Workflow
|
||||||
|
|
||||||
|
func (f ByFinishedAt) Len() int { return len(f) }
|
||||||
|
func (f ByFinishedAt) Swap(i, j int) { f[i], f[j] = f[j], f[i] }
|
||||||
|
func (f ByFinishedAt) Less(i, j int) bool {
|
||||||
|
iStart := f[i].ObjectMeta.CreationTimestamp
|
||||||
|
iFinish := f[i].Status.FinishedAt
|
||||||
|
jStart := f[j].ObjectMeta.CreationTimestamp
|
||||||
|
jFinish := f[j].Status.FinishedAt
|
||||||
|
if iFinish.IsZero() && jFinish.IsZero() {
|
||||||
|
return !iStart.Before(&jStart)
|
||||||
|
}
|
||||||
|
if iFinish.IsZero() && !jFinish.IsZero() {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if !iFinish.IsZero() && jFinish.IsZero() {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return jFinish.Before(&iFinish)
|
||||||
|
}
|
23
pkg/workflow/util/util.go
Normal file
23
pkg/workflow/util/util.go
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
package util
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/ian-howell/airshipctl/pkg/apis/workflow/v1alpha1"
|
||||||
|
)
|
||||||
|
|
||||||
|
// IsWorkflowSuspended returns whether or not a workflow is considered suspended
|
||||||
|
func IsWorkflowSuspended(wf *v1alpha1.Workflow) bool {
|
||||||
|
if wf.Spec.Suspend != nil && *wf.Spec.Suspend {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
for _, node := range wf.Status.Nodes {
|
||||||
|
if node.Type == v1alpha1.NodeTypeSuspend && node.Phase == v1alpha1.NodeRunning {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsWorkflowTerminated returns whether or not a workflow is considered terminated
|
||||||
|
func IsWorkflowTerminated(wf *v1alpha1.Workflow) bool {
|
||||||
|
return wf.Spec.ActiveDeadlineSeconds != nil && *wf.Spec.ActiveDeadlineSeconds == 0
|
||||||
|
}
|
@ -11,6 +11,8 @@ import (
|
|||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"k8s.io/apimachinery/pkg/runtime"
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
|
|
||||||
|
"github.com/ian-howell/airshipctl/pkg/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
// UpdateGolden writes out the golden files with the latest values, rather than failing the test.
|
// UpdateGolden writes out the golden files with the latest values, rather than failing the test.
|
||||||
@ -33,6 +35,7 @@ type CmdTest struct {
|
|||||||
// output from its golden file, or generates golden files if the -update flag
|
// output from its golden file, or generates golden files if the -update flag
|
||||||
// is passed
|
// is passed
|
||||||
func RunTest(t *testing.T, test *CmdTest, cmd *cobra.Command) {
|
func RunTest(t *testing.T, test *CmdTest, cmd *cobra.Command) {
|
||||||
|
util.InitClock()
|
||||||
actual := &bytes.Buffer{}
|
actual := &bytes.Buffer{}
|
||||||
cmd.SetOutput(actual)
|
cmd.SetOutput(actual)
|
||||||
args := strings.Fields(test.CmdLine)
|
args := strings.Fields(test.CmdLine)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user