Compare commits

...

40 commits
v1.3 ... master

Author SHA1 Message Date
bd93487c2d
fix: install ca certs
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2022-08-18 18:14:44 +05:30
eaa54e466c
fix; cp bin to /rageshake/
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
rageshake stores logs and screenshot in the same directory as the binary.
Copying binary to /bin/ will, well, mess /bin/ up. Not good.
2022-08-18 17:32:57 +05:30
a536481248
fix: switch to using debian base images
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2022-08-18 17:15:22 +05:30
befb3780cc
fix: golang docker img
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2022-08-18 15:47:37 +05:30
69e36736ca
debug: publish imgs without testing
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2022-08-18 15:46:28 +05:30
5ab35a58a9
feat: build only x864 linux docker img
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
2022-08-18 15:44:52 +05:30
7b72715bda
feat: add woodpecker badge
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
2022-08-18 14:29:08 +05:30
6975c3faa1
feat: publish docker img from CI
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
2022-08-18 14:28:10 +05:30
Michael Kaye
85b721070a 1.7 2022-04-14 14:15:31 +01:00
Michael Kaye
02237888c9
Merge pull request #54 from matrix-org/michaelk/persist_unique_id_in_shakes
Persist prefix as a unique id in rageshakes
2022-04-14 14:13:08 +01:00
Michael Kaye
2ac7af0c18 Move comments onto their own line 2022-04-13 13:31:54 +01:00
Michael Kaye
79454de54f Add a comment about each part of the payload. 2022-04-13 13:29:19 +01:00
Michael Kaye
62e52e880d Rename parsedPayload into just "payload", it's not just about parsing any more. 2022-04-13 13:21:46 +01:00
Michael Kaye
8cdcc4bba8 Add changelog entry for unique ID 2022-04-06 15:59:09 +01:00
Michael Kaye
7cb7309097 Add ID field containing the prefix used to create the listing.
This listing needs to be unique for the rageshake to be stored to disk,
so can be relied upon in future to be unique(enough) for other services
working with the rageshake.
2022-04-01 14:17:59 +01:00
Michael Kaye
1d008a0aad Run go fmt over the codebase 2022-04-01 14:17:48 +01:00
Michael Kaye
eb19aca921 1.6 2022-02-22 15:54:18 +00:00
Michael Kaye
b01b5a5863
Merge pull request #53 from matrix-org/michaelk/tarball_of_files
Serve a tarball containing the contents of a given directory.
2022-02-22 15:51:37 +00:00
Michael Kaye
1137cb2c04 Correct the use of return 2022-02-22 15:40:46 +00:00
Michael Kaye
ba8725a3aa Factor out serveDirectory into it's own method 2022-02-17 11:47:21 +00:00
Michael Kaye
77e66be90f Guard against including directories in tarball, handle http errors better, add documentation. 2022-02-17 11:38:36 +00:00
Michael Kaye
714cc44807 Update documentation 2022-02-17 11:37:05 +00:00
Michael Kaye
7b2d70a3c9 Fix last minute refactor fial. 2022-02-09 16:31:56 +00:00
Michael Kaye
a2caf1c546 More whitespace 2022-02-09 16:12:05 +00:00
Michael Kaye
f318399536 changelog.d 2022-02-09 16:11:37 +00:00
Michael Kaye
2a4434281c Fix whitespace 2022-02-09 16:10:55 +00:00
Michael Kaye
78060556a2 Serve a tarball containing the contents of a given directory.
This will make it easier to get all logs for a given bug; preventing users
needing to run scripts to download all files.

 - we cannot make the link exist in the directory listing as there are scripts
   that automate downloads which would pick this up.

 - Unsure if "?format=tar.gz" is the right option to enable this; I couldn't think
   of something easy to do but hard to not get picked up by existing automation, and
   wouldn't conflict with existing filenames.
