fleet/orbit/pkg/token/reader.go

91 lines
2 KiB
Go

package token
import (
"os"
"sync"
"time"
)
// Reader is used to read the token value from a file
type Reader struct {
Path string // a path to a file containing a token
mu sync.Mutex // ensures atomic reads/writes; protects the following fields
cached string // the value of the token, a.k.a. the contents of the file
mtime time.Time // the mtime of the file when the token was last read
}
// Read returns the token value from the file only if the file is
// expired or the cached value is empty
func (r *Reader) Read() (string, error) {
changed, err := r.HasChanged()
if err != nil {
return "", err
}
if changed || r.GetCached() == "" {
if err := r.readFile(); err != nil {
return "", err
}
}
return r.GetCached(), nil
}
// HasChanged checks if the in-memory `value` has changed by comparing
// the chached `r.mtime` value with the `mtime` of the file at
// `r.path`
func (r *Reader) HasChanged() (bool, error) {
info, err := os.Stat(r.Path)
if err != nil {
return false, err
}
mtime := info.ModTime()
r.mu.Lock()
defer r.mu.Unlock()
return !mtime.Equal(r.mtime), nil
}
// HasExpired checks if 1 hour has passed since the last recorded `mtime` of
// the token file. It returns true, 0 if the token has expired, or false and
// the duration until it does expire.
func (r *Reader) HasExpired() (bool, time.Duration) {
const expirationDuration = 1 * time.Hour
r.mu.Lock()
defer r.mu.Unlock()
since := time.Since(r.mtime)
if since > expirationDuration {
return true, 0
}
return false, expirationDuration - since
}
func (r *Reader) GetMtime() time.Time {
r.mu.Lock()
defer r.mu.Unlock()
return r.mtime
}
// GetCached returns the cached token value
func (r *Reader) GetCached() string {
r.mu.Lock()
defer r.mu.Unlock()
return r.cached
}
func (r *Reader) readFile() error {
f, err := os.ReadFile(r.Path)
if err != nil {
return err
}
info, err := os.Stat(r.Path)
if err != nil {
return err
}
r.mu.Lock()
r.mtime = info.ModTime()
r.cached = string(f)
r.mu.Unlock()
return nil
}