From 989b5d38631d3ef7004256d8e17d314219024bba Mon Sep 17 00:00:00 2001 From: ale <ale@incal.net> Date: Fri, 17 Aug 2018 07:24:02 +0100 Subject: [PATCH] Update clientutil.Backend interface --- client/client.go | 6 +- .../ai3/go-common/clientutil/backend.go | 111 ++----- .../ai3/go-common/clientutil/balancer.go | 271 ++++++++++++++++++ .../ai3/go-common/clientutil/cpu.prof | Bin 0 -> 4566 bytes .../ai3/go-common/clientutil/dns.go | 98 +++++++ .../ai3/go-common/clientutil/doc.go | 37 +++ .../ai3/go-common/clientutil/error.go | 35 +++ .../ai3/go-common/clientutil/json.go | 45 --- .../ai3/go-common/clientutil/retry.go | 92 ------ .../ai3/go-common/clientutil/track.go | 123 ++++++++ .../ai3/go-common/clientutil/transport.go | 175 +++-------- .../ai3/go-common/ldap/parse.go | 2 + .../ai3/go-common/ldap/pool.go | 76 +++++ .../ai3/go-common/ldap/search.go | 63 ---- .../ai3/go-common/serverutil/http.go | 103 +++++-- .../ai3/go-common/serverutil/json.go | 12 +- .../ai3/go-common/serverutil/proxy_headers.go | 78 +++++ vendor/git.autistici.org/id/go-sso/README.md | 20 ++ vendor/github.com/cenkalti/backoff/README.md | 2 +- vendor/github.com/cenkalti/backoff/context.go | 3 +- vendor/github.com/cenkalti/backoff/retry.go | 1 - vendor/github.com/cenkalti/backoff/ticker.go | 2 - vendor/github.com/cenkalti/backoff/tries.go | 4 +- .../miscreant/miscreant/go/block/block.go | 2 +- .../miscreant/miscreant/go/pmac/pmac.go | 14 +- .../prometheus/common/model/time.go | 3 + vendor/github.com/theckman/go-flock/README.md | 6 +- vendor/github.com/theckman/go-flock/flock.go | 21 ++ vendor/golang.org/x/net/context/context.go | 2 + vendor/gopkg.in/yaml.v2/LICENSE | 208 +------------- vendor/vendor.json | 126 ++++---- 31 files changed, 992 insertions(+), 749 deletions(-) create mode 100644 vendor/git.autistici.org/ai3/go-common/clientutil/balancer.go create mode 100644 vendor/git.autistici.org/ai3/go-common/clientutil/cpu.prof create mode 100644 vendor/git.autistici.org/ai3/go-common/clientutil/dns.go create mode 100644 vendor/git.autistici.org/ai3/go-common/clientutil/doc.go create mode 100644 vendor/git.autistici.org/ai3/go-common/clientutil/error.go delete mode 100644 vendor/git.autistici.org/ai3/go-common/clientutil/json.go delete mode 100644 vendor/git.autistici.org/ai3/go-common/clientutil/retry.go create mode 100644 vendor/git.autistici.org/ai3/go-common/clientutil/track.go delete mode 100644 vendor/git.autistici.org/ai3/go-common/ldap/search.go create mode 100644 vendor/git.autistici.org/ai3/go-common/serverutil/proxy_headers.go diff --git a/client/client.go b/client/client.go index 16787db7..307f3eeb 100644 --- a/client/client.go +++ b/client/client.go @@ -36,7 +36,7 @@ func (c *ksClient) Open(ctx context.Context, shard, username, password string, t TTL: ttl, } var resp keystore.OpenResponse - return clientutil.DoJSONHTTPRequest(ctx, c.be.Client(shard), c.be.URL(shard)+"/api/open", &req, &resp) + return c.be.Call(ctx, shard, "/api/open", &req, &resp) } func (c *ksClient) Get(ctx context.Context, shard, username, ssoTicket string) ([]byte, error) { @@ -45,7 +45,7 @@ func (c *ksClient) Get(ctx context.Context, shard, username, ssoTicket string) ( SSOTicket: ssoTicket, } var resp keystore.GetResponse - err := clientutil.DoJSONHTTPRequest(ctx, c.be.Client(shard), c.be.URL(shard)+"/api/get_key", &req, &resp) + err := c.be.Call(ctx, shard, "/api/get_key", &req, &resp) return resp.Key, err } @@ -54,5 +54,5 @@ func (c *ksClient) Close(ctx context.Context, shard, username string) error { Username: username, } var resp keystore.CloseResponse - return clientutil.DoJSONHTTPRequest(ctx, c.be.Client(shard), c.be.URL(shard)+"/api/close", &req, &resp) + return c.be.Call(ctx, shard, "/api/close", &req, &resp) } diff --git a/vendor/git.autistici.org/ai3/go-common/clientutil/backend.go b/vendor/git.autistici.org/ai3/go-common/clientutil/backend.go index 1f49a7e5..6580d0eb 100644 --- a/vendor/git.autistici.org/ai3/go-common/clientutil/backend.go +++ b/vendor/git.autistici.org/ai3/go-common/clientutil/backend.go @@ -1,15 +1,10 @@ package clientutil import ( - "crypto/tls" - "fmt" - "net/http" - "net/url" - "sync" - "time" + "context" ) -// BackendConfig specifies the configuration to access a service. +// BackendConfig specifies the configuration of a service backend. // // Services with multiple backends can be replicated or partitioned, // depending on a configuration switch, making it a deployment-time @@ -18,102 +13,30 @@ import ( // 'shard' parameter on their APIs. type BackendConfig struct { URL string `yaml:"url"` - Sharded bool `yaml:"sharded"` TLSConfig *TLSClientConfig `yaml:"tls_config"` + Sharded bool `yaml:"sharded"` + Debug bool `yaml:"debug"` } // Backend is a runtime class that provides http Clients for use with // a specific service backend. If the service can't be partitioned, -// pass an empty string to the Client method. +// pass an empty string to the Call method. type Backend interface { - // URL for the service for a specific shard. - URL(string) string + // Call a remote method. The sharding behavior is the following: + // + // Services that support sharding (partitioning) should always + // include the shard ID in their Call() requests. Users can + // then configure backends to be sharded or not in their + // Config. When invoking Call with a shard ID on a non-sharded + // service, the shard ID is simply ignored. Invoking Call + // *without* a shard ID on a sharded service is an error. + Call(context.Context, string, string, interface{}, interface{}) error - // Client that can be used to make a request to the service. - Client(string) *http.Client + // Close all resources associated with the backend. + Close() } // NewBackend returns a new Backend with the given config. func NewBackend(config *BackendConfig) (Backend, error) { - u, err := url.Parse(config.URL) - if err != nil { - return nil, err - } - - var tlsConfig *tls.Config - if config.TLSConfig != nil { - tlsConfig, err = config.TLSConfig.TLSConfig() - if err != nil { - return nil, err - } - } - - if config.Sharded { - return &replicatedClient{ - u: u, - c: newHTTPClient(u, tlsConfig), - }, nil - } - return &shardedClient{ - baseURL: u, - tlsConfig: tlsConfig, - urls: make(map[string]*url.URL), - shards: make(map[string]*http.Client), - }, nil -} - -type replicatedClient struct { - c *http.Client - u *url.URL -} - -func (r *replicatedClient) Client(_ string) *http.Client { return r.c } -func (r *replicatedClient) URL(_ string) string { return r.u.String() } - -type shardedClient struct { - baseURL *url.URL - tlsConfig *tls.Config - mx sync.Mutex - urls map[string]*url.URL - shards map[string]*http.Client -} - -func (s *shardedClient) getShardURL(shard string) *url.URL { - if shard == "" { - return s.baseURL - } - u, ok := s.urls[shard] - if !ok { - var tmp = *s.baseURL - tmp.Host = fmt.Sprintf("%s.%s", shard, tmp.Host) - u = &tmp - s.urls[shard] = u - } - return u -} - -func (s *shardedClient) URL(shard string) string { - s.mx.Lock() - defer s.mx.Unlock() - return s.getShardURL(shard).String() -} - -func (s *shardedClient) Client(shard string) *http.Client { - s.mx.Lock() - defer s.mx.Unlock() - - client, ok := s.shards[shard] - if !ok { - u := s.getShardURL(shard) - client = newHTTPClient(u, s.tlsConfig) - s.shards[shard] = client - } - return client -} - -func newHTTPClient(u *url.URL, tlsConfig *tls.Config) *http.Client { - return &http.Client{ - Transport: NewTransport([]string{u.Host}, tlsConfig, nil), - Timeout: 30 * time.Second, - } + return newBalancedBackend(config, defaultResolver) } diff --git a/vendor/git.autistici.org/ai3/go-common/clientutil/balancer.go b/vendor/git.autistici.org/ai3/go-common/clientutil/balancer.go new file mode 100644 index 00000000..9d9b7dd6 --- /dev/null +++ b/vendor/git.autistici.org/ai3/go-common/clientutil/balancer.go @@ -0,0 +1,271 @@ +package clientutil + +import ( + "bytes" + "context" + "crypto/tls" + "encoding/json" + "errors" + "fmt" + "log" + "math/rand" + "net/http" + "net/url" + "os" + "strconv" + "strings" + "time" + + "github.com/cenkalti/backoff" +) + +// Our own narrow logger interface. +type logger interface { + Printf(string, ...interface{}) +} + +// A nilLogger is used when Config.Debug is false. +type nilLogger struct{} + +func (l nilLogger) Printf(_ string, _ ...interface{}) {} + +// Parameters that define the exponential backoff algorithm used. +var ( + ExponentialBackOffInitialInterval = 100 * time.Millisecond + ExponentialBackOffMultiplier = 1.4142 +) + +// newExponentialBackOff creates a backoff.ExponentialBackOff object +// with our own default values. +func newExponentialBackOff() *backoff.ExponentialBackOff { + b := backoff.NewExponentialBackOff() + b.InitialInterval = ExponentialBackOffInitialInterval + b.Multiplier = ExponentialBackOffMultiplier + + // Set MaxElapsedTime to 0 because we expect the overall + // timeout to be dictated by the request Context. + b.MaxElapsedTime = 0 + + return b +} + +// Balancer for HTTP connections. It will round-robin across available +// backends, trying to avoid ones that are erroring out, until one +// succeeds or returns a permanent error. +// +// This object should not be used for load balancing of individual +// HTTP requests: it doesn't do anything smart beyond trying to avoid +// broken targets. It's meant to provide a *reliable* connection to a +// set of equivalent services for HA purposes. +type balancedBackend struct { + *backendTracker + *transportCache + baseURI *url.URL + sharded bool + resolver resolver + log logger +} + +func newBalancedBackend(config *BackendConfig, resolver resolver) (*balancedBackend, error) { + u, err := url.Parse(config.URL) + if err != nil { + return nil, err + } + + var tlsConfig *tls.Config + if config.TLSConfig != nil { + tlsConfig, err = config.TLSConfig.TLSConfig() + if err != nil { + return nil, err + } + } + + var logger logger = &nilLogger{} + if config.Debug { + logger = log.New(os.Stderr, fmt.Sprintf("backend %s: ", u.Host), 0) + } + return &balancedBackend{ + backendTracker: newBackendTracker(u.Host, resolver, logger), + transportCache: newTransportCache(tlsConfig), + sharded: config.Sharded, + baseURI: u, + resolver: resolver, + log: logger, + }, nil +} + +// Call the backend. Makes an HTTP POST request to the specified uri, +// with a JSON-encoded request body. It will attempt to decode the +// response body as JSON. +func (b *balancedBackend) Call(ctx context.Context, shard, path string, req, resp interface{}) error { + data, err := json.Marshal(req) + if err != nil { + return err + } + + var tg targetGenerator = b.backendTracker + if b.sharded && shard != "" { + tg = newShardedGenerator(shard, b.baseURI.Host, b.resolver) + } + seq := newSequence(tg) + b.log.Printf("%016x: initialized", seq.ID()) + + var httpResp *http.Response + err = backoff.Retry(func() error { + req, rerr := b.newJSONRequest(path, shard, data) + if rerr != nil { + return rerr + } + httpResp, rerr = b.do(ctx, seq, req) + return rerr + }, backoff.WithContext(newExponentialBackOff(), ctx)) + if err != nil { + return err + } + defer httpResp.Body.Close() // nolint + + if httpResp.Header.Get("Content-Type") != "application/json" { + return errors.New("not a JSON response") + } + + if resp == nil { + return nil + } + return json.NewDecoder(httpResp.Body).Decode(resp) +} + +// Return the URI to be used for the request. This is used both in the +// Host HTTP header and as the TLS server name used to pick a server +// certificate (if using TLS). +func (b *balancedBackend) getURIForRequest(shard, path string) string { + u := *b.baseURI + if b.sharded && shard != "" { + u.Host = fmt.Sprintf("%s.%s", shard, u.Host) + } + u.Path = appendPath(u.Path, path) + return u.String() +} + +// Build a http.Request object. +func (b *balancedBackend) newJSONRequest(path, shard string, data []byte) (*http.Request, error) { + req, err := http.NewRequest("POST", b.getURIForRequest(shard, path), bytes.NewReader(data)) + if err != nil { + return nil, err + } + req.Header.Set("Content-Type", "application/json") + req.Header.Set("Content-Length", strconv.FormatInt(int64(len(data)), 10)) + return req, nil +} + +// Select a new target from the given sequence and send the request to +// it. Wrap HTTP errors in a RemoteError object. +func (b *balancedBackend) do(ctx context.Context, seq *sequence, req *http.Request) (resp *http.Response, err error) { + target, terr := seq.Next() + if terr != nil { + return + } + + b.log.Printf("sequence %016x: connecting to %s", seq.ID(), target) + client := &http.Client{ + Transport: b.transportCache.getTransport(target), + } + resp, err = client.Do(req.WithContext(ctx)) + if err == nil && resp.StatusCode != 200 { + err = remoteErrorFromResponse(resp) + if !isStatusTemporary(resp.StatusCode) { + err = backoff.Permanent(err) + } + resp.Body.Close() // nolint + resp = nil + } + + seq.Done(target, err) + return +} + +var errNoTargets = errors.New("no available backends") + +type targetGenerator interface { + getTargets() []string + setStatus(string, bool) +} + +// A replicatedSequence repeatedly iterates over available backends in order of +// preference. Once in a while it refreshes its list of available +// targets. +type sequence struct { + id uint64 + tg targetGenerator + targets []string + pos int +} + +func newSequence(tg targetGenerator) *sequence { + return &sequence{ + id: rand.Uint64(), + tg: tg, + targets: tg.getTargets(), + } +} + +func (s *sequence) ID() uint64 { return s.id } + +func (s *sequence) reloadTargets() { + targets := s.tg.getTargets() + if len(targets) > 0 { + s.targets = targets + s.pos = 0 + } +} + +// Next returns the next target. +func (s *sequence) Next() (t string, err error) { + if s.pos >= len(s.targets) { + s.reloadTargets() + if len(s.targets) == 0 { + err = errNoTargets + return + } + } + t = s.targets[s.pos] + s.pos++ + return +} + +func (s *sequence) Done(t string, err error) { + s.tg.setStatus(t, err == nil) +} + +// A shardedGenerator returns a single sharded target to a sequence. +type shardedGenerator struct { + id uint64 + addrs []string +} + +func newShardedGenerator(shard, base string, resolver resolver) *shardedGenerator { + return &shardedGenerator{ + id: rand.Uint64(), + addrs: resolver.ResolveIP(fmt.Sprintf("%s.%s", shard, base)), + } +} + +func (g *shardedGenerator) getTargets() []string { return g.addrs } +func (g *shardedGenerator) setStatus(_ string, _ bool) {} + +// Concatenate two URI paths. +func appendPath(a, b string) string { + if strings.HasSuffix(a, "/") && strings.HasPrefix(b, "/") { + return a + b[1:] + } + return a + b +} + +// Some HTTP status codes are treated are temporary errors. +func isStatusTemporary(code int) bool { + switch code { + case http.StatusTooManyRequests, http.StatusBadGateway, http.StatusServiceUnavailable, http.StatusGatewayTimeout: + return true + default: + return false + } +} diff --git a/vendor/git.autistici.org/ai3/go-common/clientutil/cpu.prof b/vendor/git.autistici.org/ai3/go-common/clientutil/cpu.prof new file mode 100644 index 0000000000000000000000000000000000000000..89fe0a7d104f6c1ca9466d218abbfe393d431402 GIT binary patch literal 4566 zcmb2|=3oE;mj6pCI7>vXPyH6JaBwl}7uSXz9cwkV<u&eFda3P(<C|#{bvQR{bPNgK zpC`I%T~>H#a)Fs;o5amFiF^rzZr&R|)gN1^f1D<%zR`GI&g3~qJZ<{k>iE5>C`y~5 za?WCPvhnkMtHQj?x+VK9m#=*Heee5yyZ1-0k~y#P;`M9k#@h2S5|R_Ov~O^{u>O7L z<UjND#QQgozk6R_9G2p8_QWYJGofE`wU0vV*Y5viVymobx^$}O8kS$b_7z3FsedN@ zrKe|=WUOW9RHyqlx9|J-)_e1Y_n{wqHk|S@>+-AqE%bNthW9!DvQy3+d;j-OWAwYd zKTfrtTqq>gv?+j7n)~ss^Y?0RpEfS<c)$C%+|SfbCZ9w4wng7LmQL~fz|FZt>zIl} z|Kq0{m~vR`KAs9Z<aS_bAd@s>gMxoRL+_FM`^u(lm{`!qnWHi#dFKJI5B%%zct>!~ zk<{EEG^0cKspr$j4op@SAxgaxk&9X)HRt{RbyCoaQ@b@YDP7a&*vX<+rW}#DKW{uI z8F9%;X>SNLU|oJ<1%q_&`Tx&Y5*I(@jemX7Ky>-3sa`V-|6JT{uRf9I8n;w)riJgk zbqvzY`~SUq?IO+WUUN%gMx*hQnO-wmKe+qv@!KNsMr4b?nM0AEd{`qS|NXSyuM#CG z^DQ$~VX@BnutPaIa#laJ7xlJ^HkpZuggmkIn$dfBcOQ3#jE>+1iy7U;Pc6ANeEjj} zdYzZh@xwcX8hhI-E&S)LXW)J;Jzq3JBJbNq1Dyk`Ow4~iB{nk3e6!^iIM{JO?Lnir zz2Z#a2Qvkop0RW!g(N)v@xz))yIucBgk?zLRGWCt&7D84i=XQc@w(x1g6oz`L<jd% zSFR176~C@=D^CqF<J|X7+KzM8#;88c18R?(_3!)NNU1pO-*-~r_>P#x466^s*ZdPn z6fL;t-^aD$XjG%`!Q}ioiaA_1R#9`zSwlje&ieE+fT_0st3Qvgj;!gdUe2k#vgUR? z!l%^zX2lq~GPYH`dzzf0aA@+rj~t~D>esas%|zuZ{)KTJ4p|}QwA_i=>_jiu1}~e! zZwwc;9?r;jQ8h`NyeERuj5ogiW!a%+2W20#<V%?(Z{Fj=XeK+q);;0!Lsx+tB@tcT ztY*yj_kA|_n0Da18b=PB&f!$1*`42I#Bcam1nF&<xj|++caGS+3eS*ZIu>t_CyK2( zAo^*^Cr$&VUvYJLJ{=FQoXWhMerC>6hRGk-pXa!mTCpUgV^X5(x+&qBiDuIKOJDeH zsxW&abRj3AclA?KFV^4>*Y5B2oxpT@YLFTGy>BPEwj^}#+19nuas%J>ldnY+ia(g= z&s|n<rnR|l+u2s$9gg=_FwE}e-+$HrhE2uU<IfJN-BHO8Ig}$eZ&!TE*_wi*A3rA= z=S@rYnsN3xce`GIlWxOPrYMH+2ck?-4gB^0WE=%&s4w8ylC-*JsZg5WpUcl#L|Rue zbsZABqqIQ7u0CK+C~G%s2y5;QwM`qi`uA1N3yI-K|NB6%r7UOq`k)5gBlqv>Y$_Mr z^CY=@0kbvBHjeW)e=S%yNd3E8?q^z(wERwJ+{FCA$4_panzQrsGyM*i)nY-WA&;l? zbssqPph5nNM9$H*N*fGoKBzVNw!dfc;M!oZ@56?O&co7mQb!V{^CnN9(tmz_K$Prk z!Q~d$AAD?HboJ_i+PbJK2g>f~Efz}K`s32X%_3)_A~bu}x32z}^;3#<Mpt~zYaYpk zhxlumLvJ)%KgphIu=?=%JBk^l7Zfu}3uNQeGrm^b-x%H*#_#`gOVy1|YuEOi1OI=# z$!;+{U|ze1vFxGV&S=JK9sDu7`fKiT`2XPDDOmP6FEY5n^kKJt*Q&fG?m9(NM<!vW z=$`mJ?=3!x9gcn6s@Lu49((PS`ZHTWt+gvdU%!%;n!Q46_Q%ideG`Sw7Ti0(JIy00 zXx<Wr=%)F<q5_2KHAVa4y8eHw-MEQm-%ImFss|>XUM-Sv`a{TlL#rxHneVgD26-fU z`Dq`R_SpWO@|j3B@tPdTFA4ntm8^9?`y8IuOR@)5TC9Ju;+d-Vblv!cEjPXXypx=` z{&e%ZeKI^IPi(zrtUYA^Z1bL|6+5K%|5|tAlIRE7`>VAVh*v*deM(Wk;>)Sfi5XX4 zxq8i5d$77*;L9e_rh85O^Mu9LwWQbn@-nz6BAcTtw?|<^=!azaXjaKH967T8o;(r! z^f=4HKG>qpG$Nz3CAQ!GFZ;#2*ABciWQt}syTx?7A-={dLHEM*WZelz*X&7|uxrP% z)VI67N{b{&e{9_^w<P%5A?Ld2IPU9@ziwLGa#QWk-}^^m_wc<ss`&bVYk117v^`%s zP8|ALa5B8cw&Dfn8?Kt?2jeH6;4L_D+{}Tm_|dDJqw@c!?>KCJXnswO@|&Z3Kk;&H z(Ej)$-sOhbLEXtmD`zLjzY|ux+a!M9!hPii%lPEkiT3ZN=gXvAIp#Ziea7CRZl*Q7 z^R1P%K5<#RxStSy!2N@A(vOVe=VL?;yKCFL%f7%Rf3*M9RIUxHKWINcH|bgUA#J-d z)fygqwO8wy=hr$!>|w2YlpOuw@sGm9LiK-t4=;Lm>p`Y`cC-Hjb(g!P4`=(&Q#yP5 zA-71MKz6$w=Wmt5`FThG|M?pJ;>hX;o?6q4%0%y|JoKIP$1}_M_J;pEUc8t65Uq7a z;B~`n)jg4uvQM4<eA0Z`mbHIZ^_^R{_VyHm+kNNj{!5t7@$+a15lxy~;Bbg}Qp|dW z-BR;y1eb*T_nE-PTXK87*v$f7yShItnzvcqSN69@h|5g*ut24019Sb)^uXfihe<O9 z_KDi5K3rb&C2G%KIl+BL3sP)6nr}B>ct2~c^@^>r?fzfcuRFdL5M8(U*UU3zkL2si zj3qblF4I)avDRT)xvD3(Ia+0p>m>e*{ckVmC))oFXn1?*{(a4?7g-ihc7Mp)5qc<^ zd584=Z|{_!Jj{M@+pdQ9j@rZANj26R%O5w|*YrJ*{a_KRnwrBdGwnkGLw3cN7>56s zH3H9Z{t4V4u~Y2dIrSKBcNH5Ufs#K#{}-#gx2l-?D@bxTpPlZ*<;)hH|7)gSNK?0Q zpBZ$l?2xR`J;4Jq@-?5;KK<9Ln6Yk>(KhZruZoaK@25;L@P63n&$cg{A@f4&_TupG zdAbYLE4$`cv@ZSl(cRUl?C{}9L8r=ohx>Gt9*z~dr+V^ksNy#1chf%Pd8TCY|NAjb zq2hb+X&vrK-Ag0;na&)TZ>#(%pn0*0Z|e-U?;aKX^N&=2c;3!3tKo6oG_?nDobSJ0 zjQjJDvGG*_cdU)#vcra){egV`JbjWooO*2cdd-;mWBc)68tjt<#4q|EsujAYe)70x zie$~lFvHzN^)EOL`P5Zx9?GXb%$MKY8#ALx{13yD2TNMiRe#ng-LCkc$hiFB|K9~t zex4Ot4V!;l;P(^S%^tV-M@t0%oM|7nsI2;0_2-(noWhlc`lTOQW-z9!*f?j*u>5f@ z__*h5TgP+n8aE5goBD+PMS^&JMMTD3?siRn=QZMTQ$9F8V*32SU%%^L+ONxg9Wx93 z4i!(TS<Ix<?jJ8=9>A9Oee0<Mm7M3jlca8S>py+3E~Rox{rOz0YiA<LzEvInT2w8* z^1pv>_xBr;a@T$2%4KcmZTs8)@aUE~={x2vls3HWdE9id-n?zAORXnwT)QJr>dvR4 zd5L_#q(1L>z0&KBiO|Q|Zt309uUGpXKJ|9BU$kY^mDN{gM2Q#Ynoi1C5goJos=0~j zbrZF#%bv|zw(IDsRi-a&7G8V(wNu33^t$?FPE)5Ni!N=7>p#3T#c|oW?AaB)QP%_3 zuj=L1+`G-}=9~-S%cWku67KvJ;v4K+TO7M-o!ivMM@?l^Rx93|k`W?3OIj*q>q^Jf zm$PRJovhlcH+4;>hiOJ{XGm@Dx=h|zE3&_<#a1tu+EuoG_x-D;m+!3XE8FUG?a8{R zRktlq2fM$zIVUPDbK2XYyW1wMvpRV)a^A#;8%?&`xL>Yxx?FwL!{)iwp?h0mW2Ljj zCKePWrQazwsgKslT`k;nbw*zL_uccBeP30q?=BYI!BM&BXud!<YoO%qhyN}$o$kDJ z$7lOAPqS;sV@%)tQaf(-cX?E7k7SOJ&qV(B&tGp#Exa52TJQLNLje;Pk6U^f7lk|` zD=alD6)jKSaDU?|_C0B$R(JL5>u&5KfnA*&c8L3{vd<81Gnm|#w(4`+;+@;>-JZKO z_Wv0H!_3a=EX!JRoqlVv?^d~UuXV9C_?d4yqQxQQ{$$DCYZuO}n^tCObm!x|e{xAP z71*|X{3$=TZK~N$t85EH7lrwv9Cs8qFQ3%lA-eM5+>D-iy4#+9tZl4xRy6gwof$lR z;={6Y!L$E=f2)>O*uSFk=UVw~wUvEoJ0ov@Jh5RL{{oLCSq--qy|lIQGQDc5pgVQ* zm#EUi?V<Vo+0jcPmmb|)(f>R(dE%-@=S6kbT-&?F-^yn_IP>smz}FSUU5_*03F`E$ z3P1jIwujd9kF`g;?QS?dHCqxFyVa(lW?OdAJl%O84({6_>!D!IV`6vu+m7?PS+(=B zgYI&gcG@o3@%rY?9;@Xu?teP7Ci(Rd$&9O-hrO&UPhS#J)Bh3}{{G>~IVZ1vWHg+1 zb<VwaJ`dOM#B#AZxf%OL`@cR~?W&;i?){?A%h|jQubY(2yYas8e3|X>4VR`*fAC(B zQ`Ds>dEzS5ogrPXHeX&YZW{llSN({elK--n6P9T@L0h*jHl6jH@3wq#pQrpHKV_|@ z2|7Vnm7cX`<gC>#S`juk{>=AECxhS#0)_v6x2#xg7qC#xBzSYlyqLu=_ulbxOZJSu z`{SX?mW*YRGd3yYR@m-z@!4)%I5&Flte27>Tb6ylwK8N)bM?7@y4%v1d|Q3?W1`#I zhc^n_Z#-$c`Tw!xt6BW{6Mi{L6<)e`=jOJh&GGXeuX|qi!*Y_%v9&4Yyc1JCv2zB9 zm+w{BaaX#x#DDd(Lht+cms)1NTbU@78S!$)^@*Ex-ii3{O9=kwspWI#`n<%q4|h#B z<Gmhy_~+3rXTM9v^;+pkXeQ|{ds|dGtA1v7noaiX5Ob4om7T9GztxonZrmd8Bw>>n z!snPY(P7HF4<R4dS*O^0o(gDJ3tq<8$P~Em(9;rGejkT_Q3kg!#E9`uOA`ye`$(?# zMGMz2xsX-oFSpw3#lO@m|5kJ{bJ_lW{S6CF+|!R;kd<mDbu;FWNy%B`ob29b7Hs8g zv0{JNtQ`eKF3(%9ve%mBWb~h3CszxX{}EA_zQ6u)f?<g5;i*MBL9U$lrg^bW_BPqp z`d--c_B5}R-tDu6+a`-&|I66D<=6f&uiJ6&W^9}G#B+Z@Olj1;*(H0c9@V`)r|$Rg zkzPY<^!dB*({fe^$VgtlEVF*whJ*1MG0W%PiO!Fhm3imH+|&0{W{SL3c5XlSO7DA^ z`s6;VLXUr0yUuNnt5bQKm6>lE(;r>(K9()&?dG<<*P0k-O>tOl&{^8`ruW#9;NE9~ z6BDoaCrxbB&5X|Bx@2h=V;IQT`M<+%g%#VZGrWBdKOQ>&_EGx9SKk&cy!v)M@5;P- z&vUQkP8S_FjWK-Bd4jXd$L~o{n{0Q;&(#-iR2e`2CQ?50#;gDSdb4d6Y=TTZYOa_s zySj~CHOMbzZj^+}SGjfHFMZj0t}y4gy-k3pq{{V&dDphZ$Srj|TTuOJtL=wl*%eWX z+>5LA{x8c}VlsE~xmL6Dwu{6r#TUoEt9X6Ica@du)P)l1n=eN6r>XkZseZ3|vt;$D zqmO2vs`$7jIbu<Uy6B3e+}UZ*c0~P~clD}Y)rY7{dFpjux{GV)Hq8ifUN*On)#0LE z?ybyU6Cxj7)fF<y*d2Z*oOyqAZmhAZujrls^Ex``-OGBt*JkCV-7`*H{hqgInMYvm z^F8Hz(`Fp}=hU~?J)>@a)XR6<zwCH+Q_{KLbybUh#<MN5aetncUHJDS?vuhzi~4{l zo~rOy4>Ge@{dOl;Z_AtA+_Pj=@LY!bH?Cg2XSreJi`TCI8Y=p4%{>wL;N_jBTBmt= z(VEJ0i+?omIop_Rkm|iT?VR;ackw2Ra$WyS%ge=AQ-3zM?fdG=HmyC`ZFR8ZufrX# zc{!U){1%sgTqB~VVwuA9S5`&-k|S5w!i3;UPeR&bmU~~*4cV#?uwUxRJC?`HeJ@p# gdY64K(thcdyeo6}pZ&}X4FCVXj&iMRP-b8N06^*FKmY&$ literal 0 HcmV?d00001 diff --git a/vendor/git.autistici.org/ai3/go-common/clientutil/dns.go b/vendor/git.autistici.org/ai3/go-common/clientutil/dns.go new file mode 100644 index 00000000..ed30f873 --- /dev/null +++ b/vendor/git.autistici.org/ai3/go-common/clientutil/dns.go @@ -0,0 +1,98 @@ +package clientutil + +import ( + "log" + "net" + "sync" + "time" + + "golang.org/x/sync/singleflight" +) + +type resolver interface { + ResolveIP(string) []string +} + +type dnsResolver struct{} + +func (r *dnsResolver) ResolveIP(hostport string) []string { + var resolved []string + host, port, err := net.SplitHostPort(hostport) + if err != nil { + log.Printf("error parsing %s: %v", hostport, err) + return nil + } + hostIPs, err := net.LookupIP(host) + if err != nil { + log.Printf("error resolving %s: %v", host, err) + return nil + } + for _, ip := range hostIPs { + resolved = append(resolved, net.JoinHostPort(ip.String(), port)) + } + return resolved +} + +var defaultResolver = newDNSCache(&dnsResolver{}) + +type cacheDatum struct { + addrs []string + deadline time.Time +} + +type dnsCache struct { + resolver resolver + sf singleflight.Group + mx sync.RWMutex + cache map[string]cacheDatum +} + +func newDNSCache(resolver resolver) *dnsCache { + return &dnsCache{ + resolver: resolver, + cache: make(map[string]cacheDatum), + } +} + +func (c *dnsCache) get(host string) ([]string, bool) { + d, ok := c.cache[host] + if !ok { + return nil, false + } + return d.addrs, d.deadline.After(time.Now()) +} + +func (c *dnsCache) update(host string) []string { + v, _, _ := c.sf.Do(host, func() (interface{}, error) { + addrs := c.resolver.ResolveIP(host) + // By uncommenting this, we stop caching negative results. + // if len(addrs) == 0 { + // return nil, nil + // } + c.mx.Lock() + c.cache[host] = cacheDatum{ + addrs: addrs, + deadline: time.Now().Add(60 * time.Second), + } + c.mx.Unlock() + return addrs, nil + }) + return v.([]string) +} + +func (c *dnsCache) ResolveIP(host string) []string { + c.mx.RLock() + addrs, ok := c.get(host) + c.mx.RUnlock() + + if ok { + return addrs + } + + if len(addrs) > 0 { + go c.update(host) + return addrs + } + + return c.update(host) +} diff --git a/vendor/git.autistici.org/ai3/go-common/clientutil/doc.go b/vendor/git.autistici.org/ai3/go-common/clientutil/doc.go new file mode 100644 index 00000000..421915b6 --- /dev/null +++ b/vendor/git.autistici.org/ai3/go-common/clientutil/doc.go @@ -0,0 +1,37 @@ +// Package clientutil implements a very simple style of JSON RPC. +// +// Requests and responses are both encoded in JSON, and they should +// have the "application/json" Content-Type. +// +// HTTP response statuses other than 200 indicate an error: in this +// case, the response body may contain (in plain text) further details +// about the error. Some HTTP status codes are considered temporary +// errors (incl. 429 for throttling). The client will retry requests, +// if targets are available, until the context expires - so it's quite +// important to remember to set a timeout on the context given to the +// Call() function! +// +// The client handles both replicated services and sharded +// (partitioned) services. Users of this package that want to support +// sharded deployments are supposed to pass a shard ID to every +// Call(). At the deployment stage, sharding can be enabled via the +// configuration. +// +// For replicated services, the client will expect the provided +// hostname to resolve to one or more IP addresses, in which case it +// will pick a random IP address on every new request, while +// remembering which addresses have had errors and trying to avoid +// them. It will however send an occasional request to the failed +// targets, to see if they've come back. +// +// For sharded services, the client makes simple HTTP requests to the +// specific target identified by the shard. It does this by prepending +// the shard ID to the backend hostname (so a request to "example.com" +// with shard ID "1" becomes a request to "1.example.com"). +// +// The difference with other JSON-RPC implementations is that we use a +// different URI for every method, and we force the usage of +// request/response types. This makes it easy for projects to +// eventually migrate to GRPC. +// +package clientutil diff --git a/vendor/git.autistici.org/ai3/go-common/clientutil/error.go b/vendor/git.autistici.org/ai3/go-common/clientutil/error.go new file mode 100644 index 00000000..f011e162 --- /dev/null +++ b/vendor/git.autistici.org/ai3/go-common/clientutil/error.go @@ -0,0 +1,35 @@ +package clientutil + +import ( + "fmt" + "io/ioutil" + "net/http" +) + +// RemoteError represents a HTTP error from the server. The status +// code and response body can be retrieved with the StatusCode() and +// Body() methods. +type RemoteError struct { + statusCode int + body string +} + +func remoteErrorFromResponse(resp *http.Response) *RemoteError { + // Optimistically read the response body, ignoring errors. + var body string + if data, err := ioutil.ReadAll(resp.Body); err == nil { + body = string(data) + } + return &RemoteError{statusCode: resp.StatusCode, body: body} +} + +// Error implements the error interface. +func (e *RemoteError) Error() string { + return fmt.Sprintf("%d - %s", e.statusCode, e.body) +} + +// StatusCode returns the HTTP status code. +func (e *RemoteError) StatusCode() int { return e.statusCode } + +// Body returns the response body. +func (e *RemoteError) Body() string { return e.body } diff --git a/vendor/git.autistici.org/ai3/go-common/clientutil/json.go b/vendor/git.autistici.org/ai3/go-common/clientutil/json.go deleted file mode 100644 index 5fc1ab2e..00000000 --- a/vendor/git.autistici.org/ai3/go-common/clientutil/json.go +++ /dev/null @@ -1,45 +0,0 @@ -package clientutil - -import ( - "bytes" - "context" - "encoding/json" - "errors" - "fmt" - "net/http" -) - -// DoJSONHTTPRequest makes an HTTP POST request to the specified uri, -// with a JSON-encoded request body. It will attempt to decode the -// response body as JSON. -func DoJSONHTTPRequest(ctx context.Context, client *http.Client, uri string, req, resp interface{}) error { - data, err := json.Marshal(req) - if err != nil { - return err - } - - httpReq, err := http.NewRequest("POST", uri, bytes.NewReader(data)) - if err != nil { - return err - } - httpReq.Header.Set("Content-Type", "application/json") - httpReq = httpReq.WithContext(ctx) - - httpResp, err := RetryHTTPDo(client, httpReq, NewExponentialBackOff()) - if err != nil { - return err - } - defer httpResp.Body.Close() - - if httpResp.StatusCode != 200 { - return fmt.Errorf("HTTP status %d", httpResp.StatusCode) - } - if httpResp.Header.Get("Content-Type") != "application/json" { - return errors.New("not a JSON response") - } - - if resp == nil { - return nil - } - return json.NewDecoder(httpResp.Body).Decode(resp) -} diff --git a/vendor/git.autistici.org/ai3/go-common/clientutil/retry.go b/vendor/git.autistici.org/ai3/go-common/clientutil/retry.go deleted file mode 100644 index 3ca7b51a..00000000 --- a/vendor/git.autistici.org/ai3/go-common/clientutil/retry.go +++ /dev/null @@ -1,92 +0,0 @@ -package clientutil - -import ( - "errors" - "net/http" - "time" - - "github.com/cenkalti/backoff" -) - -// NewExponentialBackOff creates a backoff.ExponentialBackOff object -// with our own default values. -func NewExponentialBackOff() *backoff.ExponentialBackOff { - b := backoff.NewExponentialBackOff() - b.InitialInterval = 100 * time.Millisecond - //b.Multiplier = 1.4142 - return b -} - -// A temporary (retriable) error is something that has a Temporary method. -type tempError interface { - Temporary() bool -} - -type tempErrorWrapper struct { - error -} - -func (t tempErrorWrapper) Temporary() bool { return true } - -// TempError makes a temporary (retriable) error out of a normal error. -func TempError(err error) error { - return tempErrorWrapper{err} -} - -// Retry operation op until it succeeds according to the backoff -// policy b. -// -// Note that this function reverses the error semantics of -// backoff.Operation: all errors are permanent unless explicitly -// marked as temporary (i.e. they have a Temporary() method that -// returns true). This is to better align with the errors returned by -// the net package. -func Retry(op backoff.Operation, b backoff.BackOff) error { - innerOp := func() error { - err := op() - if err == nil { - return err - } - if tmpErr, ok := err.(tempError); ok && tmpErr.Temporary() { - return err - } - return backoff.Permanent(err) - } - return backoff.Retry(innerOp, b) -} - -var errHTTPBackOff = TempError(errors.New("temporary http error")) - -func isStatusTemporary(code int) bool { - switch code { - case http.StatusTooManyRequests, http.StatusBadGateway, http.StatusServiceUnavailable, http.StatusGatewayTimeout: - return true - default: - return false - } -} - -// RetryHTTPDo retries an HTTP request until it succeeds, according to -// the backoff policy b. It will retry on temporary network errors and -// upon receiving specific temporary HTTP errors. It will use the -// context associated with the HTTP request object. -func RetryHTTPDo(client *http.Client, req *http.Request, b backoff.BackOff) (*http.Response, error) { - var resp *http.Response - op := func() error { - // Clear up previous response if set. - if resp != nil { - resp.Body.Close() - } - - var err error - resp, err = client.Do(req) - if err == nil && isStatusTemporary(resp.StatusCode) { - resp.Body.Close() - return errHTTPBackOff - } - return err - } - - err := Retry(op, backoff.WithContext(b, req.Context())) - return resp, err -} diff --git a/vendor/git.autistici.org/ai3/go-common/clientutil/track.go b/vendor/git.autistici.org/ai3/go-common/clientutil/track.go new file mode 100644 index 00000000..2db20bbb --- /dev/null +++ b/vendor/git.autistici.org/ai3/go-common/clientutil/track.go @@ -0,0 +1,123 @@ +package clientutil + +import ( + "math/rand" + "sync" + "time" +) + +// The backendTracker tracks the state of the targets associated with +// a backend, and periodically checks DNS for updates. +type backendTracker struct { + log logger + addr string + resolver resolver + stopCh chan struct{} + + mx sync.Mutex + resolved []string + failed map[string]time.Time +} + +func newBackendTracker(addr string, resolver resolver, logger logger) *backendTracker { + // Resolve the targets once before returning. + b := &backendTracker{ + addr: addr, + resolver: resolver, + resolved: resolver.ResolveIP(addr), + failed: make(map[string]time.Time), + stopCh: make(chan struct{}), + log: logger, + } + go b.updateProc() + return b +} + +func (b *backendTracker) Close() { + close(b.stopCh) +} + +// Return the full list of targets in reverse preference order. +func (b *backendTracker) getTargets() []string { + b.mx.Lock() + defer b.mx.Unlock() + + var good, bad []string + for _, t := range b.resolved { + if _, ok := b.failed[t]; ok { + bad = append(bad, t) + } else { + good = append(good, t) + } + } + + good = shuffle(good) + bad = shuffle(bad) + + return append(good, bad...) +} + +func (b *backendTracker) setStatus(addr string, ok bool) { + b.mx.Lock() + + _, isFailed := b.failed[addr] + if isFailed && ok { + b.log.Printf("target %s now ok", addr) + delete(b.failed, addr) + } else if !isFailed && !ok { + b.log.Printf("target %s failed", addr) + b.failed[addr] = time.Now() + } + + b.mx.Unlock() +} + +var ( + backendUpdateInterval = 60 * time.Second + backendFailureRetryInterval = 60 * time.Second +) + +func (b *backendTracker) expireFailedTargets() { + b.mx.Lock() + now := time.Now() + for k, v := range b.failed { + if now.Sub(v) > backendFailureRetryInterval { + delete(b.failed, k) + } + } + b.mx.Unlock() +} + +func (b *backendTracker) updateProc() { + tick := time.NewTicker(backendUpdateInterval) + defer tick.Stop() + for { + select { + case <-b.stopCh: + return + case <-tick.C: + b.expireFailedTargets() + resolved := b.resolver.ResolveIP(b.addr) + if len(resolved) > 0 { + b.mx.Lock() + b.resolved = resolved + b.mx.Unlock() + } + } + } +} + +var shuffleSrc = rand.NewSource(time.Now().UnixNano()) + +// Re-order elements of a slice randomly. +func shuffle(values []string) []string { + if len(values) < 2 { + return values + } + rnd := rand.New(shuffleSrc) + for i := len(values) - 1; i > 0; i-- { + j := rnd.Intn(i + 1) + values[i], values[j] = values[j], values[i] + } + return values +} diff --git a/vendor/git.autistici.org/ai3/go-common/clientutil/transport.go b/vendor/git.autistici.org/ai3/go-common/clientutil/transport.go index e4f98e3f..843a760b 100644 --- a/vendor/git.autistici.org/ai3/go-common/clientutil/transport.go +++ b/vendor/git.autistici.org/ai3/go-common/clientutil/transport.go @@ -3,170 +3,63 @@ package clientutil import ( "context" "crypto/tls" - "errors" - "log" "net" "net/http" "sync" "time" ) -var errAllBackendsFailed = errors.New("all backends failed") - -type dnsResolver struct{} - -func (r *dnsResolver) ResolveIPs(hosts []string) []string { - var resolved []string - for _, hostport := range hosts { - host, port, err := net.SplitHostPort(hostport) - if err != nil { - log.Printf("error parsing %s: %v", hostport, err) - continue - } - hostIPs, err := net.LookupIP(host) - if err != nil { - log.Printf("error resolving %s: %v", host, err) - continue - } - for _, ip := range hostIPs { - resolved = append(resolved, net.JoinHostPort(ip.String(), port)) - } - } - return resolved -} - -var defaultResolver = &dnsResolver{} - -type resolver interface { - ResolveIPs([]string) []string -} - -// Balancer for HTTP connections. It will round-robin across available -// backends, trying to avoid ones that are erroring out, until one -// succeeds or they all fail. +// The transportCache is just a cache of http transports, each +// connecting to a specific address. // -// This object should not be used for load balancing of individual -// HTTP requests: once a new connection is established, requests will -// be sent over it until it errors out. It's meant to provide a -// *reliable* connection to a set of equivalent backends for HA -// purposes. -type balancer struct { - hosts []string - resolver resolver - stop chan bool +// We use this to control the HTTP Host header and the TLS ServerName +// independently of the target address. +type transportCache struct { + tlsConfig *tls.Config - // List of currently valid (or untested) backends, and ones - // that errored out at least once. - mx sync.Mutex - addrs []string - ok map[string]bool + mx sync.RWMutex + transports map[string]http.RoundTripper } -var backendUpdateInterval = 60 * time.Second +func newTransportCache(tlsConfig *tls.Config) *transportCache { + return &transportCache{ + tlsConfig: tlsConfig, + transports: make(map[string]http.RoundTripper), + } +} -// Periodically update the list of available backends. -func (b *balancer) updateProc() { - tick := time.NewTicker(backendUpdateInterval) - for { - select { - case <-b.stop: - return - case <-tick.C: - resolved := b.resolver.ResolveIPs(b.hosts) - if len(resolved) > 0 { - b.mx.Lock() - b.addrs = resolved - b.mx.Unlock() - } - } +func (m *transportCache) newTransport(addr string) http.RoundTripper { + return &http.Transport{ + TLSClientConfig: m.tlsConfig, + DialContext: func(ctx context.Context, network, _ string) (net.Conn, error) { + return netDialContext(ctx, network, addr) + }, } } -// Returns a list of all available backends, split into "good ones" -// (no errors seen since last successful connection) and "bad ones". -func (b *balancer) getBackends() ([]string, []string) { - b.mx.Lock() - defer b.mx.Unlock() +func (m *transportCache) getTransport(addr string) http.RoundTripper { + m.mx.RLock() + t, ok := m.transports[addr] + m.mx.RUnlock() - var good, bad []string - for _, addr := range b.addrs { - if ok := b.ok[addr]; ok { - good = append(good, addr) - } else { - bad = append(bad, addr) + if !ok { + m.mx.Lock() + if t, ok = m.transports[addr]; !ok { + t = m.newTransport(addr) + m.transports[addr] = t } + m.mx.Unlock() } - return good, bad -} -func (b *balancer) notify(addr string, ok bool) { - b.mx.Lock() - b.ok[addr] = ok - b.mx.Unlock() + return t } +// Go < 1.9 does not have net.DialContext, reimplement it in terms of +// net.DialTimeout. func netDialContext(ctx context.Context, network, addr string) (net.Conn, error) { - timeout := 30 * time.Second - // Go < 1.9 does not have net.DialContext, reimplement it in - // terms of net.DialTimeout. + timeout := 60 * time.Second // some arbitrary max timeout if deadline, ok := ctx.Deadline(); ok { timeout = time.Until(deadline) } return net.DialTimeout(network, addr, timeout) } - -func (b *balancer) dial(ctx context.Context, network, addr string) (net.Conn, error) { - // Start by attempting a connection on 'good' targets. - good, bad := b.getBackends() - - for _, addr := range good { - // Go < 1.9 does not have DialContext, deal with it - conn, err := netDialContext(ctx, network, addr) - if err == nil { - return conn, nil - } else if err == context.Canceled { - // A timeout might be bad, set the error bit - // on the connection. - b.notify(addr, false) - return nil, err - } - b.notify(addr, false) - } - - for _, addr := range bad { - conn, err := netDialContext(ctx, network, addr) - if err == nil { - b.notify(addr, true) - return conn, nil - } else if err == context.Canceled { - return nil, err - } - } - - return nil, errAllBackendsFailed -} - -// NewTransport returns a suitably configured http.RoundTripper that -// talks to a specific backend service. It performs discovery of -// available backends via DNS (using A or AAAA record lookups), tries -// to route traffic away from faulty backends. -// -// It will periodically attempt to rediscover new backends. -func NewTransport(backends []string, tlsConf *tls.Config, resolver resolver) http.RoundTripper { - if resolver == nil { - resolver = defaultResolver - } - addrs := resolver.ResolveIPs(backends) - b := &balancer{ - hosts: backends, - resolver: resolver, - addrs: addrs, - ok: make(map[string]bool), - } - go b.updateProc() - - return &http.Transport{ - DialContext: b.dial, - TLSClientConfig: tlsConf, - } -} diff --git a/vendor/git.autistici.org/ai3/go-common/ldap/parse.go b/vendor/git.autistici.org/ai3/go-common/ldap/parse.go index 22a285c6..2aec0b26 100644 --- a/vendor/git.autistici.org/ai3/go-common/ldap/parse.go +++ b/vendor/git.autistici.org/ai3/go-common/ldap/parse.go @@ -6,6 +6,8 @@ import ( "gopkg.in/ldap.v2" ) +// ParseScope parses a string representation of an LDAP scope into the +// proper enum value. func ParseScope(s string) (int, error) { switch s { case "base": diff --git a/vendor/git.autistici.org/ai3/go-common/ldap/pool.go b/vendor/git.autistici.org/ai3/go-common/ldap/pool.go index c77d0617..560d639b 100644 --- a/vendor/git.autistici.org/ai3/go-common/ldap/pool.go +++ b/vendor/git.autistici.org/ai3/go-common/ldap/pool.go @@ -7,6 +7,8 @@ import ( "net/url" "time" + "git.autistici.org/ai3/go-common/clientutil" + "github.com/cenkalti/backoff" "gopkg.in/ldap.v2" ) @@ -125,3 +127,77 @@ func NewConnectionPool(uri, bindDN, bindPw string, cacheSize int) (*ConnectionPo bindPw: bindPw, }, nil } + +func (p *ConnectionPool) doRequest(ctx context.Context, fn func(*ldap.Conn) error) error { + return clientutil.Retry(func() error { + conn, err := p.Get(ctx) + if err != nil { + // Here conn is nil, so we don't need to Release it. + if isTemporaryLDAPError(err) { + return clientutil.TempError(err) + } + return err + } + + if deadline, ok := ctx.Deadline(); ok { + conn.SetTimeout(time.Until(deadline)) + } + + err = fn(conn) + if err != nil && isTemporaryLDAPError(err) { + p.Release(conn, err) + return clientutil.TempError(err) + } + p.Release(conn, err) + return err + }, backoff.WithContext(clientutil.NewExponentialBackOff(), ctx)) +} + +// Search performs the given search request. It will retry the request +// on temporary errors. +func (p *ConnectionPool) Search(ctx context.Context, searchRequest *ldap.SearchRequest) (*ldap.SearchResult, error) { + var result *ldap.SearchResult + err := p.doRequest(ctx, func(conn *ldap.Conn) error { + var err error + result, err = conn.Search(searchRequest) + return err + }) + return result, err +} + +// Modify issues a ModifyRequest to the LDAP server. +func (p *ConnectionPool) Modify(ctx context.Context, modifyRequest *ldap.ModifyRequest) error { + return p.doRequest(ctx, func(conn *ldap.Conn) error { + return conn.Modify(modifyRequest) + }) +} + +// Add issues an AddRequest to the LDAP server. +func (p *ConnectionPool) Add(ctx context.Context, addRequest *ldap.AddRequest) error { + return p.doRequest(ctx, func(conn *ldap.Conn) error { + return conn.Add(addRequest) + }) +} + +// Interface matched by net.Error. +type hasTemporary interface { + Temporary() bool +} + +// Treat network errors as temporary. Other errors are permanent by +// default. +func isTemporaryLDAPError(err error) bool { + switch v := err.(type) { + case *ldap.Error: + switch v.ResultCode { + case ldap.ErrorNetwork: + return true + default: + return false + } + case hasTemporary: + return v.Temporary() + default: + return false + } +} diff --git a/vendor/git.autistici.org/ai3/go-common/ldap/search.go b/vendor/git.autistici.org/ai3/go-common/ldap/search.go deleted file mode 100644 index db29ba09..00000000 --- a/vendor/git.autistici.org/ai3/go-common/ldap/search.go +++ /dev/null @@ -1,63 +0,0 @@ -package ldaputil - -import ( - "context" - "time" - - "github.com/cenkalti/backoff" - "gopkg.in/ldap.v2" - - "git.autistici.org/ai3/go-common/clientutil" -) - -// Interface matched by net.Error. -type hasTemporary interface { - Temporary() bool -} - -// Treat network errors as temporary. Other errors are permanent by -// default. -func isTemporaryLDAPError(err error) bool { - switch v := err.(type) { - case *ldap.Error: - switch v.ResultCode { - case ldap.ErrorNetwork: - return true - default: - return false - } - case hasTemporary: - return v.Temporary() - default: - return false - } -} - -// Search performs the given search request. It will retry the request -// on temporary errors. -func (p *ConnectionPool) Search(ctx context.Context, searchRequest *ldap.SearchRequest) (*ldap.SearchResult, error) { - var result *ldap.SearchResult - err := clientutil.Retry(func() error { - conn, err := p.Get(ctx) - if err != nil { - // Here conn is nil, so we don't need to Release it. - if isTemporaryLDAPError(err) { - return clientutil.TempError(err) - } - return err - } - - if deadline, ok := ctx.Deadline(); ok { - conn.SetTimeout(time.Until(deadline)) - } - - result, err = conn.Search(searchRequest) - if err != nil && isTemporaryLDAPError(err) { - p.Release(conn, err) - return clientutil.TempError(err) - } - p.Release(conn, err) - return err - }, backoff.WithContext(clientutil.NewExponentialBackOff(), ctx)) - return result, err -} diff --git a/vendor/git.autistici.org/ai3/go-common/serverutil/http.go b/vendor/git.autistici.org/ai3/go-common/serverutil/http.go index 7797ae66..32329492 100644 --- a/vendor/git.autistici.org/ai3/go-common/serverutil/http.go +++ b/vendor/git.autistici.org/ai3/go-common/serverutil/http.go @@ -3,13 +3,17 @@ package serverutil import ( "context" "crypto/tls" + "io" "log" + "net" "net/http" + "net/http/pprof" "os" "os/signal" "syscall" "time" + "github.com/coreos/go-systemd/daemon" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promhttp" ) @@ -20,43 +24,73 @@ var gracefulShutdownTimeout = 3 * time.Second type ServerConfig struct { TLS *TLSServerConfig `yaml:"tls"` MaxInflightRequests int `yaml:"max_inflight_requests"` + TrustedForwarders []string `yaml:"trusted_forwarders"` } -// Serve HTTP(S) content on the specified address. If serverConfig is -// not nil, enable HTTPS and TLS authentication. -// -// This function will return an error if there are problems creating -// the listener, otherwise it will handle graceful termination on -// SIGINT or SIGTERM and return nil. -func Serve(h http.Handler, serverConfig *ServerConfig, addr string) (err error) { +func (config *ServerConfig) buildHTTPServer(h http.Handler) (*http.Server, error) { var tlsConfig *tls.Config - if serverConfig != nil { - if serverConfig.TLS != nil { - tlsConfig, err = serverConfig.TLS.TLSConfig() + var err error + if config != nil { + if config.TLS != nil { + tlsConfig, err = config.TLS.TLSConfig() if err != nil { - return err + return nil, err } - h, err = serverConfig.TLS.TLSAuthWrapper(h) + h, err = config.TLS.TLSAuthWrapper(h) if err != nil { - return err + return nil, err } } - if serverConfig.MaxInflightRequests > 0 { - h = newLoadSheddingWrapper(serverConfig.MaxInflightRequests, h) + // If TrustedForwarders is defined, rewrite the request + // headers using X-Forwarded-Proto and X-Real-IP. + if len(config.TrustedForwarders) > 0 { + h, err = newProxyHeaders(h, config.TrustedForwarders) + if err != nil { + return nil, err + } + } + + // If MaxInflightRequests is set, enable the load + // shedding wrapper. + if config.MaxInflightRequests > 0 { + h = newLoadSheddingWrapper(config.MaxInflightRequests, h) } } // These are not meant to be external-facing servers, so we // can be generous with the timeouts to keep the number of // reconnections low. - srv := &http.Server{ - Addr: addr, - Handler: instrumentHandler(h), + return &http.Server{ + Handler: defaultHandler(h), ReadTimeout: 30 * time.Second, WriteTimeout: 30 * time.Second, IdleTimeout: 600 * time.Second, TLSConfig: tlsConfig, + }, nil +} + +// Serve HTTP(S) content on the specified address. If config.TLS is +// not nil, enable HTTPS and TLS authentication. +// +// This function will return an error if there are problems creating +// the listener, otherwise it will handle graceful termination on +// SIGINT or SIGTERM and return nil. +func Serve(h http.Handler, config *ServerConfig, addr string) error { + // Create the HTTP server. + srv, err := config.buildHTTPServer(h) + if err != nil { + return err + } + + // Create the net.Listener first, so we can detect + // initialization-time errors safely. + l, err := net.Listen("tcp", addr) + if err != nil { + return err + } + if srv.TLSConfig != nil { + l = tls.NewListener(l, srv.TLSConfig) } // Install a signal handler for gentle process termination. @@ -78,13 +112,13 @@ func Serve(h http.Handler, serverConfig *ServerConfig, addr string) (err error) close(done) }() + signal.Notify(sigCh, syscall.SIGINT, syscall.SIGTERM) - if tlsConfig != nil { - err = srv.ListenAndServeTLS("", "") - } else { - err = srv.ListenAndServe() - } + // Notify systemd that we are ready to serve. + daemon.SdNotify(false, "READY=1") + + err = srv.Serve(l) if err != http.ErrServerClosed { return err } @@ -93,12 +127,27 @@ func Serve(h http.Handler, serverConfig *ServerConfig, addr string) (err error) return nil } -func instrumentHandler(h http.Handler) http.Handler { +func defaultHandler(h http.Handler) http.Handler { root := http.NewServeMux() + + // Add an endpoint for HTTP health checking probes. + root.Handle("/health", http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { + io.WriteString(w, "OK") + })) + + // Add an endpoint to serve Prometheus metrics. root.Handle("/metrics", promhttp.Handler()) - root.Handle("/", h) - return promhttp.InstrumentHandlerInFlight(inFlightRequests, - promhttp.InstrumentHandlerCounter(totalRequests, root)) + + // Add the net/http/pprof debug handlers. + root.Handle("/debug/pprof/", pprof.Handler("")) + + // Forward everything else to the main handler, adding + // Prometheus instrumentation (requests to /metrics and + // /health are not included). + root.Handle("/", promhttp.InstrumentHandlerInFlight(inFlightRequests, + promhttp.InstrumentHandlerCounter(totalRequests, h))) + + return root } // HTTP-related metrics. diff --git a/vendor/git.autistici.org/ai3/go-common/serverutil/json.go b/vendor/git.autistici.org/ai3/go-common/serverutil/json.go index b307932e..746ed201 100644 --- a/vendor/git.autistici.org/ai3/go-common/serverutil/json.go +++ b/vendor/git.autistici.org/ai3/go-common/serverutil/json.go @@ -2,6 +2,7 @@ package serverutil import ( "encoding/json" + "log" "net/http" ) @@ -28,10 +29,19 @@ func DecodeJSONRequest(w http.ResponseWriter, r *http.Request, obj interface{}) // EncodeJSONResponse writes an application/json response to w. func EncodeJSONResponse(w http.ResponseWriter, obj interface{}) { + data, err := json.Marshal(obj) + if err != nil { + log.Printf("JSON serialization error: %v", err) + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + w.Header().Set("Content-Type", "application/json") w.Header().Set("Pragma", "no-cache") w.Header().Set("Cache-Control", "no-store") w.Header().Set("Expires", "-1") w.Header().Set("X-Content-Type-Options", "nosniff") - _ = json.NewEncoder(w).Encode(obj) + if _, err = w.Write(data); err != nil { + log.Printf("error writing response: %v", err) + } } diff --git a/vendor/git.autistici.org/ai3/go-common/serverutil/proxy_headers.go b/vendor/git.autistici.org/ai3/go-common/serverutil/proxy_headers.go new file mode 100644 index 00000000..00480b93 --- /dev/null +++ b/vendor/git.autistici.org/ai3/go-common/serverutil/proxy_headers.go @@ -0,0 +1,78 @@ +package serverutil + +import ( + "fmt" + "net" + "net/http" + + "github.com/gorilla/handlers" +) + +type proxyHeaders struct { + wrap, phWrap http.Handler + forwarders []net.IPNet +} + +func newProxyHeaders(h http.Handler, trustedForwarders []string) (http.Handler, error) { + f, err := parseIPNetList(trustedForwarders) + if err != nil { + return nil, err + } + return &proxyHeaders{ + wrap: h, + phWrap: handlers.ProxyHeaders(h), + forwarders: f, + }, nil +} + +func (p *proxyHeaders) ServeHTTP(w http.ResponseWriter, r *http.Request) { + host, _, err := net.SplitHostPort(r.RemoteAddr) + if err != nil { + host = r.RemoteAddr + } + ip := net.ParseIP(host) + if ip != nil && matchIPNetList(ip, p.forwarders) { + p.phWrap.ServeHTTP(w, r) + return + } + p.wrap.ServeHTTP(w, r) +} + +func fullMask(ip net.IP) net.IPMask { + if ip.To4() == nil { + return net.CIDRMask(128, 128) + } + return net.CIDRMask(32, 32) +} + +// ParseIPNetList turns a comma-separated list of IP addresses or CIDR +// networks into a net.IPNet slice. +func parseIPNetList(iplist []string) ([]net.IPNet, error) { + var nets []net.IPNet + for _, s := range iplist { + if s == "" { + continue + } + _, ipnet, err := net.ParseCIDR(s) + if err != nil { + ip := net.ParseIP(s) + if ip == nil { + return nil, fmt.Errorf("could not parse '%s'", s) + } + ipnet = &net.IPNet{IP: ip, Mask: fullMask(ip)} + } + nets = append(nets, *ipnet) + } + return nets, nil +} + +// MatchIPNetList returns true if the given IP address matches one of +// the specified networks. +func matchIPNetList(ip net.IP, nets []net.IPNet) bool { + for _, n := range nets { + if n.Contains(ip) { + return true + } + } + return false +} diff --git a/vendor/git.autistici.org/id/go-sso/README.md b/vendor/git.autistici.org/id/go-sso/README.md index 023f892b..9bd4afb1 100644 --- a/vendor/git.autistici.org/id/go-sso/README.md +++ b/vendor/git.autistici.org/id/go-sso/README.md @@ -121,3 +121,23 @@ parameters: Note that annoyingly *cur_svc* and *cur_nonce* are redundant, as they are already contained within *cur_tkt*, but the SSO ticket API won't allow us to decode the ticket without verifying it at the same time. + + +# Implementation notes + +The single-sign-on functionality works using HTTP cookies and +redirects between the protected service and the SSO server implemented +in this package. This part works without any Javascript, it's just +plain old HTTP (the browser must accept cookies though). SSO cookies +have a builtin (signed) expiration timestamp, and are set to be +automatically deleted on browser exit. + +Logout, on the other hand, is more complex: in order to get the +browser to delete the cookies from the signed-in services, we use +XMLHttpRequests from the logout page, and expect the service logout +endpoints to support authenticated CORS. If Javascript is not +available, however, we try to clear the cookies using image requests, +but this may not work depending on the browser (Safari), or the +presence of privacy-protecting extensions meant to block third-party +cookies. In this case a message is displayed asking the user to quit +the browser, but this isn't really a satisfying solution. diff --git a/vendor/github.com/cenkalti/backoff/README.md b/vendor/github.com/cenkalti/backoff/README.md index 13b347fb..55ebc98f 100644 --- a/vendor/github.com/cenkalti/backoff/README.md +++ b/vendor/github.com/cenkalti/backoff/README.md @@ -24,7 +24,7 @@ See https://godoc.org/github.com/cenkalti/backoff#pkg-examples [coveralls]: https://coveralls.io/github/cenkalti/backoff?branch=master [coveralls image]: https://coveralls.io/repos/github/cenkalti/backoff/badge.svg?branch=master -[google-http-java-client]: https://github.com/google/google-http-java-client +[google-http-java-client]: https://github.com/google/google-http-java-client/blob/da1aa993e90285ec18579f1553339b00e19b3ab5/google-http-client/src/main/java/com/google/api/client/util/ExponentialBackOff.java [exponential backoff wiki]: http://en.wikipedia.org/wiki/Exponential_backoff [advanced example]: https://godoc.org/github.com/cenkalti/backoff#example_ diff --git a/vendor/github.com/cenkalti/backoff/context.go b/vendor/github.com/cenkalti/backoff/context.go index 5d157092..d7005522 100644 --- a/vendor/github.com/cenkalti/backoff/context.go +++ b/vendor/github.com/cenkalti/backoff/context.go @@ -1,9 +1,8 @@ package backoff import ( + "context" "time" - - "golang.org/x/net/context" ) // BackOffContext is a backoff policy that stops retrying after the context diff --git a/vendor/github.com/cenkalti/backoff/retry.go b/vendor/github.com/cenkalti/backoff/retry.go index 5dbd825b..e65cc700 100644 --- a/vendor/github.com/cenkalti/backoff/retry.go +++ b/vendor/github.com/cenkalti/backoff/retry.go @@ -15,7 +15,6 @@ type Notify func(error, time.Duration) // Retry the operation o until it does not return error or BackOff stops. // o is guaranteed to be run at least once. -// It is the caller's responsibility to reset b after Retry returns. // // If o returns a *PermanentError, the operation is not retried, and the // wrapped error is returned. diff --git a/vendor/github.com/cenkalti/backoff/ticker.go b/vendor/github.com/cenkalti/backoff/ticker.go index e742512f..e41084b0 100644 --- a/vendor/github.com/cenkalti/backoff/ticker.go +++ b/vendor/github.com/cenkalti/backoff/ticker.go @@ -1,7 +1,6 @@ package backoff import ( - "runtime" "sync" "time" ) @@ -34,7 +33,6 @@ func NewTicker(b BackOff) *Ticker { } t.b.Reset() go t.run() - runtime.SetFinalizer(t, (*Ticker).Stop) return t } diff --git a/vendor/github.com/cenkalti/backoff/tries.go b/vendor/github.com/cenkalti/backoff/tries.go index d2da7308..cfeefd9b 100644 --- a/vendor/github.com/cenkalti/backoff/tries.go +++ b/vendor/github.com/cenkalti/backoff/tries.go @@ -3,13 +3,13 @@ package backoff import "time" /* -WithMaxTries creates a wrapper around another BackOff, which will +WithMaxRetries creates a wrapper around another BackOff, which will return Stop if NextBackOff() has been called too many times since the last time Reset() was called Note: Implementation is not thread-safe. */ -func WithMaxTries(b BackOff, max uint64) BackOff { +func WithMaxRetries(b BackOff, max uint64) BackOff { return &backOffTries{delegate: b, maxTries: max} } diff --git a/vendor/github.com/miscreant/miscreant/go/block/block.go b/vendor/github.com/miscreant/miscreant/go/block/block.go index be1f22cd..9c16ed81 100644 --- a/vendor/github.com/miscreant/miscreant/go/block/block.go +++ b/vendor/github.com/miscreant/miscreant/go/block/block.go @@ -15,7 +15,7 @@ const ( R = 0x87 ) -// Block is a 128-byte array used by certain block ciphers (i.e. AES) +// Block is a 128-bit array used by certain block ciphers (i.e. AES) type Block [Size]byte // Clear zeroes out the contents of the block diff --git a/vendor/github.com/miscreant/miscreant/go/pmac/pmac.go b/vendor/github.com/miscreant/miscreant/go/pmac/pmac.go index 080574ca..65a74343 100644 --- a/vendor/github.com/miscreant/miscreant/go/pmac/pmac.go +++ b/vendor/github.com/miscreant/miscreant/go/pmac/pmac.go @@ -7,6 +7,7 @@ import ( "crypto/cipher" "crypto/subtle" "hash" + "math/bits" "github.com/miscreant/miscreant/go/block" ) @@ -183,7 +184,7 @@ func (d *pmac) BlockSize() int { return block.Size } // Update the internal tag state based on the buf contents func (d *pmac) processBuffer() { - xor(d.offset[:], d.l[ctz(d.ctr+1)][:]) + xor(d.offset[:], d.l[bits.TrailingZeros(d.ctr+1)][:]) xor(d.buf[:], d.offset[:]) d.ctr++ @@ -192,17 +193,6 @@ func (d *pmac) processBuffer() { d.pos = 0 } -// TODO: use math/bits TrailingZeros() when it becomes available -// See: https://github.com/golang/go/issues/18616 -func ctz(n uint) uint { - var c uint - for n&1 == 0 { - c++ - n >>= 1 - } - return c -} - // XOR the contents of b into a in-place func xor(a, b []byte) { for i, v := range b { diff --git a/vendor/github.com/prometheus/common/model/time.go b/vendor/github.com/prometheus/common/model/time.go index 7e87f1ac..74ed5a9f 100644 --- a/vendor/github.com/prometheus/common/model/time.go +++ b/vendor/github.com/prometheus/common/model/time.go @@ -214,6 +214,9 @@ func (d Duration) String() string { ms = int64(time.Duration(d) / time.Millisecond) unit = "ms" ) + if ms == 0 { + return "0s" + } factors := map[string]int64{ "y": 1000 * 60 * 60 * 24 * 365, "w": 1000 * 60 * 60 * 24 * 7, diff --git a/vendor/github.com/theckman/go-flock/README.md b/vendor/github.com/theckman/go-flock/README.md index 82069971..38c794c8 100644 --- a/vendor/github.com/theckman/go-flock/README.md +++ b/vendor/github.com/theckman/go-flock/README.md @@ -9,7 +9,11 @@ includes a non-blocking TryLock() function to allow locking without blocking exe ## License `flock` is released under the BSD 3-Clause License. See the `LICENSE` file for more details. -## Intsallation +## Go Compatibility +This package makes use of the `context` package that was introduced in Go 1.7. As such, this +package has an implicit dependency on Go 1.7+. + +## Installation ``` go get -u github.com/theckman/go-flock ``` diff --git a/vendor/github.com/theckman/go-flock/flock.go b/vendor/github.com/theckman/go-flock/flock.go index 2582077f..ac40ec6c 100644 --- a/vendor/github.com/theckman/go-flock/flock.go +++ b/vendor/github.com/theckman/go-flock/flock.go @@ -11,8 +11,10 @@ package flock import ( + "context" "os" "sync" + "time" ) // Flock is the struct type to handle file locking. All fields are unexported, @@ -46,6 +48,25 @@ func (f *Flock) String() string { return f.path } +// TryLockContext repeatedly tries locking until one of the conditions is met: +// TryLock succeeds, TryLock fails with error, or Context Done channel is closed. +func (f *Flock) TryLockContext(ctx context.Context, retryDelay time.Duration) (bool, error) { + if ctx.Err() != nil { + return false, ctx.Err() + } + for { + if ok, err := f.TryLock(); ok || err != nil { + return ok, err + } + select { + case <-ctx.Done(): + return false, ctx.Err() + case <-time.After(retryDelay): + // try again + } + } +} + func (f *Flock) setFh() error { // open a new os.File instance // create it if it doesn't exist, truncate it if it does exist, open the file read-write diff --git a/vendor/golang.org/x/net/context/context.go b/vendor/golang.org/x/net/context/context.go index d3681ab4..a3c021d3 100644 --- a/vendor/golang.org/x/net/context/context.go +++ b/vendor/golang.org/x/net/context/context.go @@ -5,6 +5,8 @@ // Package context defines the Context type, which carries deadlines, // cancelation signals, and other request-scoped values across API boundaries // and between processes. +// As of Go 1.7 this package is available in the standard library under the +// name context. https://golang.org/pkg/context. // // Incoming requests to a server should create a Context, and outgoing calls to // servers should accept a Context. The chain of function calls between must diff --git a/vendor/gopkg.in/yaml.v2/LICENSE b/vendor/gopkg.in/yaml.v2/LICENSE index 8dada3ed..866d74a7 100644 --- a/vendor/gopkg.in/yaml.v2/LICENSE +++ b/vendor/gopkg.in/yaml.v2/LICENSE @@ -1,201 +1,13 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ +Copyright 2011-2016 Canonical Ltd. - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at - 1. Definitions. + http://www.apache.org/licenses/LICENSE-2.0 - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "{}" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright {yyyy} {name of copyright owner} - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/vendor/vendor.json b/vendor/vendor.json index 5ddd7c90..928b89d3 100644 --- a/vendor/vendor.json +++ b/vendor/vendor.json @@ -5,44 +5,44 @@ { "checksumSHA1": "raJx5BjBbVQG0ylGSjPpi+JvqjU=", "path": "git.autistici.org/ai3/go-common", - "revision": "a65293114a1adbb45d047a8f9014a307ec0d9051", - "revisionTime": "2018-01-12T09:10:27Z" + "revision": "193e29e61c81e6bb5548bfe89bba05836d06b61f", + "revisionTime": "2018-08-16T21:47:58Z" }, { - "checksumSHA1": "o+rWKVQIDy79ZwrItwa5/whAL6g=", + "checksumSHA1": "49MChcx9D+/+pCyl/F469TcQcK4=", "path": "git.autistici.org/ai3/go-common/clientutil", - "revision": "a65293114a1adbb45d047a8f9014a307ec0d9051", - "revisionTime": "2018-01-12T09:10:27Z" + "revision": "193e29e61c81e6bb5548bfe89bba05836d06b61f", + "revisionTime": "2018-08-16T21:47:58Z" }, { - "checksumSHA1": "iHObDrZa0HlyzdelqAaGfKNzpiM=", + "checksumSHA1": "udgeRdy83f6tJmhoKXFxCfwOIZw=", "path": "git.autistici.org/ai3/go-common/ldap", - "revision": "a65293114a1adbb45d047a8f9014a307ec0d9051", - "revisionTime": "2018-01-12T09:10:27Z" + "revision": "193e29e61c81e6bb5548bfe89bba05836d06b61f", + "revisionTime": "2018-08-16T21:47:58Z" }, { - "checksumSHA1": "nlGRxexjZUxnHc/z/+ZqV/Xq51w=", + "checksumSHA1": "7VBLbwaK1m/jwsk8sLsh4iD9T/s=", "path": "git.autistici.org/ai3/go-common/serverutil", - "revision": "a65293114a1adbb45d047a8f9014a307ec0d9051", - "revisionTime": "2018-01-12T09:10:27Z" + "revision": "193e29e61c81e6bb5548bfe89bba05836d06b61f", + "revisionTime": "2018-08-16T21:47:58Z" }, { "checksumSHA1": "T2vf4xzKRqoIjfXlofMgudKA8rA=", "path": "git.autistici.org/ai3/go-common/unix", - "revision": "a65293114a1adbb45d047a8f9014a307ec0d9051", - "revisionTime": "2018-01-12T09:10:27Z" + "revision": "193e29e61c81e6bb5548bfe89bba05836d06b61f", + "revisionTime": "2018-08-16T21:47:58Z" }, { "checksumSHA1": "OY/kamdVCtqr00dhc+STq7ApbmA=", "path": "git.autistici.org/ai3/go-common/userenckey", - "revision": "9c87d6c357e641c4ad4af0c06db87e0b04f1e6f0", - "revisionTime": "2018-06-28T09:11:42Z" + "revision": "193e29e61c81e6bb5548bfe89bba05836d06b61f", + "revisionTime": "2018-08-16T21:47:58Z" }, { - "checksumSHA1": "zvdsYaPEZrgcsRJy1bOo6YF5rVQ=", + "checksumSHA1": "QHzKyLU57jphYBzuhl4ELbCwVX0=", "path": "git.autistici.org/id/go-sso", - "revision": "10356d2430081e5f6dc60a68d576ef5b476f83ea", - "revisionTime": "2018-02-16T18:29:55Z" + "revision": "dc62a1d65832c8a3f73e7a0cffb78ad0b4d3d8fc", + "revisionTime": "2018-02-18T15:46:43Z" }, { "checksumSHA1": "spyv5/YFBjYyZLZa1U2LBfDR8PM=", @@ -51,28 +51,28 @@ "revisionTime": "2016-08-04T10:47:26Z" }, { - "checksumSHA1": "hTThB1Cw2ue02RD5Oig4eu1Dkzk=", + "checksumSHA1": "EAUmmJ4ccZbyuyf8Fnf+KU+DH3w=", "path": "github.com/cenkalti/backoff", - "revision": "309aa717adbf351e92864cbedf9cca0b769a4b5a", - "revisionTime": "2017-10-07T11:45:50Z" + "revision": "b7325b0f3f1097c6546ea5e83c4a23267e58ad71", + "revisionTime": "2018-08-01T15:21:24Z" }, { "checksumSHA1": "RBwpnMpfQt7Jo7YWrRph0Vwe+f0=", "path": "github.com/coreos/go-systemd/activation", - "revision": "d2196463941895ee908e13531a23a39feb9e1243", - "revisionTime": "2017-07-31T11:19:25Z" + "revision": "1f9909e51b2dab2487c26d64c8f2e7e580e4c9f5", + "revisionTime": "2017-03-24T09:58:19Z" }, { "checksumSHA1": "+Zz+leZHHC9C0rx8DoRuffSRPso=", "path": "github.com/coreos/go-systemd/daemon", - "revision": "d2196463941895ee908e13531a23a39feb9e1243", - "revisionTime": "2017-07-31T11:19:25Z" + "revision": "1f9909e51b2dab2487c26d64c8f2e7e580e4c9f5", + "revisionTime": "2017-03-24T09:58:19Z" }, { "checksumSHA1": "yqF125xVSkmfLpIVGrLlfE05IUk=", "path": "github.com/golang/protobuf/proto", - "revision": "17ce1425424ab154092bbb43af630bd647f3bb0d", - "revisionTime": "2017-09-02T00:04:52Z" + "revision": "1e59b77b52bf8e4b449a57e6f79f21226d571845", + "revisionTime": "2017-11-13T18:07:20Z" }, { "checksumSHA1": "D5EG5yMsbtHqKbzBVbxFMKEMDNY=", @@ -93,58 +93,58 @@ "revisionTime": "2018-06-23T00:14:59Z" }, { - "checksumSHA1": "KnQFNiyIyIH+V8EbySzLhySrF9s=", + "checksumSHA1": "LAN1Gs68UZhRbEUy25LfzDEjRT0=", "path": "github.com/miscreant/miscreant/go/block", - "revision": "834b150f9163806592d075e9e235296ba5c64425", - "revisionTime": "2017-12-04T00:48:37Z" + "revision": "71d82ae9dd614c1ec09d4491e01d5d0ea762c398", + "revisionTime": "2018-06-23T00:14:59Z" }, { "checksumSHA1": "Q1pe4LrL8CwfA/r+xYSl0/3AzPQ=", "path": "github.com/miscreant/miscreant/go/cmac", - "revision": "834b150f9163806592d075e9e235296ba5c64425", - "revisionTime": "2017-12-04T00:48:37Z" + "revision": "71d82ae9dd614c1ec09d4491e01d5d0ea762c398", + "revisionTime": "2018-06-23T00:14:59Z" }, { - "checksumSHA1": "bnZ6mFgtuRnZcqNwiILP/as94aE=", + "checksumSHA1": "Xl/ZDNMfyC5t/HF2Ms+c519lGRk=", "path": "github.com/miscreant/miscreant/go/pmac", - "revision": "834b150f9163806592d075e9e235296ba5c64425", - "revisionTime": "2017-12-04T00:48:37Z" + "revision": "71d82ae9dd614c1ec09d4491e01d5d0ea762c398", + "revisionTime": "2018-06-23T00:14:59Z" }, { "checksumSHA1": "hu0MsbTdFzZxNRyAxe2HmTFFFak=", "path": "github.com/prometheus/client_golang/prometheus", - "revision": "5cec1d0429b02e4323e042eb04dafdb079ddf568", - "revisionTime": "2017-10-05T11:29:15Z" + "revision": "661e31bf844dfca9aeba15f27ea8aa0d485ad212", + "revisionTime": "2017-12-01T12:22:22Z" }, { "checksumSHA1": "wsAkYlRRUNx+OAuUOIqdjO7dICM=", "path": "github.com/prometheus/client_golang/prometheus/promhttp", - "revision": "5cec1d0429b02e4323e042eb04dafdb079ddf568", - "revisionTime": "2017-10-05T11:29:15Z" + "revision": "661e31bf844dfca9aeba15f27ea8aa0d485ad212", + "revisionTime": "2017-12-01T12:22:22Z" }, { "checksumSHA1": "DvwvOlPNAgRntBzt3b3OSRMS2N4=", "path": "github.com/prometheus/client_model/go", - "revision": "6f3806018612930941127f2a7c6c453ba2c527d2", - "revisionTime": "2017-02-16T18:52:47Z" + "revision": "99fa1f4be8e564e8a6b613da7fa6f46c9edafc6c", + "revisionTime": "2017-11-17T10:05:41Z" }, { "checksumSHA1": "xfnn0THnqNwjwimeTClsxahYrIo=", "path": "github.com/prometheus/common/expfmt", - "revision": "e3fb1a1acd7605367a2b378bc2e2f893c05174b7", - "revisionTime": "2017-11-04T09:59:07Z" + "revision": "2e54d0b93cba2fd133edc32211dcc32c06ef72ca", + "revisionTime": "2017-11-17T16:30:51Z" }, { "checksumSHA1": "GWlM3d2vPYyNATtTFgftS10/A9w=", "path": "github.com/prometheus/common/internal/bitbucket.org/ww/goautoneg", - "revision": "e3fb1a1acd7605367a2b378bc2e2f893c05174b7", - "revisionTime": "2017-11-04T09:59:07Z" + "revision": "2e54d0b93cba2fd133edc32211dcc32c06ef72ca", + "revisionTime": "2017-11-17T16:30:51Z" }, { - "checksumSHA1": "3VoqH7TFfzA6Ds0zFzIbKCUvBmw=", + "checksumSHA1": "YU+/K48IMawQnToO4ETE6a+hhj4=", "path": "github.com/prometheus/common/model", - "revision": "e3fb1a1acd7605367a2b378bc2e2f893c05174b7", - "revisionTime": "2017-11-04T09:59:07Z" + "revision": "2e54d0b93cba2fd133edc32211dcc32c06ef72ca", + "revisionTime": "2017-11-17T16:30:51Z" }, { "checksumSHA1": "pW1yt1G1J9jnQMCxr1TDI7LQr3s=", @@ -159,10 +159,10 @@ "revisionTime": "2017-10-17T21:40:25Z" }, { - "checksumSHA1": "cDiE2qLTJ2kmiC7k1KS+AbP87X8=", + "checksumSHA1": "o4pEl1nZL7+e3cjayH05qcwxprI=", "path": "github.com/theckman/go-flock", - "revision": "6de226b0d5f040ed85b88c82c381709b98277f3d", - "revisionTime": "2017-05-22T02:22:41Z" + "revision": "d0cbbf0727cd7369d6deda1da29490dc132d45b4", + "revisionTime": "2017-10-30T20:34:48Z" }, { "checksumSHA1": "FwW3Vv4jW0Nv7V2SZC7x/Huj5M4=", @@ -180,15 +180,15 @@ "checksumSHA1": "X6Q8nYb+KXh+64AKHwWOOcyijHQ=", "origin": "git.autistici.org/id/go-sso/vendor/golang.org/x/crypto/ed25519", "path": "golang.org/x/crypto/ed25519", - "revision": "2f1d893daf6ea55c4c3a704d14cf3c0996e1fec5", - "revisionTime": "2017-12-14T07:43:49Z" + "revision": "dc62a1d65832c8a3f73e7a0cffb78ad0b4d3d8fc", + "revisionTime": "2018-02-18T15:46:43Z" }, { "checksumSHA1": "LXFcVx8I587SnWmKycSDEq9yvK8=", "origin": "git.autistici.org/id/go-sso/vendor/golang.org/x/crypto/ed25519/internal/edwards25519", "path": "golang.org/x/crypto/ed25519/internal/edwards25519", - "revision": "2f1d893daf6ea55c4c3a704d14cf3c0996e1fec5", - "revisionTime": "2017-12-14T07:43:49Z" + "revision": "dc62a1d65832c8a3f73e7a0cffb78ad0b4d3d8fc", + "revisionTime": "2018-02-18T15:46:43Z" }, { "checksumSHA1": "1MGpGDQqnUoRpv7VEcQrXOBydXE=", @@ -203,16 +203,16 @@ "revisionTime": "2018-06-20T09:14:27Z" }, { - "checksumSHA1": "dr5+PfIRzXeN+l1VG+s0lea9qz8=", + "checksumSHA1": "GtamqiJoL7PGHsN454AoffBFMa8=", "path": "golang.org/x/net/context", - "revision": "66aacef3dd8a676686c7ae3716979581e8b03c47", - "revisionTime": "2016-09-14T00:11:54Z" + "revision": "c39426892332e1bb5ec0a434a079bf82f5d30c54", + "revisionTime": "2018-07-15T06:54:04Z" }, { "checksumSHA1": "REkmyB368pIiip76LiqMLspgCRk=", "path": "golang.org/x/sys/cpu", - "revision": "c4afb3effaa53fd9a06ca61262dc7ce8df4c081b", - "revisionTime": "2018-06-26T06:41:42Z" + "revision": "7138fd3d9dc8335c567ca206f4333fb75eb05d56", + "revisionTime": "2018-06-27T13:57:12Z" }, { "checksumSHA1": "xsaHqy6/sonLV6xIxTNh4FfkWbU=", @@ -227,10 +227,10 @@ "revisionTime": "2017-11-23T04:56:18Z" }, { - "checksumSHA1": "RDJpJQwkF012L6m/2BJizyOksNw=", + "checksumSHA1": "o20lmjzBQyKD5LfLZ3OhUoMkLds=", "path": "gopkg.in/yaml.v2", - "revision": "eb3733d160e74a9c7e442f435eb3bea458e1d19f", - "revisionTime": "2017-08-12T16:00:11Z" + "revision": "25c4ec802a7d637f88d584ab26798e94ad14c13b", + "revisionTime": "2017-07-21T12:20:51Z" } ], "rootPath": "git.autistici.org/id/keystore" -- GitLab