2022-02-09 16:06:44 +00:00
Michael Kaye
b7ffb434e9 1.5 2022-02-08 15:03:54 +00:00
Michael Kaye
cc2374e431
Merge pull request #52 from matrix-org/michaelk/allow_json_files
Allow upload of files with a .json postfix.
2022-02-08 15:02:09 +00:00
Michael Kaye
a7724b7bc8 Serve .json files as application/json. 2022-02-08 14:20:45 +00:00
Michael Kaye
53e8947cb9 Changelog.d 2022-02-08 11:01:12 +00:00
Michael Kaye
18c8a83173 Allow upload of files with a .json postfix. 2022-02-08 10:58:21 +00:00
Michael Kaye
589e9254e7 1.4 2022-02-01 14:03:48 +00:00
Michael Kaye
a8c57f2eb9
Merge pull request #50 from matrix-org/michaelk/proxy_rageshake_requests
A generic webhook for notifications about reports.
2022-02-01 14:02:14 +00:00
Michael Kaye
095b55e640 Only create payload struct once.
(But keep making bytes.Buffer multiple times as these is read from by each http request)
2022-02-01 13:59:01 +00:00
Michael Kaye
4e3eeec92c genericWebhookURLs as a list rather than a single endpoint. 2022-02-01 13:26:29 +00:00
Michael Kaye
8e001408d8 Reduce cyclometric complexity 2022-01-31 16:24:25 +00:00
Michael Kaye
d8a5acd2e2 Makefile was for personal testing, should not have been committed. 2022-01-31 16:07:17 +00:00
Michael Kaye
adc43f50ec Changelog.d 2022-01-31 15:30:37 +00:00
Michael Kaye
81865b0193 Initial attempt at a generic webhook for reports.
Background submission to a notification server that doesn't block rageshake submission.
2022-01-31 15:25:17 +00:00
11 changed files with 362 additions and 52 deletions

15
.woodpecker.yml Normal file
View file

@ -0,0 +1,15 @@
pipeline:
build-test:
image: golang
commands:
- go build
- go test
publish:
image: plugins/docker
settings:
username: realaravinth
password:
from_secret: DOCKER_TOKEN
repo: realaravinth/rageshake
tags: latest

View file

