Feat: Add command to list all workflows in vela-cli (#6326)

- fixes #6326

Signed-off-by: Muralicharan Gurumoorthy <muralicharan.gurumoorthy@gmail.com>
Co-authored-by: Muralicharan Gurumoorthy <muralicharan.gurumoorthy@gmail.com>
This commit is contained in:
Anoop Gopalakrishnan 2023-09-19 19:19:08 -07:00 committed by GitHub
parent 26faaaf4f9
commit a07acc02c9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 239 additions and 1 deletions

View File

@ -63,6 +63,8 @@ import (
terraformapiv1 "github.com/oam-dev/terraform-controller/api/v1beta1"
terraformapi "github.com/oam-dev/terraform-controller/api/v1beta2"
workflowv1alpha1 "github.com/kubevela/workflow/api/v1alpha1"
oamcore "github.com/oam-dev/kubevela/apis/core.oam.dev"
"github.com/oam-dev/kubevela/apis/types"
velacue "github.com/oam-dev/kubevela/pkg/cue"
@ -93,6 +95,7 @@ func init() {
_ = metricsV1beta1api.AddToScheme(Scheme)
_ = kruisev1alpha1.AddToScheme(Scheme)
_ = gatewayv1beta1.AddToScheme(Scheme)
_ = workflowv1alpha1.AddToScheme(Scheme)
// +kubebuilder:scaffold:scheme
}

View File

@ -23,12 +23,13 @@ import (
"os"
"github.com/AlecAivazis/survey/v2"
"github.com/gosuri/uitable"
workflowv1alpha1 "github.com/kubevela/workflow/api/v1alpha1"
"github.com/pkg/errors"
"github.com/spf13/cobra"
"sigs.k8s.io/controller-runtime/pkg/client"
pkgmulticluster "github.com/kubevela/pkg/multicluster"
workflowv1alpha1 "github.com/kubevela/workflow/api/v1alpha1"
wfTypes "github.com/kubevela/workflow/pkg/types"
wfUtils "github.com/kubevela/workflow/pkg/utils"
@ -65,6 +66,7 @@ func NewWorkflowCommand(c common.Args, order string, ioStreams cmdutil.IOStreams
NewWorkflowRollbackCommand(c, ioStreams, wargs),
NewWorkflowLogsCommand(c, ioStreams, wargs),
NewWorkflowDebugCommand(c, ioStreams, wargs),
NewWorkflowListCommand(c, ioStreams, wargs),
)
return cmd
}
@ -246,6 +248,85 @@ func NewWorkflowDebugCommand(c common.Args, ioStream cmdutil.IOStreams, wargs *W
return cmd
}
// NewWorkflowListCommand create workflow list command
func NewWorkflowListCommand(c common.Args, ioStream cmdutil.IOStreams, wargs *WorkflowArgs) *cobra.Command {
cmd := &cobra.Command{
Use: "list",
Short: "List running workflows",
Long: "List running workflows",
Example: "vela workflow list",
RunE: func(cmd *cobra.Command, args []string) error {
cli, err := c.GetClient()
if err != nil {
return err
}
namespace, err := GetFlagNamespaceOrEnv(cmd, c)
if err != nil {
return err
}
if AllNamespace {
namespace = ""
}
ctx := context.Background()
return printWorkflowList(ctx, cli, namespace, ioStream)
},
}
cmd.Flags().BoolVarP(&AllNamespace, "all-namespaces", "A", false, "If true, check the specified action in all namespaces.")
addNamespaceAndEnvArg(cmd)
return cmd
}
func printWorkflowList(ctx context.Context, c client.Reader, namespace string, ioStream cmdutil.IOStreams) error {
table, err := buildWorkflowListTable(ctx, c, namespace)
if err != nil {
return err
}
ioStream.Info(table.String())
return nil
}
func buildWorkflowListTable(ctx context.Context, c client.Reader, namespace string) (*uitable.Table, error) {
table := newUITable()
header := []interface{}{"NAME", "TYPE", "PHASE", "START-TIME", "END-TIME"}
if AllNamespace {
header = append([]interface{}{"NAMESPACE"}, header...)
}
table.AddRow(header...)
applist := v1beta1.ApplicationList{}
if err := c.List(ctx, &applist, client.InNamespace(namespace)); err != nil {
return nil, errors.WithMessage(err, "unable to list application workflows")
}
for _, a := range applist.Items {
status := a.Status.Workflow
if a.Status.Workflow != nil {
if AllNamespace {
table.AddRow(a.Namespace, a.Name, "Application", status.Phase, status.StartTime, status.EndTime)
} else {
table.AddRow(a.Name, "Application", status.Phase, status.StartTime, status.EndTime)
}
}
}
wrList := workflowv1alpha1.WorkflowRunList{}
if err := c.List(ctx, &wrList, client.InNamespace(namespace)); err != nil {
return nil, errors.WithMessage(err, "unable to list workflowruns")
}
for _, w := range wrList.Items {
status := w.Status
if status.Phase != "" {
if AllNamespace {
table.AddRow(w.Namespace, w.Name, "WorkflowRun", status.Phase, status.StartTime, status.EndTime)
} else {
table.AddRow(w.Name, "WorkflowRun", status.Phase, status.StartTime, status.EndTime)
}
}
}
return table, nil
}
// WorkflowArgs is the args for workflow command
type WorkflowArgs struct {
Type string

View File

@ -17,10 +17,13 @@ limitations under the License.
package cli
import (
"bytes"
"context"
"fmt"
"os"
"strings"
"testing"
"time"
"github.com/stretchr/testify/require"
corev1 "k8s.io/api/core/v1"
@ -818,3 +821,154 @@ func TestWorkflowRollback(t *testing.T) {
})
}
}
func TestWorkflowList(t *testing.T) {
c := initArgs()
buf := new(bytes.Buffer)
ioStream := cmdutil.IOStreams{In: os.Stdin, Out: buf, ErrOut: os.Stderr}
ctx := context.TODO()
testCases := map[string]struct {
workflows []interface{}
apps []*v1beta1.Application
workflowRuns []*workflowv1alpha1.WorkflowRun
expectedErr error
namespace string
expectAppListSize int
}{
"specified all namespaces flag": {
workflows: []interface{}{
&v1beta1.Application{
ObjectMeta: metav1.ObjectMeta{
Name: "app-workflow",
Namespace: "some-ns",
},
Spec: workflowSpec,
Status: common.AppStatus{
LatestRevision: &common.Revision{
Name: "revision-v1",
},
Workflow: &common.WorkflowStatus{
Terminated: true,
Phase: workflowv1alpha1.WorkflowStateInitializing,
StartTime: metav1.NewTime(time.Now().Add(-10 * time.Minute)),
EndTime: metav1.NewTime(time.Now()),
},
},
},
&workflowv1alpha1.WorkflowRun{
ObjectMeta: metav1.ObjectMeta{
Name: "workflow-run",
Namespace: "some-ns",
},
Status: workflowv1alpha1.WorkflowRunStatus{
Phase: workflowv1alpha1.WorkflowStateExecuting,
StartTime: metav1.NewTime(time.Now().Add(-10 * time.Minute)),
EndTime: metav1.NewTime(time.Now()),
},
},
},
expectedErr: nil,
},
"specified namespace flag": {
workflows: []interface{}{
&workflowv1alpha1.WorkflowRun{
ObjectMeta: metav1.ObjectMeta{
Name: "workflow-run-1",
Namespace: "test",
},
Status: workflowv1alpha1.WorkflowRunStatus{
Phase: workflowv1alpha1.WorkflowStateExecuting,
StartTime: metav1.NewTime(time.Now().Add(-10 * time.Minute)),
EndTime: metav1.NewTime(time.Now()),
},
},
},
namespace: "test",
expectedErr: nil,
},
}
for name, tc := range testCases {
t.Run(name, func(t *testing.T) {
r := require.New(t)
buf.Reset()
cmd := NewWorkflowListCommand(c, ioStream, &WorkflowArgs{Args: c, Writer: ioStream.Out})
initCommand(cmd)
// clean up the arguments before start
cmd.SetArgs([]string{})
client, err := c.GetClient()
r.NoError(err)
if tc.workflows != nil && len(tc.workflows) > 0 {
for _, w := range tc.workflows {
if workflow, ok := w.(*workflowv1alpha1.WorkflowRun); ok {
err := client.Create(ctx, workflow)
r.NoError(err)
} else if app, ok := w.(*v1beta1.Application); ok {
err := client.Create(ctx, app)
r.NoError(err)
}
}
}
args := []string{}
if tc.namespace == "" {
args = append(args, "-A")
} else {
args = append(args, "-n", tc.namespace)
}
cmd.SetArgs(args)
err = cmd.Execute()
lines := strings.Split(strings.TrimSpace(buf.String()), "\n")
headerFields := strings.Fields(lines[0])
if tc.namespace == "" {
r.Equal(headerFields[0], "NAMESPACE")
} else {
r.Equal(headerFields[0], "NAME")
}
offset := 0
for i, line := range lines[1:] {
fields := strings.Fields(line)
if workflow, ok := tc.workflows[i].(*workflowv1alpha1.WorkflowRun); ok {
if tc.namespace == "" {
r.Equal(fields[0], workflow.Namespace)
offset = offset + 1
} else if tc.namespace == workflow.Namespace {
r.Equal(fields[0+offset], workflow.Name)
r.Equal(fields[1+offset], "WorkflowRun")
r.Equal(fields[2+offset], string(workflow.Status.Phase))
r.Equal(fields[3+offset], workflow.Status.StartTime.Format("2006-01-02"))
r.Equal(fields[4+offset], workflow.Status.StartTime.Format("15:04:05"))
//skipping a few fields due to the format including timezone information
r.Equal(fields[7+offset], workflow.Status.EndTime.Format("2006-01-02"))
r.Equal(fields[8+offset], workflow.Status.EndTime.Format("15:04:05"))
}
} else if app, ok := tc.workflows[i].(*v1beta1.Application); ok {
offset = 0
if tc.namespace == "" {
r.Equal(fields[0+offset], app.Namespace)
offset = offset + 1
} else if tc.namespace == app.Namespace {
r.Equal(fields[0+offset], app.Name)
r.Equal(fields[1+offset], "Application")
r.Equal(fields[2+offset], string(app.Status.Workflow.Phase))
r.Equal(fields[3+offset], app.Status.Workflow.StartTime.Format("2006-01-02"))
r.Equal(fields[4+offset], app.Status.Workflow.StartTime.Format("15:04:05"))
//skipping a few fields due to the format including timezone information
r.Equal(fields[7+offset], app.Status.Workflow.EndTime.Format("2006-01-02"))
r.Equal(fields[8+offset], app.Status.Workflow.EndTime.Format("15:04:05"))
}
}
}
if tc.expectedErr != nil {
r.Equal(tc.expectedErr, err)
return
}
r.NoError(err)
})
}
}