diff --git a/chunk/chunk_cache.go b/chunk/chunk_cache.go index 514c2032f29..d955028b103 100644 --- a/chunk/chunk_cache.go +++ b/chunk/chunk_cache.go @@ -10,7 +10,7 @@ import ( "github.com/bradfitz/gomemcache/memcache" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/common/log" - "github.com/weaveworks/scope/common/instrument" + "github.com/weaveworks/common/instrument" "golang.org/x/net/context" ) diff --git a/chunk/chunk_store.go b/chunk/chunk_store.go index 1caa72d7b29..00cb9b1b778 100644 --- a/chunk/chunk_store.go +++ b/chunk/chunk_store.go @@ -18,10 +18,10 @@ import ( "github.com/prometheus/common/log" "github.com/prometheus/common/model" "github.com/prometheus/prometheus/storage/metric" - "github.com/weaveworks/scope/common/instrument" + "github.com/weaveworks/common/instrument" "golang.org/x/net/context" - "github.com/weaveworks/cortex/user" + "github.com/weaveworks/common/user" "github.com/weaveworks/cortex/util" ) diff --git a/chunk/chunk_store_test.go b/chunk/chunk_store_test.go index 60af6eaf3fc..52f013d03a6 100644 --- a/chunk/chunk_store_test.go +++ b/chunk/chunk_store_test.go @@ -15,7 +15,7 @@ import ( "github.com/stretchr/testify/assert" "golang.org/x/net/context" - "github.com/weaveworks/cortex/user" + "github.com/weaveworks/common/user" "github.com/weaveworks/cortex/util" ) diff --git a/chunk/dynamo_table_manager.go b/chunk/dynamo_table_manager.go index 98e11ef1718..eb1a1284444 100644 --- a/chunk/dynamo_table_manager.go +++ b/chunk/dynamo_table_manager.go @@ -11,8 +11,8 @@ import ( "github.com/aws/aws-sdk-go/service/dynamodb" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/common/log" + "github.com/weaveworks/common/instrument" "github.com/weaveworks/common/mtime" - "github.com/weaveworks/scope/common/instrument" "golang.org/x/net/context" ) diff --git a/chunk/dynamodb_client.go b/chunk/dynamodb_client.go index 3493e4334de..39926ffadc6 100644 --- a/chunk/dynamodb_client.go +++ b/chunk/dynamodb_client.go @@ -12,7 +12,7 @@ import ( "github.com/aws/aws-sdk-go/aws/session" "github.com/aws/aws-sdk-go/service/dynamodb" "github.com/prometheus/client_golang/prometheus" - "github.com/weaveworks/scope/common/instrument" + "github.com/weaveworks/common/instrument" "golang.org/x/net/context" ) diff --git a/cmd/querier/main.go b/cmd/querier/main.go index ab80a0534e1..ab73f7ef0d0 100644 --- a/cmd/querier/main.go +++ b/cmd/querier/main.go @@ -12,12 +12,12 @@ import ( "github.com/prometheus/prometheus/promql" "github.com/prometheus/prometheus/web/api/v1" + "github.com/weaveworks/common/user" "github.com/weaveworks/cortex/chunk" "github.com/weaveworks/cortex/distributor" "github.com/weaveworks/cortex/querier" "github.com/weaveworks/cortex/ring" "github.com/weaveworks/cortex/server" - "github.com/weaveworks/cortex/user" "github.com/weaveworks/cortex/util" ) @@ -51,9 +51,9 @@ func main() { engine := promql.NewEngine(queryable, nil) api := v1.NewAPI(engine, querier.DummyStorage{Queryable: queryable}) promRouter := route.New(func(r *http.Request) (context.Context, error) { - userID := r.Header.Get(user.UserIDHeaderName) + userID := r.Header.Get(user.OrgIDHeaderName) if userID == "" { - return nil, fmt.Errorf("no %s header", user.UserIDHeaderName) + return nil, fmt.Errorf("no %s header", user.OrgIDHeaderName) } return user.WithID(r.Context(), userID), nil }).WithPrefix("/api/prom/api/v1") diff --git a/cortex.proto b/cortex.proto index 08e8a899f9c..4d654013b86 100644 --- a/cortex.proto +++ b/cortex.proto @@ -12,8 +12,7 @@ service Ingester { rpc MetricsForLabelMatchers(MetricsForLabelMatchersRequest) returns (MetricsForLabelMatchersResponse) {}; } -message WriteResponse { -} +message WriteResponse {} message QueryRequest { int64 start_timestamp_ms = 1; @@ -33,8 +32,7 @@ message LabelValuesResponse { repeated string label_values = 1; } -message UserStatsRequest { -} +message UserStatsRequest {} message UserStatsResponse { double ingestion_rate = 1; diff --git a/distributor/distributor.go b/distributor/distributor.go index 43b69f185ac..bde9ce91650 100644 --- a/distributor/distributor.go +++ b/distributor/distributor.go @@ -11,7 +11,6 @@ import ( "github.com/grpc-ecosystem/grpc-opentracing/go/otgrpc" "github.com/mwitkow/go-grpc-middleware" "github.com/opentracing/opentracing-go" - "github.com/weaveworks/scope/common/instrument" "golang.org/x/net/context" "google.golang.org/grpc" @@ -21,11 +20,12 @@ import ( "github.com/prometheus/prometheus/storage/metric" "github.com/prometheus/prometheus/storage/remote" + "github.com/weaveworks/common/instrument" + "github.com/weaveworks/common/middleware" + "github.com/weaveworks/common/user" "github.com/weaveworks/cortex" "github.com/weaveworks/cortex/ring" - "github.com/weaveworks/cortex/user" "github.com/weaveworks/cortex/util" - "github.com/weaveworks/cortex/util/middleware" ) var ( diff --git a/ingester/ingester.go b/ingester/ingester.go index f02fd9c16e8..ba662ba149c 100644 --- a/ingester/ingester.go +++ b/ingester/ingester.go @@ -14,10 +14,10 @@ import ( "github.com/prometheus/prometheus/storage/remote" "golang.org/x/net/context" + "github.com/weaveworks/common/user" "github.com/weaveworks/cortex" cortex_chunk "github.com/weaveworks/cortex/chunk" "github.com/weaveworks/cortex/ring" - "github.com/weaveworks/cortex/user" "github.com/weaveworks/cortex/util" ) diff --git a/ingester/ingester_test.go b/ingester/ingester_test.go index 36d29246eb2..bd97624c6b9 100644 --- a/ingester/ingester_test.go +++ b/ingester/ingester_test.go @@ -12,8 +12,8 @@ import ( "github.com/prometheus/prometheus/storage/metric" "golang.org/x/net/context" + "github.com/weaveworks/common/user" "github.com/weaveworks/cortex/chunk" - "github.com/weaveworks/cortex/user" "github.com/weaveworks/cortex/util" ) diff --git a/ingester/user_state.go b/ingester/user_state.go index f006213a27b..15740308ff7 100644 --- a/ingester/user_state.go +++ b/ingester/user_state.go @@ -9,7 +9,7 @@ import ( "github.com/prometheus/prometheus/storage/metric" "golang.org/x/net/context" - "github.com/weaveworks/cortex/user" + "github.com/weaveworks/common/user" "github.com/weaveworks/cortex/util" ) diff --git a/ruler/ruler.go b/ruler/ruler.go index 1c66d2917dd..f57d0ed1e34 100644 --- a/ruler/ruler.go +++ b/ruler/ruler.go @@ -12,10 +12,10 @@ import ( "github.com/prometheus/prometheus/rules" "golang.org/x/net/context" + "github.com/weaveworks/common/user" "github.com/weaveworks/cortex/chunk" "github.com/weaveworks/cortex/distributor" "github.com/weaveworks/cortex/querier" - "github.com/weaveworks/cortex/user" "github.com/weaveworks/cortex/util" ) diff --git a/ruler/scheduler.go b/ruler/scheduler.go index 357bc073d2a..1760a832c09 100644 --- a/ruler/scheduler.go +++ b/ruler/scheduler.go @@ -5,11 +5,13 @@ import ( "time" "github.com/jonboulle/clockwork" + "golang.org/x/net/context" + "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/common/log" "github.com/prometheus/prometheus/rules" - "github.com/weaveworks/scope/common/instrument" - "golang.org/x/net/context" + + "github.com/weaveworks/common/instrument" ) const ( diff --git a/server/server.go b/server/server.go index d2b54738659..1963db3bd78 100644 --- a/server/server.go +++ b/server/server.go @@ -14,14 +14,15 @@ import ( "github.com/mwitkow/go-grpc-middleware" "github.com/opentracing-contrib/go-stdlib/nethttp" "github.com/opentracing/opentracing-go" - "github.com/weaveworks/scope/common/middleware" "google.golang.org/grpc" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/common/log" + "github.com/weaveworks/common/httpgrpc" + "github.com/weaveworks/common/middleware" + "github.com/weaveworks/cortex/ring" - cortex_grpc_middleware "github.com/weaveworks/cortex/util/middleware" ) var ( @@ -69,13 +70,15 @@ func New(cfg Config, r *ring.Ring) *Server { grpcServer := grpc.NewServer( grpc.UnaryInterceptor(grpc_middleware.ChainUnaryServer( - cortex_grpc_middleware.ServerLoggingInterceptor(cfg.LogSuccess), - cortex_grpc_middleware.ServerInstrumentInterceptor(requestDuration), + middleware.ServerLoggingInterceptor(cfg.LogSuccess), + middleware.ServerInstrumentInterceptor(requestDuration), otgrpc.OpenTracingServerInterceptor(opentracing.GlobalTracer()), - cortex_grpc_middleware.ServerUserHeaderInterceptor, + middleware.ServerUserHeaderInterceptor, )), ) + httpgrpc.RegisterHTTPServer(grpcServer, httpgrpc.NewServer(router)) + return &Server{ cfg: cfg, HTTP: router, diff --git a/util/http.go b/util/http.go index ba95998a485..cbb0fc2fb9f 100644 --- a/util/http.go +++ b/util/http.go @@ -11,12 +11,12 @@ import ( "github.com/prometheus/common/log" "golang.org/x/net/context" - "github.com/weaveworks/cortex/user" + "github.com/weaveworks/common/user" ) // ParseProtoRequest parses a proto from the body of a http request. func ParseProtoRequest(w http.ResponseWriter, r *http.Request, req proto.Message, compressed bool) (ctx context.Context, abort bool) { - userID := r.Header.Get(user.UserIDHeaderName) + userID := r.Header.Get(user.OrgIDHeaderName) if userID == "" { http.Error(w, "", http.StatusUnauthorized) return nil, true diff --git a/vendor/github.com/sercand/kuberesolver/balancer.go b/vendor/github.com/sercand/kuberesolver/balancer.go new file mode 100644 index 00000000000..d03162de067 --- /dev/null +++ b/vendor/github.com/sercand/kuberesolver/balancer.go @@ -0,0 +1,135 @@ +package kuberesolver + +import ( + "errors" + "fmt" + "net/url" + "strconv" + "strings" + + "google.golang.org/grpc" + "google.golang.org/grpc/grpclog" + "google.golang.org/grpc/naming" +) + +type Balancer struct { + Namespace string + client *k8sClient + resolvers []*kubeResolver +} + +type TargetUrlType int32 + +const ( + TargetTypeDNS TargetUrlType = 0 + TargetTypeKubernetes TargetUrlType = 1 + kubernetesSchema = "kubernetes" + dnsSchema = "dns" +) + +type targetInfo struct { + urlType TargetUrlType + target string + port string + resolveByPortName bool + useFirstPort bool +} + +func parseTarget(target string) (targetInfo, error) { + u, err := url.Parse(target) + if err != nil { + return targetInfo{}, err + } + ti := targetInfo{} + if u.Scheme == kubernetesSchema { + ti.urlType = TargetTypeKubernetes + spl := strings.Split(u.Host, ":") + if len(spl) == 2 { + ti.target = spl[0] + ti.port = spl[1] + ti.useFirstPort = false + if _, err := strconv.Atoi(ti.port); err != nil { + ti.resolveByPortName = true + } else { + ti.resolveByPortName = false + } + } else { + ti.target = spl[0] + ti.useFirstPort = true + } + } else if u.Scheme == dnsSchema { + ti.urlType = TargetTypeDNS + ti.target = u.Host + } else { + ti.urlType = TargetTypeDNS + ti.target = target + } + return ti, nil +} + +//Resolver returns Resolver for grpc +func (b *Balancer) Resolver() naming.Resolver { + return newResolver(b.client, b.Namespace) +} + +//DialOption returns grpc.DialOption with RoundRobin balancer and resolver +func (b *Balancer) DialOption() grpc.DialOption { + rs := newResolver(b.client, b.Namespace) + return grpc.WithBalancer(grpc.RoundRobin(rs)) +} + +// Dial calls grpc.Dial, also parses target and uses load balancer if necessary +func (b *Balancer) Dial(target string, opts ...grpc.DialOption) (*grpc.ClientConn, error) { + pt, err := parseTarget(target) + if err != nil { + return nil, err + } + switch pt.urlType { + case TargetTypeKubernetes: + if b.client == nil { + return nil, errors.New("application is not running inside kubernetes") + } + grpclog.Printf("kuberesolver: using kubernetes resolver target=%s", pt.target) + rs := newResolver(b.client, b.Namespace) + b.resolvers = append(b.resolvers, rs) + opts := append(opts, grpc.WithBalancer(grpc.RoundRobin(rs))) + return grpc.Dial(target, opts...) + case TargetTypeDNS: + return grpc.Dial(pt.target, opts...) + default: + return nil, errors.New("Unknown target type") + } +} + +func (b *Balancer) Healthy() error { + for _, r := range b.resolvers { + if r.watcher != nil { + if len(r.watcher.endpoints) == 0 { + return fmt.Errorf("target does not have endpoints") + } + } + } + return nil +} + +// IsInCluster returns true if application is running inside kubernetes cluster +func (b *Balancer) IsInCluster() bool { + return b.client != nil +} + +// New creates a Balancer with "default" namespace +func New() *Balancer { + return NewWithNamespace("default") +} + +// NewWithNamespace creates a Balancer with given namespace. +func NewWithNamespace(namespace string) *Balancer { + client, err := newInClusterClient() + if err != nil { + grpclog.Printf("kuberesolver: application is not running inside kubernetes") + } + return &Balancer{ + Namespace: namespace, + client: client, + } +} diff --git a/vendor/github.com/sercand/kuberesolver/kubernetes.go b/vendor/github.com/sercand/kuberesolver/kubernetes.go new file mode 100644 index 00000000000..737880ee4aa --- /dev/null +++ b/vendor/github.com/sercand/kuberesolver/kubernetes.go @@ -0,0 +1,71 @@ +package kuberesolver + +import ( + "crypto/tls" + "crypto/x509" + "fmt" + "io/ioutil" + "net" + "net/http" + "os" + "time" +) + +const ( + serviceAccountToken = "/var/run/secrets/kubernetes.io/serviceaccount/token" + serviceAccountCACert = "/var/run/secrets/kubernetes.io/serviceaccount/ca.crt" +) + +type k8sClient struct { + host string + token string + httpClient *http.Client +} + +func (kc *k8sClient) getRequest(url string) (*http.Request, error) { + req, err := http.NewRequest("GET", url, nil) + if err != nil { + return nil, err + } + if len(kc.token) > 0 { + req.Header.Set("Authorization", "Bearer "+kc.token) + } + return req, nil +} + +func (kc *k8sClient) Do(req *http.Request) (*http.Response, error) { + return kc.httpClient.Do(req) +} + +/* +KUBERNETES_SERVICE_PORT=443 +KUBERNETES_SERVICE_PORT_HTTPS=443 +KUBERNETES_SERVICE_HOST=10.0.0.1 +*/ +func newInClusterClient() (*k8sClient, error) { + host, port := os.Getenv("KUBERNETES_SERVICE_HOST"), os.Getenv("KUBERNETES_SERVICE_PORT") + if len(host) == 0 || len(port) == 0 { + return nil, fmt.Errorf("unable to load in-cluster configuration, KUBERNETES_SERVICE_HOST and KUBERNETES_SERVICE_PORT must be defined") + } + token, err := ioutil.ReadFile(serviceAccountToken) + if err != nil { + return nil, err + } + ca, err := ioutil.ReadFile(serviceAccountCACert) + if err != nil { + return nil, err + } + certPool := x509.NewCertPool() + certPool.AppendCertsFromPEM(ca) + transport := &http.Transport{TLSClientConfig: &tls.Config{ + MinVersion: tls.VersionTLS10, + RootCAs: certPool, + }} + httpClient := &http.Client{Transport: transport, Timeout: time.Nanosecond * 0} + + return &k8sClient{ + host: "https://" + net.JoinHostPort(host, port), + token: string(token), + httpClient: httpClient, + }, nil +} diff --git a/vendor/github.com/sercand/kuberesolver/models.go b/vendor/github.com/sercand/kuberesolver/models.go new file mode 100644 index 00000000000..3cf13fa6eea --- /dev/null +++ b/vendor/github.com/sercand/kuberesolver/models.go @@ -0,0 +1,42 @@ +package kuberesolver + +type EventType string + +const ( + Added EventType = "ADDED" + Modified EventType = "MODIFIED" + Deleted EventType = "DELETED" + Error EventType = "ERROR" +) + +// Event represents a single event to a watched resource. +type Event struct { + Type EventType `json:"type"` + Object Endpoints `json:"object"` +} + +type Endpoints struct { + Kind string `json:"kind"` + ApiVersion string `json:"apiVersion"` + Metadata Metadata `json:"metadata"` + Subsets []Subset `json:"subsets"` +} + +type Metadata struct { + Name string `json:"name"` + ResourceVersion string `json:"resourceVersion"` +} + +type Subset struct { + Addresses []Address `json:"addresses"` + Ports []Port `json:"ports"` +} + +type Address struct { + IP string `json:"ip"` +} + +type Port struct { + Name string `json:"name"` + Port int `json:"port"` +} diff --git a/vendor/github.com/sercand/kuberesolver/resolver.go b/vendor/github.com/sercand/kuberesolver/resolver.go new file mode 100644 index 00000000000..f591c62714c --- /dev/null +++ b/vendor/github.com/sercand/kuberesolver/resolver.go @@ -0,0 +1,84 @@ +package kuberesolver + +import ( + "fmt" + "net/http" + "net/url" + "time" + + "google.golang.org/grpc/grpclog" + "google.golang.org/grpc/naming" +) + +// kubeResolver resolves service names using Kubernetes endpoints. +type kubeResolver struct { + k8sClient *k8sClient + namespace string + watcher *watcher +} + +// NewResolver returns a new Kubernetes resolver. +func newResolver(client *k8sClient, namespace string) *kubeResolver { + if namespace == "" { + namespace = "default" + } + return &kubeResolver{client, namespace, nil} +} + +// Resolve creates a Kubernetes watcher for the named target. +func (r *kubeResolver) Resolve(target string) (naming.Watcher, error) { + pt, err := parseTarget(target) + if err != nil { + return nil, err + } + resultChan := make(chan watchResult) + stopCh := make(chan struct{}) + wtarget := pt.target + go until(func() { + err := r.watch(wtarget, stopCh, resultChan) + if err != nil { + grpclog.Printf("kuberesolver: watching ended with error='%v', will reconnect again", err) + } + }, time.Second, stopCh) + + r.watcher = &watcher{ + target: pt, + endpoints: make(map[string]interface{}), + stopCh: stopCh, + result: resultChan, + } + return r.watcher, nil +} + +func (r *kubeResolver) watch(target string, stopCh <-chan struct{}, resultCh chan<- watchResult) error { + u, err := url.Parse(fmt.Sprintf("%s/api/v1/watch/namespaces/%s/endpoints/%s", + r.k8sClient.host, r.namespace, target)) + if err != nil { + return err + } + req, err := r.k8sClient.getRequest(u.String()) + if err != nil { + return err + } + resp, err := r.k8sClient.Do(req) + if err != nil { + return err + } + if resp.StatusCode != http.StatusOK { + defer resp.Body.Close() + return fmt.Errorf("invalid response code %d", resp.StatusCode) + } + sw := newStreamWatcher(resp.Body) + for { + select { + case <-stopCh: + return nil + case up, more := <-sw.ResultChan(): + if more { + resultCh <- watchResult{err: nil, ep: &up} + } else { + return nil + } + } + } +} diff --git a/vendor/github.com/sercand/kuberesolver/stream.go b/vendor/github.com/sercand/kuberesolver/stream.go new file mode 100644 index 00000000000..51aab49b5db --- /dev/null +++ b/vendor/github.com/sercand/kuberesolver/stream.go @@ -0,0 +1,105 @@ +package kuberesolver + +import ( + "encoding/json" + "fmt" + "io" + "sync" + + "google.golang.org/grpc/grpclog" +) + +// Interface can be implemented by anything that knows how to watch and report changes. +type watchInterface interface { + // Stops watching. Will close the channel returned by ResultChan(). Releases + // any resources used by the watch. + Stop() + + // Returns a chan which will receive all the events. If an error occurs + // or Stop() is called, this channel will be closed, in which case the + // watch should be completely cleaned up. + ResultChan() <-chan Event +} + +// StreamWatcher turns any stream for which you can write a Decoder interface +// into a watch.Interface. +type streamWatcher struct { + result chan Event + r io.ReadCloser + decoder *json.Decoder + sync.Mutex + stopped bool +} + +// NewStreamWatcher creates a StreamWatcher from the given io.ReadClosers. +func newStreamWatcher(r io.ReadCloser) watchInterface { + sw := &streamWatcher{ + r: r, + decoder: json.NewDecoder(r), + result: make(chan Event), + } + go sw.receive() + return sw +} + +// ResultChan implements Interface. +func (sw *streamWatcher) ResultChan() <-chan Event { + return sw.result +} + +// Stop implements Interface. +func (sw *streamWatcher) Stop() { + sw.Lock() + defer sw.Unlock() + if !sw.stopped { + sw.stopped = true + sw.r.Close() + } +} + +// stopping returns true if Stop() was called previously. +func (sw *streamWatcher) stopping() bool { + sw.Lock() + defer sw.Unlock() + return sw.stopped +} + +// receive reads result from the decoder in a loop and sends down the result channel. +func (sw *streamWatcher) receive() { + defer close(sw.result) + defer sw.Stop() + for { + obj, err := sw.Decode() + if err != nil { + // Ignore expected error. + if sw.stopping() { + return + } + switch err { + case io.EOF: + // watch closed normally + case io.ErrUnexpectedEOF: + grpclog.Printf("kuberesolver: Unexpected EOF during watch stream event decoding: %v", err) + default: + grpclog.Printf("kuberesolver: Unable to decode an event from the watch stream: %v", err) + } + return + } + sw.result <- obj + } +} + +// Decode blocks until it can return the next object in the writer. Returns an error +// if the writer is closed or an object can't be decoded. +func (sw *streamWatcher) Decode() (Event, error) { + var got Event + if err := sw.decoder.Decode(&got); err != nil { + return Event{}, err + } + switch got.Type { + case Added, Modified, Deleted, Error: + return got, nil + default: + return Event{}, fmt.Errorf("got invalid watch event type: %v", got.Type) + } +} diff --git a/vendor/github.com/sercand/kuberesolver/util.go b/vendor/github.com/sercand/kuberesolver/util.go new file mode 100644 index 00000000000..6ad5e3cbbf4 --- /dev/null +++ b/vendor/github.com/sercand/kuberesolver/util.go @@ -0,0 +1,43 @@ +package kuberesolver + +import ( + "fmt" + "runtime" + "time" + + "google.golang.org/grpc/grpclog" +) + +func until(f func(), period time.Duration, stopCh <-chan struct{}) { + select { + case <-stopCh: + return + default: + } + for { + func() { + defer handleCrash() + f() + }() + select { + case <-stopCh: + return + case <-time.After(period): + } + } +} + +// HandleCrash simply catches a crash and logs an error. Meant to be called via defer. +func handleCrash() { + if r := recover(); r != nil { + callers := "" + for i := 0; true; i++ { + _, file, line, ok := runtime.Caller(i) + if !ok { + break + } + callers = callers + fmt.Sprintf("%v:%v\n", file, line) + } + grpclog.Printf("kuberesolver: recovered from panic: %#v (%v)\n%v", r, r, callers) + } +} diff --git a/vendor/github.com/sercand/kuberesolver/watcher.go b/vendor/github.com/sercand/kuberesolver/watcher.go new file mode 100644 index 00000000000..42437bd482b --- /dev/null +++ b/vendor/github.com/sercand/kuberesolver/watcher.go @@ -0,0 +1,95 @@ +package kuberesolver + +import ( + "net" + "strconv" + "sync" + + "google.golang.org/grpc/grpclog" + "google.golang.org/grpc/naming" +) + +type watchResult struct { + ep *Event + err error +} + +// A Watcher provides name resolution updates from Kubernetes endpoints +// identified by name. +type watcher struct { + target targetInfo + endpoints map[string]interface{} + stopCh chan struct{} + result chan watchResult + sync.Mutex + stopped bool +} + +// Close closes the watcher, cleaning up any open connections. +func (w *watcher) Close() { + close(w.stopCh) +} + +// Next updates the endpoints for the name being watched. +func (w *watcher) Next() ([]*naming.Update, error) { + updates := make([]*naming.Update, 0) + updatedEndpoints := make(map[string]interface{}) + var ep Event + + select { + case <-w.stopCh: + w.Lock() + if !w.stopped { + w.stopped = true + } + w.Unlock() + return updates, nil + case r := <-w.result: + if r.err == nil { + ep = *r.ep + } else { + return updates, r.err + } + } + for _, subset := range ep.Object.Subsets { + port := "" + if w.target.useFirstPort { + port = strconv.Itoa(subset.Ports[0].Port) + } else if w.target.resolveByPortName { + for _, p := range subset.Ports { + if p.Name == w.target.port { + port = strconv.Itoa(p.Port) + break + } + } + } else { + port = w.target.port + } + + if len(port) == 0 { + port = strconv.Itoa(subset.Ports[0].Port) + } + for _, address := range subset.Addresses { + endpoint := net.JoinHostPort(address.IP, port) + updatedEndpoints[endpoint] = nil + } + } + + // Create updates to add new endpoints. + for addr, md := range updatedEndpoints { + if _, ok := w.endpoints[addr]; !ok { + updates = append(updates, &naming.Update{naming.Add, addr, md}) + grpclog.Printf("kuberesolver: %s ADDED to %s", addr, w.target.target) + } + } + + // Create updates to delete old endpoints. + for addr := range w.endpoints { + if _, ok := updatedEndpoints[addr]; !ok { + updates = append(updates, &naming.Update{naming.Delete, addr, nil}) + grpclog.Printf("kuberesolver: %s DELETED from %s", addr, w.target.target) + } + } + w.endpoints = updatedEndpoints + return updates, nil +} diff --git a/vendor/github.com/weaveworks/common/mtime/LICENSE b/vendor/github.com/weaveworks/common/LICENSE similarity index 100% rename from vendor/github.com/weaveworks/common/mtime/LICENSE rename to vendor/github.com/weaveworks/common/LICENSE diff --git a/vendor/github.com/weaveworks/common/backoff/backoff.go b/vendor/github.com/weaveworks/common/backoff/backoff.go new file mode 100644 index 00000000000..94f5ad6fbf6 --- /dev/null +++ b/vendor/github.com/weaveworks/common/backoff/backoff.go @@ -0,0 +1,96 @@ +package backoff + +import ( + "time" + + log "github.com/Sirupsen/logrus" +) + +type backoff struct { + f func() (bool, error) + quit, done chan struct{} + msg string + initialBackoff, maxBackoff time.Duration +} + +// Interface does f in a loop, sleeping for initialBackoff between +// each iterations. If it hits an error, it exponentially backs +// off to maxBackoff. Backoff will log when it backs off, but +// will stop logging when it reaches maxBackoff. It will also +// log on first success. +type Interface interface { + Start() + Stop() + SetInitialBackoff(time.Duration) + SetMaxBackoff(time.Duration) +} + +// New makes a new Interface +func New(f func() (bool, error), msg string) Interface { + return &backoff{ + f: f, + quit: make(chan struct{}), + done: make(chan struct{}), + msg: msg, + initialBackoff: 10 * time.Second, + maxBackoff: 60 * time.Second, + } +} + +func (b *backoff) SetInitialBackoff(d time.Duration) { + b.initialBackoff = d +} + +func (b *backoff) SetMaxBackoff(d time.Duration) { + b.maxBackoff = d +} + +// Stop the backoff, and waits for it to stop. +func (b *backoff) Stop() { + close(b.quit) + <-b.done +} + +// Start the backoff. Can only be called once. +func (b *backoff) Start() { + defer close(b.done) + backoff := b.initialBackoff + shouldLog := true + + for { + done, err := b.f() + if done { + return + } + + if err != nil { + backoff *= 2 + if backoff > b.maxBackoff { + backoff = b.maxBackoff + } + } else if backoff > b.initialBackoff { + backoff = b.initialBackoff + shouldLog = true + } + + if shouldLog { + if err != nil { + log.Warnf("Error %s, backing off %s: %s", + b.msg, backoff, err) + } else { + log.Infof("Success %s", b.msg) + } + } + + if backoff >= b.maxBackoff || err == nil { + shouldLog = false + } + + select { + case <-time.After(backoff): + case <-b.quit: + return + } + } + +} diff --git a/vendor/github.com/weaveworks/common/exec/exec.go b/vendor/github.com/weaveworks/common/exec/exec.go new file mode 100644 index 00000000000..fc0b27ad1a7 --- /dev/null +++ b/vendor/github.com/weaveworks/common/exec/exec.go @@ -0,0 +1,30 @@ +package exec + +import ( + "io" + "os/exec" +) + +// Cmd is a hook for mocking +type Cmd interface { + StdoutPipe() (io.ReadCloser, error) + StderrPipe() (io.ReadCloser, error) + Start() error + Wait() error + Kill() error + Output() ([]byte, error) + Run() error +} + +// Command is a hook for mocking +var Command = func(name string, args ...string) Cmd { + return &realCmd{exec.Command(name, args...)} +} + +type realCmd struct { + *exec.Cmd +} + +func (c *realCmd) Kill() error { + return c.Cmd.Process.Kill() +} diff --git a/vendor/github.com/weaveworks/common/fs/fs.go b/vendor/github.com/weaveworks/common/fs/fs.go new file mode 100644 index 00000000000..6b9eb41629e --- /dev/null +++ b/vendor/github.com/weaveworks/common/fs/fs.go @@ -0,0 +1,94 @@ +package fs + +import ( + "io" + "io/ioutil" + "os" + "syscall" +) + +// Interface is the filesystem interface type. +type Interface interface { + ReadDir(string) ([]os.FileInfo, error) + ReadDirNames(string) ([]string, error) + ReadFile(string) ([]byte, error) + Lstat(string, *syscall.Stat_t) error + Stat(string, *syscall.Stat_t) error + Open(string) (io.ReadWriteCloser, error) +} + +type realFS struct{} + +// FS is the way you should access the filesystem. +var fs Interface = realFS{} + +func (realFS) ReadDir(path string) ([]os.FileInfo, error) { + return ioutil.ReadDir(path) +} + +func (realFS) ReadDirNames(path string) ([]string, error) { + fh, err := os.Open(path) + if err != nil { + return nil, err + } + defer fh.Close() + return fh.Readdirnames(-1) +} + +func (realFS) ReadFile(path string) ([]byte, error) { + return ioutil.ReadFile(path) +} + +func (realFS) Lstat(path string, stat *syscall.Stat_t) error { + return syscall.Lstat(path, stat) +} + +func (realFS) Stat(path string, stat *syscall.Stat_t) error { + return syscall.Stat(path, stat) +} + +func (realFS) Open(path string) (io.ReadWriteCloser, error) { + return os.Open(path) +} + +// trampolines here to allow users to do fs.ReadDir etc + +// ReadDir see ioutil.ReadDir +func ReadDir(path string) ([]os.FileInfo, error) { + return fs.ReadDir(path) +} + +// ReadDirNames see os.File.ReadDirNames +func ReadDirNames(path string) ([]string, error) { + return fs.ReadDirNames(path) +} + +// ReadFile see ioutil.ReadFile +func ReadFile(path string) ([]byte, error) { + return fs.ReadFile(path) +} + +// Lstat see syscall.Lstat +func Lstat(path string, stat *syscall.Stat_t) error { + return fs.Lstat(path, stat) +} + +// Stat see syscall.Stat +func Stat(path string, stat *syscall.Stat_t) error { + return fs.Stat(path, stat) +} + +// Open see os.Open +func Open(path string) (io.ReadWriteCloser, error) { + return fs.Open(path) +} + +// Mock is used to switch out the filesystem for a mock. +func Mock(mock Interface) { + fs = mock +} + +// Restore puts back the real filesystem. +func Restore() { + fs = realFS{} +} diff --git a/vendor/github.com/weaveworks/common/httpgrpc/httpgrpc.go b/vendor/github.com/weaveworks/common/httpgrpc/httpgrpc.go new file mode 100644 index 00000000000..7b7d23c7181 --- /dev/null +++ b/vendor/github.com/weaveworks/common/httpgrpc/httpgrpc.go @@ -0,0 +1,133 @@ +package httpgrpc + +import ( + "bytes" + "fmt" + "io/ioutil" + "net" + "net/http" + "net/http/httptest" + "strings" + + "github.com/grpc-ecosystem/grpc-opentracing/go/otgrpc" + "github.com/mwitkow/go-grpc-middleware" + "github.com/opentracing/opentracing-go" + "github.com/sercand/kuberesolver" + "golang.org/x/net/context" + "google.golang.org/grpc" + + "github.com/weaveworks/common/middleware" +) + +// Server implements HTTPServer. HTTPServer is a generated interface that gRPC +// servers must implement. +type Server struct { + handler http.Handler +} + +// NewServer makes a new Server. +func NewServer(handler http.Handler) *Server { + return &Server{ + handler: handler, + } +} + +// Handle implements HTTPServer. +func (s Server) Handle(ctx context.Context, r *HTTPRequest) (*HTTPResponse, error) { + req, err := http.NewRequest(r.Method, r.Url, ioutil.NopCloser(bytes.NewReader(r.Body))) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + toHeader(r.Headers, req.Header) + recorder := httptest.NewRecorder() + s.handler.ServeHTTP(recorder, req) + resp := &HTTPResponse{ + Code: int32(recorder.Code), + Headers: fromHeader(recorder.Header()), + Body: recorder.Body.Bytes(), + } + return resp, nil +} + +// Client is a http.Handler that forwards the request over gRPC. +type Client struct { + client HTTPClient + conn *grpc.ClientConn +} + +// NewClient makes a new Client, given a kubernetes service address. Expects +// an address of the form .: +func NewClient(address string) (*Client, error) { + host, port, err := net.SplitHostPort(address) + if err != nil { + return nil, err + } + parts := strings.SplitN(host, ".", 2) + service, namespace := parts[0], "default" + if len(parts) == 2 { + namespace = parts[1] + } + balancer := kuberesolver.NewWithNamespace(namespace) + conn, err := grpc.Dial( + fmt.Sprintf("kubernetes://%s:%s", service, port), + balancer.DialOption(), + grpc.WithInsecure(), + grpc.WithUnaryInterceptor(grpc_middleware.ChainUnaryClient( + otgrpc.OpenTracingClientInterceptor(opentracing.GlobalTracer()), + middleware.ClientUserHeaderInterceptor, + )), + ) + if err != nil { + return nil, err + } + return &Client{ + client: NewHTTPClient(conn), + conn: conn, + }, nil +} + +// ServeHTTP implements http.Handler +func (c *Client) ServeHTTP(w http.ResponseWriter, r *http.Request) { + body, err := ioutil.ReadAll(r.Body) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + req := &HTTPRequest{ + Method: r.Method, + Url: r.URL.String(), + Body: body, + Headers: fromHeader(r.Header), + } + + resp, err := c.client.Handle(r.Context(), req) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + toHeader(resp.Headers, w.Header()) + w.WriteHeader(int(resp.Code)) + if _, err := w.Write(resp.Body); err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } +} + +func toHeader(hs []*Header, header http.Header) { + for _, h := range hs { + header[h.Key] = h.Values + } +} + +func fromHeader(hs http.Header) []*Header { + result := make([]*Header, 0, len(hs)) + for k, vs := range hs { + result = append(result, &Header{ + Key: k, + Values: vs, + }) + } + return result +} diff --git a/vendor/github.com/weaveworks/common/httpgrpc/httpgrpc.pb.go b/vendor/github.com/weaveworks/common/httpgrpc/httpgrpc.pb.go new file mode 100644 index 00000000000..02b2a47869b --- /dev/null +++ b/vendor/github.com/weaveworks/common/httpgrpc/httpgrpc.pb.go @@ -0,0 +1,231 @@ +// Code generated by protoc-gen-go. +// source: httpgrpc.proto +// DO NOT EDIT! + +/* +Package httpgrpc is a generated protocol buffer package. + +It is generated from these files: + httpgrpc.proto + +It has these top-level messages: + HTTPRequest + HTTPResponse + Header +*/ +package httpgrpc + +import proto "github.com/golang/protobuf/proto" +import fmt "fmt" +import math "math" + +import ( + context "golang.org/x/net/context" + grpc "google.golang.org/grpc" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package + +type HTTPRequest struct { + Method string `protobuf:"bytes,1,opt,name=method" json:"method,omitempty"` + Url string `protobuf:"bytes,2,opt,name=url" json:"url,omitempty"` + Headers []*Header `protobuf:"bytes,3,rep,name=headers" json:"headers,omitempty"` + Body []byte `protobuf:"bytes,4,opt,name=body,proto3" json:"body,omitempty"` +} + +func (m *HTTPRequest) Reset() { *m = HTTPRequest{} } +func (m *HTTPRequest) String() string { return proto.CompactTextString(m) } +func (*HTTPRequest) ProtoMessage() {} +func (*HTTPRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} } + +func (m *HTTPRequest) GetMethod() string { + if m != nil { + return m.Method + } + return "" +} + +func (m *HTTPRequest) GetUrl() string { + if m != nil { + return m.Url + } + return "" +} + +func (m *HTTPRequest) GetHeaders() []*Header { + if m != nil { + return m.Headers + } + return nil +} + +func (m *HTTPRequest) GetBody() []byte { + if m != nil { + return m.Body + } + return nil +} + +type HTTPResponse struct { + Code int32 `protobuf:"varint,1,opt,name=Code" json:"Code,omitempty"` + Headers []*Header `protobuf:"bytes,2,rep,name=headers" json:"headers,omitempty"` + Body []byte `protobuf:"bytes,3,opt,name=body,proto3" json:"body,omitempty"` +} + +func (m *HTTPResponse) Reset() { *m = HTTPResponse{} } +func (m *HTTPResponse) String() string { return proto.CompactTextString(m) } +func (*HTTPResponse) ProtoMessage() {} +func (*HTTPResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} } + +func (m *HTTPResponse) GetCode() int32 { + if m != nil { + return m.Code + } + return 0 +} + +func (m *HTTPResponse) GetHeaders() []*Header { + if m != nil { + return m.Headers + } + return nil +} + +func (m *HTTPResponse) GetBody() []byte { + if m != nil { + return m.Body + } + return nil +} + +type Header struct { + Key string `protobuf:"bytes,1,opt,name=key" json:"key,omitempty"` + Values []string `protobuf:"bytes,2,rep,name=values" json:"values,omitempty"` +} + +func (m *Header) Reset() { *m = Header{} } +func (m *Header) String() string { return proto.CompactTextString(m) } +func (*Header) ProtoMessage() {} +func (*Header) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{2} } + +func (m *Header) GetKey() string { + if m != nil { + return m.Key + } + return "" +} + +func (m *Header) GetValues() []string { + if m != nil { + return m.Values + } + return nil +} + +func init() { + proto.RegisterType((*HTTPRequest)(nil), "httpgrpc.HTTPRequest") + proto.RegisterType((*HTTPResponse)(nil), "httpgrpc.HTTPResponse") + proto.RegisterType((*Header)(nil), "httpgrpc.Header") +} + +// Reference imports to suppress errors if they are not otherwise used. +var _ context.Context +var _ grpc.ClientConn + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +const _ = grpc.SupportPackageIsVersion4 + +// Client API for HTTP service + +type HTTPClient interface { + Handle(ctx context.Context, in *HTTPRequest, opts ...grpc.CallOption) (*HTTPResponse, error) +} + +type hTTPClient struct { + cc *grpc.ClientConn +} + +func NewHTTPClient(cc *grpc.ClientConn) HTTPClient { + return &hTTPClient{cc} +} + +func (c *hTTPClient) Handle(ctx context.Context, in *HTTPRequest, opts ...grpc.CallOption) (*HTTPResponse, error) { + out := new(HTTPResponse) + err := grpc.Invoke(ctx, "/httpgrpc.HTTP/Handle", in, out, c.cc, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// Server API for HTTP service + +type HTTPServer interface { + Handle(context.Context, *HTTPRequest) (*HTTPResponse, error) +} + +func RegisterHTTPServer(s *grpc.Server, srv HTTPServer) { + s.RegisterService(&_HTTP_serviceDesc, srv) +} + +func _HTTP_Handle_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(HTTPRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(HTTPServer).Handle(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/httpgrpc.HTTP/Handle", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(HTTPServer).Handle(ctx, req.(*HTTPRequest)) + } + return interceptor(ctx, in, info, handler) +} + +var _HTTP_serviceDesc = grpc.ServiceDesc{ + ServiceName: "httpgrpc.HTTP", + HandlerType: (*HTTPServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "Handle", + Handler: _HTTP_Handle_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "httpgrpc.proto", +} + +func init() { proto.RegisterFile("httpgrpc.proto", fileDescriptor0) } + +var fileDescriptor0 = []byte{ + // 231 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x8c, 0x90, 0x31, 0x4f, 0xc3, 0x30, + 0x10, 0x85, 0x49, 0x1d, 0x0c, 0xbd, 0x56, 0xa8, 0x3a, 0x89, 0xca, 0x62, 0x8a, 0x32, 0x45, 0x0c, + 0x1d, 0xc2, 0xc4, 0x88, 0x58, 0x32, 0x22, 0xab, 0x7f, 0x20, 0xc1, 0x27, 0x22, 0x11, 0x6a, 0x63, + 0x3b, 0xa0, 0xfe, 0x7b, 0x64, 0x3b, 0x85, 0x88, 0xa9, 0xdb, 0x7b, 0xe7, 0x27, 0x7f, 0xf7, 0x0e, + 0x6e, 0x7a, 0xef, 0xcd, 0x9b, 0x35, 0xaf, 0x3b, 0x63, 0xb5, 0xd7, 0x78, 0x7d, 0xf2, 0xe5, 0x37, + 0xac, 0x9a, 0xfd, 0xfe, 0x45, 0xd2, 0xe7, 0x48, 0xce, 0xe3, 0x16, 0xf8, 0x07, 0xf9, 0x5e, 0x2b, + 0x91, 0x15, 0x59, 0xb5, 0x94, 0x93, 0xc3, 0x0d, 0xb0, 0xd1, 0x0e, 0x62, 0x11, 0x87, 0x41, 0xe2, + 0x3d, 0x5c, 0xf5, 0xd4, 0x2a, 0xb2, 0x4e, 0xb0, 0x82, 0x55, 0xab, 0x7a, 0xb3, 0xfb, 0x85, 0x34, + 0xf1, 0x41, 0x9e, 0x02, 0x88, 0x90, 0x77, 0x5a, 0x1d, 0x45, 0x5e, 0x64, 0xd5, 0x5a, 0x46, 0x5d, + 0x76, 0xb0, 0x4e, 0x60, 0x67, 0xf4, 0xc1, 0x51, 0xc8, 0x3c, 0x6b, 0x45, 0x91, 0x7b, 0x29, 0xa3, + 0x9e, 0x33, 0x16, 0xe7, 0x32, 0xd8, 0x8c, 0x51, 0x03, 0x4f, 0xb1, 0xb0, 0xff, 0x3b, 0x1d, 0xa7, + 0x52, 0x41, 0x86, 0xa6, 0x5f, 0xed, 0x30, 0x52, 0xfa, 0x7a, 0x29, 0x27, 0x57, 0x3f, 0x41, 0x1e, + 0xf6, 0xc2, 0x47, 0xe0, 0x4d, 0x7b, 0x50, 0x03, 0xe1, 0xed, 0x0c, 0xfa, 0x77, 0xaa, 0xbb, 0xed, + 0xff, 0x71, 0x2a, 0x52, 0x5e, 0x74, 0x3c, 0x1e, 0xf9, 0xe1, 0x27, 0x00, 0x00, 0xff, 0xff, 0x47, + 0x4e, 0x55, 0x95, 0x76, 0x01, 0x00, 0x00, +} diff --git a/vendor/github.com/weaveworks/scope/common/instrument/instrument.go b/vendor/github.com/weaveworks/common/instrument/instrument.go similarity index 100% rename from vendor/github.com/weaveworks/scope/common/instrument/instrument.go rename to vendor/github.com/weaveworks/common/instrument/instrument.go diff --git a/vendor/github.com/weaveworks/common/logging/logging.go b/vendor/github.com/weaveworks/common/logging/logging.go new file mode 100644 index 00000000000..296bee12f41 --- /dev/null +++ b/vendor/github.com/weaveworks/common/logging/logging.go @@ -0,0 +1,45 @@ +package logging + +import ( + "bytes" + "fmt" + "os" + "strings" + + log "github.com/Sirupsen/logrus" +) + +// Setup configures logging output to stderr, sets the log level and sets the formatter. +func Setup(logLevel string) error { + log.SetOutput(os.Stderr) + level, err := log.ParseLevel(logLevel) + if err != nil { + return fmt.Errorf("Error parsing log level: %v", err) + } + log.SetLevel(level) + log.SetFormatter(&textFormatter{}) + return nil +} + +type textFormatter struct{} + +// Based off logrus.TextFormatter, which behaves completely +// differently when you don't want colored output +func (f *textFormatter) Format(entry *log.Entry) ([]byte, error) { + b := &bytes.Buffer{} + + levelText := strings.ToUpper(entry.Level.String())[0:4] + timeStamp := entry.Time.Format("2006/01/02 15:04:05.000000") + if len(entry.Data) > 0 { + fmt.Fprintf(b, "%s: %s %-44s ", levelText, timeStamp, entry.Message) + for k, v := range entry.Data { + fmt.Fprintf(b, " %s=%v", k, v) + } + } else { + // No padding when there's no fields + fmt.Fprintf(b, "%s: %s %s", levelText, timeStamp, entry.Message) + } + + b.WriteByte('\n') + return b.Bytes(), nil +} diff --git a/vendor/github.com/weaveworks/common/middleware/errorhandler.go b/vendor/github.com/weaveworks/common/middleware/errorhandler.go new file mode 100644 index 00000000000..0f7ab6141f3 --- /dev/null +++ b/vendor/github.com/weaveworks/common/middleware/errorhandler.go @@ -0,0 +1,94 @@ +package middleware + +import ( + "bufio" + "fmt" + "net" + "net/http" +) + +func copyHeaders(src, dest http.Header) { + for k, v := range src { + dest[k] = v + } +} + +// ErrorHandler lets you call an alternate http handler upon a certain response code. +// Note it will assume a 200 if the wrapped handler does not write anything +type ErrorHandler struct { + Code int + Handler http.Handler +} + +// Wrap implements Middleware +func (e ErrorHandler) Wrap(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + i := newErrorInterceptor(w, e.Code) + next.ServeHTTP(i, r) + if !i.gotCode { + i.WriteHeader(http.StatusOK) + } + if i.intercepted { + e.Handler.ServeHTTP(w, r) + } + }) +} + +// errorInterceptor wraps an underlying ResponseWriter and buffers all header changes, until it knows the return code. +// It then passes everything through, unless the code matches the target code, in which case it will discard everything. +type errorInterceptor struct { + originalWriter http.ResponseWriter + targetCode int + headers http.Header + gotCode bool + intercepted bool +} + +func newErrorInterceptor(w http.ResponseWriter, code int) *errorInterceptor { + i := errorInterceptor{originalWriter: w, targetCode: code} + i.headers = make(http.Header) + copyHeaders(w.Header(), i.headers) + return &i +} + +// Header implements http.ResponseWriter +func (i *errorInterceptor) Header() http.Header { + return i.headers +} + +// WriteHeader implements http.ResponseWriter +func (i *errorInterceptor) WriteHeader(code int) { + if i.gotCode { + panic("errorInterceptor.WriteHeader() called twice") + } + + i.gotCode = true + if code == i.targetCode { + i.intercepted = true + } else { + copyHeaders(i.headers, i.originalWriter.Header()) + i.originalWriter.WriteHeader(code) + } +} + +// Write implements http.ResponseWriter +func (i *errorInterceptor) Write(data []byte) (int, error) { + if !i.gotCode { + i.WriteHeader(http.StatusOK) + } + if !i.intercepted { + return i.originalWriter.Write(data) + } + return len(data), nil +} + +// errorInterceptor also implements net.Hijacker, to let the downstream Handler +// hijack the connection. This is needed, for example, for working with websockets. +func (i *errorInterceptor) Hijack() (net.Conn, *bufio.ReadWriter, error) { + hj, ok := i.originalWriter.(http.Hijacker) + if !ok { + return nil, nil, fmt.Errorf("error interceptor: can't cast original ResponseWriter to Hijacker") + } + i.gotCode = true + return hj.Hijack() +} diff --git a/util/middleware/auth.go b/vendor/github.com/weaveworks/common/middleware/grpc_auth.go similarity index 84% rename from util/middleware/auth.go rename to vendor/github.com/weaveworks/common/middleware/grpc_auth.go index 725ee497747..ac85c693c98 100644 --- a/util/middleware/auth.go +++ b/vendor/github.com/weaveworks/common/middleware/grpc_auth.go @@ -7,7 +7,7 @@ import ( "google.golang.org/grpc" "google.golang.org/grpc/metadata" - "github.com/weaveworks/cortex/user" + "github.com/weaveworks/common/user" ) // ClientUserHeaderInterceptor propagates the user ID from the context to gRPC metadata, which eventually ends up as a HTTP2 header. @@ -19,9 +19,9 @@ func ClientUserHeaderInterceptor(ctx context.Context, method string, req, reply md, ok := metadata.FromContext(ctx) if !ok { - md = metadata.New(map[string]string{user.LowerUserIDHeaderName: userID}) + md = metadata.New(map[string]string{user.LowerOrgIDHeaderName: userID}) } else { - md[user.LowerUserIDHeaderName] = []string{userID} + md[user.LowerOrgIDHeaderName] = []string{userID} } newCtx := metadata.NewContext(ctx, md) @@ -35,7 +35,7 @@ func ServerUserHeaderInterceptor(ctx context.Context, req interface{}, info *grp return nil, fmt.Errorf("no metadata") } - userIDs, ok := md[user.LowerUserIDHeaderName] + userIDs, ok := md[user.LowerOrgIDHeaderName] if !ok || len(userIDs) != 1 { return nil, fmt.Errorf("no user id") } diff --git a/util/middleware/instrumentation.go b/vendor/github.com/weaveworks/common/middleware/grpc_instrumentation.go similarity index 100% rename from util/middleware/instrumentation.go rename to vendor/github.com/weaveworks/common/middleware/grpc_instrumentation.go diff --git a/util/middleware/logging.go b/vendor/github.com/weaveworks/common/middleware/grpc_logging.go similarity index 94% rename from util/middleware/logging.go rename to vendor/github.com/weaveworks/common/middleware/grpc_logging.go index 986bf4de9db..1b3f4ef1f27 100644 --- a/util/middleware/logging.go +++ b/vendor/github.com/weaveworks/common/middleware/grpc_logging.go @@ -3,7 +3,7 @@ package middleware import ( "time" - "github.com/prometheus/common/log" + log "github.com/Sirupsen/logrus" "golang.org/x/net/context" "google.golang.org/grpc" ) diff --git a/vendor/github.com/weaveworks/scope/common/middleware/instrument.go b/vendor/github.com/weaveworks/common/middleware/instrument.go similarity index 81% rename from vendor/github.com/weaveworks/scope/common/middleware/instrument.go rename to vendor/github.com/weaveworks/common/middleware/instrument.go index 676a59f6ab3..6997920a206 100644 --- a/vendor/github.com/weaveworks/scope/common/middleware/instrument.go +++ b/vendor/github.com/weaveworks/common/middleware/instrument.go @@ -19,16 +19,25 @@ type Instrument struct { Duration *prometheus.HistogramVec } -func isWSHandshakeRequest(req *http.Request) bool { - return strings.ToLower(req.Header.Get("Upgrade")) == "websocket" && - strings.ToLower(req.Header.Get("Connection")) == "upgrade" +// IsWSHandshakeRequest returns true if the given request is a websocket handshake request. +func IsWSHandshakeRequest(req *http.Request) bool { + if strings.ToLower(req.Header.Get("Upgrade")) == "websocket" { + // Connection header values can be of form "foo, bar, ..." + parts := strings.Split(strings.ToLower(req.Header.Get("Connection")), ",") + for _, part := range parts { + if strings.TrimSpace(part) == "upgrade" { + return true + } + } + } + return false } // Wrap implements middleware.Interface func (i Instrument) Wrap(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { begin := time.Now() - isWS := strconv.FormatBool(isWSHandshakeRequest(r)) + isWS := strconv.FormatBool(IsWSHandshakeRequest(r)) interceptor := &interceptor{ResponseWriter: w, statusCode: http.StatusOK} route := i.getRouteName(r) next.ServeHTTP(interceptor, r) diff --git a/vendor/github.com/weaveworks/scope/common/middleware/logging.go b/vendor/github.com/weaveworks/common/middleware/logging.go similarity index 71% rename from vendor/github.com/weaveworks/scope/common/middleware/logging.go rename to vendor/github.com/weaveworks/common/middleware/logging.go index 7bda6377fa2..19d29cd84a3 100644 --- a/vendor/github.com/weaveworks/scope/common/middleware/logging.go +++ b/vendor/github.com/weaveworks/common/middleware/logging.go @@ -5,6 +5,7 @@ import ( "fmt" "net" "net/http" + "net/http/httputil" "time" log "github.com/Sirupsen/logrus" @@ -12,7 +13,8 @@ import ( // Log middleware logs http requests type Log struct { - LogSuccess bool // LogSuccess true -> log successful queries; false -> only log failed queries + LogSuccess bool // LogSuccess true -> log successful queries; false -> only log failed queries + LogRequestHeaders bool // LogRequestHeaders true -> dump http headers at debug log level } // Wrap implements Middleware @@ -20,6 +22,15 @@ func (l Log) Wrap(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { begin := time.Now() uri := r.RequestURI // capture the URI before running next, as it may get rewritten + if l.LogRequestHeaders { + // Log headers before running 'next' in case other interceptors change the data. + headers, err := httputil.DumpRequest(r, false) + if err != nil { + log.Warnf("Could not dump request headers: %v", err) + return + } + log.Debugf("Is websocket request: %v\n%s", IsWSHandshakeRequest(r), string(headers)) + } i := &interceptor{ResponseWriter: w, statusCode: http.StatusOK} next.ServeHTTP(i, r) if l.LogSuccess || !(100 <= i.statusCode && i.statusCode < 400) { @@ -31,7 +42,8 @@ func (l Log) Wrap(next http.Handler) http.Handler { // Logging middleware logs each HTTP request method, path, response code and // duration for all HTTP requests. var Logging = Log{ - LogSuccess: true, + LogSuccess: true, + LogRequestHeaders: false, } // LogFailed middleware logs each HTTP request method, path, response code and @@ -45,7 +57,7 @@ var LogFailed = Log{ // want to report on success, i.e. http.StatusOK. // // interceptor also implements net.Hijacker, to let the downstream Handler -// hijack the connection. This is needed by the app-mapper's proxy. +// hijack the connection. This is needed, for example, for working with websockets. type interceptor struct { http.ResponseWriter statusCode int diff --git a/vendor/github.com/weaveworks/scope/common/middleware/middleware.go b/vendor/github.com/weaveworks/common/middleware/middleware.go similarity index 100% rename from vendor/github.com/weaveworks/scope/common/middleware/middleware.go rename to vendor/github.com/weaveworks/common/middleware/middleware.go diff --git a/vendor/github.com/weaveworks/scope/common/middleware/path_rewrite.go b/vendor/github.com/weaveworks/common/middleware/path_rewrite.go similarity index 100% rename from vendor/github.com/weaveworks/scope/common/middleware/path_rewrite.go rename to vendor/github.com/weaveworks/common/middleware/path_rewrite.go diff --git a/vendor/github.com/weaveworks/common/middleware/redirect.go b/vendor/github.com/weaveworks/common/middleware/redirect.go new file mode 100644 index 00000000000..34c1772817b --- /dev/null +++ b/vendor/github.com/weaveworks/common/middleware/redirect.go @@ -0,0 +1,48 @@ +package middleware + +import ( + "net/http" + "net/url" +) + +// Redirect middleware, will redirect requests to hosts which match any of the +// Matches to RedirectScheme://RedirectHost +type Redirect struct { + Matches []Match + + RedirectHost string + RedirectScheme string +} + +// Match specifies a match for a redirect. Host and/or Scheme can be empty +// signify match-all. +type Match struct { + Host, Scheme string +} + +func (m Match) match(u *url.URL) bool { + if m.Host != "" && m.Host != u.Host { + return false + } + + if m.Scheme != "" && m.Scheme != u.Scheme { + return false + } + + return true +} + +// Wrap implements Middleware +func (m Redirect) Wrap(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + for _, match := range m.Matches { + if match.match(r.URL) { + r.URL.Host = m.RedirectHost + r.URL.Scheme = m.RedirectScheme + http.Redirect(w, r, r.URL.String(), http.StatusMovedPermanently) + return + } + } + next.ServeHTTP(w, r) + }) +} diff --git a/vendor/github.com/weaveworks/common/network/interface.go b/vendor/github.com/weaveworks/common/network/interface.go new file mode 100644 index 00000000000..9a94b9ac6b8 --- /dev/null +++ b/vendor/github.com/weaveworks/common/network/interface.go @@ -0,0 +1,33 @@ +package network + +import ( + "fmt" + "net" +) + +// GetFirstAddressOf returns the first IPv4 address of the supplied interface name. +func GetFirstAddressOf(name string) (string, error) { + inf, err := net.InterfaceByName(name) + if err != nil { + return "", err + } + + addrs, err := inf.Addrs() + if err != nil { + return "", err + } + if len(addrs) <= 0 { + return "", fmt.Errorf("No address found for %s", name) + } + + for _, addr := range addrs { + switch v := addr.(type) { + case *net.IPNet: + if ip := v.IP.To4(); ip != nil { + return v.IP.String(), nil + } + } + } + + return "", fmt.Errorf("No address found for %s", name) +} diff --git a/vendor/github.com/weaveworks/common/sanitize/sanitize.go b/vendor/github.com/weaveworks/common/sanitize/sanitize.go new file mode 100644 index 00000000000..0ba4eae074c --- /dev/null +++ b/vendor/github.com/weaveworks/common/sanitize/sanitize.go @@ -0,0 +1,44 @@ +package sanitize + +import ( + "fmt" + "net" + "net/url" + "strings" + + log "github.com/Sirupsen/logrus" +) + +// URL returns a function that sanitizes a URL string. It lets underspecified +// strings to be converted to usable URLs via some default arguments. +func URL(defaultScheme string, defaultPort int, defaultPath string) func(string) string { + if defaultScheme == "" { + defaultScheme = "http://" + } + return func(s string) string { + if s == "" { + return s // can't do much here + } + if !strings.Contains(s, "://") { + s = defaultScheme + s + } + u, err := url.Parse(s) + if err != nil { + log.Errorf("%q: %v", s, err) + return s // oh well + } + if _, port, err := net.SplitHostPort(u.Host); err != nil && defaultPort > 0 { + u.Host += fmt.Sprintf(":%d", defaultPort) + } else if port == "443" { + if u.Scheme == "ws" { + u.Scheme = "wss" + } else { + u.Scheme = "https" + } + } + if defaultPath != "" && u.Path != defaultPath { + u.Path = defaultPath + } + return u.String() + } +} diff --git a/vendor/github.com/weaveworks/common/test/diff.go b/vendor/github.com/weaveworks/common/test/diff.go new file mode 100644 index 00000000000..477ae98f966 --- /dev/null +++ b/vendor/github.com/weaveworks/common/test/diff.go @@ -0,0 +1,22 @@ +package test + +import ( + "github.com/davecgh/go-spew/spew" + "github.com/pmezard/go-difflib/difflib" +) + +// Diff diffs two arbitrary data structures, giving human-readable output. +func Diff(want, have interface{}) string { + config := spew.NewDefaultConfig() + config.ContinueOnMethod = true + config.SortKeys = true + config.SpewKeys = true + text, _ := difflib.GetUnifiedDiffString(difflib.UnifiedDiff{ + A: difflib.SplitLines(config.Sdump(want)), + B: difflib.SplitLines(config.Sdump(have)), + FromFile: "want", + ToFile: "have", + Context: 3, + }) + return "\n" + text +} diff --git a/vendor/github.com/weaveworks/common/test/exec/exec.go b/vendor/github.com/weaveworks/common/test/exec/exec.go new file mode 100644 index 00000000000..6e6d9cc899e --- /dev/null +++ b/vendor/github.com/weaveworks/common/test/exec/exec.go @@ -0,0 +1,79 @@ +package exec + +import ( + "bytes" + "io" + "io/ioutil" + + "github.com/weaveworks/common/exec" +) + +type mockCmd struct { + io.ReadCloser + quit chan struct{} +} + +type blockingReader struct { + quit chan struct{} +} + +// NewMockCmdString creates a new mock Cmd which has s on its stdout pipe +func NewMockCmdString(s string) exec.Cmd { + return &mockCmd{ + ReadCloser: struct { + io.Reader + io.Closer + }{ + bytes.NewBufferString(s), + ioutil.NopCloser(nil), + }, + quit: make(chan struct{}), + } +} + +// NewMockCmd creates a new mock Cmd with rc as its stdout pipe +func NewMockCmd(rc io.ReadCloser) exec.Cmd { + return &mockCmd{ + ReadCloser: rc, + quit: make(chan struct{}), + } +} + +func (c *mockCmd) Start() error { + return nil +} + +func (c *mockCmd) Wait() error { + return nil +} + +func (c *mockCmd) StdoutPipe() (io.ReadCloser, error) { + return c.ReadCloser, nil +} + +func (c *mockCmd) StderrPipe() (io.ReadCloser, error) { + return &blockingReader{c.quit}, nil +} + +func (c *mockCmd) Kill() error { + close(c.quit) + return nil +} + +func (c *mockCmd) Output() ([]byte, error) { + return ioutil.ReadAll(c.ReadCloser) +} + +func (c *mockCmd) Run() error { + return nil +} + +func (b *blockingReader) Read(p []byte) (n int, err error) { + <-b.quit + return 0, nil +} + +func (b *blockingReader) Close() error { + <-b.quit + return nil +} diff --git a/vendor/github.com/weaveworks/common/test/fs/fs.go b/vendor/github.com/weaveworks/common/test/fs/fs.go new file mode 100644 index 00000000000..b03565223ca --- /dev/null +++ b/vendor/github.com/weaveworks/common/test/fs/fs.go @@ -0,0 +1,287 @@ +package fs + +import ( + "bytes" + "fmt" + "io" + "io/ioutil" + "os" + "strings" + "syscall" + "time" + + "github.com/weaveworks/common/fs" +) + +type mockInode struct{} + +type dir struct { + mockInode + name string + entries map[string]Entry + stat syscall.Stat_t +} + +// File is a mock file +type File struct { + mockInode + FName string + FContents string + FReader io.Reader + FWriter io.Writer + FCloser io.Closer + FStat syscall.Stat_t +} + +// Entry is an entry in the mock filesystem +type Entry interface { + os.FileInfo + fs.Interface + Add(path string, e Entry) error + Remove(path string) error +} + +// Dir creates a new directory with the given entries. +func Dir(name string, entries ...Entry) Entry { + result := dir{ + name: name, + entries: map[string]Entry{}, + } + + for _, entry := range entries { + result.entries[entry.Name()] = entry + } + + return result +} + +func split(path string) (string, string) { + if !strings.HasPrefix(path, "/") { + panic(path) + } + + comps := strings.SplitN(path, "/", 3) + if len(comps) == 2 { + return comps[1], "/" + } + + return comps[1], "/" + comps[2] +} + +func (mockInode) Size() int64 { return 0 } +func (mockInode) Mode() os.FileMode { return 0 } +func (mockInode) ModTime() time.Time { return time.Now() } +func (mockInode) Sys() interface{} { return nil } + +func (p dir) Name() string { return p.name } +func (p dir) IsDir() bool { return true } + +func (p dir) ReadDir(path string) ([]os.FileInfo, error) { + if path == "/" { + result := []os.FileInfo{} + for _, v := range p.entries { + result = append(result, v) + } + return result, nil + } + + head, tail := split(path) + fs, ok := p.entries[head] + if !ok { + return nil, fmt.Errorf("Not found: %s", path) + } + + return fs.ReadDir(tail) +} + +func (p dir) ReadDirNames(path string) ([]string, error) { + if path == "/" { + result := []string{} + for _, v := range p.entries { + result = append(result, v.Name()) + } + return result, nil + } + + head, tail := split(path) + fs, ok := p.entries[head] + if !ok { + return nil, fmt.Errorf("Not found: %s", path) + } + + return fs.ReadDirNames(tail) +} + +func (p dir) ReadFile(path string) ([]byte, error) { + if path == "/" { + return nil, fmt.Errorf("I'm a directory") + } + + head, tail := split(path) + fs, ok := p.entries[head] + if !ok { + return nil, fmt.Errorf("Not found: %s", path) + } + + return fs.ReadFile(tail) +} + +func (p dir) Lstat(path string, stat *syscall.Stat_t) error { + if path == "/" { + *stat = syscall.Stat_t{Mode: syscall.S_IFDIR} + return nil + } + + head, tail := split(path) + fs, ok := p.entries[head] + if !ok { + return fmt.Errorf("Not found: %s", path) + } + + return fs.Lstat(tail, stat) +} + +func (p dir) Stat(path string, stat *syscall.Stat_t) error { + if path == "/" { + *stat = syscall.Stat_t{Mode: syscall.S_IFDIR} + return nil + } + + head, tail := split(path) + fs, ok := p.entries[head] + if !ok { + return fmt.Errorf("Not found: %s", path) + } + + return fs.Stat(tail, stat) +} + +func (p dir) Open(path string) (io.ReadWriteCloser, error) { + if path == "/" { + return nil, fmt.Errorf("I'm a directory") + } + + head, tail := split(path) + fs, ok := p.entries[head] + if !ok { + return nil, fmt.Errorf("Not found: %s", path) + } + + return fs.Open(tail) +} + +func (p dir) Add(path string, e Entry) error { + if path == "/" { + p.entries[e.Name()] = e + return nil + } + + head, tail := split(path) + fs, ok := p.entries[head] + if !ok { + fs = Dir(head) + p.entries[head] = fs + } + + return fs.Add(tail, e) +} + +func (p dir) Remove(path string) error { + if _, ok := p.entries[strings.TrimPrefix(path, "/")]; ok { + delete(p.entries, strings.TrimPrefix(path, "/")) + return nil + } + + head, tail := split(path) + fs, ok := p.entries[head] + if !ok { + return nil + } + return fs.Remove(tail) +} + +// Name implements os.FileInfo +func (p File) Name() string { return p.FName } + +// IsDir implements os.FileInfo +func (p File) IsDir() bool { return false } + +// ReadDir implements FS +func (p File) ReadDir(path string) ([]os.FileInfo, error) { + return nil, fmt.Errorf("I'm a file") +} + +// ReadDirNames implements FS +func (p File) ReadDirNames(path string) ([]string, error) { + return nil, fmt.Errorf("I'm a file") +} + +// ReadFile implements FS +func (p File) ReadFile(path string) ([]byte, error) { + if path != "/" { + return nil, fmt.Errorf("I'm a file") + } + if p.FReader != nil { + return ioutil.ReadAll(p.FReader) + } + return []byte(p.FContents), nil +} + +// Lstat implements FS +func (p File) Lstat(path string, stat *syscall.Stat_t) error { + if path != "/" { + return fmt.Errorf("I'm a file") + } + *stat = p.FStat + return nil +} + +// Stat implements FS +func (p File) Stat(path string, stat *syscall.Stat_t) error { + if path != "/" { + return fmt.Errorf("I'm a file") + } + *stat = p.FStat + return nil +} + +// Open implements FS +func (p File) Open(path string) (io.ReadWriteCloser, error) { + if path != "/" { + return nil, fmt.Errorf("I'm a file") + } + buf := bytes.NewBuffer([]byte(p.FContents)) + s := struct { + io.Reader + io.Writer + io.Closer + }{ + buf, buf, ioutil.NopCloser(nil), + } + if p.FReader != nil { + s.Reader = p.FReader + } + if p.FWriter != nil { + s.Writer = p.FWriter + } + if p.FCloser != nil { + s.Closer = p.FCloser + } + return s, nil +} + +// Add adds a new node to the fs +func (p File) Add(path string, e Entry) error { + if path != "/" { + return fmt.Errorf("I'm a file") + } + return nil +} + +// Remove removes a node from the fs +func (p File) Remove(path string) error { + if path != "/" { + return fmt.Errorf("I'm a file") + } + return nil +} diff --git a/vendor/github.com/weaveworks/common/tools/cover/cover.go b/vendor/github.com/weaveworks/common/tools/cover/cover.go new file mode 100644 index 00000000000..4c5fcfd7d61 --- /dev/null +++ b/vendor/github.com/weaveworks/common/tools/cover/cover.go @@ -0,0 +1,97 @@ +package main + +import ( + "fmt" + "os" + "sort" + + "golang.org/x/tools/cover" +) + +func merge(p1, p2 *cover.Profile) *cover.Profile { + output := cover.Profile{ + FileName: p1.FileName, + Mode: p1.Mode, + } + + i, j := 0, 0 + for i < len(p1.Blocks) && j < len(p2.Blocks) { + bi, bj := p1.Blocks[i], p2.Blocks[j] + if bi.StartLine == bj.StartLine && bi.StartCol == bj.StartCol { + + if bi.EndLine != bj.EndLine || + bi.EndCol != bj.EndCol || + bi.NumStmt != bj.NumStmt { + panic("Not run on same source!") + } + + output.Blocks = append(output.Blocks, cover.ProfileBlock{ + StartLine: bi.StartLine, + StartCol: bi.StartCol, + EndLine: bi.EndLine, + EndCol: bi.EndCol, + NumStmt: bi.NumStmt, + Count: bi.Count + bj.Count, + }) + i++ + j++ + } else if bi.StartLine < bj.StartLine || bi.StartLine == bj.StartLine && bi.StartCol < bj.StartCol { + output.Blocks = append(output.Blocks, bi) + i++ + } else { + output.Blocks = append(output.Blocks, bj) + j++ + } + } + + for ; i < len(p1.Blocks); i++ { + output.Blocks = append(output.Blocks, p1.Blocks[i]) + } + + for ; j < len(p2.Blocks); j++ { + output.Blocks = append(output.Blocks, p2.Blocks[j]) + } + + return &output +} + +func print(profiles []*cover.Profile) { + fmt.Println("mode: atomic") + for _, profile := range profiles { + for _, block := range profile.Blocks { + fmt.Printf("%s:%d.%d,%d.%d %d %d\n", profile.FileName, block.StartLine, block.StartCol, + block.EndLine, block.EndCol, block.NumStmt, block.Count) + } + } +} + +// Copied from https://github.com/golang/tools/blob/master/cover/profile.go +type byFileName []*cover.Profile + +func (p byFileName) Len() int { return len(p) } +func (p byFileName) Less(i, j int) bool { return p[i].FileName < p[j].FileName } +func (p byFileName) Swap(i, j int) { p[i], p[j] = p[j], p[i] } + +func main() { + outputProfiles := map[string]*cover.Profile{} + for _, input := range os.Args[1:] { + inputProfiles, err := cover.ParseProfiles(input) + if err != nil { + panic(fmt.Sprintf("Error parsing %s: %v", input, err)) + } + for _, ip := range inputProfiles { + op := outputProfiles[ip.FileName] + if op == nil { + outputProfiles[ip.FileName] = ip + } else { + outputProfiles[ip.FileName] = merge(op, ip) + } + } + } + profiles := make([]*cover.Profile, 0, len(outputProfiles)) + for _, profile := range outputProfiles { + profiles = append(profiles, profile) + } + sort.Sort(byFileName(profiles)) + print(profiles) +} diff --git a/vendor/github.com/weaveworks/common/tools/runner/runner.go b/vendor/github.com/weaveworks/common/tools/runner/runner.go new file mode 100644 index 00000000000..c92ac6b5ba0 --- /dev/null +++ b/vendor/github.com/weaveworks/common/tools/runner/runner.go @@ -0,0 +1,289 @@ +package main + +import ( + "bytes" + "encoding/json" + "fmt" + "net/http" + "net/url" + "os" + "os/exec" + "sort" + "strconv" + "strings" + "sync" + "time" + + "github.com/mgutz/ansi" + "github.com/weaveworks/docker/pkg/mflag" +) + +const ( + defaultSchedulerHost = "positive-cocoa-90213.appspot.com" + jsonContentType = "application/json" +) + +var ( + start = ansi.ColorCode("black+ub") + fail = ansi.ColorCode("red+b") + succ = ansi.ColorCode("green+b") + reset = ansi.ColorCode("reset") + + schedulerHost = defaultSchedulerHost + useScheduler = false + runParallel = false + verbose = false + timeout = 180 // In seconds. Three minutes ought to be enough for any test + + consoleLock = sync.Mutex{} +) + +type test struct { + name string + hosts int +} + +type schedule struct { + Tests []string `json:"tests"` +} + +type result struct { + test + errored bool + hosts []string +} + +type tests []test + +func (ts tests) Len() int { return len(ts) } +func (ts tests) Swap(i, j int) { ts[i], ts[j] = ts[j], ts[i] } +func (ts tests) Less(i, j int) bool { + if ts[i].hosts != ts[j].hosts { + return ts[i].hosts < ts[j].hosts + } + return ts[i].name < ts[j].name +} + +func (ts *tests) pick(available int) (test, bool) { + // pick the first test that fits in the available hosts + for i, test := range *ts { + if test.hosts <= available { + *ts = append((*ts)[:i], (*ts)[i+1:]...) + return test, true + } + } + + return test{}, false +} + +func (t test) run(hosts []string) bool { + consoleLock.Lock() + fmt.Printf("%s>>> Running %s on %s%s\n", start, t.name, hosts, reset) + consoleLock.Unlock() + + var out bytes.Buffer + + cmd := exec.Command(t.name) + cmd.Env = os.Environ() + cmd.Stdout = &out + cmd.Stderr = &out + + // replace HOSTS in env + for i, env := range cmd.Env { + if strings.HasPrefix(env, "HOSTS") { + cmd.Env[i] = fmt.Sprintf("HOSTS=%s", strings.Join(hosts, " ")) + break + } + } + + start := time.Now() + var err error + + c := make(chan error, 1) + go func() { c <- cmd.Run() }() + select { + case err = <-c: + case <-time.After(time.Duration(timeout) * time.Second): + err = fmt.Errorf("timed out") + } + + duration := float64(time.Now().Sub(start)) / float64(time.Second) + + consoleLock.Lock() + if err != nil { + fmt.Printf("%s>>> Test %s finished after %0.1f secs with error: %v%s\n", fail, t.name, duration, err, reset) + } else { + fmt.Printf("%s>>> Test %s finished with success after %0.1f secs%s\n", succ, t.name, duration, reset) + } + if err != nil || verbose { + fmt.Print(out.String()) + fmt.Println() + } + consoleLock.Unlock() + + if err != nil && useScheduler { + updateScheduler(t.name, duration) + } + + return err != nil +} + +func updateScheduler(test string, duration float64) { + req := &http.Request{ + Method: "POST", + Host: schedulerHost, + URL: &url.URL{ + Opaque: fmt.Sprintf("/record/%s/%0.2f", url.QueryEscape(test), duration), + Scheme: "http", + Host: schedulerHost, + }, + Close: true, + } + if resp, err := http.DefaultClient.Do(req); err != nil { + fmt.Printf("Error updating scheduler: %v\n", err) + } else { + resp.Body.Close() + } +} + +func getSchedule(tests []string) ([]string, error) { + var ( + project = os.Getenv("CIRCLE_PROJECT_REPONAME") + buildNum = os.Getenv("CIRCLE_BUILD_NUM") + testRun = project + "-integration-" + buildNum + shardCount = os.Getenv("CIRCLE_NODE_TOTAL") + shardID = os.Getenv("CIRCLE_NODE_INDEX") + requestBody = &bytes.Buffer{} + ) + if err := json.NewEncoder(requestBody).Encode(schedule{tests}); err != nil { + return []string{}, err + } + url := fmt.Sprintf("http://%s/schedule/%s/%s/%s", schedulerHost, testRun, shardCount, shardID) + resp, err := http.Post(url, jsonContentType, requestBody) + if err != nil { + return []string{}, err + } + var sched schedule + if err := json.NewDecoder(resp.Body).Decode(&sched); err != nil { + return []string{}, err + } + return sched.Tests, nil +} + +func getTests(testNames []string) (tests, error) { + var err error + if useScheduler { + testNames, err = getSchedule(testNames) + if err != nil { + return tests{}, err + } + } + tests := tests{} + for _, name := range testNames { + parts := strings.Split(strings.TrimSuffix(name, "_test.sh"), "_") + numHosts, err := strconv.Atoi(parts[len(parts)-1]) + if err != nil { + numHosts = 1 + } + tests = append(tests, test{name, numHosts}) + fmt.Printf("Test %s needs %d hosts\n", name, numHosts) + } + return tests, nil +} + +func summary(tests, failed tests) { + if len(failed) > 0 { + fmt.Printf("%s>>> Ran %d tests, %d failed%s\n", fail, len(tests), len(failed), reset) + for _, test := range failed { + fmt.Printf("%s>>> Fail %s%s\n", fail, test.name, reset) + } + } else { + fmt.Printf("%s>>> Ran %d tests, all succeeded%s\n", succ, len(tests), reset) + } +} + +func parallel(ts tests, hosts []string) bool { + testsCopy := ts + sort.Sort(sort.Reverse(ts)) + resultsChan := make(chan result) + outstanding := 0 + failed := tests{} + for len(ts) > 0 || outstanding > 0 { + // While we have some free hosts, try and schedule + // a test on them + for len(hosts) > 0 { + test, ok := ts.pick(len(hosts)) + if !ok { + break + } + testHosts := hosts[:test.hosts] + hosts = hosts[test.hosts:] + + go func() { + errored := test.run(testHosts) + resultsChan <- result{test, errored, testHosts} + }() + outstanding++ + } + + // Otherwise, wait for the test to finish and return + // the hosts to the pool + result := <-resultsChan + hosts = append(hosts, result.hosts...) + outstanding-- + if result.errored { + failed = append(failed, result.test) + } + } + summary(testsCopy, failed) + return len(failed) > 0 +} + +func sequential(ts tests, hosts []string) bool { + failed := tests{} + for _, test := range ts { + if test.run(hosts) { + failed = append(failed, test) + } + } + summary(ts, failed) + return len(failed) > 0 +} + +func main() { + mflag.BoolVar(&useScheduler, []string{"scheduler"}, false, "Use scheduler to distribute tests across shards") + mflag.BoolVar(&runParallel, []string{"parallel"}, false, "Run tests in parallel on hosts where possible") + mflag.BoolVar(&verbose, []string{"v"}, false, "Print output from all tests (Also enabled via DEBUG=1)") + mflag.StringVar(&schedulerHost, []string{"scheduler-host"}, defaultSchedulerHost, "Hostname of scheduler.") + mflag.IntVar(&timeout, []string{"timeout"}, 180, "Max time to run one test for, in seconds") + mflag.Parse() + + if len(os.Getenv("DEBUG")) > 0 { + verbose = true + } + + testArgs := mflag.Args() + tests, err := getTests(testArgs) + if err != nil { + fmt.Printf("Error parsing tests: %v (%v)\n", err, testArgs) + os.Exit(1) + } + + hosts := strings.Fields(os.Getenv("HOSTS")) + maxHosts := len(hosts) + if maxHosts == 0 { + fmt.Print("No HOSTS specified.\n") + os.Exit(1) + } + + var errored bool + if runParallel { + errored = parallel(tests, hosts) + } else { + errored = sequential(tests, hosts) + } + + if errored { + os.Exit(1) + } +} diff --git a/vendor/github.com/weaveworks/common/tools/socks/main.go b/vendor/github.com/weaveworks/common/tools/socks/main.go new file mode 100644 index 00000000000..83a21498036 --- /dev/null +++ b/vendor/github.com/weaveworks/common/tools/socks/main.go @@ -0,0 +1,97 @@ +package main + +import ( + "fmt" + "net" + "net/http" + "os" + "strings" + "text/template" + + socks5 "github.com/armon/go-socks5" + "github.com/weaveworks/docker/pkg/mflag" + "github.com/weaveworks/weave/common/mflagext" + "golang.org/x/net/context" +) + +type pacFileParameters struct { + HostMatch string + Aliases map[string]string +} + +const ( + pacfile = ` +function FindProxyForURL(url, host) { + if(shExpMatch(host, "{{.HostMatch}}")) { + return "SOCKS5 localhost:8000"; + } + {{range $key, $value := .Aliases}} + if (host == "{{$key}}") { + return "SOCKS5 localhost:8000"; + } + {{end}} + return "DIRECT"; +} +` +) + +func main() { + var ( + as []string + hostMatch string + ) + mflagext.ListVar(&as, []string{"a", "-alias"}, []string{}, "Specify hostname aliases in the form alias:hostname. Can be repeated.") + mflag.StringVar(&hostMatch, []string{"h", "-host-match"}, "*.weave.local", "Specify main host shExpMatch expression in pacfile") + mflag.Parse() + + var aliases = map[string]string{} + for _, a := range as { + parts := strings.SplitN(a, ":", 2) + if len(parts) != 2 { + fmt.Printf("'%s' is not a valid alias.\n", a) + mflag.Usage() + os.Exit(1) + } + aliases[parts[0]] = parts[1] + } + + go socksProxy(aliases) + + t := template.Must(template.New("pacfile").Parse(pacfile)) + http.HandleFunc("/proxy.pac", func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/x-ns-proxy-autoconfig") + t.Execute(w, pacFileParameters{hostMatch, aliases}) + }) + + if err := http.ListenAndServe(":8080", nil); err != nil { + panic(err) + } +} + +type aliasingResolver struct { + aliases map[string]string + socks5.NameResolver +} + +func (r aliasingResolver) Resolve(ctx context.Context, name string) (context.Context, net.IP, error) { + if alias, ok := r.aliases[name]; ok { + return r.NameResolver.Resolve(ctx, alias) + } + return r.NameResolver.Resolve(ctx, name) +} + +func socksProxy(aliases map[string]string) { + conf := &socks5.Config{ + Resolver: aliasingResolver{ + aliases: aliases, + NameResolver: socks5.DNSResolver{}, + }, + } + server, err := socks5.New(conf) + if err != nil { + panic(err) + } + if err := server.ListenAndServe("tcp", ":8000"); err != nil { + panic(err) + } +} diff --git a/user/id.go b/vendor/github.com/weaveworks/common/user/id.go similarity index 54% rename from user/id.go rename to vendor/github.com/weaveworks/common/user/id.go index cce2c00f527..4b90a2a558c 100644 --- a/user/id.go +++ b/vendor/github.com/weaveworks/common/user/id.go @@ -6,18 +6,16 @@ import ( "golang.org/x/net/context" ) -// TODO(jml): typedef for userid, maybe even put in a weaveworks library, so -// that there's a shared language around the multiple ways of identifying -// entitiies. - // UserIDContextKey is the key used in contexts to find the userid -const userIDContextKey = "CortexUserID" // TODO dedupe with storage/local +type contextKey int + +const userIDContextKey contextKey = 0 -// UserIDHeaderName is a legacy from scope as a service. -const UserIDHeaderName = "X-Scope-OrgID" +// OrgIDHeaderName is a legacy from scope as a service. +const OrgIDHeaderName = "X-Scope-OrgID" -// LowerUserIDHeaderName as gRPC / HTTP2.0 headers are lowercased. -const LowerUserIDHeaderName = "x-scope-orgid" +// LowerOrgIDHeaderName as gRPC / HTTP2.0 headers are lowercased. +const LowerOrgIDHeaderName = "x-scope-orgid" // GetID returns the user func GetID(ctx context.Context) (string, error) { diff --git a/vendor/github.com/weaveworks/scope/common/instrument/LICENSE b/vendor/github.com/weaveworks/scope/common/instrument/LICENSE deleted file mode 100644 index a710357f3b0..00000000000 --- a/vendor/github.com/weaveworks/scope/common/instrument/LICENSE +++ /dev/null @@ -1,191 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - Copyright 2014-2016 Weaveworks Ltd. - - 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. diff --git a/vendor/github.com/weaveworks/scope/common/middleware/LICENSE b/vendor/github.com/weaveworks/scope/common/middleware/LICENSE deleted file mode 100644 index a710357f3b0..00000000000 --- a/vendor/github.com/weaveworks/scope/common/middleware/LICENSE +++ /dev/null @@ -1,191 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - Copyright 2014-2016 Weaveworks Ltd. - - 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. diff --git a/vendor/manifest b/vendor/manifest index c6f52180fce..849b5f7e8da 100644 --- a/vendor/manifest +++ b/vendor/manifest @@ -245,7 +245,7 @@ "importpath": "github.com/docker/docker/pkg/ioutils", "repository": "https://github.com/docker/docker", "vcs": "git", - "revision": "27699ba8b23217525b0b026fa689834db4454a35", + "revision": "e9c0de0de65182915d1cd205f06f5c903c20a29f", "branch": "master", "path": "/pkg/ioutils", "notests": true @@ -254,20 +254,11 @@ "importpath": "github.com/docker/docker/pkg/longpath", "repository": "https://github.com/docker/docker", "vcs": "git", - "revision": "a58e3e7fefd67ebe57c183e970e5fcda096c9ad1", + "revision": "e9c0de0de65182915d1cd205f06f5c903c20a29f", "branch": "master", "path": "pkg/longpath", "notests": true }, - { - "importpath": "github.com/docker/docker/vendor/golang.org/x/net/context", - "repository": "https://github.com/docker/docker", - "vcs": "git", - "revision": "a58e3e7fefd67ebe57c183e970e5fcda096c9ad1", - "branch": "master", - "path": "vendor/golang.org/x/net/context", - "notests": true - }, { "importpath": "github.com/golang/glog", "repository": "https://github.com/golang/glog", @@ -622,6 +613,14 @@ "branch": "master", "notests": true }, + { + "importpath": "github.com/sercand/kuberesolver", + "repository": "https://github.com/sercand/kuberesolver", + "vcs": "git", + "revision": "2f561e34ecb6206fcad82f0c5842379188d8db40", + "branch": "master", + "notests": true + }, { "importpath": "github.com/shiena/ansicolor", "repository": "https://github.com/shiena/ansicolor", @@ -701,165 +700,11 @@ "notests": true }, { - "importpath": "github.com/weaveworks/common/mtime", + "importpath": "github.com/weaveworks/common", "repository": "https://github.com/weaveworks/common", "vcs": "git", - "revision": "139d0313ac15170e9de8187b26e7df03b4cb910e", - "branch": "master", - "path": "/mtime", - "notests": true - }, - { - "importpath": "github.com/weaveworks/scope/common/instrument", - "repository": "https://github.com/weaveworks/scope", - "vcs": "git", - "revision": "604661ca2afa686508492ac3eee8c327bf47eb68", - "branch": "master", - "path": "/common/instrument", - "notests": true - }, - { - "importpath": "github.com/weaveworks/scope/common/middleware", - "repository": "https://github.com/weaveworks/scope", - "vcs": "git", - "revision": "382978496727da8b2d2d03888b83404a908bc65a", - "branch": "master", - "path": "/common/middleware", - "notests": true - }, - { - "importpath": "github.com/weaveworks/scope/vendor/bitbucket.org/ww/goautoneg", - "repository": "https://github.com/weaveworks/scope", - "vcs": "git", - "revision": "fa784140a4e5163247ebc368ceba71b4d525f095", - "branch": "master", - "path": "vendor/bitbucket.org/ww/goautoneg", - "notests": true - }, - { - "importpath": "github.com/weaveworks/scope/vendor/github.com/Sirupsen/logrus", - "repository": "https://github.com/weaveworks/scope", - "vcs": "git", - "revision": "fa784140a4e5163247ebc368ceba71b4d525f095", - "branch": "master", - "path": "vendor/github.com/Sirupsen/logrus", - "notests": true - }, - { - "importpath": "github.com/weaveworks/scope/vendor/github.com/beorn7/perks/quantile", - "repository": "https://github.com/weaveworks/scope", - "vcs": "git", - "revision": "fa784140a4e5163247ebc368ceba71b4d525f095", - "branch": "master", - "path": "vendor/github.com/beorn7/perks/quantile", - "notests": true - }, - { - "importpath": "github.com/weaveworks/scope/vendor/github.com/davecgh/go-spew/spew", - "repository": "https://github.com/weaveworks/scope", - "vcs": "git", - "revision": "fa784140a4e5163247ebc368ceba71b4d525f095", - "branch": "master", - "path": "vendor/github.com/davecgh/go-spew/spew", - "notests": true - }, - { - "importpath": "github.com/weaveworks/scope/vendor/github.com/golang/protobuf/proto", - "repository": "https://github.com/weaveworks/scope", - "vcs": "git", - "revision": "fa784140a4e5163247ebc368ceba71b4d525f095", - "branch": "master", - "path": "vendor/github.com/golang/protobuf/proto", - "notests": true - }, - { - "importpath": "github.com/weaveworks/scope/vendor/github.com/gorilla/context", - "repository": "https://github.com/weaveworks/scope", - "vcs": "git", - "revision": "fa784140a4e5163247ebc368ceba71b4d525f095", - "branch": "master", - "path": "vendor/github.com/gorilla/context", - "notests": true - }, - { - "importpath": "github.com/weaveworks/scope/vendor/github.com/gorilla/mux", - "repository": "https://github.com/weaveworks/scope", - "vcs": "git", - "revision": "fa784140a4e5163247ebc368ceba71b4d525f095", - "branch": "master", - "path": "vendor/github.com/gorilla/mux", - "notests": true - }, - { - "importpath": "github.com/weaveworks/scope/vendor/github.com/matttproud/golang_protobuf_extensions/pbutil", - "repository": "https://github.com/weaveworks/scope", - "vcs": "git", - "revision": "fa784140a4e5163247ebc368ceba71b4d525f095", - "branch": "master", - "path": "vendor/github.com/matttproud/golang_protobuf_extensions/pbutil", - "notests": true - }, - { - "importpath": "github.com/weaveworks/scope/vendor/github.com/pmezard/go-difflib/difflib", - "repository": "https://github.com/weaveworks/scope", - "vcs": "git", - "revision": "fa784140a4e5163247ebc368ceba71b4d525f095", - "branch": "master", - "path": "vendor/github.com/pmezard/go-difflib/difflib", - "notests": true - }, - { - "importpath": "github.com/weaveworks/scope/vendor/github.com/prometheus/client_golang/prometheus", - "repository": "https://github.com/weaveworks/scope", - "vcs": "git", - "revision": "fa784140a4e5163247ebc368ceba71b4d525f095", - "branch": "master", - "path": "vendor/github.com/prometheus/client_golang/prometheus", - "notests": true - }, - { - "importpath": "github.com/weaveworks/scope/vendor/github.com/prometheus/client_model/go", - "repository": "https://github.com/weaveworks/scope", - "vcs": "git", - "revision": "fa784140a4e5163247ebc368ceba71b4d525f095", - "branch": "master", - "path": "vendor/github.com/prometheus/client_model/go", - "notests": true - }, - { - "importpath": "github.com/weaveworks/scope/vendor/github.com/prometheus/common/expfmt", - "repository": "https://github.com/weaveworks/scope", - "vcs": "git", - "revision": "fa784140a4e5163247ebc368ceba71b4d525f095", - "branch": "master", - "path": "vendor/github.com/prometheus/common/expfmt", - "notests": true - }, - { - "importpath": "github.com/weaveworks/scope/vendor/github.com/prometheus/common/model", - "repository": "https://github.com/weaveworks/scope", - "vcs": "git", - "revision": "fa784140a4e5163247ebc368ceba71b4d525f095", - "branch": "master", - "path": "vendor/github.com/prometheus/common/model", - "notests": true - }, - { - "importpath": "github.com/weaveworks/scope/vendor/github.com/prometheus/procfs", - "repository": "https://github.com/weaveworks/scope", - "vcs": "git", - "revision": "fa784140a4e5163247ebc368ceba71b4d525f095", - "branch": "master", - "path": "vendor/github.com/prometheus/procfs", - "notests": true - }, - { - "importpath": "github.com/weaveworks/scope/vendor/github.com/stretchr/testify/assert", - "repository": "https://github.com/weaveworks/scope", - "vcs": "git", - "revision": "fa784140a4e5163247ebc368ceba71b4d525f095", + "revision": "f7d1a7f7b792ff618d2afe2c3f2b3f38dac8bef0", "branch": "master", - "path": "vendor/github.com/stretchr/testify/assert", "notests": true }, {