Skip to content

Commit be434d8

Browse files
committed
Add support for Hooks in original Yaml String
1 parent 76639db commit be434d8

File tree

4 files changed

+152
-5
lines changed

4 files changed

+152
-5
lines changed

client/context.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ func (client *Client) CreateContext(context *Context) (*Context, error) {
6767
}
6868

6969
resp, err := client.RequestAPI(&opts)
70-
log.Printf("[DEBUG] Called API for context with Body %v", body)
70+
7171
if err != nil {
7272
log.Printf("[DEBUG] Call to API for context creation failed with Error = %v for Body %v", err, body)
7373
return nil, err

client/pipeline.go

+16-2
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ type Spec struct {
8484
FailFast bool `json:"fail_fast,omitempty"`
8585
RuntimeEnvironment RuntimeEnvironment `json:"runtimeEnvironment,omitempty"`
8686
TerminationPolicy []map[string]interface{} `json:"terminationPolicy,omitempty"`
87+
Hooks *Hooks `json:"hooks,omitempty"`
8788
}
8889

8990
type Steps struct {
@@ -93,6 +94,10 @@ type Stages struct {
9394
Stages string
9495
}
9596

97+
type Hooks struct {
98+
Hooks string
99+
}
100+
96101
func (d Steps) MarshalJSON() ([]byte, error) {
97102
bytes := []byte(d.Steps)
98103
return bytes, nil
@@ -102,14 +107,23 @@ func (d Stages) MarshalJSON() ([]byte, error) {
102107
return bytes, nil
103108
}
104109

105-
func (d Steps) UnmarshalJSON(data []byte) error {
110+
func (d Hooks) MarshalJSON() ([]byte, error) {
111+
bytes := []byte(d.Hooks)
112+
return bytes, nil
113+
}
114+
115+
func (d *Steps) UnmarshalJSON(data []byte) error {
106116
d.Steps = string(data)
107117
return nil
108118
}
109-
func (d Stages) UnmarshalJSON(data []byte) error {
119+
func (d *Stages) UnmarshalJSON(data []byte) error {
110120
d.Stages = string(data)
111121
return nil
112122
}
123+
func (d *Hooks) UnmarshalJSON(data []byte) error {
124+
d.Hooks = string(data)
125+
return nil
126+
}
113127

114128
type Pipeline struct {
115129
Metadata Metadata `json:"metadata,omitempty"`

codefresh/resource_pipeline.go

+51-1
Original file line numberDiff line numberDiff line change
@@ -661,7 +661,7 @@ func extractSpecAttributesFromOriginalYamlString(originalYamlString string, pipe
661661
m := yaml.MapSlice{}
662662
err := yaml.Unmarshal([]byte(originalYamlString), &m)
663663
if err != nil {
664-
log.Fatal("Unable to unmarshall original_yaml_string")
664+
log.Fatalf("Unable to unmarshall original_yaml_string. Error: %v", err)
665665
}
666666

667667
stages := "[]"
@@ -699,6 +699,51 @@ func extractSpecAttributesFromOriginalYamlString(originalYamlString string, pipe
699699
y, _ := yaml.Marshal(item.Value)
700700
j2, _ := ghodss.YAMLToJSON(y)
701701
stages = string(j2)
702+
case "hooks":
703+
switch hooks := item.Value.(type) {
704+
default:
705+
log.Fatalf("unsupported value type: %T", item.Value)
706+
707+
case yaml.MapSlice:
708+
numberOfHooks := len(hooks)
709+
for indexHook, hook := range hooks {
710+
// E.g. on_finish
711+
hooksBuilder.WriteString("\"" + hook.Key.(string) + "\" : {")
712+
numberOfAttributes := len(hook.Value.(yaml.MapSlice))
713+
for indexAttribute, hookAttribute := range hook.Value.(yaml.MapSlice) {
714+
attribute := hookAttribute.Key.(string)
715+
switch attribute {
716+
case "steps":
717+
hooksBuilder.WriteString("\"steps\" : {")
718+
numberOfSteps := len(hookAttribute.Value.(yaml.MapSlice))
719+
for indexStep, step := range hookAttribute.Value.(yaml.MapSlice) {
720+
// We only need to preserve order at the first level to guarantee order of the steps, hence the child nodes can be marshalled
721+
// with the standard library
722+
y, _ := yaml.Marshal(step.Value)
723+
j2, _ := ghodss.YAMLToJSON(y)
724+
hooksBuilder.WriteString("\"" + step.Key.(string) + "\" : " + string(j2))
725+
if indexStep < numberOfSteps-1 {
726+
hooksBuilder.WriteString(",")
727+
}
728+
}
729+
hooksBuilder.WriteString("}")
730+
default:
731+
// For Other elements we don't need to preserve order
732+
y, _ := yaml.Marshal(hookAttribute.Value)
733+
j2, _ := ghodss.YAMLToJSON(y)
734+
hooksBuilder.WriteString("\"" + hookAttribute.Key.(string) + "\" : " + string(j2))
735+
}
736+
737+
if indexAttribute < numberOfAttributes-1 {
738+
hooksBuilder.WriteString(",")
739+
}
740+
}
741+
hooksBuilder.WriteString("}")
742+
if indexHook < numberOfHooks-1 {
743+
hooksBuilder.WriteString(",")
744+
}
745+
}
746+
}
702747
case "mode":
703748
pipeline.Spec.Mode = item.Value.(string)
704749
case "fail_fast":
@@ -708,13 +753,18 @@ func extractSpecAttributesFromOriginalYamlString(originalYamlString string, pipe
708753
}
709754
}
710755
stepsBuilder.WriteString("}")
756+
hooksBuilder.WriteString("}")
711757
steps := stepsBuilder.String()
758+
hooks := hooksBuilder.String()
712759
pipeline.Spec.Steps = &cfClient.Steps{
713760
Steps: steps,
714761
}
715762
pipeline.Spec.Stages = &cfClient.Stages{
716763
Stages: stages,
717764
}
765+
pipeline.Spec.Hooks = &cfClient.Hooks{
766+
Hooks: hooks,
767+
}
718768

719769
}
720770

codefresh/resource_pipeline_test.go

+84-1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package codefresh
22

33
import (
44
"fmt"
5+
"reflect"
56
"regexp"
67
"testing"
78

@@ -165,7 +166,56 @@ func TestAccCodefreshPipeline_RuntimeEnvironment(t *testing.T) {
165166
func TestAccCodefreshPipeline_OriginalYamlString(t *testing.T) {
166167
name := pipelineNamePrefix + acctest.RandString(10)
167168
resourceName := "codefresh_pipeline.test"
168-
originalYamlString := "version: \"1.0\"\nfail_fast: false\nmode: parallel\nsteps:\n test:\n image: alpine:latest\n commands:\n - echo \"ACC tests\""
169+
originalYamlString := `version: 1.0
170+
fail_fast: false
171+
stages:
172+
- test
173+
mode: parallel
174+
hooks:
175+
on_finish:
176+
steps:
177+
secondmycleanup:
178+
commands:
179+
- echo echo cleanup step
180+
image: alpine:3.9
181+
firstmynotification:
182+
commands:
183+
- echo Notify slack
184+
image: cloudposse/slack-notifier
185+
on_elected:
186+
exec:
187+
commands:
188+
- echo 'Creating an adhoc test environment'
189+
image: alpine:3.9
190+
annotations:
191+
set:
192+
- annotations:
193+
- my_annotation_example1: 10.45
194+
- my_string_annotation: Hello World
195+
entity_type: build
196+
steps:
197+
zz_firstStep:
198+
stage: test
199+
image: alpine
200+
commands:
201+
- echo Hello World First Step
202+
aa_secondStep:
203+
stage: test
204+
image: alpine
205+
commands:
206+
- echo Hello World Second Step`
207+
208+
expectedSpecAttributes := &cfClient.Spec{
209+
Steps: &cfClient.Steps{
210+
Steps: `{"zz_firstStep":{"commands":["echo Hello World First Step"],"image":"alpine","stage":"test"},"aa_secondStep":{"commands":["echo Hello World Second Step"],"image":"alpine","stage":"test"}}`,
211+
},
212+
Stages: &cfClient.Stages{
213+
Stages: `["test"]`,
214+
},
215+
Hooks: &cfClient.Hooks{
216+
Hooks: `{"on_finish":{"steps":{"secondmycleanup":{"commands":["echo echo cleanup step"],"image":"alpine:3.9"},"firstmynotification":{"commands":["echo Notify slack"],"image":"cloudposse/slack-notifier"}}},"on_elected":{"exec":{"commands":["echo 'Creating an adhoc test environment'"],"image":"alpine:3.9"},"annotations":{"set":[{"annotations":[{"my_annotation_example1":10.45},{"my_string_annotation":"Hello World"}],"entity_type":"build"}]}}}`,
217+
},
218+
}
169219

170220
resource.ParallelTest(t, resource.TestCase{
171221
PreCheck: func() { testAccPreCheck(t) },
@@ -178,6 +228,7 @@ func TestAccCodefreshPipeline_OriginalYamlString(t *testing.T) {
178228

179229
testAccCheckCodefreshPipelineExists(resourceName),
180230
resource.TestCheckResourceAttr(resourceName, "original_yaml_string", originalYamlString),
231+
testAccCheckCodefreshPipelineOriginalYamlStringAttributePropagation(resourceName, expectedSpecAttributes),
181232
),
182233
},
183234
{
@@ -426,6 +477,38 @@ func testAccCheckCodefreshPipelineDestroy(s *terraform.State) error {
426477
return nil
427478
}
428479

480+
func testAccCheckCodefreshPipelineOriginalYamlStringAttributePropagation(resource string, spec *cfClient.Spec) resource.TestCheckFunc {
481+
return func(state *terraform.State) error {
482+
483+
rs, ok := state.RootModule().Resources[resource]
484+
if !ok {
485+
return fmt.Errorf("Not found: %s", resource)
486+
}
487+
if rs.Primary.ID == "" {
488+
return fmt.Errorf("No Record ID is set")
489+
}
490+
491+
pipelineID := rs.Primary.ID
492+
493+
apiClient := testAccProvider.Meta().(*cfClient.Client)
494+
pipeline, err := apiClient.GetPipeline(pipelineID)
495+
496+
if !reflect.DeepEqual(pipeline.Spec.Steps, spec.Steps) {
497+
return fmt.Errorf("Expected Step %v. Got %v", spec.Steps, pipeline.Spec.Steps)
498+
}
499+
if !reflect.DeepEqual(pipeline.Spec.Stages, spec.Stages) {
500+
return fmt.Errorf("Expected Stages %v. Got %v", spec.Stages, pipeline.Spec.Stages)
501+
}
502+
if !reflect.DeepEqual(pipeline.Spec.Hooks, spec.Hooks) {
503+
return fmt.Errorf("Expected Hooks %v. Got %v", spec.Hooks, pipeline.Spec.Hooks)
504+
}
505+
if err != nil {
506+
return fmt.Errorf("error fetching pipeline with resource %s. %s", resource, err)
507+
}
508+
return nil
509+
}
510+
}
511+
429512
// CONFIGS
430513
func testAccCodefreshPipelineBasicConfig(rName, repo, path, revision, context string) string {
431514
return fmt.Sprintf(`

0 commit comments

Comments
 (0)