@@ -3,16 +3,20 @@ package codefresh
33import (
44 "bytes"
55 "context"
6+ "encoding/json"
67 "fmt"
78 "log"
89 "sort"
10+ "strings"
911
1012 "github.com/Masterminds/semver"
1113 cfClient "github.com/codefresh-io/terraform-provider-codefresh/client"
12- "github.com/ghodss/yaml"
14+ ghodss "github.com/ghodss/yaml"
1315 "github.com/hashicorp/terraform-plugin-sdk/helper/hashcode"
1416 "github.com/hashicorp/terraform-plugin-sdk/v2/diag"
1517 "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
18+ "github.com/iancoleman/orderedmap"
19+ "gopkg.in/yaml.v2"
1620)
1721
1822func resourceStepTypesVersions () * schema.Resource {
@@ -67,15 +71,17 @@ func normalizeYamlStringStepTypes(yamlString interface{}) (string, error) {
6771 }
6872
6973 s := yamlString .(string )
70- err := yaml .Unmarshal ([]byte (s ), & j )
74+ err := ghodss .Unmarshal ([]byte (s ), & j )
7175 metadataMap := j ["metadata" ].(map [string ]interface {})
7276 //Removing "latest" attribute from metadata since it's transient based on the version
7377 delete (metadataMap , "latest" )
78+ delete (metadataMap , "name" )
79+ delete (metadataMap , "version" )
7480 if err != nil {
7581 return s , err
7682 }
7783
78- bytes , _ := yaml .Marshal (j )
84+ bytes , _ := ghodss .Marshal (j )
7985 return string (bytes [:]), nil
8086}
8187
@@ -104,7 +110,7 @@ func resourceStepTypesVersionCreate(ctx context.Context, d *schema.ResourceData,
104110 orderedVersions := sortVersions (versions )
105111 for _ , version := range orderedVersions {
106112 step := mapVersion [version .String ()]
107- log .Printf ("[DEBUG] Version for create: %q" , version )
113+ log .Printf ("[DEBUG] Version for create: %q. StepSpec: %v " , version , step . Spec . Steps )
108114 _ , err := client .CreateStepTypes (& step )
109115 if err != nil {
110116 return diag .Errorf ("[DEBUG] Error while creating step types OnCreate. Error = %v" , err )
@@ -316,10 +322,10 @@ func resourceStepTypesVersionsConfigHash(v interface{}) int {
316322 buf .WriteString (fmt .Sprintf ("%s" , m ["version_number" ].(string )))
317323 var stepTypes cfClient.StepTypes
318324 stepTypesYaml := m ["step_types_yaml" ].(string )
319- yaml .Unmarshal ([]byte (stepTypesYaml ), & stepTypes )
325+ ghodss .Unmarshal ([]byte (stepTypesYaml ), & stepTypes )
320326 // Remove runtime attributes, name and version to avoid discrepancies when comparing hashes
321327 cleanUpStepFromTransientValues (& stepTypes , "" , "" )
322- stepTypesYamlByteArray , _ := yaml .Marshal (stepTypes )
328+ stepTypesYamlByteArray , _ := ghodss .Marshal (stepTypes )
323329 buf .WriteString (fmt .Sprintf ("%s" , string (stepTypesYamlByteArray )))
324330 hash := hashcode .String (buf .String ())
325331 return hash
@@ -332,11 +338,15 @@ func flattenVersions(name string, versions []cfClient.StepTypesVersion) *schema.
332338 m := make (map [string ]interface {})
333339 m ["version_number" ] = version .VersionNumber
334340 cleanUpStepFromTransientValues (& version .StepTypes , name , version .VersionNumber )
335- stepTypesYaml , _ := yaml .Marshal (version .StepTypes )
341+ stepTypesYaml , err := ghodss .Marshal (version .StepTypes )
342+ log .Printf ("[DEBUG] Flattened StepTypes %v" , version .StepTypes .Spec )
343+ if err != nil {
344+ log .Fatalf ("Error while flattening Versions: %v. Errv=%s" , version .StepTypes , err )
345+ }
336346 m ["step_types_yaml" ] = string (stepTypesYaml )
337347 stepVersions = append (stepVersions , m )
338348 }
339-
349+ log . Printf ( "[DEBUG] Flattened Versions %s" , stepVersions )
340350 return schema .NewSet (resourceStepTypesVersionsConfigHash , stepVersions )
341351}
342352
@@ -350,16 +360,73 @@ func mapResourceToStepTypesVersions(d *schema.ResourceData) *cfClient.StepTypesV
350360 if version != "" {
351361 var stepTypes cfClient.StepTypes
352362 stepTypesYaml := step .(map [string ]interface {})["step_types_yaml" ].(string )
353- yaml .Unmarshal ([]byte (stepTypesYaml ), & stepTypes )
363+
364+ err := ghodss .Unmarshal ([]byte (stepTypesYaml ), & stepTypes )
365+ if err != nil {
366+ log .Fatalf ("[DEBUG] Unable to mapResourceToStepTypesVersions for version %s. Err= %s" , version , err )
367+ }
368+
354369 cleanUpStepFromTransientValues (& stepTypes , stepTypesVersions .Name , version )
355370 stepVersion := cfClient.StepTypesVersion {
356371 VersionNumber : version ,
357372 StepTypes : stepTypes ,
358373 }
374+ if stepVersion .StepTypes .Spec .Steps != nil {
375+ stepVersion .StepTypes .Spec .Steps = extractSteps (stepTypesYaml )
376+ }
359377
360378 stepTypesVersions .Versions = append (stepTypesVersions .Versions , stepVersion )
361379 }
362380 }
363381
364382 return & stepTypesVersions
365383}
384+
385+ // extractStagesAndSteps extracts the steps and stages from the original yaml string to enable propagation in the `Spec` attribute of the pipeline
386+ // We cannot leverage on the standard marshal/unmarshal because the steps attribute needs to maintain the order of elements
387+ // while by default the standard function doesn't do it because in JSON maps are unordered
388+ func extractSteps (stepTypesYaml string ) (steps * orderedmap.OrderedMap ) {
389+ // Use mapSlice to preserve order of items from the YAML string
390+ m := yaml.MapSlice {}
391+ err := yaml .Unmarshal ([]byte (stepTypesYaml ), & m )
392+ if err != nil {
393+ log .Fatal ("Unable to unmarshall stepTypesYaml" )
394+ }
395+ steps = orderedmap .New ()
396+ // Dynamically build JSON object for steps using String builder
397+ stepsBuilder := strings.Builder {}
398+ stepsBuilder .WriteString ("{" )
399+ // Parse elements of the YAML string to extract Steps and Stages if defined
400+ for _ , item := range m {
401+ if item .Key == "spec" {
402+ for _ , specItem := range item .Value .(yaml.MapSlice ) {
403+ if specItem .Key == "steps" {
404+ switch x := specItem .Value .(type ) {
405+ default :
406+ log .Fatalf ("unsupported value type: %T" , specItem .Value )
407+
408+ case yaml.MapSlice :
409+ numberOfSteps := len (x )
410+ for index , item := range x {
411+ // We only need to preserve order at the first level to guarantee order of the steps, hence the child nodes can be marshalled
412+ // with the standard library
413+ y , _ := yaml .Marshal (item .Value )
414+ j2 , _ := ghodss .YAMLToJSON (y )
415+ stepsBuilder .WriteString ("\" " + item .Key .(string ) + "\" : " + string (j2 ))
416+ if index < numberOfSteps - 1 {
417+ stepsBuilder .WriteString ("," )
418+ }
419+ }
420+ }
421+ }
422+ }
423+ }
424+ }
425+ stepsBuilder .WriteString ("}" )
426+ err = json .Unmarshal ([]byte (stepsBuilder .String ()), & steps )
427+ if err != nil {
428+ log .Fatalf ("[DEBUG] Unable to parse steps. " )
429+ }
430+
431+ return
432+ }
0 commit comments