This repository has been archived by the owner on Aug 13, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
/
function.go
144 lines (112 loc) · 4.21 KB
/
function.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
package function
import (
"context"
"fmt"
"io"
"log"
"net/http"
"time"
"arnested.dk/go/dsupdate"
"github.com/containrrr/shoutrrr"
"github.com/dnsimple/dnsimple-go/dnsimple/webhook"
)
// Handle is the entrypoint for the Google Cloud Function.
func Handle(w http.ResponseWriter, r *http.Request) {
// We log to Google Cloud Functions and don't need a timestamp
// since it will be present in the log anyway. On the other
// hand a reference to file and line number would be nice.
log.SetFlags(log.Lshortfile)
w.Header().Set("Content-Type", "text/plain; charset=utf-8")
query := r.URL.Query()
if (query == nil) || !isAuthorized(query.Get("token")) {
http.Error(w, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized)
return
}
if r.Method != http.MethodPost {
http.Error(w, http.StatusText(http.StatusMethodNotAllowed), http.StatusMethodNotAllowed)
return
}
services, _ := notifyConfig()
notify, err := shoutrrr.CreateSender(services.Services...)
if err != nil {
log.Printf("Error creating notification sender(s): %s", err.Error())
}
defer r.Body.Close()
payload, err := io.ReadAll(r.Body)
if err != nil {
log.Printf("Could not parse webhook payload: %s", err.Error())
notify.Send(fmt.Sprintf("Could not parse webhook payload: %s", err.Error()), nil)
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
event, err := webhook.ParseEvent(payload)
if err != nil {
log.Printf("Could not parse webhook name: %s", err.Error())
notify.Send(fmt.Sprintf("Could not parse webhook name: %s", err.Error()), nil)
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
log.Printf("Processing DNSimple event with request ID %q", event.RequestID)
if event.Name != "dnssec.rotation_start" && event.Name != "dnssec.rotation_complete" {
log.Printf("Not a rotation event: %s", event.Name)
// It's OK if this is not a DNSSEC rotation event. We
// send a 200 OK so DNSimple will not retry.
http.Error(w, "Not a rotation event", http.StatusOK)
return
}
dnssecEvent, ok := event.GetData().(*webhook.DNSSECEventData)
if !ok {
log.Printf("Could not parse webhook DNSSEC rotation event: %s", err.Error())
notify.Send(fmt.Sprintf("Could not parse webhook DNSSEC rotation event: %s", err.Error()), nil)
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
config, err := envConfig(dnssecEvent.DelegationSignerRecord.DomainID)
if err != nil {
log.Printf("No Punktum.dk / DNSimple config for %d: %s", dnssecEvent.DelegationSignerRecord.DomainID, err.Error())
// It's OK if there is no configuration. It could be a
// domain not handled by Punktum.dk and/or DNSSEC. We
// send a 200 OK so DNSimple will not retry.
http.Error(w, "Missing Punktum.dk / DNSimple credentials config", http.StatusOK)
return
}
client := dsupdate.Client{
Domain: config.Domain,
UserID: config.UserID,
Password: config.Password,
}
records, err := dsRecords(config.DnsimpleToken, config.Domain)
if err != nil {
log.Printf("Could not get DS records from DNSimple for %q: %s", config.Domain, err.Error())
notify.Send(fmt.Sprintf("Could not get DS records from DNSimple for %q: %s", config.Domain, err.Error()), nil)
http.Error(w, "Could not get DS records from DNSimple", http.StatusInternalServerError)
return
}
// We'll set a 50 second timeout in the deletion using the
// context package.
ctx, cancel := context.WithTimeout(context.Background(), 50*time.Second)
defer cancel()
resp, err := client.Update(ctx, records)
if err != nil {
log.Printf("Could not update DS records for %q: %s", config.Domain, err.Error())
notify.Send(fmt.Sprintf("Could not update DS records for %q: %s", config.Domain, err.Error()), nil)
http.Error(w, "Could not update DS records", http.StatusInternalServerError)
return
}
log.Printf("Successful update of DS records for %q: %s", config.Domain, resp)
errors := notify.Send(fmt.Sprintf("Successful update of DS records for %q: %s", config.Domain, resp), nil)
if countErrors(errors) > 0 {
log.Printf("Could not send Shoutrrr status: %v", err)
}
_, _ = w.Write(resp)
}
// countErrors but not nils.
func countErrors(slice []error) int {
i := 0
for _, elem := range slice {
if elem != nil {
i++
}
}
return i
}