github.com/olli-ai/jx/v2@v2.0.400-0.20210921045218-14731b4dd448/pkg/tekton/syntax/pipeline_test.go (about) 1 // +build unit 2 3 package syntax_test 4 5 import ( 6 "context" 7 "errors" 8 "path/filepath" 9 "testing" 10 11 v1 "github.com/jenkins-x/jx-api/pkg/apis/jenkins.io/v1" 12 "github.com/olli-ai/jx/v2/pkg/versionstream" 13 "github.com/stretchr/testify/assert" 14 "k8s.io/apimachinery/pkg/api/resource" 15 16 "github.com/google/go-cmp/cmp" 17 "github.com/olli-ai/jx/v2/pkg/config" 18 "github.com/olli-ai/jx/v2/pkg/tekton/syntax" 19 sh "github.com/olli-ai/jx/v2/pkg/tekton/syntax/syntax_helpers_test" 20 tektonv1alpha1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1" 21 tb "github.com/tektoncd/pipeline/test/builder" 22 corev1 "k8s.io/api/core/v1" 23 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 24 kubefake "k8s.io/client-go/kubernetes/fake" 25 "knative.dev/pkg/apis" 26 "knative.dev/pkg/kmp" 27 ) 28 29 var ( 30 // Needed to take address of strings since workspace is *string. Is there a better way to handle optional values? 31 defaultWorkspace = "default" 32 customWorkspace = "custom" 33 ) 34 35 // TODO: Try to write some helper functions to make Pipeline and Task expect building less bloody verbose. 36 func TestParseJenkinsfileYaml(t *testing.T) { 37 testVersionsDir := filepath.Join("test_data", "stable_versions") 38 resolvedGitMergeImage, err := versionstream.ResolveDockerImage(testVersionsDir, syntax.GitMergeImage) 39 assert.NoError(t, err) 40 41 ctx := context.Background() 42 tests := []struct { 43 name string 44 expected *syntax.ParsedPipeline 45 pipeline *tektonv1alpha1.Pipeline 46 tasks []*tektonv1alpha1.Task 47 expectedErrorMsg string 48 validationErrorMsg string 49 structure *v1.PipelineStructure 50 }{ 51 { 52 name: "simple_jenkinsfile", 53 expected: sh.ParsedPipeline( 54 sh.PipelineAgent("some-image"), 55 sh.PipelineStage("A Working Stage", 56 sh.StageStep( 57 sh.StepCmd("echo"), 58 sh.StepArg("hello"), sh.StepArg("world"), 59 sh.StepName("A Step With Spaces And Such"), 60 ), 61 ), 62 ), 63 pipeline: tb.Pipeline("somepipeline-1", tb.PipelineNamespace("jx"), tb.PipelineSpec( 64 tb.PipelineTask("a-working-stage", "somepipeline-a-working-stage-1", 65 tb.PipelineTaskInputResource("workspace", "somepipeline"), 66 ), 67 tb.PipelineDeclaredResource("somepipeline", tektonv1alpha1.PipelineResourceTypeGit))), 68 tasks: []*tektonv1alpha1.Task{ 69 tb.Task("somepipeline-a-working-stage-1", tb.TaskNamespace("jx"), sh.TaskStageLabel("A Working Stage"), 70 tb.TaskSpec( 71 tb.TaskInputs( 72 tb.InputsResource("workspace", tektonv1alpha1.PipelineResourceTypeGit, 73 tb.ResourceTargetPath("source"))), 74 tb.Step(resolvedGitMergeImage, tb.StepName("setup-builder-home"), tb.StepCommand("/bin/sh", "-c"), tb.StepArgs("[ -d /builder/home ] || mkdir -p /builder && ln -s /tekton/home /builder/home"), tb.StepWorkingDir("/workspace/source")), 75 tb.Step(resolvedGitMergeImage, tb.StepName("git-merge"), tb.StepCommand("jx"), tb.StepArgs("step", "git", "merge", "--verbose"), tb.StepWorkingDir("/workspace/source")), 76 tb.Step("some-image:0.0.1", tb.StepName("a-step-with-spaces-and-such"), tb.StepCommand("/bin/sh", "-c"), tb.StepArgs("echo hello world"), tb.StepWorkingDir("/workspace/source")), 77 )), 78 }, 79 structure: sh.PipelineStructure("somepipeline-1", 80 sh.StructureStage("A Working Stage", sh.StructureStageTaskRef("somepipeline-a-working-stage-1")), 81 ), 82 }, 83 { 84 name: "multiple_stages", 85 expected: sh.ParsedPipeline( 86 sh.PipelineAgent("some-image"), 87 sh.PipelineStage("A Working Stage", 88 sh.StageStep( 89 sh.StepCmd("echo"), 90 sh.StepArg("hello"), sh.StepArg("world")), 91 ), 92 sh.PipelineStage("Another stage", 93 sh.StageStep( 94 sh.StepCmd("echo"), 95 sh.StepArg("again"))), 96 ), 97 pipeline: tb.Pipeline("somepipeline-1", tb.PipelineNamespace("jx"), tb.PipelineSpec( 98 tb.PipelineTask("a-working-stage", "somepipeline-a-working-stage-1", 99 tb.PipelineTaskInputResource("workspace", "somepipeline"), 100 tb.PipelineTaskOutputResource("workspace", "somepipeline")), 101 tb.PipelineTask("another-stage", "somepipeline-another-stage-1", 102 tb.PipelineTaskInputResource("workspace", "somepipeline", 103 tb.From("a-working-stage")), 104 tb.RunAfter("a-working-stage")), 105 tb.PipelineDeclaredResource("somepipeline", tektonv1alpha1.PipelineResourceTypeGit))), 106 tasks: []*tektonv1alpha1.Task{ 107 tb.Task("somepipeline-a-working-stage-1", tb.TaskNamespace("jx"), sh.TaskStageLabel("A Working Stage"), tb.TaskSpec( 108 tb.TaskInputs( 109 tb.InputsResource("workspace", tektonv1alpha1.PipelineResourceTypeGit, 110 tb.ResourceTargetPath("source"))), 111 tb.TaskOutputs(sh.OutputsResource("workspace", tektonv1alpha1.PipelineResourceTypeGit, tb.ResourceTargetPath("source"))), 112 tb.Step(resolvedGitMergeImage, tb.StepName("setup-builder-home"), tb.StepCommand("/bin/sh", "-c"), tb.StepArgs("[ -d /builder/home ] || mkdir -p /builder && ln -s /tekton/home /builder/home"), tb.StepWorkingDir("/workspace/source")), 113 tb.Step(resolvedGitMergeImage, tb.StepName("git-merge"), tb.StepCommand("jx"), tb.StepArgs("step", "git", "merge", "--verbose"), tb.StepWorkingDir("/workspace/source")), 114 tb.Step("some-image:0.0.1", tb.StepName("step2"), tb.StepCommand("/bin/sh", "-c"), tb.StepArgs("echo hello world"), tb.StepWorkingDir("/workspace/source")), 115 )), 116 tb.Task("somepipeline-another-stage-1", tb.TaskNamespace("jx"), sh.TaskStageLabel("Another stage"), tb.TaskSpec( 117 tb.TaskInputs( 118 tb.InputsResource("workspace", tektonv1alpha1.PipelineResourceTypeGit, 119 tb.ResourceTargetPath("source"))), 120 tb.Step(resolvedGitMergeImage, tb.StepName("setup-builder-home"), tb.StepCommand("/bin/sh", "-c"), tb.StepArgs("[ -d /builder/home ] || mkdir -p /builder && ln -s /tekton/home /builder/home"), tb.StepWorkingDir("/workspace/source")), 121 tb.Step("some-image:0.0.1", tb.StepName("step2"), tb.StepCommand("/bin/sh", "-c"), tb.StepArgs("echo again"), tb.StepWorkingDir("/workspace/source")), 122 )), 123 }, 124 structure: sh.PipelineStructure("somepipeline-1", 125 sh.StructureStage("A Working Stage", sh.StructureStageTaskRef("somepipeline-a-working-stage-1")), 126 sh.StructureStage("Another stage", sh.StructureStageTaskRef("somepipeline-another-stage-1"), 127 sh.StructureStagePrevious("A Working Stage")), 128 ), 129 }, 130 { 131 name: "nested_stages", 132 expected: sh.ParsedPipeline( 133 sh.PipelineAgent("some-image"), 134 sh.PipelineStage("Parent Stage", 135 sh.StageSequential("A Working Stage", 136 sh.StageStep(sh.StepCmd("echo"), sh.StepArg("hello"), sh.StepArg("world"))), 137 sh.StageSequential("Another stage", 138 sh.StageStep(sh.StepCmd("echo"), sh.StepArg("again"))), 139 ), 140 ), 141 pipeline: tb.Pipeline("somepipeline-1", tb.PipelineNamespace("jx"), tb.PipelineSpec( 142 tb.PipelineTask("a-working-stage", "somepipeline-a-working-stage-1", 143 tb.PipelineTaskInputResource("workspace", "somepipeline"), 144 tb.PipelineTaskOutputResource("workspace", "somepipeline")), 145 tb.PipelineTask("another-stage", "somepipeline-another-stage-1", 146 tb.PipelineTaskInputResource("workspace", "somepipeline", 147 tb.From("a-working-stage")), 148 tb.RunAfter("a-working-stage")), 149 tb.PipelineDeclaredResource("somepipeline", tektonv1alpha1.PipelineResourceTypeGit))), 150 tasks: []*tektonv1alpha1.Task{ 151 tb.Task("somepipeline-a-working-stage-1", tb.TaskNamespace("jx"), sh.TaskStageLabel("A Working Stage"), 152 tb.TaskSpec( 153 tb.TaskInputs( 154 tb.InputsResource("workspace", tektonv1alpha1.PipelineResourceTypeGit, tb.ResourceTargetPath("source"))), 155 tb.TaskOutputs(sh.OutputsResource("workspace", tektonv1alpha1.PipelineResourceTypeGit, tb.ResourceTargetPath("source"))), 156 tb.Step(resolvedGitMergeImage, tb.StepName("setup-builder-home"), tb.StepCommand("/bin/sh", "-c"), tb.StepArgs("[ -d /builder/home ] || mkdir -p /builder && ln -s /tekton/home /builder/home"), tb.StepWorkingDir("/workspace/source")), 157 tb.Step(resolvedGitMergeImage, tb.StepName("git-merge"), tb.StepCommand("jx"), tb.StepArgs("step", "git", "merge", "--verbose"), tb.StepWorkingDir("/workspace/source")), 158 tb.Step("some-image:0.0.1", tb.StepName("step2"), tb.StepCommand("/bin/sh", "-c"), tb.StepArgs("echo hello world"), tb.StepWorkingDir("/workspace/source")), 159 )), 160 tb.Task("somepipeline-another-stage-1", tb.TaskNamespace("jx"), sh.TaskStageLabel("Another stage"), tb.TaskSpec( 161 tb.TaskInputs( 162 tb.InputsResource("workspace", tektonv1alpha1.PipelineResourceTypeGit, 163 tb.ResourceTargetPath("source"))), 164 tb.Step(resolvedGitMergeImage, tb.StepName("setup-builder-home"), tb.StepCommand("/bin/sh", "-c"), tb.StepArgs("[ -d /builder/home ] || mkdir -p /builder && ln -s /tekton/home /builder/home"), tb.StepWorkingDir("/workspace/source")), 165 tb.Step("some-image:0.0.1", tb.StepName("step2"), tb.StepCommand("/bin/sh", "-c"), tb.StepArgs("echo again"), tb.StepWorkingDir("/workspace/source")), 166 )), 167 }, 168 structure: sh.PipelineStructure("somepipeline-1", 169 sh.StructureStage("Parent Stage", 170 sh.StructureStageStages("A Working Stage", "Another stage")), 171 sh.StructureStage("A Working Stage", sh.StructureStageTaskRef("somepipeline-a-working-stage-1"), 172 sh.StructureStageDepth(1), 173 sh.StructureStageParent("Parent Stage")), 174 sh.StructureStage("Another stage", sh.StructureStageTaskRef("somepipeline-another-stage-1"), 175 sh.StructureStageDepth(1), 176 sh.StructureStageParent("Parent Stage"), 177 sh.StructureStagePrevious("A Working Stage")), 178 ), 179 }, 180 { 181 name: "parallel_stages", 182 expected: sh.ParsedPipeline( 183 sh.PipelineAgent("some-image"), 184 sh.PipelineStage("First Stage", 185 sh.StageStep(sh.StepCmd("echo"), sh.StepArg("first"))), 186 sh.PipelineStage("Parent Stage", 187 sh.StageParallel("A Working Stage", 188 sh.StageStep(sh.StepCmd("echo"), sh.StepArg("hello"), sh.StepArg("world"))), 189 sh.StageParallel("Another stage", 190 sh.StageStep(sh.StepCmd("echo"), sh.StepArg("again"))), 191 ), 192 sh.PipelineStage("Last Stage", 193 sh.StageStep(sh.StepCmd("echo"), sh.StepArg("last"))), 194 ), 195 pipeline: tb.Pipeline("somepipeline-1", tb.PipelineNamespace("jx"), tb.PipelineSpec( 196 tb.PipelineTask("first-stage", "somepipeline-first-stage-1", 197 tb.PipelineTaskInputResource("workspace", "somepipeline"), 198 tb.PipelineTaskOutputResource("workspace", "somepipeline")), 199 tb.PipelineTask("a-working-stage", "somepipeline-a-working-stage-1", 200 tb.PipelineTaskInputResource("workspace", "somepipeline", tb.From("first-stage")), 201 tb.RunAfter("first-stage")), 202 tb.PipelineTask("another-stage", "somepipeline-another-stage-1", 203 tb.PipelineTaskInputResource("workspace", "somepipeline", 204 tb.From("first-stage")), 205 tb.RunAfter("first-stage")), 206 tb.PipelineTask("last-stage", "somepipeline-last-stage-1", 207 tb.PipelineTaskInputResource("workspace", "somepipeline", tb.From("first-stage")), 208 tb.RunAfter("a-working-stage", "another-stage")), 209 tb.PipelineDeclaredResource("somepipeline", tektonv1alpha1.PipelineResourceTypeGit))), 210 tasks: []*tektonv1alpha1.Task{ 211 tb.Task("somepipeline-first-stage-1", tb.TaskNamespace("jx"), sh.TaskStageLabel("First Stage"), tb.TaskSpec( 212 tb.TaskInputs( 213 tb.InputsResource("workspace", tektonv1alpha1.PipelineResourceTypeGit, 214 tb.ResourceTargetPath("source"))), 215 tb.TaskOutputs(sh.OutputsResource("workspace", tektonv1alpha1.PipelineResourceTypeGit, tb.ResourceTargetPath("source"))), 216 tb.Step(resolvedGitMergeImage, tb.StepName("setup-builder-home"), tb.StepCommand("/bin/sh", "-c"), tb.StepArgs("[ -d /builder/home ] || mkdir -p /builder && ln -s /tekton/home /builder/home"), tb.StepWorkingDir("/workspace/source")), 217 tb.Step(resolvedGitMergeImage, tb.StepName("git-merge"), tb.StepCommand("jx"), tb.StepArgs("step", "git", "merge", "--verbose"), tb.StepWorkingDir("/workspace/source")), 218 tb.Step("some-image:0.0.1", tb.StepName("step2"), tb.StepCommand("/bin/sh", "-c"), tb.StepArgs("echo first"), tb.StepWorkingDir("/workspace/source")), 219 )), 220 tb.Task("somepipeline-a-working-stage-1", tb.TaskNamespace("jx"), sh.TaskStageLabel("A Working Stage"), 221 tb.TaskSpec( 222 tb.TaskInputs( 223 tb.InputsResource("workspace", tektonv1alpha1.PipelineResourceTypeGit, 224 tb.ResourceTargetPath("source"))), 225 tb.Step(resolvedGitMergeImage, tb.StepName("setup-builder-home"), tb.StepCommand("/bin/sh", "-c"), tb.StepArgs("[ -d /builder/home ] || mkdir -p /builder && ln -s /tekton/home /builder/home"), tb.StepWorkingDir("/workspace/source")), 226 tb.Step("some-image:0.0.1", tb.StepName("step2"), tb.StepCommand("/bin/sh", "-c"), tb.StepArgs("echo hello world"), tb.StepWorkingDir("/workspace/source")), 227 )), 228 tb.Task("somepipeline-another-stage-1", tb.TaskNamespace("jx"), sh.TaskStageLabel("Another stage"), tb.TaskSpec( 229 tb.TaskInputs( 230 tb.InputsResource("workspace", tektonv1alpha1.PipelineResourceTypeGit, 231 tb.ResourceTargetPath("source"))), 232 tb.Step(resolvedGitMergeImage, tb.StepName("setup-builder-home"), tb.StepCommand("/bin/sh", "-c"), tb.StepArgs("[ -d /builder/home ] || mkdir -p /builder && ln -s /tekton/home /builder/home"), tb.StepWorkingDir("/workspace/source")), 233 tb.Step("some-image:0.0.1", tb.StepName("step2"), tb.StepCommand("/bin/sh", "-c"), tb.StepArgs("echo again"), tb.StepWorkingDir("/workspace/source")), 234 )), 235 tb.Task("somepipeline-last-stage-1", tb.TaskNamespace("jx"), sh.TaskStageLabel("Last Stage"), tb.TaskSpec( 236 tb.TaskInputs( 237 tb.InputsResource("workspace", tektonv1alpha1.PipelineResourceTypeGit, 238 tb.ResourceTargetPath("source"))), 239 tb.Step(resolvedGitMergeImage, tb.StepName("setup-builder-home"), tb.StepCommand("/bin/sh", "-c"), tb.StepArgs("[ -d /builder/home ] || mkdir -p /builder && ln -s /tekton/home /builder/home"), tb.StepWorkingDir("/workspace/source")), 240 tb.Step("some-image:0.0.1", tb.StepName("step2"), tb.StepCommand("/bin/sh", "-c"), tb.StepArgs("echo last"), tb.StepWorkingDir("/workspace/source")), 241 )), 242 }, 243 structure: sh.PipelineStructure("somepipeline-1", 244 sh.StructureStage("First Stage", sh.StructureStageTaskRef("somepipeline-first-stage-1")), 245 sh.StructureStage("Parent Stage", 246 sh.StructureStageParallel("A Working Stage", "Another stage"), 247 sh.StructureStagePrevious("First Stage"), 248 ), 249 sh.StructureStage("A Working Stage", sh.StructureStageTaskRef("somepipeline-a-working-stage-1"), 250 sh.StructureStageDepth(1), 251 sh.StructureStageParent("Parent Stage"), 252 ), 253 sh.StructureStage("Another stage", sh.StructureStageTaskRef("somepipeline-another-stage-1"), 254 sh.StructureStageDepth(1), 255 sh.StructureStageParent("Parent Stage"), 256 ), 257 sh.StructureStage("Last Stage", sh.StructureStageTaskRef("somepipeline-last-stage-1"), 258 sh.StructureStagePrevious("Parent Stage")), 259 ), 260 }, 261 { 262 name: "parallel_and_nested_stages", 263 expected: sh.ParsedPipeline( 264 sh.PipelineAgent("some-image"), 265 sh.PipelineStage("First Stage", 266 sh.StageStep(sh.StepCmd("echo"), sh.StepArg("first"))), 267 sh.PipelineStage("Parent Stage", 268 sh.StageParallel("A Working Stage", 269 sh.StageStep(sh.StepCmd("echo"), sh.StepArg("hello"), sh.StepArg("world"))), 270 sh.StageParallel("Nested In Parallel", 271 sh.StageSequential("Another stage", 272 sh.StageStep(sh.StepCmd("echo"), sh.StepArg("again"))), 273 sh.StageSequential("Some other stage", 274 sh.StageStep(sh.StepCmd("echo"), sh.StepArg("otherwise"))), 275 ), 276 ), 277 sh.PipelineStage("Last Stage", 278 sh.StageStep(sh.StepCmd("echo"), sh.StepArg("last"))), 279 ), 280 pipeline: tb.Pipeline("somepipeline-1", tb.PipelineNamespace("jx"), tb.PipelineSpec( 281 tb.PipelineTask("first-stage", "somepipeline-first-stage-1", 282 tb.PipelineTaskInputResource("workspace", "somepipeline"), 283 tb.PipelineTaskOutputResource("workspace", "somepipeline")), 284 tb.PipelineTask("a-working-stage", "somepipeline-a-working-stage-1", 285 tb.PipelineTaskInputResource("workspace", "somepipeline", 286 tb.From("first-stage")), 287 tb.RunAfter("first-stage")), 288 tb.PipelineTask("another-stage", "somepipeline-another-stage-1", 289 tb.PipelineTaskInputResource("workspace", "somepipeline", 290 tb.From("first-stage")), 291 tb.PipelineTaskOutputResource("workspace", "somepipeline"), 292 tb.RunAfter("first-stage")), 293 tb.PipelineTask("some-other-stage", "somepipeline-some-other-stage-1", 294 tb.PipelineTaskInputResource("workspace", "somepipeline", 295 tb.From("another-stage")), 296 tb.RunAfter("another-stage")), 297 tb.PipelineTask("last-stage", "somepipeline-last-stage-1", 298 tb.PipelineTaskInputResource("workspace", "somepipeline", 299 tb.From("first-stage")), 300 tb.RunAfter("a-working-stage", "some-other-stage")), 301 tb.PipelineDeclaredResource("somepipeline", tektonv1alpha1.PipelineResourceTypeGit))), 302 tasks: []*tektonv1alpha1.Task{ 303 tb.Task("somepipeline-first-stage-1", tb.TaskNamespace("jx"), sh.TaskStageLabel("First Stage"), tb.TaskSpec( 304 tb.TaskInputs( 305 tb.InputsResource("workspace", tektonv1alpha1.PipelineResourceTypeGit, 306 tb.ResourceTargetPath("source"))), 307 tb.TaskOutputs(sh.OutputsResource("workspace", tektonv1alpha1.PipelineResourceTypeGit, tb.ResourceTargetPath("source"))), 308 tb.Step(resolvedGitMergeImage, tb.StepName("setup-builder-home"), tb.StepCommand("/bin/sh", "-c"), tb.StepArgs("[ -d /builder/home ] || mkdir -p /builder && ln -s /tekton/home /builder/home"), tb.StepWorkingDir("/workspace/source")), 309 tb.Step(resolvedGitMergeImage, tb.StepName("git-merge"), tb.StepCommand("jx"), tb.StepArgs("step", "git", "merge", "--verbose"), tb.StepWorkingDir("/workspace/source")), 310 tb.Step("some-image:0.0.1", tb.StepName("step2"), tb.StepCommand("/bin/sh", "-c"), tb.StepArgs("echo first"), tb.StepWorkingDir("/workspace/source")), 311 )), 312 tb.Task("somepipeline-a-working-stage-1", tb.TaskNamespace("jx"), sh.TaskStageLabel("A Working Stage"), 313 tb.TaskSpec( 314 tb.TaskInputs( 315 tb.InputsResource("workspace", tektonv1alpha1.PipelineResourceTypeGit, 316 tb.ResourceTargetPath("source"))), 317 tb.Step(resolvedGitMergeImage, tb.StepName("setup-builder-home"), tb.StepCommand("/bin/sh", "-c"), tb.StepArgs("[ -d /builder/home ] || mkdir -p /builder && ln -s /tekton/home /builder/home"), tb.StepWorkingDir("/workspace/source")), 318 tb.Step("some-image:0.0.1", tb.StepName("step2"), tb.StepCommand("/bin/sh", "-c"), tb.StepArgs("echo hello world"), tb.StepWorkingDir("/workspace/source")), 319 )), 320 tb.Task("somepipeline-another-stage-1", tb.TaskNamespace("jx"), sh.TaskStageLabel("Another stage"), tb.TaskSpec( 321 tb.TaskInputs( 322 tb.InputsResource("workspace", tektonv1alpha1.PipelineResourceTypeGit, 323 tb.ResourceTargetPath("source"))), 324 tb.TaskOutputs(sh.OutputsResource("workspace", tektonv1alpha1.PipelineResourceTypeGit, tb.ResourceTargetPath("source"))), 325 tb.Step(resolvedGitMergeImage, tb.StepName("setup-builder-home"), tb.StepCommand("/bin/sh", "-c"), tb.StepArgs("[ -d /builder/home ] || mkdir -p /builder && ln -s /tekton/home /builder/home"), tb.StepWorkingDir("/workspace/source")), 326 tb.Step("some-image:0.0.1", tb.StepName("step2"), tb.StepCommand("/bin/sh", "-c"), tb.StepArgs("echo again"), tb.StepWorkingDir("/workspace/source")), 327 )), 328 tb.Task("somepipeline-some-other-stage-1", tb.TaskNamespace("jx"), sh.TaskStageLabel("Some other stage"), tb.TaskSpec( 329 tb.TaskInputs( 330 tb.InputsResource("workspace", tektonv1alpha1.PipelineResourceTypeGit, 331 tb.ResourceTargetPath("source"))), 332 tb.Step(resolvedGitMergeImage, tb.StepName("setup-builder-home"), tb.StepCommand("/bin/sh", "-c"), tb.StepArgs("[ -d /builder/home ] || mkdir -p /builder && ln -s /tekton/home /builder/home"), tb.StepWorkingDir("/workspace/source")), 333 tb.Step("some-image:0.0.1", tb.StepName("step2"), tb.StepCommand("/bin/sh", "-c"), tb.StepArgs("echo otherwise"), tb.StepWorkingDir("/workspace/source")), 334 )), 335 tb.Task("somepipeline-last-stage-1", tb.TaskNamespace("jx"), sh.TaskStageLabel("Last Stage"), tb.TaskSpec( 336 tb.TaskInputs( 337 tb.InputsResource("workspace", tektonv1alpha1.PipelineResourceTypeGit, 338 tb.ResourceTargetPath("source"))), 339 tb.Step(resolvedGitMergeImage, tb.StepName("setup-builder-home"), tb.StepCommand("/bin/sh", "-c"), tb.StepArgs("[ -d /builder/home ] || mkdir -p /builder && ln -s /tekton/home /builder/home"), tb.StepWorkingDir("/workspace/source")), 340 tb.Step("some-image:0.0.1", tb.StepName("step2"), tb.StepCommand("/bin/sh", "-c"), tb.StepArgs("echo last"), tb.StepWorkingDir("/workspace/source")), 341 )), 342 }, 343 structure: sh.PipelineStructure("somepipeline-1", 344 sh.StructureStage("First Stage", sh.StructureStageTaskRef("somepipeline-first-stage-1")), 345 sh.StructureStage("Parent Stage", 346 sh.StructureStageParallel("A Working Stage", "Nested In Parallel"), 347 sh.StructureStagePrevious("First Stage"), 348 ), 349 sh.StructureStage("A Working Stage", sh.StructureStageTaskRef("somepipeline-a-working-stage-1"), 350 sh.StructureStageDepth(1), 351 sh.StructureStageParent("Parent Stage"), 352 ), 353 sh.StructureStage("Nested In Parallel", 354 sh.StructureStageParent("Parent Stage"), 355 sh.StructureStageDepth(1), 356 sh.StructureStageStages("Another stage", "Some other stage"), 357 ), 358 sh.StructureStage("Another stage", sh.StructureStageTaskRef("somepipeline-another-stage-1"), 359 sh.StructureStageDepth(2), 360 sh.StructureStageParent("Nested In Parallel"), 361 ), 362 sh.StructureStage("Some other stage", sh.StructureStageTaskRef("somepipeline-some-other-stage-1"), 363 sh.StructureStageDepth(2), 364 sh.StructureStageParent("Nested In Parallel"), 365 sh.StructureStagePrevious("Another stage"), 366 ), 367 sh.StructureStage("Last Stage", sh.StructureStageTaskRef("somepipeline-last-stage-1"), 368 sh.StructureStagePrevious("Parent Stage")), 369 ), 370 }, 371 { 372 name: "custom_workspaces", 373 expected: sh.ParsedPipeline( 374 sh.PipelineAgent("some-image"), 375 sh.PipelineStage("stage1", 376 sh.StageStep(sh.StepCmd("ls")), 377 ), 378 sh.PipelineStage("stage2", 379 sh.StageOptions( 380 sh.StageOptionsWorkspace(customWorkspace), 381 ), 382 sh.StageStep(sh.StepCmd("ls")), 383 ), 384 sh.PipelineStage("stage3", 385 sh.StageOptions( 386 sh.StageOptionsWorkspace(defaultWorkspace), 387 ), 388 sh.StageStep(sh.StepCmd("ls")), 389 ), 390 sh.PipelineStage("stage4", 391 sh.StageOptions( 392 sh.StageOptionsWorkspace(customWorkspace), 393 ), 394 sh.StageStep(sh.StepCmd("ls")), 395 ), 396 ), 397 pipeline: tb.Pipeline("somepipeline-1", tb.PipelineNamespace("jx"), tb.PipelineSpec( 398 tb.PipelineTask("stage1", "somepipeline-stage1-1", 399 tb.PipelineTaskInputResource("workspace", "somepipeline"), 400 tb.PipelineTaskOutputResource("workspace", "somepipeline")), 401 tb.PipelineTask("stage2", "somepipeline-stage2-1", 402 tb.PipelineTaskInputResource("workspace", "somepipeline"), 403 tb.PipelineTaskOutputResource("workspace", "somepipeline"), 404 tb.RunAfter("stage1")), 405 tb.PipelineTask("stage3", "somepipeline-stage3-1", 406 tb.PipelineTaskInputResource("workspace", "somepipeline", tb.From("stage1")), 407 tb.PipelineTaskOutputResource("workspace", "somepipeline"), 408 tb.RunAfter("stage2")), 409 tb.PipelineTask("stage4", "somepipeline-stage4-1", 410 tb.PipelineTaskInputResource("workspace", "somepipeline", tb.From("stage2")), 411 tb.RunAfter("stage3")), 412 tb.PipelineDeclaredResource("somepipeline", tektonv1alpha1.PipelineResourceTypeGit))), 413 tasks: []*tektonv1alpha1.Task{ 414 tb.Task("somepipeline-stage1-1", tb.TaskNamespace("jx"), sh.TaskStageLabel("stage1"), tb.TaskSpec( 415 tb.TaskInputs( 416 tb.InputsResource("workspace", tektonv1alpha1.PipelineResourceTypeGit, 417 tb.ResourceTargetPath("source"))), 418 tb.TaskOutputs(sh.OutputsResource("workspace", tektonv1alpha1.PipelineResourceTypeGit, tb.ResourceTargetPath("source"))), 419 tb.Step(resolvedGitMergeImage, tb.StepName("setup-builder-home"), tb.StepCommand("/bin/sh", "-c"), tb.StepArgs("[ -d /builder/home ] || mkdir -p /builder && ln -s /tekton/home /builder/home"), tb.StepWorkingDir("/workspace/source")), 420 tb.Step(resolvedGitMergeImage, tb.StepName("git-merge"), tb.StepCommand("jx"), tb.StepArgs("step", "git", "merge", "--verbose"), tb.StepWorkingDir("/workspace/source")), 421 tb.Step("some-image:0.0.1", tb.StepName("step2"), tb.StepCommand("/bin/sh", "-c"), tb.StepArgs("ls"), tb.StepWorkingDir("/workspace/source")), 422 )), 423 tb.Task("somepipeline-stage2-1", tb.TaskNamespace("jx"), sh.TaskStageLabel("stage2"), tb.TaskSpec( 424 tb.TaskInputs( 425 tb.InputsResource("workspace", tektonv1alpha1.PipelineResourceTypeGit, 426 tb.ResourceTargetPath("source"))), 427 tb.TaskOutputs(sh.OutputsResource("workspace", tektonv1alpha1.PipelineResourceTypeGit, tb.ResourceTargetPath("source"))), 428 tb.Step(resolvedGitMergeImage, tb.StepName("setup-builder-home"), tb.StepCommand("/bin/sh", "-c"), tb.StepArgs("[ -d /builder/home ] || mkdir -p /builder && ln -s /tekton/home /builder/home"), tb.StepWorkingDir("/workspace/source")), 429 tb.Step("some-image:0.0.1", tb.StepName("step2"), tb.StepCommand("/bin/sh", "-c"), tb.StepArgs("ls"), tb.StepWorkingDir("/workspace/source")), 430 )), 431 tb.Task("somepipeline-stage3-1", tb.TaskNamespace("jx"), sh.TaskStageLabel("stage3"), tb.TaskSpec( 432 tb.TaskInputs( 433 tb.InputsResource("workspace", tektonv1alpha1.PipelineResourceTypeGit, 434 tb.ResourceTargetPath("source"))), 435 tb.TaskOutputs(sh.OutputsResource("workspace", tektonv1alpha1.PipelineResourceTypeGit, tb.ResourceTargetPath("source"))), 436 tb.Step(resolvedGitMergeImage, tb.StepName("setup-builder-home"), tb.StepCommand("/bin/sh", "-c"), tb.StepArgs("[ -d /builder/home ] || mkdir -p /builder && ln -s /tekton/home /builder/home"), tb.StepWorkingDir("/workspace/source")), 437 tb.Step("some-image:0.0.1", tb.StepName("step2"), tb.StepCommand("/bin/sh", "-c"), tb.StepArgs("ls"), tb.StepWorkingDir("/workspace/source")), 438 )), 439 tb.Task("somepipeline-stage4-1", tb.TaskNamespace("jx"), sh.TaskStageLabel("stage4"), tb.TaskSpec( 440 tb.TaskInputs( 441 tb.InputsResource("workspace", tektonv1alpha1.PipelineResourceTypeGit, 442 tb.ResourceTargetPath("source"))), 443 tb.Step(resolvedGitMergeImage, tb.StepName("setup-builder-home"), tb.StepCommand("/bin/sh", "-c"), tb.StepArgs("[ -d /builder/home ] || mkdir -p /builder && ln -s /tekton/home /builder/home"), tb.StepWorkingDir("/workspace/source")), 444 tb.Step("some-image:0.0.1", tb.StepName("step2"), tb.StepCommand("/bin/sh", "-c"), tb.StepArgs("ls"), tb.StepWorkingDir("/workspace/source")), 445 )), 446 }, 447 structure: sh.PipelineStructure("somepipeline-1", 448 sh.StructureStage("stage1", sh.StructureStageTaskRef("somepipeline-stage1-1")), 449 sh.StructureStage("stage2", sh.StructureStageTaskRef("somepipeline-stage2-1"), sh.StructureStagePrevious("stage1")), 450 sh.StructureStage("stage3", sh.StructureStageTaskRef("somepipeline-stage3-1"), sh.StructureStagePrevious("stage2")), 451 sh.StructureStage("stage4", sh.StructureStageTaskRef("somepipeline-stage4-1"), sh.StructureStagePrevious("stage3")), 452 ), 453 }, 454 { 455 name: "inherited_custom_workspaces", 456 expected: sh.ParsedPipeline( 457 sh.PipelineAgent("some-image"), 458 sh.PipelineStage("stage1", 459 sh.StageStep(sh.StepCmd("ls")), 460 ), 461 sh.PipelineStage("stage2", 462 sh.StageOptions( 463 sh.StageOptionsWorkspace(customWorkspace), 464 ), 465 sh.StageSequential("stage3", 466 sh.StageStep(sh.StepCmd("ls")), 467 ), 468 sh.StageSequential("stage4", 469 sh.StageOptions( 470 sh.StageOptionsWorkspace(defaultWorkspace), 471 ), 472 sh.StageStep(sh.StepCmd("ls")), 473 ), 474 sh.StageSequential("stage5", 475 sh.StageStep(sh.StepCmd("ls")), 476 ), 477 ), 478 ), 479 pipeline: tb.Pipeline("somepipeline-1", tb.PipelineNamespace("jx"), tb.PipelineSpec( 480 tb.PipelineTask("stage1", "somepipeline-stage1-1", 481 tb.PipelineTaskInputResource("workspace", "somepipeline"), 482 tb.PipelineTaskOutputResource("workspace", "somepipeline")), 483 tb.PipelineTask("stage3", "somepipeline-stage3-1", 484 tb.PipelineTaskInputResource("workspace", "somepipeline"), 485 tb.PipelineTaskOutputResource("workspace", "somepipeline"), 486 tb.RunAfter("stage1")), 487 tb.PipelineTask("stage4", "somepipeline-stage4-1", 488 tb.PipelineTaskInputResource("workspace", "somepipeline", 489 tb.From("stage1")), 490 tb.PipelineTaskOutputResource("workspace", "somepipeline"), 491 tb.RunAfter("stage3")), 492 tb.PipelineTask("stage5", "somepipeline-stage5-1", 493 tb.PipelineTaskInputResource("workspace", "somepipeline", 494 tb.From("stage3")), 495 tb.RunAfter("stage4")), 496 tb.PipelineDeclaredResource("somepipeline", tektonv1alpha1.PipelineResourceTypeGit))), 497 tasks: []*tektonv1alpha1.Task{ 498 tb.Task("somepipeline-stage1-1", tb.TaskNamespace("jx"), sh.TaskStageLabel("stage1"), tb.TaskSpec( 499 tb.TaskInputs( 500 tb.InputsResource("workspace", tektonv1alpha1.PipelineResourceTypeGit, 501 tb.ResourceTargetPath("source"))), 502 tb.TaskOutputs(sh.OutputsResource("workspace", tektonv1alpha1.PipelineResourceTypeGit, tb.ResourceTargetPath("source"))), 503 tb.Step(resolvedGitMergeImage, tb.StepName("setup-builder-home"), tb.StepCommand("/bin/sh", "-c"), tb.StepArgs("[ -d /builder/home ] || mkdir -p /builder && ln -s /tekton/home /builder/home"), tb.StepWorkingDir("/workspace/source")), 504 tb.Step(resolvedGitMergeImage, tb.StepName("git-merge"), tb.StepCommand("jx"), tb.StepArgs("step", "git", "merge", "--verbose"), tb.StepWorkingDir("/workspace/source")), 505 tb.Step("some-image:0.0.1", tb.StepName("step2"), tb.StepCommand("/bin/sh", "-c"), tb.StepArgs("ls"), tb.StepWorkingDir("/workspace/source")), 506 )), 507 tb.Task("somepipeline-stage3-1", tb.TaskNamespace("jx"), sh.TaskStageLabel("stage3"), tb.TaskSpec( 508 tb.TaskInputs( 509 tb.InputsResource("workspace", tektonv1alpha1.PipelineResourceTypeGit, 510 tb.ResourceTargetPath("source"))), 511 tb.TaskOutputs(sh.OutputsResource("workspace", tektonv1alpha1.PipelineResourceTypeGit, tb.ResourceTargetPath("source"))), 512 tb.Step(resolvedGitMergeImage, tb.StepName("setup-builder-home"), tb.StepCommand("/bin/sh", "-c"), tb.StepArgs("[ -d /builder/home ] || mkdir -p /builder && ln -s /tekton/home /builder/home"), tb.StepWorkingDir("/workspace/source")), 513 tb.Step("some-image:0.0.1", tb.StepName("step2"), tb.StepCommand("/bin/sh", "-c"), tb.StepArgs("ls"), tb.StepWorkingDir("/workspace/source")), 514 )), 515 tb.Task("somepipeline-stage4-1", tb.TaskNamespace("jx"), sh.TaskStageLabel("stage4"), tb.TaskSpec( 516 tb.TaskInputs( 517 tb.InputsResource("workspace", tektonv1alpha1.PipelineResourceTypeGit, 518 tb.ResourceTargetPath("source"))), 519 tb.TaskOutputs(sh.OutputsResource("workspace", tektonv1alpha1.PipelineResourceTypeGit, tb.ResourceTargetPath("source"))), 520 tb.Step(resolvedGitMergeImage, tb.StepName("setup-builder-home"), tb.StepCommand("/bin/sh", "-c"), tb.StepArgs("[ -d /builder/home ] || mkdir -p /builder && ln -s /tekton/home /builder/home"), tb.StepWorkingDir("/workspace/source")), 521 tb.Step("some-image:0.0.1", tb.StepName("step2"), tb.StepCommand("/bin/sh", "-c"), tb.StepArgs("ls"), tb.StepWorkingDir("/workspace/source")), 522 )), 523 tb.Task("somepipeline-stage5-1", tb.TaskNamespace("jx"), sh.TaskStageLabel("stage5"), tb.TaskSpec( 524 tb.TaskInputs( 525 tb.InputsResource("workspace", tektonv1alpha1.PipelineResourceTypeGit, 526 tb.ResourceTargetPath("source"))), 527 tb.Step(resolvedGitMergeImage, tb.StepName("setup-builder-home"), tb.StepCommand("/bin/sh", "-c"), tb.StepArgs("[ -d /builder/home ] || mkdir -p /builder && ln -s /tekton/home /builder/home"), tb.StepWorkingDir("/workspace/source")), 528 tb.Step("some-image:0.0.1", tb.StepName("step2"), tb.StepCommand("/bin/sh", "-c"), tb.StepArgs("ls"), tb.StepWorkingDir("/workspace/source")), 529 )), 530 }, 531 structure: sh.PipelineStructure("somepipeline-1", 532 sh.StructureStage("stage1", sh.StructureStageTaskRef("somepipeline-stage1-1")), 533 sh.StructureStage("stage2", 534 sh.StructureStagePrevious("stage1"), 535 sh.StructureStageStages("stage3", "stage4", "stage5"), 536 ), 537 sh.StructureStage("stage3", sh.StructureStageTaskRef("somepipeline-stage3-1"), 538 sh.StructureStageDepth(1), 539 sh.StructureStageParent("stage2")), 540 sh.StructureStage("stage4", sh.StructureStageTaskRef("somepipeline-stage4-1"), 541 sh.StructureStagePrevious("stage3"), 542 sh.StructureStageDepth(1), 543 sh.StructureStageParent("stage2")), 544 sh.StructureStage("stage5", sh.StructureStageTaskRef("somepipeline-stage5-1"), 545 sh.StructureStagePrevious("stage4"), 546 sh.StructureStageDepth(1), 547 sh.StructureStageParent("stage2")), 548 ), 549 }, 550 { 551 name: "environment_at_top_and_in_stage", 552 expected: sh.ParsedPipeline( 553 sh.PipelineAgent("some-image"), 554 sh.PipelineEnvVar("SOME_VAR", "A value for the env var"), 555 sh.PipelineStage("A stage with environment", 556 sh.StageEnvVar("SOME_OTHER_VAR", "A value for the other env var"), 557 sh.StageStep(sh.StepCmd("echo"), sh.StepArg("hello"), sh.StepArg("${SOME_OTHER_VAR}")), 558 sh.StageStep( 559 sh.StepCmd("echo"), 560 sh.StepArg("goodbye"), sh.StepArg("${SOME_VAR} and ${ANOTHER_VAR}"), 561 sh.StepEnvVar("SOME_VAR", "An overriding value"), 562 sh.StepEnvVar("ANOTHER_VAR", "Yet another variable"), 563 ), 564 ), 565 ), 566 pipeline: tb.Pipeline("somepipeline-1", tb.PipelineNamespace("jx"), tb.PipelineSpec( 567 tb.PipelineTask("a-stage-with-environment", "somepipeline-a-stage-with-environment-1", 568 tb.PipelineTaskInputResource("workspace", "somepipeline"), 569 ), 570 tb.PipelineDeclaredResource("somepipeline", tektonv1alpha1.PipelineResourceTypeGit))), 571 tasks: []*tektonv1alpha1.Task{ 572 tb.Task("somepipeline-a-stage-with-environment-1", tb.TaskNamespace("jx"), 573 sh.TaskStageLabel("A stage with environment"), 574 tb.TaskSpec( 575 tb.TaskInputs( 576 tb.InputsResource("workspace", tektonv1alpha1.PipelineResourceTypeGit, 577 tb.ResourceTargetPath("source"))), 578 tb.Step(resolvedGitMergeImage, tb.StepName("setup-builder-home"), tb.StepCommand("/bin/sh", "-c"), tb.StepArgs("[ -d /builder/home ] || mkdir -p /builder && ln -s /tekton/home /builder/home"), tb.StepWorkingDir("/workspace/source"), 579 tb.StepEnvVar("SOME_OTHER_VAR", "A value for the other env var"), tb.StepEnvVar("SOME_VAR", "A value for the env var")), 580 tb.Step(resolvedGitMergeImage, tb.StepName("git-merge"), tb.StepCommand("jx"), tb.StepArgs("step", "git", "merge", "--verbose"), tb.StepWorkingDir("/workspace/source"), 581 tb.StepEnvVar("SOME_OTHER_VAR", "A value for the other env var"), tb.StepEnvVar("SOME_VAR", "A value for the env var")), 582 tb.Step("some-image:0.0.1", tb.StepName("step2"), tb.StepCommand("/bin/sh", "-c"), tb.StepArgs("echo hello ${SOME_OTHER_VAR}"), tb.StepWorkingDir("/workspace/source"), 583 tb.StepEnvVar("SOME_OTHER_VAR", "A value for the other env var"), tb.StepEnvVar("SOME_VAR", "A value for the env var")), 584 tb.Step("some-image:0.0.1", tb.StepName("step3"), tb.StepCommand("/bin/sh", "-c"), tb.StepArgs("echo goodbye ${SOME_VAR} and ${ANOTHER_VAR}"), tb.StepWorkingDir("/workspace/source"), 585 tb.StepEnvVar("ANOTHER_VAR", "Yet another variable"), 586 tb.StepEnvVar("SOME_OTHER_VAR", "A value for the other env var"), 587 tb.StepEnvVar("SOME_VAR", "An overriding value"), 588 ), 589 )), 590 }, 591 structure: sh.PipelineStructure("somepipeline-1", 592 sh.StructureStage("A stage with environment", sh.StructureStageTaskRef("somepipeline-a-stage-with-environment-1")), 593 ), 594 }, 595 { 596 name: "syntactic_sugar_step_and_a_command", 597 expected: sh.ParsedPipeline( 598 sh.PipelineAgent("some-image"), 599 sh.PipelineStage("A Working Stage", 600 sh.StageStep(sh.StepCmd("echo"), sh.StepArg("hello"), sh.StepArg("world")), 601 sh.StageStep(sh.StepStep("some-step"), 602 sh.StepOptions(map[string]string{"firstParam": "some value", "secondParam": "some other value"})), 603 ), 604 ), 605 expectedErrorMsg: "syntactic sugar steps not yet supported", 606 }, 607 { 608 name: "post", 609 expected: sh.ParsedPipeline( 610 sh.PipelineAgent("some-image"), 611 sh.PipelineStage("A Working Stage", 612 sh.StageStep(sh.StepCmd("echo"), sh.StepArg("hello"), sh.StepArg("world")), 613 sh.StagePost(syntax.PostConditionSuccess, 614 sh.PostAction("mail", map[string]string{ 615 "to": "foo@bar.com", 616 "subject": "Yay, it passed", 617 })), 618 sh.StagePost(syntax.PostConditionFailure, 619 sh.PostAction("slack", map[string]string{ 620 "whatever": "the", 621 "slack": "config", 622 "actually": "is. =)", 623 })), 624 sh.StagePost(syntax.PostConditionAlways, 625 sh.PostAction("junit", map[string]string{ 626 "pattern": "target/surefire-reports/**/*.xml", 627 }), 628 ), 629 ), 630 ), 631 expectedErrorMsg: "post on stages not yet supported", 632 }, 633 { 634 name: "top_level_and_stage_options", 635 expected: sh.ParsedPipeline( 636 sh.PipelineAgent("some-image"), 637 sh.PipelineOptions( 638 sh.PipelineOptionsTimeout(50, "minutes"), 639 sh.PipelineOptionsRetry(3), 640 ), 641 sh.PipelineStage("A Working Stage", 642 sh.StageOptions( 643 sh.StageOptionsTimeout(5, "seconds"), 644 sh.StageOptionsRetry(4), 645 sh.StageOptionsStash("Some Files", "somedir/**/*"), 646 sh.StageOptionsUnstash("Earlier Files", "some/sub/dir"), 647 ), 648 sh.StageStep(sh.StepCmd("echo"), sh.StepArg("hello"), sh.StepArg("world")), 649 ), 650 ), 651 expectedErrorMsg: "Retry at top level not yet supported", 652 }, 653 { 654 name: "stage_and_step_agent", 655 expected: sh.ParsedPipeline( 656 sh.PipelineStage("A Working Stage", 657 sh.StageAgent("some-image"), 658 sh.StageStep( 659 sh.StepCmd("echo"), 660 sh.StepArg("hello"), sh.StepArg("world"), 661 sh.StepAgent("some-other-image"), 662 ), 663 sh.StageStep(sh.StepCmd("echo"), sh.StepArg("goodbye")), 664 ), 665 ), 666 pipeline: tb.Pipeline("somepipeline-1", tb.PipelineNamespace("jx"), tb.PipelineSpec( 667 tb.PipelineTask("a-working-stage", "somepipeline-a-working-stage-1", 668 tb.PipelineTaskInputResource("workspace", "somepipeline"), 669 ), 670 tb.PipelineDeclaredResource("somepipeline", tektonv1alpha1.PipelineResourceTypeGit))), 671 tasks: []*tektonv1alpha1.Task{ 672 tb.Task("somepipeline-a-working-stage-1", tb.TaskNamespace("jx"), sh.TaskStageLabel("A Working Stage"), 673 tb.TaskSpec( 674 tb.TaskInputs( 675 tb.InputsResource("workspace", tektonv1alpha1.PipelineResourceTypeGit, 676 tb.ResourceTargetPath("source"))), 677 tb.Step(resolvedGitMergeImage, tb.StepName("setup-builder-home"), tb.StepCommand("/bin/sh", "-c"), tb.StepArgs("[ -d /builder/home ] || mkdir -p /builder && ln -s /tekton/home /builder/home"), tb.StepWorkingDir("/workspace/source")), 678 tb.Step(resolvedGitMergeImage, tb.StepName("git-merge"), tb.StepCommand("jx"), tb.StepArgs("step", "git", "merge", "--verbose"), tb.StepWorkingDir("/workspace/source")), 679 tb.Step("some-other-image", tb.StepName("step2"), tb.StepCommand("/bin/sh", "-c"), tb.StepArgs("echo hello world"), tb.StepWorkingDir("/workspace/source")), 680 tb.Step("some-image:0.0.1", tb.StepName("step3"), tb.StepCommand("/bin/sh", "-c"), tb.StepArgs("echo goodbye"), tb.StepWorkingDir("/workspace/source")), 681 )), 682 }, 683 structure: sh.PipelineStructure("somepipeline-1", 684 sh.StructureStage("A Working Stage", sh.StructureStageTaskRef("somepipeline-a-working-stage-1")), 685 ), 686 }, 687 { 688 name: "mangled_task_names", 689 expected: sh.ParsedPipeline( 690 sh.PipelineAgent("some-image"), 691 sh.PipelineStage(". -a- .", 692 sh.StageStep(sh.StepCmd("ls")), 693 ), 694 sh.PipelineStage("Wööh!!!! - This is cool.", 695 sh.StageStep(sh.StepCmd("ls")), 696 ), 697 ), 698 pipeline: tb.Pipeline("somepipeline-1", tb.PipelineNamespace("jx"), tb.PipelineSpec( 699 tb.PipelineTask("a", "somepipeline-a-1", 700 tb.PipelineTaskInputResource("workspace", "somepipeline"), 701 tb.PipelineTaskOutputResource("workspace", "somepipeline")), 702 tb.PipelineTask("wh-this-is-cool", "somepipeline-wh-this-is-cool-1", 703 tb.PipelineTaskInputResource("workspace", "somepipeline", 704 tb.From("a")), 705 tb.RunAfter("a")), 706 tb.PipelineDeclaredResource("somepipeline", tektonv1alpha1.PipelineResourceTypeGit))), 707 tasks: []*tektonv1alpha1.Task{ 708 tb.Task("somepipeline-a-1", tb.TaskNamespace("jx"), sh.TaskStageLabel("a"), tb.TaskSpec( 709 tb.TaskInputs( 710 tb.InputsResource("workspace", tektonv1alpha1.PipelineResourceTypeGit, 711 tb.ResourceTargetPath("source"))), 712 tb.TaskOutputs(sh.OutputsResource("workspace", tektonv1alpha1.PipelineResourceTypeGit, tb.ResourceTargetPath("source"))), 713 tb.Step(resolvedGitMergeImage, tb.StepName("setup-builder-home"), tb.StepCommand("/bin/sh", "-c"), tb.StepArgs("[ -d /builder/home ] || mkdir -p /builder && ln -s /tekton/home /builder/home"), tb.StepWorkingDir("/workspace/source")), 714 tb.Step(resolvedGitMergeImage, tb.StepName("git-merge"), tb.StepCommand("jx"), tb.StepArgs("step", "git", "merge", "--verbose"), tb.StepWorkingDir("/workspace/source")), 715 tb.Step("some-image:0.0.1", tb.StepName("step2"), tb.StepCommand("/bin/sh", "-c"), tb.StepArgs("ls"), tb.StepWorkingDir("/workspace/source")), 716 )), 717 tb.Task("somepipeline-wh-this-is-cool-1", tb.TaskNamespace("jx"), sh.TaskStageLabel("wh-this-is-cool"), 718 tb.TaskSpec( 719 tb.TaskInputs( 720 tb.InputsResource("workspace", tektonv1alpha1.PipelineResourceTypeGit, 721 tb.ResourceTargetPath("source"))), 722 tb.Step(resolvedGitMergeImage, tb.StepName("setup-builder-home"), tb.StepCommand("/bin/sh", "-c"), tb.StepArgs("[ -d /builder/home ] || mkdir -p /builder && ln -s /tekton/home /builder/home"), tb.StepWorkingDir("/workspace/source")), 723 tb.Step("some-image:0.0.1", tb.StepName("step2"), tb.StepCommand("/bin/sh", "-c"), tb.StepArgs("ls"), tb.StepWorkingDir("/workspace/source")), 724 )), 725 }, 726 structure: sh.PipelineStructure("somepipeline-1", 727 sh.StructureStage(". -a- .", sh.StructureStageTaskRef("somepipeline-a-1")), 728 sh.StructureStage("Wööh!!!! - This is cool.", sh.StructureStageTaskRef("somepipeline-wh-this-is-cool-1"), sh.StructureStagePrevious(". -a- .")), 729 ), 730 }, 731 { 732 name: "stage_timeout", 733 expected: sh.ParsedPipeline( 734 sh.PipelineAgent("some-image"), 735 sh.PipelineStage("A Working Stage", 736 sh.StageOptions( 737 sh.StageOptionsTimeout(50, "minutes"), 738 ), 739 sh.StageStep(sh.StepCmd("echo"), sh.StepArg("hello"), sh.StepArg("world")), 740 ), 741 ), 742 /* TODO: Stop erroring out once we figure out how to handle task timeouts again 743 pipeline: tb.Pipeline("somepipeline-1", tb.TaskNamespace("jx"), tb.PipelineSpec( 744 tb.PipelineTask("a-working-stage", "somepipeline-a-working-stage-1", 745 tb.PipelineTaskInputResource("workspace", "somepipeline"), 746 tb.PipelineTaskInputResource("temp-ordering-resource", "temp-ordering-resource"), 747 tb.PipelineTaskOutputResource("workspace", "somepipeline")), 748 tb.PipelineDeclaredResource("somepipeline", tektonv1alpha1.PipelineResourceTypeGit))), 749 tasks: []*tektonv1alpha1.Task{ 750 tb.Task("somepipeline-a-working-stage-1", tb.TaskNamespace("jx"), TaskStageLabel("A Working Stage"), 751 tb.TaskSpec( 752 tb.TaskTimeout(50*time.Minute), 753 tb.TaskInputs( 754 tb.InputsResource("workspace", tektonv1alpha1.PipelineResourceTypeGit, 755 tb.ResourceTargetPath("source"))), 756 tb.TaskOutputs(sh.OutputsResource("workspace", tektonv1alpha1.PipelineResourceTypeGit, tb.ResourceTargetPath("source"))), 757 tb.Step("some-image:0.0.1", tb.StepName("step2"), tb.StepCommand("/bin/sh", "-c"), tb.StepArgs("echo hello world"), tb.StepWorkingDir("/workspace/source")), 758 )), 759 },*/ 760 expectedErrorMsg: "Timeout on stage not yet supported", 761 }, 762 { 763 name: "top_level_timeout", 764 expected: sh.ParsedPipeline( 765 sh.PipelineAgent("some-image"), 766 sh.PipelineOptions( 767 sh.PipelineOptionsTimeout(50, "minutes"), 768 ), 769 sh.PipelineStage("A Working Stage", 770 sh.StageStep(sh.StepCmd("echo"), sh.StepArg("hello"), sh.StepArg("world")), 771 ), 772 ), 773 pipeline: tb.Pipeline("somepipeline-1", tb.PipelineNamespace("jx"), tb.PipelineSpec( 774 tb.PipelineTask("a-working-stage", "somepipeline-a-working-stage-1", 775 tb.PipelineTaskInputResource("workspace", "somepipeline"), 776 ), 777 tb.PipelineDeclaredResource("somepipeline", tektonv1alpha1.PipelineResourceTypeGit))), 778 tasks: []*tektonv1alpha1.Task{ 779 tb.Task("somepipeline-a-working-stage-1", tb.TaskNamespace("jx"), sh.TaskStageLabel("A Working Stage"), 780 tb.TaskSpec( 781 tb.TaskInputs( 782 tb.InputsResource("workspace", tektonv1alpha1.PipelineResourceTypeGit, 783 tb.ResourceTargetPath("source"))), 784 tb.Step(resolvedGitMergeImage, tb.StepName("setup-builder-home"), tb.StepCommand("/bin/sh", "-c"), tb.StepArgs("[ -d /builder/home ] || mkdir -p /builder && ln -s /tekton/home /builder/home"), tb.StepWorkingDir("/workspace/source")), 785 tb.Step(resolvedGitMergeImage, tb.StepName("git-merge"), tb.StepCommand("jx"), tb.StepArgs("step", "git", "merge", "--verbose"), tb.StepWorkingDir("/workspace/source")), 786 tb.Step("some-image:0.0.1", tb.StepName("step2"), tb.StepCommand("/bin/sh", "-c"), tb.StepArgs("echo hello world"), tb.StepWorkingDir("/workspace/source")), 787 )), 788 }, 789 structure: sh.PipelineStructure("somepipeline-1", 790 sh.StructureStage("A Working Stage", sh.StructureStageTaskRef("somepipeline-a-working-stage-1")), 791 ), 792 }, 793 { 794 name: "loop_step", 795 expected: sh.ParsedPipeline( 796 sh.PipelineEnvVar("LANGUAGE", "rust"), 797 sh.PipelineAgent("some-image"), 798 sh.PipelineStage("A Working Stage", 799 sh.StageEnvVar("DISTRO", "gentoo"), 800 sh.StageStep( 801 sh.StepLoop("LANGUAGE", []string{"maven", "gradle", "nodejs"}, 802 sh.LoopStep(sh.StepCmd("echo"), sh.StepArg("hello"), sh.StepArg("${LANGUAGE}")), 803 sh.LoopStep(sh.StepLoop("DISTRO", []string{"fedora", "ubuntu", "debian"}, 804 sh.LoopStep(sh.StepCmd("echo"), 805 sh.StepArg("running"), sh.StepArg("${LANGUAGE}"), 806 sh.StepArg("on"), sh.StepArg("${DISTRO}")), 807 )), 808 ), 809 ), 810 sh.StageStep(sh.StepCmd("echo"), sh.StepArg("hello"), sh.StepArg("after")), 811 ), 812 ), 813 pipeline: tb.Pipeline("somepipeline-1", tb.PipelineNamespace("jx"), tb.PipelineSpec( 814 tb.PipelineTask("a-working-stage", "somepipeline-a-working-stage-1", 815 tb.PipelineTaskInputResource("workspace", "somepipeline"), 816 ), 817 tb.PipelineDeclaredResource("somepipeline", tektonv1alpha1.PipelineResourceTypeGit))), 818 tasks: []*tektonv1alpha1.Task{ 819 tb.Task("somepipeline-a-working-stage-1", tb.TaskNamespace("jx"), sh.TaskStageLabel("A Working Stage"), 820 tb.TaskSpec( 821 tb.TaskInputs( 822 tb.InputsResource("workspace", tektonv1alpha1.PipelineResourceTypeGit, 823 tb.ResourceTargetPath("source"))), 824 tb.Step(resolvedGitMergeImage, tb.StepName("setup-builder-home"), tb.StepCommand("/bin/sh", "-c"), tb.StepArgs("[ -d /builder/home ] || mkdir -p /builder && ln -s /tekton/home /builder/home"), tb.StepWorkingDir("/workspace/source"), 825 tb.StepEnvVar("DISTRO", "gentoo"), tb.StepEnvVar("LANGUAGE", "rust")), 826 tb.Step(resolvedGitMergeImage, tb.StepName("git-merge"), tb.StepCommand("jx"), tb.StepArgs("step", "git", "merge", "--verbose"), tb.StepWorkingDir("/workspace/source"), 827 tb.StepEnvVar("DISTRO", "gentoo"), tb.StepEnvVar("LANGUAGE", "rust")), 828 tb.Step("some-image:0.0.1", tb.StepName("step2"), tb.StepCommand("/bin/sh", "-c"), tb.StepArgs("echo hello ${LANGUAGE}"), tb.StepWorkingDir("/workspace/source"), 829 tb.StepEnvVar("DISTRO", "gentoo"), tb.StepEnvVar("LANGUAGE", "maven")), 830 tb.Step("some-image:0.0.1", tb.StepName("step3"), tb.StepCommand("/bin/sh", "-c"), tb.StepArgs("echo running ${LANGUAGE} on ${DISTRO}"), tb.StepWorkingDir("/workspace/source"), 831 tb.StepEnvVar("DISTRO", "fedora"), tb.StepEnvVar("LANGUAGE", "maven")), 832 tb.Step("some-image:0.0.1", tb.StepName("step4"), tb.StepCommand("/bin/sh", "-c"), tb.StepArgs("echo running ${LANGUAGE} on ${DISTRO}"), tb.StepWorkingDir("/workspace/source"), 833 tb.StepEnvVar("DISTRO", "ubuntu"), tb.StepEnvVar("LANGUAGE", "maven")), 834 tb.Step("some-image:0.0.1", tb.StepName("step5"), tb.StepCommand("/bin/sh", "-c"), tb.StepArgs("echo running ${LANGUAGE} on ${DISTRO}"), tb.StepWorkingDir("/workspace/source"), 835 tb.StepEnvVar("DISTRO", "debian"), tb.StepEnvVar("LANGUAGE", "maven")), 836 tb.Step("some-image:0.0.1", tb.StepName("step6"), tb.StepCommand("/bin/sh", "-c"), tb.StepArgs("echo hello ${LANGUAGE}"), tb.StepWorkingDir("/workspace/source"), 837 tb.StepEnvVar("DISTRO", "gentoo"), tb.StepEnvVar("LANGUAGE", "gradle")), 838 tb.Step("some-image:0.0.1", tb.StepName("step7"), tb.StepCommand("/bin/sh", "-c"), tb.StepArgs("echo running ${LANGUAGE} on ${DISTRO}"), tb.StepWorkingDir("/workspace/source"), 839 tb.StepEnvVar("DISTRO", "fedora"), tb.StepEnvVar("LANGUAGE", "gradle")), 840 tb.Step("some-image:0.0.1", tb.StepName("step8"), tb.StepCommand("/bin/sh", "-c"), tb.StepArgs("echo running ${LANGUAGE} on ${DISTRO}"), tb.StepWorkingDir("/workspace/source"), 841 tb.StepEnvVar("DISTRO", "ubuntu"), tb.StepEnvVar("LANGUAGE", "gradle")), 842 tb.Step("some-image:0.0.1", tb.StepName("step9"), tb.StepCommand("/bin/sh", "-c"), tb.StepArgs("echo running ${LANGUAGE} on ${DISTRO}"), tb.StepWorkingDir("/workspace/source"), 843 tb.StepEnvVar("DISTRO", "debian"), tb.StepEnvVar("LANGUAGE", "gradle")), 844 tb.Step("some-image:0.0.1", tb.StepName("step10"), tb.StepCommand("/bin/sh", "-c"), tb.StepArgs("echo hello ${LANGUAGE}"), tb.StepWorkingDir("/workspace/source"), 845 tb.StepEnvVar("DISTRO", "gentoo"), tb.StepEnvVar("LANGUAGE", "nodejs")), 846 tb.Step("some-image:0.0.1", tb.StepName("step11"), tb.StepCommand("/bin/sh", "-c"), tb.StepArgs("echo running ${LANGUAGE} on ${DISTRO}"), tb.StepWorkingDir("/workspace/source"), 847 tb.StepEnvVar("DISTRO", "fedora"), tb.StepEnvVar("LANGUAGE", "nodejs")), 848 tb.Step("some-image:0.0.1", tb.StepName("step12"), tb.StepCommand("/bin/sh", "-c"), tb.StepArgs("echo running ${LANGUAGE} on ${DISTRO}"), tb.StepWorkingDir("/workspace/source"), 849 tb.StepEnvVar("DISTRO", "ubuntu"), tb.StepEnvVar("LANGUAGE", "nodejs")), 850 tb.Step("some-image:0.0.1", tb.StepName("step13"), tb.StepCommand("/bin/sh", "-c"), tb.StepArgs("echo running ${LANGUAGE} on ${DISTRO}"), tb.StepWorkingDir("/workspace/source"), 851 tb.StepEnvVar("DISTRO", "debian"), tb.StepEnvVar("LANGUAGE", "nodejs")), 852 tb.Step("some-image:0.0.1", tb.StepName("step14"), tb.StepCommand("/bin/sh", "-c"), tb.StepArgs("echo hello after"), tb.StepWorkingDir("/workspace/source"), 853 tb.StepEnvVar("DISTRO", "gentoo"), tb.StepEnvVar("LANGUAGE", "rust")), 854 )), 855 }, 856 structure: sh.PipelineStructure("somepipeline-1", 857 sh.StructureStage("A Working Stage", sh.StructureStageTaskRef("somepipeline-a-working-stage-1")), 858 ), 859 }, 860 { 861 name: "loop_step_with_name", 862 expected: sh.ParsedPipeline( 863 sh.PipelineEnvVar("LANGUAGE", "rust"), 864 sh.PipelineAgent("some-image"), 865 sh.PipelineStage("A Working Stage", 866 sh.StageEnvVar("DISTRO", "gentoo"), 867 sh.StageStep( 868 sh.StepLoop("LANGUAGE", []string{"maven", "gradle", "nodejs"}, 869 sh.LoopStep( 870 sh.StepName("echo-step"), 871 sh.StepCmd("echo"), 872 sh.StepArg("hello"), 873 sh.StepArg("${LANGUAGE}")), 874 ), 875 ), 876 ), 877 ), 878 pipeline: tb.Pipeline("somepipeline-1", tb.PipelineNamespace("jx"), tb.PipelineSpec( 879 tb.PipelineTask("a-working-stage", "somepipeline-a-working-stage-1", 880 tb.PipelineTaskInputResource("workspace", "somepipeline"), 881 ), 882 tb.PipelineDeclaredResource("somepipeline", tektonv1alpha1.PipelineResourceTypeGit))), 883 tasks: []*tektonv1alpha1.Task{ 884 tb.Task("somepipeline-a-working-stage-1", tb.TaskNamespace("jx"), sh.TaskStageLabel("A Working Stage"), 885 tb.TaskSpec( 886 tb.TaskInputs( 887 tb.InputsResource("workspace", tektonv1alpha1.PipelineResourceTypeGit, 888 tb.ResourceTargetPath("source"))), 889 tb.Step(resolvedGitMergeImage, tb.StepName("setup-builder-home"), tb.StepCommand("/bin/sh", "-c"), tb.StepArgs("[ -d /builder/home ] || mkdir -p /builder && ln -s /tekton/home /builder/home"), tb.StepWorkingDir("/workspace/source"), 890 tb.StepEnvVar("DISTRO", "gentoo"), tb.StepEnvVar("LANGUAGE", "rust")), 891 tb.Step(resolvedGitMergeImage, tb.StepName("git-merge"), tb.StepCommand("jx"), tb.StepArgs("step", "git", "merge", "--verbose"), tb.StepWorkingDir("/workspace/source"), 892 tb.StepEnvVar("DISTRO", "gentoo"), tb.StepEnvVar("LANGUAGE", "rust")), 893 tb.Step("some-image:0.0.1", tb.StepName("echo-step1"), tb.StepCommand("/bin/sh", "-c"), tb.StepArgs("echo hello ${LANGUAGE}"), tb.StepWorkingDir("/workspace/source"), 894 tb.StepEnvVar("DISTRO", "gentoo"), tb.StepEnvVar("LANGUAGE", "maven")), 895 tb.Step("some-image:0.0.1", tb.StepName("echo-step2"), tb.StepCommand("/bin/sh", "-c"), tb.StepArgs("echo hello ${LANGUAGE}"), tb.StepWorkingDir("/workspace/source"), 896 tb.StepEnvVar("DISTRO", "gentoo"), tb.StepEnvVar("LANGUAGE", "gradle")), 897 tb.Step("some-image:0.0.1", tb.StepName("echo-step3"), tb.StepCommand("/bin/sh", "-c"), tb.StepArgs("echo hello ${LANGUAGE}"), tb.StepWorkingDir("/workspace/source"), 898 tb.StepEnvVar("DISTRO", "gentoo"), tb.StepEnvVar("LANGUAGE", "nodejs")), 899 )), 900 }, 901 structure: sh.PipelineStructure("somepipeline-1", 902 sh.StructureStage("A Working Stage", 903 sh.StructureStageTaskRef("somepipeline-a-working-stage-1")), 904 ), 905 }, 906 { 907 name: "loop_with_syntactic_sugar_step", 908 expected: sh.ParsedPipeline( 909 sh.PipelineAgent("some-image"), 910 sh.PipelineStage("A Working Stage", 911 sh.StageStep( 912 sh.StepLoop("LANGUAGE", []string{"maven", "gradle", "nodejs"}, 913 sh.LoopStep(sh.StepCmd("echo"), sh.StepArg("hello"), sh.StepArg("${LANGUAGE}")), 914 sh.LoopStep(sh.StepStep("some-step"), 915 sh.StepOptions(map[string]string{"firstParam": "some value", "secondParam": "some other value"})), 916 ), 917 ), 918 ), 919 ), 920 expectedErrorMsg: "syntactic sugar steps not yet supported", 921 }, 922 { 923 name: "top_level_container_options", 924 expected: sh.ParsedPipeline( 925 sh.PipelineOptions( 926 sh.PipelineContainerOptions( 927 sh.ContainerResourceLimits("0.2", "128Mi"), 928 sh.ContainerResourceRequests("0.1", "64Mi"), 929 ), 930 ), 931 sh.PipelineAgent("some-image"), 932 sh.PipelineStage("A Working Stage", 933 sh.StageStep( 934 sh.StepCmd("echo"), 935 sh.StepArg("hello"), sh.StepArg("world"), 936 ), 937 ), 938 ), 939 pipeline: tb.Pipeline("somepipeline-1", tb.PipelineNamespace("jx"), tb.PipelineSpec( 940 tb.PipelineTask("a-working-stage", "somepipeline-a-working-stage-1", 941 tb.PipelineTaskInputResource("workspace", "somepipeline"), 942 ), 943 tb.PipelineDeclaredResource("somepipeline", tektonv1alpha1.PipelineResourceTypeGit))), 944 tasks: []*tektonv1alpha1.Task{ 945 tb.Task("somepipeline-a-working-stage-1", tb.TaskNamespace("jx"), sh.TaskStageLabel("A Working Stage"), 946 tb.TaskSpec( 947 tb.TaskInputs( 948 tb.InputsResource("workspace", tektonv1alpha1.PipelineResourceTypeGit, 949 tb.ResourceTargetPath("source"))), 950 tb.Step(resolvedGitMergeImage, tb.StepName("setup-builder-home"), tb.StepCommand("/bin/sh", "-c"), tb.StepArgs("[ -d /builder/home ] || mkdir -p /builder && ln -s /tekton/home /builder/home"), tb.StepWorkingDir("/workspace/source"), 951 sh.StepResourceLimits("0.2", "128Mi"), 952 sh.StepResourceRequests("0.1", "64Mi"), 953 ), 954 tb.Step(resolvedGitMergeImage, tb.StepName("git-merge"), tb.StepCommand("jx"), tb.StepArgs("step", "git", "merge", "--verbose"), tb.StepWorkingDir("/workspace/source"), 955 sh.StepResourceLimits("0.2", "128Mi"), 956 sh.StepResourceRequests("0.1", "64Mi"), 957 ), 958 tb.Step("some-image:0.0.1", tb.StepName("step2"), tb.StepCommand("/bin/sh", "-c"), tb.StepArgs("echo hello world"), tb.StepWorkingDir("/workspace/source"), 959 sh.StepResourceLimits("0.2", "128Mi"), 960 sh.StepResourceRequests("0.1", "64Mi"), 961 ), 962 )), 963 }, 964 structure: sh.PipelineStructure("somepipeline-1", 965 sh.StructureStage("A Working Stage", sh.StructureStageTaskRef("somepipeline-a-working-stage-1")), 966 ), 967 }, 968 { 969 name: "stage_overrides_top_level_container_options", 970 expected: sh.ParsedPipeline( 971 sh.PipelineOptions( 972 sh.PipelineContainerOptions( 973 sh.ContainerResourceLimits("0.2", "128Mi"), 974 sh.ContainerResourceRequests("0.1", "64Mi"), 975 ), 976 ), 977 sh.PipelineAgent("some-image"), 978 sh.PipelineStage("A Working Stage", 979 sh.StageOptions( 980 sh.StageContainerOptions( 981 sh.ContainerResourceLimits("0.4", "256Mi"), 982 sh.ContainerResourceRequests("0.2", "128Mi"), 983 ), 984 ), 985 sh.StageStep( 986 sh.StepCmd("echo"), 987 sh.StepArg("hello"), sh.StepArg("world"), 988 ), 989 ), 990 ), 991 pipeline: tb.Pipeline("somepipeline-1", tb.PipelineNamespace("jx"), tb.PipelineSpec( 992 tb.PipelineTask("a-working-stage", "somepipeline-a-working-stage-1", 993 tb.PipelineTaskInputResource("workspace", "somepipeline"), 994 ), 995 tb.PipelineDeclaredResource("somepipeline", tektonv1alpha1.PipelineResourceTypeGit))), 996 tasks: []*tektonv1alpha1.Task{ 997 tb.Task("somepipeline-a-working-stage-1", tb.TaskNamespace("jx"), sh.TaskStageLabel("A Working Stage"), 998 tb.TaskSpec( 999 tb.TaskInputs( 1000 tb.InputsResource("workspace", tektonv1alpha1.PipelineResourceTypeGit, 1001 tb.ResourceTargetPath("source"))), 1002 tb.Step(resolvedGitMergeImage, tb.StepName("setup-builder-home"), tb.StepCommand("/bin/sh", "-c"), tb.StepArgs("[ -d /builder/home ] || mkdir -p /builder && ln -s /tekton/home /builder/home"), tb.StepWorkingDir("/workspace/source"), 1003 sh.StepResourceLimits("0.4", "256Mi"), 1004 sh.StepResourceRequests("0.2", "128Mi"), 1005 ), 1006 tb.Step(resolvedGitMergeImage, tb.StepName("git-merge"), tb.StepCommand("jx"), tb.StepArgs("step", "git", "merge", "--verbose"), tb.StepWorkingDir("/workspace/source"), 1007 sh.StepResourceLimits("0.4", "256Mi"), 1008 sh.StepResourceRequests("0.2", "128Mi"), 1009 ), 1010 tb.Step("some-image:0.0.1", tb.StepName("step2"), tb.StepCommand("/bin/sh", "-c"), tb.StepArgs("echo hello world"), tb.StepWorkingDir("/workspace/source"), 1011 sh.StepResourceLimits("0.4", "256Mi"), 1012 sh.StepResourceRequests("0.2", "128Mi"), 1013 ), 1014 )), 1015 }, 1016 structure: sh.PipelineStructure("somepipeline-1", 1017 sh.StructureStage("A Working Stage", sh.StructureStageTaskRef("somepipeline-a-working-stage-1")), 1018 ), 1019 }, 1020 { 1021 name: "merge_container_options", 1022 expected: sh.ParsedPipeline( 1023 sh.PipelineOptions( 1024 sh.PipelineContainerOptions( 1025 sh.ContainerResourceRequests("0.1", "64Mi"), 1026 ), 1027 ), 1028 sh.PipelineAgent("some-image"), 1029 sh.PipelineStage("A Working Stage", 1030 sh.StageOptions( 1031 sh.StageContainerOptions( 1032 sh.ContainerResourceLimits("0.4", "256Mi"), 1033 ), 1034 ), 1035 sh.StageStep( 1036 sh.StepCmd("echo"), 1037 sh.StepArg("hello"), sh.StepArg("world"), 1038 ), 1039 ), 1040 ), 1041 pipeline: tb.Pipeline("somepipeline-1", tb.PipelineNamespace("jx"), tb.PipelineSpec( 1042 tb.PipelineTask("a-working-stage", "somepipeline-a-working-stage-1", 1043 tb.PipelineTaskInputResource("workspace", "somepipeline"), 1044 ), 1045 tb.PipelineDeclaredResource("somepipeline", tektonv1alpha1.PipelineResourceTypeGit))), 1046 tasks: []*tektonv1alpha1.Task{ 1047 tb.Task("somepipeline-a-working-stage-1", tb.TaskNamespace("jx"), sh.TaskStageLabel("A Working Stage"), 1048 tb.TaskSpec( 1049 tb.TaskInputs( 1050 tb.InputsResource("workspace", tektonv1alpha1.PipelineResourceTypeGit, 1051 tb.ResourceTargetPath("source"))), 1052 tb.Step(resolvedGitMergeImage, tb.StepName("setup-builder-home"), tb.StepCommand("/bin/sh", "-c"), tb.StepArgs("[ -d /builder/home ] || mkdir -p /builder && ln -s /tekton/home /builder/home"), tb.StepWorkingDir("/workspace/source"), 1053 sh.StepResourceLimits("0.4", "256Mi"), 1054 sh.StepResourceRequests("0.1", "64Mi"), 1055 ), 1056 tb.Step(resolvedGitMergeImage, tb.StepName("git-merge"), tb.StepCommand("jx"), tb.StepArgs("step", "git", "merge", "--verbose"), tb.StepWorkingDir("/workspace/source"), 1057 sh.StepResourceLimits("0.4", "256Mi"), 1058 sh.StepResourceRequests("0.1", "64Mi"), 1059 ), 1060 tb.Step("some-image:0.0.1", tb.StepName("step2"), tb.StepCommand("/bin/sh", "-c"), tb.StepArgs("echo hello world"), tb.StepWorkingDir("/workspace/source"), 1061 sh.StepResourceLimits("0.4", "256Mi"), 1062 sh.StepResourceRequests("0.1", "64Mi"), 1063 ), 1064 )), 1065 }, 1066 structure: sh.PipelineStructure("somepipeline-1", 1067 sh.StructureStage("A Working Stage", sh.StructureStageTaskRef("somepipeline-a-working-stage-1")), 1068 ), 1069 }, 1070 { 1071 name: "stage_level_container_options", 1072 expected: sh.ParsedPipeline( 1073 sh.PipelineAgent("some-image"), 1074 sh.PipelineStage("A Working Stage", 1075 sh.StageOptions( 1076 sh.StageContainerOptions( 1077 sh.ContainerResourceLimits("0.2", "128Mi"), 1078 sh.ContainerResourceRequests("0.1", "64Mi"), 1079 ), 1080 ), 1081 sh.StageStep( 1082 sh.StepCmd("echo"), 1083 sh.StepArg("hello"), sh.StepArg("world"), 1084 ), 1085 ), 1086 ), 1087 pipeline: tb.Pipeline("somepipeline-1", tb.PipelineNamespace("jx"), tb.PipelineSpec( 1088 tb.PipelineTask("a-working-stage", "somepipeline-a-working-stage-1", 1089 tb.PipelineTaskInputResource("workspace", "somepipeline"), 1090 ), 1091 tb.PipelineDeclaredResource("somepipeline", tektonv1alpha1.PipelineResourceTypeGit))), 1092 tasks: []*tektonv1alpha1.Task{ 1093 tb.Task("somepipeline-a-working-stage-1", tb.TaskNamespace("jx"), sh.TaskStageLabel("A Working Stage"), 1094 tb.TaskSpec( 1095 tb.TaskInputs( 1096 tb.InputsResource("workspace", tektonv1alpha1.PipelineResourceTypeGit, 1097 tb.ResourceTargetPath("source"))), 1098 tb.Step(resolvedGitMergeImage, tb.StepName("setup-builder-home"), tb.StepCommand("/bin/sh", "-c"), tb.StepArgs("[ -d /builder/home ] || mkdir -p /builder && ln -s /tekton/home /builder/home"), tb.StepWorkingDir("/workspace/source"), 1099 sh.StepResourceLimits("0.2", "128Mi"), 1100 sh.StepResourceRequests("0.1", "64Mi"), 1101 ), 1102 tb.Step(resolvedGitMergeImage, tb.StepName("git-merge"), tb.StepCommand("jx"), tb.StepArgs("step", "git", "merge", "--verbose"), tb.StepWorkingDir("/workspace/source"), 1103 sh.StepResourceLimits("0.2", "128Mi"), 1104 sh.StepResourceRequests("0.1", "64Mi"), 1105 ), 1106 tb.Step("some-image:0.0.1", tb.StepName("step2"), tb.StepCommand("/bin/sh", "-c"), tb.StepArgs("echo hello world"), tb.StepWorkingDir("/workspace/source"), 1107 sh.StepResourceLimits("0.2", "128Mi"), 1108 sh.StepResourceRequests("0.1", "64Mi"), 1109 ), 1110 )), 1111 }, 1112 structure: sh.PipelineStructure("somepipeline-1", 1113 sh.StructureStage("A Working Stage", sh.StructureStageTaskRef("somepipeline-a-working-stage-1")), 1114 ), 1115 }, 1116 { 1117 name: "container_options_env_merge", 1118 expected: sh.ParsedPipeline( 1119 sh.PipelineAgent("some-image"), 1120 sh.PipelineOptions( 1121 sh.PipelineContainerOptions( 1122 tb.EnvVar("SOME_VAR", "A value for the env var"), 1123 tb.EnvVar("OVERRIDE_ENV", "Original value"), 1124 tb.EnvVar("OVERRIDE_STAGE_ENV", "Original value"), 1125 ), 1126 ), 1127 sh.PipelineEnvVar("SOME_OTHER_VAR", "A value for the other env var"), 1128 sh.PipelineEnvVar("OVERRIDE_ENV", "New value"), 1129 sh.PipelineStage("A Working Stage", 1130 sh.StageOptions( 1131 sh.StageContainerOptions( 1132 tb.EnvVar("ANOTHER_OVERRIDE_STAGE_ENV", "Original value"), 1133 ), 1134 ), 1135 sh.StageStep(sh.StepCmd("echo"), sh.StepArg("hello"), sh.StepArg("world")), 1136 sh.StageEnvVar("OVERRIDE_STAGE_ENV", "New value"), 1137 sh.StageEnvVar("ANOTHER_OVERRIDE_STAGE_ENV", "New value"), 1138 ), 1139 ), 1140 pipeline: tb.Pipeline("somepipeline-1", tb.PipelineNamespace("jx"), tb.PipelineSpec( 1141 tb.PipelineTask("a-working-stage", "somepipeline-a-working-stage-1", 1142 tb.PipelineTaskInputResource("workspace", "somepipeline"), 1143 ), 1144 tb.PipelineDeclaredResource("somepipeline", tektonv1alpha1.PipelineResourceTypeGit))), 1145 tasks: []*tektonv1alpha1.Task{ 1146 tb.Task("somepipeline-a-working-stage-1", tb.TaskNamespace("jx"), 1147 sh.TaskStageLabel("A Working Stage"), 1148 tb.TaskSpec( 1149 tb.TaskInputs( 1150 tb.InputsResource("workspace", tektonv1alpha1.PipelineResourceTypeGit, 1151 tb.ResourceTargetPath("source"))), 1152 tb.Step(resolvedGitMergeImage, tb.StepName("setup-builder-home"), tb.StepCommand("/bin/sh", "-c"), tb.StepArgs("[ -d /builder/home ] || mkdir -p /builder && ln -s /tekton/home /builder/home"), tb.StepWorkingDir("/workspace/source"), 1153 tb.StepEnvVar("ANOTHER_OVERRIDE_STAGE_ENV", "New value"), 1154 tb.StepEnvVar("SOME_VAR", "A value for the env var"), 1155 tb.StepEnvVar("OVERRIDE_ENV", "New value"), 1156 tb.StepEnvVar("OVERRIDE_STAGE_ENV", "New value"), 1157 tb.StepEnvVar("SOME_OTHER_VAR", "A value for the other env var"), 1158 ), 1159 tb.Step(resolvedGitMergeImage, tb.StepName("git-merge"), tb.StepCommand("jx"), tb.StepArgs("step", "git", "merge", "--verbose"), tb.StepWorkingDir("/workspace/source"), 1160 tb.StepEnvVar("ANOTHER_OVERRIDE_STAGE_ENV", "New value"), 1161 tb.StepEnvVar("SOME_VAR", "A value for the env var"), 1162 tb.StepEnvVar("OVERRIDE_ENV", "New value"), 1163 tb.StepEnvVar("OVERRIDE_STAGE_ENV", "New value"), 1164 tb.StepEnvVar("SOME_OTHER_VAR", "A value for the other env var"), 1165 ), 1166 tb.Step("some-image:0.0.1", tb.StepName("step2"), tb.StepCommand("/bin/sh", "-c"), tb.StepArgs("echo hello world"), tb.StepWorkingDir("/workspace/source"), 1167 tb.StepEnvVar("ANOTHER_OVERRIDE_STAGE_ENV", "New value"), 1168 tb.StepEnvVar("OVERRIDE_ENV", "New value"), 1169 tb.StepEnvVar("OVERRIDE_STAGE_ENV", "New value"), 1170 tb.StepEnvVar("SOME_OTHER_VAR", "A value for the other env var"), 1171 tb.StepEnvVar("SOME_VAR", "A value for the env var"), 1172 ), 1173 )), 1174 }, 1175 structure: sh.PipelineStructure("somepipeline-1", 1176 sh.StructureStage("A Working Stage", sh.StructureStageTaskRef("somepipeline-a-working-stage-1")), 1177 ), 1178 }, 1179 { 1180 name: "dir_on_pipeline_and_stage", 1181 expected: sh.ParsedPipeline( 1182 sh.PipelineAgent("some-image"), 1183 sh.PipelineDir("a-relative-dir"), 1184 sh.PipelineStage("A Working Stage", 1185 sh.StageStep( 1186 sh.StepCmd("echo"), 1187 sh.StepArg("hello"), sh.StepArg("world")), 1188 ), 1189 sh.PipelineStage("Another stage", 1190 sh.StageDir("/an/absolute/dir"), 1191 sh.StageStep( 1192 sh.StepCmd("echo"), 1193 sh.StepArg("again")), 1194 sh.StageStep( 1195 sh.StepCmd("echo"), 1196 sh.StepArg("in another dir"), 1197 sh.StepDir("another-relative-dir/with/a/subdir"))), 1198 ), 1199 pipeline: tb.Pipeline("somepipeline-1", tb.PipelineNamespace("jx"), tb.PipelineSpec( 1200 tb.PipelineTask("a-working-stage", "somepipeline-a-working-stage-1", 1201 tb.PipelineTaskInputResource("workspace", "somepipeline"), 1202 tb.PipelineTaskOutputResource("workspace", "somepipeline")), 1203 tb.PipelineTask("another-stage", "somepipeline-another-stage-1", 1204 tb.PipelineTaskInputResource("workspace", "somepipeline", 1205 tb.From("a-working-stage")), 1206 tb.RunAfter("a-working-stage")), 1207 tb.PipelineDeclaredResource("somepipeline", tektonv1alpha1.PipelineResourceTypeGit))), 1208 tasks: []*tektonv1alpha1.Task{ 1209 tb.Task("somepipeline-a-working-stage-1", tb.TaskNamespace("jx"), sh.TaskStageLabel("A Working Stage"), tb.TaskSpec( 1210 tb.TaskInputs( 1211 tb.InputsResource("workspace", tektonv1alpha1.PipelineResourceTypeGit, 1212 tb.ResourceTargetPath("source"))), 1213 tb.TaskOutputs(sh.OutputsResource("workspace", tektonv1alpha1.PipelineResourceTypeGit, tb.ResourceTargetPath("source"))), 1214 tb.Step(resolvedGitMergeImage, tb.StepName("setup-builder-home"), tb.StepCommand("/bin/sh", "-c"), tb.StepArgs("[ -d /builder/home ] || mkdir -p /builder && ln -s /tekton/home /builder/home"), tb.StepWorkingDir("/workspace/source")), 1215 tb.Step(resolvedGitMergeImage, tb.StepName("git-merge"), tb.StepCommand("jx"), tb.StepArgs("step", "git", "merge", "--verbose"), tb.StepWorkingDir("/workspace/source")), 1216 tb.Step("some-image:0.0.1", tb.StepName("step2"), tb.StepCommand("/bin/sh", "-c"), 1217 tb.StepArgs("cd /workspace/source/a-relative-dir && echo hello world"), tb.StepWorkingDir("/workspace/source")), 1218 )), 1219 tb.Task("somepipeline-another-stage-1", tb.TaskNamespace("jx"), sh.TaskStageLabel("Another stage"), tb.TaskSpec( 1220 tb.TaskInputs( 1221 tb.InputsResource("workspace", tektonv1alpha1.PipelineResourceTypeGit, 1222 tb.ResourceTargetPath("source"))), 1223 tb.Step(resolvedGitMergeImage, tb.StepName("setup-builder-home"), tb.StepCommand("/bin/sh", "-c"), tb.StepArgs("[ -d /builder/home ] || mkdir -p /builder && ln -s /tekton/home /builder/home"), tb.StepWorkingDir("/workspace/source")), 1224 tb.Step("some-image:0.0.1", tb.StepName("step2"), tb.StepCommand("/bin/sh", "-c"), 1225 tb.StepArgs("cd /an/absolute/dir && echo again"), 1226 tb.StepWorkingDir("/workspace/source")), 1227 tb.Step("some-image:0.0.1", tb.StepName("step3"), tb.StepCommand("/bin/sh", "-c"), 1228 tb.StepArgs("cd /workspace/source/another-relative-dir/with/a/subdir && echo in another dir"), 1229 tb.StepWorkingDir("/workspace/source")), 1230 )), 1231 }, 1232 structure: sh.PipelineStructure("somepipeline-1", 1233 sh.StructureStage("A Working Stage", sh.StructureStageTaskRef("somepipeline-a-working-stage-1")), 1234 sh.StructureStage("Another stage", sh.StructureStageTaskRef("somepipeline-another-stage-1"), 1235 sh.StructureStagePrevious("A Working Stage")), 1236 ), 1237 }, 1238 { 1239 name: "sidecars", 1240 expected: sh.ParsedPipeline( 1241 sh.PipelineOptions( 1242 sh.PipelineSidecar(&corev1.Container{ 1243 Name: "top-level-sidecar", 1244 Image: "top-level/sidecar:tag", 1245 }), 1246 ), 1247 sh.PipelineAgent("some-image"), 1248 sh.PipelineStage("A Working Stage", 1249 sh.StageOptions( 1250 sh.StageSidecar(&corev1.Container{ 1251 Name: "stage-level-sidecar", 1252 Image: "stage-level/sidecar:latest", 1253 VolumeMounts: []corev1.VolumeMount{ 1254 { 1255 Name: "shared-volume", 1256 MountPath: "/shared", 1257 }, 1258 }, 1259 }), 1260 sh.StageVolume(&corev1.Volume{ 1261 Name: "shared-volume", 1262 VolumeSource: corev1.VolumeSource{ 1263 EmptyDir: &corev1.EmptyDirVolumeSource{}, 1264 }, 1265 }), 1266 sh.StageContainerOptions( 1267 sh.ContainerVolumeMount("shared-volume", "/mnt/shared"), 1268 ), 1269 ), 1270 sh.StageStep( 1271 sh.StepCmd("echo"), 1272 sh.StepArg("hello"), sh.StepArg("world"), 1273 sh.StepName("A Step With Spaces And Such"), 1274 ), 1275 ), 1276 ), 1277 pipeline: tb.Pipeline("somepipeline-1", tb.PipelineNamespace("jx"), tb.PipelineSpec( 1278 tb.PipelineTask("a-working-stage", "somepipeline-a-working-stage-1", 1279 tb.PipelineTaskInputResource("workspace", "somepipeline"), 1280 ), 1281 tb.PipelineDeclaredResource("somepipeline", tektonv1alpha1.PipelineResourceTypeGit))), 1282 tasks: []*tektonv1alpha1.Task{ 1283 tb.Task("somepipeline-a-working-stage-1", tb.TaskNamespace("jx"), sh.TaskStageLabel("A Working Stage"), 1284 tb.TaskSpec( 1285 tb.TaskInputs( 1286 tb.InputsResource("workspace", tektonv1alpha1.PipelineResourceTypeGit, 1287 tb.ResourceTargetPath("source"))), 1288 tb.Step(resolvedGitMergeImage, tb.StepName("setup-builder-home"), tb.StepCommand("/bin/sh", "-c"), tb.StepArgs("[ -d /builder/home ] || mkdir -p /builder && ln -s /tekton/home /builder/home"), tb.StepWorkingDir("/workspace/source"), 1289 sh.StepVolumeMount("shared-volume", "/mnt/shared"), 1290 ), 1291 tb.Step(resolvedGitMergeImage, tb.StepName("git-merge"), 1292 tb.StepCommand("jx"), 1293 tb.StepArgs("step", "git", "merge", "--verbose"), 1294 tb.StepWorkingDir("/workspace/source"), 1295 sh.StepVolumeMount("shared-volume", "/mnt/shared"), 1296 ), 1297 tb.Step("some-image:0.0.1", tb.StepName("a-step-with-spaces-and-such"), 1298 tb.StepCommand("/bin/sh", "-c"), 1299 tb.StepArgs("echo hello world"), 1300 tb.StepWorkingDir("/workspace/source"), 1301 sh.StepVolumeMount("shared-volume", "/mnt/shared"), 1302 ), 1303 tb.Sidecar("stage-level-sidecar", "stage-level/sidecar:latest", 1304 tb.VolumeMount("shared-volume", "/shared"), 1305 ), 1306 tb.Sidecar("top-level-sidecar", "top-level/sidecar:tag"), 1307 tb.TaskVolume("shared-volume", tb.VolumeSource(corev1.VolumeSource{ 1308 EmptyDir: &corev1.EmptyDirVolumeSource{}, 1309 })), 1310 )), 1311 }, 1312 structure: sh.PipelineStructure("somepipeline-1", 1313 sh.StructureStage("A Working Stage", sh.StructureStageTaskRef("somepipeline-a-working-stage-1")), 1314 ), 1315 }, 1316 { 1317 name: "volumes", 1318 expected: sh.ParsedPipeline( 1319 sh.PipelineOptions( 1320 sh.PipelineVolume(&corev1.Volume{ 1321 Name: "top-level-volume", 1322 VolumeSource: corev1.VolumeSource{ 1323 PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{ 1324 ClaimName: "top-level-volume", 1325 ReadOnly: true, 1326 }, 1327 }, 1328 }), 1329 ), 1330 sh.PipelineAgent("some-image"), 1331 sh.PipelineStage("A Working Stage", 1332 sh.StageOptions( 1333 sh.StageVolume(&corev1.Volume{ 1334 Name: "stage-level-volume", 1335 VolumeSource: corev1.VolumeSource{ 1336 GCEPersistentDisk: &corev1.GCEPersistentDiskVolumeSource{ 1337 PDName: "stage-level-volume", 1338 }, 1339 }, 1340 }), 1341 sh.StageContainerOptions( 1342 sh.ContainerVolumeMount("top-level-volume", "/mnt/top-level-volume"), 1343 sh.ContainerVolumeMount("stage-level-volume", "/mnt/stage-level-volume"), 1344 ), 1345 ), 1346 sh.StageStep( 1347 sh.StepCmd("echo"), 1348 sh.StepArg("hello"), sh.StepArg("world"), 1349 sh.StepName("A Step With Spaces And Such"), 1350 ), 1351 ), 1352 ), 1353 pipeline: tb.Pipeline("somepipeline-1", tb.PipelineNamespace("jx"), tb.PipelineSpec( 1354 tb.PipelineTask("a-working-stage", "somepipeline-a-working-stage-1", 1355 tb.PipelineTaskInputResource("workspace", "somepipeline"), 1356 ), 1357 tb.PipelineDeclaredResource("somepipeline", tektonv1alpha1.PipelineResourceTypeGit))), 1358 tasks: []*tektonv1alpha1.Task{ 1359 tb.Task("somepipeline-a-working-stage-1", tb.TaskNamespace("jx"), sh.TaskStageLabel("A Working Stage"), 1360 tb.TaskSpec( 1361 tb.TaskInputs( 1362 tb.InputsResource("workspace", tektonv1alpha1.PipelineResourceTypeGit, 1363 tb.ResourceTargetPath("source"))), 1364 tb.Step(resolvedGitMergeImage, tb.StepName("setup-builder-home"), tb.StepCommand("/bin/sh", "-c"), tb.StepArgs("[ -d /builder/home ] || mkdir -p /builder && ln -s /tekton/home /builder/home"), tb.StepWorkingDir("/workspace/source"), 1365 sh.StepVolumeMount("top-level-volume", "/mnt/top-level-volume"), 1366 sh.StepVolumeMount("stage-level-volume", "/mnt/stage-level-volume"), 1367 ), 1368 tb.Step(resolvedGitMergeImage, tb.StepName("git-merge"), 1369 tb.StepCommand("jx"), 1370 tb.StepArgs("step", "git", "merge", "--verbose"), 1371 tb.StepWorkingDir("/workspace/source"), 1372 sh.StepVolumeMount("top-level-volume", "/mnt/top-level-volume"), 1373 sh.StepVolumeMount("stage-level-volume", "/mnt/stage-level-volume"), 1374 ), 1375 tb.Step("some-image:0.0.1", tb.StepName("a-step-with-spaces-and-such"), 1376 tb.StepCommand("/bin/sh", "-c"), 1377 tb.StepArgs("echo hello world"), 1378 tb.StepWorkingDir("/workspace/source"), 1379 sh.StepVolumeMount("top-level-volume", "/mnt/top-level-volume"), 1380 sh.StepVolumeMount("stage-level-volume", "/mnt/stage-level-volume"), 1381 ), 1382 tb.TaskVolume("stage-level-volume", tb.VolumeSource(corev1.VolumeSource{ 1383 GCEPersistentDisk: &corev1.GCEPersistentDiskVolumeSource{ 1384 PDName: "stage-level-volume", 1385 }, 1386 })), 1387 tb.TaskVolume("top-level-volume", tb.VolumeSource(corev1.VolumeSource{ 1388 PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{ 1389 ClaimName: "top-level-volume", 1390 ReadOnly: true, 1391 }, 1392 })), 1393 )), 1394 }, 1395 structure: sh.PipelineStructure("somepipeline-1", 1396 sh.StructureStage("A Working Stage", sh.StructureStageTaskRef("somepipeline-a-working-stage-1")), 1397 ), 1398 }, 1399 { 1400 name: "node_distributed_parallel_stages", 1401 expected: sh.ParsedPipeline( 1402 sh.PipelineOptions(sh.PipelineOptionsDistributeParallelAcrossNodes(true)), 1403 sh.PipelineAgent("some-image"), 1404 sh.PipelineStage("First Stage", 1405 sh.StageStep(sh.StepCmd("echo"), sh.StepArg("first"))), 1406 sh.PipelineStage("Parent Stage", 1407 sh.StageParallel("A Working Stage", 1408 sh.StageStep(sh.StepCmd("echo"), sh.StepArg("hello"), sh.StepArg("world"))), 1409 sh.StageParallel("Another stage", 1410 sh.StageStep(sh.StepCmd("echo"), sh.StepArg("again"))), 1411 ), 1412 sh.PipelineStage("Last Stage", 1413 sh.StageStep(sh.StepCmd("echo"), sh.StepArg("last"))), 1414 ), 1415 pipeline: tb.Pipeline("somepipeline-1", tb.PipelineNamespace("jx"), tb.PipelineSpec( 1416 tb.PipelineTask("first-stage", "somepipeline-first-stage-1", 1417 tb.PipelineTaskInputResource("workspace", "somepipeline"), 1418 tb.PipelineTaskOutputResource("workspace", "somepipeline")), 1419 tb.PipelineTask("a-working-stage", "somepipeline-a-working-stage-1", 1420 tb.PipelineTaskInputResource("workspace", "somepipeline", tb.From("first-stage")), 1421 tb.RunAfter("first-stage")), 1422 tb.PipelineTask("another-stage", "somepipeline-another-stage-1", 1423 tb.PipelineTaskInputResource("workspace", "somepipeline", 1424 tb.From("first-stage")), 1425 tb.RunAfter("first-stage")), 1426 tb.PipelineTask("last-stage", "somepipeline-last-stage-1", 1427 tb.PipelineTaskInputResource("workspace", "somepipeline", tb.From("first-stage")), 1428 tb.RunAfter("a-working-stage", "another-stage")), 1429 tb.PipelineDeclaredResource("somepipeline", tektonv1alpha1.PipelineResourceTypeGit))), 1430 tasks: []*tektonv1alpha1.Task{ 1431 tb.Task("somepipeline-first-stage-1", tb.TaskNamespace("jx"), sh.TaskStageLabel("First Stage"), tb.TaskSpec( 1432 tb.TaskInputs( 1433 tb.InputsResource("workspace", tektonv1alpha1.PipelineResourceTypeGit, 1434 tb.ResourceTargetPath("source"))), 1435 tb.TaskOutputs(sh.OutputsResource("workspace", tektonv1alpha1.PipelineResourceTypeGit, tb.ResourceTargetPath("source"))), 1436 tb.Step(resolvedGitMergeImage, tb.StepName("setup-builder-home"), tb.StepCommand("/bin/sh", "-c"), tb.StepArgs("[ -d /builder/home ] || mkdir -p /builder && ln -s /tekton/home /builder/home"), tb.StepWorkingDir("/workspace/source")), 1437 tb.Step(resolvedGitMergeImage, tb.StepName("git-merge"), tb.StepCommand("jx"), tb.StepArgs("step", "git", "merge", "--verbose"), tb.StepWorkingDir("/workspace/source")), 1438 tb.Step("some-image:0.0.1", tb.StepName("step2"), tb.StepCommand("/bin/sh", "-c"), tb.StepArgs("echo first"), tb.StepWorkingDir("/workspace/source")), 1439 )), 1440 tb.Task("somepipeline-a-working-stage-1", tb.TaskNamespace("jx"), sh.TaskStageLabel("A Working Stage"), 1441 tb.TaskSpec( 1442 tb.TaskInputs( 1443 tb.InputsResource("workspace", tektonv1alpha1.PipelineResourceTypeGit, 1444 tb.ResourceTargetPath("source"))), 1445 tb.Step(resolvedGitMergeImage, tb.StepName("setup-builder-home"), tb.StepCommand("/bin/sh", "-c"), tb.StepArgs("[ -d /builder/home ] || mkdir -p /builder && ln -s /tekton/home /builder/home"), tb.StepWorkingDir("/workspace/source")), 1446 tb.Step("some-image:0.0.1", tb.StepName("step2"), tb.StepCommand("/bin/sh", "-c"), tb.StepArgs("echo hello world"), tb.StepWorkingDir("/workspace/source")), 1447 )), 1448 tb.Task("somepipeline-another-stage-1", tb.TaskNamespace("jx"), sh.TaskStageLabel("Another stage"), tb.TaskSpec( 1449 tb.TaskInputs( 1450 tb.InputsResource("workspace", tektonv1alpha1.PipelineResourceTypeGit, 1451 tb.ResourceTargetPath("source"))), 1452 tb.Step(resolvedGitMergeImage, tb.StepName("setup-builder-home"), tb.StepCommand("/bin/sh", "-c"), tb.StepArgs("[ -d /builder/home ] || mkdir -p /builder && ln -s /tekton/home /builder/home"), tb.StepWorkingDir("/workspace/source")), 1453 tb.Step("some-image:0.0.1", tb.StepName("step2"), tb.StepCommand("/bin/sh", "-c"), tb.StepArgs("echo again"), tb.StepWorkingDir("/workspace/source")), 1454 )), 1455 tb.Task("somepipeline-last-stage-1", tb.TaskNamespace("jx"), sh.TaskStageLabel("Last Stage"), tb.TaskSpec( 1456 tb.TaskInputs( 1457 tb.InputsResource("workspace", tektonv1alpha1.PipelineResourceTypeGit, 1458 tb.ResourceTargetPath("source"))), 1459 tb.Step(resolvedGitMergeImage, tb.StepName("setup-builder-home"), tb.StepCommand("/bin/sh", "-c"), tb.StepArgs("[ -d /builder/home ] || mkdir -p /builder && ln -s /tekton/home /builder/home"), tb.StepWorkingDir("/workspace/source")), 1460 tb.Step("some-image:0.0.1", tb.StepName("step2"), tb.StepCommand("/bin/sh", "-c"), tb.StepArgs("echo last"), tb.StepWorkingDir("/workspace/source")), 1461 )), 1462 }, 1463 structure: sh.PipelineStructure("somepipeline-1", 1464 sh.StructureStage("First Stage", sh.StructureStageTaskRef("somepipeline-first-stage-1")), 1465 sh.StructureStage("Parent Stage", 1466 sh.StructureStageParallel("A Working Stage", "Another stage"), 1467 sh.StructureStagePrevious("First Stage"), 1468 ), 1469 sh.StructureStage("A Working Stage", sh.StructureStageTaskRef("somepipeline-a-working-stage-1"), 1470 sh.StructureStageDepth(1), 1471 sh.StructureStageParent("Parent Stage"), 1472 ), 1473 sh.StructureStage("Another stage", sh.StructureStageTaskRef("somepipeline-another-stage-1"), 1474 sh.StructureStageDepth(1), 1475 sh.StructureStageParent("Parent Stage"), 1476 ), 1477 sh.StructureStage("Last Stage", sh.StructureStageTaskRef("somepipeline-last-stage-1"), 1478 sh.StructureStagePrevious("Parent Stage")), 1479 ), 1480 }, 1481 { 1482 name: "tolerations", 1483 expected: sh.ParsedPipeline( 1484 sh.PipelineAgent("some-image"), 1485 sh.PipelineOptions( 1486 sh.PipelineTolerations([]corev1.Toleration{{ 1487 Key: "some-key", 1488 Operator: "Exists", 1489 Effect: "NoSchedule", 1490 }}), 1491 sh.PipelinePodLabels(map[string]string{ 1492 "foo": "bar", 1493 "fruit": "apple", 1494 }), 1495 ), 1496 sh.PipelineStage("A Working Stage", 1497 sh.StageStep( 1498 sh.StepCmd("echo"), 1499 sh.StepArg("hello"), sh.StepArg("world"), 1500 sh.StepName("A Step With Spaces And Such"), 1501 ), 1502 ), 1503 ), 1504 pipeline: tb.Pipeline("somepipeline-1", tb.PipelineNamespace("jx"), tb.PipelineSpec( 1505 tb.PipelineTask("a-working-stage", "somepipeline-a-working-stage-1", 1506 tb.PipelineTaskInputResource("workspace", "somepipeline"), 1507 ), 1508 tb.PipelineDeclaredResource("somepipeline", tektonv1alpha1.PipelineResourceTypeGit))), 1509 tasks: []*tektonv1alpha1.Task{ 1510 tb.Task("somepipeline-a-working-stage-1", tb.TaskNamespace("jx"), sh.TaskStageLabel("A Working Stage"), 1511 tb.TaskSpec( 1512 tb.TaskInputs( 1513 tb.InputsResource("workspace", tektonv1alpha1.PipelineResourceTypeGit, 1514 tb.ResourceTargetPath("source"))), 1515 tb.Step(resolvedGitMergeImage, tb.StepName("setup-builder-home"), tb.StepCommand("/bin/sh", "-c"), tb.StepArgs("[ -d /builder/home ] || mkdir -p /builder && ln -s /tekton/home /builder/home"), tb.StepWorkingDir("/workspace/source")), 1516 tb.Step(resolvedGitMergeImage, tb.StepName("git-merge"), tb.StepCommand("jx"), tb.StepArgs("step", "git", "merge", "--verbose"), tb.StepWorkingDir("/workspace/source")), 1517 tb.Step("some-image:0.0.1", tb.StepName("a-step-with-spaces-and-such"), tb.StepCommand("/bin/sh", "-c"), tb.StepArgs("echo hello world"), tb.StepWorkingDir("/workspace/source")), 1518 )), 1519 }, 1520 structure: sh.PipelineStructure("somepipeline-1", 1521 sh.StructureStage("A Working Stage", sh.StructureStageTaskRef("somepipeline-a-working-stage-1")), 1522 ), 1523 }, 1524 } 1525 1526 for _, tt := range tests { 1527 t.Run(tt.name, func(t *testing.T) { 1528 testVersionsDir := filepath.Join("test_data", "stable_versions") 1529 projectConfig, fn, err := config.LoadProjectConfig(filepath.Join("test_data", tt.name)) 1530 if err != nil { 1531 t.Fatalf("Failed to parse YAML for %s: %q", tt.name, err) 1532 } 1533 1534 if projectConfig.PipelineConfig == nil { 1535 t.Fatalf("PipelineConfig at %s is nil: %+v", fn, projectConfig) 1536 } 1537 if &projectConfig.PipelineConfig.Pipelines == nil { 1538 t.Fatalf("Pipelines at %s is nil: %+v", fn, projectConfig.PipelineConfig) 1539 } 1540 if projectConfig.PipelineConfig.Pipelines.Release == nil { 1541 t.Fatalf("Release at %s is nil: %+v", fn, projectConfig.PipelineConfig.Pipelines) 1542 } 1543 if projectConfig.PipelineConfig.Pipelines.Release.Pipeline == nil { 1544 t.Fatalf("Pipeline at %s is nil: %+v", fn, projectConfig.PipelineConfig.Pipelines.Release) 1545 } 1546 parsed := projectConfig.PipelineConfig.Pipelines.Release.Pipeline 1547 1548 if d, _ := kmp.SafeDiff(tt.expected, parsed); d != "" && tt.expected != nil { 1549 t.Errorf("Parsed ParsedPipeline did not match expected: %s", d) 1550 } 1551 1552 validateErr := parsed.Validate(ctx) 1553 if validateErr != nil && tt.validationErrorMsg == "" { 1554 t.Errorf("Validation failed: %s", validateErr) 1555 } 1556 1557 if validateErr != nil && tt.validationErrorMsg != "" { 1558 if tt.validationErrorMsg != validateErr.Details { 1559 t.Errorf("Validation Error failed: '%s', '%s'", validateErr.Details, tt.validationErrorMsg) 1560 } 1561 } 1562 1563 crdParams := syntax.CRDsFromPipelineParams{ 1564 PipelineIdentifier: "somepipeline", 1565 BuildIdentifier: "1", 1566 Namespace: "jx", 1567 VersionsDir: testVersionsDir, 1568 SourceDir: "source", 1569 DefaultImage: "", 1570 InterpretMode: false, 1571 } 1572 pipeline, tasks, structure, err := parsed.GenerateCRDs(crdParams) 1573 1574 if err != nil { 1575 if tt.expectedErrorMsg != "" { 1576 if d := cmp.Diff(tt.expectedErrorMsg, err.Error()); d != "" { 1577 t.Fatalf("CRD generation error did not meet expectation: %s", d) 1578 } 1579 } else { 1580 t.Fatalf("Error generating CRDs: %s", err) 1581 } 1582 } 1583 1584 if tt.expectedErrorMsg == "" && tt.pipeline != nil { 1585 pipeline.TypeMeta = metav1.TypeMeta{} 1586 if d := cmp.Diff(tt.pipeline, pipeline); d != "" { 1587 t.Errorf("Generated Pipeline did not match expected: %s", d) 1588 } 1589 1590 if err := pipeline.Spec.Validate(ctx); err != nil { 1591 t.Errorf("PipelineSpec.Validate(ctx) = %v", err) 1592 } 1593 1594 for _, task := range tasks { 1595 task.TypeMeta = metav1.TypeMeta{} 1596 } 1597 if d, _ := kmp.SafeDiff(tt.tasks, tasks); d != "" { 1598 t.Errorf("Generated Tasks did not match expected: %s", d) 1599 } 1600 1601 for _, task := range tasks { 1602 if err := task.Spec.Validate(ctx); err != nil { 1603 t.Errorf("TaskSpec.Validate(ctx) = %v", err) 1604 } 1605 } 1606 1607 if tt.structure != nil { 1608 if d := cmp.Diff(tt.structure, structure); d != "" { 1609 t.Errorf("Generated PipelineStructure did not match expected: %s", d) 1610 } 1611 } 1612 } 1613 }) 1614 } 1615 } 1616 1617 func TestFailedValidation(t *testing.T) { 1618 ctx := context.Background() 1619 tests := []struct { 1620 name string 1621 expectedError error 1622 }{ 1623 /* TODO: Once we figure out how to differentiate between an empty agent and no agent specified... 1624 { 1625 name: "empty_agent", 1626 expectedError: &apis.FieldError{ 1627 Message: "Invalid apiVersion format: must be 'v(digits).(digits)", 1628 Paths: []string{"apiVersion"}, 1629 }, 1630 }, 1631 */ 1632 { 1633 name: "agent_with_both_image_and_label", 1634 expectedError: apis.ErrMultipleOneOf("label", "image"). 1635 ViaField("agent"), 1636 }, 1637 { 1638 name: "no_stages", 1639 expectedError: apis.ErrMissingField("stages"), 1640 }, 1641 { 1642 name: "no_steps_stages_or_parallel", 1643 expectedError: apis.ErrMissingOneOf("steps", "stages", "parallel").ViaFieldIndex("stages", 0), 1644 }, 1645 { 1646 name: "steps_and_stages", 1647 expectedError: apis.ErrMultipleOneOf("steps", "stages", "parallel").ViaFieldIndex("stages", 0), 1648 }, 1649 { 1650 name: "steps_and_parallel", 1651 expectedError: apis.ErrMultipleOneOf("steps", "stages", "parallel").ViaFieldIndex("stages", 0), 1652 }, 1653 { 1654 name: "stages_and_parallel", 1655 expectedError: apis.ErrMultipleOneOf("steps", "stages", "parallel").ViaFieldIndex("stages", 0), 1656 }, 1657 { 1658 name: "step_without_command_step_or_loop", 1659 expectedError: apis.ErrMissingOneOf("command", "step", "loop").ViaFieldIndex("steps", 0).ViaFieldIndex("stages", 0), 1660 }, 1661 { 1662 name: "step_with_both_command_and_step", 1663 expectedError: apis.ErrMultipleOneOf("command", "step", "loop").ViaFieldIndex("steps", 0).ViaFieldIndex("stages", 0), 1664 }, 1665 { 1666 name: "step_with_both_command_and_loop", 1667 expectedError: apis.ErrMultipleOneOf("command", "step", "loop").ViaFieldIndex("steps", 0).ViaFieldIndex("stages", 0), 1668 }, 1669 { 1670 name: "step_with_command_and_options", 1671 expectedError: (&apis.FieldError{ 1672 Message: "Cannot set options for a command or a loop", 1673 Paths: []string{"options"}, 1674 }).ViaFieldIndex("steps", 0).ViaFieldIndex("stages", 0), 1675 }, 1676 { 1677 name: "step_with_step_and_arguments", 1678 expectedError: (&apis.FieldError{ 1679 Message: "Cannot set command-line arguments for a step or a loop", 1680 Paths: []string{"args"}, 1681 }).ViaFieldIndex("steps", 0).ViaFieldIndex("stages", 0), 1682 }, 1683 { 1684 name: "step_with_loop_and_options", 1685 expectedError: (&apis.FieldError{ 1686 Message: "Cannot set options for a command or a loop", 1687 Paths: []string{"options"}, 1688 }).ViaFieldIndex("steps", 0).ViaFieldIndex("stages", 0), 1689 }, 1690 { 1691 name: "step_with_loop_and_arguments", 1692 expectedError: (&apis.FieldError{ 1693 Message: "Cannot set command-line arguments for a step or a loop", 1694 Paths: []string{"args"}, 1695 }).ViaFieldIndex("steps", 0).ViaFieldIndex("stages", 0), 1696 }, 1697 { 1698 name: "no_parent_or_stage_agent", 1699 expectedError: (&apis.FieldError{ 1700 Message: "No agent specified for stage or for its parent(s)", 1701 Paths: []string{"agent"}, 1702 }).ViaFieldIndex("stages", 0), 1703 }, 1704 { 1705 name: "top_level_timeout_without_time", 1706 expectedError: (&apis.FieldError{ 1707 Message: "Timeout must be greater than zero", 1708 Paths: []string{"time"}, 1709 }).ViaField("timeout").ViaField("options"), 1710 }, 1711 { 1712 name: "stage_timeout_without_time", 1713 expectedError: (&apis.FieldError{ 1714 Message: "Timeout must be greater than zero", 1715 Paths: []string{"time"}, 1716 }).ViaField("timeout").ViaField("options").ViaFieldIndex("stages", 0), 1717 }, 1718 { 1719 name: "top_level_timeout_with_invalid_unit", 1720 expectedError: (&apis.FieldError{ 1721 Message: "years is not a valid time unit. Valid time units are seconds, minutes, hours, days", 1722 Paths: []string{"unit"}, 1723 }).ViaField("timeout").ViaField("options"), 1724 }, 1725 { 1726 name: "stage_timeout_with_invalid_unit", 1727 expectedError: (&apis.FieldError{ 1728 Message: "years is not a valid time unit. Valid time units are seconds, minutes, hours, days", 1729 Paths: []string{"unit"}, 1730 }).ViaField("timeout").ViaField("options").ViaFieldIndex("stages", 0), 1731 }, 1732 { 1733 name: "top_level_timeout_with_invalid_time", 1734 expectedError: (&apis.FieldError{ 1735 Message: "Timeout must be greater than zero", 1736 Paths: []string{"time"}, 1737 }).ViaField("timeout").ViaField("options"), 1738 }, 1739 { 1740 name: "stage_timeout_with_invalid_time", 1741 expectedError: (&apis.FieldError{ 1742 Message: "Timeout must be greater than zero", 1743 Paths: []string{"time"}, 1744 }).ViaField("timeout").ViaField("options").ViaFieldIndex("stages", 0), 1745 }, 1746 { 1747 name: "top_level_retry_with_invalid_count", 1748 expectedError: (&apis.FieldError{ 1749 Message: "Retry count cannot be negative", 1750 Paths: []string{"retry"}, 1751 }).ViaField("options"), 1752 }, 1753 { 1754 name: "stage_retry_with_invalid_count", 1755 expectedError: (&apis.FieldError{ 1756 Message: "Retry count cannot be negative", 1757 Paths: []string{"retry"}, 1758 }).ViaField("options").ViaFieldIndex("stages", 0), 1759 }, 1760 { 1761 name: "stash_without_name", 1762 expectedError: (&apis.FieldError{ 1763 Message: "The stash name must be provided", 1764 Paths: []string{"name"}, 1765 }).ViaField("stash").ViaField("options").ViaFieldIndex("stages", 0), 1766 }, 1767 { 1768 name: "stash_without_files", 1769 expectedError: (&apis.FieldError{ 1770 Message: "files to stash must be provided", 1771 Paths: []string{"files"}, 1772 }).ViaField("stash").ViaField("options").ViaFieldIndex("stages", 0), 1773 }, 1774 { 1775 name: "unstash_without_name", 1776 expectedError: (&apis.FieldError{ 1777 Message: "The unstash name must be provided", 1778 Paths: []string{"name"}, 1779 }).ViaField("unstash").ViaField("options").ViaFieldIndex("stages", 0), 1780 }, 1781 { 1782 name: "blank_stage_name", 1783 expectedError: (&apis.FieldError{ 1784 Message: "Stage name must contain at least one ASCII letter", 1785 Paths: []string{"name"}, 1786 }).ViaFieldIndex("stages", 0), 1787 }, 1788 { 1789 name: "stage_name_duplicates", 1790 expectedError: &apis.FieldError{ 1791 Message: "Stage names must be unique", 1792 Details: "The following stage names are used more than once: 'A Working Stage'", 1793 }, 1794 }, 1795 { 1796 name: "stage_name_duplicates_deeply_nested", 1797 expectedError: &apis.FieldError{ 1798 Message: "Stage names must be unique", 1799 Details: "The following stage names are used more than once: 'Stage With Stages'", 1800 }, 1801 }, 1802 { 1803 name: "stage_name_duplicates_nested", 1804 expectedError: &apis.FieldError{ 1805 Message: "Stage names must be unique", 1806 Details: "The following stage names are used more than once: 'Stage With Stages'", 1807 }, 1808 }, 1809 { 1810 name: "stage_name_duplicates_sequential", 1811 expectedError: &apis.FieldError{ 1812 Message: "Stage names must be unique", 1813 Details: "The following stage names are used more than once: 'A Working title 2', 'A Working title'", 1814 }, 1815 }, 1816 { 1817 name: "stage_name_duplicates_unique_in_scope", 1818 expectedError: &apis.FieldError{ 1819 Message: "Stage names must be unique", 1820 Details: "The following stage names are used more than once: 'A Working title 1', 'A Working title 2'", 1821 }, 1822 }, 1823 { 1824 name: "loop_without_variable", 1825 expectedError: apis.ErrMissingField("variable").ViaField("loop").ViaFieldIndex("steps", 0).ViaFieldIndex("stages", 0), 1826 }, 1827 { 1828 name: "loop_without_steps", 1829 expectedError: apis.ErrMissingField("steps").ViaField("loop").ViaFieldIndex("steps", 0).ViaFieldIndex("stages", 0), 1830 }, 1831 { 1832 name: "loop_without_values", 1833 expectedError: apis.ErrMissingField("values").ViaField("loop").ViaFieldIndex("steps", 0).ViaFieldIndex("stages", 0), 1834 }, 1835 { 1836 name: "top_level_container_options_with_command", 1837 expectedError: (&apis.FieldError{ 1838 Message: "Command cannot be specified in containerOptions", 1839 Paths: []string{"command"}, 1840 }).ViaField("containerOptions").ViaField("options"), 1841 }, 1842 { 1843 name: "unknown_field", 1844 expectedError: errors.New("Validation failures in YAML file test_data/validation_failures/unknown_field/jenkins-x.yml:\npipelineConfig: Additional property banana is not allowed"), 1845 }, 1846 { 1847 name: "comment_field", 1848 expectedError: (&apis.FieldError{ 1849 Message: "the comment field is only valid in legacy build packs, not in jenkins-x.yml. Please remove it.", 1850 Paths: []string{"comment"}, 1851 }).ViaFieldIndex("steps", 0).ViaFieldIndex("stages", 0), 1852 }, 1853 { 1854 name: "groovy_field", 1855 expectedError: (&apis.FieldError{ 1856 Message: "the groovy field is only valid in legacy build packs, not in jenkins-x.yml. Please remove it.", 1857 Paths: []string{"groovy"}, 1858 }).ViaFieldIndex("steps", 0).ViaFieldIndex("stages", 0), 1859 }, 1860 { 1861 name: "when_field", 1862 expectedError: (&apis.FieldError{ 1863 Message: "the when field is only valid in legacy build packs, not in jenkins-x.yml. Please remove it.", 1864 Paths: []string{"when"}, 1865 }).ViaFieldIndex("steps", 0).ViaFieldIndex("stages", 0), 1866 }, 1867 { 1868 name: "container_field", 1869 expectedError: (&apis.FieldError{ 1870 Message: "the container field is deprecated - please use image instead", 1871 Paths: []string{"container"}, 1872 }).ViaFieldIndex("steps", 0).ViaFieldIndex("stages", 0), 1873 }, 1874 { 1875 name: "legacy_steps_field", 1876 expectedError: (&apis.FieldError{ 1877 Message: "the steps field is only valid in legacy build packs, not in jenkins-x.yml. Please remove it and list the nested stages sequentially instead.", 1878 Paths: []string{"steps"}, 1879 }).ViaFieldIndex("steps", 0).ViaFieldIndex("stages", 0), 1880 }, 1881 { 1882 name: "agent_dir_field", 1883 expectedError: (&apis.FieldError{ 1884 Message: "the dir field is only valid in legacy build packs, not in jenkins-x.yml. Please remove it.", 1885 Paths: []string{"dir"}, 1886 }).ViaField("agent"), 1887 }, 1888 { 1889 name: "agent_container_field", 1890 expectedError: (&apis.FieldError{ 1891 Message: "the container field is deprecated - please use image instead", 1892 Paths: []string{"container"}, 1893 }).ViaField("agent"), 1894 }, 1895 { 1896 name: "duplicate_step_names", 1897 expectedError: (&apis.FieldError{ 1898 Message: "step names within a stage must be unique", 1899 Details: "The following step names in the stage A Working Stage are used more than once: A Step With Spaces And Such, Another Step Name", 1900 Paths: []string{"steps"}, 1901 }).ViaFieldIndex("stages", 0), 1902 }, 1903 { 1904 name: "volume_missing_name", 1905 expectedError: apis.ErrMissingField("name").ViaFieldIndex("volumes", 0).ViaField("options"), 1906 }, 1907 { 1908 name: "top_level_missing_volume", 1909 expectedError: (&apis.FieldError{ 1910 Message: "Volume mount name not-present not found in volumes for stage or pipeline", 1911 Paths: []string{"name"}, 1912 }).ViaFieldIndex("volumeMounts", 0).ViaField("containerOptions").ViaField("options"), 1913 }, 1914 { 1915 name: "volume_does_not_exist", 1916 expectedError: (&apis.FieldError{ 1917 Message: "PVC does-not-exist does not exist, so cannot be used as a volume", 1918 Paths: []string{"claimName"}, 1919 }).ViaFieldIndex("volumes", 0).ViaField("options"), 1920 }, 1921 } 1922 1923 for _, tt := range tests { 1924 t.Run(tt.name, func(t *testing.T) { 1925 projectConfig, fn, err := config.LoadProjectConfig(filepath.Join("test_data", "validation_failures", tt.name)) 1926 if err != nil && err.Error() != tt.expectedError.Error() { 1927 t.Fatalf("Failed to parse YAML for %s: %q", tt.name, err) 1928 } 1929 if _, ok := tt.expectedError.(*apis.FieldError); ok { 1930 1931 if projectConfig.PipelineConfig == nil { 1932 t.Fatalf("PipelineConfig at %s is nil: %+v", fn, projectConfig) 1933 } 1934 if &projectConfig.PipelineConfig.Pipelines == nil { 1935 t.Fatalf("Pipelines at %s is nil: %+v", fn, projectConfig.PipelineConfig) 1936 } 1937 if projectConfig.PipelineConfig.Pipelines.Release == nil { 1938 t.Fatalf("Release at %s is nil: %+v", fn, projectConfig.PipelineConfig.Pipelines) 1939 } 1940 if projectConfig.PipelineConfig.Pipelines.Release.Pipeline == nil { 1941 t.Fatalf("Pipeline at %s is nil: %+v", fn, projectConfig.PipelineConfig.Pipelines.Release) 1942 } 1943 parsed := projectConfig.PipelineConfig.Pipelines.Release.Pipeline 1944 kubeClient := kubefake.NewSimpleClientset() 1945 1946 err = parsed.ValidateInCluster(ctx, kubeClient, "jx") 1947 1948 if err == nil { 1949 t.Fatalf("Expected a validation failure but none occurred") 1950 } 1951 1952 if d := cmp.Diff(tt.expectedError, err, cmp.AllowUnexported(apis.FieldError{})); d != "" { 1953 t.Fatalf("Validation error did not meet expectation: %s", d) 1954 } 1955 } 1956 }) 1957 } 1958 } 1959 1960 func getOverridesTestPipeline() *syntax.ParsedPipeline { 1961 return sh.ParsedPipeline( 1962 sh.PipelineAgent("some-image"), 1963 sh.PipelineStage("A Working Stage", 1964 sh.StageStep( 1965 sh.StepCmd("echo"), 1966 sh.StepArg("hello"), sh.StepArg("world")), 1967 ), 1968 sh.PipelineStage("Another stage", 1969 sh.StageStep( 1970 sh.StepCmd("echo"), 1971 sh.StepArg("again"))), 1972 ) 1973 } 1974 1975 func getOverridesTestVolume() *corev1.Volume { 1976 return &corev1.Volume{ 1977 Name: "stage-volume", 1978 VolumeSource: corev1.VolumeSource{ 1979 PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{ 1980 ClaimName: "stage-volume", 1981 ReadOnly: true, 1982 }, 1983 }, 1984 } 1985 } 1986 1987 func getOverridesTestSidecar() *corev1.Container { 1988 return &corev1.Container{ 1989 Name: "stage-sidecar", 1990 Image: "stage/sidecar:tag", 1991 } 1992 } 1993 1994 func getOverridesTestContainerOptions() *corev1.Container { 1995 return &corev1.Container{ 1996 Resources: corev1.ResourceRequirements{ 1997 Limits: corev1.ResourceList{ 1998 "cpu": resource.MustParse("100m"), 1999 "memory": resource.MustParse("128Mi"), 2000 }, 2001 }, 2002 } 2003 } 2004 2005 func TestApplyNonStepOverridesToPipeline(t *testing.T) { 2006 tests := []struct { 2007 name string 2008 override *syntax.PipelineOverride 2009 expected *syntax.ParsedPipeline 2010 }{ 2011 { 2012 name: "volume-on-whole-pipeline", 2013 override: &syntax.PipelineOverride{ 2014 Volumes: []*corev1.Volume{getOverridesTestVolume()}, 2015 }, 2016 expected: sh.ParsedPipeline( 2017 sh.PipelineOptions(sh.PipelineVolume(getOverridesTestVolume())), 2018 sh.PipelineAgent("some-image"), 2019 sh.PipelineStage("A Working Stage", 2020 sh.StageStep( 2021 sh.StepCmd("echo"), 2022 sh.StepArg("hello"), sh.StepArg("world")), 2023 ), 2024 sh.PipelineStage("Another stage", 2025 sh.StageStep( 2026 sh.StepCmd("echo"), 2027 sh.StepArg("again"))), 2028 ), 2029 }, 2030 { 2031 name: "volume-on-single-stage", 2032 override: &syntax.PipelineOverride{ 2033 Stage: "Another stage", 2034 Volumes: []*corev1.Volume{getOverridesTestVolume()}, 2035 }, 2036 expected: sh.ParsedPipeline( 2037 sh.PipelineAgent("some-image"), 2038 sh.PipelineStage("A Working Stage", 2039 sh.StageStep( 2040 sh.StepCmd("echo"), 2041 sh.StepArg("hello"), sh.StepArg("world")), 2042 ), 2043 sh.PipelineStage("Another stage", 2044 sh.StageOptions(sh.StageVolume(getOverridesTestVolume())), 2045 sh.StageStep( 2046 sh.StepCmd("echo"), 2047 sh.StepArg("again"))), 2048 ), 2049 }, 2050 { 2051 name: "sidecar-on-whole-pipeline", 2052 override: &syntax.PipelineOverride{ 2053 Sidecars: []*corev1.Container{getOverridesTestSidecar()}, 2054 }, 2055 expected: sh.ParsedPipeline( 2056 sh.PipelineOptions(sh.PipelineSidecar(getOverridesTestSidecar())), 2057 sh.PipelineAgent("some-image"), 2058 sh.PipelineStage("A Working Stage", 2059 sh.StageStep( 2060 sh.StepCmd("echo"), 2061 sh.StepArg("hello"), sh.StepArg("world")), 2062 ), 2063 sh.PipelineStage("Another stage", 2064 sh.StageStep( 2065 sh.StepCmd("echo"), 2066 sh.StepArg("again"))), 2067 ), 2068 }, 2069 { 2070 name: "sidecar-on-single-stage", 2071 override: &syntax.PipelineOverride{ 2072 Stage: "Another stage", 2073 Sidecars: []*corev1.Container{getOverridesTestSidecar()}, 2074 }, 2075 expected: sh.ParsedPipeline( 2076 sh.PipelineAgent("some-image"), 2077 sh.PipelineStage("A Working Stage", 2078 sh.StageStep( 2079 sh.StepCmd("echo"), 2080 sh.StepArg("hello"), sh.StepArg("world")), 2081 ), 2082 sh.PipelineStage("Another stage", 2083 sh.StageOptions(sh.StageSidecar(getOverridesTestSidecar())), 2084 sh.StageStep( 2085 sh.StepCmd("echo"), 2086 sh.StepArg("again"))), 2087 ), 2088 }, 2089 { 2090 name: "containerOptions-on-whole-pipeline", 2091 override: &syntax.PipelineOverride{ 2092 ContainerOptions: getOverridesTestContainerOptions(), 2093 }, 2094 expected: sh.ParsedPipeline( 2095 sh.PipelineOptions( 2096 sh.PipelineContainerOptions( 2097 sh.ContainerResourceLimits("100m", "128Mi"), 2098 ), 2099 ), 2100 sh.PipelineAgent("some-image"), 2101 sh.PipelineStage("A Working Stage", 2102 sh.StageStep( 2103 sh.StepCmd("echo"), 2104 sh.StepArg("hello"), sh.StepArg("world")), 2105 ), 2106 sh.PipelineStage("Another stage", 2107 sh.StageStep( 2108 sh.StepCmd("echo"), 2109 sh.StepArg("again"))), 2110 ), 2111 }, 2112 { 2113 name: "containerOptions-on-single-stage", 2114 override: &syntax.PipelineOverride{ 2115 Stage: "Another stage", 2116 ContainerOptions: getOverridesTestContainerOptions(), 2117 }, 2118 expected: sh.ParsedPipeline( 2119 sh.PipelineAgent("some-image"), 2120 sh.PipelineStage("A Working Stage", 2121 sh.StageStep( 2122 sh.StepCmd("echo"), 2123 sh.StepArg("hello"), sh.StepArg("world")), 2124 ), 2125 sh.PipelineStage("Another stage", 2126 sh.StageOptions( 2127 sh.StageContainerOptions( 2128 sh.ContainerResourceLimits("100m", "128Mi"), 2129 ), 2130 ), 2131 sh.StageStep( 2132 sh.StepCmd("echo"), 2133 sh.StepArg("again"))), 2134 ), 2135 }, 2136 { 2137 name: "agent-on-whole-pipeline", 2138 override: &syntax.PipelineOverride{ 2139 Agent: &syntax.Agent{ 2140 Image: "some-other-image", 2141 }, 2142 }, 2143 expected: sh.ParsedPipeline( 2144 sh.PipelineAgent("some-other-image"), 2145 sh.PipelineStage("A Working Stage", 2146 sh.StageStep( 2147 sh.StepCmd("echo"), 2148 sh.StepArg("hello"), sh.StepArg("world")), 2149 ), 2150 sh.PipelineStage("Another stage", 2151 sh.StageStep( 2152 sh.StepCmd("echo"), 2153 sh.StepArg("again"))), 2154 ), 2155 }, 2156 { 2157 name: "agent-on-single-stage", 2158 override: &syntax.PipelineOverride{ 2159 Stage: "Another stage", 2160 Agent: &syntax.Agent{ 2161 Image: "some-other-image", 2162 }, 2163 }, 2164 expected: sh.ParsedPipeline( 2165 sh.PipelineAgent("some-image"), 2166 sh.PipelineStage("A Working Stage", 2167 sh.StageStep( 2168 sh.StepCmd("echo"), 2169 sh.StepArg("hello"), sh.StepArg("world")), 2170 ), 2171 sh.PipelineStage("Another stage", 2172 sh.StageAgent("some-other-image"), 2173 sh.StageStep( 2174 sh.StepCmd("echo"), 2175 sh.StepArg("again"))), 2176 ), 2177 }, 2178 } 2179 2180 for _, tt := range tests { 2181 t.Run(tt.name, func(t *testing.T) { 2182 newPipeline := syntax.ApplyNonStepOverridesToPipeline(getOverridesTestPipeline(), tt.override) 2183 2184 if d, _ := kmp.SafeDiff(tt.expected, newPipeline); d != "" { 2185 t.Errorf("Overridden pipeline did not match expected: %s", d) 2186 } 2187 }) 2188 } 2189 } 2190 2191 func TestRfc1035LabelMangling(t *testing.T) { 2192 tests := []struct { 2193 name string 2194 input string 2195 expected string 2196 }{ 2197 { 2198 name: "unmodified", 2199 input: "unmodified", 2200 expected: "unmodified-suffix", 2201 }, 2202 { 2203 name: "spaces", 2204 input: "A Simple Test.", 2205 expected: "a-simple-test-suffix", 2206 }, 2207 { 2208 name: "no leading digits", 2209 input: "0123456789no-leading-digits", 2210 expected: "no-leading-digits-suffix", 2211 }, 2212 { 2213 name: "no leading hyphens", 2214 input: "----no-leading-hyphens", 2215 expected: "no-leading-hyphens-suffix", 2216 }, 2217 { 2218 name: "no consecutive hyphens", 2219 input: "no--consecutive- hyphens", 2220 expected: "no-consecutive-hyphens-suffix", 2221 }, 2222 { 2223 name: "no trailing hyphens", 2224 input: "no-trailing-hyphens----", 2225 expected: "no-trailing-hyphens-suffix", 2226 }, 2227 { 2228 name: "no symbols", 2229 input: "&$^#@(*&$^-whoops", 2230 expected: "whoops-suffix", 2231 }, 2232 { 2233 name: "no unprintable characters", 2234 input: "a\n\t\x00b", 2235 expected: "ab-suffix", 2236 }, 2237 { 2238 name: "no unicode", 2239 input: "japan-日本", 2240 expected: "japan-suffix", 2241 }, 2242 { 2243 name: "no non-bmp characters", 2244 input: "happy 😃", 2245 expected: "happy-suffix", 2246 }, 2247 { 2248 name: "truncated to 63", 2249 input: "a0123456789012345678901234567890123456789012345678901234567890123456789", 2250 expected: "a0123456789012345678901234567890123456789012345678901234-suffix", 2251 }, 2252 { 2253 name: "truncated to 62", 2254 input: "a012345678901234567890123456789012345678901234567890123-567890123456789", 2255 expected: "a012345678901234567890123456789012345678901234567890123-suffix", 2256 }, 2257 } 2258 2259 for _, tt := range tests { 2260 t.Run(tt.name, func(t *testing.T) { 2261 mangled := syntax.MangleToRfc1035Label(tt.input, "suffix") 2262 if d := cmp.Diff(tt.expected, mangled); d != "" { 2263 t.Fatalf("Mangled output did not match expected output: %s", d) 2264 } 2265 }) 2266 } 2267 } 2268 2269 func TestParsedPipelineHelpers(t *testing.T) { 2270 input := sh.ParsedPipeline( 2271 sh.PipelineAgent("some-image"), 2272 sh.PipelineOptions( 2273 sh.PipelineOptionsRetry(5), 2274 sh.PipelineOptionsTimeout(30, syntax.TimeoutUnitSeconds), 2275 sh.PipelineSidecar(&corev1.Container{Name: "my-sidecar", Image: "banana:latest"}), 2276 sh.PipelineVolume(&corev1.Volume{Name: "banana"}), 2277 ), 2278 sh.PipelineEnvVar("ANIMAL", "MONKEY"), 2279 sh.PipelineEnvVar("FRUIT", "BANANA"), 2280 sh.PipelinePost(syntax.PostConditionSuccess, 2281 sh.PostAction("mail", map[string]string{ 2282 "to": "foo@bar.com", 2283 "subject": "Yay, it passed", 2284 })), 2285 sh.PipelinePost(syntax.PostConditionFailure, 2286 sh.PostAction("slack", map[string]string{ 2287 "whatever": "the", 2288 "slack": "config", 2289 "actually": "is. =)", 2290 })), 2291 sh.PipelineStage("A Working Stage", 2292 sh.StageOptions( 2293 sh.StageOptionsWorkspace(customWorkspace), 2294 sh.StageOptionsStash("some-name", "**/*"), 2295 sh.StageOptionsUnstash("some-name", ""), 2296 sh.StageOptionsTimeout(15, syntax.TimeoutUnitMinutes), 2297 sh.StageOptionsRetry(2), 2298 sh.StageSidecar(&corev1.Container{Name: "some-sidecar", Image: "some/sidecar:tag"}), 2299 sh.StageVolume(&corev1.Volume{Name: "apple"}), 2300 sh.StageVolume(&corev1.Volume{Name: "orange"}), 2301 ), 2302 sh.StageStep( 2303 sh.StepCmd("echo"), 2304 sh.StepArg("hello"), 2305 sh.StepArg("world"), 2306 ), 2307 ), 2308 sh.PipelineStage("Parent Stage", 2309 sh.StageParallel("First Nested Stage", 2310 sh.StageAgent("some-other-image"), 2311 sh.StageStep( 2312 sh.StepCmd("echo"), 2313 sh.StepArg("hello"), 2314 sh.StepArg("world"), 2315 sh.StepAgent("some-other-image"), 2316 ), 2317 sh.StageEnvVar("STAGE_VAR_ONE", "some value"), 2318 sh.StageEnvVar("STAGE_VAR_TWO", "some other value"), 2319 sh.StagePost(syntax.PostConditionAlways, 2320 sh.PostAction("junit", map[string]string{ 2321 "pattern": "target/surefire-reports/**/*.xml", 2322 }), 2323 ), 2324 ), 2325 sh.StageParallel("Nested In Parallel", 2326 sh.StageSequential("Another stage", 2327 sh.StageStep( 2328 sh.StepLoop("SOME_VAR", []string{"a", "b", "c"}, 2329 sh.LoopStep( 2330 sh.StepCmd("echo"), 2331 sh.StepArg("SOME_VAR is ${SOME_VAR}"), 2332 ), 2333 ), 2334 ), 2335 ), 2336 sh.StageSequential("Some other stage", 2337 sh.StageStep( 2338 sh.StepCmd("echo"), 2339 sh.StepArg("otherwise"), 2340 sh.StepDir(customWorkspace), 2341 ), 2342 sh.StageStep( 2343 sh.StepStep("some-step"), 2344 sh.StepOptions(map[string]string{"first": "arg", "second": "arg"}), 2345 ), 2346 ), 2347 ), 2348 ), 2349 ) 2350 2351 expected := &syntax.ParsedPipeline{ 2352 Agent: &syntax.Agent{ 2353 Image: "some-image", 2354 }, 2355 Options: &syntax.RootOptions{ 2356 Retry: 5, 2357 Timeout: &syntax.Timeout{ 2358 Time: 30, 2359 Unit: syntax.TimeoutUnitSeconds, 2360 }, 2361 Sidecars: []*corev1.Container{{ 2362 Name: "my-sidecar", 2363 Image: "banana:latest", 2364 }}, 2365 Volumes: []*corev1.Volume{{ 2366 Name: "banana", 2367 }}, 2368 }, 2369 Env: []corev1.EnvVar{ 2370 { 2371 Name: "ANIMAL", 2372 Value: "MONKEY", 2373 }, 2374 { 2375 Name: "FRUIT", 2376 Value: "BANANA", 2377 }, 2378 }, 2379 Post: []syntax.Post{ 2380 { 2381 Condition: "success", 2382 Actions: []syntax.PostAction{{ 2383 Name: "mail", 2384 Options: map[string]string{ 2385 "to": "foo@bar.com", 2386 "subject": "Yay, it passed", 2387 }, 2388 }}, 2389 }, 2390 { 2391 Condition: "failure", 2392 Actions: []syntax.PostAction{{ 2393 Name: "slack", 2394 Options: map[string]string{ 2395 "whatever": "the", 2396 "slack": "config", 2397 "actually": "is. =)", 2398 }, 2399 }}, 2400 }, 2401 }, 2402 Stages: []syntax.Stage{ 2403 { 2404 Name: "A Working Stage", 2405 Options: &syntax.StageOptions{ 2406 Workspace: &customWorkspace, 2407 Stash: &syntax.Stash{ 2408 Name: "some-name", 2409 Files: "**/*", 2410 }, 2411 Unstash: &syntax.Unstash{ 2412 Name: "some-name", 2413 }, 2414 RootOptions: &syntax.RootOptions{ 2415 Timeout: &syntax.Timeout{ 2416 Time: 15, 2417 Unit: syntax.TimeoutUnitMinutes, 2418 }, 2419 Retry: 2, 2420 Sidecars: []*corev1.Container{{ 2421 Name: "some-sidecar", 2422 Image: "some/sidecar:tag", 2423 }}, 2424 Volumes: []*corev1.Volume{{ 2425 Name: "apple", 2426 }, { 2427 Name: "orange", 2428 }}, 2429 }, 2430 }, 2431 Steps: []syntax.Step{{ 2432 Command: "echo", 2433 Arguments: []string{"hello", "world"}, 2434 }}, 2435 }, 2436 { 2437 Name: "Parent Stage", 2438 Parallel: []syntax.Stage{ 2439 { 2440 Name: "First Nested Stage", 2441 Agent: &syntax.Agent{ 2442 Image: "some-other-image", 2443 }, 2444 Steps: []syntax.Step{{ 2445 Command: "echo", 2446 Arguments: []string{"hello", "world"}, 2447 Agent: &syntax.Agent{ 2448 Image: "some-other-image", 2449 }, 2450 }}, 2451 Env: []corev1.EnvVar{ 2452 { 2453 Name: "STAGE_VAR_ONE", 2454 Value: "some value", 2455 }, 2456 { 2457 Name: "STAGE_VAR_TWO", 2458 Value: "some other value", 2459 }, 2460 }, 2461 Post: []syntax.Post{{ 2462 Condition: "always", 2463 Actions: []syntax.PostAction{{ 2464 Name: "junit", 2465 Options: map[string]string{ 2466 "pattern": "target/surefire-reports/**/*.xml", 2467 }, 2468 }}, 2469 }}, 2470 }, 2471 { 2472 Name: "Nested In Parallel", 2473 Stages: []syntax.Stage{ 2474 { 2475 Name: "Another stage", 2476 Steps: []syntax.Step{{ 2477 Loop: &syntax.Loop{ 2478 Variable: "SOME_VAR", 2479 Values: []string{"a", "b", "c"}, 2480 Steps: []syntax.Step{{ 2481 Command: "echo", 2482 Arguments: []string{"SOME_VAR is ${SOME_VAR}"}, 2483 }}, 2484 }, 2485 }}, 2486 }, 2487 { 2488 Name: "Some other stage", 2489 Steps: []syntax.Step{ 2490 { 2491 Command: "echo", 2492 Arguments: []string{"otherwise"}, 2493 Dir: customWorkspace, 2494 }, 2495 { 2496 Step: "some-step", 2497 Options: map[string]string{"first": "arg", "second": "arg"}, 2498 }, 2499 }, 2500 }, 2501 }, 2502 }, 2503 }, 2504 }, 2505 }, 2506 } 2507 2508 if d := cmp.Diff(expected, input); d != "" { 2509 t.Fatalf("ParsedPipeline diff -want, +got: %v", d) 2510 } 2511 }