Skip to content

Commit 6a0c6f5

Browse files
committed
Bugfix to preserve order of steps attribute in step-types
1 parent 6e81ef2 commit 6a0c6f5

File tree

5 files changed

+95
-13
lines changed

5 files changed

+95
-13
lines changed

client/step_types.go

+12-3
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import (
44
"fmt"
55
"log"
66
"net/url"
7+
8+
"github.com/iancoleman/orderedmap"
79
)
810

911
type StepTypesVersions struct {
@@ -19,7 +21,15 @@ type StepTypes struct {
1921
Version string `json:"version,omitempty"`
2022
Kind string `json:"kind,omitempty"`
2123
Metadata map[string]interface{} `json:"metadata,omitempty"`
22-
Spec map[string]interface{} `json:"spec,omitempty"`
24+
Spec SpecStepTypes `json:"spec,omitempty"`
25+
}
26+
27+
type SpecStepTypes struct {
28+
Arguments string `json:"arguments,omitempty"`
29+
Delimiters map[string]interface{} `json:"delimiters,omitempty"`
30+
Returns string `json:"returns,omitempty"`
31+
Steps *orderedmap.OrderedMap `json:"steps,omitempty"`
32+
StepsTemplate string `json:"stepsTemplate,omitempty"`
2333
}
2434

2535
func (stepTypes *StepTypes) GetID() string {
@@ -89,10 +99,9 @@ func (client *Client) CreateStepTypes(stepTypes *StepTypes) (*StepTypes, error)
8999
var respStepTypes StepTypes
90100
err = DecodeResponseInto(resp, &respStepTypes)
91101
if err != nil {
92-
log.Printf("[DEBUG] Error while decoding step types. Error = %v, Response: %q", err, respStepTypes)
102+
log.Printf("[DEBUG] Error while decoding step types. Error = %v, Response: %v", err, respStepTypes)
93103
return nil, err
94104
}
95-
log.Printf("[DEBUG] Decoded step types response: %q", respStepTypes.Metadata["name"])
96105
return &respStepTypes, nil
97106

98107
}

codefresh/resource_step_types.go

+4-1
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ func resourceStepTypesCreate(d *schema.ResourceData, meta interface{}) error {
3838
stepTypes := *mapResourceToStepTypes(d)
3939
resp, err := client.CreateStepTypes(&stepTypes)
4040
if err != nil {
41-
log.Printf("[DEBUG] Error while creating step types. Error = %v", err)
41+
log.Printf("[DEBUG] Error while creating step types for resource_step_types. Error = %v", err)
4242
return err
4343
}
4444

@@ -145,6 +145,9 @@ func mapResourceToStepTypes(d *schema.ResourceData) *cfClient.StepTypes {
145145
var stepTypes cfClient.StepTypes
146146
stepTypesYaml := d.Get("step_types_yaml")
147147
yaml.Unmarshal([]byte(stepTypesYaml.(string)), &stepTypes)
148+
if stepTypes.Spec.Steps != nil {
149+
stepTypes.Spec.Steps = extractSteps(stepTypesYaml.(string))
150+
}
148151

149152
return &stepTypes
150153
}

codefresh/resource_step_types_versions.go

+76-9
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,20 @@ package codefresh
33
import (
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

1822
func 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+
}

go.mod

+1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ require (
1111
github.com/hashicorp/terraform-config-inspect v0.0.0-20191212124732-c6ae6269b9d7 // indirect
1212
github.com/hashicorp/terraform-plugin-sdk v1.7.0
1313
github.com/hashicorp/terraform-plugin-sdk/v2 v2.0.0-rc.2.0.20200717132200-7435e2abc9d1
14+
github.com/iancoleman/orderedmap v0.2.0
1415
github.com/imdario/mergo v0.3.9
1516
github.com/stretchr/objx v0.1.1
1617
github.com/stretchr/testify v1.6.1 // indirect

go.sum

+2
Original file line numberDiff line numberDiff line change
@@ -258,6 +258,8 @@ github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d h1:kJCB4vdITiW1eC1
258258
github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM=
259259
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
260260
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
261+
github.com/iancoleman/orderedmap v0.2.0 h1:sq1N/TFpYH++aViPcaKjys3bDClUEU7s5B+z6jq8pNA=
262+
github.com/iancoleman/orderedmap v0.2.0/go.mod h1:N0Wam8K1arqPXNWjMo21EXnBPOPp36vB07FNRdD2geA=
261263
github.com/imdario/mergo v0.3.9 h1:UauaLniWCFHWd+Jp9oCEkTBj8VO/9DKg3PV3VCNMDIg=
262264
github.com/imdario/mergo v0.3.9/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
263265
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=

0 commit comments

Comments
 (0)