Merge pull request #799 from minikube-bot/jenkins-v1.4.5

Upgrade to k8s version v1.4.5
pull/805/head
Matt Rickard 2016-11-03 15:27:33 -07:00 committed by GitHub
commit 57cbbe99b9
269 changed files with 19986 additions and 6369 deletions

2574
Godeps/Godeps.json generated

File diff suppressed because it is too large Load Diff

View File

@ -27,7 +27,7 @@ minikube start
--insecure-registry value Insecure Docker registries to pass to the Docker daemon (default []) --insecure-registry value Insecure Docker registries to pass to the Docker daemon (default [])
--iso-url string Location of the minikube iso (default "https://storage.googleapis.com/minikube/minikube-0.7.iso") --iso-url string Location of the minikube iso (default "https://storage.googleapis.com/minikube/minikube-0.7.iso")
--kubernetes-version string The kubernetes version that the minikube VM will (ex: v1.2.3) --kubernetes-version string The kubernetes version that the minikube VM will (ex: v1.2.3)
OR a URI which contains a localkube binary (ex: https://storage.googleapis.com/minikube/k8sReleases/v1.3.0/localkube-linux-amd64) (default "v1.4.3") OR a URI which contains a localkube binary (ex: https://storage.googleapis.com/minikube/k8sReleases/v1.3.0/localkube-linux-amd64) (default "v1.4.5")
--kvm-network string The KVM network name. (only supported with KVM driver) (default "default") --kvm-network string The KVM network name. (only supported with KVM driver) (default "default")
--memory int Amount of RAM allocated to the minikube VM (default 2048) --memory int Amount of RAM allocated to the minikube VM (default 2048)
--network-plugin string The name of the network plugin --network-plugin string The name of the network plugin

View File

@ -27,6 +27,7 @@ import (
"net/http" "net/http"
"net/url" "net/url"
"os" "os"
"runtime"
"strings" "strings"
"sync" "sync"
"time" "time"
@ -34,11 +35,20 @@ import (
"golang.org/x/net/context" "golang.org/x/net/context"
"golang.org/x/net/context/ctxhttp" "golang.org/x/net/context/ctxhttp"
"google.golang.org/cloud/internal" "cloud.google.com/go/internal"
) )
// metadataIP is the documented metadata server IP address. const (
const metadataIP = "169.254.169.254" // metadataIP is the documented metadata server IP address.
metadataIP = "169.254.169.254"
// metadataHostEnv is the environment variable specifying the
// GCE metadata hostname. If empty, the default value of
// metadataIP ("169.254.169.254") is used instead.
// This is variable name is not defined by any spec, as far as
// I know; it was made up for the Go package.
metadataHostEnv = "GCE_METADATA_HOST"
)
type cachedValue struct { type cachedValue struct {
k string k string
@ -110,7 +120,7 @@ func getETag(client *http.Client, suffix string) (value, etag string, err error)
// deployments. To enable spoofing of the metadata service, the environment // deployments. To enable spoofing of the metadata service, the environment
// variable GCE_METADATA_HOST is first inspected to decide where metadata // variable GCE_METADATA_HOST is first inspected to decide where metadata
// requests shall go. // requests shall go.
host := os.Getenv("GCE_METADATA_HOST") host := os.Getenv(metadataHostEnv)
if host == "" { if host == "" {
// Using 169.254.169.254 instead of "metadata" here because Go // Using 169.254.169.254 instead of "metadata" here because Go
// binaries built with the "netgo" tag and without cgo won't // binaries built with the "netgo" tag and without cgo won't
@ -163,32 +173,34 @@ func (c *cachedValue) get() (v string, err error) {
return return
} }
var onGCE struct { var (
sync.Mutex onGCEOnce sync.Once
set bool onGCE bool
v bool )
}
// OnGCE reports whether this process is running on Google Compute Engine. // OnGCE reports whether this process is running on Google Compute Engine.
func OnGCE() bool { func OnGCE() bool {
defer onGCE.Unlock() onGCEOnce.Do(initOnGCE)
onGCE.Lock() return onGCE
if onGCE.set { }
return onGCE.v
} func initOnGCE() {
onGCE.set = true onGCE = testOnGCE()
onGCE.v = testOnGCE()
return onGCE.v
} }
func testOnGCE() bool { func testOnGCE() bool {
// The user explicitly said they're on GCE, so trust them.
if os.Getenv(metadataHostEnv) != "" {
return true
}
ctx, cancel := context.WithCancel(context.Background()) ctx, cancel := context.WithCancel(context.Background())
defer cancel() defer cancel()
resc := make(chan bool, 2) resc := make(chan bool, 2)
// Try two strategies in parallel. // Try two strategies in parallel.
// See https://github.com/GoogleCloudPlatform/gcloud-golang/issues/194 // See https://github.com/GoogleCloudPlatform/google-cloud-go/issues/194
go func() { go func() {
res, err := ctxhttp.Get(ctx, metaClient, "http://"+metadataIP) res, err := ctxhttp.Get(ctx, metaClient, "http://"+metadataIP)
if err != nil { if err != nil {
@ -208,9 +220,53 @@ func testOnGCE() bool {
resc <- strsContains(addrs, metadataIP) resc <- strsContains(addrs, metadataIP)
}() }()
tryHarder := systemInfoSuggestsGCE()
if tryHarder {
res := <-resc
if res {
// The first strategy succeeded, so let's use it.
return true
}
// Wait for either the DNS or metadata server probe to
// contradict the other one and say we are running on
// GCE. Give it a lot of time to do so, since the system
// info already suggests we're running on a GCE BIOS.
timer := time.NewTimer(5 * time.Second)
defer timer.Stop()
select {
case res = <-resc:
return res
case <-timer.C:
// Too slow. Who knows what this system is.
return false
}
}
// There's no hint from the system info that we're running on
// GCE, so use the first probe's result as truth, whether it's
// true or false. The goal here is to optimize for speed for
// users who are NOT running on GCE. We can't assume that
// either a DNS lookup or an HTTP request to a blackholed IP
// address is fast. Worst case this should return when the
// metaClient's Transport.ResponseHeaderTimeout or
// Transport.Dial.Timeout fires (in two seconds).
return <-resc return <-resc
} }
// systemInfoSuggestsGCE reports whether the local system (without
// doing network requests) suggests that we're running on GCE. If this
// returns true, testOnGCE tries a bit harder to reach its metadata
// server.
func systemInfoSuggestsGCE() bool {
if runtime.GOOS != "linux" {
// We don't have any non-Linux clues available, at least yet.
return false
}
slurp, _ := ioutil.ReadFile("/sys/class/dmi/id/product_name")
name := strings.TrimSpace(string(slurp))
return name == "Google" || name == "Google Compute Engine"
}
// Subscribe subscribes to a value from the metadata service. // Subscribe subscribes to a value from the metadata service.
// The suffix is appended to "http://${GCE_METADATA_HOST}/computeMetadata/v1/". // The suffix is appended to "http://${GCE_METADATA_HOST}/computeMetadata/v1/".
// The suffix may contain query parameters. // The suffix may contain query parameters.

64
vendor/cloud.google.com/go/internal/cloud.go generated vendored Normal file
View File

@ -0,0 +1,64 @@
// Copyright 2014 Google Inc. All Rights Reserved.
//
// 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.
// Package internal provides support for the cloud packages.
//
// Users should not import this package directly.
package internal
import (
"fmt"
"net/http"
)
const userAgent = "gcloud-golang/0.1"
// Transport is an http.RoundTripper that appends Google Cloud client's
// user-agent to the original request's user-agent header.
type Transport struct {
// TODO(bradfitz): delete internal.Transport. It's too wrappy for what it does.
// Do User-Agent some other way.
// Base is the actual http.RoundTripper
// requests will use. It must not be nil.
Base http.RoundTripper
}
// RoundTrip appends a user-agent to the existing user-agent
// header and delegates the request to the base http.RoundTripper.
func (t *Transport) RoundTrip(req *http.Request) (*http.Response, error) {
req = cloneRequest(req)
ua := req.Header.Get("User-Agent")
if ua == "" {
ua = userAgent
} else {
ua = fmt.Sprintf("%s %s", ua, userAgent)
}
req.Header.Set("User-Agent", ua)
return t.Base.RoundTrip(req)
}
// cloneRequest returns a clone of the provided *http.Request.
// The clone is a shallow copy of the struct and its Header map.
func cloneRequest(r *http.Request) *http.Request {
// shallow copy of the struct
r2 := new(http.Request)
*r2 = *r
// deep copy of the Header
r2.Header = make(http.Header)
for k, s := range r.Header {
r2.Header[k] = s
}
return r2
}

View File

@ -21,9 +21,9 @@ import (
proto "github.com/golang/protobuf/proto" proto "github.com/golang/protobuf/proto"
math "math" math "math"
)
import io "io" io "io"
)
// Reference imports to suppress errors if they are not otherwise used. // Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal var _ = proto.Marshal

View File

@ -22,7 +22,10 @@ import (
"github.com/coreos/etcd/mvcc/backend" "github.com/coreos/etcd/mvcc/backend"
) )
// isSubset returns true if a is a subset of b // isSubset returns true if a is a subset of b.
// If a is a prefix of b, then a is a subset of b.
// Given intervals [a1,a2) and [b1,b2), is
// the a interval a subset of b?
func isSubset(a, b *rangePerm) bool { func isSubset(a, b *rangePerm) bool {
switch { switch {
case len(a.end) == 0 && len(b.end) == 0: case len(a.end) == 0 && len(b.end) == 0:
@ -32,9 +35,11 @@ func isSubset(a, b *rangePerm) bool {
// b is a key, a is a range // b is a key, a is a range
return false return false
case len(a.end) == 0: case len(a.end) == 0:
return 0 <= bytes.Compare(a.begin, b.begin) && bytes.Compare(a.begin, b.end) <= 0 // a is a key, b is a range. need b1 <= a1 and a1 < b2
return bytes.Compare(b.begin, a.begin) <= 0 && bytes.Compare(a.begin, b.end) < 0
default: default:
return 0 <= bytes.Compare(a.begin, b.begin) && bytes.Compare(a.end, b.end) <= 0 // both are ranges. need b1 <= a1 and a2 <= b2
return bytes.Compare(b.begin, a.begin) <= 0 && bytes.Compare(a.end, b.end) <= 0
} }
} }
@ -88,12 +93,18 @@ func mergeRangePerms(perms []*rangePerm) []*rangePerm {
i := 0 i := 0
for i < len(perms) { for i < len(perms) {
begin, next := i, i begin, next := i, i
for next+1 < len(perms) && bytes.Compare(perms[next].end, perms[next+1].begin) != -1 { for next+1 < len(perms) && bytes.Compare(perms[next].end, perms[next+1].begin) >= 0 {
next++ next++
} }
// don't merge ["a", "b") with ["b", ""), because perms[next+1].end is empty.
merged = append(merged, &rangePerm{begin: perms[begin].begin, end: perms[next].end}) if next != begin && len(perms[next].end) > 0 {
merged = append(merged, &rangePerm{begin: perms[begin].begin, end: perms[next].end})
} else {
merged = append(merged, perms[begin])
if next != begin {
merged = append(merged, perms[next])
}
}
i = next + 1 i = next + 1
} }

View File

@ -45,6 +45,8 @@ type simpleBalancer struct {
// pinAddr is the currently pinned address; set to the empty string on // pinAddr is the currently pinned address; set to the empty string on
// intialization and shutdown. // intialization and shutdown.
pinAddr string pinAddr string
closed bool
} }
func newSimpleBalancer(eps []string) *simpleBalancer { func newSimpleBalancer(eps []string) *simpleBalancer {
@ -74,15 +76,25 @@ func (b *simpleBalancer) ConnectNotify() <-chan struct{} {
func (b *simpleBalancer) Up(addr grpc.Address) func(error) { func (b *simpleBalancer) Up(addr grpc.Address) func(error) {
b.mu.Lock() b.mu.Lock()
defer b.mu.Unlock()
// gRPC might call Up after it called Close. We add this check
// to "fix" it up at application layer. Or our simplerBalancer
// might panic since b.upc is closed.
if b.closed {
return func(err error) {}
}
if len(b.upEps) == 0 { if len(b.upEps) == 0 {
// notify waiting Get()s and pin first connected address // notify waiting Get()s and pin first connected address
close(b.upc) close(b.upc)
b.pinAddr = addr.Addr b.pinAddr = addr.Addr
} }
b.upEps[addr.Addr] = struct{}{} b.upEps[addr.Addr] = struct{}{}
b.mu.Unlock()
// notify client that a connection is up // notify client that a connection is up
b.readyOnce.Do(func() { close(b.readyc) }) b.readyOnce.Do(func() { close(b.readyc) })
return func(err error) { return func(err error) {
b.mu.Lock() b.mu.Lock()
delete(b.upEps, addr.Addr) delete(b.upEps, addr.Addr)
@ -128,13 +140,19 @@ func (b *simpleBalancer) Notify() <-chan []grpc.Address { return b.notifyCh }
func (b *simpleBalancer) Close() error { func (b *simpleBalancer) Close() error {
b.mu.Lock() b.mu.Lock()
defer b.mu.Unlock()
// In case gRPC calls close twice. TODO: remove the checking
// when we are sure that gRPC wont call close twice.
if b.closed {
return nil
}
b.closed = true
close(b.notifyCh) close(b.notifyCh)
// terminate all waiting Get()s // terminate all waiting Get()s
b.pinAddr = "" b.pinAddr = ""
if len(b.upEps) == 0 { if len(b.upEps) == 0 {
close(b.upc) close(b.upc)
} }
b.mu.Unlock()
return nil return nil
} }

View File

@ -157,14 +157,14 @@ func (kv *kv) do(ctx context.Context, op Op) (OpResponse, error) {
} }
case tPut: case tPut:
var resp *pb.PutResponse var resp *pb.PutResponse
r := &pb.PutRequest{Key: op.key, Value: op.val, Lease: int64(op.leaseID)} r := &pb.PutRequest{Key: op.key, Value: op.val, Lease: int64(op.leaseID), PrevKv: op.prevKV}
resp, err = kv.remote.Put(ctx, r) resp, err = kv.remote.Put(ctx, r)
if err == nil { if err == nil {
return OpResponse{put: (*PutResponse)(resp)}, nil return OpResponse{put: (*PutResponse)(resp)}, nil
} }
case tDeleteRange: case tDeleteRange:
var resp *pb.DeleteRangeResponse var resp *pb.DeleteRangeResponse
r := &pb.DeleteRangeRequest{Key: op.key, RangeEnd: op.end} r := &pb.DeleteRangeRequest{Key: op.key, RangeEnd: op.end, PrevKv: op.prevKV}
resp, err = kv.remote.DeleteRange(ctx, r) resp, err = kv.remote.DeleteRange(ctx, r)
if err == nil { if err == nil {
return OpResponse{del: (*DeleteResponse)(resp)}, nil return OpResponse{del: (*DeleteResponse)(resp)}, nil

View File

@ -47,6 +47,9 @@ type Op struct {
// for range, watch // for range, watch
rev int64 rev int64
// for watch, put, delete
prevKV bool
// progressNotify is for progress updates. // progressNotify is for progress updates.
progressNotify bool progressNotify bool
@ -73,10 +76,10 @@ func (op Op) toRequestOp() *pb.RequestOp {
} }
return &pb.RequestOp{Request: &pb.RequestOp_RequestRange{RequestRange: r}} return &pb.RequestOp{Request: &pb.RequestOp_RequestRange{RequestRange: r}}
case tPut: case tPut:
r := &pb.PutRequest{Key: op.key, Value: op.val, Lease: int64(op.leaseID)} r := &pb.PutRequest{Key: op.key, Value: op.val, Lease: int64(op.leaseID), PrevKv: op.prevKV}
return &pb.RequestOp{Request: &pb.RequestOp_RequestPut{RequestPut: r}} return &pb.RequestOp{Request: &pb.RequestOp_RequestPut{RequestPut: r}}
case tDeleteRange: case tDeleteRange:
r := &pb.DeleteRangeRequest{Key: op.key, RangeEnd: op.end} r := &pb.DeleteRangeRequest{Key: op.key, RangeEnd: op.end, PrevKv: op.prevKV}
return &pb.RequestOp{Request: &pb.RequestOp_RequestDeleteRange{RequestDeleteRange: r}} return &pb.RequestOp{Request: &pb.RequestOp_RequestDeleteRange{RequestDeleteRange: r}}
default: default:
panic("Unknown Op") panic("Unknown Op")
@ -271,3 +274,11 @@ func WithProgressNotify() OpOption {
op.progressNotify = true op.progressNotify = true
} }
} }
// WithPrevKV gets the previous key-value pair before the event happens. If the previous KV is already compacted,
// nothing will be returned.
func WithPrevKV() OpOption {
return func(op *Op) {
op.prevKV = true
}
}

View File

@ -61,6 +61,9 @@ type WatchResponse struct {
// the channel sends a final response that has Canceled set to true with a non-nil Err(). // the channel sends a final response that has Canceled set to true with a non-nil Err().
Canceled bool Canceled bool
// created is used to indicate the creation of the watcher.
created bool
closeErr error closeErr error
} }
@ -89,7 +92,7 @@ func (wr *WatchResponse) Err() error {
// IsProgressNotify returns true if the WatchResponse is progress notification. // IsProgressNotify returns true if the WatchResponse is progress notification.
func (wr *WatchResponse) IsProgressNotify() bool { func (wr *WatchResponse) IsProgressNotify() bool {
return len(wr.Events) == 0 && !wr.Canceled return len(wr.Events) == 0 && !wr.Canceled && !wr.created && wr.CompactRevision == 0 && wr.Header.Revision != 0
} }
// watcher implements the Watcher interface // watcher implements the Watcher interface
@ -102,6 +105,7 @@ type watcher struct {
streams map[string]*watchGrpcStream streams map[string]*watchGrpcStream
} }
// watchGrpcStream tracks all watch resources attached to a single grpc stream.
type watchGrpcStream struct { type watchGrpcStream struct {
owner *watcher owner *watcher
remote pb.WatchClient remote pb.WatchClient
@ -112,10 +116,10 @@ type watchGrpcStream struct {
ctxKey string ctxKey string
cancel context.CancelFunc cancel context.CancelFunc
// mu protects the streams map // substreams holds all active watchers on this grpc stream
mu sync.RWMutex substreams map[int64]*watcherStream
// streams holds all active watchers // resuming holds all resuming watchers on this grpc stream
streams map[int64]*watcherStream resuming []*watcherStream
// reqc sends a watch request from Watch() to the main goroutine // reqc sends a watch request from Watch() to the main goroutine
reqc chan *watchRequest reqc chan *watchRequest
@ -127,8 +131,12 @@ type watchGrpcStream struct {
donec chan struct{} donec chan struct{}
// errc transmits errors from grpc Recv to the watch stream reconn logic // errc transmits errors from grpc Recv to the watch stream reconn logic
errc chan error errc chan error
// closingc gets the watcherStream of closing watchers
closingc chan *watcherStream
// the error that closed the watch stream // resumec closes to signal that all substreams should begin resuming
resumec chan struct{}
// closeErr is the error that closed the watch stream
closeErr error closeErr error
} }
@ -140,6 +148,8 @@ type watchRequest struct {
rev int64 rev int64
// progressNotify is for progress updates. // progressNotify is for progress updates.
progressNotify bool progressNotify bool
// get the previous key-value pair before the event happens
prevKV bool
// retc receives a chan WatchResponse once the watcher is established // retc receives a chan WatchResponse once the watcher is established
retc chan chan WatchResponse retc chan chan WatchResponse
} }
@ -150,15 +160,18 @@ type watcherStream struct {
initReq watchRequest initReq watchRequest
// outc publishes watch responses to subscriber // outc publishes watch responses to subscriber
outc chan<- WatchResponse outc chan WatchResponse
// recvc buffers watch responses before publishing // recvc buffers watch responses before publishing
recvc chan *WatchResponse recvc chan *WatchResponse
id int64 // donec closes when the watcherStream goroutine stops.
donec chan struct{}
// closing is set to true when stream should be scheduled to shutdown.
closing bool
// id is the registered watch id on the grpc stream
id int64
// lastRev is revision last successfully sent over outc // buf holds all events received from etcd but not yet consumed by the client
lastRev int64 buf []*WatchResponse
// resumec indicates the stream must recover at a given revision
resumec chan int64
} }
func NewWatcher(c *Client) Watcher { func NewWatcher(c *Client) Watcher {
@ -182,18 +195,20 @@ func (vc *valCtx) Err() error { return nil }
func (w *watcher) newWatcherGrpcStream(inctx context.Context) *watchGrpcStream { func (w *watcher) newWatcherGrpcStream(inctx context.Context) *watchGrpcStream {
ctx, cancel := context.WithCancel(&valCtx{inctx}) ctx, cancel := context.WithCancel(&valCtx{inctx})
wgs := &watchGrpcStream{ wgs := &watchGrpcStream{
owner: w, owner: w,
remote: w.remote, remote: w.remote,
ctx: ctx, ctx: ctx,
ctxKey: fmt.Sprintf("%v", inctx), ctxKey: fmt.Sprintf("%v", inctx),
cancel: cancel, cancel: cancel,
streams: make(map[int64]*watcherStream), substreams: make(map[int64]*watcherStream),
respc: make(chan *pb.WatchResponse), respc: make(chan *pb.WatchResponse),
reqc: make(chan *watchRequest), reqc: make(chan *watchRequest),
stopc: make(chan struct{}), stopc: make(chan struct{}),
donec: make(chan struct{}), donec: make(chan struct{}),
errc: make(chan error, 1), errc: make(chan error, 1),
closingc: make(chan *watcherStream),
resumec: make(chan struct{}),
} }
go wgs.run() go wgs.run()
return wgs return wgs
@ -203,14 +218,14 @@ func (w *watcher) newWatcherGrpcStream(inctx context.Context) *watchGrpcStream {
func (w *watcher) Watch(ctx context.Context, key string, opts ...OpOption) WatchChan { func (w *watcher) Watch(ctx context.Context, key string, opts ...OpOption) WatchChan {
ow := opWatch(key, opts...) ow := opWatch(key, opts...)
retc := make(chan chan WatchResponse, 1)
wr := &watchRequest{ wr := &watchRequest{
ctx: ctx, ctx: ctx,
key: string(ow.key), key: string(ow.key),
end: string(ow.end), end: string(ow.end),
rev: ow.rev, rev: ow.rev,
progressNotify: ow.progressNotify, progressNotify: ow.progressNotify,
retc: retc, prevKV: ow.prevKV,
retc: make(chan chan WatchResponse, 1),
} }
ok := false ok := false
@ -242,7 +257,6 @@ func (w *watcher) Watch(ctx context.Context, key string, opts ...OpOption) Watch
case reqc <- wr: case reqc <- wr:
ok = true ok = true
case <-wr.ctx.Done(): case <-wr.ctx.Done():
wgs.stopIfEmpty()
case <-donec: case <-donec:
if wgs.closeErr != nil { if wgs.closeErr != nil {
closeCh <- WatchResponse{closeErr: wgs.closeErr} closeCh <- WatchResponse{closeErr: wgs.closeErr}
@ -255,7 +269,7 @@ func (w *watcher) Watch(ctx context.Context, key string, opts ...OpOption) Watch
// receive channel // receive channel
if ok { if ok {
select { select {
case ret := <-retc: case ret := <-wr.retc:
return ret return ret
case <-ctx.Done(): case <-ctx.Done():
case <-donec: case <-donec:
@ -286,12 +300,7 @@ func (w *watcher) Close() (err error) {
} }
func (w *watchGrpcStream) Close() (err error) { func (w *watchGrpcStream) Close() (err error) {
w.mu.Lock() close(w.stopc)
if w.stopc != nil {
close(w.stopc)
w.stopc = nil
}
w.mu.Unlock()
<-w.donec <-w.donec
select { select {
case err = <-w.errc: case err = <-w.errc:
@ -300,67 +309,57 @@ func (w *watchGrpcStream) Close() (err error) {
return toErr(w.ctx, err) return toErr(w.ctx, err)
} }
func (w *watchGrpcStream) addStream(resp *pb.WatchResponse, pendingReq *watchRequest) { func (w *watcher) closeStream(wgs *watchGrpcStream) {
if pendingReq == nil {
// no pending request; ignore
return
}
if resp.Canceled || resp.CompactRevision != 0 {
// a cancel at id creation time means the start revision has
// been compacted out of the store
ret := make(chan WatchResponse, 1)
ret <- WatchResponse{
Header: *resp.Header,
CompactRevision: resp.CompactRevision,
Canceled: true}
close(ret)
pendingReq.retc <- ret
return
}
ret := make(chan WatchResponse)
if resp.WatchId == -1 {
// failed; no channel
close(ret)
pendingReq.retc <- ret
return
}
ws := &watcherStream{
initReq: *pendingReq,
id: resp.WatchId,
outc: ret,
// buffered so unlikely to block on sending while holding mu
recvc: make(chan *WatchResponse, 4),
resumec: make(chan int64),
}
if pendingReq.rev == 0 {
// note the header revision so that a put following a current watcher
// disconnect will arrive on the watcher channel after reconnect
ws.initReq.rev = resp.Header.Revision
}
w.mu.Lock() w.mu.Lock()
w.streams[ws.id] = ws close(wgs.donec)
wgs.cancel()
if w.streams != nil {
delete(w.streams, wgs.ctxKey)
}
w.mu.Unlock() w.mu.Unlock()
// pass back the subscriber channel for the watcher
pendingReq.retc <- ret
// send messages to subscriber
go w.serveStream(ws)
} }
// closeStream closes the watcher resources and removes it func (w *watchGrpcStream) addSubstream(resp *pb.WatchResponse, ws *watcherStream) {
func (w *watchGrpcStream) closeStream(ws *watcherStream) { if resp.WatchId == -1 {
w.mu.Lock() // failed; no channel
// cancels request stream; subscriber receives nil channel close(ws.recvc)
close(ws.initReq.retc) return
// close subscriber's channel }
ws.id = resp.WatchId
w.substreams[ws.id] = ws
}
func (w *watchGrpcStream) sendCloseSubstream(ws *watcherStream, resp *WatchResponse) {
select {
case ws.outc <- *resp:
case <-ws.initReq.ctx.Done():
case <-time.After(closeSendErrTimeout):
}
close(ws.outc) close(ws.outc)
delete(w.streams, ws.id) }
w.mu.Unlock()
func (w *watchGrpcStream) closeSubstream(ws *watcherStream) {
// send channel response in case stream was never established
select {
case ws.initReq.retc <- ws.outc:
default:
}
// close subscriber's channel
if closeErr := w.closeErr; closeErr != nil && ws.initReq.ctx.Err() == nil {
go w.sendCloseSubstream(ws, &WatchResponse{closeErr: w.closeErr})
} else {
close(ws.outc)
}
if ws.id != -1 {
delete(w.substreams, ws.id)
return
}
for i := range w.resuming {
if w.resuming[i] == ws {
w.resuming[i] = nil
return
}
}
} }
// run is the root of the goroutines for managing a watcher client // run is the root of the goroutines for managing a watcher client
@ -368,66 +367,79 @@ func (w *watchGrpcStream) run() {
var wc pb.Watch_WatchClient var wc pb.Watch_WatchClient
var closeErr error var closeErr error
defer func() { // substreams marked to close but goroutine still running; needed for
w.owner.mu.Lock() // avoiding double-closing recvc on grpc stream teardown
w.closeErr = closeErr closing := make(map[*watcherStream]struct{})
if w.owner.streams != nil {
delete(w.owner.streams, w.ctxKey)
}
close(w.donec)
w.owner.mu.Unlock()
w.cancel()
}()
// already stopped? defer func() {
w.mu.RLock() w.closeErr = closeErr
stopc := w.stopc // shutdown substreams and resuming substreams
w.mu.RUnlock() for _, ws := range w.substreams {
if stopc == nil { if _, ok := closing[ws]; !ok {
return close(ws.recvc)
} }
}
for _, ws := range w.resuming {
if _, ok := closing[ws]; ws != nil && !ok {
close(ws.recvc)
}
}
w.joinSubstreams()
for toClose := len(w.substreams) + len(w.resuming); toClose > 0; toClose-- {
w.closeSubstream(<-w.closingc)
}
w.owner.closeStream(w)
}()
// start a stream with the etcd grpc server // start a stream with the etcd grpc server
if wc, closeErr = w.newWatchClient(); closeErr != nil { if wc, closeErr = w.newWatchClient(); closeErr != nil {
return return
} }
var pendingReq, failedReq *watchRequest
curReqC := w.reqc
cancelSet := make(map[int64]struct{}) cancelSet := make(map[int64]struct{})
for { for {
select { select {
// Watch() requested // Watch() requested
case pendingReq = <-curReqC: case wreq := <-w.reqc:
// no more watch requests until there's a response outc := make(chan WatchResponse, 1)
curReqC = nil ws := &watcherStream{
if err := wc.Send(pendingReq.toPB()); err == nil { initReq: *wreq,
// pendingReq now waits on w.respc id: -1,
break outc: outc,
// unbufffered so resumes won't cause repeat events
recvc: make(chan *WatchResponse),
}
ws.donec = make(chan struct{})
go w.serveSubstream(ws, w.resumec)
// queue up for watcher creation/resume
w.resuming = append(w.resuming, ws)
if len(w.resuming) == 1 {
// head of resume queue, can register a new watcher
wc.Send(ws.initReq.toPB())
} }
failedReq = pendingReq
// New events from the watch client // New events from the watch client
case pbresp := <-w.respc: case pbresp := <-w.respc:
switch { switch {
case pbresp.Created: case pbresp.Created:
// response to pending req, try to add // response to head of queue creation
w.addStream(pbresp, pendingReq) if ws := w.resuming[0]; ws != nil {
pendingReq = nil w.addSubstream(pbresp, ws)
curReqC = w.reqc w.dispatchEvent(pbresp)
w.resuming[0] = nil
}
if ws := w.nextResume(); ws != nil {
wc.Send(ws.initReq.toPB())
}
case pbresp.Canceled: case pbresp.Canceled:
delete(cancelSet, pbresp.WatchId) delete(cancelSet, pbresp.WatchId)
// shutdown serveStream, if any if ws, ok := w.substreams[pbresp.WatchId]; ok {
w.mu.Lock() // signal to stream goroutine to update closingc
if ws, ok := w.streams[pbresp.WatchId]; ok {
close(ws.recvc) close(ws.recvc)
delete(w.streams, ws.id) closing[ws] = struct{}{}
}
numStreams := len(w.streams)
w.mu.Unlock()
if numStreams == 0 {
// don't leak watcher streams
return
} }
default: default:
// dispatch to appropriate watch stream // dispatch to appropriate watch stream
@ -448,7 +460,6 @@ func (w *watchGrpcStream) run() {
wc.Send(req) wc.Send(req)
} }
// watch client failed to recv; spawn another if possible // watch client failed to recv; spawn another if possible
// TODO report watch client errors from errc?
case err := <-w.errc: case err := <-w.errc:
if toErr(w.ctx, err) == v3rpc.ErrNoLeader { if toErr(w.ctx, err) == v3rpc.ErrNoLeader {
closeErr = err closeErr = err
@ -457,48 +468,58 @@ func (w *watchGrpcStream) run() {
if wc, closeErr = w.newWatchClient(); closeErr != nil { if wc, closeErr = w.newWatchClient(); closeErr != nil {
return return
} }
curReqC = w.reqc if ws := w.nextResume(); ws != nil {
if pendingReq != nil { wc.Send(ws.initReq.toPB())
failedReq = pendingReq
} }
cancelSet = make(map[int64]struct{}) cancelSet = make(map[int64]struct{})
case <-stopc: case <-w.stopc:
return return
} case ws := <-w.closingc:
w.closeSubstream(ws)
// send failed; queue for retry delete(closing, ws)
if failedReq != nil { if len(w.substreams)+len(w.resuming) == 0 {
go func(wr *watchRequest) { // no more watchers on this stream, shutdown
select { return
case w.reqc <- wr: }
case <-wr.ctx.Done():
case <-w.donec:
}
}(pendingReq)
failedReq = nil
pendingReq = nil
} }
} }
} }
// nextResume chooses the next resuming to register with the grpc stream. Abandoned
// streams are marked as nil in the queue since the head must wait for its inflight registration.
func (w *watchGrpcStream) nextResume() *watcherStream {
for len(w.resuming) != 0 {
if w.resuming[0] != nil {
return w.resuming[0]
}
w.resuming = w.resuming[1:len(w.resuming)]
}
return nil
}
// dispatchEvent sends a WatchResponse to the appropriate watcher stream // dispatchEvent sends a WatchResponse to the appropriate watcher stream
func (w *watchGrpcStream) dispatchEvent(pbresp *pb.WatchResponse) bool { func (w *watchGrpcStream) dispatchEvent(pbresp *pb.WatchResponse) bool {
w.mu.RLock() ws, ok := w.substreams[pbresp.WatchId]
defer w.mu.RUnlock() if !ok {
ws, ok := w.streams[pbresp.WatchId] return false
}
events := make([]*Event, len(pbresp.Events)) events := make([]*Event, len(pbresp.Events))
for i, ev := range pbresp.Events { for i, ev := range pbresp.Events {
events[i] = (*Event)(ev) events[i] = (*Event)(ev)
} }
if ok { wr := &WatchResponse{
wr := &WatchResponse{ Header: *pbresp.Header,
Header: *pbresp.Header, Events: events,
Events: events, CompactRevision: pbresp.CompactRevision,
CompactRevision: pbresp.CompactRevision, created: pbresp.Created,
Canceled: pbresp.Canceled} Canceled: pbresp.Canceled,
ws.recvc <- wr
} }
return ok select {
case ws.recvc <- wr:
case <-ws.donec:
return false
}
return true
} }
// serveWatchClient forwards messages from the grpc stream to run() // serveWatchClient forwards messages from the grpc stream to run()
@ -520,134 +541,123 @@ func (w *watchGrpcStream) serveWatchClient(wc pb.Watch_WatchClient) {
} }
} }
// serveStream forwards watch responses from run() to the subscriber // serveSubstream forwards watch responses from run() to the subscriber
func (w *watchGrpcStream) serveStream(ws *watcherStream) { func (w *watchGrpcStream) serveSubstream(ws *watcherStream, resumec chan struct{}) {
var closeErr error if ws.closing {
emptyWr := &WatchResponse{} panic("created substream goroutine but substream is closing")
wrs := []*WatchResponse{} }
// nextRev is the minimum expected next revision
nextRev := ws.initReq.rev
resuming := false resuming := false
closing := false defer func() {
for !closing { if !resuming {
ws.closing = true
}
close(ws.donec)
if !resuming {
w.closingc <- ws
}
}()
emptyWr := &WatchResponse{}
for {
curWr := emptyWr curWr := emptyWr
outc := ws.outc outc := ws.outc
if len(wrs) > 0 {
curWr = wrs[0] if len(ws.buf) > 0 && ws.buf[0].created {
select {
case ws.initReq.retc <- ws.outc:
default:
}
ws.buf = ws.buf[1:]
}
if len(ws.buf) > 0 {
curWr = ws.buf[0]
} else { } else {
outc = nil outc = nil
} }
select { select {
case outc <- *curWr: case outc <- *curWr:
if wrs[0].Err() != nil { if ws.buf[0].Err() != nil {
closing = true
break
}
var newRev int64
if len(wrs[0].Events) > 0 {
newRev = wrs[0].Events[len(wrs[0].Events)-1].Kv.ModRevision
} else {
newRev = wrs[0].Header.Revision
}
if newRev != ws.lastRev {
ws.lastRev = newRev
}
wrs[0] = nil
wrs = wrs[1:]
case wr, ok := <-ws.recvc:
if !ok {
// shutdown from closeStream
return return
} }
// resume up to last seen event if disconnected ws.buf[0] = nil
if resuming && wr.Err() == nil { ws.buf = ws.buf[1:]
resuming = false case wr, ok := <-ws.recvc:
// trim events already seen if !ok {
for i := 0; i < len(wr.Events); i++ { // shutdown from closeSubstream
if wr.Events[i].Kv.ModRevision > ws.lastRev { return
wr.Events = wr.Events[i:]
break
}
}
// only forward new events
if wr.Events[0].Kv.ModRevision == ws.lastRev {
break
}
} }
resuming = false // TODO pause channel if buffer gets too large
// TODO don't keep buffering if subscriber stops reading ws.buf = append(ws.buf, wr)
wrs = append(wrs, wr) nextRev = wr.Header.Revision
case resumeRev := <-ws.resumec: if len(wr.Events) > 0 {
wrs = nil nextRev = wr.Events[len(wr.Events)-1].Kv.ModRevision + 1
resuming = true
if resumeRev == -1 {
// pause serving stream while resume gets set up
break
} }
if resumeRev != ws.lastRev { ws.initReq.rev = nextRev
panic("unexpected resume revision")
}
case <-w.donec:
closing = true
closeErr = w.closeErr
case <-ws.initReq.ctx.Done(): case <-ws.initReq.ctx.Done():
closing = true return
case <-resumec:
resuming = true
return
} }
} }
// try to send off close error
if closeErr != nil {
select {
case ws.outc <- WatchResponse{closeErr: w.closeErr}:
case <-w.donec:
case <-time.After(closeSendErrTimeout):
}
}
w.closeStream(ws)
w.stopIfEmpty()
// lazily send cancel message if events on missing id // lazily send cancel message if events on missing id
} }
func (wgs *watchGrpcStream) stopIfEmpty() {
wgs.mu.Lock()
if len(wgs.streams) == 0 && wgs.stopc != nil {
close(wgs.stopc)
wgs.stopc = nil
}
wgs.mu.Unlock()
}
func (w *watchGrpcStream) newWatchClient() (pb.Watch_WatchClient, error) { func (w *watchGrpcStream) newWatchClient() (pb.Watch_WatchClient, error) {
ws, rerr := w.resume() // connect to grpc stream
if rerr != nil { wc, err := w.openWatchClient()
return nil, rerr if err != nil {
return nil, v3rpc.Error(err)
} }
go w.serveWatchClient(ws) // mark all substreams as resuming
return ws, nil if len(w.substreams)+len(w.resuming) > 0 {
} close(w.resumec)
w.resumec = make(chan struct{})
// resume creates a new WatchClient with all current watchers reestablished w.joinSubstreams()
func (w *watchGrpcStream) resume() (ws pb.Watch_WatchClient, err error) { for _, ws := range w.substreams {
for { ws.id = -1
if ws, err = w.openWatchClient(); err != nil { w.resuming = append(w.resuming, ws)
break }
} else if err = w.resumeWatchers(ws); err == nil { for _, ws := range w.resuming {
break if ws == nil || ws.closing {
continue
}
ws.donec = make(chan struct{})
go w.serveSubstream(ws, w.resumec)
}
}
w.substreams = make(map[int64]*watcherStream)
// receive data from new grpc stream
go w.serveWatchClient(wc)
return wc, nil
}
// joinSubstream waits for all substream goroutines to complete
func (w *watchGrpcStream) joinSubstreams() {
for _, ws := range w.substreams {
<-ws.donec
}
for _, ws := range w.resuming {
if ws != nil {
<-ws.donec
} }
} }
return ws, v3rpc.Error(err)
} }
// openWatchClient retries opening a watchclient until retryConnection fails // openWatchClient retries opening a watchclient until retryConnection fails
func (w *watchGrpcStream) openWatchClient() (ws pb.Watch_WatchClient, err error) { func (w *watchGrpcStream) openWatchClient() (ws pb.Watch_WatchClient, err error) {
for { for {
w.mu.Lock() select {
stopc := w.stopc case <-w.stopc:
w.mu.Unlock()
if stopc == nil {
if err == nil { if err == nil {
err = context.Canceled return nil, context.Canceled
} }
return nil, err return nil, err
default:
} }
if ws, err = w.remote.Watch(w.ctx, grpc.FailFast(false)); ws != nil && err == nil { if ws, err = w.remote.Watch(w.ctx, grpc.FailFast(false)); ws != nil && err == nil {
break break
@ -659,48 +669,6 @@ func (w *watchGrpcStream) openWatchClient() (ws pb.Watch_WatchClient, err error)
return ws, nil return ws, nil
} }
// resumeWatchers rebuilds every registered watcher on a new client
func (w *watchGrpcStream) resumeWatchers(wc pb.Watch_WatchClient) error {
w.mu.RLock()
streams := make([]*watcherStream, 0, len(w.streams))
for _, ws := range w.streams {
streams = append(streams, ws)
}
w.mu.RUnlock()
for _, ws := range streams {
// pause serveStream
ws.resumec <- -1
// reconstruct watcher from initial request
if ws.lastRev != 0 {
ws.initReq.rev = ws.lastRev
}
if err := wc.Send(ws.initReq.toPB()); err != nil {
return err
}
// wait for request ack
resp, err := wc.Recv()
if err != nil {
return err
} else if len(resp.Events) != 0 || !resp.Created {
return fmt.Errorf("watcher: unexpected response (%+v)", resp)
}
// id may be different since new remote watcher; update map
w.mu.Lock()
delete(w.streams, ws.id)
ws.id = resp.WatchId
w.streams[ws.id] = ws
w.mu.Unlock()
// unpause serveStream
ws.resumec <- ws.lastRev
}
return nil
}
// toPB converts an internal watch request structure to its protobuf messagefunc (wr *watchRequest) // toPB converts an internal watch request structure to its protobuf messagefunc (wr *watchRequest)
func (wr *watchRequest) toPB() *pb.WatchRequest { func (wr *watchRequest) toPB() *pb.WatchRequest {
req := &pb.WatchCreateRequest{ req := &pb.WatchCreateRequest{
@ -708,6 +676,7 @@ func (wr *watchRequest) toPB() *pb.WatchRequest {
Key: []byte(wr.key), Key: []byte(wr.key),
RangeEnd: []byte(wr.end), RangeEnd: []byte(wr.end),
ProgressNotify: wr.progressNotify, ProgressNotify: wr.progressNotify,
PrevKv: wr.prevKV,
} }
cr := &pb.WatchRequest_CreateRequest{CreateRequest: req} cr := &pb.WatchRequest_CreateRequest{CreateRequest: req}
return &pb.WatchRequest{RequestUnion: cr} return &pb.WatchRequest{RequestUnion: cr}

View File

@ -53,8 +53,8 @@ func SRVGetCluster(name, dns string, defaultToken string, apurls types.URLs) (st
return err return err
} }
for _, srv := range addrs { for _, srv := range addrs {
target := strings.TrimSuffix(srv.Target, ".") port := fmt.Sprintf("%d", srv.Port)
host := net.JoinHostPort(target, fmt.Sprintf("%d", srv.Port)) host := net.JoinHostPort(srv.Target, port)
tcpAddr, err := resolveTCPAddr("tcp", host) tcpAddr, err := resolveTCPAddr("tcp", host)
if err != nil { if err != nil {
plog.Warningf("couldn't resolve host %s during SRV discovery", host) plog.Warningf("couldn't resolve host %s during SRV discovery", host)
@ -70,8 +70,11 @@ func SRVGetCluster(name, dns string, defaultToken string, apurls types.URLs) (st
n = fmt.Sprintf("%d", tempName) n = fmt.Sprintf("%d", tempName)
tempName += 1 tempName += 1
} }
stringParts = append(stringParts, fmt.Sprintf("%s=%s%s", n, prefix, host)) // SRV records have a trailing dot but URL shouldn't.
plog.Noticef("got bootstrap from DNS for %s at %s%s", service, prefix, host) shortHost := strings.TrimSuffix(srv.Target, ".")
urlHost := net.JoinHostPort(shortHost, port)
stringParts = append(stringParts, fmt.Sprintf("%s=%s%s", n, prefix, urlHost))
plog.Noticef("got bootstrap from DNS for %s at %s%s", service, prefix, urlHost)
} }
return nil return nil
} }

View File

@ -159,6 +159,22 @@ func (a *applierV3backend) Put(txnID int64, p *pb.PutRequest) (*pb.PutResponse,
rev int64 rev int64
err error err error
) )
var rr *mvcc.RangeResult
if p.PrevKv {
if txnID != noTxn {
rr, err = a.s.KV().TxnRange(txnID, p.Key, nil, mvcc.RangeOptions{})
if err != nil {
return nil, err
}
} else {
rr, err = a.s.KV().Range(p.Key, nil, mvcc.RangeOptions{})
if err != nil {
return nil, err
}
}
}
if txnID != noTxn { if txnID != noTxn {
rev, err = a.s.KV().TxnPut(txnID, p.Key, p.Value, lease.LeaseID(p.Lease)) rev, err = a.s.KV().TxnPut(txnID, p.Key, p.Value, lease.LeaseID(p.Lease))
if err != nil { if err != nil {
@ -174,6 +190,9 @@ func (a *applierV3backend) Put(txnID int64, p *pb.PutRequest) (*pb.PutResponse,
rev = a.s.KV().Put(p.Key, p.Value, leaseID) rev = a.s.KV().Put(p.Key, p.Value, leaseID)
} }
resp.Header.Revision = rev resp.Header.Revision = rev
if rr != nil && len(rr.KVs) != 0 {
resp.PrevKv = &rr.KVs[0]
}
return resp, nil return resp, nil
} }
@ -191,6 +210,21 @@ func (a *applierV3backend) DeleteRange(txnID int64, dr *pb.DeleteRangeRequest) (
dr.RangeEnd = []byte{} dr.RangeEnd = []byte{}
} }
var rr *mvcc.RangeResult
if dr.PrevKv {
if txnID != noTxn {
rr, err = a.s.KV().TxnRange(txnID, dr.Key, dr.RangeEnd, mvcc.RangeOptions{})
if err != nil {
return nil, err
}
} else {
rr, err = a.s.KV().Range(dr.Key, dr.RangeEnd, mvcc.RangeOptions{})
if err != nil {
return nil, err
}
}
}
if txnID != noTxn { if txnID != noTxn {
n, rev, err = a.s.KV().TxnDeleteRange(txnID, dr.Key, dr.RangeEnd) n, rev, err = a.s.KV().TxnDeleteRange(txnID, dr.Key, dr.RangeEnd)
if err != nil { if err != nil {
@ -201,6 +235,11 @@ func (a *applierV3backend) DeleteRange(txnID int64, dr *pb.DeleteRangeRequest) (
} }
resp.Deleted = n resp.Deleted = n
if rr != nil {
for i := range rr.KVs {
resp.PrevKvs = append(resp.PrevKvs, &rr.KVs[i])
}
}
resp.Header.Revision = rev resp.Header.Revision = rev
return resp, nil return resp, nil
} }

View File

@ -56,6 +56,9 @@ func (aa *authApplierV3) Put(txnID int64, r *pb.PutRequest) (*pb.PutResponse, er
if !aa.as.IsPutPermitted(aa.user, r.Key) { if !aa.as.IsPutPermitted(aa.user, r.Key) {
return nil, auth.ErrPermissionDenied return nil, auth.ErrPermissionDenied
} }
if r.PrevKv && !aa.as.IsRangePermitted(aa.user, r.Key, nil) {
return nil, auth.ErrPermissionDenied
}
return aa.applierV3.Put(txnID, r) return aa.applierV3.Put(txnID, r)
} }
@ -70,6 +73,9 @@ func (aa *authApplierV3) DeleteRange(txnID int64, r *pb.DeleteRangeRequest) (*pb
if !aa.as.IsDeleteRangePermitted(aa.user, r.Key, r.RangeEnd) { if !aa.as.IsDeleteRangePermitted(aa.user, r.Key, r.RangeEnd) {
return nil, auth.ErrPermissionDenied return nil, auth.ErrPermissionDenied
} }
if r.PrevKv && !aa.as.IsRangePermitted(aa.user, r.Key, r.RangeEnd) {
return nil, auth.ErrPermissionDenied
}
return aa.applierV3.DeleteRange(txnID, r) return aa.applierV3.DeleteRange(txnID, r)
} }
@ -99,7 +105,7 @@ func (aa *authApplierV3) checkTxnReqsPermission(reqs []*pb.RequestOp) bool {
continue continue
} }
if !aa.as.IsDeleteRangePermitted(aa.user, tv.RequestDeleteRange.Key, tv.RequestDeleteRange.RangeEnd) { if tv.RequestDeleteRange.PrevKv && !aa.as.IsRangePermitted(aa.user, tv.RequestDeleteRange.Key, tv.RequestDeleteRange.RangeEnd) {
return false return false
} }
} }

View File

@ -102,9 +102,9 @@ import (
proto "github.com/golang/protobuf/proto" proto "github.com/golang/protobuf/proto"
math "math" math "math"
)
import io "io" io "io"
)
// Reference imports to suppress errors if they are not otherwise used. // Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal var _ = proto.Marshal

View File

@ -10,9 +10,9 @@ import (
proto "github.com/golang/protobuf/proto" proto "github.com/golang/protobuf/proto"
math "math" math "math"
)
import io "io" io "io"
)
// Reference imports to suppress errors if they are not otherwise used. // Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal var _ = proto.Marshal

File diff suppressed because it is too large Load Diff

View File

@ -396,10 +396,16 @@ message PutRequest {
// lease is the lease ID to associate with the key in the key-value store. A lease // lease is the lease ID to associate with the key in the key-value store. A lease
// value of 0 indicates no lease. // value of 0 indicates no lease.
int64 lease = 3; int64 lease = 3;
// If prev_kv is set, etcd gets the previous key-value pair before changing it.
// The previous key-value pair will be returned in the put response.
bool prev_kv = 4;
} }
message PutResponse { message PutResponse {
ResponseHeader header = 1; ResponseHeader header = 1;
// if prev_kv is set in the request, the previous key-value pair will be returned.
mvccpb.KeyValue prev_kv = 2;
} }
message DeleteRangeRequest { message DeleteRangeRequest {
@ -409,12 +415,17 @@ message DeleteRangeRequest {
// If range_end is not given, the range is defined to contain only the key argument. // If range_end is not given, the range is defined to contain only the key argument.
// If range_end is '\0', the range is all keys greater than or equal to the key argument. // If range_end is '\0', the range is all keys greater than or equal to the key argument.
bytes range_end = 2; bytes range_end = 2;
// If prev_kv is set, etcd gets the previous key-value pairs before deleting it.
// The previous key-value pairs will be returned in the delte response.
bool prev_kv = 3;
} }
message DeleteRangeResponse { message DeleteRangeResponse {
ResponseHeader header = 1; ResponseHeader header = 1;
// deleted is the number of keys deleted by the delete range request. // deleted is the number of keys deleted by the delete range request.
int64 deleted = 2; int64 deleted = 2;
// if prev_kv is set in the request, the previous key-value pairs will be returned.
repeated mvccpb.KeyValue prev_kvs = 3;
} }
message RequestOp { message RequestOp {
@ -563,6 +574,9 @@ message WatchCreateRequest {
// wish to recover a disconnected watcher starting from a recent known revision. // wish to recover a disconnected watcher starting from a recent known revision.
// The etcd server may decide how often it will send notifications based on current load. // The etcd server may decide how often it will send notifications based on current load.
bool progress_notify = 4; bool progress_notify = 4;
// If prev_kv is set, created watcher gets the previous KV before the event happens.
// If the previous KV is already compacted, nothing will be returned.
bool prev_kv = 6;
} }
message WatchCancelRequest { message WatchCancelRequest {

View File

@ -412,8 +412,13 @@ func NewServer(cfg *ServerConfig) (srv *EtcdServer, err error) {
srv.kv = mvcc.New(srv.be, srv.lessor, &srv.consistIndex) srv.kv = mvcc.New(srv.be, srv.lessor, &srv.consistIndex)
if beExist { if beExist {
kvindex := srv.kv.ConsistentIndex() kvindex := srv.kv.ConsistentIndex()
// TODO: remove kvindex != 0 checking when we do not expect users to upgrade
// etcd from pre-3.0 release.
if snapshot != nil && kvindex < snapshot.Metadata.Index { if snapshot != nil && kvindex < snapshot.Metadata.Index {
return nil, fmt.Errorf("database file (%v index %d) does not match with snapshot (index %d).", bepath, kvindex, snapshot.Metadata.Index) if kvindex != 0 {
return nil, fmt.Errorf("database file (%v index %d) does not match with snapshot (index %d).", bepath, kvindex, snapshot.Metadata.Index)
}
plog.Warningf("consistent index never saved (snapshot index=%d)", snapshot.Metadata.Index)
} }
} }
srv.consistIndex.setConsistentIndex(srv.kv.ConsistentIndex()) srv.consistIndex.setConsistentIndex(srv.kv.ConsistentIndex())

View File

@ -551,4 +551,4 @@ func (s *EtcdServer) processInternalRaftRequest(ctx context.Context, r pb.Intern
} }
// Watchable returns a watchable interface attached to the etcdserver. // Watchable returns a watchable interface attached to the etcdserver.
func (s *EtcdServer) Watchable() mvcc.Watchable { return s.KV() } func (s *EtcdServer) Watchable() mvcc.WatchableKV { return s.KV() }

View File

@ -19,9 +19,9 @@ import (
proto "github.com/golang/protobuf/proto" proto "github.com/golang/protobuf/proto"
math "math" math "math"
)
import io "io" io "io"
)
// Reference imports to suppress errors if they are not otherwise used. // Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal var _ = proto.Marshal

View File

@ -20,9 +20,9 @@ import (
proto "github.com/golang/protobuf/proto" proto "github.com/golang/protobuf/proto"
math "math" math "math"
)
import io "io" io "io"
)
// Reference imports to suppress errors if they are not otherwise used. // Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal var _ = proto.Marshal
@ -89,6 +89,8 @@ type Event struct {
// A DELETE/EXPIRE event contains the deleted key with // A DELETE/EXPIRE event contains the deleted key with
// its modification revision set to the revision of deletion. // its modification revision set to the revision of deletion.
Kv *KeyValue `protobuf:"bytes,2,opt,name=kv" json:"kv,omitempty"` Kv *KeyValue `protobuf:"bytes,2,opt,name=kv" json:"kv,omitempty"`
// prev_kv holds the key-value pair before the event happens.
PrevKv *KeyValue `protobuf:"bytes,3,opt,name=prev_kv,json=prevKv" json:"prev_kv,omitempty"`
} }
func (m *Event) Reset() { *m = Event{} } func (m *Event) Reset() { *m = Event{} }
@ -181,6 +183,16 @@ func (m *Event) MarshalTo(data []byte) (int, error) {
} }
i += n1 i += n1
} }
if m.PrevKv != nil {
data[i] = 0x1a
i++
i = encodeVarintKv(data, i, uint64(m.PrevKv.Size()))
n2, err := m.PrevKv.MarshalTo(data[i:])
if err != nil {
return 0, err
}
i += n2
}
return i, nil return i, nil
} }
@ -247,6 +259,10 @@ func (m *Event) Size() (n int) {
l = m.Kv.Size() l = m.Kv.Size()
n += 1 + l + sovKv(uint64(l)) n += 1 + l + sovKv(uint64(l))
} }
if m.PrevKv != nil {
l = m.PrevKv.Size()
n += 1 + l + sovKv(uint64(l))
}
return n return n
} }
@ -532,6 +548,39 @@ func (m *Event) Unmarshal(data []byte) error {
return err return err
} }
iNdEx = postIndex iNdEx = postIndex
case 3:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field PrevKv", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowKv
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := data[iNdEx]
iNdEx++
msglen |= (int(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLengthKv
}
postIndex := iNdEx + msglen
if postIndex > l {
return io.ErrUnexpectedEOF
}
if m.PrevKv == nil {
m.PrevKv = &KeyValue{}
}
if err := m.PrevKv.Unmarshal(data[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
default: default:
iNdEx = preIndex iNdEx = preIndex
skippy, err := skipKv(data[iNdEx:]) skippy, err := skipKv(data[iNdEx:])
@ -659,23 +708,24 @@ var (
) )
var fileDescriptorKv = []byte{ var fileDescriptorKv = []byte{
// 285 bytes of a gzipped FileDescriptorProto // 303 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x44, 0x90, 0x41, 0x4a, 0xc3, 0x40, 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x6c, 0x90, 0x41, 0x4e, 0xc2, 0x40,
0x14, 0x86, 0x33, 0x4d, 0x9b, 0xd6, 0xd7, 0x52, 0xc3, 0x50, 0x70, 0x70, 0x31, 0xc4, 0x6e, 0x2c, 0x14, 0x86, 0x3b, 0x14, 0x0a, 0x3e, 0x08, 0x36, 0x13, 0x12, 0x27, 0x2e, 0x26, 0x95, 0x8d, 0x18,
0x08, 0x11, 0xea, 0x0d, 0xc4, 0xac, 0x74, 0x21, 0x21, 0xba, 0x95, 0x34, 0x7d, 0x94, 0x92, 0xa6, 0x13, 0x4c, 0xf0, 0x06, 0xc6, 0xae, 0x70, 0x61, 0x1a, 0x74, 0x4b, 0x4a, 0x79, 0x21, 0xa4, 0x94,
0x13, 0xd2, 0x38, 0x98, 0x9b, 0x78, 0x0a, 0xcf, 0xd1, 0x65, 0x8f, 0x60, 0xe3, 0x45, 0x24, 0x6f, 0x69, 0x4a, 0x9d, 0xa4, 0x37, 0x71, 0xef, 0xde, 0x73, 0xb0, 0xe4, 0x08, 0x52, 0x2f, 0x62, 0xfa,
0x4c, 0xdd, 0x0c, 0xef, 0xff, 0xff, 0x6f, 0x98, 0xff, 0x0d, 0x0c, 0x52, 0xed, 0xe7, 0x85, 0x2a, 0xc6, 0xe2, 0xc6, 0xcd, 0xe4, 0xfd, 0xff, 0xff, 0x65, 0xe6, 0x7f, 0x03, 0x9d, 0x58, 0x8f, 0xd3,
0x15, 0x77, 0x32, 0x9d, 0x24, 0xf9, 0xe2, 0x72, 0xb2, 0x52, 0x2b, 0x45, 0xd6, 0x6d, 0x33, 0x99, 0x4c, 0xe5, 0x8a, 0x3b, 0x89, 0x8e, 0xa2, 0x74, 0x71, 0x39, 0x58, 0xa9, 0x95, 0x22, 0xeb, 0xae,
0x74, 0xfa, 0xc5, 0x60, 0xf0, 0x88, 0xd5, 0x6b, 0xbc, 0x79, 0x47, 0xee, 0x82, 0x9d, 0x62, 0x25, 0x9a, 0x4c, 0x3a, 0xfc, 0x64, 0xd0, 0x99, 0x62, 0xf1, 0x1a, 0x6e, 0xde, 0x90, 0xbb, 0x60, 0xc7,
0x98, 0xc7, 0x66, 0xa3, 0xb0, 0x19, 0xf9, 0x35, 0x9c, 0x27, 0x05, 0xc6, 0x25, 0xbe, 0x15, 0xa8, 0x58, 0x08, 0xe6, 0xb1, 0x51, 0x2f, 0xa8, 0x46, 0x7e, 0x0d, 0xe7, 0x51, 0x86, 0x61, 0x8e, 0xf3,
0xd7, 0xbb, 0xb5, 0xda, 0x8a, 0x8e, 0xc7, 0x66, 0x76, 0x38, 0x36, 0x76, 0xf8, 0xe7, 0xf2, 0x2b, 0x0c, 0xf5, 0x7a, 0xb7, 0x56, 0x5b, 0xd1, 0xf0, 0xd8, 0xc8, 0x0e, 0xfa, 0xc6, 0x0e, 0x7e, 0x5d,
0x18, 0x65, 0x6a, 0xf9, 0x4f, 0xd9, 0x44, 0x0d, 0x33, 0xb5, 0x3c, 0x21, 0x02, 0xfa, 0x1a, 0x0b, 0x7e, 0x05, 0xbd, 0x44, 0x2d, 0xff, 0x28, 0x9b, 0xa8, 0x6e, 0xa2, 0x96, 0x27, 0x44, 0x40, 0x5b,
0x4a, 0xbb, 0x94, 0xb6, 0x92, 0x4f, 0xa0, 0xa7, 0x9b, 0x02, 0xa2, 0x47, 0x2f, 0x1b, 0xd1, 0xb8, 0x63, 0x46, 0x69, 0x93, 0xd2, 0x5a, 0xf2, 0x01, 0xb4, 0x74, 0x55, 0x40, 0xb4, 0xe8, 0x65, 0x23,
0x1b, 0x8c, 0x77, 0x28, 0x1c, 0xa2, 0x8d, 0x98, 0x7e, 0x40, 0x2f, 0xd0, 0xb8, 0x2d, 0xf9, 0x0d, 0x2a, 0x77, 0x83, 0xe1, 0x0e, 0x85, 0x43, 0xb4, 0x11, 0xc3, 0x0f, 0x06, 0x2d, 0x5f, 0xe3, 0x36,
0x74, 0xcb, 0x2a, 0x47, 0x6a, 0x3b, 0x9e, 0x5f, 0xf8, 0x66, 0x4d, 0x9f, 0x42, 0x73, 0x46, 0x55, 0xe7, 0xb7, 0xd0, 0xcc, 0x8b, 0x14, 0xa9, 0x6e, 0x7f, 0x72, 0x31, 0x36, 0x7b, 0x8e, 0x29, 0x34,
0x8e, 0x21, 0x41, 0xdc, 0x83, 0x4e, 0xaa, 0xa9, 0xfa, 0x70, 0xee, 0xb6, 0x68, 0xbb, 0x77, 0xd8, 0xe7, 0xac, 0x48, 0x31, 0x20, 0x88, 0x7b, 0xd0, 0x88, 0x35, 0x75, 0xef, 0x4e, 0xdc, 0x1a, 0xad,
0x49, 0xf5, 0xd4, 0x83, 0xb3, 0xd3, 0x25, 0xde, 0x07, 0xfb, 0xf9, 0x25, 0x72, 0x2d, 0x0e, 0xe0, 0x17, 0x0f, 0x1a, 0xb1, 0xe6, 0x37, 0xd0, 0x4e, 0x33, 0xd4, 0xf3, 0x58, 0x53, 0xf9, 0xff, 0x30,
0x3c, 0x04, 0x4f, 0x41, 0x14, 0xb8, 0xec, 0x5e, 0xec, 0x8f, 0xd2, 0x3a, 0x1c, 0xa5, 0xb5, 0xaf, 0xa7, 0x02, 0xa6, 0x7a, 0xe8, 0xc1, 0xd9, 0xe9, 0x7e, 0xde, 0x06, 0xfb, 0xf9, 0x65, 0xe6, 0x5a,
0x25, 0x3b, 0xd4, 0x92, 0x7d, 0xd7, 0x92, 0x7d, 0xfe, 0x48, 0x6b, 0xe1, 0xd0, 0x5f, 0xde, 0xfd, 0x1c, 0xc0, 0x79, 0xf4, 0x9f, 0xfc, 0x99, 0xef, 0xb2, 0x07, 0xb1, 0x3f, 0x4a, 0xeb, 0x70, 0x94,
0x06, 0x00, 0x00, 0xff, 0xff, 0xd6, 0x21, 0x8f, 0x2c, 0x75, 0x01, 0x00, 0x00, 0xd6, 0xbe, 0x94, 0xec, 0x50, 0x4a, 0xf6, 0x55, 0x4a, 0xf6, 0xfe, 0x2d, 0xad, 0x85, 0x43, 0xff,
0x7e, 0xff, 0x13, 0x00, 0x00, 0xff, 0xff, 0xb5, 0x45, 0x92, 0x5d, 0xa1, 0x01, 0x00, 0x00,
} }

View File

@ -43,4 +43,6 @@ message Event {
// A DELETE/EXPIRE event contains the deleted key with // A DELETE/EXPIRE event contains the deleted key with
// its modification revision set to the revision of deletion. // its modification revision set to the revision of deletion.
KeyValue kv = 2; KeyValue kv = 2;
// prev_kv holds the key-value pair before the event happens.
KeyValue prev_kv = 3;
} }

22
vendor/github.com/coreos/etcd/pkg/fileutil/dir_unix.go generated vendored Normal file
View File

@ -0,0 +1,22 @@
// Copyright 2016 The etcd Authors
//
// 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.
// +build !windows
package fileutil
import "os"
// OpenDir opens a directory for syncing.
func OpenDir(path string) (*os.File, error) { return os.Open(path) }

View File

@ -0,0 +1,46 @@
// Copyright 2016 The etcd Authors
//
// 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.
// +build windows
package fileutil
import (
"os"
"syscall"
)
// OpenDir opens a directory in windows with write access for syncing.
func OpenDir(path string) (*os.File, error) {
fd, err := openDir(path)
if err != nil {
return nil, err
}
return os.NewFile(uintptr(fd), path), nil
}
func openDir(path string) (fd syscall.Handle, err error) {
if len(path) == 0 {
return syscall.InvalidHandle, syscall.ERROR_FILE_NOT_FOUND
}
pathp, err := syscall.UTF16PtrFromString(path)
if err != nil {
return syscall.InvalidHandle, err
}
access := uint32(syscall.GENERIC_READ | syscall.GENERIC_WRITE)
sharemode := uint32(syscall.FILE_SHARE_READ | syscall.FILE_SHARE_WRITE)
createmode := uint32(syscall.OPEN_EXISTING)
fl := uint32(syscall.FILE_FLAG_BACKUP_SEMANTICS)
return syscall.CreateFile(pathp, access, sharemode, nil, createmode, fl, 0)
}

View File

@ -96,3 +96,26 @@ func Exist(name string) bool {
_, err := os.Stat(name) _, err := os.Stat(name)
return err == nil return err == nil
} }
// ZeroToEnd zeros a file starting from SEEK_CUR to its SEEK_END. May temporarily
// shorten the length of the file.
func ZeroToEnd(f *os.File) error {
// TODO: support FALLOC_FL_ZERO_RANGE
off, err := f.Seek(0, os.SEEK_CUR)
if err != nil {
return err
}
lenf, lerr := f.Seek(0, os.SEEK_END)
if lerr != nil {
return lerr
}
if err = f.Truncate(off); err != nil {
return err
}
// make sure blocks remain allocated
if err = Preallocate(f, lenf, true); err != nil {
return err
}
_, err = f.Seek(off, os.SEEK_SET)
return err
}

106
vendor/github.com/coreos/etcd/pkg/ioutil/pagewriter.go generated vendored Normal file
View File

@ -0,0 +1,106 @@
// Copyright 2016 The etcd Authors
//
// 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.
package ioutil
import (
"io"
)
var defaultBufferBytes = 128 * 1024
// PageWriter implements the io.Writer interface so that writes will
// either be in page chunks or from flushing.
type PageWriter struct {
w io.Writer
// pageOffset tracks the page offset of the base of the buffer
pageOffset int
// pageBytes is the number of bytes per page
pageBytes int
// bufferedBytes counts the number of bytes pending for write in the buffer
bufferedBytes int
// buf holds the write buffer
buf []byte
// bufWatermarkBytes is the number of bytes the buffer can hold before it needs
// to be flushed. It is less than len(buf) so there is space for slack writes
// to bring the writer to page alignment.
bufWatermarkBytes int
}
// NewPageWriter creates a new PageWriter. pageBytes is the number of bytes
// to write per page. pageOffset is the starting offset of io.Writer.
func NewPageWriter(w io.Writer, pageBytes, pageOffset int) *PageWriter {
return &PageWriter{
w: w,
pageOffset: pageOffset,
pageBytes: pageBytes,
buf: make([]byte, defaultBufferBytes+pageBytes),
bufWatermarkBytes: defaultBufferBytes,
}
}
func (pw *PageWriter) Write(p []byte) (n int, err error) {
if len(p)+pw.bufferedBytes <= pw.bufWatermarkBytes {
// no overflow
copy(pw.buf[pw.bufferedBytes:], p)
pw.bufferedBytes += len(p)
return len(p), nil
}
// complete the slack page in the buffer if unaligned
slack := pw.pageBytes - ((pw.pageOffset + pw.bufferedBytes) % pw.pageBytes)
if slack != pw.pageBytes {
partial := slack > len(p)
if partial {
// not enough data to complete the slack page
slack = len(p)
}
// special case: writing to slack page in buffer
copy(pw.buf[pw.bufferedBytes:], p[:slack])
pw.bufferedBytes += slack
n = slack
p = p[slack:]
if partial {
// avoid forcing an unaligned flush
return n, nil
}
}
// buffer contents are now page-aligned; clear out
if err = pw.Flush(); err != nil {
return n, err
}
// directly write all complete pages without copying
if len(p) > pw.pageBytes {
pages := len(p) / pw.pageBytes
c, werr := pw.w.Write(p[:pages*pw.pageBytes])
n += c
if werr != nil {
return n, werr
}
p = p[pages*pw.pageBytes:]
}
// write remaining tail to buffer
c, werr := pw.Write(p)
n += c
return n, werr
}
func (pw *PageWriter) Flush() error {
if pw.bufferedBytes == 0 {
return nil
}
_, err := pw.w.Write(pw.buf[:pw.bufferedBytes])
pw.pageOffset = (pw.pageOffset + pw.bufferedBytes) % pw.pageBytes
pw.bufferedBytes = 0
return err
}

View File

@ -25,9 +25,9 @@ import (
proto "github.com/golang/protobuf/proto" proto "github.com/golang/protobuf/proto"
math "math" math "math"
)
import io "io" io "io"
)
// Reference imports to suppress errors if they are not otherwise used. // Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal var _ = proto.Marshal

View File

@ -49,6 +49,7 @@ var (
"2.1.0": {streamTypeMsgAppV2, streamTypeMessage}, "2.1.0": {streamTypeMsgAppV2, streamTypeMessage},
"2.2.0": {streamTypeMsgAppV2, streamTypeMessage}, "2.2.0": {streamTypeMsgAppV2, streamTypeMessage},
"2.3.0": {streamTypeMsgAppV2, streamTypeMessage}, "2.3.0": {streamTypeMsgAppV2, streamTypeMessage},
"3.0.0": {streamTypeMsgAppV2, streamTypeMessage},
} }
) )

View File

@ -19,9 +19,9 @@ import (
proto "github.com/golang/protobuf/proto" proto "github.com/golang/protobuf/proto"
math "math" math "math"
)
import io "io" io "io"
)
// Reference imports to suppress errors if they are not otherwise used. // Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal var _ = proto.Marshal

View File

@ -29,7 +29,7 @@ import (
var ( var (
// MinClusterVersion is the min cluster version this etcd binary is compatible with. // MinClusterVersion is the min cluster version this etcd binary is compatible with.
MinClusterVersion = "2.3.0" MinClusterVersion = "2.3.0"
Version = "3.0.6" Version = "3.0.12"
// Git SHA Value will be set during build // Git SHA Value will be set during build
GitSHA = "Not provided (use ./build instead of go build)" GitSHA = "Not provided (use ./build instead of go build)"

View File

@ -15,28 +15,34 @@
package wal package wal
import ( import (
"bufio"
"encoding/binary" "encoding/binary"
"hash" "hash"
"io" "io"
"os"
"sync" "sync"
"github.com/coreos/etcd/pkg/crc" "github.com/coreos/etcd/pkg/crc"
"github.com/coreos/etcd/pkg/ioutil"
"github.com/coreos/etcd/wal/walpb" "github.com/coreos/etcd/wal/walpb"
) )
// walPageBytes is the alignment for flushing records to the backing Writer.
// It should be a multiple of the minimum sector size so that WAL repair can
// safely between torn writes and ordinary data corruption.
const walPageBytes = 8 * minSectorSize
type encoder struct { type encoder struct {
mu sync.Mutex mu sync.Mutex
bw *bufio.Writer bw *ioutil.PageWriter
crc hash.Hash32 crc hash.Hash32
buf []byte buf []byte
uint64buf []byte uint64buf []byte
} }
func newEncoder(w io.Writer, prevCrc uint32) *encoder { func newEncoder(w io.Writer, prevCrc uint32, pageOffset int) *encoder {
return &encoder{ return &encoder{
bw: bufio.NewWriter(w), bw: ioutil.NewPageWriter(w, walPageBytes, pageOffset),
crc: crc.New(prevCrc, crcTable), crc: crc.New(prevCrc, crcTable),
// 1MB buffer // 1MB buffer
buf: make([]byte, 1024*1024), buf: make([]byte, 1024*1024),
@ -44,6 +50,15 @@ func newEncoder(w io.Writer, prevCrc uint32) *encoder {
} }
} }
// newFileEncoder creates a new encoder with current file offset for the page writer.
func newFileEncoder(f *os.File, prevCrc uint32) (*encoder, error) {
offset, err := f.Seek(0, os.SEEK_CUR)
if err != nil {
return nil, err
}
return newEncoder(f, prevCrc, int(offset)), nil
}
func (e *encoder) encode(rec *walpb.Record) error { func (e *encoder) encode(rec *walpb.Record) error {
e.mu.Lock() e.mu.Lock()
defer e.mu.Unlock() defer e.mu.Unlock()

View File

@ -67,7 +67,11 @@ var (
// A just opened WAL is in read mode, and ready for reading records. // A just opened WAL is in read mode, and ready for reading records.
// The WAL will be ready for appending after reading out all the previous records. // The WAL will be ready for appending after reading out all the previous records.
type WAL struct { type WAL struct {
dir string // the living directory of the underlay files dir string // the living directory of the underlay files
// dirFile is a fd for the wal directory for syncing on Rename
dirFile *os.File
metadata []byte // metadata recorded at the head of each WAL metadata []byte // metadata recorded at the head of each WAL
state raftpb.HardState // hardstate recorded at the head of WAL state raftpb.HardState // hardstate recorded at the head of WAL
@ -106,45 +110,49 @@ func Create(dirpath string, metadata []byte) (*WAL, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
if _, err := f.Seek(0, os.SEEK_END); err != nil { if _, err = f.Seek(0, os.SEEK_END); err != nil {
return nil, err return nil, err
} }
if err := fileutil.Preallocate(f.File, segmentSizeBytes, true); err != nil { if err = fileutil.Preallocate(f.File, segmentSizeBytes, true); err != nil {
return nil, err return nil, err
} }
w := &WAL{ w := &WAL{
dir: dirpath, dir: dirpath,
metadata: metadata, metadata: metadata,
encoder: newEncoder(f, 0), }
w.encoder, err = newFileEncoder(f.File, 0)
if err != nil {
return nil, err
} }
w.locks = append(w.locks, f) w.locks = append(w.locks, f)
if err := w.saveCrc(0); err != nil { if err = w.saveCrc(0); err != nil {
return nil, err return nil, err
} }
if err := w.encoder.encode(&walpb.Record{Type: metadataType, Data: metadata}); err != nil { if err = w.encoder.encode(&walpb.Record{Type: metadataType, Data: metadata}); err != nil {
return nil, err return nil, err
} }
if err := w.SaveSnapshot(walpb.Snapshot{}); err != nil { if err = w.SaveSnapshot(walpb.Snapshot{}); err != nil {
return nil, err return nil, err
} }
// rename of directory with locked files doesn't work on windows; close if w, err = w.renameWal(tmpdirpath); err != nil {
// the WAL to release the locks so the directory can be renamed
w.Close()
if err := os.Rename(tmpdirpath, dirpath); err != nil {
return nil, err return nil, err
} }
// reopen and relock
newWAL, oerr := Open(dirpath, walpb.Snapshot{}) // directory was renamed; sync parent dir to persist rename
if oerr != nil { pdir, perr := fileutil.OpenDir(path.Dir(w.dir))
return nil, oerr if perr != nil {
return nil, perr
} }
if _, _, _, err := newWAL.ReadAll(); err != nil { if perr = fileutil.Fsync(pdir); perr != nil {
newWAL.Close() return nil, perr
return nil, err
} }
return newWAL, nil if perr = pdir.Close(); err != nil {
return nil, perr
}
return w, nil
} }
// Open opens the WAL at the given snap. // Open opens the WAL at the given snap.
@ -154,7 +162,14 @@ func Create(dirpath string, metadata []byte) (*WAL, error) {
// the given snap. The WAL cannot be appended to before reading out all of its // the given snap. The WAL cannot be appended to before reading out all of its
// previous records. // previous records.
func Open(dirpath string, snap walpb.Snapshot) (*WAL, error) { func Open(dirpath string, snap walpb.Snapshot) (*WAL, error) {
return openAtIndex(dirpath, snap, true) w, err := openAtIndex(dirpath, snap, true)
if err != nil {
return nil, err
}
if w.dirFile, err = fileutil.OpenDir(w.dir); err != nil {
return nil, err
}
return w, nil
} }
// OpenForRead only opens the wal files for read. // OpenForRead only opens the wal files for read.
@ -299,6 +314,18 @@ func (w *WAL) ReadAll() (metadata []byte, state raftpb.HardState, ents []raftpb.
state.Reset() state.Reset()
return nil, state, nil, err return nil, state, nil, err
} }
// decodeRecord() will return io.EOF if it detects a zero record,
// but this zero record may be followed by non-zero records from
// a torn write. Overwriting some of these non-zero records, but
// not all, will cause CRC errors on WAL open. Since the records
// were never fully synced to disk in the first place, it's safe
// to zero them out to avoid any CRC errors from new writes.
if _, err = w.tail().Seek(w.decoder.lastOffset(), os.SEEK_SET); err != nil {
return nil, state, nil, err
}
if err = fileutil.ZeroToEnd(w.tail().File); err != nil {
return nil, state, nil, err
}
} }
err = nil err = nil
@ -317,8 +344,10 @@ func (w *WAL) ReadAll() (metadata []byte, state raftpb.HardState, ents []raftpb.
if w.tail() != nil { if w.tail() != nil {
// create encoder (chain crc with the decoder), enable appending // create encoder (chain crc with the decoder), enable appending
_, err = w.tail().Seek(w.decoder.lastOffset(), os.SEEK_SET) w.encoder, err = newFileEncoder(w.tail().File, w.decoder.lastCRC())
w.encoder = newEncoder(w.tail(), w.decoder.lastCRC()) if err != nil {
return
}
} }
w.decoder = nil w.decoder = nil
@ -352,7 +381,10 @@ func (w *WAL) cut() error {
// update writer and save the previous crc // update writer and save the previous crc
w.locks = append(w.locks, newTail) w.locks = append(w.locks, newTail)
prevCrc := w.encoder.crc.Sum32() prevCrc := w.encoder.crc.Sum32()
w.encoder = newEncoder(w.tail(), prevCrc) w.encoder, err = newFileEncoder(w.tail().File, prevCrc)
if err != nil {
return err
}
if err = w.saveCrc(prevCrc); err != nil { if err = w.saveCrc(prevCrc); err != nil {
return err return err
} }
@ -375,6 +407,10 @@ func (w *WAL) cut() error {
if err = os.Rename(newTail.Name(), fpath); err != nil { if err = os.Rename(newTail.Name(), fpath); err != nil {
return err return err
} }
if err = fileutil.Fsync(w.dirFile); err != nil {
return err
}
newTail.Close() newTail.Close()
if newTail, err = fileutil.LockFile(fpath, os.O_WRONLY, fileutil.PrivateFileMode); err != nil { if newTail, err = fileutil.LockFile(fpath, os.O_WRONLY, fileutil.PrivateFileMode); err != nil {
@ -387,7 +423,10 @@ func (w *WAL) cut() error {
w.locks[len(w.locks)-1] = newTail w.locks[len(w.locks)-1] = newTail
prevCrc = w.encoder.crc.Sum32() prevCrc = w.encoder.crc.Sum32()
w.encoder = newEncoder(w.tail(), prevCrc) w.encoder, err = newFileEncoder(w.tail().File, prevCrc)
if err != nil {
return err
}
plog.Infof("segmented wal file %v is created", fpath) plog.Infof("segmented wal file %v is created", fpath)
return nil return nil
@ -477,7 +516,7 @@ func (w *WAL) Close() error {
plog.Errorf("failed to unlock during closing wal: %s", err) plog.Errorf("failed to unlock during closing wal: %s", err)
} }
} }
return nil return w.dirFile.Close()
} }
func (w *WAL) saveEntry(e *raftpb.Entry) error { func (w *WAL) saveEntry(e *raftpb.Entry) error {

44
vendor/github.com/coreos/etcd/wal/wal_unix.go generated vendored Normal file
View File

@ -0,0 +1,44 @@
// Copyright 2016 The etcd Authors
//
// 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.
// +build !windows
package wal
import (
"os"
"github.com/coreos/etcd/pkg/fileutil"
)
func (w *WAL) renameWal(tmpdirpath string) (*WAL, error) {
// On non-Windows platforms, hold the lock while renaming. Releasing
// the lock and trying to reacquire it quickly can be flaky because
// it's possible the process will fork to spawn a process while this is
// happening. The fds are set up as close-on-exec by the Go runtime,
// but there is a window between the fork and the exec where another
// process holds the lock.
if err := os.RemoveAll(w.dir); err != nil {
return nil, err
}
if err := os.Rename(tmpdirpath, w.dir); err != nil {
return nil, err
}
w.fp = newFilePipeline(w.dir, segmentSizeBytes)
df, err := fileutil.OpenDir(w.dir)
w.dirFile = df
return w, err
}

41
vendor/github.com/coreos/etcd/wal/wal_windows.go generated vendored Normal file
View File

@ -0,0 +1,41 @@
// Copyright 2016 The etcd Authors
//
// 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.
package wal
import (
"os"
"github.com/coreos/etcd/wal/walpb"
)
func (w *WAL) renameWal(tmpdirpath string) (*WAL, error) {
// rename of directory with locked files doesn't work on
// windows; close the WAL to release the locks so the directory
// can be renamed
w.Close()
if err := os.Rename(tmpdirpath, w.dir); err != nil {
return nil, err
}
// reopen and relock
newWAL, oerr := Open(w.dir, walpb.Snapshot{})
if oerr != nil {
return nil, oerr
}
if _, _, _, err := newWAL.ReadAll(); err != nil {
newWAL.Close()
return nil, err
}
return newWAL, nil
}

View File

@ -20,9 +20,9 @@ import (
proto "github.com/golang/protobuf/proto" proto "github.com/golang/protobuf/proto"
math "math" math "math"
)
import io "io" io "io"
)
// Reference imports to suppress errors if they are not otherwise used. // Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal var _ = proto.Marshal

View File

@ -22,43 +22,43 @@ import (
) )
type Config struct { type Config struct {
//the endpoint to hit to scrape metrics // the endpoint to hit to scrape metrics
Endpoint EndpointConfig `json:"endpoint"` Endpoint EndpointConfig `json:"endpoint"`
//holds information about different metrics that can be collected // holds information about different metrics that can be collected
MetricsConfig []MetricConfig `json:"metrics_config"` MetricsConfig []MetricConfig `json:"metrics_config"`
} }
// metricConfig holds information extracted from the config file about a metric // metricConfig holds information extracted from the config file about a metric
type MetricConfig struct { type MetricConfig struct {
//the name of the metric // the name of the metric
Name string `json:"name"` Name string `json:"name"`
//enum type for the metric type // enum type for the metric type
MetricType v1.MetricType `json:"metric_type"` MetricType v1.MetricType `json:"metric_type"`
// metric units to display on UI and in storage (eg: MB, cores) // metric units to display on UI and in storage (eg: MB, cores)
// this is only used for display. // this is only used for display.
Units string `json:"units"` Units string `json:"units"`
//data type of the metric (eg: int, float) // data type of the metric (eg: int, float)
DataType v1.DataType `json:"data_type"` DataType v1.DataType `json:"data_type"`
//the frequency at which the metric should be collected // the frequency at which the metric should be collected
PollingFrequency time.Duration `json:"polling_frequency"` PollingFrequency time.Duration `json:"polling_frequency"`
//the regular expression that can be used to extract the metric // the regular expression that can be used to extract the metric
Regex string `json:"regex"` Regex string `json:"regex"`
} }
type Prometheus struct { type Prometheus struct {
//the endpoint to hit to scrape metrics // the endpoint to hit to scrape metrics
Endpoint EndpointConfig `json:"endpoint"` Endpoint EndpointConfig `json:"endpoint"`
//the frequency at which metrics should be collected // the frequency at which metrics should be collected
PollingFrequency time.Duration `json:"polling_frequency"` PollingFrequency time.Duration `json:"polling_frequency"`
//holds names of different metrics that can be collected // holds names of different metrics that can be collected
MetricsConfig []string `json:"metrics_config"` MetricsConfig []string `json:"metrics_config"`
} }

View File

@ -29,13 +29,13 @@ import (
) )
type GenericCollector struct { type GenericCollector struct {
//name of the collector // name of the collector
name string name string
//holds information extracted from the config file for a collector // holds information extracted from the config file for a collector
configFile Config configFile Config
//holds information necessary to extract metrics // holds information necessary to extract metrics
info *collectorInfo info *collectorInfo
// The Http client to use when connecting to metric endpoints // The Http client to use when connecting to metric endpoints
@ -43,10 +43,10 @@ type GenericCollector struct {
} }
type collectorInfo struct { type collectorInfo struct {
//minimum polling frequency among all metrics // minimum polling frequency among all metrics
minPollingFrequency time.Duration minPollingFrequency time.Duration
//regular expresssions for all metrics // regular expresssions for all metrics
regexps []*regexp.Regexp regexps []*regexp.Regexp
// Limit for the number of srcaped metrics. If the count is higher, // Limit for the number of srcaped metrics. If the count is higher,
@ -54,7 +54,7 @@ type collectorInfo struct {
metricCountLimit int metricCountLimit int
} }
//Returns a new collector using the information extracted from the configfile // Returns a new collector using the information extracted from the configfile
func NewCollector(collectorName string, configFile []byte, metricCountLimit int, containerHandler container.ContainerHandler, httpClient *http.Client) (*GenericCollector, error) { func NewCollector(collectorName string, configFile []byte, metricCountLimit int, containerHandler container.ContainerHandler, httpClient *http.Client) (*GenericCollector, error) {
var configInJSON Config var configInJSON Config
err := json.Unmarshal(configFile, &configInJSON) err := json.Unmarshal(configFile, &configInJSON)
@ -64,7 +64,7 @@ func NewCollector(collectorName string, configFile []byte, metricCountLimit int,
configInJSON.Endpoint.configure(containerHandler) configInJSON.Endpoint.configure(containerHandler)
//TODO : Add checks for validity of config file (eg : Accurate JSON fields) // TODO : Add checks for validity of config file (eg : Accurate JSON fields)
if len(configInJSON.MetricsConfig) == 0 { if len(configInJSON.MetricsConfig) == 0 {
return nil, fmt.Errorf("No metrics provided in config") return nil, fmt.Errorf("No metrics provided in config")
@ -109,7 +109,7 @@ func NewCollector(collectorName string, configFile []byte, metricCountLimit int,
}, nil }, nil
} }
//Returns name of the collector // Returns name of the collector
func (collector *GenericCollector) Name() string { func (collector *GenericCollector) Name() string {
return collector.name return collector.name
} }
@ -132,7 +132,7 @@ func (collector *GenericCollector) GetSpec() []v1.MetricSpec {
return specs return specs
} }
//Returns collected metrics and the next collection time of the collector // Returns collected metrics and the next collection time of the collector
func (collector *GenericCollector) Collect(metrics map[string][]v1.MetricVal) (time.Time, map[string][]v1.MetricVal, error) { func (collector *GenericCollector) Collect(metrics map[string][]v1.MetricVal) (time.Time, map[string][]v1.MetricVal, error) {
currentTime := time.Now() currentTime := time.Now()
nextCollectionTime := currentTime.Add(time.Duration(collector.info.minPollingFrequency)) nextCollectionTime := currentTime.Add(time.Duration(collector.info.minPollingFrequency))

View File

@ -15,27 +15,30 @@
package collector package collector
import ( import (
"bytes"
"encoding/json" "encoding/json"
"fmt" "fmt"
"io/ioutil" "io"
"math"
"net/http" "net/http"
"strconv" "sort"
"strings"
"time" "time"
rawmodel "github.com/prometheus/client_model/go"
"github.com/prometheus/common/expfmt"
"github.com/prometheus/common/model"
"github.com/google/cadvisor/container" "github.com/google/cadvisor/container"
"github.com/google/cadvisor/info/v1" "github.com/google/cadvisor/info/v1"
) )
type PrometheusCollector struct { type PrometheusCollector struct {
//name of the collector // name of the collector
name string name string
//rate at which metrics are collected // rate at which metrics are collected
pollingFrequency time.Duration pollingFrequency time.Duration
//holds information extracted from the config file for a collector // holds information extracted from the config file for a collector
configFile Prometheus configFile Prometheus
// the metrics to gather (uses a map as a set) // the metrics to gather (uses a map as a set)
@ -49,7 +52,7 @@ type PrometheusCollector struct {
httpClient *http.Client httpClient *http.Client
} }
//Returns a new collector using the information extracted from the configfile // Returns a new collector using the information extracted from the configfile
func NewPrometheusCollector(collectorName string, configFile []byte, metricCountLimit int, containerHandler container.ContainerHandler, httpClient *http.Client) (*PrometheusCollector, error) { func NewPrometheusCollector(collectorName string, configFile []byte, metricCountLimit int, containerHandler container.ContainerHandler, httpClient *http.Client) (*PrometheusCollector, error) {
var configInJSON Prometheus var configInJSON Prometheus
err := json.Unmarshal(configFile, &configInJSON) err := json.Unmarshal(configFile, &configInJSON)
@ -84,7 +87,7 @@ func NewPrometheusCollector(collectorName string, configFile []byte, metricCount
return nil, fmt.Errorf("Too many metrics defined: %d limit %d", len(configInJSON.MetricsConfig), metricCountLimit) return nil, fmt.Errorf("Too many metrics defined: %d limit %d", len(configInJSON.MetricsConfig), metricCountLimit)
} }
//TODO : Add checks for validity of config file (eg : Accurate JSON fields) // TODO : Add checks for validity of config file (eg : Accurate JSON fields)
return &PrometheusCollector{ return &PrometheusCollector{
name: collectorName, name: collectorName,
pollingFrequency: minPollingFrequency, pollingFrequency: minPollingFrequency,
@ -95,68 +98,110 @@ func NewPrometheusCollector(collectorName string, configFile []byte, metricCount
}, nil }, nil
} }
//Returns name of the collector // Returns name of the collector
func (collector *PrometheusCollector) Name() string { func (collector *PrometheusCollector) Name() string {
return collector.name return collector.name
} }
func getMetricData(line string) string {
fields := strings.Fields(line)
data := fields[3]
if len(fields) > 4 {
for i := range fields {
if i > 3 {
data = data + "_" + fields[i]
}
}
}
return strings.TrimSpace(data)
}
func (collector *PrometheusCollector) GetSpec() []v1.MetricSpec { func (collector *PrometheusCollector) GetSpec() []v1.MetricSpec {
specs := []v1.MetricSpec{}
response, err := collector.httpClient.Get(collector.configFile.Endpoint.URL) response, err := collector.httpClient.Get(collector.configFile.Endpoint.URL)
if err != nil { if err != nil {
return specs return nil
} }
defer response.Body.Close() defer response.Body.Close()
pageContent, err := ioutil.ReadAll(response.Body) if response.StatusCode != http.StatusOK {
if err != nil { return nil
return specs
} }
lines := strings.Split(string(pageContent), "\n") dec := expfmt.NewDecoder(response.Body, expfmt.ResponseFormat(response.Header))
lineCount := len(lines)
for i, line := range lines {
if strings.HasPrefix(line, "# HELP") {
if i+2 >= lineCount {
break
}
stopIndex := strings.IndexAny(lines[i+2], "{ ") var specs []v1.MetricSpec
if stopIndex == -1 {
continue
}
name := strings.TrimSpace(lines[i+2][0:stopIndex]) for {
if _, ok := collector.metricsSet[name]; collector.metricsSet != nil && !ok { d := rawmodel.MetricFamily{}
continue if err = dec.Decode(&d); err != nil {
} break
spec := v1.MetricSpec{
Name: name,
Type: v1.MetricType(getMetricData(lines[i+1])),
Format: "float",
Units: getMetricData(lines[i]),
}
specs = append(specs, spec)
} }
name := d.GetName()
if len(name) == 0 {
continue
}
// If metrics to collect is specified, skip any metrics not in the list to collect.
if _, ok := collector.metricsSet[name]; collector.metricsSet != nil && !ok {
continue
}
spec := v1.MetricSpec{
Name: name,
Type: metricType(d.GetType()),
Format: v1.FloatType,
}
specs = append(specs, spec)
} }
if err != nil && err != io.EOF {
return nil
}
return specs return specs
} }
//Returns collected metrics and the next collection time of the collector // metricType converts Prometheus metric type to cadvisor metric type.
// If there is no mapping then just return the name of the Prometheus metric type.
func metricType(t rawmodel.MetricType) v1.MetricType {
switch t {
case rawmodel.MetricType_COUNTER:
return v1.MetricCumulative
case rawmodel.MetricType_GAUGE:
return v1.MetricGauge
default:
return v1.MetricType(t.String())
}
}
type prometheusLabels []*rawmodel.LabelPair
func labelSetToLabelPairs(labels model.Metric) prometheusLabels {
var promLabels prometheusLabels
for k, v := range labels {
name := string(k)
value := string(v)
promLabels = append(promLabels, &rawmodel.LabelPair{Name: &name, Value: &value})
}
return promLabels
}
func (s prometheusLabels) Len() int { return len(s) }
func (s prometheusLabels) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
// ByName implements sort.Interface by providing Less and using the Len and
// Swap methods of the embedded PrometheusLabels value.
type byName struct{ prometheusLabels }
func (s byName) Less(i, j int) bool {
return s.prometheusLabels[i].GetName() < s.prometheusLabels[j].GetName()
}
func prometheusLabelSetToCadvisorLabel(promLabels model.Metric) string {
labels := labelSetToLabelPairs(promLabels)
sort.Sort(byName{labels})
var b bytes.Buffer
for i, l := range labels {
if i > 0 {
b.WriteString("\xff")
}
b.WriteString(l.GetName())
b.WriteString("=")
b.WriteString(l.GetValue())
}
return string(b.Bytes())
}
// Returns collected metrics and the next collection time of the collector
func (collector *PrometheusCollector) Collect(metrics map[string][]v1.MetricVal) (time.Time, map[string][]v1.MetricVal, error) { func (collector *PrometheusCollector) Collect(metrics map[string][]v1.MetricVal) (time.Time, map[string][]v1.MetricVal, error) {
currentTime := time.Now() currentTime := time.Now()
nextCollectionTime := currentTime.Add(time.Duration(collector.pollingFrequency)) nextCollectionTime := currentTime.Add(time.Duration(collector.pollingFrequency))
@ -168,59 +213,61 @@ func (collector *PrometheusCollector) Collect(metrics map[string][]v1.MetricVal)
} }
defer response.Body.Close() defer response.Body.Close()
pageContent, err := ioutil.ReadAll(response.Body) if response.StatusCode != http.StatusOK {
if err != nil { return nextCollectionTime, nil, fmt.Errorf("server returned HTTP status %s", response.Status)
return nextCollectionTime, nil, err
} }
var errorSlice []error sdec := expfmt.SampleDecoder{
lines := strings.Split(string(pageContent), "\n") Dec: expfmt.NewDecoder(response.Body, expfmt.ResponseFormat(response.Header)),
Opts: &expfmt.DecodeOptions{
Timestamp: model.TimeFromUnixNano(currentTime.UnixNano()),
},
}
newMetrics := make(map[string][]v1.MetricVal) var (
// 50 is chosen as a reasonable guesstimate at a number of metrics we can
for _, line := range lines { // expect from virtually any endpoint to try to save allocations.
if line == "" { decSamples = make(model.Vector, 0, 50)
newMetrics = make(map[string][]v1.MetricVal)
)
for {
if err = sdec.Decode(&decSamples); err != nil {
break break
} }
if !strings.HasPrefix(line, "# HELP") && !strings.HasPrefix(line, "# TYPE") {
var metLabel string
startLabelIndex := strings.Index(line, "{")
spaceIndex := strings.Index(line, " ")
if startLabelIndex == -1 {
startLabelIndex = spaceIndex
}
metName := strings.TrimSpace(line[0:startLabelIndex]) for _, sample := range decSamples {
metName := string(sample.Metric[model.MetricNameLabel])
if len(metName) == 0 {
continue
}
// If metrics to collect is specified, skip any metrics not in the list to collect.
if _, ok := collector.metricsSet[metName]; collector.metricsSet != nil && !ok { if _, ok := collector.metricsSet[metName]; collector.metricsSet != nil && !ok {
continue continue
} }
// TODO Handle multiple labels nicer. Prometheus metrics can have multiple
if startLabelIndex+1 <= spaceIndex-1 { // labels, cadvisor only accepts a single string for the metric label.
metLabel = strings.TrimSpace(line[(startLabelIndex + 1):(spaceIndex - 1)]) label := prometheusLabelSetToCadvisorLabel(sample.Metric)
}
metVal, err := strconv.ParseFloat(line[spaceIndex+1:], 64)
if err != nil {
errorSlice = append(errorSlice, err)
}
if math.IsNaN(metVal) {
metVal = 0
}
metric := v1.MetricVal{ metric := v1.MetricVal{
Label: metLabel, FloatValue: float64(sample.Value),
FloatValue: metVal, Timestamp: sample.Timestamp.Time(),
Timestamp: currentTime, Label: label,
} }
newMetrics[metName] = append(newMetrics[metName], metric) newMetrics[metName] = append(newMetrics[metName], metric)
if len(newMetrics) > collector.metricCountLimit { if len(newMetrics) > collector.metricCountLimit {
return nextCollectionTime, nil, fmt.Errorf("too many metrics to collect") return nextCollectionTime, nil, fmt.Errorf("too many metrics to collect")
} }
} }
decSamples = decSamples[:0]
} }
if err != nil && err != io.EOF {
return nextCollectionTime, nil, err
}
for key, val := range newMetrics { for key, val := range newMetrics {
metrics[key] = append(metrics[key], val...) metrics[key] = append(metrics[key], val...)
} }
return nextCollectionTime, metrics, compileErrors(errorSlice) return nextCollectionTime, metrics, nil
} }

View File

@ -16,6 +16,7 @@
package common package common
import ( import (
"fmt"
"sync" "sync"
"time" "time"
@ -26,71 +27,85 @@ import (
type FsHandler interface { type FsHandler interface {
Start() Start()
Usage() (baseUsageBytes uint64, totalUsageBytes uint64) Usage() FsUsage
Stop() Stop()
} }
type FsUsage struct {
BaseUsageBytes uint64
TotalUsageBytes uint64
InodeUsage uint64
}
type realFsHandler struct { type realFsHandler struct {
sync.RWMutex sync.RWMutex
lastUpdate time.Time lastUpdate time.Time
usageBytes uint64 usage FsUsage
baseUsageBytes uint64 period time.Duration
period time.Duration minPeriod time.Duration
minPeriod time.Duration rootfs string
rootfs string extraDir string
extraDir string fsInfo fs.FsInfo
fsInfo fs.FsInfo
// Tells the container to stop. // Tells the container to stop.
stopChan chan struct{} stopChan chan struct{}
} }
const ( const (
longDu = time.Second longOp = time.Second
duTimeout = time.Minute timeout = 2 * time.Minute
maxDuBackoffFactor = 20 maxBackoffFactor = 20
) )
const DefaultPeriod = time.Minute
var _ FsHandler = &realFsHandler{} var _ FsHandler = &realFsHandler{}
func NewFsHandler(period time.Duration, rootfs, extraDir string, fsInfo fs.FsInfo) FsHandler { func NewFsHandler(period time.Duration, rootfs, extraDir string, fsInfo fs.FsInfo) FsHandler {
return &realFsHandler{ return &realFsHandler{
lastUpdate: time.Time{}, lastUpdate: time.Time{},
usageBytes: 0, usage: FsUsage{},
baseUsageBytes: 0, period: period,
period: period, minPeriod: period,
minPeriod: period, rootfs: rootfs,
rootfs: rootfs, extraDir: extraDir,
extraDir: extraDir, fsInfo: fsInfo,
fsInfo: fsInfo, stopChan: make(chan struct{}, 1),
stopChan: make(chan struct{}, 1),
} }
} }
func (fh *realFsHandler) update() error { func (fh *realFsHandler) update() error {
var ( var (
baseUsage, extraDirUsage uint64 baseUsage, extraDirUsage, inodeUsage uint64
err error rootDiskErr, rootInodeErr, extraDiskErr error
) )
// TODO(vishh): Add support for external mounts. // TODO(vishh): Add support for external mounts.
if fh.rootfs != "" { if fh.rootfs != "" {
baseUsage, err = fh.fsInfo.GetDirUsage(fh.rootfs, duTimeout) baseUsage, rootDiskErr = fh.fsInfo.GetDirDiskUsage(fh.rootfs, timeout)
if err != nil { inodeUsage, rootInodeErr = fh.fsInfo.GetDirInodeUsage(fh.rootfs, timeout)
return err
}
} }
if fh.extraDir != "" { if fh.extraDir != "" {
extraDirUsage, err = fh.fsInfo.GetDirUsage(fh.extraDir, duTimeout) extraDirUsage, extraDiskErr = fh.fsInfo.GetDirDiskUsage(fh.extraDir, timeout)
if err != nil {
return err
}
} }
// Wait to handle errors until after all operartions are run.
// An error in one will not cause an early return, skipping others
fh.Lock() fh.Lock()
defer fh.Unlock() defer fh.Unlock()
fh.lastUpdate = time.Now() fh.lastUpdate = time.Now()
fh.usageBytes = baseUsage + extraDirUsage if rootDiskErr == nil && fh.rootfs != "" {
fh.baseUsageBytes = baseUsage fh.usage.InodeUsage = inodeUsage
}
if rootInodeErr == nil && fh.rootfs != "" {
fh.usage.TotalUsageBytes = baseUsage + extraDirUsage
}
if extraDiskErr == nil && fh.extraDir != "" {
fh.usage.BaseUsageBytes = baseUsage
}
// Combine errors into a single error to return
if rootDiskErr != nil || rootInodeErr != nil || extraDiskErr != nil {
return fmt.Errorf("rootDiskErr: %v, rootInodeErr: %v, extraDiskErr: %v", rootDiskErr, rootInodeErr, extraDiskErr)
}
return nil return nil
} }
@ -105,15 +120,15 @@ func (fh *realFsHandler) trackUsage() {
if err := fh.update(); err != nil { if err := fh.update(); err != nil {
glog.Errorf("failed to collect filesystem stats - %v", err) glog.Errorf("failed to collect filesystem stats - %v", err)
fh.period = fh.period * 2 fh.period = fh.period * 2
if fh.period > maxDuBackoffFactor*fh.minPeriod { if fh.period > maxBackoffFactor*fh.minPeriod {
fh.period = maxDuBackoffFactor * fh.minPeriod fh.period = maxBackoffFactor * fh.minPeriod
} }
} else { } else {
fh.period = fh.minPeriod fh.period = fh.minPeriod
} }
duration := time.Since(start) duration := time.Since(start)
if duration > longDu { if duration > longOp {
glog.V(2).Infof("`du` on following dirs took %v: %v", duration, []string{fh.rootfs, fh.extraDir}) glog.V(2).Infof("du and find on following dirs took %v: %v", duration, []string{fh.rootfs, fh.extraDir})
} }
} }
} }
@ -127,8 +142,8 @@ func (fh *realFsHandler) Stop() {
close(fh.stopChan) close(fh.stopChan)
} }
func (fh *realFsHandler) Usage() (baseUsageBytes, totalUsageBytes uint64) { func (fh *realFsHandler) Usage() FsUsage {
fh.RLock() fh.RLock()
defer fh.RUnlock() defer fh.RUnlock()
return fh.baseUsageBytes, fh.usageBytes return fh.usage
} }

View File

@ -243,7 +243,7 @@ func newDockerContainerHandler(
if !ignoreMetrics.Has(container.DiskUsageMetrics) { if !ignoreMetrics.Has(container.DiskUsageMetrics) {
handler.fsHandler = &dockerFsHandler{ handler.fsHandler = &dockerFsHandler{
fsHandler: common.NewFsHandler(time.Minute, rootfsStorageDir, otherStorageDir, fsInfo), fsHandler: common.NewFsHandler(common.DefaultPeriod, rootfsStorageDir, otherStorageDir, fsInfo),
thinPoolWatcher: thinPoolWatcher, thinPoolWatcher: thinPoolWatcher,
deviceID: handler.deviceID, deviceID: handler.deviceID,
} }
@ -283,8 +283,8 @@ func (h *dockerFsHandler) Stop() {
h.fsHandler.Stop() h.fsHandler.Stop()
} }
func (h *dockerFsHandler) Usage() (uint64, uint64) { func (h *dockerFsHandler) Usage() common.FsUsage {
baseUsage, usage := h.fsHandler.Usage() usage := h.fsHandler.Usage()
// When devicemapper is the storage driver, the base usage of the container comes from the thin pool. // When devicemapper is the storage driver, the base usage of the container comes from the thin pool.
// We still need the result of the fsHandler for any extra storage associated with the container. // We still need the result of the fsHandler for any extra storage associated with the container.
@ -299,12 +299,12 @@ func (h *dockerFsHandler) Usage() (uint64, uint64) {
// had at least 1 refresh and we still can't find the device. // had at least 1 refresh and we still can't find the device.
glog.V(5).Infof("unable to get fs usage from thin pool for device %s: %v", h.deviceID, err) glog.V(5).Infof("unable to get fs usage from thin pool for device %s: %v", h.deviceID, err)
} else { } else {
baseUsage = thinPoolUsage usage.BaseUsageBytes = thinPoolUsage
usage += thinPoolUsage usage.TotalUsageBytes += thinPoolUsage
} }
} }
return baseUsage, usage return usage
} }
func (self *dockerContainerHandler) Start() { func (self *dockerContainerHandler) Start() {
@ -387,7 +387,10 @@ func (self *dockerContainerHandler) getFsStats(stats *info.ContainerStats) error
} }
fsStat := info.FsStats{Device: device, Type: fsType, Limit: limit} fsStat := info.FsStats{Device: device, Type: fsType, Limit: limit}
fsStat.BaseUsage, fsStat.Usage = self.fsHandler.Usage() usage := self.fsHandler.Usage()
fsStat.BaseUsage = usage.BaseUsageBytes
fsStat.Usage = usage.TotalUsageBytes
fsStat.Inodes = usage.InodeUsage
stats.Filesystem = append(stats.Filesystem, fsStat) stats.Filesystem = append(stats.Filesystem, fsStat)

View File

@ -45,7 +45,7 @@ type CgroupSubsystems struct {
// Get information about the cgroup subsystems. // Get information about the cgroup subsystems.
func GetCgroupSubsystems() (CgroupSubsystems, error) { func GetCgroupSubsystems() (CgroupSubsystems, error) {
// Get all cgroup mounts. // Get all cgroup mounts.
allCgroups, err := cgroups.GetCgroupMounts() allCgroups, err := cgroups.GetCgroupMounts(true)
if err != nil { if err != nil {
return CgroupSubsystems{}, err return CgroupSubsystems{}, err
} }

View File

@ -18,7 +18,6 @@ package rkt
import ( import (
"fmt" "fmt"
"os" "os"
"time"
rktapi "github.com/coreos/rkt/api/v1alpha" rktapi "github.com/coreos/rkt/api/v1alpha"
"github.com/google/cadvisor/container" "github.com/google/cadvisor/container"
@ -84,7 +83,7 @@ func newRktContainerHandler(name string, rktClient rktapi.PublicAPIClient, rktPa
return nil, fmt.Errorf("this should be impossible!, new handler failing, but factory allowed, name = %s", name) return nil, fmt.Errorf("this should be impossible!, new handler failing, but factory allowed, name = %s", name)
} }
//rktnetes uses containerID: rkt://fff40827-b994-4e3a-8f88-6427c2c8a5ac:nginx // rktnetes uses containerID: rkt://fff40827-b994-4e3a-8f88-6427c2c8a5ac:nginx
if parsed.Container == "" { if parsed.Container == "" {
isPod = true isPod = true
aliases = append(aliases, "rkt://"+parsed.Pod) aliases = append(aliases, "rkt://"+parsed.Pod)
@ -150,7 +149,7 @@ func newRktContainerHandler(name string, rktClient rktapi.PublicAPIClient, rktPa
} }
if !ignoreMetrics.Has(container.DiskUsageMetrics) { if !ignoreMetrics.Has(container.DiskUsageMetrics) {
handler.fsHandler = common.NewFsHandler(time.Minute, rootfsStorageDir, "", fsInfo) handler.fsHandler = common.NewFsHandler(common.DefaultPeriod, rootfsStorageDir, "", fsInfo)
} }
return handler, nil return handler, nil
@ -228,7 +227,10 @@ func (handler *rktContainerHandler) getFsStats(stats *info.ContainerStats) error
fsStat := info.FsStats{Device: deviceInfo.Device, Limit: limit} fsStat := info.FsStats{Device: deviceInfo.Device, Limit: limit}
fsStat.BaseUsage, fsStat.Usage = handler.fsHandler.Usage() usage := handler.fsHandler.Usage()
fsStat.BaseUsage = usage.BaseUsageBytes
fsStat.Usage = usage.TotalUsageBytes
fsStat.Inodes = usage.InodeUsage
stats.Filesystem = append(stats.Filesystem, fsStat) stats.Filesystem = append(stats.Filesystem, fsStat)

View File

@ -19,6 +19,7 @@ package fs
import ( import (
"bufio" "bufio"
"bytes"
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"os" "os"
@ -44,24 +45,24 @@ const (
LabelRktImages = "rkt-images" LabelRktImages = "rkt-images"
) )
// The maximum number of `du` tasks that can be running at once. // The maximum number of `du` and `find` tasks that can be running at once.
const maxConsecutiveDus = 20 const maxConcurrentOps = 20
// A pool for restricting the number of consecutive `du` tasks running. // A pool for restricting the number of consecutive `du` and `find` tasks running.
var duPool = make(chan struct{}, maxConsecutiveDus) var pool = make(chan struct{}, maxConcurrentOps)
func init() { func init() {
for i := 0; i < maxConsecutiveDus; i++ { for i := 0; i < maxConcurrentOps; i++ {
releaseDuToken() releaseToken()
} }
} }
func claimDuToken() { func claimToken() {
<-duPool <-pool
} }
func releaseDuToken() { func releaseToken() {
duPool <- struct{}{} pool <- struct{}{}
} }
type partition struct { type partition struct {
@ -428,12 +429,12 @@ func (self *RealFsInfo) GetDirFsDevice(dir string) (*DeviceInfo, error) {
return nil, fmt.Errorf("could not find device with major: %d, minor: %d in cached partitions map", major, minor) return nil, fmt.Errorf("could not find device with major: %d, minor: %d in cached partitions map", major, minor)
} }
func (self *RealFsInfo) GetDirUsage(dir string, timeout time.Duration) (uint64, error) { func (self *RealFsInfo) GetDirDiskUsage(dir string, timeout time.Duration) (uint64, error) {
if dir == "" { if dir == "" {
return 0, fmt.Errorf("invalid directory") return 0, fmt.Errorf("invalid directory")
} }
claimDuToken() claimToken()
defer releaseDuToken() defer releaseToken()
cmd := exec.Command("nice", "-n", "19", "du", "-s", dir) cmd := exec.Command("nice", "-n", "19", "du", "-s", dir)
stdoutp, err := cmd.StdoutPipe() stdoutp, err := cmd.StdoutPipe()
if err != nil { if err != nil {
@ -447,21 +448,21 @@ func (self *RealFsInfo) GetDirUsage(dir string, timeout time.Duration) (uint64,
if err := cmd.Start(); err != nil { if err := cmd.Start(); err != nil {
return 0, fmt.Errorf("failed to exec du - %v", err) return 0, fmt.Errorf("failed to exec du - %v", err)
} }
stdoutb, souterr := ioutil.ReadAll(stdoutp)
stderrb, _ := ioutil.ReadAll(stderrp)
timer := time.AfterFunc(timeout, func() { timer := time.AfterFunc(timeout, func() {
glog.Infof("killing cmd %v due to timeout(%s)", cmd.Args, timeout.String()) glog.Infof("killing cmd %v due to timeout(%s)", cmd.Args, timeout.String())
cmd.Process.Kill() cmd.Process.Kill()
}) })
stdoutb, souterr := ioutil.ReadAll(stdoutp)
if souterr != nil {
glog.Errorf("failed to read from stdout for cmd %v - %v", cmd.Args, souterr)
}
stderrb, _ := ioutil.ReadAll(stderrp)
err = cmd.Wait() err = cmd.Wait()
timer.Stop() timer.Stop()
if err != nil { if err != nil {
return 0, fmt.Errorf("du command failed on %s with output stdout: %s, stderr: %s - %v", dir, string(stdoutb), string(stderrb), err) return 0, fmt.Errorf("du command failed on %s with output stdout: %s, stderr: %s - %v", dir, string(stdoutb), string(stderrb), err)
} }
stdout := string(stdoutb) stdout := string(stdoutb)
if souterr != nil {
glog.Errorf("failed to read from stdout for cmd %v - %v", cmd.Args, souterr)
}
usageInKb, err := strconv.ParseUint(strings.Fields(stdout)[0], 10, 64) usageInKb, err := strconv.ParseUint(strings.Fields(stdout)[0], 10, 64)
if err != nil { if err != nil {
return 0, fmt.Errorf("cannot parse 'du' output %s - %s", stdout, err) return 0, fmt.Errorf("cannot parse 'du' output %s - %s", stdout, err)
@ -469,6 +470,48 @@ func (self *RealFsInfo) GetDirUsage(dir string, timeout time.Duration) (uint64,
return usageInKb * 1024, nil return usageInKb * 1024, nil
} }
func (self *RealFsInfo) GetDirInodeUsage(dir string, timeout time.Duration) (uint64, error) {
if dir == "" {
return 0, fmt.Errorf("invalid directory")
}
var stdout, stdwcerr, stdfinderr bytes.Buffer
var err error
claimToken()
defer releaseToken()
findCmd := exec.Command("find", dir, "-xdev", "-printf", ".")
wcCmd := exec.Command("wc", "-c")
if wcCmd.Stdin, err = findCmd.StdoutPipe(); err != nil {
return 0, fmt.Errorf("failed to setup stdout for cmd %v - %v", findCmd.Args, err)
}
wcCmd.Stdout, wcCmd.Stderr, findCmd.Stderr = &stdout, &stdwcerr, &stdfinderr
if err = findCmd.Start(); err != nil {
return 0, fmt.Errorf("failed to exec cmd %v - %v; stderr: %v", findCmd.Args, err, stdfinderr.String())
}
if err = wcCmd.Start(); err != nil {
return 0, fmt.Errorf("failed to exec cmd %v - %v; stderr %v", wcCmd.Args, err, stdwcerr.String())
}
timer := time.AfterFunc(timeout, func() {
glog.Infof("killing cmd %v, and cmd %v due to timeout(%s)", findCmd.Args, wcCmd.Args, timeout.String())
wcCmd.Process.Kill()
findCmd.Process.Kill()
})
err = findCmd.Wait()
if err != nil {
return 0, fmt.Errorf("cmd %v failed. stderr: %s; err: %v", findCmd.Args, stdfinderr.String(), err)
}
err = wcCmd.Wait()
if err != nil {
return 0, fmt.Errorf("cmd %v failed. stderr: %s; err: %v", wcCmd.Args, stdwcerr.String(), err)
}
timer.Stop()
inodeUsage, err := strconv.ParseUint(strings.TrimSpace(stdout.String()), 10, 64)
if err != nil {
return 0, fmt.Errorf("cannot parse cmds: %v, %v output %s - %s", findCmd.Args, wcCmd.Args, stdout.String(), err)
}
return inodeUsage, nil
}
func getVfsStats(path string) (total uint64, free uint64, avail uint64, inodes uint64, inodesFree uint64, err error) { func getVfsStats(path string) (total uint64, free uint64, avail uint64, inodes uint64, inodesFree uint64, err error) {
var s syscall.Statfs_t var s syscall.Statfs_t
if err = syscall.Statfs(path, &s); err != nil { if err = syscall.Statfs(path, &s); err != nil {

View File

@ -67,7 +67,10 @@ type FsInfo interface {
GetFsInfoForPath(mountSet map[string]struct{}) ([]Fs, error) GetFsInfoForPath(mountSet map[string]struct{}) ([]Fs, error)
// Returns number of bytes occupied by 'dir'. // Returns number of bytes occupied by 'dir'.
GetDirUsage(dir string, timeout time.Duration) (uint64, error) GetDirDiskUsage(dir string, timeout time.Duration) (uint64, error)
// Returns number of inodes used by 'dir'.
GetDirInodeUsage(dir string, timeout time.Duration) (uint64, error)
// Returns the block device info of the filesystem on which 'dir' resides. // Returns the block device info of the filesystem on which 'dir' resides.
GetDirFsDevice(dir string) (*DeviceInfo, error) GetDirFsDevice(dir string) (*DeviceInfo, error)

View File

@ -389,27 +389,27 @@ type NetworkStats struct {
} }
type TcpStat struct { type TcpStat struct {
//Count of TCP connections in state "Established" // Count of TCP connections in state "Established"
Established uint64 Established uint64
//Count of TCP connections in state "Syn_Sent" // Count of TCP connections in state "Syn_Sent"
SynSent uint64 SynSent uint64
//Count of TCP connections in state "Syn_Recv" // Count of TCP connections in state "Syn_Recv"
SynRecv uint64 SynRecv uint64
//Count of TCP connections in state "Fin_Wait1" // Count of TCP connections in state "Fin_Wait1"
FinWait1 uint64 FinWait1 uint64
//Count of TCP connections in state "Fin_Wait2" // Count of TCP connections in state "Fin_Wait2"
FinWait2 uint64 FinWait2 uint64
//Count of TCP connections in state "Time_Wait // Count of TCP connections in state "Time_Wait
TimeWait uint64 TimeWait uint64
//Count of TCP connections in state "Close" // Count of TCP connections in state "Close"
Close uint64 Close uint64
//Count of TCP connections in state "Close_Wait" // Count of TCP connections in state "Close_Wait"
CloseWait uint64 CloseWait uint64
//Count of TCP connections in state "Listen_Ack" // Count of TCP connections in state "Listen_Ack"
LastAck uint64 LastAck uint64
//Count of TCP connections in state "Listen" // Count of TCP connections in state "Listen"
Listen uint64 Listen uint64
//Count of TCP connections in state "Closing" // Count of TCP connections in state "Closing"
Closing uint64 Closing uint64
} }
@ -511,7 +511,7 @@ type ContainerStats struct {
// Task load stats // Task load stats
TaskStats LoadStats `json:"task_stats,omitempty"` TaskStats LoadStats `json:"task_stats,omitempty"`
//Custom metrics from all collectors // Custom metrics from all collectors
CustomMetrics map[string][]MetricVal `json:"custom_metrics,omitempty"` CustomMetrics map[string][]MetricVal `json:"custom_metrics,omitempty"`
} }

View File

@ -26,10 +26,10 @@ const (
MetricGauge MetricType = "gauge" MetricGauge MetricType = "gauge"
// A counter-like value that is only expected to increase. // A counter-like value that is only expected to increase.
MetricCumulative = "cumulative" MetricCumulative MetricType = "cumulative"
// Rate over a time period. // Rate over a time period.
MetricDelta = "delta" MetricDelta MetricType = "delta"
) )
// DataType for metric being exported. // DataType for metric being exported.
@ -37,7 +37,7 @@ type DataType string
const ( const (
IntType DataType = "int" IntType DataType = "int"
FloatType = "float" FloatType DataType = "float"
) )
// Spec for custom metric. // Spec for custom metric.

View File

@ -301,4 +301,8 @@ type FilesystemStats struct {
TotalUsageBytes *uint64 `json:"totalUsageBytes,omitempty"` TotalUsageBytes *uint64 `json:"totalUsageBytes,omitempty"`
// Number of bytes consumed by a container through its root filesystem. // Number of bytes consumed by a container through its root filesystem.
BaseUsageBytes *uint64 `json:"baseUsageBytes,omitempty"` BaseUsageBytes *uint64 `json:"baseUsageBytes,omitempty"`
// Number of inodes used within the container's root filesystem.
// This only accounts for inodes that are shared across containers,
// and does not include inodes used in mounted directories.
InodeUsage *uint64 `json:"containter_inode_usage,omitempty"`
} }

View File

@ -127,6 +127,7 @@ func ContainerStatsFromV1(spec *v1.ContainerSpec, stats []*v1.ContainerStats) []
stat.Filesystem = &FilesystemStats{ stat.Filesystem = &FilesystemStats{
TotalUsageBytes: &val.Filesystem[0].Usage, TotalUsageBytes: &val.Filesystem[0].Usage,
BaseUsageBytes: &val.Filesystem[0].BaseUsage, BaseUsageBytes: &val.Filesystem[0].BaseUsage,
InodeUsage: &val.Filesystem[0].Inodes,
} }
} else if len(val.Filesystem) > 1 { } else if len(val.Filesystem) > 1 {
// Cannot handle multiple devices per container. // Cannot handle multiple devices per container.

View File

@ -101,6 +101,9 @@ type Manager interface {
// Get version information about different components we depend on. // Get version information about different components we depend on.
GetVersionInfo() (*info.VersionInfo, error) GetVersionInfo() (*info.VersionInfo, error)
// Get filesystem information for the filesystem that contains the given directory
GetDirFsInfo(dir string) (v2.FsInfo, error)
// Get filesystem information for a given label. // Get filesystem information for a given label.
// Returns information for all global filesystems if label is empty. // Returns information for all global filesystems if label is empty.
GetFsInfo(label string) ([]v2.FsInfo, error) GetFsInfo(label string) ([]v2.FsInfo, error)
@ -657,6 +660,27 @@ func (self *manager) getRequestedContainers(containerName string, options v2.Req
return containersMap, nil return containersMap, nil
} }
func (self *manager) GetDirFsInfo(dir string) (v2.FsInfo, error) {
dirDevice, err := self.fsInfo.GetDirFsDevice(dir)
if err != nil {
return v2.FsInfo{}, fmt.Errorf("error trying to get filesystem Device for dir %v: err: %v", dir, err)
}
dirMountpoint, err := self.fsInfo.GetMountpointForDevice(dirDevice.Device)
if err != nil {
return v2.FsInfo{}, fmt.Errorf("error trying to get MountPoint for Root Device: %v, err: %v", dirDevice, err)
}
infos, err := self.GetFsInfo("")
if err != nil {
return v2.FsInfo{}, err
}
for _, info := range infos {
if info.Mountpoint == dirMountpoint {
return info, nil
}
}
return v2.FsInfo{}, fmt.Errorf("did not find fs info for dir: %v", dir)
}
func (self *manager) GetFsInfo(label string) ([]v2.FsInfo, error) { func (self *manager) GetFsInfo(label string) ([]v2.FsInfo, error) {
var empty time.Time var empty time.Time
// Get latest data from filesystems hanging off root container. // Get latest data from filesystems hanging off root container.

View File

@ -228,6 +228,26 @@ func NewPrometheusCollector(i infoProvider, f ContainerLabelsFunc) *PrometheusCo
}, },
} }
}, },
}, {
name: "container_fs_inodes_free",
help: "Number of available Inodes",
valueType: prometheus.GaugeValue,
extraLabels: []string{"device"},
getValues: func(s *info.ContainerStats) metricValues {
return fsValues(s.Filesystem, func(fs *info.FsStats) float64 {
return float64(fs.InodesFree)
})
},
}, {
name: "container_fs_inodes_total",
help: "Number of Inodes",
valueType: prometheus.GaugeValue,
extraLabels: []string{"device"},
getValues: func(s *info.ContainerStats) metricValues {
return fsValues(s.Filesystem, func(fs *info.FsStats) float64 {
return float64(fs.Inodes)
})
},
}, { }, {
name: "container_fs_limit_bytes", name: "container_fs_limit_bytes",
help: "Number of bytes that can be consumed by the container on this filesystem.", help: "Number of bytes that can be consumed by the container on this filesystem.",

File diff suppressed because one or more lines are too long

View File

@ -98,7 +98,7 @@ func pagesAssetsHtmlContainersHtml() (*asset, error) {
return nil, err return nil, err
} }
info := bindataFileInfo{name: "pages/assets/html/containers.html", size: 9533, mode: os.FileMode(416), modTime: time.Unix(1462817463, 0)} info := bindataFileInfo{name: "pages/assets/html/containers.html", size: 0, mode: os.FileMode(0), modTime: time.Unix(0, 0)}
a := &asset{bytes: bytes, info: info} a := &asset{bytes: bytes, info: info}
return a, nil return a, nil
} }

View File

@ -34,7 +34,7 @@ func onAzure() bool {
return strings.Contains(string(data), MicrosoftCorporation) return strings.Contains(string(data), MicrosoftCorporation)
} }
//TODO: Implement method. // TODO: Implement method.
func getAzureInstanceType() info.InstanceType { func getAzureInstanceType() info.InstanceType {
return info.UnknownInstance return info.UnknownInstance
} }

View File

@ -97,7 +97,7 @@ func detectInstanceID(cloudProvider info.CloudProvider) info.InstanceID {
return info.UnNamedInstance return info.UnNamedInstance
} }
//TODO: Implement method. // TODO: Implement method.
func onBaremetal() bool { func onBaremetal() bool {
return false return false
} }

View File

@ -20,8 +20,8 @@ import (
info "github.com/google/cadvisor/info/v1" info "github.com/google/cadvisor/info/v1"
"cloud.google.com/go/compute/metadata"
"github.com/golang/glog" "github.com/golang/glog"
"google.golang.org/cloud/compute/metadata"
) )
const ( const (

View File

@ -88,7 +88,7 @@ func (t *Tail) attemptOpen() error {
t.file, err = os.Open(t.filename) t.file, err = os.Open(t.filename)
if err == nil { if err == nil {
// TODO: not interested in old events? // TODO: not interested in old events?
//t.file.Seek(0, os.SEEK_END) // t.file.Seek(0, os.SEEK_END)
t.reader = bufio.NewReader(t.file) t.reader = bufio.NewReader(t.file)
return nil return nil
} }

View File

@ -71,7 +71,6 @@ that are required for executing a container's process.
| /dev/tty | 0666 | rwm | | /dev/tty | 0666 | rwm |
| /dev/random | 0666 | rwm | | /dev/random | 0666 | rwm |
| /dev/urandom | 0666 | rwm | | /dev/urandom | 0666 | rwm |
| /dev/fuse | 0666 | rwm |
**ptmx** **ptmx**

View File

@ -37,7 +37,7 @@ type Manager interface {
// restore the object later. // restore the object later.
GetPaths() map[string]string GetPaths() map[string]string
// Set the cgroup as configured. // Sets the cgroup as configured.
Set(container *configs.Config) error Set(container *configs.Config) error
} }

View File

@ -104,6 +104,8 @@ func (m *Manager) Apply(pid int) (err error) {
if m.Cgroups == nil { if m.Cgroups == nil {
return nil return nil
} }
m.mu.Lock()
defer m.mu.Unlock()
var c = m.Cgroups var c = m.Cgroups
@ -128,8 +130,6 @@ func (m *Manager) Apply(pid int) (err error) {
return cgroups.EnterPid(m.Paths, pid) return cgroups.EnterPid(m.Paths, pid)
} }
m.mu.Lock()
defer m.mu.Unlock()
paths := make(map[string]string) paths := make(map[string]string)
for _, sys := range subsystems { for _, sys := range subsystems {
if err := sys.Apply(d); err != nil { if err := sys.Apply(d); err != nil {
@ -195,18 +195,10 @@ func (m *Manager) Set(container *configs.Config) error {
if m.Cgroups.Paths != nil { if m.Cgroups.Paths != nil {
return nil return nil
} }
for _, sys := range subsystems {
// Generate fake cgroup data.
d, err := getCgroupData(container.Cgroups, -1)
if err != nil {
return err
}
// Get the path, but don't error out if the cgroup wasn't found.
path, err := d.path(sys.Name())
if err != nil && !cgroups.IsNotFound(err) {
return err
}
paths := m.GetPaths()
for _, sys := range subsystems {
path := paths[sys.Name()]
if err := sys.Set(path, container.Cgroups); err != nil { if err := sys.Set(path, container.Cgroups); err != nil {
return err return err
} }
@ -223,14 +215,8 @@ func (m *Manager) Set(container *configs.Config) error {
// Freeze toggles the container's freezer cgroup depending on the state // Freeze toggles the container's freezer cgroup depending on the state
// provided // provided
func (m *Manager) Freeze(state configs.FreezerState) error { func (m *Manager) Freeze(state configs.FreezerState) error {
d, err := getCgroupData(m.Cgroups, 0) paths := m.GetPaths()
if err != nil { dir := paths["freezer"]
return err
}
dir, err := d.path("freezer")
if err != nil {
return err
}
prevState := m.Cgroups.Resources.Freezer prevState := m.Cgroups.Resources.Freezer
m.Cgroups.Resources.Freezer = state m.Cgroups.Resources.Freezer = state
freezer, err := subsystems.Get("freezer") freezer, err := subsystems.Get("freezer")
@ -246,28 +232,13 @@ func (m *Manager) Freeze(state configs.FreezerState) error {
} }
func (m *Manager) GetPids() ([]int, error) { func (m *Manager) GetPids() ([]int, error) {
dir, err := getCgroupPath(m.Cgroups) paths := m.GetPaths()
if err != nil { return cgroups.GetPids(paths["devices"])
return nil, err
}
return cgroups.GetPids(dir)
} }
func (m *Manager) GetAllPids() ([]int, error) { func (m *Manager) GetAllPids() ([]int, error) {
dir, err := getCgroupPath(m.Cgroups) paths := m.GetPaths()
if err != nil { return cgroups.GetAllPids(paths["devices"])
return nil, err
}
return cgroups.GetAllPids(dir)
}
func getCgroupPath(c *configs.Cgroup) (string, error) {
d, err := getCgroupData(c, 0)
if err != nil {
return "", err
}
return d.path("devices")
} }
func getCgroupData(c *configs.Cgroup, pid int) (*cgroupData, error) { func getCgroupData(c *configs.Cgroup, pid int) (*cgroupData, error) {

View File

@ -22,10 +22,48 @@ func (s *CpuGroup) Name() string {
func (s *CpuGroup) Apply(d *cgroupData) error { func (s *CpuGroup) Apply(d *cgroupData) error {
// We always want to join the cpu group, to allow fair cpu scheduling // We always want to join the cpu group, to allow fair cpu scheduling
// on a container basis // on a container basis
_, err := d.join("cpu") path, err := d.path("cpu")
if err != nil && !cgroups.IsNotFound(err) { if err != nil && !cgroups.IsNotFound(err) {
return err return err
} }
return s.ApplyDir(path, d.config, d.pid)
}
func (s *CpuGroup) ApplyDir(path string, cgroup *configs.Cgroup, pid int) error {
// This might happen if we have no cpu cgroup mounted.
// Just do nothing and don't fail.
if path == "" {
return nil
}
if err := os.MkdirAll(path, 0755); err != nil {
return err
}
// We should set the real-Time group scheduling settings before moving
// in the process because if the process is already in SCHED_RR mode
// and no RT bandwidth is set, adding it will fail.
if err := s.SetRtSched(path, cgroup); err != nil {
return err
}
// because we are not using d.join we need to place the pid into the procs file
// unlike the other subsystems
if err := cgroups.WriteCgroupProc(path, pid); err != nil {
return err
}
return nil
}
func (s *CpuGroup) SetRtSched(path string, cgroup *configs.Cgroup) error {
if cgroup.Resources.CpuRtPeriod != 0 {
if err := writeFile(path, "cpu.rt_period_us", strconv.FormatInt(cgroup.Resources.CpuRtPeriod, 10)); err != nil {
return err
}
}
if cgroup.Resources.CpuRtRuntime != 0 {
if err := writeFile(path, "cpu.rt_runtime_us", strconv.FormatInt(cgroup.Resources.CpuRtRuntime, 10)); err != nil {
return err
}
}
return nil return nil
} }
@ -45,15 +83,8 @@ func (s *CpuGroup) Set(path string, cgroup *configs.Cgroup) error {
return err return err
} }
} }
if cgroup.Resources.CpuRtPeriod != 0 { if err := s.SetRtSched(path, cgroup); err != nil {
if err := writeFile(path, "cpu.rt_period_us", strconv.FormatInt(cgroup.Resources.CpuRtPeriod, 10)); err != nil { return err
return err
}
}
if cgroup.Resources.CpuRtRuntime != 0 {
if err := writeFile(path, "cpu.rt_runtime_us", strconv.FormatInt(cgroup.Resources.CpuRtRuntime, 10)); err != nil {
return err
}
} }
return nil return nil

View File

@ -139,7 +139,7 @@ func (m Mount) GetThisCgroupDir(cgroups map[string]string) (string, error) {
return getControllerPath(m.Subsystems[0], cgroups) return getControllerPath(m.Subsystems[0], cgroups)
} }
func getCgroupMountsHelper(ss map[string]bool, mi io.Reader) ([]Mount, error) { func getCgroupMountsHelper(ss map[string]bool, mi io.Reader, all bool) ([]Mount, error) {
res := make([]Mount, 0, len(ss)) res := make([]Mount, 0, len(ss))
scanner := bufio.NewScanner(mi) scanner := bufio.NewScanner(mi)
numFound := 0 numFound := 0
@ -166,7 +166,9 @@ func getCgroupMountsHelper(ss map[string]bool, mi io.Reader) ([]Mount, error) {
} else { } else {
m.Subsystems = append(m.Subsystems, opt) m.Subsystems = append(m.Subsystems, opt)
} }
numFound++ if !all {
numFound++
}
} }
res = append(res, m) res = append(res, m)
} }
@ -176,23 +178,25 @@ func getCgroupMountsHelper(ss map[string]bool, mi io.Reader) ([]Mount, error) {
return res, nil return res, nil
} }
func GetCgroupMounts() ([]Mount, error) { // GetCgroupMounts returns the mounts for the cgroup subsystems.
// all indicates whether to return just the first instance or all the mounts.
func GetCgroupMounts(all bool) ([]Mount, error) {
f, err := os.Open("/proc/self/mountinfo") f, err := os.Open("/proc/self/mountinfo")
if err != nil { if err != nil {
return nil, err return nil, err
} }
defer f.Close() defer f.Close()
all, err := ParseCgroupFile("/proc/self/cgroup") allSubsystems, err := ParseCgroupFile("/proc/self/cgroup")
if err != nil { if err != nil {
return nil, err return nil, err
} }
allMap := make(map[string]bool) allMap := make(map[string]bool)
for s := range all { for s := range allSubsystems {
allMap[s] = true allMap[s] = true
} }
return getCgroupMountsHelper(allMap, f) return getCgroupMountsHelper(allMap, f, all)
} }
// GetAllSubsystems returns all the cgroup subsystems supported by the kernel // GetAllSubsystems returns all the cgroup subsystems supported by the kernel

View File

@ -120,5 +120,5 @@ type Resources struct {
NetPrioIfpriomap []*IfPrioMap `json:"net_prio_ifpriomap"` NetPrioIfpriomap []*IfPrioMap `json:"net_prio_ifpriomap"`
// Set class identifier for container's network packets // Set class identifier for container's network packets
NetClsClassid uint32 `json:"net_cls_classid"` NetClsClassid uint32 `json:"net_cls_classid_u"`
} }

View File

@ -300,29 +300,38 @@ func (c Command) Run(s HookState) error {
if err != nil { if err != nil {
return err return err
} }
var stdout, stderr bytes.Buffer
cmd := exec.Cmd{ cmd := exec.Cmd{
Path: c.Path, Path: c.Path,
Args: c.Args, Args: c.Args,
Env: c.Env, Env: c.Env,
Stdin: bytes.NewReader(b), Stdin: bytes.NewReader(b),
Stdout: &stdout,
Stderr: &stderr,
}
if err := cmd.Start(); err != nil {
return err
} }
errC := make(chan error, 1) errC := make(chan error, 1)
go func() { go func() {
out, err := cmd.CombinedOutput() err := cmd.Wait()
if err != nil { if err != nil {
err = fmt.Errorf("%s: %s", err, out) err = fmt.Errorf("error running hook: %v, stdout: %s, stderr: %s", err, stdout.String(), stderr.String())
} }
errC <- err errC <- err
}() }()
var timerCh <-chan time.Time
if c.Timeout != nil { if c.Timeout != nil {
select { timer := time.NewTimer(*c.Timeout)
case err := <-errC: defer timer.Stop()
return err timerCh = timer.C
case <-time.After(*c.Timeout): }
cmd.Process.Kill() select {
cmd.Wait() case err := <-errC:
return fmt.Errorf("hook ran past specified timeout of %.1fs", c.Timeout.Seconds()) return err
} case <-timerCh:
cmd.Process.Kill()
cmd.Wait()
return fmt.Errorf("hook ran past specified timeout of %.1fs", c.Timeout.Seconds())
} }
return <-errC
} }

View File

@ -107,19 +107,5 @@ var (
Permissions: "rwm", Permissions: "rwm",
}, },
}, DefaultSimpleDevices...) }, DefaultSimpleDevices...)
DefaultAutoCreatedDevices = append([]*Device{ DefaultAutoCreatedDevices = append([]*Device{}, DefaultSimpleDevices...)
{
// /dev/fuse is created but not allowed.
// This is to allow java to work. Because java
// Insists on there being a /dev/fuse
// https://github.com/docker/docker/issues/514
// https://github.com/docker/docker/issues/2393
//
Path: "/dev/fuse",
Type: 'c',
Major: 10,
Minor: 229,
Permissions: "rwm",
},
}, DefaultSimpleDevices...)
) )

View File

@ -75,8 +75,8 @@ type BaseContainer interface {
// Returns the current status of the container. // Returns the current status of the container.
// //
// errors: // errors:
// ContainerDestroyed - Container no longer exists, // ContainerNotExists - Container no longer exists,
// SystemError - System error. // Systemerror - System error.
Status() (Status, error) Status() (Status, error)
// State returns the current container's state information. // State returns the current container's state information.
@ -91,8 +91,8 @@ type BaseContainer interface {
// Returns the PIDs inside this container. The PIDs are in the namespace of the calling process. // Returns the PIDs inside this container. The PIDs are in the namespace of the calling process.
// //
// errors: // errors:
// ContainerDestroyed - Container no longer exists, // ContainerNotExists - Container no longer exists,
// SystemError - System error. // Systemerror - System error.
// //
// Some of the returned PIDs may no longer refer to processes in the Container, unless // Some of the returned PIDs may no longer refer to processes in the Container, unless
// the Container state is PAUSED in which case every PID in the slice is valid. // the Container state is PAUSED in which case every PID in the slice is valid.
@ -101,8 +101,8 @@ type BaseContainer interface {
// Returns statistics for the container. // Returns statistics for the container.
// //
// errors: // errors:
// ContainerDestroyed - Container no longer exists, // ContainerNotExists - Container no longer exists,
// SystemError - System error. // Systemerror - System error.
Stats() (*Stats, error) Stats() (*Stats, error)
// Set resources of container as configured // Set resources of container as configured
@ -117,7 +117,7 @@ type BaseContainer interface {
// start. You can track process lifecycle with passed Process structure. // start. You can track process lifecycle with passed Process structure.
// //
// errors: // errors:
// ContainerDestroyed - Container no longer exists, // ContainerNotExists - Container no longer exists,
// ConfigInvalid - config is invalid, // ConfigInvalid - config is invalid,
// ContainerPaused - Container is paused, // ContainerPaused - Container is paused,
// SystemError - System error. // SystemError - System error.
@ -128,7 +128,7 @@ type BaseContainer interface {
// opens the fifo after start returns. // opens the fifo after start returns.
// //
// errors: // errors:
// ContainerDestroyed - Container no longer exists, // ContainerNotExists - Container no longer exists,
// ConfigInvalid - config is invalid, // ConfigInvalid - config is invalid,
// ContainerPaused - Container is paused, // ContainerPaused - Container is paused,
// SystemError - System error. // SystemError - System error.

View File

@ -35,7 +35,6 @@ type linuxContainer struct {
root string root string
config *configs.Config config *configs.Config
cgroupManager cgroups.Manager cgroupManager cgroups.Manager
initPath string
initArgs []string initArgs []string
initProcess parentProcess initProcess parentProcess
initProcessStartTime string initProcessStartTime string
@ -86,13 +85,14 @@ type Container interface {
// Systemerror - System error. // Systemerror - System error.
Restore(process *Process, criuOpts *CriuOpts) error Restore(process *Process, criuOpts *CriuOpts) error
// If the Container state is RUNNING, sets the Container state to PAUSING and pauses // If the Container state is RUNNING or CREATED, sets the Container state to PAUSING and pauses
// the execution of any user processes. Asynchronously, when the container finished being paused the // the execution of any user processes. Asynchronously, when the container finished being paused the
// state is changed to PAUSED. // state is changed to PAUSED.
// If the Container state is PAUSED, do nothing. // If the Container state is PAUSED, do nothing.
// //
// errors: // errors:
// ContainerDestroyed - Container no longer exists, // ContainerNotExists - Container no longer exists,
// ContainerNotRunning - Container not running or created,
// Systemerror - System error. // Systemerror - System error.
Pause() error Pause() error
@ -101,7 +101,8 @@ type Container interface {
// If the Container state is RUNNING, do nothing. // If the Container state is RUNNING, do nothing.
// //
// errors: // errors:
// ContainerDestroyed - Container no longer exists, // ContainerNotExists - Container no longer exists,
// ContainerNotPaused - Container is not paused,
// Systemerror - System error. // Systemerror - System error.
Resume() error Resume() error
@ -308,10 +309,7 @@ func (c *linuxContainer) newParentProcess(p *Process, doInit bool) (parentProces
} }
func (c *linuxContainer) commandTemplate(p *Process, childPipe, rootDir *os.File) (*exec.Cmd, error) { func (c *linuxContainer) commandTemplate(p *Process, childPipe, rootDir *os.File) (*exec.Cmd, error) {
cmd := &exec.Cmd{ cmd := exec.Command(c.initArgs[0], c.initArgs[1:]...)
Path: c.initPath,
Args: c.initArgs,
}
cmd.Stdin = p.Stdin cmd.Stdin = p.Stdin
cmd.Stdout = p.Stdout cmd.Stdout = p.Stdout
cmd.Stderr = p.Stderr cmd.Stderr = p.Stderr
@ -447,7 +445,7 @@ func (c *linuxContainer) Pause() error {
c: c, c: c,
}) })
} }
return newGenericError(fmt.Errorf("container not running: %s", status), ContainerNotRunning) return newGenericError(fmt.Errorf("container not running or created: %s", status), ContainerNotRunning)
} }
func (c *linuxContainer) Resume() error { func (c *linuxContainer) Resume() error {
@ -1049,6 +1047,8 @@ func (c *linuxContainer) criuNotifications(resp *criurpc.CriuResp, process *Proc
}); err != nil { }); err != nil {
return err return err
} }
// create a timestamp indicating when the restored checkpoint was started
c.created = time.Now().UTC()
if _, err := c.updateState(r); err != nil { if _, err := c.updateState(r); err != nil {
return err return err
} }

View File

@ -6,7 +6,6 @@ import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"os" "os"
"os/exec"
"path/filepath" "path/filepath"
"regexp" "regexp"
"runtime/debug" "runtime/debug"
@ -33,32 +32,9 @@ var (
) )
// InitArgs returns an options func to configure a LinuxFactory with the // InitArgs returns an options func to configure a LinuxFactory with the
// provided init arguments. // provided init binary path and arguments.
func InitArgs(args ...string) func(*LinuxFactory) error { func InitArgs(args ...string) func(*LinuxFactory) error {
return func(l *LinuxFactory) error { return func(l *LinuxFactory) error {
name := args[0]
if filepath.Base(name) == name {
if lp, err := exec.LookPath(name); err == nil {
name = lp
}
} else {
abs, err := filepath.Abs(name)
if err != nil {
return err
}
name = abs
}
l.InitPath = "/proc/self/exe"
l.InitArgs = append([]string{name}, args[1:]...)
return nil
}
}
// InitPath returns an options func to configure a LinuxFactory with the
// provided absolute path to the init binary and arguements.
func InitPath(path string, args ...string) func(*LinuxFactory) error {
return func(l *LinuxFactory) error {
l.InitPath = path
l.InitArgs = args l.InitArgs = args
return nil return nil
} }
@ -122,10 +98,10 @@ func New(root string, options ...func(*LinuxFactory) error) (Factory, error) {
} }
l := &LinuxFactory{ l := &LinuxFactory{
Root: root, Root: root,
InitArgs: []string{"/proc/self/exe", "init"},
Validator: validate.New(), Validator: validate.New(),
CriuPath: "criu", CriuPath: "criu",
} }
InitArgs(os.Args[0], "init")(l)
Cgroupfs(l) Cgroupfs(l)
for _, opt := range options { for _, opt := range options {
if err := opt(l); err != nil { if err := opt(l); err != nil {
@ -140,9 +116,6 @@ type LinuxFactory struct {
// Root directory for the factory to store state. // Root directory for the factory to store state.
Root string Root string
// InitPath is the absolute path to the init binary.
InitPath string
// InitArgs are arguments for calling the init responsibilities for spawning // InitArgs are arguments for calling the init responsibilities for spawning
// a container. // a container.
InitArgs []string InitArgs []string
@ -202,7 +175,6 @@ func (l *LinuxFactory) Create(id string, config *configs.Config) (Container, err
id: id, id: id,
root: containerRoot, root: containerRoot,
config: config, config: config,
initPath: l.InitPath,
initArgs: l.InitArgs, initArgs: l.InitArgs,
criuPath: l.CriuPath, criuPath: l.CriuPath,
cgroupManager: l.NewCgroupsManager(config.Cgroups, nil), cgroupManager: l.NewCgroupsManager(config.Cgroups, nil),
@ -216,7 +188,7 @@ func (l *LinuxFactory) Load(id string) (Container, error) {
return nil, newGenericError(fmt.Errorf("invalid root"), ConfigInvalid) return nil, newGenericError(fmt.Errorf("invalid root"), ConfigInvalid)
} }
containerRoot := filepath.Join(l.Root, id) containerRoot := filepath.Join(l.Root, id)
state, err := l.loadState(containerRoot) state, err := l.loadState(containerRoot, id)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -230,7 +202,6 @@ func (l *LinuxFactory) Load(id string) (Container, error) {
initProcessStartTime: state.InitProcessStartTime, initProcessStartTime: state.InitProcessStartTime,
id: id, id: id,
config: &state.Config, config: &state.Config,
initPath: l.InitPath,
initArgs: l.InitArgs, initArgs: l.InitArgs,
criuPath: l.CriuPath, criuPath: l.CriuPath,
cgroupManager: l.NewCgroupsManager(state.Config.Cgroups, state.CgroupPaths), cgroupManager: l.NewCgroupsManager(state.Config.Cgroups, state.CgroupPaths),
@ -302,11 +273,11 @@ func (l *LinuxFactory) StartInitialization() (err error) {
return i.Init() return i.Init()
} }
func (l *LinuxFactory) loadState(root string) (*State, error) { func (l *LinuxFactory) loadState(root, id string) (*State, error) {
f, err := os.Open(filepath.Join(root, stateFilename)) f, err := os.Open(filepath.Join(root, stateFilename))
if err != nil { if err != nil {
if os.IsNotExist(err) { if os.IsNotExist(err) {
return nil, newGenericError(err, ContainerNotExists) return nil, newGenericError(fmt.Errorf("container %q does not exists", id), ContainerNotExists)
} }
return nil, newGenericError(err, SystemError) return nil, newGenericError(err, SystemError)
} }

View File

@ -67,9 +67,6 @@ func newSystemErrorWithCause(err error, cause string) Error {
// stack frames skipped. This is only to be called by the other functions for // stack frames skipped. This is only to be called by the other functions for
// formatting the error. // formatting the error.
func createSystemError(err error, cause string) Error { func createSystemError(err error, cause string) Error {
if le, ok := err.(Error); ok {
return le
}
gerr := &genericError{ gerr := &genericError{
Timestamp: time.Now(), Timestamp: time.Now(),
Err: err, Err: err,

View File

@ -144,7 +144,7 @@ func finalizeNamespace(config *initConfig) error {
} }
if config.Cwd != "" { if config.Cwd != "" {
if err := syscall.Chdir(config.Cwd); err != nil { if err := syscall.Chdir(config.Cwd); err != nil {
return err return fmt.Errorf("chdir to cwd (%q) set in config.json failed: %v", config.Cwd, err)
} }
} }
return nil return nil

View File

@ -129,7 +129,7 @@ func Relabel(path string, fileLabel string, shared bool) error {
exclude_paths := map[string]bool{"/": true, "/usr": true, "/etc": true} exclude_paths := map[string]bool{"/": true, "/usr": true, "/etc": true}
if exclude_paths[path] { if exclude_paths[path] {
return fmt.Errorf("Relabeling of %s is not allowed", path) return fmt.Errorf("SELinux relabeling of %s is not allowed", path)
} }
if shared { if shared {
@ -137,7 +137,10 @@ func Relabel(path string, fileLabel string, shared bool) error {
c["level"] = "s0" c["level"] = "s0"
fileLabel = c.Get() fileLabel = c.Get()
} }
return selinux.Chcon(path, fileLabel, true) if err := selinux.Chcon(path, fileLabel, true); err != nil {
return fmt.Errorf("SELinux relabeling of %s is not allowed: %q", path, err)
}
return nil
} }
// GetPidLabel will return the label of the process running with the specified pid // GetPidLabel will return the label of the process running with the specified pid

View File

@ -32,7 +32,7 @@ type parentProcess interface {
// wait waits on the process returning the process state. // wait waits on the process returning the process state.
wait() (*os.ProcessState, error) wait() (*os.ProcessState, error)
// startTime return's the process start time. // startTime returns the process start time.
startTime() (string, error) startTime() (string, error)
signal(os.Signal) error signal(os.Signal) error
@ -356,7 +356,7 @@ loop:
} }
} }
if !sentRun { if !sentRun {
return newSystemErrorWithCause(ierr, "container init failed") return newSystemErrorWithCause(ierr, "container init")
} }
if p.config.Config.Namespaces.Contains(configs.NEWNS) && !sentResume { if p.config.Config.Namespaces.Contains(configs.NEWNS) && !sentResume {
return newSystemError(fmt.Errorf("could not synchronise after executing prestart hooks with container process")) return newSystemError(fmt.Errorf("could not synchronise after executing prestart hooks with container process"))

View File

@ -50,7 +50,7 @@ func setupRootfs(config *configs.Config, console *linuxConsole, pipe io.ReadWrit
} }
} }
if err := mountToRootfs(m, config.Rootfs, config.MountLabel); err != nil { if err := mountToRootfs(m, config.Rootfs, config.MountLabel); err != nil {
return newSystemErrorWithCausef(err, "mounting %q to rootfs %q", m.Destination, config.Rootfs) return newSystemErrorWithCausef(err, "mounting %q to rootfs %q at %q", m.Source, config.Rootfs, m.Destination)
} }
for _, postcmd := range m.PostmountCmds { for _, postcmd := range m.PostmountCmds {
@ -270,7 +270,7 @@ func mountToRootfs(m *configs.Mount, rootfs, mountLabel string) error {
} }
func getCgroupMounts(m *configs.Mount) ([]*configs.Mount, error) { func getCgroupMounts(m *configs.Mount) ([]*configs.Mount, error) {
mounts, err := cgroups.GetCgroupMounts() mounts, err := cgroups.GetCgroupMounts(false)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -320,6 +320,8 @@ func checkMountDestination(rootfs, dest string) error {
"/proc/diskstats", "/proc/diskstats",
"/proc/meminfo", "/proc/meminfo",
"/proc/stat", "/proc/stat",
"/proc/swaps",
"/proc/uptime",
"/proc/net/dev", "/proc/net/dev",
} }
for _, valid := range validDestinations { for _, valid := range validDestinations {

View File

@ -8,7 +8,7 @@ import (
// Setuid sets the uid of the calling thread to the specified uid. // Setuid sets the uid of the calling thread to the specified uid.
func Setuid(uid int) (err error) { func Setuid(uid int) (err error) {
_, _, e1 := syscall.RawSyscall(syscall.SYS_SETUID, uintptr(uid), 0, 0) _, _, e1 := syscall.RawSyscall(syscall.SYS_SETUID32, uintptr(uid), 0, 0)
if e1 != 0 { if e1 != 0 {
err = e1 err = e1
} }

View File

@ -61,7 +61,7 @@ func (p *prober) AddHTTP(id string, probingInterval time.Duration, endpoints []s
} }
resp, err := p.tr.RoundTrip(req) resp, err := p.tr.RoundTrip(req)
if err != nil { if err != nil {
s.recordFailure() s.recordFailure(err)
pinned = (pinned + 1) % len(endpoints) pinned = (pinned + 1) % len(endpoints)
continue continue
} }
@ -71,7 +71,7 @@ func (p *prober) AddHTTP(id string, probingInterval time.Duration, endpoints []s
err = d.Decode(&hh) err = d.Decode(&hh)
resp.Body.Close() resp.Body.Close()
if err != nil || !hh.OK { if err != nil || !hh.OK {
s.recordFailure() s.recordFailure(err)
pinned = (pinned + 1) % len(endpoints) pinned = (pinned + 1) % len(endpoints)
continue continue
} }

View File

@ -14,6 +14,7 @@ type Status interface {
Total() int64 Total() int64
Loss() int64 Loss() int64
Health() bool Health() bool
Err() error
// Estimated smoothed round trip time // Estimated smoothed round trip time
SRTT() time.Duration SRTT() time.Duration
// Estimated clock difference // Estimated clock difference
@ -27,6 +28,7 @@ type status struct {
total int64 total int64
loss int64 loss int64
health bool health bool
err error
clockdiff time.Duration clockdiff time.Duration
stopC chan struct{} stopC chan struct{}
} }
@ -56,6 +58,12 @@ func (s *status) Health() bool {
return s.health return s.health
} }
func (s *status) Err() error {
s.mu.Lock()
defer s.mu.Unlock()
return s.err
}
func (s *status) ClockDiff() time.Duration { func (s *status) ClockDiff() time.Duration {
s.mu.Lock() s.mu.Lock()
defer s.mu.Unlock() defer s.mu.Unlock()
@ -74,15 +82,17 @@ func (s *status) record(rtt time.Duration, when time.Time) {
s.health = true s.health = true
s.srtt = time.Duration((1-α)*float64(s.srtt) + α*float64(rtt)) s.srtt = time.Duration((1-α)*float64(s.srtt) + α*float64(rtt))
s.clockdiff = time.Now().Sub(when) - s.srtt/2 s.clockdiff = time.Now().Sub(when) - s.srtt/2
s.err = nil
} }
func (s *status) recordFailure() { func (s *status) recordFailure(err error) {
s.mu.Lock() s.mu.Lock()
defer s.mu.Unlock() defer s.mu.Unlock()
s.total++ s.total++
s.health = false s.health = false
s.loss += 1 s.loss += 1
s.err = err
} }
func (s *status) reset() { func (s *status) reset() {
@ -91,6 +101,8 @@ func (s *status) reset() {
s.srtt = 0 s.srtt = 0
s.total = 0 s.total = 0
s.loss = 0
s.health = false s.health = false
s.clockdiff = 0 s.clockdiff = 0
s.err = nil
} }

View File

@ -1,8 +1,7 @@
language: go language: go
go: go:
- 1.3 - tip
- 1.4
install: install:
- export GOPATH="$HOME/gopath" - export GOPATH="$HOME/gopath"

View File

@ -1,6 +1,7 @@
# OAuth2 for Go # OAuth2 for Go
[![Build Status](https://travis-ci.org/golang/oauth2.svg?branch=master)](https://travis-ci.org/golang/oauth2) [![Build Status](https://travis-ci.org/golang/oauth2.svg?branch=master)](https://travis-ci.org/golang/oauth2)
[![GoDoc](https://godoc.org/golang.org/x/oauth2?status.svg)](https://godoc.org/golang.org/x/oauth2)
oauth2 package contains a client implementation for OAuth 2.0 spec. oauth2 package contains a client implementation for OAuth 2.0 spec.

View File

@ -1,8 +1,8 @@
// Copyright 2014 The oauth2 Authors. All rights reserved. // Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
// +build appengine appenginevm // +build appengine
// App Engine hooks. // App Engine hooks.

View File

@ -1,4 +1,4 @@
// Copyright 2014 The oauth2 Authors. All rights reserved. // Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
@ -14,6 +14,9 @@ import (
"golang.org/x/oauth2" "golang.org/x/oauth2"
) )
// Set at init time by appenginevm_hook.go. If true, we are on App Engine Managed VMs.
var appengineVM bool
// Set at init time by appengine_hook.go. If nil, we're not on App Engine. // Set at init time by appengine_hook.go. If nil, we're not on App Engine.
var appengineTokenFunc func(c context.Context, scopes ...string) (token string, expiry time.Time, err error) var appengineTokenFunc func(c context.Context, scopes ...string) (token string, expiry time.Time, err error)

View File

@ -1,8 +1,8 @@
// Copyright 2015 The oauth2 Authors. All rights reserved. // Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
// +build appengine appenginevm // +build appengine
package google package google

14
vendor/golang.org/x/oauth2/google/appenginevm_hook.go generated vendored Normal file
View File

@ -0,0 +1,14 @@
// Copyright 2015 The oauth2 Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build appenginevm
package google
import "google.golang.org/appengine"
func init() {
appengineVM = true
appengineTokenFunc = appengine.AccessToken
}

View File

@ -1,4 +1,4 @@
// Copyright 2015 The oauth2 Authors. All rights reserved. // Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
@ -14,10 +14,10 @@ import (
"path/filepath" "path/filepath"
"runtime" "runtime"
"cloud.google.com/go/compute/metadata"
"golang.org/x/net/context" "golang.org/x/net/context"
"golang.org/x/oauth2" "golang.org/x/oauth2"
"golang.org/x/oauth2/jwt" "golang.org/x/oauth2/jwt"
"google.golang.org/cloud/compute/metadata"
) )
// DefaultClient returns an HTTP Client that uses the // DefaultClient returns an HTTP Client that uses the
@ -50,7 +50,8 @@ func DefaultClient(ctx context.Context, scope ...string) (*http.Client, error) {
// On Windows, this is %APPDATA%/gcloud/application_default_credentials.json. // On Windows, this is %APPDATA%/gcloud/application_default_credentials.json.
// On other systems, $HOME/.config/gcloud/application_default_credentials.json. // On other systems, $HOME/.config/gcloud/application_default_credentials.json.
// 3. On Google App Engine it uses the appengine.AccessToken function. // 3. On Google App Engine it uses the appengine.AccessToken function.
// 4. On Google Compute Engine, it fetches credentials from the metadata server. // 4. On Google Compute Engine and Google App Engine Managed VMs, it fetches
// credentials from the metadata server.
// (In this final case any provided scopes are ignored.) // (In this final case any provided scopes are ignored.)
// //
// For more details, see: // For more details, see:
@ -84,7 +85,7 @@ func DefaultTokenSource(ctx context.Context, scope ...string) (oauth2.TokenSourc
} }
// Third, if we're on Google App Engine use those credentials. // Third, if we're on Google App Engine use those credentials.
if appengineTokenFunc != nil { if appengineTokenFunc != nil && !appengineVM {
return AppEngineTokenSource(ctx, scope...), nil return AppEngineTokenSource(ctx, scope...), nil
} }

View File

@ -1,4 +1,4 @@
// Copyright 2014 The oauth2 Authors. All rights reserved. // Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
@ -21,9 +21,9 @@ import (
"strings" "strings"
"time" "time"
"cloud.google.com/go/compute/metadata"
"golang.org/x/oauth2" "golang.org/x/oauth2"
"golang.org/x/oauth2/jwt" "golang.org/x/oauth2/jwt"
"google.golang.org/cloud/compute/metadata"
) )
// Endpoint is Google's OAuth 2.0 endpoint. // Endpoint is Google's OAuth 2.0 endpoint.
@ -37,9 +37,10 @@ const JWTTokenURL = "https://accounts.google.com/o/oauth2/token"
// ConfigFromJSON uses a Google Developers Console client_credentials.json // ConfigFromJSON uses a Google Developers Console client_credentials.json
// file to construct a config. // file to construct a config.
// client_credentials.json can be downloadable from https://console.developers.google.com, // client_credentials.json can be downloaded from
// under "APIs & Auth" > "Credentials". Download the Web application credentials in the // https://console.developers.google.com, under "Credentials". Download the Web
// JSON format and provide the contents of the file as jsonKey. // application credentials in the JSON format and provide the contents of the
// file as jsonKey.
func ConfigFromJSON(jsonKey []byte, scope ...string) (*oauth2.Config, error) { func ConfigFromJSON(jsonKey []byte, scope ...string) (*oauth2.Config, error) {
type cred struct { type cred struct {
ClientID string `json:"client_id"` ClientID string `json:"client_id"`
@ -81,22 +82,29 @@ func ConfigFromJSON(jsonKey []byte, scope ...string) (*oauth2.Config, error) {
// JWTConfigFromJSON uses a Google Developers service account JSON key file to read // JWTConfigFromJSON uses a Google Developers service account JSON key file to read
// the credentials that authorize and authenticate the requests. // the credentials that authorize and authenticate the requests.
// Create a service account on "Credentials" page under "APIs & Auth" for your // Create a service account on "Credentials" for your project at
// project at https://console.developers.google.com to download a JSON key file. // https://console.developers.google.com to download a JSON key file.
func JWTConfigFromJSON(jsonKey []byte, scope ...string) (*jwt.Config, error) { func JWTConfigFromJSON(jsonKey []byte, scope ...string) (*jwt.Config, error) {
var key struct { var key struct {
Email string `json:"client_email"` Email string `json:"client_email"`
PrivateKey string `json:"private_key"` PrivateKey string `json:"private_key"`
PrivateKeyID string `json:"private_key_id"`
TokenURL string `json:"token_uri"`
} }
if err := json.Unmarshal(jsonKey, &key); err != nil { if err := json.Unmarshal(jsonKey, &key); err != nil {
return nil, err return nil, err
} }
return &jwt.Config{ config := &jwt.Config{
Email: key.Email, Email: key.Email,
PrivateKey: []byte(key.PrivateKey), PrivateKey: []byte(key.PrivateKey),
Scopes: scope, PrivateKeyID: key.PrivateKeyID,
TokenURL: JWTTokenURL, Scopes: scope,
}, nil TokenURL: key.TokenURL,
}
if config.TokenURL == "" {
config.TokenURL = JWTTokenURL
}
return config, nil
} }
// ComputeTokenSource returns a token source that fetches access tokens // ComputeTokenSource returns a token source that fetches access tokens

74
vendor/golang.org/x/oauth2/google/jwt.go generated vendored Normal file
View File

@ -0,0 +1,74 @@
// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package google
import (
"crypto/rsa"
"fmt"
"time"
"golang.org/x/oauth2"
"golang.org/x/oauth2/internal"
"golang.org/x/oauth2/jws"
)
// JWTAccessTokenSourceFromJSON uses a Google Developers service account JSON
// key file to read the credentials that authorize and authenticate the
// requests, and returns a TokenSource that does not use any OAuth2 flow but
// instead creates a JWT and sends that as the access token.
// The audience is typically a URL that specifies the scope of the credentials.
//
// Note that this is not a standard OAuth flow, but rather an
// optimization supported by a few Google services.
// Unless you know otherwise, you should use JWTConfigFromJSON instead.
func JWTAccessTokenSourceFromJSON(jsonKey []byte, audience string) (oauth2.TokenSource, error) {
cfg, err := JWTConfigFromJSON(jsonKey)
if err != nil {
return nil, fmt.Errorf("google: could not parse JSON key: %v", err)
}
pk, err := internal.ParseKey(cfg.PrivateKey)
if err != nil {
return nil, fmt.Errorf("google: could not parse key: %v", err)
}
ts := &jwtAccessTokenSource{
email: cfg.Email,
audience: audience,
pk: pk,
pkID: cfg.PrivateKeyID,
}
tok, err := ts.Token()
if err != nil {
return nil, err
}
return oauth2.ReuseTokenSource(tok, ts), nil
}
type jwtAccessTokenSource struct {
email, audience string
pk *rsa.PrivateKey
pkID string
}
func (ts *jwtAccessTokenSource) Token() (*oauth2.Token, error) {
iat := time.Now()
exp := iat.Add(time.Hour)
cs := &jws.ClaimSet{
Iss: ts.email,
Sub: ts.email,
Aud: ts.audience,
Iat: iat.Unix(),
Exp: exp.Unix(),
}
hdr := &jws.Header{
Algorithm: "RS256",
Typ: "JWT",
KeyID: string(ts.pkID),
}
msg, err := jws.Encode(hdr, cs, ts.pk)
if err != nil {
return nil, fmt.Errorf("google: could not encode JWT: %v", err)
}
return &oauth2.Token{AccessToken: msg, TokenType: "Bearer", Expiry: exp}, nil
}

View File

@ -1,4 +1,4 @@
// Copyright 2015 The oauth2 Authors. All rights reserved. // Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.

View File

@ -1,4 +1,4 @@
// Copyright 2014 The oauth2 Authors. All rights reserved. // Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.

View File

@ -1,4 +1,4 @@
// Copyright 2014 The oauth2 Authors. All rights reserved. // Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
@ -91,24 +91,36 @@ func (e *expirationTime) UnmarshalJSON(b []byte) error {
var brokenAuthHeaderProviders = []string{ var brokenAuthHeaderProviders = []string{
"https://accounts.google.com/", "https://accounts.google.com/",
"https://www.googleapis.com/",
"https://github.com/",
"https://api.instagram.com/",
"https://www.douban.com/",
"https://api.dropbox.com/", "https://api.dropbox.com/",
"https://api.soundcloud.com/", "https://api.dropboxapi.com/",
"https://www.linkedin.com/", "https://api.instagram.com/",
"https://api.twitch.tv/", "https://api.netatmo.net/",
"https://oauth.vk.com/",
"https://api.odnoklassniki.ru/", "https://api.odnoklassniki.ru/",
"https://connect.stripe.com/",
"https://api.pushbullet.com/", "https://api.pushbullet.com/",
"https://api.soundcloud.com/",
"https://api.twitch.tv/",
"https://app.box.com/",
"https://connect.stripe.com/",
"https://login.microsoftonline.com/",
"https://login.salesforce.com/",
"https://oauth.sandbox.trainingpeaks.com/", "https://oauth.sandbox.trainingpeaks.com/",
"https://oauth.trainingpeaks.com/", "https://oauth.trainingpeaks.com/",
"https://www.strava.com/oauth/", "https://oauth.vk.com/",
"https://app.box.com/", "https://openapi.baidu.com/",
"https://slack.com/",
"https://test-sandbox.auth.corp.google.com", "https://test-sandbox.auth.corp.google.com",
"https://test.salesforce.com/",
"https://user.gini.net/", "https://user.gini.net/",
"https://www.douban.com/",
"https://www.googleapis.com/",
"https://www.linkedin.com/",
"https://www.strava.com/oauth/",
"https://www.wunderlist.com/oauth/",
"https://api.patreon.com/",
}
func RegisterBrokenAuthHeaderProvider(tokenURL string) {
brokenAuthHeaderProviders = append(brokenAuthHeaderProviders, tokenURL)
} }
// providerAuthHeaderWorks reports whether the OAuth2 server identified by the tokenURL // providerAuthHeaderWorks reports whether the OAuth2 server identified by the tokenURL
@ -134,23 +146,23 @@ func providerAuthHeaderWorks(tokenURL string) bool {
return true return true
} }
func RetrieveToken(ctx context.Context, ClientID, ClientSecret, TokenURL string, v url.Values) (*Token, error) { func RetrieveToken(ctx context.Context, clientID, clientSecret, tokenURL string, v url.Values) (*Token, error) {
hc, err := ContextClient(ctx) hc, err := ContextClient(ctx)
if err != nil { if err != nil {
return nil, err return nil, err
} }
v.Set("client_id", ClientID) v.Set("client_id", clientID)
bustedAuth := !providerAuthHeaderWorks(TokenURL) bustedAuth := !providerAuthHeaderWorks(tokenURL)
if bustedAuth && ClientSecret != "" { if bustedAuth && clientSecret != "" {
v.Set("client_secret", ClientSecret) v.Set("client_secret", clientSecret)
} }
req, err := http.NewRequest("POST", TokenURL, strings.NewReader(v.Encode())) req, err := http.NewRequest("POST", tokenURL, strings.NewReader(v.Encode()))
if err != nil { if err != nil {
return nil, err return nil, err
} }
req.Header.Set("Content-Type", "application/x-www-form-urlencoded") req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
if !bustedAuth { if !bustedAuth {
req.SetBasicAuth(ClientID, ClientSecret) req.SetBasicAuth(clientID, clientSecret)
} }
r, err := hc.Do(req) r, err := hc.Do(req)
if err != nil { if err != nil {

View File

@ -1,4 +1,4 @@
// Copyright 2014 The oauth2 Authors. All rights reserved. // Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
@ -33,6 +33,11 @@ func RegisterContextClientFunc(fn ContextClientFunc) {
} }
func ContextClient(ctx context.Context) (*http.Client, error) { func ContextClient(ctx context.Context) (*http.Client, error) {
if ctx != nil {
if hc, ok := ctx.Value(HTTPClient).(*http.Client); ok {
return hc, nil
}
}
for _, fn := range contextClientFuncs { for _, fn := range contextClientFuncs {
c, err := fn(ctx) c, err := fn(ctx)
if err != nil { if err != nil {
@ -42,9 +47,6 @@ func ContextClient(ctx context.Context) (*http.Client, error) {
return c, nil return c, nil
} }
} }
if hc, ok := ctx.Value(HTTPClient).(*http.Client); ok {
return hc, nil
}
return http.DefaultClient, nil return http.DefaultClient, nil
} }

110
vendor/golang.org/x/oauth2/jws/jws.go generated vendored
View File

@ -1,9 +1,17 @@
// Copyright 2014 The oauth2 Authors. All rights reserved. // Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
// Package jws provides encoding and decoding utilities for // Package jws provides a partial implementation
// signed JWS messages. // of JSON Web Signature encoding and decoding.
// It exists to support the golang.org/x/oauth2 package.
//
// See RFC 7515.
//
// Deprecated: this package is not intended for public use and might be
// removed in the future. It exists for internal use only.
// Please switch to another JWS package or copy this package into your own
// source tree.
package jws package jws
import ( import (
@ -27,8 +35,8 @@ type ClaimSet struct {
Iss string `json:"iss"` // email address of the client_id of the application making the access token request Iss string `json:"iss"` // email address of the client_id of the application making the access token request
Scope string `json:"scope,omitempty"` // space-delimited list of the permissions the application requests Scope string `json:"scope,omitempty"` // space-delimited list of the permissions the application requests
Aud string `json:"aud"` // descriptor of the intended target of the assertion (Optional). Aud string `json:"aud"` // descriptor of the intended target of the assertion (Optional).
Exp int64 `json:"exp"` // the expiration time of the assertion Exp int64 `json:"exp"` // the expiration time of the assertion (seconds since Unix epoch)
Iat int64 `json:"iat"` // the time the assertion was issued. Iat int64 `json:"iat"` // the time the assertion was issued (seconds since Unix epoch)
Typ string `json:"typ,omitempty"` // token type (Optional). Typ string `json:"typ,omitempty"` // token type (Optional).
// Email for which the application is requesting delegated access (Optional). // Email for which the application is requesting delegated access (Optional).
@ -41,23 +49,22 @@ type ClaimSet struct {
// See http://tools.ietf.org/html/draft-jones-json-web-token-10#section-4.3 // See http://tools.ietf.org/html/draft-jones-json-web-token-10#section-4.3
// This array is marshalled using custom code (see (c *ClaimSet) encode()). // This array is marshalled using custom code (see (c *ClaimSet) encode()).
PrivateClaims map[string]interface{} `json:"-"` PrivateClaims map[string]interface{} `json:"-"`
exp time.Time
iat time.Time
} }
func (c *ClaimSet) encode() (string, error) { func (c *ClaimSet) encode() (string, error) {
if c.exp.IsZero() || c.iat.IsZero() { // Reverting time back for machines whose time is not perfectly in sync.
// Reverting time back for machines whose time is not perfectly in sync. // If client machine's time is in the future according
// If client machine's time is in the future according // to Google servers, an access token will not be issued.
// to Google servers, an access token will not be issued. now := time.Now().Add(-10 * time.Second)
now := time.Now().Add(-10 * time.Second) if c.Iat == 0 {
c.iat = now c.Iat = now.Unix()
c.exp = now.Add(time.Hour) }
if c.Exp == 0 {
c.Exp = now.Add(time.Hour).Unix()
}
if c.Exp < c.Iat {
return "", fmt.Errorf("jws: invalid Exp = %v; must be later than Iat = %v", c.Exp, c.Iat)
} }
c.Exp = c.exp.Unix()
c.Iat = c.iat.Unix()
b, err := json.Marshal(c) b, err := json.Marshal(c)
if err != nil { if err != nil {
@ -65,7 +72,7 @@ func (c *ClaimSet) encode() (string, error) {
} }
if len(c.PrivateClaims) == 0 { if len(c.PrivateClaims) == 0 {
return base64Encode(b), nil return base64.RawURLEncoding.EncodeToString(b), nil
} }
// Marshal private claim set and then append it to b. // Marshal private claim set and then append it to b.
@ -83,7 +90,7 @@ func (c *ClaimSet) encode() (string, error) {
} }
b[len(b)-1] = ',' // Replace closing curly brace with a comma. b[len(b)-1] = ',' // Replace closing curly brace with a comma.
b = append(b, prv[1:]...) // Append private claims. b = append(b, prv[1:]...) // Append private claims.
return base64Encode(b), nil return base64.RawURLEncoding.EncodeToString(b), nil
} }
// Header represents the header for the signed JWS payloads. // Header represents the header for the signed JWS payloads.
@ -93,6 +100,9 @@ type Header struct {
// Represents the token type. // Represents the token type.
Typ string `json:"typ"` Typ string `json:"typ"`
// The optional hint of which key is being used.
KeyID string `json:"kid,omitempty"`
} }
func (h *Header) encode() (string, error) { func (h *Header) encode() (string, error) {
@ -100,7 +110,7 @@ func (h *Header) encode() (string, error) {
if err != nil { if err != nil {
return "", err return "", err
} }
return base64Encode(b), nil return base64.RawURLEncoding.EncodeToString(b), nil
} }
// Decode decodes a claim set from a JWS payload. // Decode decodes a claim set from a JWS payload.
@ -111,7 +121,7 @@ func Decode(payload string) (*ClaimSet, error) {
// TODO(jbd): Provide more context about the error. // TODO(jbd): Provide more context about the error.
return nil, errors.New("jws: invalid token received") return nil, errors.New("jws: invalid token received")
} }
decoded, err := base64Decode(s[1]) decoded, err := base64.RawURLEncoding.DecodeString(s[1])
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -120,8 +130,11 @@ func Decode(payload string) (*ClaimSet, error) {
return c, err return c, err
} }
// Encode encodes a signed JWS with provided header and claim set. // Signer returns a signature for the given data.
func Encode(header *Header, c *ClaimSet, signature *rsa.PrivateKey) (string, error) { type Signer func(data []byte) (sig []byte, err error)
// EncodeWithSigner encodes a header and claim set with the provided signer.
func EncodeWithSigner(header *Header, c *ClaimSet, sg Signer) (string, error) {
head, err := header.encode() head, err := header.encode()
if err != nil { if err != nil {
return "", err return "", err
@ -131,30 +144,39 @@ func Encode(header *Header, c *ClaimSet, signature *rsa.PrivateKey) (string, err
return "", err return "", err
} }
ss := fmt.Sprintf("%s.%s", head, cs) ss := fmt.Sprintf("%s.%s", head, cs)
h := sha256.New() sig, err := sg([]byte(ss))
h.Write([]byte(ss))
b, err := rsa.SignPKCS1v15(rand.Reader, signature, crypto.SHA256, h.Sum(nil))
if err != nil { if err != nil {
return "", err return "", err
} }
sig := base64Encode(b) return fmt.Sprintf("%s.%s", ss, base64.RawURLEncoding.EncodeToString(sig)), nil
return fmt.Sprintf("%s.%s", ss, sig), nil
} }
// base64Encode returns and Base64url encoded version of the input string with any // Encode encodes a signed JWS with provided header and claim set.
// trailing "=" stripped. // This invokes EncodeWithSigner using crypto/rsa.SignPKCS1v15 with the given RSA private key.
func base64Encode(b []byte) string { func Encode(header *Header, c *ClaimSet, key *rsa.PrivateKey) (string, error) {
return strings.TrimRight(base64.URLEncoding.EncodeToString(b), "=") sg := func(data []byte) (sig []byte, err error) {
} h := sha256.New()
h.Write(data)
// base64Decode decodes the Base64url encoded string return rsa.SignPKCS1v15(rand.Reader, key, crypto.SHA256, h.Sum(nil))
func base64Decode(s string) ([]byte, error) {
// add back missing padding
switch len(s) % 4 {
case 2:
s += "=="
case 3:
s += "="
} }
return base64.URLEncoding.DecodeString(s) return EncodeWithSigner(header, c, sg)
}
// Verify tests whether the provided JWT token's signature was produced by the private key
// associated with the supplied public key.
func Verify(token string, key *rsa.PublicKey) error {
parts := strings.Split(token, ".")
if len(parts) != 3 {
return errors.New("jws: invalid token received, token must have 3 parts")
}
signedContent := parts[0] + "." + parts[1]
signatureString, err := base64.RawURLEncoding.DecodeString(parts[2])
if err != nil {
return err
}
h := sha256.New()
h.Write([]byte(signedContent))
return rsa.VerifyPKCS1v15(key, crypto.SHA256, h.Sum(nil), []byte(signatureString))
} }

View File

@ -1,4 +1,4 @@
// Copyright 2014 The oauth2 Authors. All rights reserved. // Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
@ -46,6 +46,10 @@ type Config struct {
// //
PrivateKey []byte PrivateKey []byte
// PrivateKeyID contains an optional hint indicating which key is being
// used.
PrivateKeyID string
// Subject is the optional user to impersonate. // Subject is the optional user to impersonate.
Subject string Subject string
@ -54,6 +58,9 @@ type Config struct {
// TokenURL is the endpoint required to complete the 2-legged JWT flow. // TokenURL is the endpoint required to complete the 2-legged JWT flow.
TokenURL string TokenURL string
// Expires optionally specifies how long the token is valid for.
Expires time.Duration
} }
// TokenSource returns a JWT TokenSource using the configuration // TokenSource returns a JWT TokenSource using the configuration
@ -95,6 +102,9 @@ func (js jwtSource) Token() (*oauth2.Token, error) {
// to be compatible with legacy OAuth 2.0 providers. // to be compatible with legacy OAuth 2.0 providers.
claimSet.Prn = subject claimSet.Prn = subject
} }
if t := js.conf.Expires; t > 0 {
claimSet.Exp = time.Now().Add(t).Unix()
}
payload, err := jws.Encode(defaultHeader, claimSet, pk) payload, err := jws.Encode(defaultHeader, claimSet, pk)
if err != nil { if err != nil {
return nil, err return nil, err

20
vendor/golang.org/x/oauth2/oauth2.go generated vendored
View File

@ -1,4 +1,4 @@
// Copyright 2014 The oauth2 Authors. All rights reserved. // Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
@ -21,10 +21,26 @@ import (
// NoContext is the default context you should supply if not using // NoContext is the default context you should supply if not using
// your own context.Context (see https://golang.org/x/net/context). // your own context.Context (see https://golang.org/x/net/context).
//
// Deprecated: Use context.Background() or context.TODO() instead.
var NoContext = context.TODO() var NoContext = context.TODO()
// RegisterBrokenAuthHeaderProvider registers an OAuth2 server
// identified by the tokenURL prefix as an OAuth2 implementation
// which doesn't support the HTTP Basic authentication
// scheme to authenticate with the authorization server.
// Once a server is registered, credentials (client_id and client_secret)
// will be passed as query parameters rather than being present
// in the Authorization header.
// See https://code.google.com/p/goauth2/issues/detail?id=31 for background.
func RegisterBrokenAuthHeaderProvider(tokenURL string) {
internal.RegisterBrokenAuthHeaderProvider(tokenURL)
}
// Config describes a typical 3-legged OAuth2 flow, with both the // Config describes a typical 3-legged OAuth2 flow, with both the
// client application information and the server's endpoint URLs. // client application information and the server's endpoint URLs.
// For the client credentials 2-legged OAuth2 flow, see the clientcredentials
// package (https://golang.org/x/oauth2/clientcredentials).
type Config struct { type Config struct {
// ClientID is the application's ID. // ClientID is the application's ID.
ClientID string ClientID string
@ -283,7 +299,7 @@ func NewClient(ctx context.Context, src TokenSource) *http.Client {
if src == nil { if src == nil {
c, err := internal.ContextClient(ctx) c, err := internal.ContextClient(ctx)
if err != nil { if err != nil {
return &http.Client{Transport: internal.ErrorTransport{err}} return &http.Client{Transport: internal.ErrorTransport{Err: err}}
} }
return c return c
} }

27
vendor/golang.org/x/oauth2/token.go generated vendored
View File

@ -1,4 +1,4 @@
// Copyright 2014 The oauth2 Authors. All rights reserved. // Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
@ -7,6 +7,7 @@ package oauth2
import ( import (
"net/http" "net/http"
"net/url" "net/url"
"strconv"
"strings" "strings"
"time" "time"
@ -92,14 +93,28 @@ func (t *Token) WithExtra(extra interface{}) *Token {
// Extra fields are key-value pairs returned by the server as a // Extra fields are key-value pairs returned by the server as a
// part of the token retrieval response. // part of the token retrieval response.
func (t *Token) Extra(key string) interface{} { func (t *Token) Extra(key string) interface{} {
if vals, ok := t.raw.(url.Values); ok {
// TODO(jbd): Cast numeric values to int64 or float64.
return vals.Get(key)
}
if raw, ok := t.raw.(map[string]interface{}); ok { if raw, ok := t.raw.(map[string]interface{}); ok {
return raw[key] return raw[key]
} }
return nil
vals, ok := t.raw.(url.Values)
if !ok {
return nil
}
v := vals.Get(key)
switch s := strings.TrimSpace(v); strings.Count(s, ".") {
case 0: // Contains no "."; try to parse as int
if i, err := strconv.ParseInt(s, 10, 64); err == nil {
return i
}
case 1: // Contains a single "."; try to parse as float
if f, err := strconv.ParseFloat(s, 64); err == nil {
return f
}
}
return v
} }
// expired reports whether the token is expired. // expired reports whether the token is expired.

View File

@ -1,4 +1,4 @@
// Copyright 2014 The oauth2 Authors. All rights reserved. // Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.

View File

@ -771,25 +771,23 @@ func (c *MetricDescriptorsCreateCall) Context(ctx context.Context) *MetricDescri
} }
func (c *MetricDescriptorsCreateCall) doRequest(alt string) (*http.Response, error) { func (c *MetricDescriptorsCreateCall) doRequest(alt string) (*http.Response, error) {
reqHeaders := make(http.Header)
reqHeaders.Set("User-Agent", c.s.userAgent())
var body io.Reader = nil var body io.Reader = nil
body, err := googleapi.WithoutDataWrapper.JSONReader(c.metricdescriptor) body, err := googleapi.WithoutDataWrapper.JSONReader(c.metricdescriptor)
if err != nil { if err != nil {
return nil, err return nil, err
} }
ctype := "application/json" reqHeaders.Set("Content-Type", "application/json")
c.urlParams_.Set("alt", alt) c.urlParams_.Set("alt", alt)
urls := googleapi.ResolveRelative(c.s.BasePath, "{project}/metricDescriptors") urls := googleapi.ResolveRelative(c.s.BasePath, "{project}/metricDescriptors")
urls += "?" + c.urlParams_.Encode() urls += "?" + c.urlParams_.Encode()
req, _ := http.NewRequest("POST", urls, body) req, _ := http.NewRequest("POST", urls, body)
req.Header = reqHeaders
googleapi.Expand(req.URL, map[string]string{ googleapi.Expand(req.URL, map[string]string{
"project": c.project, "project": c.project,
}) })
req.Header.Set("Content-Type", ctype) return gensupport.SendRequest(c.ctx_, c.s.client, req)
req.Header.Set("User-Agent", c.s.userAgent())
if c.ctx_ != nil {
return ctxhttp.Do(c.ctx_, c.s.client, req)
}
return c.s.client.Do(req)
} }
// Do executes the "cloudmonitoring.metricDescriptors.create" call. // Do executes the "cloudmonitoring.metricDescriptors.create" call.
@ -824,7 +822,8 @@ func (c *MetricDescriptorsCreateCall) Do(opts ...googleapi.CallOption) (*MetricD
HTTPStatusCode: res.StatusCode, HTTPStatusCode: res.StatusCode,
}, },
} }
if err := json.NewDecoder(res.Body).Decode(&ret); err != nil { target := &ret
if err := json.NewDecoder(res.Body).Decode(target); err != nil {
return nil, err return nil, err
} }
return ret, nil return ret, nil
@ -893,20 +892,19 @@ func (c *MetricDescriptorsDeleteCall) Context(ctx context.Context) *MetricDescri
} }
func (c *MetricDescriptorsDeleteCall) doRequest(alt string) (*http.Response, error) { func (c *MetricDescriptorsDeleteCall) doRequest(alt string) (*http.Response, error) {
reqHeaders := make(http.Header)
reqHeaders.Set("User-Agent", c.s.userAgent())
var body io.Reader = nil var body io.Reader = nil
c.urlParams_.Set("alt", alt) c.urlParams_.Set("alt", alt)
urls := googleapi.ResolveRelative(c.s.BasePath, "{project}/metricDescriptors/{metric}") urls := googleapi.ResolveRelative(c.s.BasePath, "{project}/metricDescriptors/{metric}")
urls += "?" + c.urlParams_.Encode() urls += "?" + c.urlParams_.Encode()
req, _ := http.NewRequest("DELETE", urls, body) req, _ := http.NewRequest("DELETE", urls, body)
req.Header = reqHeaders
googleapi.Expand(req.URL, map[string]string{ googleapi.Expand(req.URL, map[string]string{
"project": c.project, "project": c.project,
"metric": c.metric, "metric": c.metric,
}) })
req.Header.Set("User-Agent", c.s.userAgent()) return gensupport.SendRequest(c.ctx_, c.s.client, req)
if c.ctx_ != nil {
return ctxhttp.Do(c.ctx_, c.s.client, req)
}
return c.s.client.Do(req)
} }
// Do executes the "cloudmonitoring.metricDescriptors.delete" call. // Do executes the "cloudmonitoring.metricDescriptors.delete" call.
@ -941,7 +939,8 @@ func (c *MetricDescriptorsDeleteCall) Do(opts ...googleapi.CallOption) (*DeleteM
HTTPStatusCode: res.StatusCode, HTTPStatusCode: res.StatusCode,
}, },
} }
if err := json.NewDecoder(res.Body).Decode(&ret); err != nil { target := &ret
if err := json.NewDecoder(res.Body).Decode(target); err != nil {
return nil, err return nil, err
} }
return ret, nil return ret, nil
@ -1057,22 +1056,21 @@ func (c *MetricDescriptorsListCall) Context(ctx context.Context) *MetricDescript
} }
func (c *MetricDescriptorsListCall) doRequest(alt string) (*http.Response, error) { func (c *MetricDescriptorsListCall) doRequest(alt string) (*http.Response, error) {
reqHeaders := make(http.Header)
reqHeaders.Set("User-Agent", c.s.userAgent())
if c.ifNoneMatch_ != "" {
reqHeaders.Set("If-None-Match", c.ifNoneMatch_)
}
var body io.Reader = nil var body io.Reader = nil
c.urlParams_.Set("alt", alt) c.urlParams_.Set("alt", alt)
urls := googleapi.ResolveRelative(c.s.BasePath, "{project}/metricDescriptors") urls := googleapi.ResolveRelative(c.s.BasePath, "{project}/metricDescriptors")
urls += "?" + c.urlParams_.Encode() urls += "?" + c.urlParams_.Encode()
req, _ := http.NewRequest("GET", urls, body) req, _ := http.NewRequest("GET", urls, body)
req.Header = reqHeaders
googleapi.Expand(req.URL, map[string]string{ googleapi.Expand(req.URL, map[string]string{
"project": c.project, "project": c.project,
}) })
req.Header.Set("User-Agent", c.s.userAgent()) return gensupport.SendRequest(c.ctx_, c.s.client, req)
if c.ifNoneMatch_ != "" {
req.Header.Set("If-None-Match", c.ifNoneMatch_)
}
if c.ctx_ != nil {
return ctxhttp.Do(c.ctx_, c.s.client, req)
}
return c.s.client.Do(req)
} }
// Do executes the "cloudmonitoring.metricDescriptors.list" call. // Do executes the "cloudmonitoring.metricDescriptors.list" call.
@ -1107,7 +1105,8 @@ func (c *MetricDescriptorsListCall) Do(opts ...googleapi.CallOption) (*ListMetri
HTTPStatusCode: res.StatusCode, HTTPStatusCode: res.StatusCode,
}, },
} }
if err := json.NewDecoder(res.Body).Decode(&ret); err != nil { target := &ret
if err := json.NewDecoder(res.Body).Decode(target); err != nil {
return nil, err return nil, err
} }
return ret, nil return ret, nil
@ -1321,23 +1320,22 @@ func (c *TimeseriesListCall) Context(ctx context.Context) *TimeseriesListCall {
} }
func (c *TimeseriesListCall) doRequest(alt string) (*http.Response, error) { func (c *TimeseriesListCall) doRequest(alt string) (*http.Response, error) {
reqHeaders := make(http.Header)
reqHeaders.Set("User-Agent", c.s.userAgent())
if c.ifNoneMatch_ != "" {
reqHeaders.Set("If-None-Match", c.ifNoneMatch_)
}
var body io.Reader = nil var body io.Reader = nil
c.urlParams_.Set("alt", alt) c.urlParams_.Set("alt", alt)
urls := googleapi.ResolveRelative(c.s.BasePath, "{project}/timeseries/{metric}") urls := googleapi.ResolveRelative(c.s.BasePath, "{project}/timeseries/{metric}")
urls += "?" + c.urlParams_.Encode() urls += "?" + c.urlParams_.Encode()
req, _ := http.NewRequest("GET", urls, body) req, _ := http.NewRequest("GET", urls, body)
req.Header = reqHeaders
googleapi.Expand(req.URL, map[string]string{ googleapi.Expand(req.URL, map[string]string{
"project": c.project, "project": c.project,
"metric": c.metric, "metric": c.metric,
}) })
req.Header.Set("User-Agent", c.s.userAgent()) return gensupport.SendRequest(c.ctx_, c.s.client, req)
if c.ifNoneMatch_ != "" {
req.Header.Set("If-None-Match", c.ifNoneMatch_)
}
if c.ctx_ != nil {
return ctxhttp.Do(c.ctx_, c.s.client, req)
}
return c.s.client.Do(req)
} }
// Do executes the "cloudmonitoring.timeseries.list" call. // Do executes the "cloudmonitoring.timeseries.list" call.
@ -1372,7 +1370,8 @@ func (c *TimeseriesListCall) Do(opts ...googleapi.CallOption) (*ListTimeseriesRe
HTTPStatusCode: res.StatusCode, HTTPStatusCode: res.StatusCode,
}, },
} }
if err := json.NewDecoder(res.Body).Decode(&ret); err != nil { target := &ret
if err := json.NewDecoder(res.Body).Decode(target); err != nil {
return nil, err return nil, err
} }
return ret, nil return ret, nil
@ -1538,25 +1537,23 @@ func (c *TimeseriesWriteCall) Context(ctx context.Context) *TimeseriesWriteCall
} }
func (c *TimeseriesWriteCall) doRequest(alt string) (*http.Response, error) { func (c *TimeseriesWriteCall) doRequest(alt string) (*http.Response, error) {
reqHeaders := make(http.Header)
reqHeaders.Set("User-Agent", c.s.userAgent())
var body io.Reader = nil var body io.Reader = nil
body, err := googleapi.WithoutDataWrapper.JSONReader(c.writetimeseriesrequest) body, err := googleapi.WithoutDataWrapper.JSONReader(c.writetimeseriesrequest)
if err != nil { if err != nil {
return nil, err return nil, err
} }
ctype := "application/json" reqHeaders.Set("Content-Type", "application/json")
c.urlParams_.Set("alt", alt) c.urlParams_.Set("alt", alt)
urls := googleapi.ResolveRelative(c.s.BasePath, "{project}/timeseries:write") urls := googleapi.ResolveRelative(c.s.BasePath, "{project}/timeseries:write")
urls += "?" + c.urlParams_.Encode() urls += "?" + c.urlParams_.Encode()
req, _ := http.NewRequest("POST", urls, body) req, _ := http.NewRequest("POST", urls, body)
req.Header = reqHeaders
googleapi.Expand(req.URL, map[string]string{ googleapi.Expand(req.URL, map[string]string{
"project": c.project, "project": c.project,
}) })
req.Header.Set("Content-Type", ctype) return gensupport.SendRequest(c.ctx_, c.s.client, req)
req.Header.Set("User-Agent", c.s.userAgent())
if c.ctx_ != nil {
return ctxhttp.Do(c.ctx_, c.s.client, req)
}
return c.s.client.Do(req)
} }
// Do executes the "cloudmonitoring.timeseries.write" call. // Do executes the "cloudmonitoring.timeseries.write" call.
@ -1591,7 +1588,8 @@ func (c *TimeseriesWriteCall) Do(opts ...googleapi.CallOption) (*WriteTimeseries
HTTPStatusCode: res.StatusCode, HTTPStatusCode: res.StatusCode,
}, },
} }
if err := json.NewDecoder(res.Body).Decode(&ret); err != nil { target := &ret
if err := json.NewDecoder(res.Body).Decode(target); err != nil {
return nil, err return nil, err
} }
return ret, nil return ret, nil
@ -1766,23 +1764,22 @@ func (c *TimeseriesDescriptorsListCall) Context(ctx context.Context) *Timeseries
} }
func (c *TimeseriesDescriptorsListCall) doRequest(alt string) (*http.Response, error) { func (c *TimeseriesDescriptorsListCall) doRequest(alt string) (*http.Response, error) {
reqHeaders := make(http.Header)
reqHeaders.Set("User-Agent", c.s.userAgent())
if c.ifNoneMatch_ != "" {
reqHeaders.Set("If-None-Match", c.ifNoneMatch_)
}
var body io.Reader = nil var body io.Reader = nil
c.urlParams_.Set("alt", alt) c.urlParams_.Set("alt", alt)
urls := googleapi.ResolveRelative(c.s.BasePath, "{project}/timeseriesDescriptors/{metric}") urls := googleapi.ResolveRelative(c.s.BasePath, "{project}/timeseriesDescriptors/{metric}")
urls += "?" + c.urlParams_.Encode() urls += "?" + c.urlParams_.Encode()
req, _ := http.NewRequest("GET", urls, body) req, _ := http.NewRequest("GET", urls, body)
req.Header = reqHeaders
googleapi.Expand(req.URL, map[string]string{ googleapi.Expand(req.URL, map[string]string{
"project": c.project, "project": c.project,
"metric": c.metric, "metric": c.metric,
}) })
req.Header.Set("User-Agent", c.s.userAgent()) return gensupport.SendRequest(c.ctx_, c.s.client, req)
if c.ifNoneMatch_ != "" {
req.Header.Set("If-None-Match", c.ifNoneMatch_)
}
if c.ctx_ != nil {
return ctxhttp.Do(c.ctx_, c.s.client, req)
}
return c.s.client.Do(req)
} }
// Do executes the "cloudmonitoring.timeseriesDescriptors.list" call. // Do executes the "cloudmonitoring.timeseriesDescriptors.list" call.
@ -1818,7 +1815,8 @@ func (c *TimeseriesDescriptorsListCall) Do(opts ...googleapi.CallOption) (*ListT
HTTPStatusCode: res.StatusCode, HTTPStatusCode: res.StatusCode,
}, },
} }
if err := json.NewDecoder(res.Body).Decode(&ret); err != nil { target := &ret
if err := json.NewDecoder(res.Body).Decode(target); err != nil {
return nil, err return nil, err
} }
return ret, nil return ret, nil

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -878,23 +878,22 @@ func (c *ProjectsZonesGetServerconfigCall) Context(ctx context.Context) *Project
} }
func (c *ProjectsZonesGetServerconfigCall) doRequest(alt string) (*http.Response, error) { func (c *ProjectsZonesGetServerconfigCall) doRequest(alt string) (*http.Response, error) {
reqHeaders := make(http.Header)
reqHeaders.Set("User-Agent", c.s.userAgent())
if c.ifNoneMatch_ != "" {
reqHeaders.Set("If-None-Match", c.ifNoneMatch_)
}
var body io.Reader = nil var body io.Reader = nil
c.urlParams_.Set("alt", alt) c.urlParams_.Set("alt", alt)
urls := googleapi.ResolveRelative(c.s.BasePath, "v1/projects/{projectId}/zones/{zone}/serverconfig") urls := googleapi.ResolveRelative(c.s.BasePath, "v1/projects/{projectId}/zones/{zone}/serverconfig")
urls += "?" + c.urlParams_.Encode() urls += "?" + c.urlParams_.Encode()
req, _ := http.NewRequest("GET", urls, body) req, _ := http.NewRequest("GET", urls, body)
req.Header = reqHeaders
googleapi.Expand(req.URL, map[string]string{ googleapi.Expand(req.URL, map[string]string{
"projectId": c.projectId, "projectId": c.projectId,
"zone": c.zone, "zone": c.zone,
}) })
req.Header.Set("User-Agent", c.s.userAgent()) return gensupport.SendRequest(c.ctx_, c.s.client, req)
if c.ifNoneMatch_ != "" {
req.Header.Set("If-None-Match", c.ifNoneMatch_)
}
if c.ctx_ != nil {
return ctxhttp.Do(c.ctx_, c.s.client, req)
}
return c.s.client.Do(req)
} }
// Do executes the "container.projects.zones.getServerconfig" call. // Do executes the "container.projects.zones.getServerconfig" call.
@ -929,7 +928,8 @@ func (c *ProjectsZonesGetServerconfigCall) Do(opts ...googleapi.CallOption) (*Se
HTTPStatusCode: res.StatusCode, HTTPStatusCode: res.StatusCode,
}, },
} }
if err := json.NewDecoder(res.Body).Decode(&ret); err != nil { target := &ret
if err := json.NewDecoder(res.Body).Decode(target); err != nil {
return nil, err return nil, err
} }
return ret, nil return ret, nil
@ -1011,26 +1011,24 @@ func (c *ProjectsZonesClustersCreateCall) Context(ctx context.Context) *Projects
} }
func (c *ProjectsZonesClustersCreateCall) doRequest(alt string) (*http.Response, error) { func (c *ProjectsZonesClustersCreateCall) doRequest(alt string) (*http.Response, error) {
reqHeaders := make(http.Header)
reqHeaders.Set("User-Agent", c.s.userAgent())
var body io.Reader = nil var body io.Reader = nil
body, err := googleapi.WithoutDataWrapper.JSONReader(c.createclusterrequest) body, err := googleapi.WithoutDataWrapper.JSONReader(c.createclusterrequest)
if err != nil { if err != nil {
return nil, err return nil, err
} }
ctype := "application/json" reqHeaders.Set("Content-Type", "application/json")
c.urlParams_.Set("alt", alt) c.urlParams_.Set("alt", alt)
urls := googleapi.ResolveRelative(c.s.BasePath, "v1/projects/{projectId}/zones/{zone}/clusters") urls := googleapi.ResolveRelative(c.s.BasePath, "v1/projects/{projectId}/zones/{zone}/clusters")
urls += "?" + c.urlParams_.Encode() urls += "?" + c.urlParams_.Encode()
req, _ := http.NewRequest("POST", urls, body) req, _ := http.NewRequest("POST", urls, body)
req.Header = reqHeaders
googleapi.Expand(req.URL, map[string]string{ googleapi.Expand(req.URL, map[string]string{
"projectId": c.projectId, "projectId": c.projectId,
"zone": c.zone, "zone": c.zone,
}) })
req.Header.Set("Content-Type", ctype) return gensupport.SendRequest(c.ctx_, c.s.client, req)
req.Header.Set("User-Agent", c.s.userAgent())
if c.ctx_ != nil {
return ctxhttp.Do(c.ctx_, c.s.client, req)
}
return c.s.client.Do(req)
} }
// Do executes the "container.projects.zones.clusters.create" call. // Do executes the "container.projects.zones.clusters.create" call.
@ -1065,7 +1063,8 @@ func (c *ProjectsZonesClustersCreateCall) Do(opts ...googleapi.CallOption) (*Ope
HTTPStatusCode: res.StatusCode, HTTPStatusCode: res.StatusCode,
}, },
} }
if err := json.NewDecoder(res.Body).Decode(&ret); err != nil { target := &ret
if err := json.NewDecoder(res.Body).Decode(target); err != nil {
return nil, err return nil, err
} }
return ret, nil return ret, nil
@ -1147,21 +1146,20 @@ func (c *ProjectsZonesClustersDeleteCall) Context(ctx context.Context) *Projects
} }
func (c *ProjectsZonesClustersDeleteCall) doRequest(alt string) (*http.Response, error) { func (c *ProjectsZonesClustersDeleteCall) doRequest(alt string) (*http.Response, error) {
reqHeaders := make(http.Header)
reqHeaders.Set("User-Agent", c.s.userAgent())
var body io.Reader = nil var body io.Reader = nil
c.urlParams_.Set("alt", alt) c.urlParams_.Set("alt", alt)
urls := googleapi.ResolveRelative(c.s.BasePath, "v1/projects/{projectId}/zones/{zone}/clusters/{clusterId}") urls := googleapi.ResolveRelative(c.s.BasePath, "v1/projects/{projectId}/zones/{zone}/clusters/{clusterId}")
urls += "?" + c.urlParams_.Encode() urls += "?" + c.urlParams_.Encode()
req, _ := http.NewRequest("DELETE", urls, body) req, _ := http.NewRequest("DELETE", urls, body)
req.Header = reqHeaders
googleapi.Expand(req.URL, map[string]string{ googleapi.Expand(req.URL, map[string]string{
"projectId": c.projectId, "projectId": c.projectId,
"zone": c.zone, "zone": c.zone,
"clusterId": c.clusterId, "clusterId": c.clusterId,
}) })
req.Header.Set("User-Agent", c.s.userAgent()) return gensupport.SendRequest(c.ctx_, c.s.client, req)
if c.ctx_ != nil {
return ctxhttp.Do(c.ctx_, c.s.client, req)
}
return c.s.client.Do(req)
} }
// Do executes the "container.projects.zones.clusters.delete" call. // Do executes the "container.projects.zones.clusters.delete" call.
@ -1196,7 +1194,8 @@ func (c *ProjectsZonesClustersDeleteCall) Do(opts ...googleapi.CallOption) (*Ope
HTTPStatusCode: res.StatusCode, HTTPStatusCode: res.StatusCode,
}, },
} }
if err := json.NewDecoder(res.Body).Decode(&ret); err != nil { target := &ret
if err := json.NewDecoder(res.Body).Decode(target); err != nil {
return nil, err return nil, err
} }
return ret, nil return ret, nil
@ -1288,24 +1287,23 @@ func (c *ProjectsZonesClustersGetCall) Context(ctx context.Context) *ProjectsZon
} }
func (c *ProjectsZonesClustersGetCall) doRequest(alt string) (*http.Response, error) { func (c *ProjectsZonesClustersGetCall) doRequest(alt string) (*http.Response, error) {
reqHeaders := make(http.Header)
reqHeaders.Set("User-Agent", c.s.userAgent())
if c.ifNoneMatch_ != "" {
reqHeaders.Set("If-None-Match", c.ifNoneMatch_)
}
var body io.Reader = nil var body io.Reader = nil
c.urlParams_.Set("alt", alt) c.urlParams_.Set("alt", alt)
urls := googleapi.ResolveRelative(c.s.BasePath, "v1/projects/{projectId}/zones/{zone}/clusters/{clusterId}") urls := googleapi.ResolveRelative(c.s.BasePath, "v1/projects/{projectId}/zones/{zone}/clusters/{clusterId}")
urls += "?" + c.urlParams_.Encode() urls += "?" + c.urlParams_.Encode()
req, _ := http.NewRequest("GET", urls, body) req, _ := http.NewRequest("GET", urls, body)
req.Header = reqHeaders
googleapi.Expand(req.URL, map[string]string{ googleapi.Expand(req.URL, map[string]string{
"projectId": c.projectId, "projectId": c.projectId,
"zone": c.zone, "zone": c.zone,
"clusterId": c.clusterId, "clusterId": c.clusterId,
}) })
req.Header.Set("User-Agent", c.s.userAgent()) return gensupport.SendRequest(c.ctx_, c.s.client, req)
if c.ifNoneMatch_ != "" {
req.Header.Set("If-None-Match", c.ifNoneMatch_)
}
if c.ctx_ != nil {
return ctxhttp.Do(c.ctx_, c.s.client, req)
}
return c.s.client.Do(req)
} }
// Do executes the "container.projects.zones.clusters.get" call. // Do executes the "container.projects.zones.clusters.get" call.
@ -1340,7 +1338,8 @@ func (c *ProjectsZonesClustersGetCall) Do(opts ...googleapi.CallOption) (*Cluste
HTTPStatusCode: res.StatusCode, HTTPStatusCode: res.StatusCode,
}, },
} }
if err := json.NewDecoder(res.Body).Decode(&ret); err != nil { target := &ret
if err := json.NewDecoder(res.Body).Decode(target); err != nil {
return nil, err return nil, err
} }
return ret, nil return ret, nil
@ -1431,23 +1430,22 @@ func (c *ProjectsZonesClustersListCall) Context(ctx context.Context) *ProjectsZo
} }
func (c *ProjectsZonesClustersListCall) doRequest(alt string) (*http.Response, error) { func (c *ProjectsZonesClustersListCall) doRequest(alt string) (*http.Response, error) {
reqHeaders := make(http.Header)
reqHeaders.Set("User-Agent", c.s.userAgent())
if c.ifNoneMatch_ != "" {
reqHeaders.Set("If-None-Match", c.ifNoneMatch_)
}
var body io.Reader = nil var body io.Reader = nil
c.urlParams_.Set("alt", alt) c.urlParams_.Set("alt", alt)
urls := googleapi.ResolveRelative(c.s.BasePath, "v1/projects/{projectId}/zones/{zone}/clusters") urls := googleapi.ResolveRelative(c.s.BasePath, "v1/projects/{projectId}/zones/{zone}/clusters")
urls += "?" + c.urlParams_.Encode() urls += "?" + c.urlParams_.Encode()
req, _ := http.NewRequest("GET", urls, body) req, _ := http.NewRequest("GET", urls, body)
req.Header = reqHeaders
googleapi.Expand(req.URL, map[string]string{ googleapi.Expand(req.URL, map[string]string{
"projectId": c.projectId, "projectId": c.projectId,
"zone": c.zone, "zone": c.zone,
}) })
req.Header.Set("User-Agent", c.s.userAgent()) return gensupport.SendRequest(c.ctx_, c.s.client, req)
if c.ifNoneMatch_ != "" {
req.Header.Set("If-None-Match", c.ifNoneMatch_)
}
if c.ctx_ != nil {
return ctxhttp.Do(c.ctx_, c.s.client, req)
}
return c.s.client.Do(req)
} }
// Do executes the "container.projects.zones.clusters.list" call. // Do executes the "container.projects.zones.clusters.list" call.
@ -1482,7 +1480,8 @@ func (c *ProjectsZonesClustersListCall) Do(opts ...googleapi.CallOption) (*ListC
HTTPStatusCode: res.StatusCode, HTTPStatusCode: res.StatusCode,
}, },
} }
if err := json.NewDecoder(res.Body).Decode(&ret); err != nil { target := &ret
if err := json.NewDecoder(res.Body).Decode(target); err != nil {
return nil, err return nil, err
} }
return ret, nil return ret, nil
@ -1558,27 +1557,25 @@ func (c *ProjectsZonesClustersUpdateCall) Context(ctx context.Context) *Projects
} }
func (c *ProjectsZonesClustersUpdateCall) doRequest(alt string) (*http.Response, error) { func (c *ProjectsZonesClustersUpdateCall) doRequest(alt string) (*http.Response, error) {
reqHeaders := make(http.Header)
reqHeaders.Set("User-Agent", c.s.userAgent())
var body io.Reader = nil var body io.Reader = nil
body, err := googleapi.WithoutDataWrapper.JSONReader(c.updateclusterrequest) body, err := googleapi.WithoutDataWrapper.JSONReader(c.updateclusterrequest)
if err != nil { if err != nil {
return nil, err return nil, err
} }
ctype := "application/json" reqHeaders.Set("Content-Type", "application/json")
c.urlParams_.Set("alt", alt) c.urlParams_.Set("alt", alt)
urls := googleapi.ResolveRelative(c.s.BasePath, "v1/projects/{projectId}/zones/{zone}/clusters/{clusterId}") urls := googleapi.ResolveRelative(c.s.BasePath, "v1/projects/{projectId}/zones/{zone}/clusters/{clusterId}")
urls += "?" + c.urlParams_.Encode() urls += "?" + c.urlParams_.Encode()
req, _ := http.NewRequest("PUT", urls, body) req, _ := http.NewRequest("PUT", urls, body)
req.Header = reqHeaders
googleapi.Expand(req.URL, map[string]string{ googleapi.Expand(req.URL, map[string]string{
"projectId": c.projectId, "projectId": c.projectId,
"zone": c.zone, "zone": c.zone,
"clusterId": c.clusterId, "clusterId": c.clusterId,
}) })
req.Header.Set("Content-Type", ctype) return gensupport.SendRequest(c.ctx_, c.s.client, req)
req.Header.Set("User-Agent", c.s.userAgent())
if c.ctx_ != nil {
return ctxhttp.Do(c.ctx_, c.s.client, req)
}
return c.s.client.Do(req)
} }
// Do executes the "container.projects.zones.clusters.update" call. // Do executes the "container.projects.zones.clusters.update" call.
@ -1613,7 +1610,8 @@ func (c *ProjectsZonesClustersUpdateCall) Do(opts ...googleapi.CallOption) (*Ope
HTTPStatusCode: res.StatusCode, HTTPStatusCode: res.StatusCode,
}, },
} }
if err := json.NewDecoder(res.Body).Decode(&ret); err != nil { target := &ret
if err := json.NewDecoder(res.Body).Decode(target); err != nil {
return nil, err return nil, err
} }
return ret, nil return ret, nil
@ -1699,27 +1697,25 @@ func (c *ProjectsZonesClustersNodePoolsCreateCall) Context(ctx context.Context)
} }
func (c *ProjectsZonesClustersNodePoolsCreateCall) doRequest(alt string) (*http.Response, error) { func (c *ProjectsZonesClustersNodePoolsCreateCall) doRequest(alt string) (*http.Response, error) {
reqHeaders := make(http.Header)
reqHeaders.Set("User-Agent", c.s.userAgent())
var body io.Reader = nil var body io.Reader = nil
body, err := googleapi.WithoutDataWrapper.JSONReader(c.createnodepoolrequest) body, err := googleapi.WithoutDataWrapper.JSONReader(c.createnodepoolrequest)
if err != nil { if err != nil {
return nil, err return nil, err
} }
ctype := "application/json" reqHeaders.Set("Content-Type", "application/json")
c.urlParams_.Set("alt", alt) c.urlParams_.Set("alt", alt)
urls := googleapi.ResolveRelative(c.s.BasePath, "v1/projects/{projectId}/zones/{zone}/clusters/{clusterId}/nodePools") urls := googleapi.ResolveRelative(c.s.BasePath, "v1/projects/{projectId}/zones/{zone}/clusters/{clusterId}/nodePools")
urls += "?" + c.urlParams_.Encode() urls += "?" + c.urlParams_.Encode()
req, _ := http.NewRequest("POST", urls, body) req, _ := http.NewRequest("POST", urls, body)
req.Header = reqHeaders
googleapi.Expand(req.URL, map[string]string{ googleapi.Expand(req.URL, map[string]string{
"projectId": c.projectId, "projectId": c.projectId,
"zone": c.zone, "zone": c.zone,
"clusterId": c.clusterId, "clusterId": c.clusterId,
}) })
req.Header.Set("Content-Type", ctype) return gensupport.SendRequest(c.ctx_, c.s.client, req)
req.Header.Set("User-Agent", c.s.userAgent())
if c.ctx_ != nil {
return ctxhttp.Do(c.ctx_, c.s.client, req)
}
return c.s.client.Do(req)
} }
// Do executes the "container.projects.zones.clusters.nodePools.create" call. // Do executes the "container.projects.zones.clusters.nodePools.create" call.
@ -1754,7 +1750,8 @@ func (c *ProjectsZonesClustersNodePoolsCreateCall) Do(opts ...googleapi.CallOpti
HTTPStatusCode: res.StatusCode, HTTPStatusCode: res.StatusCode,
}, },
} }
if err := json.NewDecoder(res.Body).Decode(&ret); err != nil { target := &ret
if err := json.NewDecoder(res.Body).Decode(target); err != nil {
return nil, err return nil, err
} }
return ret, nil return ret, nil
@ -1840,22 +1837,21 @@ func (c *ProjectsZonesClustersNodePoolsDeleteCall) Context(ctx context.Context)
} }
func (c *ProjectsZonesClustersNodePoolsDeleteCall) doRequest(alt string) (*http.Response, error) { func (c *ProjectsZonesClustersNodePoolsDeleteCall) doRequest(alt string) (*http.Response, error) {
reqHeaders := make(http.Header)
reqHeaders.Set("User-Agent", c.s.userAgent())
var body io.Reader = nil var body io.Reader = nil
c.urlParams_.Set("alt", alt) c.urlParams_.Set("alt", alt)
urls := googleapi.ResolveRelative(c.s.BasePath, "v1/projects/{projectId}/zones/{zone}/clusters/{clusterId}/nodePools/{nodePoolId}") urls := googleapi.ResolveRelative(c.s.BasePath, "v1/projects/{projectId}/zones/{zone}/clusters/{clusterId}/nodePools/{nodePoolId}")
urls += "?" + c.urlParams_.Encode() urls += "?" + c.urlParams_.Encode()
req, _ := http.NewRequest("DELETE", urls, body) req, _ := http.NewRequest("DELETE", urls, body)
req.Header = reqHeaders
googleapi.Expand(req.URL, map[string]string{ googleapi.Expand(req.URL, map[string]string{
"projectId": c.projectId, "projectId": c.projectId,
"zone": c.zone, "zone": c.zone,
"clusterId": c.clusterId, "clusterId": c.clusterId,
"nodePoolId": c.nodePoolId, "nodePoolId": c.nodePoolId,
}) })
req.Header.Set("User-Agent", c.s.userAgent()) return gensupport.SendRequest(c.ctx_, c.s.client, req)
if c.ctx_ != nil {
return ctxhttp.Do(c.ctx_, c.s.client, req)
}
return c.s.client.Do(req)
} }
// Do executes the "container.projects.zones.clusters.nodePools.delete" call. // Do executes the "container.projects.zones.clusters.nodePools.delete" call.
@ -1890,7 +1886,8 @@ func (c *ProjectsZonesClustersNodePoolsDeleteCall) Do(opts ...googleapi.CallOpti
HTTPStatusCode: res.StatusCode, HTTPStatusCode: res.StatusCode,
}, },
} }
if err := json.NewDecoder(res.Body).Decode(&ret); err != nil { target := &ret
if err := json.NewDecoder(res.Body).Decode(target); err != nil {
return nil, err return nil, err
} }
return ret, nil return ret, nil
@ -1991,25 +1988,24 @@ func (c *ProjectsZonesClustersNodePoolsGetCall) Context(ctx context.Context) *Pr
} }
func (c *ProjectsZonesClustersNodePoolsGetCall) doRequest(alt string) (*http.Response, error) { func (c *ProjectsZonesClustersNodePoolsGetCall) doRequest(alt string) (*http.Response, error) {
reqHeaders := make(http.Header)
reqHeaders.Set("User-Agent", c.s.userAgent())
if c.ifNoneMatch_ != "" {
reqHeaders.Set("If-None-Match", c.ifNoneMatch_)
}
var body io.Reader = nil var body io.Reader = nil
c.urlParams_.Set("alt", alt) c.urlParams_.Set("alt", alt)
urls := googleapi.ResolveRelative(c.s.BasePath, "v1/projects/{projectId}/zones/{zone}/clusters/{clusterId}/nodePools/{nodePoolId}") urls := googleapi.ResolveRelative(c.s.BasePath, "v1/projects/{projectId}/zones/{zone}/clusters/{clusterId}/nodePools/{nodePoolId}")
urls += "?" + c.urlParams_.Encode() urls += "?" + c.urlParams_.Encode()
req, _ := http.NewRequest("GET", urls, body) req, _ := http.NewRequest("GET", urls, body)
req.Header = reqHeaders
googleapi.Expand(req.URL, map[string]string{ googleapi.Expand(req.URL, map[string]string{
"projectId": c.projectId, "projectId": c.projectId,
"zone": c.zone, "zone": c.zone,
"clusterId": c.clusterId, "clusterId": c.clusterId,
"nodePoolId": c.nodePoolId, "nodePoolId": c.nodePoolId,
}) })
req.Header.Set("User-Agent", c.s.userAgent()) return gensupport.SendRequest(c.ctx_, c.s.client, req)
if c.ifNoneMatch_ != "" {
req.Header.Set("If-None-Match", c.ifNoneMatch_)
}
if c.ctx_ != nil {
return ctxhttp.Do(c.ctx_, c.s.client, req)
}
return c.s.client.Do(req)
} }
// Do executes the "container.projects.zones.clusters.nodePools.get" call. // Do executes the "container.projects.zones.clusters.nodePools.get" call.
@ -2044,7 +2040,8 @@ func (c *ProjectsZonesClustersNodePoolsGetCall) Do(opts ...googleapi.CallOption)
HTTPStatusCode: res.StatusCode, HTTPStatusCode: res.StatusCode,
}, },
} }
if err := json.NewDecoder(res.Body).Decode(&ret); err != nil { target := &ret
if err := json.NewDecoder(res.Body).Decode(target); err != nil {
return nil, err return nil, err
} }
return ret, nil return ret, nil
@ -2143,24 +2140,23 @@ func (c *ProjectsZonesClustersNodePoolsListCall) Context(ctx context.Context) *P
} }
func (c *ProjectsZonesClustersNodePoolsListCall) doRequest(alt string) (*http.Response, error) { func (c *ProjectsZonesClustersNodePoolsListCall) doRequest(alt string) (*http.Response, error) {
reqHeaders := make(http.Header)
reqHeaders.Set("User-Agent", c.s.userAgent())
if c.ifNoneMatch_ != "" {
reqHeaders.Set("If-None-Match", c.ifNoneMatch_)
}
var body io.Reader = nil var body io.Reader = nil
c.urlParams_.Set("alt", alt) c.urlParams_.Set("alt", alt)
urls := googleapi.ResolveRelative(c.s.BasePath, "v1/projects/{projectId}/zones/{zone}/clusters/{clusterId}/nodePools") urls := googleapi.ResolveRelative(c.s.BasePath, "v1/projects/{projectId}/zones/{zone}/clusters/{clusterId}/nodePools")
urls += "?" + c.urlParams_.Encode() urls += "?" + c.urlParams_.Encode()
req, _ := http.NewRequest("GET", urls, body) req, _ := http.NewRequest("GET", urls, body)
req.Header = reqHeaders
googleapi.Expand(req.URL, map[string]string{ googleapi.Expand(req.URL, map[string]string{
"projectId": c.projectId, "projectId": c.projectId,
"zone": c.zone, "zone": c.zone,
"clusterId": c.clusterId, "clusterId": c.clusterId,
}) })
req.Header.Set("User-Agent", c.s.userAgent()) return gensupport.SendRequest(c.ctx_, c.s.client, req)
if c.ifNoneMatch_ != "" {
req.Header.Set("If-None-Match", c.ifNoneMatch_)
}
if c.ctx_ != nil {
return ctxhttp.Do(c.ctx_, c.s.client, req)
}
return c.s.client.Do(req)
} }
// Do executes the "container.projects.zones.clusters.nodePools.list" call. // Do executes the "container.projects.zones.clusters.nodePools.list" call.
@ -2195,7 +2191,8 @@ func (c *ProjectsZonesClustersNodePoolsListCall) Do(opts ...googleapi.CallOption
HTTPStatusCode: res.StatusCode, HTTPStatusCode: res.StatusCode,
}, },
} }
if err := json.NewDecoder(res.Body).Decode(&ret); err != nil { target := &ret
if err := json.NewDecoder(res.Body).Decode(target); err != nil {
return nil, err return nil, err
} }
return ret, nil return ret, nil
@ -2287,24 +2284,23 @@ func (c *ProjectsZonesOperationsGetCall) Context(ctx context.Context) *ProjectsZ
} }
func (c *ProjectsZonesOperationsGetCall) doRequest(alt string) (*http.Response, error) { func (c *ProjectsZonesOperationsGetCall) doRequest(alt string) (*http.Response, error) {
reqHeaders := make(http.Header)
reqHeaders.Set("User-Agent", c.s.userAgent())
if c.ifNoneMatch_ != "" {
reqHeaders.Set("If-None-Match", c.ifNoneMatch_)
}
var body io.Reader = nil var body io.Reader = nil
c.urlParams_.Set("alt", alt) c.urlParams_.Set("alt", alt)
urls := googleapi.ResolveRelative(c.s.BasePath, "v1/projects/{projectId}/zones/{zone}/operations/{operationId}") urls := googleapi.ResolveRelative(c.s.BasePath, "v1/projects/{projectId}/zones/{zone}/operations/{operationId}")
urls += "?" + c.urlParams_.Encode() urls += "?" + c.urlParams_.Encode()
req, _ := http.NewRequest("GET", urls, body) req, _ := http.NewRequest("GET", urls, body)
req.Header = reqHeaders
googleapi.Expand(req.URL, map[string]string{ googleapi.Expand(req.URL, map[string]string{
"projectId": c.projectId, "projectId": c.projectId,
"zone": c.zone, "zone": c.zone,
"operationId": c.operationId, "operationId": c.operationId,
}) })
req.Header.Set("User-Agent", c.s.userAgent()) return gensupport.SendRequest(c.ctx_, c.s.client, req)
if c.ifNoneMatch_ != "" {
req.Header.Set("If-None-Match", c.ifNoneMatch_)
}
if c.ctx_ != nil {
return ctxhttp.Do(c.ctx_, c.s.client, req)
}
return c.s.client.Do(req)
} }
// Do executes the "container.projects.zones.operations.get" call. // Do executes the "container.projects.zones.operations.get" call.
@ -2339,7 +2335,8 @@ func (c *ProjectsZonesOperationsGetCall) Do(opts ...googleapi.CallOption) (*Oper
HTTPStatusCode: res.StatusCode, HTTPStatusCode: res.StatusCode,
}, },
} }
if err := json.NewDecoder(res.Body).Decode(&ret); err != nil { target := &ret
if err := json.NewDecoder(res.Body).Decode(target); err != nil {
return nil, err return nil, err
} }
return ret, nil return ret, nil
@ -2430,23 +2427,22 @@ func (c *ProjectsZonesOperationsListCall) Context(ctx context.Context) *Projects
} }
func (c *ProjectsZonesOperationsListCall) doRequest(alt string) (*http.Response, error) { func (c *ProjectsZonesOperationsListCall) doRequest(alt string) (*http.Response, error) {
reqHeaders := make(http.Header)
reqHeaders.Set("User-Agent", c.s.userAgent())
if c.ifNoneMatch_ != "" {
reqHeaders.Set("If-None-Match", c.ifNoneMatch_)
}
var body io.Reader = nil var body io.Reader = nil
c.urlParams_.Set("alt", alt) c.urlParams_.Set("alt", alt)
urls := googleapi.ResolveRelative(c.s.BasePath, "v1/projects/{projectId}/zones/{zone}/operations") urls := googleapi.ResolveRelative(c.s.BasePath, "v1/projects/{projectId}/zones/{zone}/operations")
urls += "?" + c.urlParams_.Encode() urls += "?" + c.urlParams_.Encode()
req, _ := http.NewRequest("GET", urls, body) req, _ := http.NewRequest("GET", urls, body)
req.Header = reqHeaders
googleapi.Expand(req.URL, map[string]string{ googleapi.Expand(req.URL, map[string]string{
"projectId": c.projectId, "projectId": c.projectId,
"zone": c.zone, "zone": c.zone,
}) })
req.Header.Set("User-Agent", c.s.userAgent()) return gensupport.SendRequest(c.ctx_, c.s.client, req)
if c.ifNoneMatch_ != "" {
req.Header.Set("If-None-Match", c.ifNoneMatch_)
}
if c.ctx_ != nil {
return ctxhttp.Do(c.ctx_, c.s.client, req)
}
return c.s.client.Do(req)
} }
// Do executes the "container.projects.zones.operations.list" call. // Do executes the "container.projects.zones.operations.list" call.
@ -2481,7 +2477,8 @@ func (c *ProjectsZonesOperationsListCall) Do(opts ...googleapi.CallOption) (*Lis
HTTPStatusCode: res.StatusCode, HTTPStatusCode: res.StatusCode,
}, },
} }
if err := json.NewDecoder(res.Body).Decode(&ret); err != nil { target := &ret
if err := json.NewDecoder(res.Body).Decode(target); err != nil {
return nil, err return nil, err
} }
return ret, nil return ret, nil

