Skip to content

Commit 8bb6613

Browse files
authored
tfrender: properly correlate schedule and team on import (#41)
* tfrender: properly correlate schedule and team on import * pager/pagerduty: fix link + annotate for schedule, escalation policy
1 parent dc7d9c9 commit 8bb6613

File tree

14 files changed

+137
-33
lines changed

14 files changed

+137
-33
lines changed

cmd/import.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,10 @@ func importTeams(ctx context.Context, provider pager.Pager, fh *firehydrant.Clie
245245
return fmt.Errorf("linking team '%s' to FireHydrant: %w", t.Name, err)
246246
}
247247
}
248+
249+
if err := store.UseQueries(ctx).DeleteExtTeamUnimported(ctx); err != nil {
250+
return fmt.Errorf("unable to delete unimported teams: %w", err)
251+
}
248252
return nil
249253
}
250254

docs/pagerduty.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,4 @@ Afterwards, run `signals-migrator import` and follow the prompts.
1616
## Known limitations
1717

1818
- While we support importing "PagerDuty Service" as "FireHydrant Team", we still require the Teams API to be accessible. If your account does not have access to the Teams API, please see [#27](https://github.com/firehydrant/signals-migrator/issues/27) and let us know what error you encountered.
19+
- Inactive users will have "warning" lines.

pager/pagerduty.go

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package pager
22

33
import (
44
"context"
5+
"database/sql"
56
"fmt"
67
"slices"
78
"strconv"
@@ -485,16 +486,55 @@ func (p *PagerDuty) LoadEscalationPolicies(ctx context.Context) error {
485486
}
486487

487488
func (p *PagerDuty) saveEscalationPolicyToDB(ctx context.Context, policy pagerduty.EscalationPolicy) error {
489+
annotations := fmt.Sprintf("[PagerDuty]\n %s %s", policy.Name, policy.HTMLURL)
490+
if len(policy.Teams) > 0 {
491+
annotations += "\n[Teams]"
492+
for _, team := range policy.Teams {
493+
annotations += fmt.Sprintf("\n - %s", team.ID)
494+
}
495+
}
496+
if len(policy.Services) > 0 {
497+
annotations += "\n[Services]"
498+
for _, service := range policy.Services {
499+
annotations += fmt.Sprintf("\n - %s %s %s", service.ID, service.HTMLURL, service.Summary)
500+
}
501+
}
488502
ep := store.InsertExtEscalationPolicyParams{
489503
ID: policy.ID,
490504
Name: policy.Name,
491505
Description: policy.Description,
492506
RepeatLimit: int64(policy.NumLoops),
507+
Annotations: annotations,
493508
}
509+
494510
if err := store.UseQueries(ctx).InsertExtEscalationPolicy(ctx, ep); err != nil {
495511
return fmt.Errorf("saving escalation policy %s (%s): %w", ep.Name, ep.ID, err)
496512
}
497513

514+
// We do our best to match first one from the list of teams / services. Since the full list is annotated in comments,
515+
// users can duplicate as needed. While it's possible for us to fan out and replicate, it's likely not the desired behavior
516+
// as in such scenario, user should really be merging the teams instead of duplicating escalation policies across teams.
517+
if pdTeamInterface == "service" {
518+
for _, service := range policy.Services {
519+
if err := store.UseQueries(ctx).UpdateExtEscalationPolicyTeam(ctx, store.UpdateExtEscalationPolicyTeamParams{
520+
ID: ep.ID,
521+
TeamID: sql.NullString{Valid: true, String: service.ID},
522+
}); err == nil {
523+
break
524+
}
525+
}
526+
} else {
527+
for _, team := range policy.Teams {
528+
if err := store.UseQueries(ctx).UpdateExtEscalationPolicyTeam(ctx, store.UpdateExtEscalationPolicyTeamParams{
529+
ID: ep.ID,
530+
TeamID: sql.NullString{Valid: true, String: team.ID},
531+
}); err == nil {
532+
break
533+
}
534+
}
535+
536+
}
537+
498538
// PagerDuty's Escalation Rule is equivalent to FireHydrant Escalation Policy Step.
499539
for i, rule := range policy.EscalationRules {
500540
if err := p.saveEscalationPolicyStepToDB(ctx, ep.ID, rule, int64(i)); err != nil {

pager/testdata/TestOpsgenie/LoadEscalationPolicies.golden.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
},
1515
"handoff_target_type": "",
1616
"handoff_target_id": "",
17+
"annotations": "",
1718
"to_import": 0
1819
}
1920
]

pager/testdata/TestPagerDuty/LoadEscalationPolicies.golden.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
},
1616
"handoff_target_type": "",
1717
"handoff_target_id": "",
18+
"annotations": "[PagerDuty]\n Endeavour https://pdt-apidocs.pagerduty.com/escalation_policies/P6F7EI2",
1819
"to_import": 0
1920
}
2021
],

