Source file src/cmd/vendor/golang.org/x/telemetry/internal/upload/upload.go

     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  }
   118  

View as plain text