github.com/pf-qiu/concourse/v6@v6.7.3-0.20201207032516-1f455d73275f/atc/engine/builder.go (about) 1 package engine 2 3 import ( 4 "encoding/json" 5 6 "errors" 7 "strconv" 8 "strings" 9 10 "github.com/pf-qiu/concourse/v6/atc" 11 "github.com/pf-qiu/concourse/v6/atc/db" 12 "github.com/pf-qiu/concourse/v6/atc/exec" 13 "github.com/pf-qiu/concourse/v6/atc/policy" 14 ) 15 16 const supportedSchema = "exec.v2" 17 18 //go:generate counterfeiter . CoreStepFactory 19 20 type CoreStepFactory interface { 21 GetStep(atc.Plan, exec.StepMetadata, db.ContainerMetadata, DelegateFactory) exec.Step 22 PutStep(atc.Plan, exec.StepMetadata, db.ContainerMetadata, DelegateFactory) exec.Step 23 TaskStep(atc.Plan, exec.StepMetadata, db.ContainerMetadata, DelegateFactory) exec.Step 24 CheckStep(atc.Plan, exec.StepMetadata, db.ContainerMetadata, DelegateFactory) exec.Step 25 SetPipelineStep(atc.Plan, exec.StepMetadata, DelegateFactory) exec.Step 26 LoadVarStep(atc.Plan, exec.StepMetadata, DelegateFactory) exec.Step 27 ArtifactInputStep(atc.Plan, db.Build) exec.Step 28 ArtifactOutputStep(atc.Plan, db.Build) exec.Step 29 } 30 31 //go:generate counterfeiter . StepperFactory 32 33 type StepperFactory interface { 34 StepperForBuild(db.Build) (exec.Stepper, error) 35 } 36 37 func NewStepperFactory( 38 coreFactory CoreStepFactory, 39 externalURL string, 40 rateLimiter RateLimiter, 41 policyChecker policy.Checker, 42 ) StepperFactory { 43 return &stepperFactory{ 44 coreFactory: coreFactory, 45 externalURL: externalURL, 46 rateLimiter: rateLimiter, 47 policyChecker: policyChecker, 48 } 49 } 50 51 type stepperFactory struct { 52 coreFactory CoreStepFactory 53 delegateFactory DelegateFactory 54 externalURL string 55 rateLimiter RateLimiter 56 policyChecker policy.Checker 57 } 58 59 func (factory *stepperFactory) StepperForBuild(build db.Build) (exec.Stepper, error) { 60 if build.Schema() != supportedSchema { 61 return nil, errors.New("schema not supported") 62 } 63 64 return func(plan atc.Plan) exec.Step { 65 return factory.buildStep(build, plan) 66 }, nil 67 } 68 69 func (factory *stepperFactory) buildStep(build db.Build, plan atc.Plan) exec.Step { 70 if plan.Aggregate != nil { 71 return factory.buildAggregateStep(build, plan) 72 } 73 74 if plan.InParallel != nil { 75 return factory.buildParallelStep(build, plan) 76 } 77 78 if plan.Across != nil { 79 return factory.buildAcrossStep(build, plan) 80 } 81 82 if plan.Do != nil { 83 return factory.buildDoStep(build, plan) 84 } 85 86 if plan.Timeout != nil { 87 return factory.buildTimeoutStep(build, plan) 88 } 89 90 if plan.Try != nil { 91 return factory.buildTryStep(build, plan) 92 } 93 94 if plan.OnAbort != nil { 95 return factory.buildOnAbortStep(build, plan) 96 } 97 98 if plan.OnError != nil { 99 return factory.buildOnErrorStep(build, plan) 100 } 101 102 if plan.OnSuccess != nil { 103 return factory.buildOnSuccessStep(build, plan) 104 } 105 106 if plan.OnFailure != nil { 107 return factory.buildOnFailureStep(build, plan) 108 } 109 110 if plan.Ensure != nil { 111 return factory.buildEnsureStep(build, plan) 112 } 113 114 if plan.Task != nil { 115 return factory.buildTaskStep(build, plan) 116 } 117 118 if plan.SetPipeline != nil { 119 return factory.buildSetPipelineStep(build, plan) 120 } 121 122 if plan.LoadVar != nil { 123 return factory.buildLoadVarStep(build, plan) 124 } 125 126 if plan.Check != nil { 127 return factory.buildCheckStep(build, plan) 128 } 129 130 if plan.Get != nil { 131 return factory.buildGetStep(build, plan) 132 } 133 134 if plan.Put != nil { 135 return factory.buildPutStep(build, plan) 136 } 137 138 if plan.Retry != nil { 139 return factory.buildRetryStep(build, plan) 140 } 141 142 if plan.ArtifactInput != nil { 143 return factory.buildArtifactInputStep(build, plan) 144 } 145 146 if plan.ArtifactOutput != nil { 147 return factory.buildArtifactOutputStep(build, plan) 148 } 149 150 return exec.IdentityStep{} 151 } 152 153 func (factory *stepperFactory) buildAggregateStep(build db.Build, plan atc.Plan) exec.Step { 154 155 agg := exec.AggregateStep{} 156 157 for _, innerPlan := range *plan.Aggregate { 158 innerPlan.Attempts = plan.Attempts 159 step := factory.buildStep(build, innerPlan) 160 agg = append(agg, step) 161 } 162 163 return agg 164 } 165 166 func (factory *stepperFactory) buildParallelStep(build db.Build, plan atc.Plan) exec.Step { 167 168 var steps []exec.Step 169 170 for _, innerPlan := range plan.InParallel.Steps { 171 innerPlan.Attempts = plan.Attempts 172 step := factory.buildStep(build, innerPlan) 173 steps = append(steps, step) 174 } 175 176 return exec.InParallel(steps, plan.InParallel.Limit, plan.InParallel.FailFast) 177 } 178 179 func (factory *stepperFactory) buildAcrossStep(build db.Build, plan atc.Plan) exec.Step { 180 stepMetadata := factory.stepMetadata( 181 build, 182 factory.externalURL, 183 ) 184 185 steps := make([]exec.ScopedStep, len(plan.Across.Steps)) 186 for i, s := range plan.Across.Steps { 187 steps[i] = exec.ScopedStep{ 188 Step: factory.buildStep(build, s.Step), 189 Values: s.Values, 190 } 191 } 192 193 return exec.Across( 194 plan.Across.Vars, 195 steps, 196 plan.Across.FailFast, 197 buildDelegateFactory(build, plan, factory.rateLimiter, factory.policyChecker), 198 stepMetadata, 199 ) 200 } 201 202 func (factory *stepperFactory) buildDoStep(build db.Build, plan atc.Plan) exec.Step { 203 var step exec.Step = exec.IdentityStep{} 204 205 for i := len(*plan.Do) - 1; i >= 0; i-- { 206 innerPlan := (*plan.Do)[i] 207 innerPlan.Attempts = plan.Attempts 208 previous := factory.buildStep(build, innerPlan) 209 step = exec.OnSuccess(previous, step) 210 } 211 212 return step 213 } 214 215 func (factory *stepperFactory) buildTimeoutStep(build db.Build, plan atc.Plan) exec.Step { 216 innerPlan := plan.Timeout.Step 217 innerPlan.Attempts = plan.Attempts 218 step := factory.buildStep(build, innerPlan) 219 return exec.Timeout(step, plan.Timeout.Duration) 220 } 221 222 func (factory *stepperFactory) buildTryStep(build db.Build, plan atc.Plan) exec.Step { 223 innerPlan := plan.Try.Step 224 innerPlan.Attempts = plan.Attempts 225 step := factory.buildStep(build, innerPlan) 226 return exec.Try(step) 227 } 228 229 func (factory *stepperFactory) buildOnAbortStep(build db.Build, plan atc.Plan) exec.Step { 230 plan.OnAbort.Step.Attempts = plan.Attempts 231 step := factory.buildStep(build, plan.OnAbort.Step) 232 plan.OnAbort.Next.Attempts = plan.Attempts 233 next := factory.buildStep(build, plan.OnAbort.Next) 234 return exec.OnAbort(step, next) 235 } 236 237 func (factory *stepperFactory) buildOnErrorStep(build db.Build, plan atc.Plan) exec.Step { 238 plan.OnError.Step.Attempts = plan.Attempts 239 step := factory.buildStep(build, plan.OnError.Step) 240 plan.OnError.Next.Attempts = plan.Attempts 241 next := factory.buildStep(build, plan.OnError.Next) 242 return exec.OnError(step, next) 243 } 244 245 func (factory *stepperFactory) buildOnSuccessStep(build db.Build, plan atc.Plan) exec.Step { 246 plan.OnSuccess.Step.Attempts = plan.Attempts 247 step := factory.buildStep(build, plan.OnSuccess.Step) 248 plan.OnSuccess.Next.Attempts = plan.Attempts 249 next := factory.buildStep(build, plan.OnSuccess.Next) 250 return exec.OnSuccess(step, next) 251 } 252 253 func (factory *stepperFactory) buildOnFailureStep(build db.Build, plan atc.Plan) exec.Step { 254 plan.OnFailure.Step.Attempts = plan.Attempts 255 step := factory.buildStep(build, plan.OnFailure.Step) 256 plan.OnFailure.Next.Attempts = plan.Attempts 257 next := factory.buildStep(build, plan.OnFailure.Next) 258 return exec.OnFailure(step, next) 259 } 260 261 func (factory *stepperFactory) buildEnsureStep(build db.Build, plan atc.Plan) exec.Step { 262 plan.Ensure.Step.Attempts = plan.Attempts 263 step := factory.buildStep(build, plan.Ensure.Step) 264 plan.Ensure.Next.Attempts = plan.Attempts 265 next := factory.buildStep(build, plan.Ensure.Next) 266 return exec.Ensure(step, next) 267 } 268 269 func (factory *stepperFactory) buildRetryStep(build db.Build, plan atc.Plan) exec.Step { 270 steps := []exec.Step{} 271 272 for index, innerPlan := range *plan.Retry { 273 innerPlan.Attempts = append(plan.Attempts, index+1) 274 275 step := factory.buildStep(build, innerPlan) 276 steps = append(steps, step) 277 } 278 279 return exec.Retry(steps...) 280 } 281 282 func (factory *stepperFactory) buildGetStep(build db.Build, plan atc.Plan) exec.Step { 283 284 containerMetadata := factory.containerMetadata( 285 build, 286 db.ContainerTypeGet, 287 plan.Get.Name, 288 plan.Attempts, 289 ) 290 291 stepMetadata := factory.stepMetadata( 292 build, 293 factory.externalURL, 294 ) 295 296 return factory.coreFactory.GetStep( 297 plan, 298 stepMetadata, 299 containerMetadata, 300 buildDelegateFactory(build, plan, factory.rateLimiter, factory.policyChecker), 301 ) 302 } 303 304 func (factory *stepperFactory) buildPutStep(build db.Build, plan atc.Plan) exec.Step { 305 306 containerMetadata := factory.containerMetadata( 307 build, 308 db.ContainerTypePut, 309 plan.Put.Name, 310 plan.Attempts, 311 ) 312 313 stepMetadata := factory.stepMetadata( 314 build, 315 factory.externalURL, 316 ) 317 318 return factory.coreFactory.PutStep( 319 plan, 320 stepMetadata, 321 containerMetadata, 322 buildDelegateFactory(build, plan, factory.rateLimiter, factory.policyChecker), 323 ) 324 } 325 326 func (factory *stepperFactory) buildCheckStep(build db.Build, plan atc.Plan) exec.Step { 327 containerMetadata := factory.containerMetadata( 328 build, 329 db.ContainerTypeCheck, 330 plan.Check.Name, 331 plan.Attempts, 332 ) 333 334 stepMetadata := factory.stepMetadata( 335 build, 336 factory.externalURL, 337 ) 338 339 return factory.coreFactory.CheckStep( 340 plan, 341 stepMetadata, 342 containerMetadata, 343 buildDelegateFactory(build, plan, factory.rateLimiter, factory.policyChecker), 344 ) 345 } 346 347 func (factory *stepperFactory) buildTaskStep(build db.Build, plan atc.Plan) exec.Step { 348 349 containerMetadata := factory.containerMetadata( 350 build, 351 db.ContainerTypeTask, 352 plan.Task.Name, 353 plan.Attempts, 354 ) 355 356 stepMetadata := factory.stepMetadata( 357 build, 358 factory.externalURL, 359 ) 360 361 return factory.coreFactory.TaskStep( 362 plan, 363 stepMetadata, 364 containerMetadata, 365 buildDelegateFactory(build, plan, factory.rateLimiter, factory.policyChecker), 366 ) 367 } 368 369 func (factory *stepperFactory) buildSetPipelineStep(build db.Build, plan atc.Plan) exec.Step { 370 371 stepMetadata := factory.stepMetadata( 372 build, 373 factory.externalURL, 374 ) 375 376 return factory.coreFactory.SetPipelineStep( 377 plan, 378 stepMetadata, 379 buildDelegateFactory(build, plan, factory.rateLimiter, factory.policyChecker), 380 ) 381 } 382 383 func (factory *stepperFactory) buildLoadVarStep(build db.Build, plan atc.Plan) exec.Step { 384 385 stepMetadata := factory.stepMetadata( 386 build, 387 factory.externalURL, 388 ) 389 390 return factory.coreFactory.LoadVarStep( 391 plan, 392 stepMetadata, 393 buildDelegateFactory(build, plan, factory.rateLimiter, factory.policyChecker), 394 ) 395 } 396 397 func (factory *stepperFactory) buildArtifactInputStep(build db.Build, plan atc.Plan) exec.Step { 398 return factory.coreFactory.ArtifactInputStep( 399 plan, 400 build, 401 ) 402 } 403 404 func (factory *stepperFactory) buildArtifactOutputStep(build db.Build, plan atc.Plan) exec.Step { 405 return factory.coreFactory.ArtifactOutputStep( 406 plan, 407 build, 408 ) 409 } 410 411 func (factory *stepperFactory) containerMetadata( 412 build db.Build, 413 containerType db.ContainerType, 414 stepName string, 415 attempts []int, 416 ) db.ContainerMetadata { 417 attemptStrs := []string{} 418 for _, a := range attempts { 419 attemptStrs = append(attemptStrs, strconv.Itoa(a)) 420 } 421 422 var pipelineInstanceVars string 423 if build.PipelineInstanceVars() != nil { 424 instanceVars, _ := json.Marshal(build.PipelineInstanceVars()) 425 pipelineInstanceVars = string(instanceVars) 426 } 427 428 return db.ContainerMetadata{ 429 Type: containerType, 430 431 PipelineID: build.PipelineID(), 432 JobID: build.JobID(), 433 BuildID: build.ID(), 434 435 PipelineName: build.PipelineName(), 436 PipelineInstanceVars: pipelineInstanceVars, 437 JobName: build.JobName(), 438 BuildName: build.Name(), 439 440 StepName: stepName, 441 Attempt: strings.Join(attemptStrs, "."), 442 } 443 } 444 445 func (factory *stepperFactory) stepMetadata( 446 build db.Build, 447 externalURL string, 448 ) exec.StepMetadata { 449 return exec.StepMetadata{ 450 BuildID: build.ID(), 451 BuildName: build.Name(), 452 TeamID: build.TeamID(), 453 TeamName: build.TeamName(), 454 JobID: build.JobID(), 455 JobName: build.JobName(), 456 PipelineID: build.PipelineID(), 457 PipelineName: build.PipelineName(), 458 PipelineInstanceVars: build.PipelineInstanceVars(), 459 ExternalURL: externalURL, 460 } 461 }