@ -1,3 +1,39 @@
1.7 (2022-04-14)
================
Features
--------
- Pass the prefix as a unique ID for the rageshake to the generic webhook mechanism. ([\#54](https://github.com/matrix-org/rageshake/issues/54))
1.6 (2022-02-22)
================
Features
--------
- Provide ?format=tar.gz option on directory listings to download tarball. ([\#53](https://github.com/matrix-org/rageshake/issues/53))
1.5 (2022-02-08)
================
Features
--------
- Allow upload of Files with a .json postfix. ([\#52](https://github.com/matrix-org/rageshake/issues/52))
1.4 (2022-02-01)
================
Features
--------
- Allow forwarding of a request to a webhook endpoint. ([\#50](https://github.com/matrix-org/rageshake/issues/50))
1.3 (2022-01-25)
================

View file

@ -1,27 +1,23 @@
ARG GO_VERSION=1.17
ARG DEBIAN_VERSION=11
ARG DEBIAN_VERSION_NAME=bullseye
## Build stage ##
FROM --platform=${BUILDPLATFORM} docker.io/library/golang:${GO_VERSION}-${DEBIAN_VERSION_NAME} AS builder
FROM golang as builder
WORKDIR /build
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=${TARGETOS} GOARCH=${TARGETARCH} go build -o rageshake
RUN go build -o rageshake
## Runtime stage, debug variant ##
FROM --platform=${TARGETPLATFORM} gcr.io/distroless/static-debian${DEBIAN_VERSION}:debug-nonroot AS debug
COPY --from=builder /build/rageshake /rageshake
FROM debian:bullseye as debug
COPY --from=builder /build/rageshake /rageshake/
WORKDIR /
EXPOSE 9110
ENTRYPOINT ["/rageshake"]
ENTRYPOINT ["/rageshake/rageshake"]
## Runtime stage ##
FROM --platform=${TARGETPLATFORM} gcr.io/distroless/static-debian${DEBIAN_VERSION}:nonroot
COPY --from=builder /build/rageshake /rageshake
FROM debian:bullseye as rageshake
LABEL org.opencontainers.image.source https://git.batsense.net/mystiq/rageshake
RUN apt-get update && apt-get install -y ca-certificates
WORKDIR /
COPY --from=builder /build/rageshake /rageshake/
EXPOSE 9110
ENTRYPOINT ["/rageshake"]
ENTRYPOINT ["/rageshake/rageshake"]

View file

@ -1,3 +1,4 @@
WOODPECKER: [![status-badge](https://ci.batsense.net/api/badges/mystiq/rageshake/status.svg)](https://ci.batsense.net/mystiq/rageshake)
# rageshake [![Build status](https://badge.buildkite.com/76a4362a20b12dcd589f9308a905ffcc537278b9c363c0b5f1.svg?branch=master)](https://buildkite.com/matrix-dot-org/rageshake)
Web service which collects and serves bug reports.
@ -28,6 +29,8 @@ Serves submitted bug reports. Protected by basic HTTP auth using the
username/password provided in the environment. A browsable list, collated by
report submission date and time.
A whole directory can be downloaded as a tarball by appending the parameter `?format=tar.gz` to the end of the URL path
### POST `/api/submit`
Submission endpoint: this is where applications should send their reports.
@ -105,3 +108,13 @@ You can get notifications when a new rageshake arrives on the server.
Currently this tool supports pushing notifications as GitHub issues in a repo,
through a Slack webhook or by email, cf sample config file for how to
configure them.
### Generic Webhook Notifications
You can receive a webhook notifications when a new rageshake arrives on the server.
These requests contain all the parsed metadata, and links to the uploaded files, and any github/gitlab
issues created.
Details on the request and expected response are [available](docs/generic\_webhook.md).

40
docs/generic_webhook.md Normal file
View file

@ -0,0 +1,40 @@
## Generic webhook request
If the configuration option `generic_webhook_urls` is set, then an asynchronous request to
each endpoint listed will be sent in parallel, after the incoming request is parsed and the
files are uploaded.
The webhook is designed for notification or other tracking services, and does not contain
the original log files uploaded.
(If you want the original log files, we suggest to implement the rageshake interface itself).
A sample JSON body is as follows:
```
{
'user_text': 'test\r\n\r\nIssue: No issue link given',
'app': 'element-web',
'data': {
'User-Agent': 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:96.0) Gecko/20100101 Firefox/96.0',
'Version': '0f15ba34cdf5-react-0f15ba34cdf5-js-0f15ba34cdf5',
...
'user_id': '@michaelgoesforawalk:matrix.org'},
'labels': None,
'logs': [
'logs-0000.log.gz',
'logs-0001.log.gz',
'logs-0002.log.gz',
],
'logErrors': None,
'files': [
'screenshot.png'
],
'fileErrors': None,
'report_url': 'https://github.com/your-org/your-repo/issues/1251',
'listing_url': 'http://your-rageshake-server/api/listing/2022-01-25/154742-OOXBVGIX'
}
```
The log and other files can be individually downloaded by concatenating the `listing_url` and the `logs` or `files` name.
You may need to provide a HTTP basic auth user/pass if configured on your rageshake server.

View file

@ -17,6 +17,7 @@ limitations under the License.
package main
import (
"archive/tar"
"compress/gzip"
"io"
"log"
@ -79,10 +80,9 @@ func serveFile(w http.ResponseWriter, r *http.Request, path string) {
// for anti-XSS belt-and-braces, set a very restrictive CSP
w.Header().Set("Content-Security-Policy", "default-src: none")
// if it's a directory, serve a listing
// if it's a directory, serve a listing or a tarball
if d.IsDir() {
log.Println("Serving", path)
http.ServeFile(w, r, path)
serveDirectory(w, r, path)
return
}
@ -119,9 +119,122 @@ func extensionToMimeType(path string) string {
return "image/jpeg"
}
if strings.HasSuffix(path, ".json") {
return "application/json"
}
return "application/octet-stream"
}
// Chooses to serve either a directory listing or tarball based on the 'format' parameter.
func serveDirectory(w http.ResponseWriter, r *http.Request, path string) {
format, _ := r.URL.Query()["format"]
if len(format) == 1 && format[0] == "tar.gz" {
log.Println("Serving tarball of", path)
err := serveTarball(w, r, path)
if err != nil {
msg, code := toHTTPError(err)
http.Error(w, msg, code)
log.Println("Error", err)
}
return
}
log.Println("Serving directory listing of", path)
http.ServeFile(w, r, path)
}
// Streams a dynamically created tar.gz file with the contents of the given directory
// Will serve a partial, corrupted response if there is a error partway through the
// operation as we stream the response.
//
// The resultant tarball will contain a single directory containing all the files
// so it can unpack cleanly without overwriting other files.
//
// Errors are only returned if generated before the tarball has started being
// written to the ResponseWriter
func serveTarball(w http.ResponseWriter, r *http.Request, dir string) error {
directory, err := os.Open(dir)
if err != nil {
return err
}
// Creates a "disposition filename"
// Take a URL.path like `/2022-01-10/184843-BZZXEGYH/`
// and removes leading and trailing `/` and replaces internal `/` with `_`
// to form a suitable filename for use in the content-disposition header
// dfilename would turn into `2022-01-10_184843-BZZXEGYH`
dfilename := strings.Trim(r.URL.Path, "/")
dfilename = strings.Replace(dfilename, "/", "_", -1)
// There is no application/tgz or similar; return a gzip file as best option.
// This tends to trigger archive type tools, which will then use the filename to
// identify the contents correctly.
w.Header().Set("Content-Type", "application/gzip")
w.Header().Set("Content-Disposition", "attachment; filename="+dfilename+".tar.gz")
files, err := directory.Readdir(-1)
if err != nil {
return err
}
gzip := gzip.NewWriter(w)
defer gzip.Close()
targz := tar.NewWriter(gzip)
defer targz.Close()
for _, file := range files {
if file.IsDir() {
// We avoid including nested directories
// This will result in requests for directories with only directories in
// to return an empty tarball instead of recursively including directories.
// This helps the server remain performant as a download of 'everything' would be slow
continue
}
path := dir + "/" + file.Name()
// We use the existing disposition filename to create a base directory structure for the files
// so when they are unpacked, they are grouped in a unique folder on disk
err := addToArchive(targz, dfilename, path)
if err != nil {
// From this point we assume that data may have been sent to the client already.
// We therefore do not http.Error() after this point, instead closing the stream and
// allowing the client to deal with a partial file as if there was a network issue.
log.Println("Error streaming tarball", err)
return nil
}
}
return nil
}
// Add a single file into the archive.
func addToArchive(targz *tar.Writer, dfilename string, filename string) error {
file, err := os.Open(filename)
if err != nil {
return err
}
defer file.Close()
info, err := file.Stat()
if err != nil {
return err
}
header, err := tar.FileInfoHeader(info, info.Name())
if err != nil {
return err
}
header.Name = dfilename + "/" + info.Name()
err = targz.WriteHeader(header)
if err != nil {
return err
}
_, err = io.Copy(targz, file)
if err != nil {
return err
}
return nil
}
func serveGzippedFile(w http.ResponseWriter, r *http.Request, path string, size int64) {
w.Header().Set("Content-Type", "text/plain; charset=utf-8")

17
main.go
View file

@ -71,6 +71,8 @@ type config struct {
SMTPUsername string `yaml:"smtp_username"`
SMTPPassword string `yaml:"smtp_password"`
GenericWebhookURLs []string `yaml:"generic_webhook_urls"`
}
func basicAuth(handler http.Handler, username, password, realm string) http.Handler {
@ -134,6 +136,8 @@ func main() {
log.Fatal("Email address(es) specified but no smtp_server configured. Wrong configuration, aborting...")
}
genericWebhookClient := configureGenericWebhookClient(cfg)
apiPrefix := cfg.APIPrefix
if apiPrefix == "" {
_, port, err := net.SplitHostPort(*bindAddr)
@ -148,7 +152,7 @@ func main() {
log.Printf("Using %s/listing as public URI", apiPrefix)
rand.Seed(time.Now().UnixNano())
http.Handle("/api/submit", &submitServer{ghClient, glClient, apiPrefix, slack, cfg})
http.Handle("/api/submit", &submitServer{ghClient, glClient, apiPrefix, slack, genericWebhookClient, cfg})
// Make sure bugs directory exists
_ = os.Mkdir("bugs", os.ModePerm)
@ -176,6 +180,17 @@ func main() {
log.Fatal(http.ListenAndServe(*bindAddr, nil))
}
func configureGenericWebhookClient(cfg *config) *http.Client {
if len(cfg.GenericWebhookURLs) == 0 {
fmt.Println("No generic_webhook_urls configured.")
return nil
}
fmt.Println("Will forward metadata of all requests to ", cfg.GenericWebhookURLs)
return &http.Client{
Timeout: time.Second * 300,
}
}
func loadConfig(configPath string) (*config, error) {
contents, err := ioutil.ReadFile(configPath)
if err != nil {

BIN
rageshake Executable file

Binary file not shown.

View file

@ -50,3 +50,9 @@ email_from: Rageshake <rageshake@matrix.org>
smtp_server: localhost:25
smtp_username: myemailuser
smtp_password: myemailpass
# a list of webhook URLs, (see docs/generic_webhook.md)
generic_webhook_urls:
- https://server.example.com/your-server/api
- http://another-server.com/api

136
submit.go
View file

@ -57,7 +57,8 @@ type submitServer struct {
slack *slackClient
cfg *config
genericWebhookClient *http.Client
cfg *config
}
// the type of payload which can be uploaded as JSON to the submit endpoint
@ -76,19 +77,38 @@ type jsonLogEntry struct {
Lines string `json:"lines"`
}
// the payload after parsing
type parsedPayload struct {
UserText string
AppName string
Data map[string]string
Labels []string
Logs []string
LogErrors []string
Files []string
FileErrors []string
// Stores additional information created during processing of a payload
type genericWebhookPayload struct {
payload
// If a github/gitlab report is generated, this is set.
ReportURL string `json:"report_url"`
// Complete link to the listing URL that contains all uploaded logs
ListingURL string `json:"listing_url"`
}
func (p parsedPayload) WriteTo(out io.Writer) {
// Stores information about a request made to this server
type payload struct {
// A unique ID for this payload, generated within this server
ID string `json:"id"`
// A multi-line string containing the user description of the fault.
UserText string `json:"user_text"`
// A short slug to identify the app making the report
AppName string `json:"app"`
// Arbitrary data to annotate the report
Data map[string]string `json:"data"`
// Short labels to group reports
Labels []string `json:"labels"`
// A list of names of logs recognised by the server
Logs []string `json:"logs"`
// Set if there are log parsing errors
LogErrors []string `json:"logErrors"`
// A list of other files (not logs) uploaded as part of the rageshake
Files []string `json:"files"`
// Set if there are file parsing errors
FileErrors []string `json:"fileErrors"`
}
func (p payload) WriteTo(out io.Writer) {
fmt.Fprintf(
out,
"%s\n\nNumber of logs: %d\nApplication: %s\n",
@ -171,6 +191,11 @@ func (s *submitServer) ServeHTTP(w http.ResponseWriter, req *http.Request) {
return
}
// We use this prefix (eg, 2022-05-01/125223-abcde) as a unique identifier for this rageshake.
// This is going to be used to uniquely identify rageshakes, even if they are not submitted to
// an issue tracker for instance with automatic rageshakes that can be plentiful
p.ID = prefix
resp, err := s.saveReport(req.Context(), *p, reportDir, listingURL)
if err != nil {
log.Println("Error handling report submission:", err)
@ -185,7 +210,7 @@ func (s *submitServer) ServeHTTP(w http.ResponseWriter, req *http.Request) {
// parseRequest attempts to parse a received request as a bug report. If
// the request cannot be parsed, it responds with an error and returns nil.
func parseRequest(w http.ResponseWriter, req *http.Request, reportDir string) *parsedPayload {
func parseRequest(w http.ResponseWriter, req *http.Request, reportDir string) *payload {
length, err := strconv.Atoi(req.Header.Get("Content-Length"))
if err != nil {
log.Println("Couldn't parse content-length", err)
@ -221,13 +246,13 @@ func parseRequest(w http.ResponseWriter, req *http.Request, reportDir string) *p
return p
}
func parseJSONRequest(w http.ResponseWriter, req *http.Request, reportDir string) (*parsedPayload, error) {
func parseJSONRequest(w http.ResponseWriter, req *http.Request, reportDir string) (*payload, error) {
var p jsonPayload
if err := json.NewDecoder(req.Body).Decode(&p); err != nil {
return nil, err
}
parsed := parsedPayload{
parsed := payload{
UserText: strings.TrimSpace(p.Text),
Data: make(map[string]string),
Labels: p.Labels,
@ -282,13 +307,13 @@ func parseJSONRequest(w http.ResponseWriter, req *http.Request, reportDir string
return &parsed, nil
}
func parseMultipartRequest(w http.ResponseWriter, req *http.Request, reportDir string) (*parsedPayload, error) {
func parseMultipartRequest(w http.ResponseWriter, req *http.Request, reportDir string) (*payload, error) {
rdr, err := req.MultipartReader()
if err != nil {
return nil, err
}
p := parsedPayload{
p := payload{
Data: make(map[string]string),
}
@ -307,7 +332,7 @@ func parseMultipartRequest(w http.ResponseWriter, req *http.Request, reportDir s
return &p, nil
}
func parseFormPart(part *multipart.Part, p *parsedPayload, reportDir string) error {
func parseFormPart(part *multipart.Part, p *payload, reportDir string) error {
defer part.Close()
field := part.FormName()
partName := part.FileName()
@ -368,7 +393,7 @@ func parseFormPart(part *multipart.Part, p *parsedPayload, reportDir string) err
// formPartToPayload updates the relevant part of *p from a name/value pair
// read from the form data.
func formPartToPayload(field, data string, p *parsedPayload) {
func formPartToPayload(field, data string, p *payload) {
if field == "text" {
p.UserText = data
} else if field == "app" {
@ -394,7 +419,7 @@ func formPartToPayload(field, data string, p *parsedPayload) {
// * no silly characters (/, ctrl chars, etc)
//
// * nothing starting with '.'
var filenameRegexp = regexp.MustCompile(`^[a-zA-Z0-9_-]+\.(jpg|png|txt)$`)
var filenameRegexp = regexp.MustCompile(`^[a-zA-Z0-9_-]+\.(jpg|png|txt|json)$`)
// saveFormPart saves a file upload to the report directory.
//
@ -468,7 +493,7 @@ func saveLogPart(logNum int, filename string, reader io.Reader, reportDir string
return leafName, nil
}
func (s *submitServer) saveReport(ctx context.Context, p parsedPayload, reportDir, listingURL string) (*submitResponse, error) {
func (s *submitServer) saveReport(ctx context.Context, p payload, reportDir, listingURL string) (*submitResponse, error) {
var summaryBuf bytes.Buffer
resp := submitResponse{}
p.WriteTo(&summaryBuf)
@ -492,10 +517,61 @@ func (s *submitServer) saveReport(ctx context.Context, p parsedPayload, reportDi
return nil, err
}
if err := s.submitGenericWebhook(p, listingURL, resp.ReportURL); err != nil {
return nil, err
}
return &resp, nil
}
func (s *submitServer) submitGithubIssue(ctx context.Context, p parsedPayload, listingURL string, resp *submitResponse) error {
// submitGenericWebhook submits a basic JSON body to an endpoint configured in the config
//
// The request does not include the log body, only the metadata in the payload,
// with the required listingURL to obtain the logs over http if required.
//
// If a github or gitlab issue was previously made, the reportURL will also be passed.
//
// Uses a goroutine to handle the http request asynchronously as by this point all critical
// information has been stored.
func (s *submitServer) submitGenericWebhook(p payload, listingURL string, reportURL string) error {
if s.genericWebhookClient == nil {
return nil
}
genericHookPayload := genericWebhookPayload{
payload: p,
ReportURL: reportURL,
ListingURL: listingURL,
}
for _, url := range s.cfg.GenericWebhookURLs {
// Enrich the payload with a reportURL and listingURL, to convert a single struct
// to JSON easily
payloadBuffer := new(bytes.Buffer)
json.NewEncoder(payloadBuffer).Encode(genericHookPayload)
req, err := http.NewRequest("POST", url, payloadBuffer)
req.Header.Set("Content-Type", "application/json")
if err != nil {
log.Println("Unable to submit to URL ", url, " ", err)
return err
}
log.Println("Making generic webhook request to URL ", url)
go s.sendGenericWebhook(req)
}
return nil
}
func (s *submitServer) sendGenericWebhook(req *http.Request) {
resp, err := s.genericWebhookClient.Do(req)
if err != nil {
log.Println("Unable to submit notification", err)
} else {
defer resp.Body.Close()
log.Println("Got response", resp.Status)
}
}
func (s *submitServer) submitGithubIssue(ctx context.Context, p payload, listingURL string, resp *submitResponse) error {
if s.ghClient == nil {
return nil
}
@ -526,7 +602,7 @@ func (s *submitServer) submitGithubIssue(ctx context.Context, p parsedPayload, l
return nil
}
func (s *submitServer) submitGitlabIssue(p parsedPayload, listingURL string, resp *submitResponse) error {
func (s *submitServer) submitGitlabIssue(p payload, listingURL string, resp *submitResponse) error {
if s.glClient == nil {
return nil
}
@ -549,7 +625,7 @@ func (s *submitServer) submitGitlabIssue(p parsedPayload, listingURL string, res
return nil
}
func (s *submitServer) submitSlackNotification(p parsedPayload, listingURL string) error {
func (s *submitServer) submitSlackNotification(p payload, listingURL string) error {
if s.slack == nil {
return nil
}
@ -567,7 +643,7 @@ func (s *submitServer) submitSlackNotification(p parsedPayload, listingURL strin
return nil
}
func buildReportTitle(p parsedPayload) string {
func buildReportTitle(p payload) string {
// set the title to the first (non-empty) line of the user's report, if any
trimmedUserText := strings.TrimSpace(p.UserText)
if trimmedUserText == "" {
@ -581,7 +657,7 @@ func buildReportTitle(p parsedPayload) string {
return trimmedUserText
}
func buildReportBody(p parsedPayload, newline, quoteChar string) *bytes.Buffer {
func buildReportBody(p payload, newline, quoteChar string) *bytes.Buffer {
var bodyBuf bytes.Buffer
fmt.Fprintf(&bodyBuf, "User message:\n\n%s\n\n", p.UserText)
var dataKeys []string
@ -597,7 +673,7 @@ func buildReportBody(p parsedPayload, newline, quoteChar string) *bytes.Buffer {
return &bodyBuf
}
func buildGenericIssueRequest(p parsedPayload, listingURL string) (title, body string) {
func buildGenericIssueRequest(p payload, listingURL string) (title, body string) {
bodyBuf := buildReportBody(p, " \n", "`")
// Add log links to the body
@ -619,7 +695,7 @@ func buildGenericIssueRequest(p parsedPayload, listingURL string) (title, body s
return
}
func buildGithubIssueRequest(p parsedPayload, listingURL string) github.IssueRequest {
func buildGithubIssueRequest(p payload, listingURL string) github.IssueRequest {
title, body := buildGenericIssueRequest(p, listingURL)
labels := p.Labels
@ -634,7 +710,7 @@ func buildGithubIssueRequest(p parsedPayload, listingURL string) github.IssueReq
}
}
func buildGitlabIssueRequest(p parsedPayload, listingURL string, labels []string, confidential bool) *gitlab.CreateIssueOptions {
func buildGitlabIssueRequest(p payload, listingURL string, labels []string, confidential bool) *gitlab.CreateIssueOptions {
title, body := buildGenericIssueRequest(p, listingURL)
if p.Labels != nil {
@ -649,7 +725,7 @@ func buildGitlabIssueRequest(p parsedPayload, listingURL string, labels []string
}
}
func (s *submitServer) sendEmail(p parsedPayload, reportDir string) error {
func (s *submitServer) sendEmail(p payload, reportDir string) error {
if len(s.cfg.EmailAddresses) == 0 {
return nil
}

View file

@ -35,7 +35,7 @@ import (
//
// if tempDir is empty, a new temp dir is created, and deleted when the test
// completes.
func testParsePayload(t *testing.T, body, contentType string, tempDir string) (*parsedPayload, *http.Response) {
func testParsePayload(t *testing.T, body, contentType string, tempDir string) (*payload, *http.Response) {
req, err := http.NewRequest("POST", "/api/submit", strings.NewReader(body))
if err != nil {
t.Fatal(err)
@ -232,7 +232,7 @@ Content-Type: application/octet-stream
return
}
func checkParsedMultipartUpload(t *testing.T, p *parsedPayload) {
func checkParsedMultipartUpload(t *testing.T, p *payload) {
wanted := "test words."
if p.UserText != wanted {
t.Errorf("User text: got %s, want %s", p.UserText, wanted)
@ -478,7 +478,7 @@ user_id: id
}
var buf bytes.Buffer
for _, v := range sample {
p := parsedPayload{Data: v.data}
p := payload{Data: v.data}
buf.Reset()
p.WriteTo(&buf)
got := strings.TrimSpace(buf.String())
@ -488,7 +488,7 @@ user_id: id
}
for k, v := range sample {
p := parsedPayload{Data: v.data}
p := payload{Data: v.data}
res := buildGithubIssueRequest(p, "")
got := *res.Body
if k == 0 {