store/models.go

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

store/queries.sql

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,9 @@ WHERE id IN (
7878
-- name: InsertExtTeamGroup :exec
7979
INSERT INTO ext_team_groups (group_team_id, member_team_id) VALUES (?, ?);
8080

81+
-- name: DeleteExtTeamUnimported :exec
82+
DELETE FROM ext_teams WHERE to_import = 0;
83+
8184
-- name: LinkExtUser :exec
8285
UPDATE ext_users SET fh_user_id = ? WHERE id = ?;
8386

@@ -150,8 +153,11 @@ VALUES (?, ?);
150153
SELECT * FROM ext_escalation_policies;
151154

152155
-- name: InsertExtEscalationPolicy :exec
153-
INSERT INTO ext_escalation_policies (id, name, description, team_id, repeat_interval, repeat_limit, handoff_target_type, handoff_target_id, to_import)
154-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?);
156+
INSERT INTO ext_escalation_policies (id, name, description, team_id, repeat_interval, repeat_limit, handoff_target_type, handoff_target_id, annotations, to_import)
157+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?);
158+
159+
-- name: UpdateExtEscalationPolicyTeam :exec
160+
UPDATE ext_escalation_policies SET team_id = ? WHERE id = ?;
155161

156162
-- name: MarkAllExtEscalationPolicyToImport :exec
157163
UPDATE ext_escalation_policies SET to_import = 1;

store/queries.sql.go

Lines changed: 29 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

store/schema.sql

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@ CREATE TABLE IF NOT EXISTS ext_escalation_policies (
103103
repeat_interval TEXT,
104104
handoff_target_type TEXT NOT NULL,
105105
handoff_target_id TEXT NOT NULL,
106+
annotations TEXT NOT NULL DEFAULT '',
106107
to_import INTEGER NOT NULL DEFAULT 0
107108
) STRICT;
108109

