github.com/pachyderm/pachyderm@v1.13.4/src/server/pps/cmds/cmds_test.go (about) 1 // TODO(msteffen) Add tests for: 2 // 3 // - restart datum 4 // - stop job 5 // - delete job 6 // 7 // - inspect job 8 // - list job 9 // 10 // - create pipeline 11 // - create pipeline --push-images (re-enable existing test) 12 // - update pipeline 13 // - delete pipeline 14 // 15 // - inspect pipeline 16 // - list pipeline 17 // 18 // - start pipeline 19 // - stop pipeline 20 // 21 // - list datum 22 // - inspect datum 23 // - logs 24 25 package cmds 26 27 import ( 28 "bytes" 29 "fmt" 30 "os" 31 "testing" 32 33 "github.com/pachyderm/pachyderm/src/client/pkg/require" 34 tu "github.com/pachyderm/pachyderm/src/server/pkg/testutil" 35 ) 36 37 const badJSON1 = ` 38 { 39 "356weryt 40 41 } 42 ` 43 44 const badJSON2 = `{ 45 { 46 "a": 1, 47 "b": [23,4,4,64,56,36,7456,7], 48 "c": {"e,f,g,h,j,j}, 49 "d": 3452.36456, 50 } 51 ` 52 53 func TestSyntaxErrorsReportedCreatePipeline(t *testing.T) { 54 if testing.Short() { 55 t.Skip("Skipping integration tests in short mode") 56 } 57 require.NoError(t, tu.BashCmd(` 58 echo -n '{{.badJSON1}}' \ 59 | ( pachctl create pipeline -f - 2>&1 || true ) \ 60 | match "malformed pipeline spec" 61 62 echo -n '{{.badJSON2}}' \ 63 | ( pachctl create pipeline -f - 2>&1 || true ) \ 64 | match "malformed pipeline spec" 65 `, 66 "badJSON1", badJSON1, 67 "badJSON2", badJSON2, 68 ).Run()) 69 } 70 71 func TestSyntaxErrorsReportedUpdatePipeline(t *testing.T) { 72 if testing.Short() { 73 t.Skip("Skipping integration tests in short mode") 74 } 75 require.NoError(t, tu.BashCmd(` 76 echo -n '{{.badJSON1}}' \ 77 | ( pachctl update pipeline -f - 2>&1 || true ) \ 78 | match "malformed pipeline spec" 79 80 echo -n '{{.badJSON2}}' \ 81 | ( pachctl update pipeline -f - 2>&1 || true ) \ 82 | match "malformed pipeline spec" 83 `, 84 "badJSON1", badJSON1, 85 "badJSON2", badJSON2, 86 ).Run()) 87 } 88 89 func TestRawFullPipelineInfo(t *testing.T) { 90 if testing.Short() { 91 t.Skip("Skipping integration tests in short mode") 92 } 93 require.NoError(t, tu.BashCmd(` 94 yes | pachctl delete all 95 pachctl garbage-collect 96 `).Run()) 97 require.NoError(t, tu.BashCmd(` 98 pachctl create repo data 99 pachctl put file data@master:/file <<<"This is a test" 100 pachctl create pipeline <<EOF 101 { 102 "pipeline": {"name": "{{.pipeline}}"}, 103 "input": { 104 "pfs": { 105 "glob": "/*", 106 "repo": "data" 107 } 108 }, 109 "transform": { 110 "cmd": ["bash"], 111 "stdin": ["cp /pfs/data/file /pfs/out"] 112 } 113 } 114 EOF 115 `, 116 "pipeline", tu.UniqueString("p-")).Run()) 117 require.NoError(t, tu.BashCmd(` 118 pachctl flush commit data@master 119 120 # make sure the results have the full pipeline info, including version 121 pachctl list job --raw --history=all \ 122 | match "pipeline_version" 123 `).Run()) 124 } 125 126 // TestJSONMultiplePipelines tests that pipeline specs with multiple pipelines 127 // in them continue to be accepted by 'pachctl create pipeline'. We may want to 128 // stop supporting this behavior eventually, but Pachyderm has supported it 129 // historically, so we should continue to support it until we formally deprecate 130 // it. 131 func TestJSONMultiplePipelines(t *testing.T) { 132 if testing.Short() { 133 t.Skip("Skipping integration tests in short mode") 134 } 135 require.NoError(t, tu.BashCmd(` 136 yes | pachctl delete all 137 pachctl create repo input 138 pachctl create pipeline -f - <<EOF 139 { 140 "pipeline": { 141 "name": "first" 142 }, 143 "input": { 144 "pfs": { 145 "glob": "/*", 146 "repo": "input" 147 } 148 }, 149 "transform": { 150 "cmd": [ "/bin/bash" ], 151 "stdin": [ 152 "cp /pfs/input/* /pfs/out" 153 ] 154 } 155 } 156 { 157 "pipeline": { 158 "name": "second" 159 }, 160 "input": { 161 "pfs": { 162 "glob": "/*", 163 "repo": "first" 164 } 165 }, 166 "transform": { 167 "cmd": [ "/bin/bash" ], 168 "stdin": [ 169 "cp /pfs/first/* /pfs/out" 170 ] 171 } 172 } 173 EOF 174 175 pachctl start commit input@master 176 echo foo | pachctl put file input@master:/foo 177 echo bar | pachctl put file input@master:/bar 178 echo baz | pachctl put file input@master:/baz 179 pachctl finish commit input@master 180 pachctl flush commit input@master 181 pachctl get file second@master:/foo | match foo 182 pachctl get file second@master:/bar | match bar 183 pachctl get file second@master:/baz | match baz 184 `, 185 ).Run()) 186 require.NoError(t, tu.BashCmd(`pachctl list pipeline`).Run()) 187 } 188 189 // TestJSONStringifiedNumberstests that JSON pipelines may use strings to 190 // specify numeric values such as a pipeline's parallelism (a feature of gogo's 191 // JSON parser). 192 func TestJSONStringifiedNumbers(t *testing.T) { 193 if testing.Short() { 194 t.Skip("Skipping integration tests in short mode") 195 } 196 require.NoError(t, tu.BashCmd(` 197 yes | pachctl delete all 198 pachctl create repo input 199 pachctl create pipeline -f - <<EOF 200 { 201 "pipeline": { 202 "name": "first" 203 }, 204 "input": { 205 "pfs": { 206 "glob": "/*", 207 "repo": "input" 208 } 209 }, 210 "parallelism_spec": { 211 "constant": "1" 212 }, 213 "transform": { 214 "cmd": [ "/bin/bash" ], 215 "stdin": [ 216 "cp /pfs/input/* /pfs/out" 217 ] 218 } 219 } 220 EOF 221 222 pachctl start commit input@master 223 echo foo | pachctl put file input@master:/foo 224 echo bar | pachctl put file input@master:/bar 225 echo baz | pachctl put file input@master:/baz 226 pachctl finish commit input@master 227 pachctl flush commit input@master 228 pachctl get file first@master:/foo | match foo 229 pachctl get file first@master:/bar | match bar 230 pachctl get file first@master:/baz | match baz 231 `, 232 ).Run()) 233 require.NoError(t, tu.BashCmd(`pachctl list pipeline`).Run()) 234 } 235 236 // TestJSONMultiplePipelinesError tests that when creating multiple pipelines 237 // (which only the encoding/json parser can parse) you get an error indicating 238 // the problem in the JSON, rather than an error complaining about multiple 239 // documents. 240 func TestJSONMultiplePipelinesError(t *testing.T) { 241 if testing.Short() { 242 t.Skip("Skipping integration tests in short mode") 243 } 244 // pipeline spec has no quotes around "name" in first pipeline 245 require.NoError(t, tu.BashCmd(` 246 yes | pachctl delete all 247 pachctl create repo input 248 ( pachctl create pipeline -f - 2>&1 <<EOF || true 249 { 250 "pipeline": { 251 name: "first" 252 }, 253 "input": { 254 "pfs": { 255 "glob": "/*", 256 "repo": "input" 257 } 258 }, 259 "transform": { 260 "cmd": [ "/bin/bash" ], 261 "stdin": [ 262 "cp /pfs/input/* /pfs/out" 263 ] 264 } 265 } 266 { 267 "pipeline": { 268 "name": "second" 269 }, 270 "input": { 271 "pfs": { 272 "glob": "/*", 273 "repo": "first" 274 } 275 }, 276 "transform": { 277 "cmd": [ "/bin/bash" ], 278 "stdin": [ 279 "cp /pfs/first/* /pfs/out" 280 ] 281 } 282 } 283 EOF 284 ) | match "invalid character 'n' looking for beginning of object key string" 285 `, 286 ).Run()) 287 } 288 289 func TestRunPipeline(t *testing.T) { 290 if os.Getenv("RUN_BAD_TESTS") == "" { 291 t.Skip("Skipping because RUN_BAD_TESTS was empty") 292 } 293 if testing.Short() { 294 t.Skip("Skipping integration tests in short mode") 295 } 296 pipeline := tu.UniqueString("p-") 297 require.NoError(t, tu.BashCmd(` 298 yes | pachctl delete all 299 pachctl garbage-collect 300 `).Run()) 301 require.NoError(t, tu.BashCmd(` 302 pachctl create repo data 303 304 # Create a commit and put some data in it 305 commit1=$(pachctl start commit data@master) 306 echo "file contents" | pachctl put file data@${commit1}:/file -f - 307 pachctl finish commit data@${commit1} 308 309 pachctl put file data@master:/file <<<"This is a test" 310 pachctl create pipeline <<EOF 311 { 312 "pipeline": {"name": "{{.pipeline}}"}, 313 "input": { 314 "pfs": { 315 "glob": "/*", 316 "repo": "data" 317 } 318 }, 319 "transform": { 320 "cmd": ["bash"], 321 "stdin": ["cp /pfs/data/file /pfs/out"] 322 } 323 } 324 EOF 325 326 # make sure the run pipeline command accepts various provenance formats 327 pachctl run pipeline {{.pipeline}} data@${commit1} 328 pachctl run pipeline {{.pipeline}} data@master=${commit1} 329 pachctl run pipeline {{.pipeline}} data@master 330 `, 331 "pipeline", pipeline).Run()) 332 } 333 334 // TestYAMLPipelineSpec tests creating a pipeline with a YAML pipeline spec 335 func TestYAMLPipelineSpec(t *testing.T) { 336 if os.Getenv("RUN_BAD_TESTS") == "" { 337 t.Skip("Skipping because RUN_BAD_TESTS was empty") 338 } 339 340 if testing.Short() { 341 t.Skip("Skipping integration tests in short mode") 342 } 343 // Note that BashCmd dedents all lines below including the YAML (which 344 // wouldn't parse otherwise) 345 require.NoError(t, tu.BashCmd(` 346 yes | pachctl delete all 347 pachctl create repo input 348 pachctl create pipeline -f - <<EOF 349 pipeline: 350 name: first 351 input: 352 pfs: 353 glob: /* 354 repo: input 355 transform: 356 cmd: [ /bin/bash ] 357 stdin: 358 - "cp /pfs/input/* /pfs/out" 359 --- 360 pipeline: 361 name: second 362 input: 363 pfs: 364 glob: /* 365 repo: first 366 transform: 367 cmd: [ /bin/bash ] 368 stdin: 369 - "cp /pfs/first/* /pfs/out" 370 EOF 371 pachctl start commit input@master 372 echo foo | pachctl put file input@master:/foo 373 echo bar | pachctl put file input@master:/bar 374 echo baz | pachctl put file input@master:/baz 375 pachctl finish commit input@master 376 pachctl flush commit input@master 377 pachctl get file second@master:/foo | match foo 378 pachctl get file second@master:/bar | match bar 379 pachctl get file second@master:/baz | match baz 380 `, 381 ).Run()) 382 } 383 384 func TestListPipelineFilter(t *testing.T) { 385 if os.Getenv("RUN_BAD_TESTS") == "" { 386 t.Skip("Skipping because RUN_BAD_TESTS was empty") 387 } 388 389 if testing.Short() { 390 t.Skip("Skipping integration tests in short mode") 391 } 392 require.NoError(t, tu.BashCmd(` 393 yes | pachctl delete all 394 pachctl garbage-collect 395 `).Run()) 396 require.NoError(t, tu.BashCmd(` 397 yes | pachctl delete all 398 pachctl create repo input 399 pachctl create pipeline -f - <<EOF 400 { 401 "pipeline": { 402 "name": "first" 403 }, 404 "input": { 405 "pfs": { 406 "glob": "/*", 407 "repo": "input" 408 } 409 }, 410 "parallelism_spec": { 411 "constant": "1" 412 }, 413 "transform": { 414 "cmd": [ "/bin/bash" ], 415 "stdin": [ 416 "cp /pfs/input/* /pfs/out" 417 ] 418 } 419 } 420 EOF 421 422 echo foo | pachctl put file input@master:/foo 423 pachctl flush commit input@master 424 # make sure we see the pipeline with the appropriate state filters 425 pachctl list pipeline | match first 426 pachctl list pipeline --state starting --state running | match first 427 pachctl list pipeline --state crashing --state failure | match -v first 428 `, 429 ).Run()) 430 } 431 432 // TestYAMLError tests that when creating pipelines using a YAML spec with an 433 // error, you get an error indicating the problem in the YAML, rather than an 434 // error complaining about multiple documents. 435 // 436 // Note that with the new parsing method added to support free-form fields like 437 // TFJob, this YAML is parsed, serialized and then re-parsed, so the error will 438 // refer to "json" (the format used for the canonicalized pipeline), but the 439 // issue referenced by the error (use of a string instead of an array for 'cmd') 440 // is the main problem below 441 func TestYAMLError(t *testing.T) { 442 if testing.Short() { 443 t.Skip("Skipping integration tests in short mode") 444 } 445 // "cmd" should be a list, instead of a string 446 require.NoError(t, tu.BashCmd(` 447 yes | pachctl delete all 448 pachctl create repo input 449 ( pachctl create pipeline -f - 2>&1 <<EOF || true 450 pipeline: 451 name: first 452 input: 453 pfs: 454 glob: /* 455 repo: input 456 transform: 457 cmd: /bin/bash # should be list, instead of string 458 stdin: 459 - "cp /pfs/input/* /pfs/out" 460 EOF 461 ) | match "cannot unmarshal string into Go value of type \[\]json.RawMessage" 462 `, 463 ).Run()) 464 } 465 466 func TestTFJobBasic(t *testing.T) { 467 if testing.Short() { 468 t.Skip("Skipping integration tests in short mode") 469 } 470 require.NoError(t, tu.BashCmd(` 471 yes | pachctl delete all 472 pachctl create repo input 473 ( pachctl create pipeline -f - 2>&1 <<EOF || true 474 pipeline: 475 name: first 476 input: 477 pfs: 478 glob: /* 479 repo: input 480 tf_job: 481 apiVersion: kubeflow.org/v1 482 kind: TFJob 483 metadata: 484 generateName: tfjob 485 namespace: kubeflow 486 spec: 487 tfReplicaSpecs: 488 PS: 489 replicas: 1 490 restartPolicy: OnFailure 491 template: 492 spec: 493 containers: 494 - name: tensorflow 495 image: gcr.io/your-project/your-image 496 command: 497 - python 498 - -m 499 - trainer.task 500 - --batch_size=32 501 - --training_steps=1000 502 Worker: 503 replicas: 3 504 restartPolicy: OnFailure 505 template: 506 spec: 507 containers: 508 - name: tensorflow 509 image: gcr.io/your-project/your-image 510 command: 511 - python 512 - -m 513 - trainer.task 514 - --batch_size=32 515 - --training_steps=1000 516 EOF 517 ) | match "not supported yet" 518 `, 519 ).Run()) 520 } 521 522 // TestYAMLSecret tests creating a YAML pipeline with a secret (i.e. the fix for 523 // https://github.com/pachyderm/pachyderm/issues/4119) 524 func TestYAMLSecret(t *testing.T) { 525 if testing.Short() { 526 t.Skip("Skipping integration tests in short mode") 527 } 528 // Note that BashCmd dedents all lines below including the YAML (which 529 // wouldn't parse otherwise) 530 require.NoError(t, tu.BashCmd(` 531 yes | pachctl delete all 532 533 # kubectl get secrets >&2 534 kubectl delete secrets/test-yaml-secret || true 535 kubectl create secret generic test-yaml-secret --from-literal=my-key=my-value 536 537 pachctl create repo input 538 pachctl put file input@master:/foo <<<"foo" 539 pachctl create pipeline -f - <<EOF 540 pipeline: 541 name: pipeline 542 input: 543 pfs: 544 glob: /* 545 repo: input 546 transform: 547 cmd: [ /bin/bash ] 548 stdin: 549 - "env | grep MY_SECRET >/pfs/out/vars" 550 secrets: 551 - name: test-yaml-secret 552 env_var: MY_SECRET 553 key: my-key 554 EOF 555 pachctl flush commit input@master 556 pachctl get file pipeline@master:/vars | match MY_SECRET=my-value 557 `, 558 ).Run()) 559 } 560 561 // TestYAMLTimestamp tests creating a YAML pipeline with a timestamp (i.e. the 562 // fix for https://github.com/pachyderm/pachyderm/issues/4209) 563 func TestYAMLTimestamp(t *testing.T) { 564 if testing.Short() { 565 t.Skip("Skipping integration tests in short mode") 566 } 567 // Note that BashCmd dedents all lines below including the YAML (which 568 // wouldn't parse otherwise) 569 require.NoError(t, tu.BashCmd(` 570 yes | pachctl delete all 571 572 # If the pipeline comes up without error, then the YAML parsed 573 pachctl create pipeline -f - <<EOF 574 pipeline: 575 name: pipeline 576 input: 577 cron: 578 name: in 579 start: "2019-10-10T22:30:05Z" 580 spec: "@yearly" 581 transform: 582 cmd: [ /bin/bash ] 583 stdin: 584 - "cp /pfs/in/* /pfs/out" 585 EOF 586 pachctl list pipeline | match 'pipeline' 587 `, 588 ).Run()) 589 } 590 591 func TestEditPipeline(t *testing.T) { 592 if testing.Short() { 593 t.Skip("Skipping integration tests in short mode") 594 } 595 require.NoError(t, tu.BashCmd(` 596 yes | pachctl delete all 597 `).Run()) 598 require.NoError(t, tu.BashCmd(` 599 pachctl create repo data 600 pachctl create pipeline <<EOF 601 pipeline: 602 name: my-pipeline 603 input: 604 pfs: 605 glob: /* 606 repo: data 607 transform: 608 cmd: [ /bin/bash ] 609 stdin: 610 - "cp /pfs/data/* /pfs/out" 611 EOF 612 `).Run()) 613 require.NoError(t, tu.BashCmd(` 614 EDITOR="cat -u" pachctl edit pipeline my-pipeline -o yaml \ 615 | match 'name: my-pipeline' \ 616 | match 'repo: data' \ 617 | match 'cmd:' \ 618 | match 'cp /pfs/data/\* /pfs/out' 619 `).Run()) 620 } 621 622 func TestPipelineBuildLifecyclePython(t *testing.T) { 623 require.NoError(t, tu.BashCmd("yes | pachctl delete all").Run()) 624 pipeline := testPipelineBuildLifecycle(t, "python", "python") 625 626 // the python example also contains a `.pachignore`, so we can verify it's 627 // intended behavior here 628 require.NoError(t, tu.BashCmd(` 629 pachctl get file {{.pipeline}}_build@source:/.pachignore 630 `, "pipeline", pipeline).Run()) 631 require.YesError(t, tu.BashCmd(` 632 pachctl get file {{.pipeline}}_build@source:/foo.txt 633 `, "pipeline", pipeline).Run()) 634 } 635 636 func TestPipelineBuildLifecyclePythonNoDeps(t *testing.T) { 637 require.NoError(t, tu.BashCmd("yes | pachctl delete all").Run()) 638 testPipelineBuildLifecycle(t, "python", "python_no_deps") 639 } 640 641 func TestPipelineBuildLifecycleGo(t *testing.T) { 642 require.NoError(t, tu.BashCmd("yes | pachctl delete all").Run()) 643 testPipelineBuildLifecycle(t, "go", "go") 644 } 645 646 func TestAuthorizedPipelineBuildLifecycle(t *testing.T) { 647 require.NoError(t, tu.BashCmd("yes | pachctl delete all").Run()) 648 _ = tu.GetAuthenticatedPachClient(t, "unused") // enable auth as a side effect 649 650 defer tu.DeleteAll(t) // make sure to clean up auth 651 652 testPipelineBuildLifecycle(t, "go", "go") 653 } 654 655 func testPipelineBuildLifecycle(t *testing.T, lang, dir string) string { 656 t.Helper() 657 if testing.Short() { 658 t.Skip("Skipping integration tests in short mode") 659 } 660 661 prefix := "../../../../etc/testing/pipeline-build" 662 663 // reset and create some test input 664 require.NoError(t, tu.BashCmd(` 665 pachctl create repo in 666 pachctl put file -r in@master:/ -f {{.prefix}}/input 667 `, "prefix", prefix).Run()) 668 669 // give pipeline a unique name to work around 670 // github.com/kubernetes/kubernetes/issues/82130 671 pipeline := tu.UniqueString("test-pipeline-build") 672 spec := fmt.Sprintf(` 673 { 674 "pipeline": { 675 "name": %q 676 }, 677 "transform": { 678 "build": { 679 "language": %q 680 } 681 }, 682 "input": { 683 "pfs": { 684 "repo": "in", 685 "glob": "/*" 686 } 687 } 688 } 689 `, pipeline, lang) 690 691 // test a barebones pipeline with a build spec and verify results 692 require.NoError(t, tu.BashCmd(` 693 cd {{.prefix}}/{{.dir}} 694 pachctl create pipeline <<EOF 695 {{.spec}} 696 EOF 697 pachctl flush commit in@master 698 `, 699 "dir", dir, 700 "spec", spec, 701 "prefix", prefix, 702 ).Run()) 703 require.YesError(t, tu.BashCmd(fmt.Sprintf(` 704 pachctl list pipeline --state failure | match %s 705 `, pipeline)).Run()) 706 verifyPipelineBuildOutput(t, pipeline, "0") 707 708 // update the barebones pipeline and verify results 709 require.NoError(t, tu.BashCmd(` 710 cd {{.prefix}}/{{.dir}} 711 pachctl update pipeline <<EOF 712 {{.spec}} 713 EOF 714 pachctl flush commit in@master 715 `, 716 "dir", dir, 717 "spec", spec, 718 "prefix", prefix, 719 ).Run()) 720 verifyPipelineBuildOutput(t, pipeline, "0") 721 722 // update the pipeline with a custom cmd and verify results 723 spec = fmt.Sprintf(` 724 { 725 "pipeline": { 726 "name": %q 727 }, 728 "transform": { 729 "cmd": [ 730 "sh", 731 "/pfs/build/run.sh", 732 "_" 733 ], 734 "build": { 735 "language": %q, 736 "path": "." 737 } 738 }, 739 "input": { 740 "pfs": { 741 "repo": "in", 742 "glob": "/*" 743 } 744 } 745 } 746 `, pipeline, lang) 747 748 require.NoError(t, tu.BashCmd(` 749 cd {{.prefix}}/{{.dir}} 750 pachctl update pipeline --reprocess <<EOF 751 {{.spec}} 752 EOF 753 pachctl flush commit in@master 754 `, 755 "dir", dir, 756 "spec", spec, 757 "prefix", prefix, 758 ).Run()) 759 verifyPipelineBuildOutput(t, pipeline, "_") 760 return pipeline 761 } 762 763 func verifyPipelineBuildOutput(t *testing.T, pipeline, prefix string) { 764 t.Helper() 765 766 require.NoError(t, tu.BashCmd(` 767 pachctl flush commit in@master 768 pachctl get file {{.pipeline}}@master:/1.txt | match {{.prefix}}{{.prefix}}{{.prefix}}1 769 pachctl get file {{.pipeline}}@master:/11.txt | match {{.prefix}}{{.prefix}}11 770 pachctl get file {{.pipeline}}@master:/111.txt | match {{.prefix}}111 771 `, 772 "pipeline", pipeline, 773 "prefix", prefix, 774 ).Run()) 775 } 776 777 func TestMissingPipeline(t *testing.T) { 778 if testing.Short() { 779 t.Skip("Skipping integration tests in short mode") 780 } 781 // should fail because there's no pipeline object in the spec 782 require.YesError(t, tu.BashCmd(` 783 pachctl create pipeline <<EOF 784 { 785 "transform": { 786 "image": "ubuntu" 787 }, 788 "input": { 789 "pfs": { 790 "repo": "in", 791 "glob": "/*" 792 } 793 } 794 } 795 EOF 796 `).Run()) 797 } 798 799 func TestUnnamedPipeline(t *testing.T) { 800 if testing.Short() { 801 t.Skip("Skipping integration tests in short mode") 802 } 803 // should fail because there's no pipeline name 804 require.YesError(t, tu.BashCmd(` 805 pachctl create pipeline <<EOF 806 { 807 "pipeline": {}, 808 "transform": { 809 "image": "ubuntu" 810 }, 811 "input": { 812 "pfs": { 813 "repo": "in", 814 "glob": "/*" 815 } 816 } 817 } 818 EOF 819 `).Run()) 820 } 821 822 func TestPipelineBuildSpout(t *testing.T) { 823 if testing.Short() { 824 t.Skip("Skipping integration tests in short mode") 825 } 826 // should fail because pipeline build can't be used w/ spouts 827 require.YesError(t, tu.BashCmd(` 828 pachctl create pipeline <<EOF 829 { 830 "pipeline": { 831 "name": "test" 832 }, 833 "transform": { 834 "build": { 835 "language": "go" 836 } 837 }, 838 "spout": {} 839 } 840 EOF 841 `).Run()) 842 } 843 844 func TestPipelineBuildMissingInput(t *testing.T) { 845 if testing.Short() { 846 t.Skip("Skipping integration tests in short mode") 847 } 848 // should fail because pipeline build can't be used w/ spouts 849 require.YesError(t, tu.BashCmd(` 850 pachctl create pipeline <<EOF 851 { 852 "pipeline": { 853 "name": "test" 854 }, 855 "transform": { 856 "build": { 857 "language": "go" 858 } 859 } 860 } 861 EOF 862 `).Run()) 863 } 864 865 func TestPipelineBuildBadInput(t *testing.T) { 866 if testing.Short() { 867 t.Skip("Skipping integration tests in short mode") 868 } 869 // should fail because 'build' and 'source' are reserved input names when 870 // using pipeline builds 871 require.YesError(t, tu.BashCmd(` 872 pachctl create pipeline <<EOF 873 { 874 "pipeline": { 875 "name": "test" 876 }, 877 "transform": { 878 "build": { 879 "language": "go" 880 } 881 }, 882 "input": { 883 "pfs": { 884 "name": "build", 885 "repo": "in", 886 "glob": "/*" 887 } 888 } 889 } 890 EOF 891 `).Run()) 892 require.YesError(t, tu.BashCmd(` 893 pachctl create pipeline <<EOF 894 { 895 "pipeline": { 896 "name": "test" 897 }, 898 "transform": { 899 "build": { 900 "language": "go" 901 } 902 }, 903 "input": { 904 "pfs": { 905 "name": "source", 906 "repo": "in", 907 "glob": "/*" 908 } 909 } 910 } 911 EOF 912 `).Run()) 913 } 914 915 func TestPipelineBuildMissingPath(t *testing.T) { 916 if testing.Short() { 917 t.Skip("Skipping integration tests in short mode") 918 } 919 // should fail because 'build' and 'source' are reserved input names for 920 // when using pipeline builds 921 require.YesError(t, tu.BashCmd(` 922 pachctl create pipeline <<EOF 923 { 924 "pipeline": { 925 "name": "test" 926 }, 927 "transform": { 928 "build": { 929 "language": "go", 930 "path": "/some/path/that/doesnt/exist" 931 } 932 }, 933 "input": { 934 "pfs": { 935 "repo": "in", 936 "glob": "/*" 937 } 938 } 939 } 940 EOF 941 `).Run()) 942 } 943 944 // func TestPushImages(t *testing.T) { 945 // if testing.Short() { 946 // t.Skip("Skipping integration tests in short mode") 947 // } 948 // ioutil.WriteFile("test-push-images.json", []byte(`{ 949 // "pipeline": { 950 // "name": "test_push_images" 951 // }, 952 // "transform": { 953 // "cmd": [ "true" ], 954 // "image": "test-job-shim" 955 // } 956 // }`), 0644) 957 // os.Args = []string{"pachctl", "create", "pipeline", "--push-images", "-f", "test-push-images.json"} 958 // require.NoError(t, rootCmd().Execute()) 959 // } 960 961 func runPipelineWithImageGetStderr(t *testing.T, image string) (string, error) { 962 // reset and create some test input 963 require.NoError(t, tu.BashCmd(` 964 yes | pachctl delete all 965 pachctl create repo in 966 pachctl put file -r in@master:/ -f ../../../../etc/testing/pipeline-build/input 967 `).Run()) 968 969 cmd := tu.BashCmd(` 970 pachctl create pipeline <<EOF 971 { 972 "pipeline": { 973 "name": "first" 974 }, 975 "transform": { 976 "image": "{{.image}}" 977 }, 978 "input": { 979 "pfs": { 980 "repo": "in", 981 "glob": "/*" 982 } 983 } 984 } 985 EOF 986 `, "image", image) 987 buf := &bytes.Buffer{} 988 cmd.Stderr = buf 989 // cmd.Stdout = os.Stdout // uncomment for debugging 990 cmd.Env = os.Environ() 991 err := cmd.Run() 992 stderr := buf.String() 993 return stderr, err 994 } 995 996 func TestPipelineBuildRunCron(t *testing.T) { 997 if testing.Short() { 998 t.Skip("Skipping integration tests in short mode") 999 } 1000 1001 // build a cron pipeline successfully 1002 require.NoError(t, tu.BashCmd(` 1003 pachctl create pipeline <<EOF 1004 { 1005 "pipeline": { 1006 "name": "crontest" 1007 }, 1008 "input": { 1009 "cron": { 1010 "name": "tick", 1011 "spec": "*/1 * * * *", 1012 "overwrite": true 1013 } 1014 }, 1015 "transform": { 1016 "build":{ 1017 "language": "go" 1018 }, 1019 "cmd":["echo", "tick"] 1020 }, 1021 "enable_stats": true 1022 } 1023 EOF 1024 `).Run()) 1025 1026 // and make sure you can use `run cron` on it 1027 require.NoError(t, tu.BashCmd(` 1028 pachctl run cron crontest 1029 `).Run()) 1030 } 1031 1032 func TestWarningLatestTag(t *testing.T) { 1033 if testing.Short() { 1034 t.Skip("Skipping integration tests in short mode") 1035 } 1036 // should emit a warning because user specified latest tag on docker image 1037 stderr, err := runPipelineWithImageGetStderr(t, "ubuntu:latest") 1038 require.NoError(t, err, "%v", err) 1039 require.Matches(t, "WARNING", stderr) 1040 } 1041 1042 func TestWarningEmptyTag(t *testing.T) { 1043 if testing.Short() { 1044 t.Skip("Skipping integration tests in short mode") 1045 } 1046 // should emit a warning because user specified empty tag, equivalent to 1047 // :latest 1048 stderr, err := runPipelineWithImageGetStderr(t, "ubuntu") 1049 require.NoError(t, err) 1050 require.Matches(t, "WARNING", stderr) 1051 } 1052 1053 func TestNoWarningTagSpecified(t *testing.T) { 1054 if testing.Short() { 1055 t.Skip("Skipping integration tests in short mode") 1056 } 1057 // should not emit a warning (stderr should be empty) because user 1058 // specified non-empty, non-latest tag 1059 stderr, err := runPipelineWithImageGetStderr(t, "ubuntu:xenial") 1060 require.NoError(t, err) 1061 require.Equal(t, "", stderr) 1062 }