Upgrade to go 1.19 and update dependencies (#3069)

* Update to go 1.19
* Update dependencies
* Update cross-compile script
* Add missing targets to cross-compile-all
* Update cache action to remove warning
This commit is contained in:
WithoutPants
2022-11-04 13:41:26 +11:00
committed by GitHub
parent f25881a3bf
commit bba7c23957
939 changed files with 101336 additions and 43819 deletions

View File

@@ -2,11 +2,12 @@ language: go
go:
- 1.x
- tip
before_install:
- go install github.com/mattn/goveralls@latest
install:
- bash .travis.sh
- go get -t ./...
- go get golang.org/x/tools/cmd/cover
- go get github.com/mattn/goveralls
matrix:
allow_failures:
- go: tip

View File

@@ -72,11 +72,7 @@ func (w *BitsWriter) Write(i interface{}) error {
}
}
case []byte:
for _, b := range a {
if err := w.writeFullByte(b); err != nil {
return err
}
}
return w.writeByteSlice(a)
case bool:
if a {
return w.writeBit(1)
@@ -102,17 +98,21 @@ func (w *BitsWriter) Write(i interface{}) error {
// Writes first n bytes of bs if len(bs) > n
// Pads with padByte at the end if len(bs) < n
func (w *BitsWriter) WriteBytesN(bs []byte, n int, padByte uint8) error {
if len(bs) >= n {
return w.Write(bs[:n])
if n == 0 {
return nil
}
if err := w.Write(bs); err != nil {
if len(bs) >= n {
return w.writeByteSlice(bs[:n])
}
if err := w.writeByteSlice(bs); err != nil {
return err
}
// no bytes.Repeat here to avoid allocation
for i := 0; i < n-len(bs); i++ {
if err := w.Write(padByte); err != nil {
if err := w.writeFullByte(padByte); err != nil {
return err
}
}
@@ -120,17 +120,42 @@ func (w *BitsWriter) WriteBytesN(bs []byte, n int, padByte uint8) error {
return nil
}
func (w *BitsWriter) writeFullInt(in uint64, len int) error {
if w.bo == binary.BigEndian {
for i := len - 1; i >= 0; i-- {
err := w.writeFullByte(byte((in >> (i * 8)) & 0xff))
if err != nil {
func (w *BitsWriter) writeByteSlice(in []byte) error {
if len(in) == 0 {
return nil
}
if w.cacheLen != 0 {
for _, b := range in {
if err := w.writeFullByte(b); err != nil {
return err
}
}
} else {
return w.write(in)
}
return nil
}
func (w *BitsWriter) write(b []byte) error {
if _, err := w.w.Write(b); err != nil {
return err
}
if w.writeCb != nil {
for i := range b {
w.writeCb(b[i : i+1])
}
}
return nil
}
func (w *BitsWriter) writeFullInt(in uint64, len int) error {
if w.bo == binary.BigEndian {
return w.writeBitsN(in, len*8)
} else {
for i := 0; i < len; i++ {
err := w.writeFullByte(byte((in >> (i * 8)) & 0xff))
err := w.writeFullByte(byte(in >> (i * 8)))
if err != nil {
return err
}
@@ -141,15 +166,7 @@ func (w *BitsWriter) writeFullInt(in uint64, len int) error {
}
func (w *BitsWriter) flushBsCache() error {
if _, err := w.w.Write(w.bsCache); err != nil {
return err
}
if w.writeCb != nil {
w.writeCb(w.bsCache)
}
return nil
return w.write(w.bsCache)
}
func (w *BitsWriter) writeFullByte(b byte) error {
@@ -163,7 +180,9 @@ func (w *BitsWriter) writeFullByte(b byte) error {
}
func (w *BitsWriter) writeBit(bit byte) error {
w.cache = w.cache | (bit)<<(7-w.cacheLen)
if bit != 0 {
w.cache |= 1 << (7 - w.cacheLen)
}
w.cacheLen++
if w.cacheLen == 8 {
w.bsCache[0] = w.cache
@@ -177,6 +196,53 @@ func (w *BitsWriter) writeBit(bit byte) error {
return nil
}
func (w *BitsWriter) writeBitsN(toWrite uint64, n int) (err error) {
toWrite &= ^uint64(0) >> (64 - n)
for n > 0 {
if w.cacheLen == 0 {
if n >= 8 {
n -= 8
w.bsCache[0] = byte(toWrite >> n)
if err = w.flushBsCache(); err != nil {
return
}
} else {
w.cacheLen = uint8(n)
w.cache = byte(toWrite << (8 - w.cacheLen))
n = 0
}
} else {
free := int(8 - w.cacheLen)
m := n
if m >= free {
m = free
}
if n <= free {
w.cache |= byte(toWrite << (free - m))
} else {
w.cache |= byte(toWrite >> (n - m))
}
n -= m
w.cacheLen += uint8(m)
if w.cacheLen == 8 {
w.bsCache[0] = w.cache
if err = w.flushBsCache(); err != nil {
return err
}
w.cacheLen = 0
w.cache = 0
}
}
}
return
}
// WriteN writes the input into n bits
func (w *BitsWriter) WriteN(i interface{}, n int) error {
var toWrite uint64
@@ -193,13 +259,7 @@ func (w *BitsWriter) WriteN(i interface{}, n int) error {
return errors.New("astikit: invalid type")
}
for i := n - 1; i >= 0; i-- {
err := w.writeBit(byte(toWrite>>i) & 0x1)
if err != nil {
return err
}
}
return nil
return w.writeBitsN(toWrite, n)
}
// BitsWriterBatch allows to chain multiple Write* calls and check for error only once

8
vendor/github.com/asticode/go-astikit/bool.go generated vendored Normal file
View File

@@ -0,0 +1,8 @@
package astikit
func BoolToUInt32(b bool) uint32 {
if b {
return 1
}
return 0
}

View File

@@ -4,36 +4,56 @@ import (
"sync"
)
// CloseFunc is a method that closes something
type CloseFunc func() error
type CloseFunc func()
type CloseFuncWithError func() error
// Closer is an object that can close several things
type Closer struct {
fs []CloseFunc
m *sync.Mutex
closed bool
fs []CloseFuncWithError
// We need to split into 2 mutexes to allow using .Add() in .Do()
mc *sync.Mutex // Locks .Close()
mf *sync.Mutex // Locks fs
onClosed CloserOnClosed
}
type CloserOnClosed func(err error)
// NewCloser creates a new closer
func NewCloser() *Closer {
return &Closer{
m: &sync.Mutex{},
mc: &sync.Mutex{},
mf: &sync.Mutex{},
}
}
// Close implements the io.Closer interface
func (c *Closer) Close() error {
// Lock
c.m.Lock()
defer c.m.Unlock()
c.mc.Lock()
defer c.mc.Unlock()
// Get funcs
c.mf.Lock()
fs := c.fs
c.mf.Unlock()
// Loop through closers
err := NewErrors()
for _, f := range c.fs {
for _, f := range fs {
err.Add(f())
}
// Reset closers
c.fs = []CloseFunc{}
c.fs = []CloseFuncWithError{}
// Update attribute
c.closed = true
// Callback
if c.onClosed != nil {
c.onClosed(err)
}
// Return
if err.IsNil() {
@@ -42,16 +62,65 @@ func (c *Closer) Close() error {
return err
}
// Add adds a close func at the beginning of the list
func (c *Closer) Add(f CloseFunc) {
c.m.Lock()
defer c.m.Unlock()
c.fs = append([]CloseFunc{f}, c.fs...)
c.AddWithError(func() error {
f()
return nil
})
}
func (c *Closer) AddWithError(f CloseFuncWithError) {
// Lock
c.mf.Lock()
defer c.mf.Unlock()
// Append
c.fs = append([]CloseFuncWithError{f}, c.fs...)
}
func (c *Closer) Append(dst *Closer) {
// Lock
c.mf.Lock()
dst.mf.Lock()
defer c.mf.Unlock()
defer dst.mf.Unlock()
// Append
c.fs = append(c.fs, dst.fs...)
}
// NewChild creates a new child closer
func (c *Closer) NewChild() (child *Closer) {
child = NewCloser()
c.Add(child.Close)
c.AddWithError(child.Close)
return
}
// Do executes a callback while ensuring :
// - closer hasn't been closed before
// - closer can't be closed in between
func (c *Closer) Do(fn func()) {
// Lock
c.mc.Lock()
defer c.mc.Unlock()
// Closer already closed
if c.closed {
return
}
// Callback
fn()
}
func (c *Closer) OnClosed(fn CloserOnClosed) {
c.mc.Lock()
defer c.mc.Unlock()
c.onClosed = fn
}
func (c *Closer) IsClosed() bool {
c.mc.Lock()
defer c.mc.Unlock()
return c.closed
}

View File

@@ -7,6 +7,7 @@ import (
"errors"
"fmt"
"io"
"io/ioutil"
"net"
"net/http"
"os"
@@ -155,7 +156,7 @@ func (s *HTTPSender) SendWithTimeout(req *http.Request, timeout time.Duration) (
s.l.Debugf("astikit: sending %s", nr)
if resp, err = s.client.Do(req); err != nil {
// Retry if error is temporary, stop here otherwise
if netError, ok := err.(net.Error); !ok || !netError.Temporary() {
if netError, ok := err.(net.Error); !ok || !netError.Timeout() {
err = fmt.Errorf("astikit: sending %s failed: %w", nr, err)
return
}
@@ -184,14 +185,20 @@ func (s *HTTPSender) SendWithTimeout(req *http.Request, timeout time.Duration) (
return
}
type HTTPSenderHeaderFunc func(h http.Header)
type HTTPSenderStatusCodeFunc func(code int) error
// HTTPSendJSONOptions represents SendJSON options
type HTTPSendJSONOptions struct {
BodyError interface{}
BodyIn interface{}
BodyOut interface{}
Headers map[string]string
Method string
URL string
BodyError interface{}
BodyIn interface{}
BodyOut interface{}
HeadersIn map[string]string
HeadersOut HTTPSenderHeaderFunc
Method string
StatusCodeFunc HTTPSenderStatusCodeFunc
URL string
}
// SendJSON sends a new JSON HTTP request
@@ -215,7 +222,7 @@ func (s *HTTPSender) SendJSON(o HTTPSendJSONOptions) (err error) {
}
// Add headers
for k, v := range o.Headers {
for k, v := range o.HeadersIn {
req.Header.Set(k, v)
}
@@ -227,8 +234,17 @@ func (s *HTTPSender) SendJSON(o HTTPSendJSONOptions) (err error) {
}
defer resp.Body.Close()
// Process headers
if o.HeadersOut != nil {
o.HeadersOut(resp.Header)
}
// Process status code
if code := resp.StatusCode; code < 200 || code > 299 {
fn := HTTPSenderDefaultStatusCodeFunc
if o.StatusCodeFunc != nil {
fn = o.StatusCodeFunc
}
if err = fn(resp.StatusCode); err != nil {
// Try unmarshaling error
if o.BodyError != nil {
if err2 := json.NewDecoder(resp.Body).Decode(o.BodyError); err2 == nil {
@@ -238,20 +254,35 @@ func (s *HTTPSender) SendJSON(o HTTPSendJSONOptions) (err error) {
}
// Default error
err = fmt.Errorf("astikit: invalid status code %d", code)
err = fmt.Errorf("astikit: validating status code %d failed: %w", resp.StatusCode, err)
return
}
// Unmarshal body out
if o.BodyOut != nil {
if err = json.NewDecoder(resp.Body).Decode(o.BodyOut); err != nil {
err = fmt.Errorf("astikit: unmarshaling failed: %w", err)
// Read all
var b []byte
if b, err = ioutil.ReadAll(resp.Body); err != nil {
err = fmt.Errorf("astikit: reading all failed: %w", err)
return
}
// Unmarshal
if err = json.Unmarshal(b, o.BodyOut); err != nil {
err = fmt.Errorf("astikit: unmarshaling failed: %w (json: %s)", err, b)
return
}
}
return
}
func HTTPSenderDefaultStatusCodeFunc(code int) error {
if code < 200 || code > 299 {
return errors.New("astikit: status code should be between 200 and 299")
}
return nil
}
// HTTPResponseFunc is a func that can process an $http.Response
type HTTPResponseFunc func(resp *http.Response) error
@@ -380,6 +411,7 @@ func (d *HTTPDownloader) download(ctx context.Context, srcs []HTTPDownloaderSrc,
}
// Do
//nolint:errcheck
d.l.Do(func() {
// Task is done
defer wg.Done()

35
vendor/github.com/asticode/go-astikit/json.go generated vendored Normal file
View File

@@ -0,0 +1,35 @@
package astikit
import (
"bytes"
"encoding/json"
"fmt"
)
func JSONEqual(a, b interface{}) bool {
ba, err := json.Marshal(a)
if err != nil {
return false
}
bb, err := json.Marshal(b)
if err != nil {
return false
}
return bytes.Equal(ba, bb)
}
func JSONClone(src, dst interface{}) (err error) {
// Marshal
var b []byte
if b, err = json.Marshal(src); err != nil {
err = fmt.Errorf("main: marshaling failed: %w", err)
return
}
// Unmarshal
if err = json.Unmarshal(b, dst); err != nil {
err = fmt.Errorf("main: unmarshaling failed: %w", err)
return
}
return
}

View File

@@ -4,11 +4,66 @@ import (
"context"
)
// LoggerLevel represents a logger level
type LoggerLevel int
// Logger levels
const (
LoggerLevelDebug LoggerLevel = iota
LoggerLevelInfo
LoggerLevelWarn
LoggerLevelError
LoggerLevelFatal
)
// LoggerLevelFromString creates a logger level from string
func LoggerLevelFromString(s string) LoggerLevel {
switch s {
case "debug":
return LoggerLevelDebug
case "error":
return LoggerLevelError
case "fatal":
return LoggerLevelFatal
case "warn":
return LoggerLevelWarn
default:
return LoggerLevelInfo
}
}
func (l LoggerLevel) String() string {
switch l {
case LoggerLevelDebug:
return "debug"
case LoggerLevelError:
return "error"
case LoggerLevelFatal:
return "fatal"
case LoggerLevelWarn:
return "warn"
default:
return "info"
}
}
func (l *LoggerLevel) UnmarshalText(b []byte) error {
*l = LoggerLevelFromString(string(b))
return nil
}
func (l LoggerLevel) MarshalText() ([]byte, error) {
b := []byte(l.String())
return b, nil
}
// CompleteLogger represents a complete logger
type CompleteLogger interface {
StdLogger
SeverityLogger
SeverityCtxLogger
SeverityLogger
SeverityWriteLogger
SeverityWriteCtxLogger
StdLogger
}
// StdLogger represents a standard logger
@@ -31,6 +86,15 @@ type SeverityLogger interface {
Warnf(format string, v ...interface{})
}
type TestLogger interface {
Error(v ...interface{})
Errorf(format string, v ...interface{})
Fatal(v ...interface{})
Fatalf(format string, v ...interface{})
Log(v ...interface{})
Logf(format string, v ...interface{})
}
// SeverityCtxLogger represents a severity with context logger
type SeverityCtxLogger interface {
DebugC(ctx context.Context, v ...interface{})
@@ -45,38 +109,108 @@ type SeverityCtxLogger interface {
WarnCf(ctx context.Context, format string, v ...interface{})
}
type SeverityWriteLogger interface {
Write(l LoggerLevel, v ...interface{})
Writef(l LoggerLevel, format string, v ...interface{})
}
type SeverityWriteCtxLogger interface {
WriteC(ctx context.Context, l LoggerLevel, v ...interface{})
WriteCf(ctx context.Context, l LoggerLevel, format string, v ...interface{})
}
type completeLogger struct {
print, debug, error, fatal, info, warn func(v ...interface{})
printf, debugf, errorf, fatalf, infof, warnf func(format string, v ...interface{})
debugC, errorC, fatalC, infoC, warnC func(ctx context.Context, v ...interface{})
debugCf, errorCf, fatalCf, infoCf, warnCf func(ctx context.Context, format string, v ...interface{})
write func(l LoggerLevel, v ...interface{})
writeC func(ctx context.Context, l LoggerLevel, v ...interface{})
writeCf func(ctx context.Context, l LoggerLevel, format string, v ...interface{})
writef func(l LoggerLevel, format string, v ...interface{})
}
func newCompleteLogger() *completeLogger {
return &completeLogger{
debug: func(v ...interface{}) {},
debugf: func(format string, v ...interface{}) {},
debugC: func(ctx context.Context, v ...interface{}) {},
debugCf: func(ctx context.Context, format string, v ...interface{}) {},
error: func(v ...interface{}) {},
errorf: func(format string, v ...interface{}) {},
errorC: func(ctx context.Context, v ...interface{}) {},
errorCf: func(ctx context.Context, format string, v ...interface{}) {},
fatal: func(v ...interface{}) {},
fatalf: func(format string, v ...interface{}) {},
fatalC: func(ctx context.Context, v ...interface{}) {},
fatalCf: func(ctx context.Context, format string, v ...interface{}) {},
info: func(v ...interface{}) {},
infof: func(format string, v ...interface{}) {},
infoC: func(ctx context.Context, v ...interface{}) {},
infoCf: func(ctx context.Context, format string, v ...interface{}) {},
print: func(v ...interface{}) {},
printf: func(format string, v ...interface{}) {},
warn: func(v ...interface{}) {},
warnf: func(format string, v ...interface{}) {},
warnC: func(ctx context.Context, v ...interface{}) {},
warnCf: func(ctx context.Context, format string, v ...interface{}) {},
l := &completeLogger{}
l.debug = func(v ...interface{}) { l.print(v...) }
l.debugf = func(format string, v ...interface{}) { l.printf(format, v...) }
l.debugC = func(ctx context.Context, v ...interface{}) { l.debug(v...) }
l.debugCf = func(ctx context.Context, format string, v ...interface{}) { l.debugf(format, v...) }
l.error = func(v ...interface{}) { l.print(v...) }
l.errorf = func(format string, v ...interface{}) { l.printf(format, v...) }
l.errorC = func(ctx context.Context, v ...interface{}) { l.error(v...) }
l.errorCf = func(ctx context.Context, format string, v ...interface{}) { l.errorf(format, v...) }
l.fatal = func(v ...interface{}) { l.print(v...) }
l.fatalf = func(format string, v ...interface{}) { l.printf(format, v...) }
l.fatalC = func(ctx context.Context, v ...interface{}) { l.fatal(v...) }
l.fatalCf = func(ctx context.Context, format string, v ...interface{}) { l.fatalf(format, v...) }
l.info = func(v ...interface{}) { l.print(v...) }
l.infof = func(format string, v ...interface{}) { l.printf(format, v...) }
l.infoC = func(ctx context.Context, v ...interface{}) { l.info(v...) }
l.infoCf = func(ctx context.Context, format string, v ...interface{}) { l.infof(format, v...) }
l.print = func(v ...interface{}) {}
l.printf = func(format string, v ...interface{}) {}
l.warn = func(v ...interface{}) { l.print(v...) }
l.warnf = func(format string, v ...interface{}) { l.printf(format, v...) }
l.warnC = func(ctx context.Context, v ...interface{}) { l.warn(v...) }
l.warnCf = func(ctx context.Context, format string, v ...interface{}) { l.warnf(format, v...) }
l.write = func(lv LoggerLevel, v ...interface{}) {
switch lv {
case LoggerLevelDebug:
l.debug(v...)
case LoggerLevelError:
l.error(v...)
case LoggerLevelFatal:
l.fatal(v...)
case LoggerLevelWarn:
l.warn(v...)
default:
l.info(v...)
}
}
l.writeC = func(ctx context.Context, lv LoggerLevel, v ...interface{}) {
switch lv {
case LoggerLevelDebug:
l.debugC(ctx, v...)
case LoggerLevelError:
l.errorC(ctx, v...)
case LoggerLevelFatal:
l.fatalC(ctx, v...)
case LoggerLevelWarn:
l.warnC(ctx, v...)
default:
l.infoC(ctx, v...)
}
}
l.writeCf = func(ctx context.Context, lv LoggerLevel, format string, v ...interface{}) {
switch lv {
case LoggerLevelDebug:
l.debugCf(ctx, format, v...)
case LoggerLevelError:
l.errorCf(ctx, format, v...)
case LoggerLevelFatal:
l.fatalCf(ctx, format, v...)
case LoggerLevelWarn:
l.warnCf(ctx, format, v...)
default:
l.infoCf(ctx, format, v...)
}
}
l.writef = func(lv LoggerLevel, format string, v ...interface{}) {
switch lv {
case LoggerLevelDebug:
l.debugf(format, v...)
case LoggerLevelError:
l.errorf(format, v...)
case LoggerLevelFatal:
l.fatalf(format, v...)
case LoggerLevelWarn:
l.warnf(format, v...)
default:
l.infof(format, v...)
}
}
return l
}
func (l *completeLogger) Debug(v ...interface{}) { l.debug(v...) }
@@ -111,6 +245,16 @@ func (l *completeLogger) WarnC(ctx context.Context, v ...interface{}) { l.warnC(
func (l *completeLogger) WarnCf(ctx context.Context, format string, v ...interface{}) {
l.warnCf(ctx, format, v...)
}
func (l *completeLogger) Write(lv LoggerLevel, v ...interface{}) { l.write(lv, v...) }
func (l *completeLogger) Writef(lv LoggerLevel, format string, v ...interface{}) {
l.writef(lv, format, v...)
}
func (l *completeLogger) WriteC(ctx context.Context, lv LoggerLevel, v ...interface{}) {
l.writeC(ctx, lv, v...)
}
func (l *completeLogger) WriteCf(ctx context.Context, lv LoggerLevel, format string, v ...interface{}) {
l.writeCf(ctx, lv, format, v...)
}
// AdaptStdLogger transforms an StdLogger into a CompleteLogger if needed
func AdaptStdLogger(i StdLogger) CompleteLogger {
@@ -134,15 +278,6 @@ func AdaptStdLogger(i StdLogger) CompleteLogger {
l.infof = v.Infof
l.warn = v.Warn
l.warnf = v.Warnf
} else {
l.debug = l.print
l.debugf = l.printf
l.error = l.print
l.errorf = l.printf
l.info = l.print
l.infof = l.printf
l.warn = l.print
l.warnf = l.printf
}
if v, ok := i.(SeverityCtxLogger); ok {
l.debugC = v.DebugC
@@ -155,17 +290,38 @@ func AdaptStdLogger(i StdLogger) CompleteLogger {
l.infoCf = v.InfoCf
l.warnC = v.WarnC
l.warnCf = v.WarnCf
} else {
l.debugC = func(ctx context.Context, v ...interface{}) { l.debug(v...) }
l.debugCf = func(ctx context.Context, format string, v ...interface{}) { l.debugf(format, v...) }
l.errorC = func(ctx context.Context, v ...interface{}) { l.error(v...) }
l.errorCf = func(ctx context.Context, format string, v ...interface{}) { l.errorf(format, v...) }
l.fatalC = func(ctx context.Context, v ...interface{}) { l.fatal(v...) }
l.fatalCf = func(ctx context.Context, format string, v ...interface{}) { l.fatalf(format, v...) }
l.infoC = func(ctx context.Context, v ...interface{}) { l.info(v...) }
l.infoCf = func(ctx context.Context, format string, v ...interface{}) { l.infof(format, v...) }
l.warnC = func(ctx context.Context, v ...interface{}) { l.warn(v...) }
l.warnCf = func(ctx context.Context, format string, v ...interface{}) { l.warnf(format, v...) }
}
if v, ok := i.(SeverityWriteLogger); ok {
l.write = v.Write
l.writef = v.Writef
}
if v, ok := i.(SeverityWriteCtxLogger); ok {
l.writeC = v.WriteC
l.writeCf = v.WriteCf
}
return l
}
// AdaptTestLogger transforms a TestLogger into a CompleteLogger if needed
func AdaptTestLogger(i TestLogger) CompleteLogger {
if v, ok := i.(CompleteLogger); ok {
return v
}
l := newCompleteLogger()
if i == nil {
return l
}
l.error = i.Error
l.errorf = i.Errorf
l.fatal = i.Fatal
l.fatalf = i.Fatalf
l.print = i.Log
l.printf = i.Logf
l.debug = l.print
l.debugf = l.printf
l.info = l.print
l.infof = l.printf
l.warn = l.print
l.warnf = l.printf
return l
}

View File

@@ -6,6 +6,7 @@ import (
"io"
"os"
"path/filepath"
"strings"
)
// SSHSession represents an SSH Session
@@ -27,6 +28,9 @@ func SSHCopyFileFunc(fn SSHSessionFunc) CopyFileFunc {
return
}
// Escape dir path
d := strings.ReplaceAll(filepath.Dir(dst), " ", "\\ ")
// Using local closure allows better readibility for the defer c.Close() since it
// isolates the use of the ssh session
if err = func() (err error) {
@@ -40,7 +44,7 @@ func SSHCopyFileFunc(fn SSHSessionFunc) CopyFileFunc {
defer c.Close()
// Create the destination folder
if err = s.Run("mkdir -p " + filepath.Dir(dst)); err != nil {
if err = s.Run("mkdir -p " + d); err != nil {
err = fmt.Errorf("astikit: creating %s failed: %w", filepath.Dir(dst), err)
return
}
@@ -70,7 +74,7 @@ func SSHCopyFileFunc(fn SSHSessionFunc) CopyFileFunc {
defer stdin.Close()
// Use "scp" command
if err = s.Start("scp -qt \"" + filepath.Dir(dst) + "\""); err != nil {
if err = s.Start("scp -qt " + d); err != nil {
err = fmt.Errorf("astikit: scp to %s failed: %w", dst, err)
return
}

View File

@@ -20,8 +20,9 @@ type Stater struct {
// StatOptions represents stat options
type StatOptions struct {
Handler StatHandler
Metadata *StatMetadata
// Either a StatValuer or StatValuerOverTime
Valuer interface{}
}
// StatsHandleFunc is a method that can handle stat values
@@ -35,10 +36,13 @@ type StatMetadata struct {
Unit string
}
// StatHandler represents a stat handler
type StatHandler interface {
Start()
Stop()
// StatValuer represents a stat valuer
type StatValuer interface {
Value() interface{}
}
// StatValuerOverTime represents a stat valuer over time
type StatValuerOverTime interface {
Value(delta time.Duration) interface{}
}
@@ -96,10 +100,21 @@ func (s *Stater) Start(ctx context.Context) {
// Loop through stats
var stats []StatValue
s.m.Lock()
for _, v := range s.ss {
for _, o := range s.ss {
// Get value
var v interface{}
if h, ok := o.Valuer.(StatValuer); ok {
v = h.Value()
} else if h, ok := o.Valuer.(StatValuerOverTime); ok {
v = h.Value(delta)
} else {
continue
}
// Append
stats = append(stats, StatValue{
StatMetadata: v.Metadata,
Value: v.Handler.Value(delta),
StatMetadata: o.Metadata,
Value: v,
})
}
s.m.Unlock()
@@ -138,41 +153,34 @@ func (s *Stater) DelStats(os ...StatOptions) {
}
}
type durationStat struct {
d time.Duration
fn func(d, delta time.Duration) interface{}
isStarted bool
m *sync.Mutex // Locks isStarted
startedAt time.Time
type durationStatOverTime struct {
d time.Duration
fn func(d, delta time.Duration) interface{}
m *sync.Mutex // Locks isStarted
lastBeginAt time.Time
}
func newDurationStat(fn func(d, delta time.Duration) interface{}) *durationStat {
return &durationStat{
func newDurationStatOverTime(fn func(d, delta time.Duration) interface{}) *durationStatOverTime {
return &durationStatOverTime{
fn: fn,
m: &sync.Mutex{},
}
}
func (s *durationStat) Begin() {
func (s *durationStatOverTime) Begin() {
s.m.Lock()
defer s.m.Unlock()
if !s.isStarted {
return
}
s.startedAt = now()
s.lastBeginAt = now()
}
func (s *durationStat) End() {
func (s *durationStatOverTime) End() {
s.m.Lock()
defer s.m.Unlock()
if !s.isStarted {
return
}
s.d += now().Sub(s.startedAt)
s.startedAt = time.Time{}
s.d += now().Sub(s.lastBeginAt)
s.lastBeginAt = time.Time{}
}
func (s *durationStat) Value(delta time.Duration) (o interface{}) {
func (s *durationStatOverTime) Value(delta time.Duration) (o interface{}) {
// Lock
s.m.Lock()
defer s.m.Unlock()
@@ -182,9 +190,9 @@ func (s *durationStat) Value(delta time.Duration) (o interface{}) {
d := s.d
// Recording is still in process
if !s.startedAt.IsZero() {
d += n.Sub(s.startedAt)
s.startedAt = n
if !s.lastBeginAt.IsZero() {
d += n.Sub(s.lastBeginAt)
s.lastBeginAt = n
}
// Compute stat
@@ -193,27 +201,14 @@ func (s *durationStat) Value(delta time.Duration) (o interface{}) {
return
}
func (s *durationStat) Start() {
s.m.Lock()
defer s.m.Unlock()
s.d = 0
s.isStarted = true
}
func (s *durationStat) Stop() {
s.m.Lock()
defer s.m.Unlock()
s.isStarted = false
}
// DurationPercentageStat is an object capable of computing the percentage of time some work is taking per second
type DurationPercentageStat struct {
*durationStat
*durationStatOverTime
}
// NewDurationPercentageStat creates a new duration percentage stat
func NewDurationPercentageStat() *DurationPercentageStat {
return &DurationPercentageStat{durationStat: newDurationStat(func(d, delta time.Duration) interface{} {
return &DurationPercentageStat{durationStatOverTime: newDurationStatOverTime(func(d, delta time.Duration) interface{} {
if delta == 0 {
return 0
}
@@ -221,46 +216,28 @@ func NewDurationPercentageStat() *DurationPercentageStat {
})}
}
type counterStat struct {
c float64
fn func(c, t float64, delta time.Duration) interface{}
isStarted bool
m *sync.Mutex // Locks isStarted
t float64
type counterStatOverTime struct {
c float64
fn func(c, t float64, delta time.Duration) interface{}
m *sync.Mutex // Locks isStarted
t float64
}
func newCounterStat(fn func(c, t float64, delta time.Duration) interface{}) *counterStat {
return &counterStat{
func newCounterStatOverTime(fn func(c, t float64, delta time.Duration) interface{}) *counterStatOverTime {
return &counterStatOverTime{
fn: fn,
m: &sync.Mutex{},
}
}
func (s *counterStat) Add(delta float64) {
func (s *counterStatOverTime) Add(delta float64) {
s.m.Lock()
defer s.m.Unlock()
if !s.isStarted {
return
}
s.c += delta
s.t++
}
func (s *counterStat) Start() {
s.m.Lock()
defer s.m.Unlock()
s.c = 0
s.isStarted = true
s.t = 0
}
func (s *counterStat) Stop() {
s.m.Lock()
defer s.m.Unlock()
s.isStarted = true
}
func (s *counterStat) Value(delta time.Duration) interface{} {
func (s *counterStatOverTime) Value(delta time.Duration) interface{} {
s.m.Lock()
defer s.m.Unlock()
c := s.c
@@ -272,12 +249,12 @@ func (s *counterStat) Value(delta time.Duration) interface{} {
// CounterAvgStat is an object capable of computing the average value of a counter
type CounterAvgStat struct {
*counterStat
*counterStatOverTime
}
// NewCounterAvgStat creates a new counter avg stat
func NewCounterAvgStat() *CounterAvgStat {
return &CounterAvgStat{counterStat: newCounterStat(func(c, t float64, delta time.Duration) interface{} {
return &CounterAvgStat{counterStatOverTime: newCounterStatOverTime(func(c, t float64, delta time.Duration) interface{} {
if t == 0 {
return 0
}
@@ -287,15 +264,38 @@ func NewCounterAvgStat() *CounterAvgStat {
// CounterRateStat is an object capable of computing the average value of a counter per second
type CounterRateStat struct {
*counterStat
*counterStatOverTime
}
// NewCounterRateStat creates a new counter rate stat
func NewCounterRateStat() *CounterRateStat {
return &CounterRateStat{counterStat: newCounterStat(func(c, t float64, delta time.Duration) interface{} {
return &CounterRateStat{counterStatOverTime: newCounterStatOverTime(func(c, t float64, delta time.Duration) interface{} {
if delta.Seconds() == 0 {
return 0
}
return c / delta.Seconds()
})}
}
// CounterStat is an object capable of computing a counter that never gets reset
type CounterStat struct {
c float64
m *sync.Mutex
}
// NewCounterStat creates a new counter stat
func NewCounterStat() *CounterStat {
return &CounterStat{m: &sync.Mutex{}}
}
func (s *CounterStat) Add(delta float64) {
s.m.Lock()
defer s.m.Unlock()
s.c += delta
}
func (s *CounterStat) Value() interface{} {
s.m.Lock()
defer s.m.Unlock()
return s.c
}

View File

@@ -209,13 +209,13 @@ func (c *Chan) Stats() []StatOptions {
}
return []StatOptions{
{
Handler: c.statWorkRatio,
Metadata: &StatMetadata{
Description: "Percentage of time doing work",
Label: "Work ratio",
Name: StatNameWorkRatio,
Unit: "%",
},
Valuer: c.statWorkRatio,
},
}
}

View File

@@ -7,32 +7,42 @@ import (
"net/http"
"os"
"path/filepath"
"sort"
"strconv"
"strings"
"sync"
)
// Translator represents an object capable of translating stuff
type Translator struct {
m *sync.RWMutex // Lock p
o TranslatorOptions
p map[string]string
defaultLanguage string
m *sync.RWMutex // Lock p
p map[string]string
validLanguages map[string]bool
}
// TranslatorOptions represents Translator options
type TranslatorOptions struct {
DefaultLanguage string
ValidLanguages []string
}
// NewTranslator creates a new Translator
func NewTranslator(o TranslatorOptions) *Translator {
return &Translator{
m: &sync.RWMutex{},
o: o,
p: make(map[string]string),
func NewTranslator(o TranslatorOptions) (t *Translator) {
t = &Translator{
defaultLanguage: o.DefaultLanguage,
m: &sync.RWMutex{},
p: make(map[string]string),
validLanguages: make(map[string]bool),
}
for _, l := range o.ValidLanguages {
t.validLanguages[l] = true
}
return
}
// ParseDir adds translations located in ".json" files in the specified dir
// If ".json" files are located in child dirs, keys will be prefixed with their paths
func (t *Translator) ParseDir(dirPath string) (err error) {
// Default dir path
if dirPath == "" {
@@ -50,11 +60,8 @@ func (t *Translator) ParseDir(dirPath string) (err error) {
return
}
// Only process first level files
// Only process files
if info.IsDir() {
if path != dirPath {
err = filepath.SkipDir
}
return
}
@@ -64,7 +71,7 @@ func (t *Translator) ParseDir(dirPath string) (err error) {
}
// Parse file
if err = t.ParseFile(path); err != nil {
if err = t.ParseFile(dirPath, path); err != nil {
err = fmt.Errorf("astikit: parsing %s failed: %w", path, err)
return
}
@@ -77,7 +84,7 @@ func (t *Translator) ParseDir(dirPath string) (err error) {
}
// ParseFile adds translation located in the provided path
func (t *Translator) ParseFile(path string) (err error) {
func (t *Translator) ParseFile(dirPath, path string) (err error) {
// Lock
t.m.Lock()
defer t.m.Unlock()
@@ -97,8 +104,26 @@ func (t *Translator) ParseFile(path string) (err error) {
return
}
// Get language
language := strings.TrimSuffix(filepath.Base(path), filepath.Ext(path))
// Update valid languages
t.validLanguages[language] = true
// Get prefix
prefix := language
if dp := filepath.Dir(path); dp != dirPath {
var fs []string
for _, v := range strings.Split(strings.TrimPrefix(dp, dirPath), string(os.PathSeparator)) {
if v != "" {
fs = append(fs, v)
}
}
prefix += "." + strings.Join(fs, ".")
}
// Parse
t.parse(p, strings.TrimSuffix(filepath.Base(path), filepath.Ext(path)))
t.parse(p, prefix)
return
}
@@ -123,7 +148,7 @@ func (t *Translator) HTTPMiddleware(h http.Handler) http.Handler {
return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
// Store language in context
if l := r.Header.Get("Accept-Language"); l != "" {
*r = *r.WithContext(contextWithTranslatorLanguage(r.Context(), l))
*r = *r.WithContext(contextWithTranslatorLanguage(r.Context(), t.parseAcceptLanguage(l)))
}
// Next handler
@@ -131,7 +156,56 @@ func (t *Translator) HTTPMiddleware(h http.Handler) http.Handler {
})
}
const contextKeyTranslatorLanguage = "astikit.translator.language"
func (t *Translator) parseAcceptLanguage(h string) string {
// Split on comma
var qs []float64
ls := make(map[float64][]string)
for _, c := range strings.Split(strings.TrimSpace(h), ",") {
// Empty
c = strings.TrimSpace(c)
if c == "" {
continue
}
// Split on semi colon
ss := strings.Split(c, ";")
// Parse coefficient
q := float64(1)
if len(ss) > 1 {
s := strings.TrimSpace(ss[1])
if strings.HasPrefix(s, "q=") {
var err error
if q, err = strconv.ParseFloat(strings.TrimPrefix(s, "q="), 64); err != nil {
q = 1
}
}
}
// Add
if _, ok := ls[q]; !ok {
qs = append(qs, q)
}
ls[q] = append(ls[q], strings.TrimSpace(ss[0]))
}
// Order coefficients
sort.Float64s(qs)
// Loop through coefficients in reverse order
for idx := len(qs) - 1; idx >= 0; idx-- {
for _, l := range ls[qs[idx]] {
if _, ok := t.validLanguages[l]; ok {
return l
}
}
}
return ""
}
const contextKeyTranslatorLanguage = contextKey("astikit.translator.language")
type contextKey string
func contextWithTranslatorLanguage(ctx context.Context, language string) context.Context {
return context.WithValue(ctx, contextKeyTranslatorLanguage, language)
@@ -147,7 +221,7 @@ func translatorLanguageFromContext(ctx context.Context) string {
func (t *Translator) language(language string) string {
if language == "" {
return t.o.DefaultLanguage
return t.defaultLanguage
}
return language
}
@@ -171,14 +245,28 @@ func (t *Translator) Translate(language, key string) string {
}
// Default translation
k2 := t.key(t.o.DefaultLanguage, key)
k2 := t.key(t.defaultLanguage, key)
if v, ok = t.p[k2]; ok {
return v
}
return k1
}
// TranslateCtx translates a key using the language specified in the context
// Translatef translates a key into a specific language with optional formatting args
func (t *Translator) Translatef(language, key string, args ...interface{}) string {
return fmt.Sprintf(t.Translate(language, key), args...)
}
// TranslateCtx is an alias for TranslateC
func (t *Translator) TranslateCtx(ctx context.Context, key string) string {
return t.TranslateC(ctx, key)
}
// TranslateC translates a key using the language specified in the context
func (t *Translator) TranslateC(ctx context.Context, key string) string {
return t.Translate(translatorLanguageFromContext(ctx), key)
}
func (t *Translator) TranslateCf(ctx context.Context, key string, args ...interface{}) string {
return t.Translatef(translatorLanguageFromContext(ctx), key, args...)
}