/
main.go
190 lines (160 loc) · 6.15 KB
/
main.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
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
package main
import (
"context"
"fmt"
"io/ioutil"
"log"
"net/http"
"os"
"regexp"
"strconv"
"strings"
"github.com/cloudflare/cloudflare-go"
"github.com/google/go-github/github"
"golang.org/x/oauth2"
)
var (
needsTriageLabel = "needs-triage"
needsInformationLabel = "triage/needs-information"
debugLogDetected = "triage/debug-log-attached"
bugLabel = "kind/bug"
missingLogFileBody = "Thank you for reporting this issue! For maintainers to dig into issues it is required that all issues include the entirety of `TF_LOG=DEBUG` output to be provided. The only parts that should be redacted are your user credentials in the `X-Auth-Key`, `X-Auth-Email` and `Authorization` HTTP headers. Details such as zone or account identifiers are not considered sensitive but can be redacted if you are very cautious. This log file provides additional context from Terraform, the provider and the Cloudflare API that helps in debugging issues. Without it, maintainers are very limited in what they can do and may hamper diagnosis efforts.\n\nThis issue has been marked with `triage/needs-information` and is unlikely to receive maintainer attention until the log file is provided making this a complete bug report."
debugLogProvidedMsg = "Terraform debug log detected :white_check_mark:"
)
func main() {
ctx := context.Background()
if len(os.Args) < 2 {
log.Fatalf("Usage: tf-log-check issue#\n")
}
issueParam := os.Args[1]
issueNumber, err := strconv.Atoi(issueParam)
if err != nil {
log.Fatalf("error parsing issue %q as a number: %s", issueParam, err)
}
owner := os.Getenv("GITHUB_OWNER")
repo := os.Getenv("GITHUB_REPO")
token := os.Getenv("GITHUB_TOKEN")
if owner == "" {
log.Fatalf("GITHUB_OWNER not set")
}
if repo == "" {
log.Fatalf("GITHUB_REPO not set")
}
if token == "" {
log.Fatalf("GITHUB_TOKEN not set")
}
ts := oauth2.StaticTokenSource(
&oauth2.Token{AccessToken: token},
)
tc := oauth2.NewClient(ctx, ts)
client := github.NewClient(tc)
issue, _, err := client.Issues.Get(ctx, owner, repo, issueNumber)
if err != nil {
log.Fatalf("error retrieving issue %s/%s#%d: %s", owner, repo, issueNumber, err)
}
if !hasLabel(issue, bugLabel) {
log.Printf("not a %s report, skipping", bugLabel)
os.Exit(0)
}
comments, _, _ := client.Issues.ListComments(ctx, owner, repo, issueNumber, &github.IssueListCommentsOptions{})
for _, comment := range comments {
if strings.Contains(comment.GetBody(), "This issue has been marked with `triage/needs-information`") {
if hasLabel(issue, debugLogDetected) {
client.Issues.EditComment(ctx, owner, repo, *comment.ID, &github.IssueComment{
Body: cloudflare.StringPtr(debugLogProvidedMsg),
})
os.Exit(0)
}
}
}
var re = regexp.MustCompile(`### Link to debug output\s+(?P<link>.*)\s+### Panic output`)
matches := re.FindStringSubmatch(*issue.Body)
if len(matches) == 0 {
postMissingLogPayload(ctx, client, owner, repo, issueNumber)
os.Exit(0)
}
link := strings.TrimSpace(matches[1])
if strings.Contains(link, "gist.github.com") {
link = link + "/raw"
}
res, err := http.Get(link)
if err != nil {
fmt.Printf("error making http request: %s\n", err)
postMissingLogPayload(ctx, client, owner, repo, issueNumber)
os.Exit(1)
}
if res.StatusCode != 200 {
log.Printf("failed to fetch remote link: %s, status %d", link, res.StatusCode)
postMissingLogPayload(ctx, client, owner, repo, issueNumber)
os.Exit(1)
}
resBody, err := ioutil.ReadAll(res.Body)
if err != nil {
log.Printf("could not ready response: %s", err)
postMissingLogPayload(ctx, client, owner, repo, issueNumber)
os.Exit(1)
}
body := string(resBody)
if !strings.Contains(body, "Terraform version") &&
!strings.Contains(body, "Go runtime version") &&
!strings.Contains(body, "CLI args") &&
!strings.Contains(body, "created provider logger") &&
!strings.Contains(body, "CLI command args") &&
!strings.Contains(body, "provider: plugin process exited") {
postMissingLogPayload(ctx, client, owner, repo, issueNumber)
} else {
if len(comments) == 0 {
client.Issues.CreateComment(ctx, owner, repo, issueNumber, &github.IssueComment{
Body: cloudflare.StringPtr(debugLogProvidedMsg),
})
} else {
for _, comment := range comments {
if strings.Contains(comment.GetBody(), "This issue has been marked with `triage/needs-information`") {
client.Issues.EditComment(ctx, owner, repo, *comment.ID, &github.IssueComment{
Body: cloudflare.StringPtr(debugLogProvidedMsg),
})
}
}
}
_, err = client.Issues.RemoveLabelForIssue(ctx, owner, repo, issueNumber, needsInformationLabel)
if err != nil {
log.Printf("error removing label for issue %s/%s#%d: %s", owner, repo, issueNumber, err)
}
_, _, err = client.Issues.AddLabelsToIssue(ctx, owner, repo, issueNumber, []string{debugLogDetected})
if err != nil {
log.Printf("error adding label for issue %s/%s#%d: %s", owner, repo, issueNumber, err)
}
}
os.Exit(0)
}
func hasLabel(issue *github.Issue, label string) bool {
for _, l := range issue.Labels {
if *l.Name == label {
return true
}
}
return false
}
func postMissingLogPayload(ctx context.Context, client *github.Client, owner, repo string, issueNumber int) {
comments, _, _ := client.Issues.ListComments(ctx, owner, repo, issueNumber, &github.IssueListCommentsOptions{})
for _, comment := range comments {
if strings.Contains(comment.GetBody(), "This issue has been marked with `triage/needs-information`") {
log.Printf("request for debug log already exists, exiting")
os.Exit(0)
}
}
_, _, err := client.Issues.CreateComment(ctx, owner, repo, issueNumber, &github.IssueComment{
Body: &missingLogFileBody,
})
if err != nil {
log.Printf("failed to create comment for issue %s/%s#%d: %s", owner, repo, issueNumber, err)
}
_, _, err = client.Issues.AddLabelsToIssue(ctx, owner, repo, issueNumber, []string{needsInformationLabel})
if err != nil {
log.Printf("error adding label for issue %s/%s#%d: %s", owner, repo, issueNumber, err)
}
_, err = client.Issues.RemoveLabelForIssue(ctx, owner, repo, issueNumber, needsTriageLabel)
if err != nil {
log.Printf("error removing label for issue %s/%s#%d: %s", owner, repo, issueNumber, err)
}
}