src

Go monorepo.
git clone git://code.dwrz.net/src
Log | Files | Refs

upload.go (3622B)


      1 // Copyright 2023 The Go Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style
      3 // license that can be found in the LICENSE file.
      4 
      5 package upload
      6 
      7 import (
      8 	"bytes"
      9 	"net/http"
     10 	"os"
     11 	"path/filepath"
     12 	"regexp"
     13 	"strings"
     14 	"time"
     15 
     16 	"golang.org/x/telemetry/internal/telemetry"
     17 )
     18 
     19 var (
     20 	dateRE     = regexp.MustCompile(`(\d\d\d\d-\d\d-\d\d)[.]json$`)
     21 	dateFormat = telemetry.DateOnly
     22 	// TODO(rfindley): use dateFormat throughout.
     23 )
     24 
     25 // uploadReportDate returns the date component of the upload file name, or "" if the
     26 // date was unmatched.
     27 func (u *uploader) uploadReportDate(fname string) time.Time {
     28 	match := dateRE.FindStringSubmatch(fname)
     29 	if match == nil || len(match) < 2 {
     30 		u.logger.Printf("malformed report name: missing date: %q", filepath.Base(fname))
     31 		return time.Time{}
     32 	}
     33 	d, err := time.Parse(dateFormat, match[1])
     34 	if err != nil {
     35 		u.logger.Printf("malformed report name: bad date: %q", filepath.Base(fname))
     36 		return time.Time{}
     37 	}
     38 	return d
     39 }
     40 
     41 func (u *uploader) uploadReport(fname string) {
     42 	thisInstant := u.startTime
     43 	// TODO(rfindley): use uploadReportDate here, once we've done a gopls release.
     44 
     45 	// first make sure it is not in the future
     46 	today := thisInstant.Format(telemetry.DateOnly)
     47 	match := dateRE.FindStringSubmatch(fname)
     48 	if match == nil || len(match) < 2 {
     49 		u.logger.Printf("Report name %q missing date", filepath.Base(fname))
     50 	} else if match[1] > today {
     51 		u.logger.Printf("Report date for %q is later than today (%s)", filepath.Base(fname), today)
     52 		return // report is in the future, which shouldn't happen
     53 	}
     54 	buf, err := os.ReadFile(fname)
     55 	if err != nil {
     56 		u.logger.Printf("%v reading %s", err, fname)
     57 		return
     58 	}
     59 	if u.uploadReportContents(fname, buf) {
     60 		// anything left to do?
     61 	}
     62 }
     63 
     64 // try to upload the report, 'true' if successful
     65 func (u *uploader) uploadReportContents(fname string, buf []byte) bool {
     66 	fdate := strings.TrimSuffix(filepath.Base(fname), ".json")
     67 	fdate = fdate[len(fdate)-len(telemetry.DateOnly):]
     68 
     69 	newname := filepath.Join(u.dir.UploadDir(), fdate+".json")
     70 
     71 	// Lock the upload, to prevent duplicate uploads.
     72 	{
     73 		lockname := newname + ".lock"
     74 		lockfile, err := os.OpenFile(lockname, os.O_CREATE|os.O_EXCL, 0666)
     75 		if err != nil {
     76 			u.logger.Printf("Failed to acquire lock %s: %v", lockname, err)
     77 			return false
     78 		}
     79 		_ = lockfile.Close()
     80 		defer os.Remove(lockname)
     81 	}
     82 
     83 	if _, err := os.Stat(newname); err == nil {
     84 		// Another process uploaded but failed to clean up (or hasn't yet cleaned
     85 		// up). Ensure that cleanup occurs.
     86 		u.logger.Printf("After acquire: report already uploaded")
     87 		_ = os.Remove(fname)
     88 		return false
     89 	}
     90 
     91 	endpoint := u.uploadServerURL + "/" + fdate
     92 	b := bytes.NewReader(buf)
     93 	resp, err := http.Post(endpoint, "application/json", b)
     94 	if err != nil {
     95 		u.logger.Printf("Error upload %s to %s: %v", filepath.Base(fname), endpoint, err)
     96 		return false
     97 	}
     98 	// hope for a 200, remove file on a 4xx, otherwise it will be retried by another process
     99 	if resp.StatusCode != 200 {
    100 		u.logger.Printf("Failed to upload %s to %s: %s", filepath.Base(fname), endpoint, resp.Status)
    101 		if resp.StatusCode >= 400 && resp.StatusCode < 500 {
    102 			err := os.Remove(fname)
    103 			if err == nil {
    104 				u.logger.Printf("Removed local/%s", filepath.Base(fname))
    105 			} else {
    106 				u.logger.Printf("Error removing local/%s: %v", filepath.Base(fname), err)
    107 			}
    108 		}
    109 		return false
    110 	}
    111 	// Store a copy of the uploaded report in the uploaded directory.
    112 	if err := os.WriteFile(newname, buf, 0644); err == nil {
    113 		os.Remove(fname) // if it exists
    114 	}
    115 	u.logger.Printf("Uploaded %s to %q", fdate+".json", endpoint)
    116 	return true
    117 }