From 002d2c4165dc5d1cdc7b96f676f8cd7b4e6102fa Mon Sep 17 00:00:00 2001 From: Tom Wilkie Date: Wed, 25 Jan 2017 17:21:35 +0000 Subject: [PATCH] HTTP over gRPC: encode HTTP requests as gRPC requests Why? Want to use gRPC for everything, as it make propagating context, load balacing and connection persisting easier than using raw HTTP. By embedding HTTP in gRPC, we have a migration path, and a way for authfe to no have to know how to forward requests for every service type. Also, move user context code and grpc middleware to weaveworks/common, remove scope vendoring. --- chunk/chunk_cache.go | 2 +- chunk/chunk_store.go | 4 +- chunk/chunk_store_test.go | 2 +- chunk/dynamo_table_manager.go | 2 +- chunk/dynamodb_client.go | 2 +- cmd/querier/main.go | 6 +- cortex.proto | 6 +- distributor/distributor.go | 6 +- ingester/ingester.go | 2 +- ingester/ingester_test.go | 2 +- ingester/user_state.go | 2 +- ruler/ruler.go | 2 +- ruler/scheduler.go | 6 +- server/server.go | 13 +- util/http.go | 4 +- .../sercand/kuberesolver/balancer.go | 135 ++++++++ .../sercand/kuberesolver/kubernetes.go | 71 +++++ .../github.com/sercand/kuberesolver/models.go | 42 +++ .../sercand/kuberesolver/resolver.go | 84 +++++ .../github.com/sercand/kuberesolver/stream.go | 105 +++++++ .../github.com/sercand/kuberesolver/util.go | 43 +++ .../sercand/kuberesolver/watcher.go | 95 ++++++ .../weaveworks/common/{mtime => }/LICENSE | 0 .../weaveworks/common/backoff/backoff.go | 96 ++++++ .../github.com/weaveworks/common/exec/exec.go | 30 ++ vendor/github.com/weaveworks/common/fs/fs.go | 94 ++++++ .../weaveworks/common/httpgrpc/httpgrpc.go | 133 ++++++++ .../weaveworks/common/httpgrpc/httpgrpc.pb.go | 231 ++++++++++++++ .../common/instrument/instrument.go | 0 .../weaveworks/common/logging/logging.go | 45 +++ .../common/middleware/errorhandler.go | 94 ++++++ .../weaveworks/common/middleware/grpc_auth.go | 8 +- .../common/middleware/grpc_instrumentation.go | 0 .../common/middleware/grpc_logging.go | 2 +- .../common/middleware/instrument.go | 17 +- .../{scope => }/common/middleware/logging.go | 18 +- .../common/middleware/middleware.go | 0 .../common/middleware/path_rewrite.go | 0 .../weaveworks/common/middleware/redirect.go | 48 +++ .../weaveworks/common/network/interface.go | 33 ++ .../weaveworks/common/sanitize/sanitize.go | 44 +++ .../github.com/weaveworks/common/test/diff.go | 22 ++ .../weaveworks/common/test/exec/exec.go | 79 +++++ .../weaveworks/common/test/fs/fs.go | 287 +++++++++++++++++ .../weaveworks/common/tools/cover/cover.go | 97 ++++++ .../weaveworks/common/tools/runner/runner.go | 289 ++++++++++++++++++ .../weaveworks/common/tools/socks/main.go | 97 ++++++ .../github.com/weaveworks/common/user}/id.go | 16 +- .../scope/common/instrument/LICENSE | 191 ------------ .../scope/common/middleware/LICENSE | 191 ------------ vendor/manifest | 179 +---------- 51 files changed, 2378 insertions(+), 599 deletions(-) create mode 100644 vendor/github.com/sercand/kuberesolver/balancer.go create mode 100644 vendor/github.com/sercand/kuberesolver/kubernetes.go create mode 100644 vendor/github.com/sercand/kuberesolver/models.go create mode 100644 vendor/github.com/sercand/kuberesolver/resolver.go create mode 100644 vendor/github.com/sercand/kuberesolver/stream.go create mode 100644 vendor/github.com/sercand/kuberesolver/util.go create mode 100644 vendor/github.com/sercand/kuberesolver/watcher.go rename vendor/github.com/weaveworks/common/{mtime => }/LICENSE (100%) create mode 100644 vendor/github.com/weaveworks/common/backoff/backoff.go create mode 100644 vendor/github.com/weaveworks/common/exec/exec.go create mode 100644 vendor/github.com/weaveworks/common/fs/fs.go create mode 100644 vendor/github.com/weaveworks/common/httpgrpc/httpgrpc.go create mode 100644 vendor/github.com/weaveworks/common/httpgrpc/httpgrpc.pb.go rename vendor/github.com/weaveworks/{scope => }/common/instrument/instrument.go (100%) create mode 100644 vendor/github.com/weaveworks/common/logging/logging.go create mode 100644 vendor/github.com/weaveworks/common/middleware/errorhandler.go rename util/middleware/auth.go => vendor/github.com/weaveworks/common/middleware/grpc_auth.go (84%) rename util/middleware/instrumentation.go => vendor/github.com/weaveworks/common/middleware/grpc_instrumentation.go (100%) rename util/middleware/logging.go => vendor/github.com/weaveworks/common/middleware/grpc_logging.go (94%) rename vendor/github.com/weaveworks/{scope => }/common/middleware/instrument.go (81%) rename vendor/github.com/weaveworks/{scope => }/common/middleware/logging.go (71%) rename vendor/github.com/weaveworks/{scope => }/common/middleware/middleware.go (100%) rename vendor/github.com/weaveworks/{scope => }/common/middleware/path_rewrite.go (100%) create mode 100644 vendor/github.com/weaveworks/common/middleware/redirect.go create mode 100644 vendor/github.com/weaveworks/common/network/interface.go create mode 100644 vendor/github.com/weaveworks/common/sanitize/sanitize.go create mode 100644 vendor/github.com/weaveworks/common/test/diff.go create mode 100644 vendor/github.com/weaveworks/common/test/exec/exec.go create mode 100644 vendor/github.com/weaveworks/common/test/fs/fs.go create mode 100644 vendor/github.com/weaveworks/common/tools/cover/cover.go create mode 100644 vendor/github.com/weaveworks/common/tools/runner/runner.go create mode 100644 vendor/github.com/weaveworks/common/tools/socks/main.go rename {user => vendor/github.com/weaveworks/common/user}/id.go (54%) delete mode 100644 vendor/github.com/weaveworks/scope/common/instrument/LICENSE delete mode 100644 vendor/github.com/weaveworks/scope/common/middleware/LICENSE 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 }, {