Skip to content

Commit b75d483

Browse files
authored
Fix: Cron Trigger Plan-time Validation; Cron Trigger Updates; Cron Trigger Testing (#100)
## What * Add Cron Trigger plan-time validation * Fix Cron Trigger updating (force replacement of resource on any change, because there is no update verb in the cron trigger API) * Improve Cron Trigger Testing * Misc: remove use of "Hermes" service name in Provider logs. While accurate in the [API docs](https://g.codefresh.io/api/#tag/Triggers-Events), this may cause unnecessary confusion to users of the Terraform provider. ## Why * Updating a Cron Trigger results in duplicates * No plan-time validation on Cron Triggers ## Notes * Closes #99 * Closes #98 * Adding a `minor` label because now we have plan-validation, which can potentially break existing cron trigger configurations in Terraform) Co-authored-by: Yonatan Koren <[email protected]>
1 parent 22bf53a commit b75d483

10 files changed

+274
-22
lines changed

client/hermes_trigger.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ func (client *Client) GetHermesTriggerByEventAndPipeline(event string, pipeline
4545
}
4646
}
4747
if hermesTrigger.Event == "" {
48-
return nil, fmt.Errorf("No trigger found for event %s and pipeline %s", event, pipeline)
48+
return nil, fmt.Errorf("no Trigger found for event: %s, pipeline: %s", event, pipeline)
4949
}
5050

