blob: 3959c2421e674324c9c8a5b5f1d73e922975473b [file] [log] [blame]
// Copyright 2023 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package upload
import (
"fmt"
"os"
"sync"
"time"
"golang.org/x/telemetry/internal/counter"
)
// time and date handling
// another name for time.Now() to use for testing
var now = time.Now
var distantPast = 21 * 24 * time.Hour
// reports that are too old (21 days) are not uploaded
func tooOld(date string) bool {
t, err := time.Parse("2006-01-02", date)
if err != nil {
logger.Printf("tooOld: %v", err)
return false
}
return now().Sub(t) > distantPast
}
// return the expiry date of a countfile in YYYY-MM-DD format
func expiryDate(fname string) string {
t := expiry(fname)
if t.IsZero() {
return ""
}
// PJW: is this sometimes off by a day?
year, month, day := t.Date()
return fmt.Sprintf("%04d-%02d-%02d", year, month, day)
}
// a time in the far future for the expiry time with errors
var farFuture = time.UnixMilli(1 << 62)
// expiry returns the expiry time of a countfile. For errors
// it returns a time far in the future, so that erroneous files
// don't look like they should be used.
func expiry(fname string) time.Time {
parsed, err := parse(fname)
if err != nil {
logger.Printf("expiry Parse: %v for %s", err, fname)
return farFuture // don't process it, whatever it is
}
expiry, err := time.Parse(time.RFC3339, parsed.Meta["TimeEnd"])
if err != nil {
logger.Printf("time.Parse: %v for %s", err, fname)
return farFuture // don't process it, whatever it is
}
// TODO(pjw): check for off-by-one-day?
return expiry
}
// stillOpen returns true if the counter file might still be active
func stillOpen(fname string) bool {
expiry := expiry(fname)
return expiry.After(now()) // TODO(pjw): off by one day?
}
// avoid parsing count files multiple times
type parsedCache struct {
mu sync.Mutex
m map[string]*counter.File
}
var cache parsedCache
func parse(fname string) (*counter.File, error) {
cache.mu.Lock()
defer cache.mu.Unlock()
if cache.m == nil {
cache.m = make(map[string]*counter.File)
}
if f, ok := cache.m[fname]; ok {
return f, nil
}
buf, err := os.ReadFile(fname)
if err != nil {
return nil, fmt.Errorf("parse ReadFile: %v for %s", err, fname)
}
f, err := counter.Parse(fname, buf)
if err != nil {
return nil, fmt.Errorf("parse Parse: %v for %s", err, fname)
}
cache.m[fname] = f
return f, nil
}