github.com/oam-dev/kubevela@v1.9.11/references/cli/workflow_test.go (about) 1 /* 2 Copyright 2021 The KubeVela Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package cli 18 19 import ( 20 "bytes" 21 "context" 22 "fmt" 23 "os" 24 "strings" 25 "testing" 26 "time" 27 28 "github.com/stretchr/testify/require" 29 corev1 "k8s.io/api/core/v1" 30 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 31 "k8s.io/apimachinery/pkg/runtime" 32 "k8s.io/apimachinery/pkg/types" 33 34 workflowv1alpha1 "github.com/kubevela/workflow/api/v1alpha1" 35 wfTypes "github.com/kubevela/workflow/pkg/types" 36 37 "github.com/oam-dev/kubevela/apis/core.oam.dev/common" 38 "github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1" 39 cmdutil "github.com/oam-dev/kubevela/pkg/utils/util" 40 ) 41 42 var workflowSpec = v1beta1.ApplicationSpec{ 43 Components: []common.ApplicationComponent{{ 44 Name: "test-component", 45 Type: "worker", 46 Properties: &runtime.RawExtension{Raw: []byte(`{"cmd":["sleep","1000"],"image":"busybox"}`)}, 47 }}, 48 Workflow: &v1beta1.Workflow{ 49 Steps: []workflowv1alpha1.WorkflowStep{{ 50 WorkflowStepBase: workflowv1alpha1.WorkflowStepBase{ 51 Name: "test-wf1", 52 Type: "foowf", 53 Properties: &runtime.RawExtension{Raw: []byte(`{"namespace":"default"}`)}, 54 }, 55 }}, 56 }, 57 } 58 59 func TestWorkflowSuspend(t *testing.T) { 60 c := initArgs() 61 ioStream := cmdutil.IOStreams{In: os.Stdin, Out: os.Stdout, ErrOut: os.Stderr} 62 ctx := context.TODO() 63 64 testCases := map[string]struct { 65 app *v1beta1.Application 66 expected *v1beta1.Application 67 step string 68 expectedErr string 69 }{ 70 "no app name specified": { 71 expectedErr: "please specify the name of application/workflow", 72 }, 73 "workflow not running": { 74 app: &v1beta1.Application{ 75 ObjectMeta: metav1.ObjectMeta{ 76 Name: "workflow-not-running", 77 Namespace: "default", 78 }, 79 Spec: workflowSpec, 80 Status: common.AppStatus{}, 81 }, 82 expectedErr: "the workflow in application workflow-not-running is not start", 83 }, 84 "suspend successfully": { 85 app: &v1beta1.Application{ 86 ObjectMeta: metav1.ObjectMeta{ 87 Name: "workflow", 88 Namespace: "test", 89 }, 90 Spec: workflowSpec, 91 Status: common.AppStatus{ 92 Workflow: &common.WorkflowStatus{ 93 Suspend: false, 94 }, 95 }, 96 }, 97 expected: &v1beta1.Application{ 98 ObjectMeta: metav1.ObjectMeta{ 99 Name: "workflow", 100 Namespace: "test", 101 }, 102 Spec: workflowSpec, 103 Status: common.AppStatus{ 104 Workflow: &common.WorkflowStatus{ 105 Suspend: true, 106 }, 107 }, 108 }, 109 }, 110 "step not found": { 111 app: &v1beta1.Application{ 112 ObjectMeta: metav1.ObjectMeta{ 113 Name: "step-not-found", 114 Namespace: "default", 115 }, 116 Spec: workflowSpec, 117 Status: common.AppStatus{ 118 Workflow: &common.WorkflowStatus{ 119 Suspend: false, 120 }, 121 }, 122 }, 123 step: "not-found", 124 expectedErr: "can not find", 125 }, 126 "suspend all": { 127 app: &v1beta1.Application{ 128 ObjectMeta: metav1.ObjectMeta{ 129 Name: "suspend-all", 130 Namespace: "default", 131 }, 132 Spec: workflowSpec, 133 Status: common.AppStatus{ 134 Workflow: &common.WorkflowStatus{ 135 Steps: []workflowv1alpha1.WorkflowStepStatus{ 136 { 137 StepStatus: workflowv1alpha1.StepStatus{ 138 Name: "step1", 139 Phase: workflowv1alpha1.WorkflowStepPhaseRunning, 140 }, 141 SubStepsStatus: []workflowv1alpha1.StepStatus{ 142 { 143 Name: "sub1", 144 Phase: workflowv1alpha1.WorkflowStepPhaseRunning, 145 }, 146 }, 147 }, 148 { 149 StepStatus: workflowv1alpha1.StepStatus{ 150 Name: "step2", 151 Phase: workflowv1alpha1.WorkflowStepPhaseRunning, 152 }, 153 SubStepsStatus: []workflowv1alpha1.StepStatus{ 154 { 155 Name: "sub2", 156 Phase: workflowv1alpha1.WorkflowStepPhaseSucceeded, 157 }, 158 }, 159 }, 160 }, 161 }, 162 }, 163 }, 164 expected: &v1beta1.Application{ 165 Status: common.AppStatus{ 166 Workflow: &common.WorkflowStatus{ 167 Suspend: true, 168 Steps: []workflowv1alpha1.WorkflowStepStatus{ 169 { 170 StepStatus: workflowv1alpha1.StepStatus{ 171 Name: "step1", 172 Phase: workflowv1alpha1.WorkflowStepPhaseSuspending, 173 }, 174 SubStepsStatus: []workflowv1alpha1.StepStatus{ 175 { 176 Name: "sub1", 177 Phase: workflowv1alpha1.WorkflowStepPhaseSuspending, 178 }, 179 }, 180 }, 181 { 182 StepStatus: workflowv1alpha1.StepStatus{ 183 Name: "step2", 184 Phase: workflowv1alpha1.WorkflowStepPhaseSuspending, 185 }, 186 SubStepsStatus: []workflowv1alpha1.StepStatus{ 187 { 188 Name: "sub2", 189 Phase: workflowv1alpha1.WorkflowStepPhaseSucceeded, 190 }, 191 }, 192 }, 193 }, 194 }, 195 }, 196 }, 197 }, 198 "suspend specific step": { 199 app: &v1beta1.Application{ 200 ObjectMeta: metav1.ObjectMeta{ 201 Name: "suspend-step", 202 Namespace: "default", 203 }, 204 Spec: workflowSpec, 205 Status: common.AppStatus{ 206 Workflow: &common.WorkflowStatus{ 207 Steps: []workflowv1alpha1.WorkflowStepStatus{ 208 { 209 StepStatus: workflowv1alpha1.StepStatus{ 210 Name: "step1", 211 Phase: workflowv1alpha1.WorkflowStepPhaseRunning, 212 }, 213 SubStepsStatus: []workflowv1alpha1.StepStatus{ 214 { 215 Name: "sub1", 216 Phase: workflowv1alpha1.WorkflowStepPhaseRunning, 217 }, 218 }, 219 }, 220 }, 221 }, 222 }, 223 }, 224 expected: &v1beta1.Application{ 225 Status: common.AppStatus{ 226 Workflow: &common.WorkflowStatus{ 227 Suspend: true, 228 Steps: []workflowv1alpha1.WorkflowStepStatus{ 229 { 230 StepStatus: workflowv1alpha1.StepStatus{ 231 Name: "step1", 232 Phase: workflowv1alpha1.WorkflowStepPhaseSuspending, 233 }, 234 SubStepsStatus: []workflowv1alpha1.StepStatus{ 235 { 236 Name: "sub1", 237 Phase: workflowv1alpha1.WorkflowStepPhaseSuspending, 238 }, 239 }, 240 }, 241 }, 242 }, 243 }, 244 }, 245 step: "step1", 246 }, 247 "suspend specific sub step": { 248 app: &v1beta1.Application{ 249 ObjectMeta: metav1.ObjectMeta{ 250 Name: "suspend-sub-step", 251 Namespace: "default", 252 }, 253 Spec: workflowSpec, 254 Status: common.AppStatus{ 255 Workflow: &common.WorkflowStatus{ 256 Steps: []workflowv1alpha1.WorkflowStepStatus{ 257 { 258 StepStatus: workflowv1alpha1.StepStatus{ 259 Name: "step1", 260 Phase: workflowv1alpha1.WorkflowStepPhaseRunning, 261 }, 262 SubStepsStatus: []workflowv1alpha1.StepStatus{ 263 { 264 Name: "sub1", 265 Phase: workflowv1alpha1.WorkflowStepPhaseRunning, 266 }, 267 }, 268 }, 269 }, 270 }, 271 }, 272 }, 273 expected: &v1beta1.Application{ 274 Status: common.AppStatus{ 275 Workflow: &common.WorkflowStatus{ 276 Suspend: true, 277 Steps: []workflowv1alpha1.WorkflowStepStatus{ 278 { 279 StepStatus: workflowv1alpha1.StepStatus{ 280 Name: "step1", 281 Phase: workflowv1alpha1.WorkflowStepPhaseRunning, 282 }, 283 SubStepsStatus: []workflowv1alpha1.StepStatus{ 284 { 285 Name: "sub1", 286 Phase: workflowv1alpha1.WorkflowStepPhaseSuspending, 287 }, 288 }, 289 }, 290 }, 291 }, 292 }, 293 }, 294 step: "sub1", 295 }, 296 } 297 298 for name, tc := range testCases { 299 t.Run(name, func(t *testing.T) { 300 r := require.New(t) 301 cmd := NewWorkflowSuspendCommand(c, ioStream, &WorkflowArgs{Args: c, Writer: ioStream.Out}) 302 initCommand(cmd) 303 // clean up the arguments before start 304 cmd.SetArgs([]string{}) 305 client, err := c.GetClient() 306 r.NoError(err) 307 if tc.app != nil { 308 err := client.Create(ctx, tc.app) 309 r.NoError(err) 310 cmdArgs := []string{tc.app.Name} 311 if tc.app.Namespace != corev1.NamespaceDefault { 312 err := client.Create(ctx, &corev1.Namespace{ 313 ObjectMeta: metav1.ObjectMeta{ 314 Name: tc.app.Namespace, 315 }, 316 }) 317 r.NoError(err) 318 cmdArgs = append(cmdArgs, "-n", tc.app.Namespace) 319 cmd.SetArgs([]string{tc.app.Name, "-n", tc.app.Namespace}) 320 } 321 if tc.step != "" { 322 cmdArgs = append(cmdArgs, "--step", tc.step) 323 } 324 cmd.SetArgs(cmdArgs) 325 } 326 err = cmd.Execute() 327 if tc.expectedErr != "" { 328 r.Contains(err.Error(), tc.expectedErr) 329 return 330 } 331 r.NoError(err) 332 333 wf := &v1beta1.Application{} 334 err = client.Get(ctx, types.NamespacedName{ 335 Namespace: tc.app.Namespace, 336 Name: tc.app.Name, 337 }, wf) 338 r.NoError(err) 339 r.Equal(true, wf.Status.Workflow.Suspend) 340 r.Equal(tc.expected.Status, wf.Status) 341 }) 342 } 343 } 344 345 func TestWorkflowResume(t *testing.T) { 346 c := initArgs() 347 ioStream := cmdutil.IOStreams{In: os.Stdin, Out: os.Stdout, ErrOut: os.Stderr} 348 ctx := context.TODO() 349 350 testCases := map[string]struct { 351 app *v1beta1.Application 352 expectedErr error 353 }{ 354 "no app name specified": { 355 expectedErr: fmt.Errorf("please specify the name of application/workflow"), 356 }, 357 "workflow not suspended": { 358 app: &v1beta1.Application{ 359 ObjectMeta: metav1.ObjectMeta{ 360 Name: "workflow-not-suspended", 361 Namespace: "default", 362 }, 363 Spec: workflowSpec, 364 Status: common.AppStatus{ 365 Workflow: &common.WorkflowStatus{ 366 Suspend: false, 367 }, 368 }, 369 }, 370 }, 371 "workflow not running": { 372 app: &v1beta1.Application{ 373 ObjectMeta: metav1.ObjectMeta{ 374 Name: "workflow-not-running", 375 Namespace: "default", 376 }, 377 Spec: workflowSpec, 378 Status: common.AppStatus{}, 379 }, 380 expectedErr: fmt.Errorf("the workflow in application workflow-not-running is not start"), 381 }, 382 "workflow terminated": { 383 app: &v1beta1.Application{ 384 ObjectMeta: metav1.ObjectMeta{ 385 Name: "workflow-terminated", 386 Namespace: "default", 387 }, 388 Spec: workflowSpec, 389 Status: common.AppStatus{ 390 Workflow: &common.WorkflowStatus{ 391 Terminated: true, 392 }, 393 }, 394 }, 395 expectedErr: fmt.Errorf("can not resume a terminated workflow"), 396 }, 397 "resume successfully": { 398 app: &v1beta1.Application{ 399 ObjectMeta: metav1.ObjectMeta{ 400 Name: "workflow", 401 Namespace: "test", 402 }, 403 Spec: workflowSpec, 404 Status: common.AppStatus{ 405 Workflow: &common.WorkflowStatus{ 406 Suspend: true, 407 Steps: []workflowv1alpha1.WorkflowStepStatus{ 408 { 409 StepStatus: workflowv1alpha1.StepStatus{ 410 Type: "suspend", 411 Phase: "running", 412 }, 413 }, 414 { 415 StepStatus: workflowv1alpha1.StepStatus{ 416 Type: "step-group", 417 }, 418 SubStepsStatus: []workflowv1alpha1.StepStatus{ 419 { 420 Type: "suspend", 421 Phase: "running", 422 }, 423 }, 424 }, 425 }, 426 }, 427 }, 428 }, 429 }, 430 } 431 432 for name, tc := range testCases { 433 t.Run(name, func(t *testing.T) { 434 r := require.New(t) 435 cmd := NewWorkflowResumeCommand(c, ioStream, &WorkflowArgs{Args: c, Writer: ioStream.Out}) 436 initCommand(cmd) 437 // clean up the arguments before start 438 cmd.SetArgs([]string{}) 439 client, err := c.GetClient() 440 r.NoError(err) 441 if tc.app != nil { 442 err := client.Create(ctx, tc.app) 443 r.NoError(err) 444 445 if tc.app.Namespace != corev1.NamespaceDefault { 446 err := client.Create(ctx, &corev1.Namespace{ 447 ObjectMeta: metav1.ObjectMeta{ 448 Name: tc.app.Namespace, 449 }, 450 }) 451 r.NoError(err) 452 cmd.SetArgs([]string{tc.app.Name, "-n", tc.app.Namespace}) 453 } else { 454 cmd.SetArgs([]string{tc.app.Name}) 455 } 456 } 457 err = cmd.Execute() 458 if tc.expectedErr != nil { 459 r.Equal(tc.expectedErr, err) 460 return 461 } 462 r.NoError(err) 463 464 wf := &v1beta1.Application{} 465 err = client.Get(ctx, types.NamespacedName{ 466 Namespace: tc.app.Namespace, 467 Name: tc.app.Name, 468 }, wf) 469 r.NoError(err) 470 r.Equal(false, wf.Status.Workflow.Suspend) 471 for _, step := range wf.Status.Workflow.Steps { 472 if step.Type == "suspend" { 473 r.Equal(step.Phase, workflowv1alpha1.WorkflowStepPhaseRunning) 474 } 475 for _, sub := range step.SubStepsStatus { 476 if sub.Type == "suspend" { 477 r.Equal(sub.Phase, workflowv1alpha1.WorkflowStepPhaseRunning) 478 } 479 } 480 } 481 }) 482 } 483 } 484 485 func TestWorkflowTerminate(t *testing.T) { 486 c := initArgs() 487 ioStream := cmdutil.IOStreams{In: os.Stdin, Out: os.Stdout, ErrOut: os.Stderr} 488 ctx := context.TODO() 489 490 testCases := map[string]struct { 491 app *v1beta1.Application 492 expectedErr error 493 }{ 494 "no app name specified": { 495 expectedErr: fmt.Errorf("please specify the name of application/workflow"), 496 }, 497 "workflow not running": { 498 app: &v1beta1.Application{ 499 ObjectMeta: metav1.ObjectMeta{ 500 Name: "workflow-not-running", 501 Namespace: "default", 502 }, 503 Spec: workflowSpec, 504 Status: common.AppStatus{}, 505 }, 506 expectedErr: fmt.Errorf("the workflow in application workflow-not-running is not start"), 507 }, 508 "terminate successfully": { 509 app: &v1beta1.Application{ 510 ObjectMeta: metav1.ObjectMeta{ 511 Name: "workflow", 512 Namespace: "test", 513 }, 514 Spec: workflowSpec, 515 Status: common.AppStatus{ 516 Workflow: &common.WorkflowStatus{ 517 Terminated: false, 518 Steps: []workflowv1alpha1.WorkflowStepStatus{ 519 { 520 StepStatus: workflowv1alpha1.StepStatus{ 521 Name: "1", 522 Type: "suspend", 523 Phase: "succeeded", 524 }, 525 }, 526 { 527 StepStatus: workflowv1alpha1.StepStatus{ 528 Name: "2", 529 Type: "suspend", 530 Phase: "running", 531 }, 532 }, 533 { 534 StepStatus: workflowv1alpha1.StepStatus{ 535 Name: "3", 536 Type: "step-group", 537 Phase: "running", 538 }, 539 SubStepsStatus: []workflowv1alpha1.StepStatus{ 540 { 541 Type: "suspend", 542 Phase: "running", 543 }, 544 { 545 Type: "suspend", 546 Phase: "succeeded", 547 }, 548 }, 549 }, 550 }, 551 }, 552 }, 553 }, 554 }, 555 } 556 557 for name, tc := range testCases { 558 t.Run(name, func(t *testing.T) { 559 r := require.New(t) 560 cmd := NewWorkflowTerminateCommand(c, ioStream, &WorkflowArgs{Args: c, Writer: ioStream.Out}) 561 initCommand(cmd) 562 // clean up the arguments before start 563 cmd.SetArgs([]string{}) 564 client, err := c.GetClient() 565 r.NoError(err) 566 if tc.app != nil { 567 err := client.Create(ctx, tc.app) 568 r.NoError(err) 569 570 if tc.app.Namespace != corev1.NamespaceDefault { 571 err := client.Create(ctx, &corev1.Namespace{ 572 ObjectMeta: metav1.ObjectMeta{ 573 Name: tc.app.Namespace, 574 }, 575 }) 576 r.NoError(err) 577 cmd.SetArgs([]string{tc.app.Name, "-n", tc.app.Namespace}) 578 } else { 579 cmd.SetArgs([]string{tc.app.Name}) 580 } 581 } 582 err = cmd.Execute() 583 if tc.expectedErr != nil { 584 r.Equal(tc.expectedErr, err) 585 return 586 } 587 r.NoError(err) 588 589 wf := &v1beta1.Application{} 590 err = client.Get(ctx, types.NamespacedName{ 591 Namespace: tc.app.Namespace, 592 Name: tc.app.Name, 593 }, wf) 594 r.NoError(err) 595 r.Equal(true, wf.Status.Workflow.Terminated) 596 for _, step := range wf.Status.Workflow.Steps { 597 if step.Phase != workflowv1alpha1.WorkflowStepPhaseSucceeded { 598 r.Equal(step.Phase, workflowv1alpha1.WorkflowStepPhaseFailed) 599 r.Equal(step.Reason, wfTypes.StatusReasonTerminate) 600 } 601 for _, sub := range step.SubStepsStatus { 602 if sub.Phase != workflowv1alpha1.WorkflowStepPhaseSucceeded { 603 r.Equal(sub.Phase, workflowv1alpha1.WorkflowStepPhaseFailed) 604 r.Equal(sub.Reason, wfTypes.StatusReasonTerminate) 605 } 606 } 607 } 608 }) 609 } 610 } 611 612 func TestWorkflowRestart(t *testing.T) { 613 c := initArgs() 614 ioStream := cmdutil.IOStreams{In: os.Stdin, Out: os.Stdout, ErrOut: os.Stderr} 615 ctx := context.TODO() 616 617 testCases := map[string]struct { 618 app *v1beta1.Application 619 expectedErr error 620 }{ 621 "no app name specified": { 622 expectedErr: fmt.Errorf("please specify the name of application/workflow"), 623 }, 624 "workflow not running": { 625 app: &v1beta1.Application{ 626 ObjectMeta: metav1.ObjectMeta{ 627 Name: "workflow-not-running", 628 Namespace: "default", 629 }, 630 Spec: workflowSpec, 631 Status: common.AppStatus{}, 632 }, 633 expectedErr: fmt.Errorf("the workflow in application workflow-not-running is not start"), 634 }, 635 "restart successfully": { 636 app: &v1beta1.Application{ 637 ObjectMeta: metav1.ObjectMeta{ 638 Name: "workflow", 639 Namespace: "test", 640 }, 641 Spec: workflowSpec, 642 Status: common.AppStatus{ 643 Workflow: &common.WorkflowStatus{ 644 Terminated: true, 645 }, 646 }, 647 }, 648 }, 649 } 650 651 for name, tc := range testCases { 652 t.Run(name, func(t *testing.T) { 653 r := require.New(t) 654 cmd := NewWorkflowRestartCommand(c, ioStream, &WorkflowArgs{Args: c, Writer: ioStream.Out}) 655 initCommand(cmd) 656 // clean up the arguments before start 657 cmd.SetArgs([]string{}) 658 client, err := c.GetClient() 659 r.NoError(err) 660 if tc.app != nil { 661 err := client.Create(ctx, tc.app) 662 r.NoError(err) 663 664 if tc.app.Namespace != corev1.NamespaceDefault { 665 err := client.Create(ctx, &corev1.Namespace{ 666 ObjectMeta: metav1.ObjectMeta{ 667 Name: tc.app.Namespace, 668 }, 669 }) 670 r.NoError(err) 671 cmd.SetArgs([]string{tc.app.Name, "-n", tc.app.Namespace}) 672 } else { 673 cmd.SetArgs([]string{tc.app.Name}) 674 } 675 } 676 err = cmd.Execute() 677 if tc.expectedErr != nil { 678 r.Equal(tc.expectedErr, err) 679 return 680 } 681 r.NoError(err) 682 683 wf := &v1beta1.Application{} 684 err = client.Get(ctx, types.NamespacedName{ 685 Namespace: tc.app.Namespace, 686 Name: tc.app.Name, 687 }, wf) 688 r.NoError(err) 689 var nilStatus *common.WorkflowStatus = nil 690 r.Equal(nilStatus, wf.Status.Workflow) 691 }) 692 } 693 } 694 695 func TestWorkflowRollback(t *testing.T) { 696 c := initArgs() 697 ioStream := cmdutil.IOStreams{In: os.Stdin, Out: os.Stdout, ErrOut: os.Stderr} 698 ctx := context.TODO() 699 700 testCases := map[string]struct { 701 app *v1beta1.Application 702 revision *v1beta1.ApplicationRevision 703 expectedErr error 704 }{ 705 "no app name specified": { 706 expectedErr: fmt.Errorf("please specify the name of application/workflow"), 707 }, 708 "workflow running": { 709 app: &v1beta1.Application{ 710 ObjectMeta: metav1.ObjectMeta{ 711 Name: "workflow-running", 712 Namespace: "default", 713 }, 714 Spec: workflowSpec, 715 Status: common.AppStatus{ 716 Workflow: &common.WorkflowStatus{ 717 Suspend: false, 718 Terminated: false, 719 Finished: false, 720 }, 721 }, 722 }, 723 expectedErr: fmt.Errorf("can not rollback a running workflow"), 724 }, 725 "invalid revision": { 726 app: &v1beta1.Application{ 727 ObjectMeta: metav1.ObjectMeta{ 728 Name: "invalid-revision", 729 Namespace: "default", 730 }, 731 Spec: workflowSpec, 732 Status: common.AppStatus{ 733 Workflow: &common.WorkflowStatus{ 734 Suspend: true, 735 }, 736 }, 737 }, 738 expectedErr: fmt.Errorf("the latest revision is not set: invalid-revision"), 739 }, 740 "rollback successfully": { 741 app: &v1beta1.Application{ 742 ObjectMeta: metav1.ObjectMeta{ 743 Name: "workflow", 744 Namespace: "test", 745 }, 746 Spec: workflowSpec, 747 Status: common.AppStatus{ 748 LatestRevision: &common.Revision{ 749 Name: "revision-v1", 750 }, 751 Workflow: &common.WorkflowStatus{ 752 Terminated: true, 753 }, 754 }, 755 }, 756 revision: &v1beta1.ApplicationRevision{ 757 ObjectMeta: metav1.ObjectMeta{ 758 Name: "revision-v1", 759 Namespace: "test", 760 }, 761 Spec: v1beta1.ApplicationRevisionSpec{ 762 ApplicationRevisionCompressibleFields: v1beta1.ApplicationRevisionCompressibleFields{ 763 Application: v1beta1.Application{ 764 Spec: v1beta1.ApplicationSpec{ 765 Components: []common.ApplicationComponent{{ 766 Name: "revision-component", 767 Type: "worker", 768 Properties: &runtime.RawExtension{Raw: []byte(`{"cmd":["sleep","1000"],"image":"busybox"}`)}, 769 }}, 770 }, 771 }, 772 }, 773 }, 774 }, 775 }, 776 } 777 778 for name, tc := range testCases { 779 t.Run(name, func(t *testing.T) { 780 r := require.New(t) 781 cmd := NewWorkflowRollbackCommand(c, ioStream, &WorkflowArgs{Args: c, Writer: ioStream.Out}) 782 initCommand(cmd) 783 // clean up the arguments before start 784 cmd.SetArgs([]string{}) 785 client, err := c.GetClient() 786 r.NoError(err) 787 if tc.app != nil { 788 err := client.Create(ctx, tc.app) 789 r.NoError(err) 790 791 if tc.app.Namespace != corev1.NamespaceDefault { 792 err := client.Create(ctx, &corev1.Namespace{ 793 ObjectMeta: metav1.ObjectMeta{ 794 Name: tc.app.Namespace, 795 }, 796 }) 797 r.NoError(err) 798 cmd.SetArgs([]string{tc.app.Name, "-n", tc.app.Namespace}) 799 } else { 800 cmd.SetArgs([]string{tc.app.Name}) 801 } 802 } 803 if tc.revision != nil { 804 err := client.Create(ctx, tc.revision) 805 r.NoError(err) 806 } 807 err = cmd.Execute() 808 if tc.expectedErr != nil { 809 r.Equal(tc.expectedErr, err) 810 return 811 } 812 r.NoError(err) 813 814 wf := &v1beta1.Application{} 815 err = client.Get(ctx, types.NamespacedName{ 816 Namespace: tc.app.Namespace, 817 Name: tc.app.Name, 818 }, wf) 819 r.NoError(err) 820 r.Equal(wf.Spec.Components[0].Name, "revision-component") 821 }) 822 } 823 } 824 825 func TestWorkflowList(t *testing.T) { 826 c := initArgs() 827 buf := new(bytes.Buffer) 828 ioStream := cmdutil.IOStreams{In: os.Stdin, Out: buf, ErrOut: os.Stderr} 829 ctx := context.TODO() 830 testCases := map[string]struct { 831 workflows []interface{} 832 apps []*v1beta1.Application 833 workflowRuns []*workflowv1alpha1.WorkflowRun 834 expectedErr error 835 namespace string 836 expectAppListSize int 837 }{ 838 "specified all namespaces flag": { 839 workflows: []interface{}{ 840 &v1beta1.Application{ 841 ObjectMeta: metav1.ObjectMeta{ 842 Name: "app-workflow", 843 Namespace: "some-ns", 844 }, 845 Spec: workflowSpec, 846 Status: common.AppStatus{ 847 LatestRevision: &common.Revision{ 848 Name: "revision-v1", 849 }, 850 Workflow: &common.WorkflowStatus{ 851 Terminated: true, 852 Phase: workflowv1alpha1.WorkflowStateInitializing, 853 StartTime: metav1.NewTime(time.Now().Add(-10 * time.Minute)), 854 EndTime: metav1.NewTime(time.Now()), 855 }, 856 }, 857 }, 858 &workflowv1alpha1.WorkflowRun{ 859 ObjectMeta: metav1.ObjectMeta{ 860 Name: "workflow-run", 861 Namespace: "some-ns", 862 }, 863 Status: workflowv1alpha1.WorkflowRunStatus{ 864 Phase: workflowv1alpha1.WorkflowStateExecuting, 865 StartTime: metav1.NewTime(time.Now().Add(-10 * time.Minute)), 866 EndTime: metav1.NewTime(time.Now()), 867 }, 868 }, 869 }, 870 expectedErr: nil, 871 }, 872 "specified namespace flag": { 873 workflows: []interface{}{ 874 &workflowv1alpha1.WorkflowRun{ 875 ObjectMeta: metav1.ObjectMeta{ 876 Name: "workflow-run-1", 877 Namespace: "test", 878 }, 879 Status: workflowv1alpha1.WorkflowRunStatus{ 880 Phase: workflowv1alpha1.WorkflowStateExecuting, 881 StartTime: metav1.NewTime(time.Now().Add(-10 * time.Minute)), 882 EndTime: metav1.NewTime(time.Now()), 883 }, 884 }, 885 }, 886 namespace: "test", 887 expectedErr: nil, 888 }, 889 } 890 for name, tc := range testCases { 891 t.Run(name, func(t *testing.T) { 892 r := require.New(t) 893 buf.Reset() 894 cmd := NewWorkflowListCommand(c, ioStream, &WorkflowArgs{Args: c, Writer: ioStream.Out}) 895 initCommand(cmd) 896 // clean up the arguments before start 897 cmd.SetArgs([]string{}) 898 client, err := c.GetClient() 899 r.NoError(err) 900 if tc.workflows != nil && len(tc.workflows) > 0 { 901 for _, w := range tc.workflows { 902 if workflow, ok := w.(*workflowv1alpha1.WorkflowRun); ok { 903 err := client.Create(ctx, workflow) 904 r.NoError(err) 905 } else if app, ok := w.(*v1beta1.Application); ok { 906 err := client.Create(ctx, app) 907 r.NoError(err) 908 } 909 } 910 } 911 args := []string{} 912 if tc.namespace == "" { 913 args = append(args, "-A") 914 } else { 915 args = append(args, "-n", tc.namespace) 916 } 917 918 cmd.SetArgs(args) 919 err = cmd.Execute() 920 921 lines := strings.Split(strings.TrimSpace(buf.String()), "\n") 922 923 headerFields := strings.Fields(lines[0]) 924 if tc.namespace == "" { 925 r.Equal(headerFields[0], "NAMESPACE") 926 927 } else { 928 r.Equal(headerFields[0], "NAME") 929 } 930 offset := 0 931 for i, line := range lines[1:] { 932 fields := strings.Fields(line) 933 if workflow, ok := tc.workflows[i].(*workflowv1alpha1.WorkflowRun); ok { 934 if tc.namespace == "" { 935 r.Equal(fields[0], workflow.Namespace) 936 offset = offset + 1 937 } else if tc.namespace == workflow.Namespace { 938 r.Equal(fields[0+offset], workflow.Name) 939 r.Equal(fields[1+offset], "WorkflowRun") 940 r.Equal(fields[2+offset], string(workflow.Status.Phase)) 941 r.Equal(fields[3+offset], workflow.Status.StartTime.Format("2006-01-02")) 942 r.Equal(fields[4+offset], workflow.Status.StartTime.Format("15:04:05")) 943 //skipping a few fields due to the format including timezone information 944 r.Equal(fields[7+offset], workflow.Status.EndTime.Format("2006-01-02")) 945 r.Equal(fields[8+offset], workflow.Status.EndTime.Format("15:04:05")) 946 } 947 } else if app, ok := tc.workflows[i].(*v1beta1.Application); ok { 948 offset = 0 949 if tc.namespace == "" { 950 r.Equal(fields[0+offset], app.Namespace) 951 offset = offset + 1 952 } else if tc.namespace == app.Namespace { 953 r.Equal(fields[0+offset], app.Name) 954 r.Equal(fields[1+offset], "Application") 955 r.Equal(fields[2+offset], string(app.Status.Workflow.Phase)) 956 r.Equal(fields[3+offset], app.Status.Workflow.StartTime.Format("2006-01-02")) 957 r.Equal(fields[4+offset], app.Status.Workflow.StartTime.Format("15:04:05")) 958 //skipping a few fields due to the format including timezone information 959 r.Equal(fields[7+offset], app.Status.Workflow.EndTime.Format("2006-01-02")) 960 r.Equal(fields[8+offset], app.Status.Workflow.EndTime.Format("15:04:05")) 961 } 962 } 963 964 } 965 966 if tc.expectedErr != nil { 967 r.Equal(tc.expectedErr, err) 968 return 969 } 970 r.NoError(err) 971 972 }) 973 } 974 }