@@ -3,16 +3,20 @@ package codefresh
3
3
import (
4
4
"bytes"
5
5
"context"
6
+ "encoding/json"
6
7
"fmt"
7
8
"log"
8
9
"sort"
10
+ "strings"
9
11
10
12
"github.com/Masterminds/semver"
11
13
cfClient "github.com/codefresh-io/terraform-provider-codefresh/client"
12
- "github.com/ghodss/yaml"
14
+ ghodss "github.com/ghodss/yaml"
13
15
"github.com/hashicorp/terraform-plugin-sdk/helper/hashcode"
14
16
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
15
17
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
18
+ "github.com/iancoleman/orderedmap"
19
+ "gopkg.in/yaml.v2"
16
20
)
17
21
18
22
func resourceStepTypesVersions () * schema.Resource {
@@ -67,15 +71,17 @@ func normalizeYamlStringStepTypes(yamlString interface{}) (string, error) {
67
71
}
68
72
69
73
s := yamlString .(string )
70
- err := yaml .Unmarshal ([]byte (s ), & j )
74
+ err := ghodss .Unmarshal ([]byte (s ), & j )
71
75
metadataMap := j ["metadata" ].(map [string ]interface {})
72
76
//Removing "latest" attribute from metadata since it's transient based on the version
73
77
delete (metadataMap , "latest" )
78
+ delete (metadataMap , "name" )
79
+ delete (metadataMap , "version" )
74
80
if err != nil {
75
81
return s , err
76
82
}
77
83
78
- bytes , _ := yaml .Marshal (j )
84
+ bytes , _ := ghodss .Marshal (j )
79
85
return string (bytes [:]), nil
80
86
}
81
87
@@ -104,7 +110,7 @@ func resourceStepTypesVersionCreate(ctx context.Context, d *schema.ResourceData,
104
110
orderedVersions := sortVersions (versions )
105
111
for _ , version := range orderedVersions {
106
112
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 )
108
114
_ , err := client .CreateStepTypes (& step )
109
115
if err != nil {
110
116
return diag .Errorf ("[DEBUG] Error while creating step types OnCreate. Error = %v" , err )
@@ -316,10 +322,10 @@ func resourceStepTypesVersionsConfigHash(v interface{}) int {
316
322
buf .WriteString (fmt .Sprintf ("%s" , m ["version_number" ].(string )))
317
323
var stepTypes cfClient.StepTypes
318
324
stepTypesYaml := m ["step_types_yaml" ].(string )
319
- yaml .Unmarshal ([]byte (stepTypesYaml ), & stepTypes )
325
+ ghodss .Unmarshal ([]byte (stepTypesYaml ), & stepTypes )
320
326
// Remove runtime attributes, name and version to avoid discrepancies when comparing hashes
321
327
cleanUpStepFromTransientValues (& stepTypes , "" , "" )
322
- stepTypesYamlByteArray , _ := yaml .Marshal (stepTypes )
328
+ stepTypesYamlByteArray , _ := ghodss .Marshal (stepTypes )
323
329
buf .WriteString (fmt .Sprintf ("%s" , string (stepTypesYamlByteArray )))
324
330
hash := hashcode .String (buf .String ())
325
331
return hash
@@ -332,11 +338,15 @@ func flattenVersions(name string, versions []cfClient.StepTypesVersion) *schema.
332
338
m := make (map [string ]interface {})
333
339
m ["version_number" ] = version .VersionNumber
334
340
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
+ }
336
346
m ["step_types_yaml" ] = string (stepTypesYaml )
337
347
stepVersions = append (stepVersions , m )
338
348
}
339
-
349
+ log . Printf ( "[DEBUG] Flattened Versions %s" , stepVersions )
340
350
return schema .NewSet (resourceStepTypesVersionsConfigHash , stepVersions )
341
351
}
342
352
@@ -350,16 +360,73 @@ func mapResourceToStepTypesVersions(d *schema.ResourceData) *cfClient.StepTypesV
350
360
if version != "" {
351
361
var stepTypes cfClient.StepTypes
352
362
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
+
354
369
cleanUpStepFromTransientValues (& stepTypes , stepTypesVersions .Name , version )
355
370
stepVersion := cfClient.StepTypesVersion {
356
371
VersionNumber : version ,
357
372
StepTypes : stepTypes ,
358
373
}
374
+ if stepVersion .StepTypes .Spec .Steps != nil {
375
+ stepVersion .StepTypes .Spec .Steps = extractSteps (stepTypesYaml )
376
+ }
359
377
360
378
stepTypesVersions .Versions = append (stepTypesVersions .Versions , stepVersion )
361
379
}
362
380
}
363
381
364
382
return & stepTypesVersions
365
383
}
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