5151
return &hermesTrigger, nil
@@ -73,7 +73,7 @@ func (client *Client) DeleteHermesTriggerByEventAndPipeline(event string, pipeli
7373
_, err := client.RequestAPI(&opts)
7474

7575
if err != nil {
76-
return err
76+
return fmt.Errorf("failed to delete Trigger: \n%v", err)
7777
}
7878

7979
return nil

client/hermes_trigger_event.go

+4-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ func (client *Client) GetHermesTriggerEvent(event string) (*HermesTriggerEvent,
2222

2323
resp, err := client.RequestAPI(&opts)
2424
if err != nil {
25-
return nil, err
25+
return nil, fmt.Errorf("failed to retrieve Trigger Event: \n%v", err)
2626
}
2727

2828
var hermesTriggerEvent HermesTriggerEvent
@@ -49,6 +49,9 @@ func (client *Client) CreateHermesTriggerEvent(event *HermesTriggerEvent) (strin
4949
}
5050

5151
resp, err := client.RequestAPI(&opts)
52+
if err != nil {
53+
return "", fmt.Errorf("failed to create Trigger Event: \n%v", err)
54+
}
5255

5356
var eventString string
5457
err = DecodeResponseInto(resp, &eventString)

client/team.go

-1
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,6 @@ func (client *Client) GetTeamByID(id string) (*Team, error) {
9090
return nil, nil
9191
}
9292

93-
//
9493
func ConvertToNewTeam(team *Team) *NewTeam {
9594
var users []string
9695

codefresh/resource_idp.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ func dataSourceIdps() *schema.Resource {
1313
}
1414
}
1515

16-
//IdpSchema -
16+
// IdpSchema -
1717
func IdpSchema() map[string]*schema.Schema {
1818
return map[string]*schema.Schema{
1919
"_id": {

codefresh/resource_pipeline_cron_trigger.go

+53-4
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,17 @@
11
package codefresh
22

33
import (
4+
"context"
45
"fmt"
56
"regexp"
67
"strings"
78

89
cfClient "github.com/codefresh-io/terraform-provider-codefresh/client"
10+
"github.com/hashicorp/go-cty/cty"
11+
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
12+
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/customdiff"
913
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
14+
"github.com/robfig/cron"
1015
)
1116

1217
func resourcePipelineCronTrigger() *schema.Resource {
@@ -20,7 +25,7 @@ func resourcePipelineCronTrigger() *schema.Resource {
2025
idParts := strings.Split(d.Id(), ",")
2126

2227
if len(idParts) != 2 || idParts[0] == "" || idParts[1] == "" {
23-
return nil, fmt.Errorf("Unexpected format of ID (%q), expected EVENT,PIPELINE_ID", d.Id())
28+
return nil, fmt.Errorf("unexpected format of ID (%q), expected EVENT,PIPELINE_ID", d.Id())
2429
}
2530

2631
event := idParts[0]
@@ -39,12 +44,55 @@ func resourcePipelineCronTrigger() *schema.Resource {
3944
"expression": {
4045
Type: schema.TypeString,
4146
Required: true,
47+
ValidateDiagFunc: func(v interface{}, path cty.Path) (diags diag.Diagnostics) {
48+
expression := v.(string)
49+
50+
// Cron expression requirements: 6 fields, with ability to use descriptors (e.g. @yearly)
51+
parser := cron.NewParser(cron.Second | cron.Minute | cron.Hour | cron.Dom | cron.Month | cron.Dow | cron.Descriptor)
52+
if _, err := parser.Parse(expression); err != nil {
53+
diags = append(diags, diag.Diagnostic{
54+
Severity: diag.Error,
55+
Summary: "Invalid cron expression.",
56+
Detail: fmt.Sprintf("The cron expression %q is invalid: %s", expression, err),
57+
})
58+
}
59+
60+
return
61+
},
4262
},
4363
"message": {
4464
Type: schema.TypeString,
4565
Required: true,
66+
ValidateDiagFunc: func(v interface{}, path cty.Path) (diags diag.Diagnostics) {
67+
message := v.(string)
68+
69+
// https://github.com/codefresh-io/hermes/blob/6d75b347cb8ff471ce970a766b2285788e5e19fe/pkg/backend/dev_compose_types.json#L226
70+
re := regexp.MustCompile(`^[a-zA-Z0-9_+\s-#?.:]{2,128}$`)
71+
72+
if !re.MatchString(message) {
73+
diags = append(diags, diag.Diagnostic{
74+
Severity: diag.Error,
75+
Summary: "Invalid message.",
76+
Detail: fmt.Sprintf("The message %q is invalid (must match %q).", message, re.String()),
77+
})
78+
}
79+
80+
return
81+
},
4682
},
4783
},
84+
// Force new resource if any field changes. This is because the Codefresh API does not support updating cron triggers.
85+
CustomizeDiff: customdiff.All(
86+
customdiff.ForceNewIfChange("pipeline_id", func(ctx context.Context, old, new, meta interface{}) bool {
87+
return true
88+
}),
89+
customdiff.ForceNewIfChange("expression", func(ctx context.Context, old, new, meta interface{}) bool {
90+
return true
91+
}),
92+
customdiff.ForceNewIfChange("message", func(ctx context.Context, old, new, meta interface{}) bool {
93+
return true
94+
}),
95+
),
4896
}
4997
}
5098

@@ -97,7 +145,8 @@ func resourcePipelineCronTriggerRead(d *schema.ResourceData, meta interface{}) e
97145
}
98146

99147
func resourcePipelineCronTriggerUpdate(d *schema.ResourceData, meta interface{}) error {
100-
return resourcePipelineCronTriggerCreate(d, meta)
148+
// see notes in resourcePipelineCronTrigger()
149+
return fmt.Errorf("cron triggers cannot be updated")
101150
}
102151

103152
func resourcePipelineCronTriggerDelete(d *schema.ResourceData, meta interface{}) error {
@@ -107,7 +156,7 @@ func resourcePipelineCronTriggerDelete(d *schema.ResourceData, meta interface{})
107156

108157
err := client.DeleteHermesTriggerByEventAndPipeline(hermesTrigger.Event, hermesTrigger.PipelineID)
109158
if err != nil {
110-
return fmt.Errorf("Failed to delete cron trigger: %s", err)
159+
return fmt.Errorf("failed to delete cron trigger: %v", err)
111160
}
112161

113162
return nil
@@ -122,7 +171,7 @@ func mapPipelineCronTriggerToResource(hermesTrigger *cfClient.HermesTrigger, d *
122171
r := regexp.MustCompile("[^:]+:[^:]+:[^:]+:[^:]+")
123172
eventStringAttributes := strings.Split(hermesTrigger.Event, ":")
124173
if !r.MatchString(hermesTrigger.Event) {
125-
return fmt.Errorf("Event string must be in format 'cron:codefresh:[expression]:[message]:[uid]': %s", hermesTrigger.Event)
174+
return fmt.Errorf("event string must be in format 'cron:codefresh:[expression]:[message]:[uid]': %s", hermesTrigger.Event)
126175
}
127176
d.Set("expression", eventStringAttributes[2])
128177
d.Set("message", eventStringAttributes[3])

0 commit comments

Comments
 (0)