github.com/drone/go-convert@v0.0.0-20240307072510-6bd371c65e61/convert/harness/downgrader/downgrade.go (about) 1 // Copyright 2022 Harness, Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package downgrader 16 17 import ( 18 "bytes" 19 "fmt" 20 "io/ioutil" 21 "sort" 22 "strings" 23 "time" 24 25 "github.com/drone/go-convert/internal/slug" 26 "github.com/drone/go-convert/internal/store" 27 28 harness "github.com/drone/go-convert/convert/harness/downgrader/yaml" 29 v0 "github.com/drone/go-convert/convert/harness/yaml" 30 v1 "github.com/drone/spec/dist/go" 31 "github.com/ghodss/yaml" 32 ) 33 34 // Downgrader downgrades pipelines from the v0 harness 35 // configuration format to the v1 configuration format. 36 type Downgrader struct { 37 codebaseName string 38 codebaseConn string 39 dockerhubConn string 40 kubeConnector string 41 kubeNamespace string 42 kubeEnabled bool 43 pipelineId string 44 pipelineName string 45 pipelineOrg string 46 pipelineProj string 47 identifiers *store.Identifiers 48 } 49 50 const MaxDepth = 100 51 52 var eventMap = map[string]map[string]string{ 53 "pull_request": { 54 "jexl": "<+trigger.event>", 55 "event": "PR", 56 "operator": "==", 57 "inverseOperator": "!=", 58 }, 59 "push": { 60 "jexl": "<+trigger.event>", 61 "event": "PUSH", 62 "operator": "==", 63 "inverseOperator": "!=", 64 }, 65 "tag": { 66 "jexl": "<+trigger.payload.ref>", 67 "event": "refs/tags/", 68 "operator": "=^", 69 "inverseOperator": "!^", 70 }, 71 } 72 73 // New creates a new Downgrader that downgrades pipelines 74 // from the v0 harness configuration format to the v1 75 // configuration format. 76 func New(options ...Option) *Downgrader { 77 d := new(Downgrader) 78 79 // create the unique identifier store. this store 80 // is used for registering unique identifiers to 81 // prevent duplicate names, unique index violations. 82 d.identifiers = store.New() 83 84 // loop through and apply the options. 85 for _, option := range options { 86 option(d) 87 } 88 89 // set the default pipeline name. 90 if d.pipelineName == "" { 91 d.pipelineName = "default" 92 } 93 94 // set the default pipeline id. 95 if d.pipelineId == "" { 96 d.pipelineId = slug.Create(d.pipelineName) 97 } 98 99 // set the default pipeline org. 100 if d.pipelineOrg == "" { 101 d.pipelineOrg = "default" 102 } 103 104 // set the default pipeline org. 105 if d.pipelineProj == "" { 106 d.pipelineProj = "default" 107 } 108 109 // set the default kubernetes namespace. 110 if d.kubeNamespace == "" { 111 d.kubeNamespace = "default" 112 } 113 114 // set the runtime to kubernetes if the kubernetes 115 // connector is configured. 116 if d.kubeConnector != "" { 117 d.kubeEnabled = true 118 } 119 120 return d 121 } 122 123 // Downgrade downgrades a v1 pipeline. 124 func (d *Downgrader) Downgrade(b []byte) ([]byte, error) { 125 src, err := harness.ParseBytes(b) 126 if err != nil { 127 return nil, err 128 } 129 return d.DowngradeFrom(src) 130 } 131 132 // DowngradeString downgrades a v1 pipeline. 133 func (d *Downgrader) DowngradeString(s string) ([]byte, error) { 134 return d.Downgrade([]byte(s)) 135 } 136 137 // DowngradeFile downgrades a v1 pipeline. 138 func (d *Downgrader) DowngradeFile(path string) ([]byte, error) { 139 out, err := ioutil.ReadFile(path) 140 if err != nil { 141 return nil, err 142 } 143 return d.Downgrade(out) 144 } 145 146 // DowngradeFrom downgrades a v1 pipeline object. 147 func (d *Downgrader) DowngradeFrom(src []*v1.Config) ([]byte, error) { 148 return d.downgrade(src) 149 } 150 151 // downgrade downgrades a v1 pipeline. 152 func (d *Downgrader) downgrade(src []*v1.Config) ([]byte, error) { 153 var buf bytes.Buffer 154 for i, p := range src { 155 config := new(v0.Config) 156 157 // TODO pipeline.name removed from spec 158 159 // use name from yaml if set and name not provided 160 // if p.Name != "" && d.pipelineId == "default" { 161 // config.Pipeline.ID = slug.Create(p.Name) 162 // config.Pipeline.Name = p.Name 163 // } else { 164 config.Pipeline.ID = d.pipelineId 165 config.Pipeline.Name = d.pipelineName 166 // } 167 168 config.Pipeline.Org = d.pipelineOrg 169 config.Pipeline.Project = d.pipelineProj 170 config.Pipeline.Props.CI.Codebase = v0.Codebase{ 171 Name: d.codebaseName, 172 Conn: d.codebaseConn, 173 Build: "<+input>", 174 } 175 // FIXME: this is subject to a nil pointer 176 if p.Spec.(*v1.Pipeline).Options != nil { 177 config.Pipeline.Variables = convertVariables(p.Spec.(*v1.Pipeline).Options.Envs) 178 } 179 180 // convert stages 181 // FIXME: this is subject to a nil pointer 182 for _, stage := range p.Spec.(*v1.Pipeline).Stages { 183 // skip nil stages. this is un-necessary, we have 184 // this logic in place just to be safe. 185 if stage == nil { 186 continue 187 } 188 189 // skip stages that are not CI stages, for now 190 if _, ok := stage.Spec.(*v1.StageCI); !ok { 191 continue 192 } 193 194 // convert the stage and add to the list 195 config.Pipeline.Stages = append(config.Pipeline.Stages, &v0.Stages{ 196 Stage: d.convertStage(stage), 197 }) 198 } 199 out, err := yaml.Marshal(config) 200 if err != nil { 201 return nil, err 202 } 203 if i > 0 { 204 buf.WriteString("\n---\n") 205 } 206 buf.Write(out) 207 } 208 return buf.Bytes(), nil 209 } 210 211 // helper function converts a drone pipeline stage to a 212 // harness stage. 213 // 214 // TODO env variables to vars (stage-level) 215 // TODO delegate selectors 216 // TODO tags 217 // TODO when 218 // TODO failure strategy 219 // TODO volumes 220 // TODO if no stage clone, use global clone, if exists 221 func (d *Downgrader) convertStage(stage *v1.Stage) *v0.Stage { 222 // extract the spec from the v1 stage 223 spec := stage.Spec.(*v1.StageCI) 224 225 var steps []*v0.Steps 226 // convert each drone step to a harness step. 227 for _, v := range spec.Steps { 228 // the v0 yaml does not have the concept of 229 // a group step, so we append all steps in 230 // the group directly to the stage to emulate 231 // this behavior. 232 if _, ok := v.Spec.(*v1.StepGroup); ok { 233 steps = append(steps, d.convertStepGroup(v, 10)...) 234 235 } else { 236 // else convert the step and append to 237 // the stage. 238 if step := d.convertStep(v); step != nil { 239 steps = append(steps, step) 240 } 241 } 242 } 243 244 // enable clone by default 245 enableClone := true 246 if spec.Clone != nil && spec.Clone.Disabled == true { 247 enableClone = false 248 } 249 250 // convert volumes 251 if len(spec.Volumes) > 0 { 252 // TODO 253 } 254 255 // 256 // START TODO - refactor this into a helper function 257 // 258 259 var infra *v0.Infrastructure 260 var runtime *v0.Runtime 261 262 if spec.Runtime != nil { 263 // convert kubernetes 264 if kube, ok := spec.Runtime.Spec.(*v1.RuntimeKube); ok { 265 infra = &v0.Infrastructure{ 266 Type: v0.InfraTypeKubernetesDirect, 267 Spec: &v0.InfraSpec{ 268 Namespace: kube.Namespace, 269 Conn: kube.Connector, 270 }, 271 } 272 if infra.Spec.Namespace == "" { 273 kube.Namespace = d.kubeNamespace 274 } 275 if infra.Spec.Conn == "" { 276 kube.Connector = d.kubeConnector 277 } 278 } 279 280 // convert cloud 281 if _, ok := spec.Runtime.Spec.(*v1.RuntimeCloud); ok { 282 runtime = &v0.Runtime{ 283 Type: "Cloud", 284 Spec: struct{}{}, 285 } 286 } 287 } 288 289 // if neither cloud nor kubernetes are specified 290 // we default to cloud. 291 if runtime == nil && infra == nil { 292 runtime = &v0.Runtime{ 293 Type: "Cloud", 294 Spec: struct{}{}, 295 } 296 } 297 298 // if the user explicitly provides a kubernetes connector, 299 // we should override whatever was in the source yaml and 300 // force kubernetes. 301 if d.kubeConnector != "" { 302 runtime = nil 303 infra = &v0.Infrastructure{ 304 Type: v0.InfraTypeKubernetesDirect, 305 Spec: &v0.InfraSpec{ 306 Namespace: d.kubeNamespace, 307 Conn: d.kubeConnector, 308 }, 309 } 310 } 311 312 // 313 // END TODO 314 // 315 316 // convert the stage to a harness stage. 317 return &v0.Stage{ 318 ID: d.identifiers.Generate( 319 slug.Create(stage.Id), 320 slug.Create(stage.Name), 321 slug.Create(stage.Type), 322 ), 323 Name: convertName(stage.Name), 324 Type: v0.StageTypeCI, 325 Spec: v0.StageCI{ 326 Cache: convertCache(spec.Cache), 327 Clone: enableClone, 328 Infrastructure: infra, 329 Platform: convertPlatform(spec.Platform, runtime), 330 Runtime: runtime, 331 Execution: v0.Execution{ 332 Steps: steps, 333 }, 334 }, 335 When: convertStageWhen(stage.When, ""), 336 Strategy: convertStrategy(stage.Strategy), 337 Vars: convertVariables(spec.Envs), 338 } 339 } 340 341 // convertStrategy converts the v1.Strategy to the v0.Strategy 342 func convertStrategy(v1Strategy *v1.Strategy) *v0.Strategy { 343 if v1Strategy == nil { 344 return nil 345 } 346 v0Strategy := v0.Strategy{} 347 switch v1Strategy.Type { 348 case "matrix": 349 v0Matrix := convertMatrix(v1Strategy.Spec.(*v1.Matrix)) 350 v0Strategy.Matrix = v0Matrix 351 default: 352 } 353 354 return &v0Strategy 355 } 356 357 // convertMatrix converts the v1.Matrix to the v0.Matrix 358 func convertMatrix(v1Matrix *v1.Matrix) map[string]interface{} { 359 matrix := make(map[string]interface{}) 360 361 // Convert axis 362 for key, values := range v1Matrix.Axis { 363 matrix[key] = values 364 } 365 366 // Convert exclusions 367 var exclusions []v0.Exclusion 368 for _, v1Exclusion := range v1Matrix.Exclude { 369 exclusion := make(v0.Exclusion) 370 for key, values := range v1Exclusion { 371 exclusion[key] = values 372 } 373 exclusions = append(exclusions, exclusion) 374 } 375 matrix["exclude"] = exclusions 376 377 // Convert maxConcurrency 378 if v1Matrix.Concurrency != 0 { 379 matrix["maxConcurrency"] = v1Matrix.Concurrency 380 } 381 382 return matrix 383 } 384 385 // helper function converts a drone pipeline step to a 386 // harness step. 387 // 388 // TODO unique identifier 389 // TODO failure strategy 390 // TODO matrix strategy 391 // TODO when 392 func (d *Downgrader) convertStep(src *v1.Step) *v0.Steps { 393 switch src.Spec.(type) { 394 case *v1.StepExec: 395 return &v0.Steps{Step: d.convertStepRun(src)} 396 case *v1.StepPlugin: 397 return &v0.Steps{Step: d.convertStepPlugin(src)} 398 case *v1.StepAction: 399 return &v0.Steps{Step: d.convertStepAction(src)} 400 case *v1.StepBitrise: 401 return &v0.Steps{Step: d.convertStepBitrise(src)} 402 case *v1.StepParallel: 403 return &v0.Steps{Parallel: d.convertStepParallel(src)} 404 case *v1.StepBackground: 405 return &v0.Steps{Step: d.convertStepBackground(src)} 406 default: 407 return nil // should not happen 408 } 409 } 410 411 // helper function to convert a Group step from the v1 412 // structure to a list of steps. The v0 yaml does not have 413 // an equivalent to the group step. 414 func (d *Downgrader) convertStepGroup(src *v1.Step, depth int) []*v0.Steps { 415 if depth > MaxDepth { 416 return nil // Reached maximum depth. Stop recursion to prevent stack overflow 417 } 418 spec_ := src.Spec.(*v1.StepGroup) 419 420 var steps []*v0.Steps 421 for _, step := range spec_.Steps { 422 // If this step is a step group, recursively convert it 423 if _, ok := step.Spec.(*v1.StepGroup); ok { 424 steps = append(steps, d.convertStepGroup(step, depth+1)...) 425 } else { 426 // Else, convert the step 427 if dst := d.convertStep(step); dst != nil { 428 steps = append(steps, &v0.Steps{Step: dst.Step}) 429 } 430 } 431 } 432 return steps 433 } 434 435 // helper function to convert a Parallel step from the v1 436 // structure to the v0 harness structure. 437 func (d *Downgrader) convertStepParallel(src *v1.Step) []*v0.Steps { 438 spec_ := src.Spec.(*v1.StepParallel) 439 440 var steps []*v0.Steps 441 for _, step := range spec_.Steps { 442 if dst := d.convertStep(step); dst != nil { 443 steps = append(steps, &v0.Steps{Step: dst.Step}) 444 } 445 } 446 return steps 447 } 448 449 // helper function to convert a Run step from the v1 450 // structure to the v0 harness structure. 451 // 452 // TODO convert outputs 453 // TODO convert resources 454 // TODO convert reports 455 func (d *Downgrader) convertStepRun(src *v1.Step) *v0.Step { 456 spec_ := src.Spec.(*v1.StepExec) 457 var id = d.identifiers.Generate( 458 slug.Create(src.Id), 459 slug.Create(src.Name), 460 slug.Create(src.Type)) 461 if src.Name == "" { 462 src.Name = id 463 } 464 return &v0.Step{ 465 ID: id, 466 Name: convertName(src.Name), 467 Type: v0.StepTypeRun, 468 Timeout: convertTimeout(src.Timeout), 469 Spec: &v0.StepRun{ 470 Env: spec_.Envs, 471 Command: spec_.Run, 472 ConnRef: d.dockerhubConn, 473 Image: spec_.Image, 474 ImagePullPolicy: convertImagePull(spec_.Pull), 475 Privileged: spec_.Privileged, 476 RunAsUser: spec_.User, 477 Reports: convertReports(spec_.Reports), 478 Shell: strings.Title(spec_.Shell), 479 }, 480 When: convertStepWhen(src.When, id), 481 Strategy: convertStrategy(src.Strategy), 482 } 483 } 484 485 // helper function to convert reports from the v1 to v0 486 func convertReports(reports []*v1.Report) *v0.Report { 487 if reports == nil || len(reports) == 0 { 488 return nil 489 } 490 491 // Initialize an empty slice to store all paths 492 allPaths := []string{} 493 494 // Loop over reports and collect all paths 495 for _, report := range reports { 496 allPaths = append(allPaths, report.Path...) 497 } 498 499 reportJunit := v0.ReportJunit{ 500 Paths: allPaths, 501 } 502 503 v0Report := v0.Report{ 504 // Assuming all reports have the same type 505 Type: "JUnit", 506 Spec: &reportJunit, 507 } 508 509 return &v0Report 510 } 511 512 // helper function to convert a Bitrise step from the v1 513 // structure to the v0 harness structure. 514 // 515 // TODO convert resources 516 func (d *Downgrader) convertStepBackground(src *v1.Step) *v0.Step { 517 spec_ := src.Spec.(*v1.StepBackground) 518 var id = d.identifiers.Generate( 519 slug.Create(src.Id), 520 slug.Create(src.Name), 521 slug.Create(src.Type)) 522 if src.Name == "" { 523 src.Name = id 524 } 525 // convert the entrypoint string to a slice. 526 var entypoint []string 527 if spec_.Entrypoint != "" { 528 entypoint = []string{spec_.Entrypoint} 529 } 530 return &v0.Step{ 531 ID: id, 532 Name: convertName(src.Name), 533 Type: v0.StepTypeBackground, 534 Spec: &v0.StepBackground{ 535 Command: spec_.Run, 536 ConnRef: d.dockerhubConn, 537 Entrypoint: entypoint, 538 Env: spec_.Envs, 539 Image: spec_.Image, 540 ImagePullPolicy: convertImagePull(spec_.Pull), 541 Privileged: spec_.Privileged, 542 RunAsUser: spec_.User, 543 PortBindings: convertPorts(spec_.Ports), 544 }, 545 When: convertStepWhen(src.When, id), 546 } 547 } 548 549 // helper function to convert a Plugin step from the v1 550 // structure to the v0 harness structure. 551 // 552 // TODO convert resources 553 // TODO convert reports 554 func (d *Downgrader) convertStepPlugin(src *v1.Step) *v0.Step { 555 spec_ := src.Spec.(*v1.StepPlugin) 556 557 if strings.Contains(spec_.Image, "kaniko-docker") { 558 return d.convertStepPluginToDocker(src) 559 } 560 561 var id = d.identifiers.Generate( 562 slug.Create(src.Id), 563 slug.Create(src.Name), 564 slug.Create(src.Type)) 565 if src.Name == "" { 566 src.Name = id 567 } 568 return &v0.Step{ 569 ID: id, 570 Name: src.Name, 571 Type: v0.StepTypePlugin, 572 Timeout: convertTimeout(src.Timeout), 573 Spec: &v0.StepPlugin{ 574 Env: spec_.Envs, 575 ConnRef: d.dockerhubConn, 576 Image: spec_.Image, 577 ImagePullPolicy: convertImagePull(spec_.Pull), 578 Settings: convertSettings(spec_.With), 579 Privileged: spec_.Privileged, 580 RunAsUser: spec_.User, 581 }, 582 When: convertStepWhen(src.When, id), 583 } 584 } 585 586 // helper function to convert a Plugin step from the v1 587 // structure to the v0 harness structure. 588 // 589 // TODO convert resources 590 // TODO convert reports 591 func (d *Downgrader) convertStepPluginToDocker(src *v1.Step) *v0.Step { 592 spec_ := src.Spec.(*v1.StepPlugin) 593 var id = d.identifiers.Generate( 594 slug.Create(src.Id), 595 slug.Create(src.Name), 596 slug.Create(src.Type)) 597 if src.Name == "" { 598 src.Name = id 599 } 600 601 stepDocker := &v0.StepDocker{ 602 Caching: true, 603 ConnectorRef: "<+input>", 604 Privileged: spec_.Privileged, 605 RunAsUser: spec_.User, 606 } 607 608 // Check if "repo" exists in spec_.With 609 if repo, ok := spec_.With["repo"].(string); ok { 610 // If "repo" exists, set it in StepDocker 611 stepDocker.Repo = repo 612 } 613 if context, ok := spec_.With["context"].(string); ok { 614 // If "context" exists, set it in StepDocker 615 stepDocker.Context = context 616 } 617 if dockerfile, ok := spec_.With["dockerfile"].(string); ok { 618 // If "dockerfile" exists, set it in StepDocker 619 stepDocker.Dockerfile = dockerfile 620 } 621 if target, ok := spec_.With["target"].(string); ok { 622 // If "target" exists, set it in StepDocker 623 stepDocker.Target = target 624 } 625 // if cacheRepo, ok := spec_.With["cache_repo"].(string); ok { 626 // // If "cache_repo" exists, set it in StepDocker 627 // stepDocker.RemoteCacheRepo = cacheRepo 628 // } 629 if tagsInterface, ok := spec_.With["tags"]; ok { 630 stepDocker.Tags = extractStringSlice(tagsInterface) 631 } 632 if buildArgsInterface, ok := spec_.With["build_args"]; ok { 633 stepDocker.BuildsArgs = extractStringMap(buildArgsInterface) 634 } 635 if labelsInterface, ok := spec_.With["custom_labels"]; ok { 636 stepDocker.Labels = extractStringMap(labelsInterface) 637 } 638 639 return &v0.Step{ 640 ID: id, 641 Name: src.Name, 642 Type: v0.StepTypeBuildAndPushDockerRegistry, 643 Timeout: convertTimeout(src.Timeout), 644 Spec: stepDocker, 645 When: convertStepWhen(src.When, id), 646 } 647 } 648 649 // helper function to convert an Action step from the v1 650 // structure to the v0 harness structure. 651 func (d *Downgrader) convertStepAction(src *v1.Step) *v0.Step { 652 spec_ := src.Spec.(*v1.StepAction) 653 var id = d.identifiers.Generate( 654 slug.Create(src.Id), 655 slug.Create(src.Name), 656 slug.Create(src.Type)) 657 if src.Name == "" { 658 src.Name = id 659 } 660 return &v0.Step{ 661 ID: id, 662 Name: convertName(src.Name), 663 Type: v0.StepTypeAction, 664 Timeout: convertTimeout(src.Timeout), 665 Spec: &v0.StepAction{ 666 Uses: spec_.Uses, 667 With: convertSettings(spec_.With), 668 Envs: spec_.Envs, 669 }, 670 When: convertStepWhen(src.When, id), 671 } 672 } 673 674 // helper function to convert a Bitrise step from the v1 675 // structure to the v0 harness structure. 676 func (d *Downgrader) convertStepBitrise(src *v1.Step) *v0.Step { 677 spec_ := src.Spec.(*v1.StepBitrise) 678 var id = d.identifiers.Generate( 679 slug.Create(src.Id), 680 slug.Create(src.Name), 681 slug.Create(src.Type)) 682 if src.Name == "" { 683 src.Name = id 684 } 685 return &v0.Step{ 686 ID: id, 687 Name: convertName(src.Name), 688 Type: v0.StepTypeBitrise, 689 Timeout: convertTimeout(src.Timeout), 690 Spec: &v0.StepBitrise{ 691 Uses: spec_.Uses, 692 With: convertSettings(spec_.With), 693 Envs: spec_.Envs, 694 }, 695 When: convertStepWhen(src.When, id), 696 } 697 } 698 699 func convertPorts(ports []string) map[string]string { 700 if len(ports) == 0 { 701 return nil 702 } 703 bindings := make(map[string]string, len(ports)) 704 for _, port := range ports { 705 split := strings.Split(port, ":") 706 if len(split) == 1 { 707 bindings[split[0]] = split[0] 708 } else if len(split) == 2 { 709 bindings[split[0]] = split[1] 710 } 711 } 712 return bindings 713 } 714 715 func convertCache(src *v1.Cache) *v0.Cache { 716 if src == nil { 717 return nil 718 } 719 return &v0.Cache{ 720 Enabled: src.Enabled, 721 Key: src.Key, 722 Paths: src.Paths, 723 } 724 } 725 726 func convertVariables(src map[string]string) []*v0.Variable { 727 if src == nil { 728 return nil 729 } 730 var vars []*v0.Variable 731 var keys []string 732 for k := range src { 733 keys = append(keys, k) 734 } 735 sort.Strings(keys) 736 for _, k := range keys { 737 v := src[k] 738 vars = append(vars, &v0.Variable{ 739 Name: k, 740 Value: v, 741 Type: "String", 742 }) 743 } 744 return vars 745 } 746 747 func convertSettings(src map[string]interface{}) map[string]interface{} { 748 dst := map[string]interface{}{} 749 for k, v := range src { 750 switch v := v.(type) { 751 case []interface{}: 752 var strList []string 753 for _, item := range v { 754 strList = append(strList, fmt.Sprint(item)) 755 } 756 dst[k] = strList 757 default: 758 dst[k] = fmt.Sprint(v) 759 } 760 } 761 return dst 762 } 763 764 func convertTimeout(s string) v0.Duration { 765 d, _ := time.ParseDuration(s) 766 return v0.Duration{ 767 Duration: d, 768 } 769 } 770 771 func convertImagePull(v string) (s string) { 772 switch v { 773 case "always": 774 return v0.ImagePullAlways 775 case "never": 776 return v0.ImagePullNever 777 case "if-not-exists": 778 return v0.ImagePullIfNotPresent 779 default: 780 return "" 781 } 782 } 783 784 func convertPlatform(platform *v1.Platform, runtime *v0.Runtime) *v0.Platform { 785 if platform != nil { 786 var os, arch string 787 788 // convert the OS name 789 switch platform.Os { 790 case "linux": 791 os = "Linux" 792 case "windows": 793 os = "Windows" 794 case "macos", "mac", "darwin": 795 os = "MacOS" 796 default: 797 os = "Linux" 798 } 799 800 // convert the Arch name 801 switch platform.Arch { 802 case "amd64": 803 arch = "Amd64" 804 case "arm", "arm64": 805 arch = "Arm64" 806 default: 807 // choose the default architecture 808 // based on the os. 809 switch os { 810 case "MacOS": 811 arch = "Arm64" 812 default: 813 arch = "Amd64" 814 } 815 } 816 817 // ensure supported infra when using harness cloud 818 if runtime != nil && runtime.Type == "Cloud" { 819 switch os { 820 case "MacOS": 821 // force amd64 for Mac when using Cloud 822 arch = "Arm64" 823 case "Windows": 824 // force amd64 for Windows when using Cloud 825 arch = "Amd64" 826 } 827 } 828 829 return &v0.Platform{ 830 OS: os, 831 Arch: arch, 832 } 833 } else { 834 // default to linux amd64 835 return &v0.Platform{ 836 OS: "Linux", 837 Arch: "Amd64", 838 } 839 } 840 } 841 842 func convertStepWhen(when *v1.When, stepId string) *v0.StepWhen { 843 if when == nil { 844 return nil 845 } 846 847 newWhen := &v0.StepWhen{ 848 StageStatus: "Success", // default 849 } 850 var conditions []string 851 852 for _, cond := range when.Cond { 853 for k, v := range cond { 854 switch k { 855 case "event": 856 if v.In != nil { 857 var eventConditions []string 858 for _, event := range v.In { 859 eventMapping, ok := eventMap[event] 860 if !ok { 861 continue 862 } 863 eventConditions = append(eventConditions, fmt.Sprintf("%s %s %q", eventMapping["jexl"], eventMapping["operator"], eventMapping["event"])) 864 } 865 if len(eventConditions) > 0 { 866 conditions = append(conditions, fmt.Sprintf("(%s)", strings.Join(eventConditions, " || "))) 867 } 868 } 869 if v.Not != nil && v.Not.In != nil { 870 var notEventConditions []string 871 for _, event := range v.Not.In { 872 eventMapping, ok := eventMap[event] 873 if !ok { 874 continue 875 } 876 notEventConditions = append(notEventConditions, fmt.Sprintf("%s %s %q", eventMapping["jexl"], eventMapping["inverseOperator"], eventMapping["event"])) 877 } 878 if len(notEventConditions) > 0 { 879 conditions = append(conditions, fmt.Sprintf("(%s)", strings.Join(notEventConditions, " && "))) 880 } 881 } 882 case "status": 883 if v.Eq != "" { 884 newWhen.StageStatus = v.Eq 885 } 886 if v.In != nil { 887 var statusConditions []string 888 for _, status := range v.In { 889 statusConditions = append(statusConditions, fmt.Sprintf("<+execution.steps.%s.status> == %q", stepId, status)) 890 } 891 conditions = append(conditions, fmt.Sprintf("%s", strings.Join(statusConditions, " || "))) 892 } 893 case "branch": 894 if v.In != nil { 895 var branchConditions []string 896 for _, branch := range v.In { 897 branchConditions = append(branchConditions, fmt.Sprintf("<+trigger.targetBranch> == %q", branch)) 898 } 899 conditions = append(conditions, fmt.Sprintf("%s", strings.Join(branchConditions, " || "))) 900 } 901 if v.Not != nil && v.Not.In != nil { 902 var notBranchConditions []string 903 for _, branch := range v.Not.In { 904 notBranchConditions = append(notBranchConditions, fmt.Sprintf("<+trigger.targetBranch> != %q", branch)) 905 } 906 conditions = append(conditions, fmt.Sprintf("%s", strings.Join(notBranchConditions, " && "))) 907 } 908 case "repo": 909 if v.In != nil { 910 var repoConditions []string 911 for _, repo := range v.In { 912 repoConditions = append(repoConditions, fmt.Sprintf("<+trigger.payload.repository.name> == %q", repo)) 913 } 914 conditions = append(conditions, fmt.Sprintf("%s", strings.Join(repoConditions, " || "))) 915 } 916 if v.Not != nil && v.Not.In != nil { 917 var notRepoConditions []string 918 for _, repo := range v.Not.In { 919 notRepoConditions = append(notRepoConditions, fmt.Sprintf("<+trigger.payload.repository.name> != %q", repo)) 920 } 921 conditions = append(conditions, fmt.Sprintf("%s", strings.Join(notRepoConditions, " && "))) 922 } 923 case "ref": 924 if v.In != nil { 925 var refConditions []string 926 for _, ref := range v.In { 927 refConditions = append(refConditions, fmt.Sprintf("<+trigger.payload.ref> == %q", ref)) 928 } 929 conditions = append(conditions, fmt.Sprintf("%s", strings.Join(refConditions, " || "))) 930 } 931 if v.Not != nil && v.Not.In != nil { 932 var notRefConditions []string 933 for _, ref := range v.Not.In { 934 notRefConditions = append(notRefConditions, fmt.Sprintf("<+trigger.payload.ref> != %q", ref)) 935 } 936 conditions = append(conditions, fmt.Sprintf("%s", strings.Join(notRefConditions, " && "))) 937 } 938 } 939 } 940 } 941 942 if len(conditions) > 0 { 943 newWhen.Condition = strings.Join(conditions, " && ") 944 } 945 946 return newWhen 947 } 948 949 func convertStageWhen(when *v1.When, stepId string) *v0.StageWhen { 950 if when == nil { 951 return nil 952 } 953 954 newWhen := &v0.StageWhen{ 955 PipelineStatus: "Success", // default 956 } 957 var conditions []string 958 959 for _, cond := range when.Cond { 960 for k, v := range cond { 961 switch k { 962 case "event": 963 if v.In != nil { 964 var eventConditions []string 965 for _, event := range v.In { 966 eventMapping, ok := eventMap[event] 967 if !ok { 968 continue 969 } 970 eventConditions = append(eventConditions, fmt.Sprintf("%s %s %q", eventMapping["jexl"], eventMapping["operator"], eventMapping["event"])) 971 } 972 if len(eventConditions) > 0 { 973 conditions = append(conditions, fmt.Sprintf("(%s)", strings.Join(eventConditions, " || "))) 974 } 975 } 976 if v.Not != nil && v.Not.In != nil { 977 var notEventConditions []string 978 for _, event := range v.Not.In { 979 eventMapping, ok := eventMap[event] 980 if !ok { 981 continue 982 } 983 notEventConditions = append(notEventConditions, fmt.Sprintf("%s %s %q", eventMapping["jexl"], eventMapping["inverseOperator"], eventMapping["event"])) 984 } 985 if len(notEventConditions) > 0 { 986 conditions = append(conditions, fmt.Sprintf("(%s)", strings.Join(notEventConditions, " && "))) 987 } 988 } 989 case "status": 990 if v.Eq != "" { 991 newWhen.PipelineStatus = v.Eq 992 } 993 if v.In != nil { 994 var statusConditions []string 995 for _, status := range v.In { 996 statusConditions = append(statusConditions, fmt.Sprintf("<+execution.steps.%s.status> == %q", stepId, status)) 997 } 998 conditions = append(conditions, fmt.Sprintf("%s", strings.Join(statusConditions, " || "))) 999 } 1000 case "branch": 1001 if v.In != nil { 1002 var branchConditions []string 1003 for _, branch := range v.In { 1004 branchConditions = append(branchConditions, fmt.Sprintf("<+trigger.targetBranch> == %q", branch)) 1005 } 1006 conditions = append(conditions, fmt.Sprintf("%s", strings.Join(branchConditions, " || "))) 1007 } 1008 if v.Not != nil && v.Not.In != nil { 1009 var notBranchConditions []string 1010 for _, branch := range v.Not.In { 1011 notBranchConditions = append(notBranchConditions, fmt.Sprintf("<+trigger.targetBranch> != %q", branch)) 1012 } 1013 conditions = append(conditions, fmt.Sprintf("%s", strings.Join(notBranchConditions, " && "))) 1014 } 1015 case "repo": 1016 if v.In != nil { 1017 var repoConditions []string 1018 for _, repo := range v.In { 1019 repoConditions = append(repoConditions, fmt.Sprintf("<+trigger.payload.repository.name> == %q", repo)) 1020 } 1021 conditions = append(conditions, fmt.Sprintf("%s", strings.Join(repoConditions, " || "))) 1022 } 1023 if v.Not != nil && v.Not.In != nil { 1024 var notRepoConditions []string 1025 for _, repo := range v.Not.In { 1026 notRepoConditions = append(notRepoConditions, fmt.Sprintf("<+trigger.payload.repository.name> != %q", repo)) 1027 } 1028 conditions = append(conditions, fmt.Sprintf("%s", strings.Join(notRepoConditions, " && "))) 1029 } 1030 case "ref": 1031 if v.In != nil { 1032 var refConditions []string 1033 for _, ref := range v.In { 1034 refConditions = append(refConditions, fmt.Sprintf("<+trigger.payload.ref> == %q", ref)) 1035 } 1036 conditions = append(conditions, fmt.Sprintf("%s", strings.Join(refConditions, " || "))) 1037 } 1038 if v.Not != nil && v.Not.In != nil { 1039 var notRefConditions []string 1040 for _, ref := range v.Not.In { 1041 notRefConditions = append(notRefConditions, fmt.Sprintf("<+trigger.payload.ref> != %q", ref)) 1042 } 1043 conditions = append(conditions, fmt.Sprintf("%s", strings.Join(notRefConditions, " && "))) 1044 } 1045 } 1046 } 1047 } 1048 1049 if len(conditions) > 0 { 1050 newWhen.Condition = strings.Join(conditions, " && ") 1051 } 1052 1053 return newWhen 1054 } 1055 1056 func extractStringSlice(input interface{}) []string { 1057 switch data := input.(type) { 1058 case []interface{}: 1059 var result []string 1060 for _, item := range data { 1061 result = append(result, strings.SplitN(fmt.Sprintf("%v", item), ",", -1)...) 1062 } 1063 return result 1064 case interface{}: 1065 return []string{fmt.Sprintf("%v", data)} 1066 default: 1067 return nil 1068 } 1069 } 1070 1071 func extractStringMap(input interface{}) map[string]string { 1072 switch data := input.(type) { 1073 case []interface{}: 1074 result := make(map[string]string) 1075 for _, item := range data { 1076 for _, keyValue := range strings.SplitN(fmt.Sprintf("%v", item), ",", -1) { 1077 pair := strings.SplitN(keyValue, "=", 2) 1078 if len(pair) == 2 { 1079 result[pair[0]] = pair[1] 1080 } 1081 } 1082 } 1083 return result 1084 case interface{}: 1085 result := make(map[string]string) 1086 for _, keyValue := range strings.SplitN(fmt.Sprintf("%v", data), ",", -1) { 1087 pair := strings.SplitN(keyValue, "=", 2) 1088 if len(pair) == 2 { 1089 result[pair[0]] = pair[1] 1090 } 1091 } 1092 return result 1093 default: 1094 return nil 1095 } 1096 }