|
1 | 1 | package codefresh |
2 | 2 |
|
3 | 3 | import ( |
4 | | - "encoding/json" |
5 | 4 | "fmt" |
6 | 5 | "log" |
7 | 6 | "regexp" |
| 7 | + "strconv" |
8 | 8 | "strings" |
9 | 9 |
|
10 | 10 | cfClient "github.com/codefresh-io/terraform-provider-codefresh/client" |
11 | 11 | "github.com/hashicorp/go-cty/cty" |
12 | 12 | "github.com/hashicorp/terraform-plugin-sdk/v2/diag" |
13 | 13 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" |
14 | 14 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" |
15 | | - "gopkg.in/yaml.v2" |
16 | 15 | ) |
17 | 16 |
|
18 | 17 | var terminationPolicyOnCreateBranchAttributes = []string{"branchName", "ignoreTrigger", "ignoreBranch"} |
@@ -482,9 +481,12 @@ func resourcePipelineCreate(d *schema.ResourceData, meta interface{}) error { |
482 | 481 |
|
483 | 482 | client := meta.(*cfClient.Client) |
484 | 483 |
|
485 | | - pipeline := *mapResourceToPipeline(d) |
| 484 | + pipeline, err := mapResourceToPipeline(d) |
| 485 | + if err != nil { |
| 486 | + return err |
| 487 | + } |
486 | 488 |
|
487 | | - resp, err := client.CreatePipeline(&pipeline) |
| 489 | + resp, err := client.CreatePipeline(pipeline) |
488 | 490 | if err != nil { |
489 | 491 | return err |
490 | 492 | } |
@@ -522,10 +524,14 @@ func resourcePipelineUpdate(d *schema.ResourceData, meta interface{}) error { |
522 | 524 |
|
523 | 525 | client := meta.(*cfClient.Client) |
524 | 526 |
|
525 | | - pipeline := *mapResourceToPipeline(d) |
| 527 | + pipeline, err := mapResourceToPipeline(d) |
| 528 | + if err != nil { |
| 529 | + return err |
| 530 | + } |
| 531 | + |
526 | 532 | pipeline.Metadata.ID = d.Id() |
527 | 533 |
|
528 | | - _, err := client.UpdatePipeline(&pipeline) |
| 534 | + _, err = client.UpdatePipeline(pipeline) |
529 | 535 | if err != nil { |
530 | 536 | return err |
531 | 537 | } |
@@ -735,7 +741,7 @@ func flattenTriggers(triggers []cfClient.Trigger) []map[string]interface{} { |
735 | 741 | return res |
736 | 742 | } |
737 | 743 |
|
738 | | -func mapResourceToPipeline(d *schema.ResourceData) *cfClient.Pipeline { |
| 744 | +func mapResourceToPipeline(d *schema.ResourceData) (*cfClient.Pipeline, error) { |
739 | 745 |
|
740 | 746 | tags := d.Get("tags").(*schema.Set).List() |
741 | 747 |
|
@@ -774,7 +780,10 @@ func mapResourceToPipeline(d *schema.ResourceData) *cfClient.Pipeline { |
774 | 780 | Context: d.Get("spec.0.spec_template.0.context").(string), |
775 | 781 | } |
776 | 782 | } else { |
777 | | - extractSpecAttributesFromOriginalYamlString(originalYamlString, pipeline) |
| 783 | + err := extractSpecAttributesFromOriginalYamlString(originalYamlString, pipeline) |
| 784 | + if err != nil { |
| 785 | + return nil, err |
| 786 | + } |
778 | 787 | } |
779 | 788 |
|
780 | 789 | if _, ok := d.GetOk("spec.0.runtime_environment"); ok { |
@@ -870,71 +879,61 @@ func mapResourceToPipeline(d *schema.ResourceData) *cfClient.Pipeline { |
870 | 879 |
|
871 | 880 | pipeline.Spec.TerminationPolicy = codefreshTerminationPolicy |
872 | 881 |
|
873 | | - return pipeline |
| 882 | + return pipeline, nil |
874 | 883 | } |
875 | 884 |
|
876 | | -// extractSpecAttributesFromOriginalYamlString extracts the steps and stages from the original yaml string to enable propagation in the `Spec` attribute of the pipeline |
877 | | -// We cannot leverage on the standard marshal/unmarshal because the steps attribute needs to maintain the order of elements |
878 | | -// while by default the standard function doesn't do it because in JSON maps are unordered |
879 | | -func extractSpecAttributesFromOriginalYamlString(originalYamlString string, pipeline *cfClient.Pipeline) { |
880 | | - ms := OrderedMapSlice{} |
881 | | - err := yaml.Unmarshal([]byte(originalYamlString), &ms) |
882 | | - if err != nil { |
883 | | - log.Fatalf("Unable to unmarshall original_yaml_string. Error: %v", err) |
884 | | - } |
| 885 | +// This function is used to extract the spec attributes from the original_yaml_string attribute. |
| 886 | +// Typically, unmarshalling the YAML string is problematic because the order of the attributes is not preserved. |
| 887 | +// Namely, we care a lot about the order of the steps and stages attributes. |
| 888 | +// Luckily, the yj package introduces a MapSlice type that preserves the order Map items (see utils.go). |
| 889 | +func extractSpecAttributesFromOriginalYamlString(originalYamlString string, pipeline *cfClient.Pipeline) error { |
| 890 | + for _, attribute := range []string{"stages", "steps", "hooks"} { |
| 891 | + yamlString, err := yq(fmt.Sprintf(".%s", attribute), originalYamlString) |
| 892 | + if err != nil { |
| 893 | + return fmt.Errorf("error while extracting '%s' from original YAML string: %v", attribute, err) |
| 894 | + } else if yamlString == "" { |
| 895 | + continue |
| 896 | + } |
885 | 897 |
|
886 | | - stages := "[]" |
887 | | - steps := "{}" |
888 | | - hooks := "{}" |
| 898 | + attributeJson, err := yamlToJson(yamlString) |
| 899 | + if err != nil { |
| 900 | + return fmt.Errorf("error while converting '%s' YAML to JSON: %v", attribute, err) |
| 901 | + } |
889 | 902 |
|
890 | | - // Parse elements of the YAML string to extract Steps, Hooks and Stages if defined |
891 | | - for _, item := range ms { |
892 | | - key := item.Key.(string) |
893 | | - switch key { |
| 903 | + switch attribute { |
| 904 | + case "stages": |
| 905 | + pipeline.Spec.Stages = &cfClient.Stages{ |
| 906 | + Stages: attributeJson, |
| 907 | + } |
894 | 908 | case "steps": |
895 | | - switch x := item.Value.(type) { |
896 | | - default: |
897 | | - log.Fatalf("unsupported value type: %T", item.Value) |
898 | | - |
899 | | - case OrderedMapSlice: |
900 | | - s, _ := json.Marshal(x) |
901 | | - steps = string(s) |
| 909 | + pipeline.Spec.Steps = &cfClient.Steps{ |
| 910 | + Steps: attributeJson, |
902 | 911 | } |
903 | | - case "stages": |
904 | | - s, _ := json.Marshal(item.Value) |
905 | | - stages = string(s) |
906 | | - |
907 | 912 | case "hooks": |
908 | | - switch x := item.Value.(type) { |
909 | | - default: |
910 | | - log.Fatalf("unsupported value type: %T", item.Value) |
911 | | - |
912 | | - case OrderedMapSlice: |
913 | | - h, _ := json.Marshal(x) |
914 | | - hooks = string(h) |
| 913 | + pipeline.Spec.Hooks = &cfClient.Hooks{ |
| 914 | + Hooks: attributeJson, |
915 | 915 | } |
916 | | - case "mode": |
917 | | - pipeline.Spec.Mode = item.Value.(string) |
918 | | - case "fail_fast": |
919 | | - ff, ok := item.Value.(bool) |
920 | | - if ok { |
921 | | - pipeline.Spec.FailFast = &ff |
922 | | - } |
923 | | - default: |
924 | | - log.Printf("Unsupported entry %s", key) |
925 | 916 | } |
926 | 917 | } |
927 | 918 |
|
928 | | - pipeline.Spec.Steps = &cfClient.Steps{ |
929 | | - Steps: steps, |
930 | | - } |
931 | | - pipeline.Spec.Stages = &cfClient.Stages{ |
932 | | - Stages: stages, |
933 | | - } |
934 | | - pipeline.Spec.Hooks = &cfClient.Hooks{ |
935 | | - Hooks: hooks, |
| 919 | + mode, err := yq(".mode", originalYamlString) |
| 920 | + if err != nil { |
| 921 | + return fmt.Errorf("error while extracting 'mode' from original YAML string: %v", err) |
| 922 | + } else if mode != "" { |
| 923 | + pipeline.Spec.Mode = mode |
936 | 924 | } |
937 | 925 |
|
| 926 | + ff, err := yq(".fail_fast", originalYamlString) |
| 927 | + if err != nil { |
| 928 | + return fmt.Errorf("error while extracting 'mode' from original YAML string: %v", err) |
| 929 | + } else if ff != "" { |
| 930 | + ff_b, err := strconv.ParseBool(strings.TrimSpace(ff)) |
| 931 | + if err != nil { |
| 932 | + return fmt.Errorf("error while parsing 'fail_fast' as boolean: %v", err) |
| 933 | + } |
| 934 | + pipeline.Spec.FailFast = &ff_b |
| 935 | + } |
| 936 | + return nil |
938 | 937 | } |
939 | 938 |
|
940 | 939 | func getSupportedTerminationPolicyAttributes(policy string) map[string]interface{} { |
|
0 commit comments