Commit dd831517 authored by ale's avatar ale

Update go-common and other dependencies

parent 0eb2c78d
Pipeline #1416 passed with stages
in 1 minute and 9 seconds
...@@ -40,6 +40,8 @@ type cacheDatum struct { ...@@ -40,6 +40,8 @@ type cacheDatum struct {
deadline time.Time deadline time.Time
} }
var dnsCacheTTL = 1 * time.Minute
type dnsCache struct { type dnsCache struct {
resolver resolver resolver resolver
sf singleflight.Group sf singleflight.Group
...@@ -72,7 +74,7 @@ func (c *dnsCache) update(host string) []string { ...@@ -72,7 +74,7 @@ func (c *dnsCache) update(host string) []string {
c.mx.Lock() c.mx.Lock()
c.cache[host] = cacheDatum{ c.cache[host] = cacheDatum{
addrs: addrs, addrs: addrs,
deadline: time.Now().Add(60 * time.Second), deadline: time.Now().Add(dnsCacheTTL),
} }
c.mx.Unlock() c.mx.Unlock()
return addrs, nil return addrs, nil
......
package clientutil
import (
"fmt"
"sync"
"time"
)
var dnsWatcherInterval = 1 * time.Minute
// A DNSWatcher monitors a DNS name for changes, constantly attempting
// to resolve it every minute and notifying a channel when the list of
// returned IP addresses changes. All addresses must be in host:port
// format.
type DNSWatcher struct {
hostport string
resolver resolver
addrs []string
updateCh chan []string
stopCh chan struct{}
}
// NewDNSWatcher creates a new DNSWatcher.
func NewDNSWatcher(hostport string) (*DNSWatcher, error) {
return newDNSWatcherWithResolver(hostport, defaultResolver)
}
func newDNSWatcherWithResolver(hostport string, resolver resolver) (*DNSWatcher, error) {
// Resolve names once before returning. Return a fatal error
// when there are no results, as it may indicate a syntax
// error in hostport.
addrs := resolver.ResolveIP(hostport)
if len(addrs) == 0 {
return nil, fmt.Errorf("can't resolve %s", hostport)
}
w := &DNSWatcher{
hostport: hostport,
resolver: resolver,
addrs: addrs,
updateCh: make(chan []string, 10),
stopCh: make(chan struct{}),
}
w.updateCh <- addrs
go w.loop()
return w, nil
}
// Stop the watcher.
func (w *DNSWatcher) Stop() {
close(w.stopCh)
}
// Changes returns a channel where the resolved addresses are sent
// whenever they change.
func (w *DNSWatcher) Changes() <-chan []string {
return w.updateCh
}
func (w *DNSWatcher) check() {
addrs := w.resolver.ResolveIP(w.hostport)
if len(addrs) > 0 && !addrListEqual(addrs, w.addrs) {
w.addrs = addrs
w.updateCh <- addrs
}
}
func (w *DNSWatcher) loop() {
defer close(w.updateCh)
tick := time.NewTicker(dnsWatcherInterval)
defer tick.Stop()
for {
select {
case <-tick.C:
w.check()
case <-w.stopCh:
return
}
}
}
type multiDNSUpdate struct {
hostport string
addrs []string
}
// A MultiDNSWatcher watches multiple addresses for DNS changes. The
// results are merged and returned as a list of addresses.
type MultiDNSWatcher struct {
watchers []*DNSWatcher
addrmap map[string][]string
faninCh chan multiDNSUpdate
updateCh chan []string
}
// NewMultiDNSWatcher creates a new MultiDNSWatcher.
func NewMultiDNSWatcher(hostports []string) (*MultiDNSWatcher, error) {
return newMultiDNSWatcherWithResolver(hostports, defaultResolver)
}
func newMultiDNSWatcherWithResolver(hostports []string, resolver resolver) (*MultiDNSWatcher, error) {
mw := &MultiDNSWatcher{
addrmap: make(map[string][]string),
faninCh: make(chan multiDNSUpdate, 10),
updateCh: make(chan []string, 10),
}
// All the MultiDNSWatcher does is multiplex updates from the
// individual DNSWatchers onto faninCh, then merging those
// updates with all the others and sending the result to
// updateCh.
go func() {
defer close(mw.updateCh)
for up := range mw.faninCh {
mw.addrmap[up.hostport] = up.addrs
mw.updateCh <- mw.allAddrs()
}
}()
var wg sync.WaitGroup
for _, hostport := range hostports {
w, err := newDNSWatcherWithResolver(hostport, resolver)
if err != nil {
return nil, err
}
mw.watchers = append(mw.watchers, w)
wg.Add(1)
go func(hostport string) {
for addrs := range w.Changes() {
mw.faninCh <- multiDNSUpdate{
hostport: hostport,
addrs: addrs,
}
}
wg.Done()
}(hostport)
}
go func() {
wg.Wait()
close(mw.faninCh)
}()
return mw, nil
}
func (mw *MultiDNSWatcher) allAddrs() []string {
var out []string
for _, addrs := range mw.addrmap {
out = append(out, addrs...)
}
return out
}
// Stop the watcher.
func (mw *MultiDNSWatcher) Stop() {
for _, w := range mw.watchers {
w.Stop()
}
}
// Changes returns a channel where the aggregate resolved addresses
// are sent whenever they change.
func (mw *MultiDNSWatcher) Changes() <-chan []string {
return mw.updateCh
}
func addrListEqual(a, b []string) bool {
if len(a) != len(b) {
return false
}
tmp := make(map[string]struct{})
for _, aa := range a {
tmp[aa] = struct{}{}
}
for _, bb := range b {
if _, ok := tmp[bb]; !ok {
return false
}
delete(tmp, bb)
}
return len(tmp) == 0
}
...@@ -79,7 +79,7 @@ func NewUNIXSocketServer(socketPath string, h Handler) (*SocketServer, error) { ...@@ -79,7 +79,7 @@ func NewUNIXSocketServer(socketPath string, h Handler) (*SocketServer, error) {
// NewSystemdSocketServer uses systemd socket activation, receiving // NewSystemdSocketServer uses systemd socket activation, receiving
// the open socket as a file descriptor on exec. // the open socket as a file descriptor on exec.
func NewSystemdSocketServer(h Handler) (*SocketServer, error) { func NewSystemdSocketServer(h Handler) (*SocketServer, error) {
listeners, err := activation.Listeners(false) listeners, err := activation.Listeners()
if err != nil { if err != nil {
return nil, err return nil, err
} }
......
...@@ -7,7 +7,7 @@ import ( ...@@ -7,7 +7,7 @@ import (
"io" "io"
"github.com/lunixbochs/struc" "github.com/lunixbochs/struc"
"github.com/miscreant/miscreant/go" "github.com/miscreant/miscreant-go"
"golang.org/x/crypto/argon2" "golang.org/x/crypto/argon2"
) )
......
<?xml version="1.0" encoding="UTF-8"?>
<testsuites>
<testsuite tests="2" failures="0" time="0.000" name="git.autistici.org/id/go-sso">
<properties>
<property name="go.version" value="go1.10.3"></property>
</properties>
<testcase classname="go-sso" name="TestEd25519" time="0.000"></testcase>
<testcase classname="go-sso" name="TestLegacy" time="0.000"></testcase>
</testsuite>
<testsuite tests="1" failures="0" time="0.000" name="git.autistici.org/id/go-sso/cmd/sso-server">
<properties>
<property name="go.version" value="go1.10.3"></property>
</properties>
<testcase classname="sso-server" name="TestMain_LoadConfig" time="0.000"></testcase>
</testsuite>
<testsuite tests="1" failures="0" time="0.000" name="git.autistici.org/id/go-sso/httpsso">
<properties>
<property name="go.version" value="go1.10.3"></property>
</properties>
<testcase classname="httpsso" name="TestSSOWrapper" time="0.010"></testcase>
</testsuite>
<testsuite tests="1" failures="0" time="0.000" name="git.autistici.org/id/go-sso/httputil">
<properties>
<property name="go.version" value="go1.10.3"></property>
</properties>
<testcase classname="httputil" name="TestExpiringSession" time="0.000"></testcase>
</testsuite>
<testsuite tests="1" failures="0" time="0.000" name="git.autistici.org/id/go-sso/proxy">
<properties>
<property name="go.version" value="go1.10.3"></property>
</properties>
<testcase classname="proxy" name="TestProxy" time="0.010"></testcase>
</testsuite>
<testsuite tests="6" failures="0" time="0.000" name="git.autistici.org/id/go-sso/server">
<properties>
<property name="go.version" value="go1.10.3"></property>
</properties>
<testcase classname="server" name="TestHTTP_ServeStaticAsset" time="0.010"></testcase>
<testcase classname="server" name="TestHTTP_Login" time="0.000"></testcase>
<testcase classname="server" name="TestHTTP_LoginOTP" time="0.000"></testcase>
<testcase classname="server" name="TestHTTP_LoginWithKeyStore" time="0.010"></testcase>
<testcase classname="server" name="TestLoginService_Ok" time="0.000"></testcase>
<testcase classname="server" name="TestLoginService_SanityChecks" time="0.000"></testcase>
</testsuite>
</testsuites>
...@@ -77,15 +77,20 @@ func NewHighBiased(epsilon float64) *Stream { ...@@ -77,15 +77,20 @@ func NewHighBiased(epsilon float64) *Stream {
// is guaranteed to be within (Quantile±Epsilon). // is guaranteed to be within (Quantile±Epsilon).
// //
// See http://www.cs.rutgers.edu/~muthu/bquant.pdf for time, space, and error properties. // See http://www.cs.rutgers.edu/~muthu/bquant.pdf for time, space, and error properties.
func NewTargeted(targets map[float64]float64) *Stream { func NewTargeted(targetMap map[float64]float64) *Stream {
// Convert map to slice to avoid slow iterations on a map.
// ƒ is called on the hot path, so converting the map to a slice
// beforehand results in significant CPU savings.
targets := targetMapToSlice(targetMap)
ƒ := func(s *stream, r float64) float64 { ƒ := func(s *stream, r float64) float64 {
var m = math.MaxFloat64 var m = math.MaxFloat64
var f float64 var f float64
for quantile, epsilon := range targets { for _, t := range targets {
if quantile*s.n <= r { if t.quantile*s.n <= r {
f = (2 * epsilon * r) / quantile f = (2 * t.epsilon * r) / t.quantile
} else { } else {
f = (2 * epsilon * (s.n - r)) / (1 - quantile) f = (2 * t.epsilon * (s.n - r)) / (1 - t.quantile)
} }
if f < m { if f < m {
m = f m = f
...@@ -96,6 +101,25 @@ func NewTargeted(targets map[float64]float64) *Stream { ...@@ -96,6 +101,25 @@ func NewTargeted(targets map[float64]float64) *Stream {
return newStream(ƒ) return newStream(ƒ)
} }
type target struct {
quantile float64
epsilon float64
}
func targetMapToSlice(targetMap map[float64]float64) []target {
targets := make([]target, 0, len(targetMap))
for quantile, epsilon := range targetMap {
t := target{
quantile: quantile,
epsilon: epsilon,
}
targets = append(targets, t)
}
return targets
}
// Stream computes quantiles for a stream of float64s. It is not thread-safe by // Stream computes quantiles for a stream of float64s. It is not thread-safe by
// design. Take care when using across multiple goroutines. // design. Take care when using across multiple goroutines.
type Stream struct { type Stream struct {
......
CoreOS Project
Copyright 2018 CoreOS, Inc
This product includes software developed at CoreOS, Inc.
(http://www.coreos.com/).
...@@ -18,18 +18,26 @@ package activation ...@@ -18,18 +18,26 @@ package activation
import ( import (
"os" "os"
"strconv" "strconv"
"strings"
"syscall" "syscall"
) )
// based on: https://gist.github.com/alberts/4640792
const ( const (
// listenFdsStart corresponds to `SD_LISTEN_FDS_START`.
listenFdsStart = 3 listenFdsStart = 3
) )
// Files returns a slice containing a `os.File` object for each
// file descriptor passed to this process via systemd fd-passing protocol.
//
// The order of the file descriptors is preserved in the returned slice.
// `unsetEnv` is typically set to `true` in order to avoid clashes in
// fd usage and to avoid leaking environment flags to child processes.
func Files(unsetEnv bool) []*os.File { func Files(unsetEnv bool) []*os.File {
if unsetEnv { if unsetEnv {
defer os.Unsetenv("LISTEN_PID") defer os.Unsetenv("LISTEN_PID")
defer os.Unsetenv("LISTEN_FDS") defer os.Unsetenv("LISTEN_FDS")
defer os.Unsetenv("LISTEN_FDNAMES")
} }
pid, err := strconv.Atoi(os.Getenv("LISTEN_PID")) pid, err := strconv.Atoi(os.Getenv("LISTEN_PID"))
...@@ -42,10 +50,17 @@ func Files(unsetEnv bool) []*os.File { ...@@ -42,10 +50,17 @@ func Files(unsetEnv bool) []*os.File {
return nil return nil
} }
names := strings.Split(os.Getenv("LISTEN_FDNAMES"), ":")
files := make([]*os.File, 0, nfds) files := make([]*os.File, 0, nfds)
for fd := listenFdsStart; fd < listenFdsStart+nfds; fd++ { for fd := listenFdsStart; fd < listenFdsStart+nfds; fd++ {
syscall.CloseOnExec(fd) syscall.CloseOnExec(fd)
files = append(files, os.NewFile(uintptr(fd), "LISTEN_FD_"+strconv.Itoa(fd))) name := "LISTEN_FD_" + strconv.Itoa(fd)
offset := fd - listenFdsStart
if offset < len(names) && len(names[offset]) > 0 {
name = names[offset]
}
files = append(files, os.NewFile(uintptr(fd), name))
} }
return files return files
......
...@@ -25,13 +25,33 @@ import ( ...@@ -25,13 +25,33 @@ import (
// The order of the file descriptors is preserved in the returned slice. // The order of the file descriptors is preserved in the returned slice.
// Nil values are used to fill any gaps. For example if systemd were to return file descriptors // Nil values are used to fill any gaps. For example if systemd were to return file descriptors
// corresponding with "udp, tcp, tcp", then the slice would contain {nil, net.Listener, net.Listener} // corresponding with "udp, tcp, tcp", then the slice would contain {nil, net.Listener, net.Listener}
func Listeners(unsetEnv bool) ([]net.Listener, error) { func Listeners() ([]net.Listener, error) {
files := Files(unsetEnv) files := Files(true)
listeners := make([]net.Listener, len(files)) listeners := make([]net.Listener, len(files))
for i, f := range files { for i, f := range files {
if pc, err := net.FileListener(f); err == nil { if pc, err := net.FileListener(f); err == nil {
listeners[i] = pc listeners[i] = pc
f.Close()
}
}
return listeners, nil
}
// ListenersWithNames maps a listener name to a set of net.Listener instances.
func ListenersWithNames() (map[string][]net.Listener, error) {
files := Files(true)
listeners := map[string][]net.Listener{}
for _, f := range files {
if pc, err := net.FileListener(f); err == nil {
current, ok := listeners[f.Name()]
if !ok {
listeners[f.Name()] = []net.Listener{pc}
} else {
listeners[f.Name()] = append(current, pc)
}
f.Close()
} }
} }
return listeners, nil return listeners, nil
...@@ -40,8 +60,8 @@ func Listeners(unsetEnv bool) ([]net.Listener, error) { ...@@ -40,8 +60,8 @@ func Listeners(unsetEnv bool) ([]net.Listener, error) {
// TLSListeners returns a slice containing a net.listener for each matching TCP socket type // TLSListeners returns a slice containing a net.listener for each matching TCP socket type
// passed to this process. // passed to this process.
// It uses default Listeners func and forces TCP sockets handlers to use TLS based on tlsConfig. // It uses default Listeners func and forces TCP sockets handlers to use TLS based on tlsConfig.
func TLSListeners(unsetEnv bool, tlsConfig *tls.Config) ([]net.Listener, error) { func TLSListeners(tlsConfig *tls.Config) ([]net.Listener, error) {
listeners, err := Listeners(unsetEnv) listeners, err := Listeners()
if listeners == nil || err != nil { if listeners == nil || err != nil {
return nil, err return nil, err
...@@ -58,3 +78,26 @@ func TLSListeners(unsetEnv bool, tlsConfig *tls.Config) ([]net.Listener, error) ...@@ -58,3 +78,26 @@ func TLSListeners(unsetEnv bool, tlsConfig *tls.Config) ([]net.Listener, error)
return listeners, err return listeners, err
} }
// TLSListenersWithNames maps a listener name to a net.Listener with
// the associated TLS configuration.
func TLSListenersWithNames(tlsConfig *tls.Config) (map[string][]net.Listener, error) {
listeners, err := ListenersWithNames()
if listeners == nil || err != nil {
return nil, err
}
if tlsConfig != nil && err == nil {
for _, ll := range listeners {
// Activate TLS only for TCP sockets
for i, l := range ll {
if l.Addr().Network() == "tcp" {
ll[i] = tls.NewListener(l, tlsConfig)
}
}
}
}
return listeners, err
}
...@@ -24,13 +24,14 @@ import ( ...@@ -24,13 +24,14 @@ import (
// The order of the file descriptors is preserved in the returned slice. // The order of the file descriptors is preserved in the returned slice.
// Nil values are used to fill any gaps. For example if systemd were to return file descriptors // Nil values are used to fill any gaps. For example if systemd were to return file descriptors
// corresponding with "udp, tcp, udp", then the slice would contain {net.PacketConn, nil, net.PacketConn} // corresponding with "udp, tcp, udp", then the slice would contain {net.PacketConn, nil, net.PacketConn}
func PacketConns(unsetEnv bool) ([]net.PacketConn, error) { func PacketConns() ([]net.PacketConn, error) {
files := Files(unsetEnv) files := Files(true)
conns := make([]net.PacketConn, len(files)) conns := make([]net.PacketConn, len(files))
for i, f := range files { for i, f := range files {
if pc, err := net.FilePacketConn(f); err == nil { if pc, err := net.FilePacketConn(f); err == nil {
conns[i] = pc conns[i] = pc
f.Close()
} }
} }
return conns, nil return conns, nil
......
// Copyright 2014 Docker, Inc. // Copyright 2014 Docker, Inc.
// Copyright 2015-2018 CoreOS, Inc.
// //
// Licensed under the Apache License, Version 2.0 (the "License"); // Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. // you may not use this file except in compliance with the License.
...@@ -13,7 +14,11 @@ ...@@ -13,7 +14,11 @@
// limitations under the License. // limitations under the License.
// //
// Code forked from Docker project // Package daemon provides a Go implementation of the sd_notify protocol.
// It can be used to inform systemd of service start-up completion, watchdog
// events, and other status changes.
//
// https://www.freedesktop.org/software/systemd/man/sd_notify.html#Description
package daemon package daemon
import ( import (
...@@ -21,6 +26,25 @@ import ( ...@@ -21,6 +26,25 @@ import (
"os" "os"
) )
const (
// SdNotifyReady tells the service manager that service startup is finished
// or the service finished loading its configuration.
SdNotifyReady = "READY=1"
// SdNotifyStopping tells the service manager that the service is beginning
// its shutdown.
SdNotifyStopping = "STOPPING=1"
// SdNotifyReloading tells the service manager that this service is
// reloading its configuration. Note that you must call SdNotifyReady when
// it completed reloading.
SdNotifyReloading = "RELOADING=1"
// SdNotifyWatchdog tells the service manager to update the watchdog
// timestamp for the service.
SdNotifyWatchdog = "WATCHDOG=1"
)
// SdNotify sends a message to the init daemon. It is common to ignore the error. // SdNotify sends a message to the init daemon. It is common to ignore the error.
// If `unsetEnvironment` is true, the environment variable `NOTIFY_SOCKET` // If `unsetEnvironment` is true, the environment variable `NOTIFY_SOCKET`
// will be unconditionally unset. // will be unconditionally unset.
...@@ -29,7 +53,7 @@ import ( ...@@ -29,7 +53,7 @@ import (
// (false, nil) - notification not supported (i.e. NOTIFY_SOCKET is unset) // (false, nil) - notification not supported (i.e. NOTIFY_SOCKET is unset)
// (false, err) - notification supported, but failure happened (e.g. error connecting to NOTIFY_SOCKET or while sending data) // (false, err) - notification supported, but failure happened (e.g. error connecting to NOTIFY_SOCKET or while sending data)
// (true, nil) - notification supported, data has been sent // (true, nil) - notification supported, data has been sent
func SdNotify(unsetEnvironment bool, state string) (sent bool, err error) { func SdNotify(unsetEnvironment bool, state string) (bool, error) {
socketAddr := &net.UnixAddr{ socketAddr := &net.UnixAddr{
Name: os.Getenv("NOTIFY_SOCKET"), Name: os.Getenv("NOTIFY_SOCKET"),
Net: "unixgram", Net: "unixgram",
...@@ -41,10 +65,9 @@ func SdNotify(unsetEnvironment bool, state string) (sent bool, err error) { ...@@ -41,10 +65,9 @@ func SdNotify(unsetEnvironment bool, state string) (sent bool, err error) {
} }
if unsetEnvironment { if unsetEnvironment {
err = os.Unsetenv("NOTIFY_SOCKET") if err := os.Unsetenv("NOTIFY_SOCKET"); err != nil {
} return false, err
if err != nil { }
return false, err
} }
conn, err := net.DialUnix(socketAddr.Net, nil, socketAddr) conn, err := net.DialUnix(socketAddr.Net, nil, socketAddr)
...@@ -54,9 +77,7 @@ func SdNotify(unsetEnvironment bool, state string) (sent bool, err error) { ...@@ -54,9 +77,7 @@ func SdNotify(unsetEnvironment bool, state string) (sent bool, err error) {
} }
defer conn.Close() defer conn.Close()
_, err = conn.Write([]byte(state)) if _, err = conn.Write([]byte(state)); err != nil {
// Error sending the message
if err != nil {
return false, err return false, err