View File

@ -12,7 +12,6 @@ import (
"time" "time"
"golang.org/x/net/context" "golang.org/x/net/context"
"golang.org/x/net/context/ctxhttp"
) )
const ( const (
@ -80,7 +79,7 @@ func (rx *ResumableUpload) doUploadRequest(ctx context.Context, data io.Reader,
req.Header.Set("Content-Range", contentRange) req.Header.Set("Content-Range", contentRange)
req.Header.Set("Content-Type", rx.MediaType) req.Header.Set("Content-Type", rx.MediaType)
req.Header.Set("User-Agent", rx.UserAgent) req.Header.Set("User-Agent", rx.UserAgent)
return ctxhttp.Do(ctx, rx.Client, req) return SendRequest(ctx, rx.Client, req)
} }
@ -135,6 +134,8 @@ func contextDone(ctx context.Context) bool {
// It retries using the provided back off strategy until cancelled or the // It retries using the provided back off strategy until cancelled or the
// strategy indicates to stop retrying. // strategy indicates to stop retrying.
// It is called from the auto-generated API code and is not visible to the user. // It is called from the auto-generated API code and is not visible to the user.
// Before sending an HTTP request, Upload calls any registered hook functions,
// and calls the returned functions after the request returns (see send.go).
// rx is private to the auto-generated API code. // rx is private to the auto-generated API code.
// Exactly one of resp or err will be nil. If resp is non-nil, the caller must call resp.Body.Close. // Exactly one of resp or err will be nil. If resp is non-nil, the caller must call resp.Body.Close.
func (rx *ResumableUpload) Upload(ctx context.Context) (resp *http.Response, err error) { func (rx *ResumableUpload) Upload(ctx context.Context) (resp *http.Response, err error) {

55
vendor/google.golang.org/api/gensupport/send.go generated vendored Normal file
View File

@ -0,0 +1,55 @@
// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package gensupport
import (
"net/http"
"golang.org/x/net/context"
"golang.org/x/net/context/ctxhttp"
)
// Hook is the type of a function that is called once before each HTTP request
// that is sent by a generated API. It returns a function that is called after
// the request returns.
// Hooks are not called if the context is nil.
type Hook func(ctx context.Context, req *http.Request) func(resp *http.Response)
var hooks []Hook
// RegisterHook registers a Hook to be called before each HTTP request by a
// generated API. Hooks are called in the order they are registered. Each
// hook can return a function; if it is non-nil, it is called after the HTTP
// request returns. These functions are called in the reverse order.
// RegisterHook should not be called concurrently with itself or SendRequest.
func RegisterHook(h Hook) {
hooks = append(hooks, h)
}
// SendRequest sends a single HTTP request using the given client.
// If ctx is non-nil, it calls all hooks, then sends the request with
// ctxhttp.Do, then calls any functions returned by the hooks in reverse order.
func SendRequest(ctx context.Context, client *http.Client, req *http.Request) (*http.Response, error) {
if ctx == nil {
return client.Do(req)
}
// Call hooks in order of registration, store returned funcs.
post := make([]func(resp *http.Response), len(hooks))
for i, h := range hooks {
fn := h(ctx, req)
post[i] = fn
}
// Send request.
resp, err := ctxhttp.Do(ctx, client, req)
// Call returned funcs in reverse order.
for i := len(post) - 1; i >= 0; i-- {
if fn := post[i]; fn != nil {
fn(resp)
}
}
return resp, err
}

Some files were not shown because too many files have changed in this diff Show More