tfrender/testdata/TestRenderOpsgenie/EscalationPolicy_seed.sql

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ INSERT INTO ext_schedule_members VALUES('8ab5a183-8ef5-47db-9de0-56663cfbae7c-9b
5656
INSERT INTO ext_schedule_members VALUES('8ab5a183-8ef5-47db-9de0-56663cfbae7c-9b488cc6-efa0-44f3-a432-b913acab9147','e94e17aa-418c-44f7-8e47-1eaebf6b5343');
5757
INSERT INTO ext_schedule_members VALUES('8ab5a183-8ef5-47db-9de0-56663cfbae7c-9b488cc6-efa0-44f3-a432-b913acab9147','e0a51be7-3c7e-407f-8678-292ab421f55f');
5858

59-
INSERT INTO ext_escalation_policies VALUES('880ec24e-58db-441b-9681-2cb527bd24b2','AJ Team_escalation','','946bf740-0497-4d5d-b31f-23a6e55a2719',0,NULL,'','',1);
59+
INSERT INTO ext_escalation_policies VALUES('880ec24e-58db-441b-9681-2cb527bd24b2','AJ Team_escalation','','946bf740-0497-4d5d-b31f-23a6e55a2719',0,NULL,'','','',1);
6060

6161
INSERT INTO ext_escalation_policy_steps VALUES('880ec24e-58db-441b-9681-2cb527bd24b2-0','880ec24e-58db-441b-9681-2cb527bd24b2',0,'PT1M');
6262

tfrender/testdata/TestRenderPagerDuty/EscalationPolicy.golden.tf

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ resource "firehydrant_on_call_schedule" "cowboy_coders_atalice_bob_is_always_on_
3434
team_id = firehydrant_team.cowboy_coders.id
3535
time_zone = "America/Los_Angeles"
3636

37+
# [PagerDuty] team-rocket https://pdt-apidocs.pagerduty.com/service-directory/PV9JOXL
38+
3739
member_ids = [data.firehydrant_user.alice_bob.id]
3840

3941
strategy {
@@ -51,7 +53,7 @@ resource "firehydrant_escalation_policy" "atalice_bob_test_service_ep" {
5153

5254
targets {
5355
type = "OnCallSchedule"
54-
id = firehydrant_on_call_schedule.atalice_bob_is_always_on_call_layer_1.id
56+
id = firehydrant_on_call_schedule.cowboy_coders_atalice_bob_is_always_on_call_layer_1.id
5557
}
5658
}
5759

tfrender/testdata/TestRenderPagerDuty/EscalationPolicy_seed.sql

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@ INSERT INTO ext_schedule_teams VALUES('PGR96WL-PR3J6XJ','PV9JOXL');
1818

1919
INSERT INTO ext_schedule_members VALUES('PGR96WL-PR3J6XJ','PRXEEQ8');
2020

21-
INSERT INTO ext_escalation_policies VALUES('P2D2WR1','🐴 @alice.bob Test Service-ep','',NULL,0,NULL,'','',1);
22-
INSERT INTO ext_escalation_policies VALUES('PS6ITO0','🐴 Notify @alice.bob','',NULL,0,NULL,'','',1);
21+
INSERT INTO ext_escalation_policies VALUES('P2D2WR1','🐴 @alice.bob Test Service-ep','',NULL,0,NULL,'','','',1);
22+
INSERT INTO ext_escalation_policies VALUES('PS6ITO0','🐴 Notify @alice.bob','',NULL,0,NULL,'','','',1);
2323

2424
INSERT INTO ext_escalation_policy_steps VALUES('PKQDFZH','P2D2WR1',0,'PT30M');
2525
INSERT INTO ext_escalation_policy_steps VALUES('P08T67P','PS6ITO0',0,'PT30M');

tfrender/testdata/TestRenderPagerDuty/TeamWithSchedules.golden.tf

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,8 @@ resource "firehydrant_on_call_schedule" "aaaa_ipv6_migration_strategy_jen_primar
7676
time_zone = "America/Los_Angeles"
7777
start_time = "2024-04-10T20:39:29-07:00"
7878

79+
# [PagerDuty] Jen https://pdt-apidocs.pagerduty.com/service-directory/PT54U20
80+
7981
member_ids = [data.firehydrant_user.kiran.id]
8082

8183
strategy {
@@ -98,6 +100,8 @@ resource "firehydrant_on_call_schedule" "aaaa_ipv6_migration_strategy_jen_primar
98100
time_zone = "America/Los_Angeles"
99101
start_time = "2024-04-10T20:39:29-07:00"
100102

103+
# [PagerDuty] Jen https://pdt-apidocs.pagerduty.com/service-directory/PT54U20
104+
101105
member_ids = [data.firehydrant_user.wong.id, data.firehydrant_user.local.id]
102106

103107
strategy {
@@ -161,6 +165,8 @@ resource "firehydrant_on_call_schedule" "dunder_mifflin_scranton_jack_on_call_sc
161165
team_id = firehydrant_team.dunder_mifflin_scranton.id
162166
time_zone = "America/Los_Angeles"
163167

168+
# [PagerDuty] Jack Team https://pdt-apidocs.pagerduty.com/service-directory/PD2F80U
169+
164170
member_ids = [data.firehydrant_user.jack.id]
165171

166172
strategy {

tfrender/tfrender.go

Lines changed: 39 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,7 @@ func (r *TFRender) Write(ctx context.Context) error {
108108
if _, err := f.Write(r.f.Bytes()); err != nil {
109109
return fmt.Errorf("writing file: %w", err)
110110
}
111+
console.Successf("Terraform file has been written to %s\n", r.Filepath())
111112

112113
return nil
113114
}
@@ -129,20 +130,23 @@ func (r *TFRender) ResourceFireHydrantEscalationPolicy(ctx context.Context) erro
129130
b.SetAttributeValue("description", cty.StringVal(p.Description))
130131
}
131132

132-
var currentTeam *store.LinkedTeam
133133
if p.TeamID.Valid && p.TeamID.String != "" {
134134
t, err := store.UseQueries(ctx).GetTeamByExtID(ctx, p.TeamID.String)
135135
if err != nil {
136136
return fmt.Errorf("querying team '%s' for policy '%s': %w", p.TeamID.String, p.Name, err)
137137
}
138-
currentTeam = &t
139138
b.SetAttributeTraversal("team_id", hcl.Traversal{
140139
hcl.TraverseRoot{Name: "firehydrant_team"},
141140
hcl.TraverseAttr{Name: t.TFSlug()},
142141
hcl.TraverseAttr{Name: "id"},
143142
})
144143
}
145144

145+
if p.Annotations != "" {
146+
b.AppendNewline()
147+
r.AppendComment(b, p.Annotations)
148+
}
149+
146150
steps, err := store.UseQueries(ctx).ListExtEscalationPolicySteps(ctx, p.ID)
147151
if err != nil {
148152
return fmt.Errorf("querying steps for policy '%s': %w", p.Name, err)
@@ -181,15 +185,18 @@ func (r *TFRender) ResourceFireHydrantEscalationPolicy(ctx context.Context) erro
181185
continue
182186
}
183187
for _, schedule := range schedules {
184-
slug := schedule.TFSlug()
185-
if currentTeam != nil {
186-
slug = fmt.Sprintf("%s_%s", currentTeam.TFSlug(), slug)
188+
teams, err := store.UseQueries(ctx).ListTeamsByExtScheduleID(ctx, schedule.ID)
189+
if err != nil {
190+
return fmt.Errorf("querying teams for schedule '%s': %w", schedule.Name, err)
191+
}
192+
for _, team := range teams {
193+
slug := fmt.Sprintf("%s_%s", team.TFSlug(), schedule.TFSlug())
194+
idTraversals = append(idTraversals, hcl.Traversal{ //nolint:staticcheck // See "safeguard" below
195+
hcl.TraverseRoot{Name: "firehydrant_on_call_schedule"},
196+
hcl.TraverseAttr{Name: slug},
197+
hcl.TraverseAttr{Name: "id"},
198+
})
187199
}
188-
idTraversals = append(idTraversals, hcl.Traversal{ //nolint:staticcheck // See "safeguard" below
189-
hcl.TraverseRoot{Name: "firehydrant_on_call_schedule"},
190-
hcl.TraverseAttr{Name: slug},
191-
hcl.TraverseAttr{Name: "id"},
192-
})
193200
}
194201
default:
195202
console.Errorf("unknown target type '%s' for step %d of %s\n", t.TargetType, s.Position, p.Name)
@@ -245,6 +252,11 @@ func (r *TFRender) ResourceFireHydrantOnCallSchedule(ctx context.Context) error
245252
b.SetAttributeValue("start_time", cty.StringVal(s.StartTime))
246253
}
247254

255+
if t.Annotations != "" {
256+
b.AppendNewline()
257+
r.AppendComment(b, t.Annotations)
258+
}
259+
248260
members, err := store.UseQueries(ctx).ListFhMembersByExtScheduleID(ctx, s.ID)
249261
if err != nil {
250262
return fmt.Errorf("querying members for schedule '%s': %w", s.Name, err)
@@ -414,19 +426,22 @@ func (r *TFRender) AppendComment(b *hclwrite.Body, comment string) {
414426
if str == "" {
415427
return
416428
}
417-
// If the body is multi-line, prefix the newline with comment tag.
418-
str = strings.ReplaceAll(str, "\n", "\n# ")
419-
// Then make sure to prepend first line with comment tag as well,
420-
// and end with newline.
421-
str = fmt.Sprintf("# %s\n", str)
422-
b.AppendUnstructuredTokens(
423-
hclwrite.Tokens{
424-
&hclwrite.Token{
425-
Type: hclsyntax.TokenComment,
426-
Bytes: []byte(str),
427-
428-
SpacesBefore: 2,
429+
430+
lines := strings.Split(str, "\n")
431+
for _, line := range lines {
432+
if line == "" {
433+
continue
434+
}
435+
comment := fmt.Sprintf("# %s\n", line)
436+
b.AppendUnstructuredTokens(
437+
hclwrite.Tokens{
438+
&hclwrite.Token{
439+
Type: hclsyntax.TokenComment,
440+
Bytes: []byte(comment),
441+
442+
SpacesBefore: 2,
443+
},
429444
},
430-
},
431-
)
445+
)
446+
}
432447
}

0 commit comments

Comments
 (0)