created docker-prometheus compose file and edited the Prometheus yml file
This commit is contained in:
168
prom/util/cli/cli.go
Normal file
168
prom/util/cli/cli.go
Normal file
@ -0,0 +1,168 @@
|
||||
// Copyright 2015 The Prometheus Authors
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package cli
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"sort"
|
||||
"strings"
|
||||
"text/template"
|
||||
)
|
||||
|
||||
// Command represents a single command within an application.
|
||||
type Command struct {
|
||||
Desc string
|
||||
Run func(t Term, args ...string) int
|
||||
}
|
||||
|
||||
// Term handles an application's output.
|
||||
type Term interface {
|
||||
Infof(format string, v ...interface{})
|
||||
Errorf(format string, v ...interface{})
|
||||
Out(format string)
|
||||
}
|
||||
|
||||
type basicTerm struct {
|
||||
out, err io.Writer
|
||||
}
|
||||
|
||||
// Infof implements Term.
|
||||
func (t *basicTerm) Infof(format string, v ...interface{}) {
|
||||
fmt.Fprintf(t.err, format, v...)
|
||||
fmt.Fprint(t.err, "\n")
|
||||
}
|
||||
|
||||
// Errorf implements Term.
|
||||
func (t *basicTerm) Errorf(format string, v ...interface{}) {
|
||||
fmt.Fprintf(t.err, format, v...)
|
||||
fmt.Fprint(t.err, "\n")
|
||||
}
|
||||
|
||||
// Out implements Term.
|
||||
func (t *basicTerm) Out(msg string) {
|
||||
fmt.Fprint(t.out, msg)
|
||||
fmt.Fprint(t.out, "\n")
|
||||
}
|
||||
|
||||
// BasicTerm returns a Term writing Infof and Errorf to err and Out to out.
|
||||
func BasicTerm(out, err io.Writer) Term {
|
||||
return &basicTerm{out: out, err: err}
|
||||
}
|
||||
|
||||
// App represents an application that may consist of multiple commands.
|
||||
type App struct {
|
||||
Name string
|
||||
Help func() string
|
||||
|
||||
commands map[string]*Command
|
||||
}
|
||||
|
||||
// NewApp creates a new application with a pre-registered help command.
|
||||
func NewApp(name string) *App {
|
||||
app := &App{
|
||||
Name: name,
|
||||
commands: map[string]*Command{},
|
||||
}
|
||||
app.Register("help", &Command{
|
||||
Desc: "prints this help text",
|
||||
Run: func(t Term, _ ...string) int {
|
||||
help := app.Help
|
||||
if help == nil {
|
||||
help = BasicHelp(app, tmpl)
|
||||
}
|
||||
t.Infof(help() + "\n")
|
||||
return 0
|
||||
},
|
||||
})
|
||||
return app
|
||||
}
|
||||
|
||||
// Register adds a new command to the application.
|
||||
func (app *App) Register(name string, cmd *Command) {
|
||||
name = strings.TrimSpace(name)
|
||||
if name == "" {
|
||||
panic("command name must not be empty")
|
||||
}
|
||||
if _, ok := app.commands[name]; ok {
|
||||
panic("command cannot be registered twice")
|
||||
}
|
||||
app.commands[name] = cmd
|
||||
}
|
||||
|
||||
// Run the application with the given arguments. Output is sent to t.
|
||||
func (app *App) Run(t Term, args ...string) int {
|
||||
help := app.commands["help"]
|
||||
|
||||
if len(args) == 0 || strings.HasPrefix(args[0], "-") {
|
||||
help.Run(t)
|
||||
return 2
|
||||
}
|
||||
cmd, ok := app.commands[args[0]]
|
||||
if !ok {
|
||||
help.Run(t)
|
||||
return 2
|
||||
}
|
||||
|
||||
return cmd.Run(t, args[1:]...)
|
||||
}
|
||||
|
||||
var tmpl = `
|
||||
usage: {{ .Name }} <command> [<args>]
|
||||
|
||||
Available commands:
|
||||
{{ range .Commands }}{{ .Name }} {{ .Desc }}
|
||||
{{ end }}
|
||||
`
|
||||
|
||||
// BasicHelp returns a function that creates a basic help text for the application
|
||||
// with its commands.
|
||||
func BasicHelp(app *App, ts string) func() string {
|
||||
t := template.Must(template.New("help").Parse(ts))
|
||||
|
||||
return func() string {
|
||||
type command struct {
|
||||
Name, Desc string
|
||||
}
|
||||
cmds := []command{}
|
||||
|
||||
var maxLen int
|
||||
names := []string{}
|
||||
for name := range app.commands {
|
||||
names = append(names, name)
|
||||
if len(name) > maxLen {
|
||||
maxLen = len(name)
|
||||
}
|
||||
}
|
||||
sort.Strings(names)
|
||||
|
||||
for _, name := range names {
|
||||
cmds = append(cmds, command{
|
||||
Name: name + strings.Repeat(" ", maxLen-len(name)),
|
||||
Desc: app.commands[name].Desc,
|
||||
})
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
t.Execute(&buf, struct {
|
||||
Name string
|
||||
Commands []command
|
||||
}{
|
||||
Name: app.Name,
|
||||
Commands: cmds,
|
||||
})
|
||||
return strings.TrimSpace(buf.String())
|
||||
}
|
||||
}
|
33
prom/util/flock/flock.go
Normal file
33
prom/util/flock/flock.go
Normal file
@ -0,0 +1,33 @@
|
||||
// Package flock provides portable file locking. It is essentially ripped out
|
||||
// from the code of github.com/syndtr/goleveldb. Strange enough that the
|
||||
// standard library does not provide this functionality. Once this package has
|
||||
// proven to work as expected, we should probably turn it into a separate
|
||||
// general purpose package for humanity.
|
||||
package flock
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
// Releaser provides the Release method to release a file lock.
|
||||
type Releaser interface {
|
||||
Release() error
|
||||
}
|
||||
|
||||
// New locks the file with the provided name. If the file does not exist, it is
|
||||
// created. The returned Releaser is used to release the lock. existed is true
|
||||
// if the file to lock already existed. A non-nil error is returned if the
|
||||
// locking has failed. Neither this function nor the returned Releaser is
|
||||
// goroutine-safe.
|
||||
func New(fileName string) (r Releaser, existed bool, err error) {
|
||||
if err = os.MkdirAll(filepath.Dir(fileName), 0755); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
_, err = os.Stat(fileName)
|
||||
existed = err == nil
|
||||
|
||||
r, err = newLock(fileName)
|
||||
return
|
||||
}
|
19
prom/util/flock/flock_plan9.go
Normal file
19
prom/util/flock/flock_plan9.go
Normal file
@ -0,0 +1,19 @@
|
||||
package flock
|
||||
|
||||
import "os"
|
||||
|
||||
type plan9Lock struct {
|
||||
f *os.File
|
||||
}
|
||||
|
||||
func (l *plan9Lock) Release() error {
|
||||
return l.f.Close()
|
||||
}
|
||||
|
||||
func newLock(fileName string) (Releaser, error) {
|
||||
f, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE, os.ModeExclusive|0644)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &plan9Lock{f}, nil
|
||||
}
|
46
prom/util/flock/flock_solaris.go
Normal file
46
prom/util/flock/flock_solaris.go
Normal file
@ -0,0 +1,46 @@
|
||||
// +build solaris
|
||||
|
||||
package flock
|
||||
|
||||
import (
|
||||
"os"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
type unixLock struct {
|
||||
f *os.File
|
||||
}
|
||||
|
||||
func (l *unixLock) Release() error {
|
||||
if err := l.set(false); err != nil {
|
||||
return err
|
||||
}
|
||||
return l.f.Close()
|
||||
}
|
||||
|
||||
func (l *unixLock) set(lock bool) error {
|
||||
flock := syscall.Flock_t{
|
||||
Type: syscall.F_UNLCK,
|
||||
Start: 0,
|
||||
Len: 0,
|
||||
Whence: 1,
|
||||
}
|
||||
if lock {
|
||||
flock.Type = syscall.F_WRLCK
|
||||
}
|
||||
return syscall.FcntlFlock(l.f.Fd(), syscall.F_SETLK, &flock)
|
||||
}
|
||||
|
||||
func newLock(fileName string) (Releaser, error) {
|
||||
f, err := os.OpenFile(fileName, os.O_RDWR|os.O_CREATE, 0644)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
l := &unixLock{f}
|
||||
err = l.set(true)
|
||||
if err != nil {
|
||||
f.Close()
|
||||
return nil, err
|
||||
}
|
||||
return l, nil
|
||||
}
|
67
prom/util/flock/flock_test.go
Normal file
67
prom/util/flock/flock_test.go
Normal file
@ -0,0 +1,67 @@
|
||||
package flock
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/prometheus/prometheus/util/testutil"
|
||||
)
|
||||
|
||||
func TestLocking(t *testing.T) {
|
||||
dir := testutil.NewTemporaryDirectory("test_flock", t)
|
||||
defer dir.Close()
|
||||
|
||||
fileName := filepath.Join(dir.Path(), "LOCK")
|
||||
|
||||
if _, err := os.Stat(fileName); err == nil {
|
||||
t.Fatalf("File %q unexpectedly exists.", fileName)
|
||||
}
|
||||
|
||||
lock, existed, err := New(fileName)
|
||||
if err != nil {
|
||||
t.Fatalf("Error locking file %q: %s", fileName, err)
|
||||
}
|
||||
if existed {
|
||||
t.Errorf("File %q reported as existing during locking.", fileName)
|
||||
}
|
||||
|
||||
// File must now exist.
|
||||
if _, err := os.Stat(fileName); err != nil {
|
||||
t.Errorf("Could not stat file %q expected to exist: %s", fileName, err)
|
||||
}
|
||||
|
||||
// Try to lock again.
|
||||
lockedAgain, existed, err := New(fileName)
|
||||
if err == nil {
|
||||
t.Fatalf("File %q locked twice.", fileName)
|
||||
}
|
||||
if lockedAgain != nil {
|
||||
t.Error("Unsuccessful locking did not return nil.")
|
||||
}
|
||||
if !existed {
|
||||
t.Errorf("Existing file %q not recognized.", fileName)
|
||||
}
|
||||
|
||||
if err := lock.Release(); err != nil {
|
||||
t.Errorf("Error releasing lock for file %q: %s", fileName, err)
|
||||
}
|
||||
|
||||
// File must still exist.
|
||||
if _, err := os.Stat(fileName); err != nil {
|
||||
t.Errorf("Could not stat file %q expected to exist: %s", fileName, err)
|
||||
}
|
||||
|
||||
// Lock existing file.
|
||||
lock, existed, err = New(fileName)
|
||||
if err != nil {
|
||||
t.Fatalf("Error locking file %q: %s", fileName, err)
|
||||
}
|
||||
if !existed {
|
||||
t.Errorf("Existing file %q not recognized.", fileName)
|
||||
}
|
||||
|
||||
if err := lock.Release(); err != nil {
|
||||
t.Errorf("Error releasing lock for file %q: %s", fileName, err)
|
||||
}
|
||||
}
|
41
prom/util/flock/flock_unix.go
Normal file
41
prom/util/flock/flock_unix.go
Normal file
@ -0,0 +1,41 @@
|
||||
// +build darwin dragonfly freebsd linux netbsd openbsd
|
||||
|
||||
package flock
|
||||
|
||||
import (
|
||||
"os"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
type unixLock struct {
|
||||
f *os.File
|
||||
}
|
||||
|
||||
func (l *unixLock) Release() error {
|
||||
if err := l.set(false); err != nil {
|
||||
return err
|
||||
}
|
||||
return l.f.Close()
|
||||
}
|
||||
|
||||
func (l *unixLock) set(lock bool) error {
|
||||
how := syscall.LOCK_UN
|
||||
if lock {
|
||||
how = syscall.LOCK_EX
|
||||
}
|
||||
return syscall.Flock(int(l.f.Fd()), how|syscall.LOCK_NB)
|
||||
}
|
||||
|
||||
func newLock(fileName string) (Releaser, error) {
|
||||
f, err := os.OpenFile(fileName, os.O_RDWR|os.O_CREATE, 0644)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
l := &unixLock{f}
|
||||
err = l.set(true)
|
||||
if err != nil {
|
||||
f.Close()
|
||||
return nil, err
|
||||
}
|
||||
return l, nil
|
||||
}
|
23
prom/util/flock/flock_windows.go
Normal file
23
prom/util/flock/flock_windows.go
Normal file
@ -0,0 +1,23 @@
|
||||
package flock
|
||||
|
||||
import "syscall"
|
||||
|
||||
type windowsLock struct {
|
||||
fd syscall.Handle
|
||||
}
|
||||
|
||||
func (fl *windowsLock) Release() error {
|
||||
return syscall.Close(fl.fd)
|
||||
}
|
||||
|
||||
func newLock(fileName string) (Releaser, error) {
|
||||
pathp, err := syscall.UTF16PtrFromString(fileName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
fd, err := syscall.CreateFile(pathp, syscall.GENERIC_READ|syscall.GENERIC_WRITE, 0, nil, syscall.CREATE_ALWAYS, syscall.FILE_ATTRIBUTE_NORMAL, 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &windowsLock{fd}, nil
|
||||
}
|
110
prom/util/httputil/client.go
Normal file
110
prom/util/httputil/client.go
Normal file
@ -0,0 +1,110 @@
|
||||
// Copyright 2013 The Prometheus Authors
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package httputil
|
||||
|
||||
import (
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"time"
|
||||
)
|
||||
|
||||
// NewClient returns a http.Client using the specified http.RoundTripper.
|
||||
func NewClient(rt http.RoundTripper) *http.Client {
|
||||
return &http.Client{Transport: rt}
|
||||
}
|
||||
|
||||
// NewDeadlineClient returns a new http.Client which will time out long running
|
||||
// requests.
|
||||
func NewDeadlineClient(timeout time.Duration, proxyURL *url.URL) *http.Client {
|
||||
return NewClient(NewDeadlineRoundTripper(timeout, proxyURL))
|
||||
}
|
||||
|
||||
// NewDeadlineRoundTripper returns a new http.RoundTripper which will time out
|
||||
// long running requests.
|
||||
func NewDeadlineRoundTripper(timeout time.Duration, proxyURL *url.URL) http.RoundTripper {
|
||||
return &http.Transport{
|
||||
// Set proxy (if null, then becomes a direct connection)
|
||||
Proxy: http.ProxyURL(proxyURL),
|
||||
// We need to disable keepalive, because we set a deadline on the
|
||||
// underlying connection.
|
||||
DisableKeepAlives: true,
|
||||
Dial: func(netw, addr string) (c net.Conn, err error) {
|
||||
start := time.Now()
|
||||
|
||||
c, err = net.DialTimeout(netw, addr, timeout)
|
||||
|
||||
if err == nil {
|
||||
c.SetDeadline(start.Add(timeout))
|
||||
}
|
||||
|
||||
return
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
type bearerAuthRoundTripper struct {
|
||||
bearerToken string
|
||||
rt http.RoundTripper
|
||||
}
|
||||
|
||||
// NewBearerAuthRoundTripper adds the provided bearer token to a request unless the authorization
|
||||
// header has already been set.
|
||||
func NewBearerAuthRoundTripper(bearer string, rt http.RoundTripper) http.RoundTripper {
|
||||
return &bearerAuthRoundTripper{bearer, rt}
|
||||
}
|
||||
|
||||
func (rt *bearerAuthRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||
if len(req.Header.Get("Authorization")) == 0 {
|
||||
req = cloneRequest(req)
|
||||
req.Header.Set("Authorization", "Bearer "+rt.bearerToken)
|
||||
}
|
||||
|
||||
return rt.rt.RoundTrip(req)
|
||||
}
|
||||
|
||||
type basicAuthRoundTripper struct {
|
||||
username string
|
||||
password string
|
||||
rt http.RoundTripper
|
||||
}
|
||||
|
||||
// NewBasicAuthRoundTripper will apply a BASIC auth authorization header to a request unless it has
|
||||
// already been set.
|
||||
func NewBasicAuthRoundTripper(username, password string, rt http.RoundTripper) http.RoundTripper {
|
||||
return &basicAuthRoundTripper{username, password, rt}
|
||||
}
|
||||
|
||||
func (rt *basicAuthRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||
if len(req.Header.Get("Authorization")) != 0 {
|
||||
return rt.rt.RoundTrip(req)
|
||||
}
|
||||
req = cloneRequest(req)
|
||||
req.SetBasicAuth(rt.username, rt.password)
|
||||
return rt.rt.RoundTrip(req)
|
||||
}
|
||||
|
||||
// cloneRequest returns a clone of the provided *http.Request.
|
||||
// The clone is a shallow copy of the struct and its Header map.
|
||||
func cloneRequest(r *http.Request) *http.Request {
|
||||
// Shallow copy of the struct.
|
||||
r2 := new(http.Request)
|
||||
*r2 = *r
|
||||
// Deep copy of the Header.
|
||||
r2.Header = make(http.Header)
|
||||
for k, s := range r.Header {
|
||||
r2.Header[k] = s
|
||||
}
|
||||
return r2
|
||||
}
|
92
prom/util/httputil/compression.go
Normal file
92
prom/util/httputil/compression.go
Normal file
@ -0,0 +1,92 @@
|
||||
// Copyright 2013 The Prometheus Authors
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package httputil
|
||||
|
||||
import (
|
||||
"compress/gzip"
|
||||
"compress/zlib"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
acceptEncodingHeader = "Accept-Encoding"
|
||||
contentEncodingHeader = "Content-Encoding"
|
||||
gzipEncoding = "gzip"
|
||||
deflateEncoding = "deflate"
|
||||
)
|
||||
|
||||
// Wrapper around http.Handler which adds suitable response compression based
|
||||
// on the client's Accept-Encoding headers.
|
||||
type compressedResponseWriter struct {
|
||||
http.ResponseWriter
|
||||
writer io.Writer
|
||||
}
|
||||
|
||||
// Writes HTTP response content data.
|
||||
func (c *compressedResponseWriter) Write(p []byte) (int, error) {
|
||||
return c.writer.Write(p)
|
||||
}
|
||||
|
||||
// Closes the compressedResponseWriter and ensures to flush all data before.
|
||||
func (c *compressedResponseWriter) Close() {
|
||||
if zlibWriter, ok := c.writer.(*zlib.Writer); ok {
|
||||
zlibWriter.Flush()
|
||||
}
|
||||
if gzipWriter, ok := c.writer.(*gzip.Writer); ok {
|
||||
gzipWriter.Flush()
|
||||
}
|
||||
if closer, ok := c.writer.(io.Closer); ok {
|
||||
defer closer.Close()
|
||||
}
|
||||
}
|
||||
|
||||
// Constructs a new compressedResponseWriter based on client request headers.
|
||||
func newCompressedResponseWriter(writer http.ResponseWriter, req *http.Request) *compressedResponseWriter {
|
||||
encodings := strings.Split(req.Header.Get(acceptEncodingHeader), ",")
|
||||
for _, encoding := range encodings {
|
||||
switch strings.TrimSpace(encoding) {
|
||||
case gzipEncoding:
|
||||
writer.Header().Set(contentEncodingHeader, gzipEncoding)
|
||||
return &compressedResponseWriter{
|
||||
ResponseWriter: writer,
|
||||
writer: gzip.NewWriter(writer),
|
||||
}
|
||||
case deflateEncoding:
|
||||
writer.Header().Set(contentEncodingHeader, deflateEncoding)
|
||||
return &compressedResponseWriter{
|
||||
ResponseWriter: writer,
|
||||
writer: zlib.NewWriter(writer),
|
||||
}
|
||||
}
|
||||
}
|
||||
return &compressedResponseWriter{
|
||||
ResponseWriter: writer,
|
||||
writer: writer,
|
||||
}
|
||||
}
|
||||
|
||||
// CompressionHandler is a wrapper around http.Handler which adds suitable
|
||||
// response compression based on the client's Accept-Encoding headers.
|
||||
type CompressionHandler struct {
|
||||
Handler http.Handler
|
||||
}
|
||||
|
||||
// ServeHTTP adds compression to the original http.Handler's ServeHTTP() method.
|
||||
func (c CompressionHandler) ServeHTTP(writer http.ResponseWriter, req *http.Request) {
|
||||
compWriter := newCompressedResponseWriter(writer, req)
|
||||
c.Handler.ServeHTTP(compWriter, req)
|
||||
compWriter.Close()
|
||||
}
|
102
prom/util/route/route.go
Normal file
102
prom/util/route/route.go
Normal file
@ -0,0 +1,102 @@
|
||||
package route
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"sync"
|
||||
|
||||
"github.com/julienschmidt/httprouter"
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
var (
|
||||
mtx = sync.RWMutex{}
|
||||
ctxts = map[*http.Request]context.Context{}
|
||||
)
|
||||
|
||||
// Context returns the context for the request.
|
||||
func Context(r *http.Request) context.Context {
|
||||
mtx.RLock()
|
||||
defer mtx.RUnlock()
|
||||
return ctxts[r]
|
||||
}
|
||||
|
||||
type param string
|
||||
|
||||
// Param returns param p for the context.
|
||||
func Param(ctx context.Context, p string) string {
|
||||
return ctx.Value(param(p)).(string)
|
||||
}
|
||||
|
||||
// WithParam returns a new context with param p set to v.
|
||||
func WithParam(ctx context.Context, p, v string) context.Context {
|
||||
return context.WithValue(ctx, param(p), v)
|
||||
}
|
||||
|
||||
// handle turns a Handle into httprouter.Handle
|
||||
func handle(h http.HandlerFunc) httprouter.Handle {
|
||||
return func(w http.ResponseWriter, r *http.Request, params httprouter.Params) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
for _, p := range params {
|
||||
ctx = context.WithValue(ctx, param(p.Key), p.Value)
|
||||
}
|
||||
|
||||
mtx.Lock()
|
||||
ctxts[r] = ctx
|
||||
mtx.Unlock()
|
||||
|
||||
h(w, r)
|
||||
|
||||
mtx.Lock()
|
||||
delete(ctxts, r)
|
||||
mtx.Unlock()
|
||||
}
|
||||
}
|
||||
|
||||
// Router wraps httprouter.Router and adds support for prefixed sub-routers.
|
||||
type Router struct {
|
||||
rtr *httprouter.Router
|
||||
prefix string
|
||||
}
|
||||
|
||||
// New returns a new Router.
|
||||
func New() *Router {
|
||||
return &Router{rtr: httprouter.New()}
|
||||
}
|
||||
|
||||
// WithPrefix returns a router that prefixes all registered routes with prefix.
|
||||
func (r *Router) WithPrefix(prefix string) *Router {
|
||||
return &Router{rtr: r.rtr, prefix: r.prefix + prefix}
|
||||
}
|
||||
|
||||
// Get registers a new GET route.
|
||||
func (r *Router) Get(path string, h http.HandlerFunc) {
|
||||
r.rtr.GET(r.prefix+path, handle(h))
|
||||
}
|
||||
|
||||
// Del registers a new DELETE route.
|
||||
func (r *Router) Del(path string, h http.HandlerFunc) {
|
||||
r.rtr.DELETE(r.prefix+path, handle(h))
|
||||
}
|
||||
|
||||
// Post registers a new POST route.
|
||||
func (r *Router) Post(path string, h http.HandlerFunc) {
|
||||
r.rtr.POST(r.prefix+path, handle(h))
|
||||
}
|
||||
|
||||
// ServeHTTP implements http.Handler.
|
||||
func (r *Router) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||
r.rtr.ServeHTTP(w, req)
|
||||
}
|
||||
|
||||
// FileServe returns a new http.HandlerFunc that serves files from dir.
|
||||
// Using routes must provide the *filepath parameter.
|
||||
func FileServe(dir string) http.HandlerFunc {
|
||||
fs := http.FileServer(http.Dir(dir))
|
||||
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
r.URL.Path = Param(Context(r), "filepath")
|
||||
fs.ServeHTTP(w, r)
|
||||
}
|
||||
}
|
78
prom/util/stats/query_stats.go
Normal file
78
prom/util/stats/query_stats.go
Normal file
@ -0,0 +1,78 @@
|
||||
// Copyright 2013 The Prometheus Authors
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package stats
|
||||
|
||||
// QueryTiming identifies the code area or functionality in which time is spent
|
||||
// during a query.
|
||||
type QueryTiming int
|
||||
|
||||
// Query timings.
|
||||
const (
|
||||
TotalEvalTime QueryTiming = iota
|
||||
ResultSortTime
|
||||
JSONEncodeTime
|
||||
PreloadTime
|
||||
TotalQueryPreparationTime
|
||||
InnerViewBuildingTime
|
||||
InnerEvalTime
|
||||
ResultAppendTime
|
||||
QueryAnalysisTime
|
||||
GetValueAtTimeTime
|
||||
GetBoundaryValuesTime
|
||||
GetRangeValuesTime
|
||||
ExecQueueTime
|
||||
ViewDiskPreparationTime
|
||||
ViewDataExtractionTime
|
||||
ViewDiskExtractionTime
|
||||
)
|
||||
|
||||
// Return a string represenation of a QueryTiming identifier.
|
||||
func (s QueryTiming) String() string {
|
||||
switch s {
|
||||
case TotalEvalTime:
|
||||
return "Total eval time"
|
||||
case ResultSortTime:
|
||||
return "Result sorting time"
|
||||
case JSONEncodeTime:
|
||||
return "JSON encoding time"
|
||||
case PreloadTime:
|
||||
return "Query preloading time"
|
||||
case TotalQueryPreparationTime:
|
||||
return "Total query preparation time"
|
||||
case InnerViewBuildingTime:
|
||||
return "Inner view building time"
|
||||
case InnerEvalTime:
|
||||
return "Inner eval time"
|
||||
case ResultAppendTime:
|
||||
return "Result append time"
|
||||
case QueryAnalysisTime:
|
||||
return "Query analysis time"
|
||||
case GetValueAtTimeTime:
|
||||
return "GetValueAtTime() time"
|
||||
case GetBoundaryValuesTime:
|
||||
return "GetBoundaryValues() time"
|
||||
case GetRangeValuesTime:
|
||||
return "GetRangeValues() time"
|
||||
case ExecQueueTime:
|
||||
return "Exec queue wait time"
|
||||
case ViewDiskPreparationTime:
|
||||
return "View building disk preparation time"
|
||||
case ViewDataExtractionTime:
|
||||
return "Total view data extraction time"
|
||||
case ViewDiskExtractionTime:
|
||||
return "View disk data extraction time"
|
||||
default:
|
||||
return "Unknown query timing"
|
||||
}
|
||||
}
|
109
prom/util/stats/timer.go
Normal file
109
prom/util/stats/timer.go
Normal file
@ -0,0 +1,109 @@
|
||||
// Copyright 2013 The Prometheus Authors
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package stats
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"sort"
|
||||
"time"
|
||||
)
|
||||
|
||||
// A Timer that can be started and stopped and accumulates the total time it
|
||||
// was running (the time between Start() and Stop()).
|
||||
type Timer struct {
|
||||
name fmt.Stringer
|
||||
created time.Time
|
||||
start time.Time
|
||||
duration time.Duration
|
||||
}
|
||||
|
||||
// Start the timer.
|
||||
func (t *Timer) Start() *Timer {
|
||||
t.start = time.Now()
|
||||
return t
|
||||
}
|
||||
|
||||
// Stop the timer.
|
||||
func (t *Timer) Stop() {
|
||||
t.duration += time.Since(t.start)
|
||||
}
|
||||
|
||||
// ElapsedTime returns the time that passed since starting the timer.
|
||||
func (t *Timer) ElapsedTime() time.Duration {
|
||||
return time.Since(t.start)
|
||||
}
|
||||
|
||||
// Return a string representation of the Timer.
|
||||
func (t *Timer) String() string {
|
||||
return fmt.Sprintf("%s: %s", t.name, t.duration)
|
||||
}
|
||||
|
||||
// A TimerGroup represents a group of timers relevant to a single query.
|
||||
type TimerGroup struct {
|
||||
timers map[fmt.Stringer]*Timer
|
||||
child *TimerGroup
|
||||
}
|
||||
|
||||
// NewTimerGroup constructs a new TimerGroup.
|
||||
func NewTimerGroup() *TimerGroup {
|
||||
return &TimerGroup{timers: map[fmt.Stringer]*Timer{}}
|
||||
}
|
||||
|
||||
// GetTimer gets (and creates, if necessary) the Timer for a given code section.
|
||||
func (t *TimerGroup) GetTimer(name fmt.Stringer) *Timer {
|
||||
if timer, exists := t.timers[name]; exists {
|
||||
return timer
|
||||
}
|
||||
timer := &Timer{
|
||||
name: name,
|
||||
created: time.Now(),
|
||||
}
|
||||
t.timers[name] = timer
|
||||
return timer
|
||||
}
|
||||
|
||||
// Timers is a slice of Timer pointers that implements Len and Swap from
|
||||
// sort.Interface.
|
||||
type Timers []*Timer
|
||||
|
||||
type byCreationTimeSorter struct{ Timers }
|
||||
|
||||
// Len implements sort.Interface.
|
||||
func (t Timers) Len() int {
|
||||
return len(t)
|
||||
}
|
||||
|
||||
// Swap implements sort.Interface.
|
||||
func (t Timers) Swap(i, j int) {
|
||||
t[i], t[j] = t[j], t[i]
|
||||
}
|
||||
|
||||
func (s byCreationTimeSorter) Less(i, j int) bool {
|
||||
return s.Timers[i].created.Before(s.Timers[j].created)
|
||||
}
|
||||
|
||||
// Return a string representation of a TimerGroup.
|
||||
func (t *TimerGroup) String() string {
|
||||
timers := byCreationTimeSorter{}
|
||||
for _, timer := range t.timers {
|
||||
timers.Timers = append(timers.Timers, timer)
|
||||
}
|
||||
sort.Sort(timers)
|
||||
result := &bytes.Buffer{}
|
||||
for _, timer := range timers.Timers {
|
||||
fmt.Fprintf(result, "%s\n", timer)
|
||||
}
|
||||
return result.String()
|
||||
}
|
114
prom/util/strutil/strconv.go
Normal file
114
prom/util/strutil/strconv.go
Normal file
@ -0,0 +1,114 @@
|
||||
// Copyright 2013 The Prometheus Authors
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package strutil
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
durationRE = regexp.MustCompile("^([0-9]+)([ywdhms]+)$")
|
||||
invalidLabelCharRE = regexp.MustCompile(`[^a-zA-Z0-9_]`)
|
||||
)
|
||||
|
||||
// DurationToString formats a time.Duration as a string with the assumption that
|
||||
// a year always has 365 days and a day always has 24h. (The former doesn't work
|
||||
// in leap years, the latter is broken by DST switches, not to speak about leap
|
||||
// seconds, but those are not even treated properly by the duration strings in
|
||||
// the standard library.)
|
||||
func DurationToString(duration time.Duration) string {
|
||||
seconds := int64(duration / time.Second)
|
||||
factors := map[string]int64{
|
||||
"y": 60 * 60 * 24 * 365,
|
||||
"d": 60 * 60 * 24,
|
||||
"h": 60 * 60,
|
||||
"m": 60,
|
||||
"s": 1,
|
||||
}
|
||||
unit := "s"
|
||||
switch int64(0) {
|
||||
case seconds % factors["y"]:
|
||||
unit = "y"
|
||||
case seconds % factors["d"]:
|
||||
unit = "d"
|
||||
case seconds % factors["h"]:
|
||||
unit = "h"
|
||||
case seconds % factors["m"]:
|
||||
unit = "m"
|
||||
}
|
||||
return fmt.Sprintf("%v%v", seconds/factors[unit], unit)
|
||||
}
|
||||
|
||||
// StringToDuration parses a string into a time.Duration, assuming that a year
|
||||
// always has 365d, a week 7d, a day 24h. See DurationToString for problems with
|
||||
// that.
|
||||
func StringToDuration(durationStr string) (duration time.Duration, err error) {
|
||||
matches := durationRE.FindStringSubmatch(durationStr)
|
||||
if len(matches) != 3 {
|
||||
err = fmt.Errorf("not a valid duration string: %q", durationStr)
|
||||
return
|
||||
}
|
||||
durationSeconds, _ := strconv.Atoi(matches[1])
|
||||
duration = time.Duration(durationSeconds) * time.Second
|
||||
unit := matches[2]
|
||||
switch unit {
|
||||
case "y":
|
||||
duration *= 60 * 60 * 24 * 365
|
||||
case "w":
|
||||
duration *= 60 * 60 * 24 * 7
|
||||
case "d":
|
||||
duration *= 60 * 60 * 24
|
||||
case "h":
|
||||
duration *= 60 * 60
|
||||
case "m":
|
||||
duration *= 60
|
||||
case "s":
|
||||
duration *= 1
|
||||
default:
|
||||
return 0, fmt.Errorf("invalid time unit in duration string: %q", unit)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// TableLinkForExpression creates an escaped relative link to the table view of
|
||||
// the provided expression.
|
||||
func TableLinkForExpression(expr string) string {
|
||||
// url.QueryEscape percent-escapes everything except spaces, for which it
|
||||
// uses "+". However, in the non-query part of a URI, only percent-escaped
|
||||
// spaces are legal, so we need to manually replace "+" with "%20" after
|
||||
// query-escaping the string.
|
||||
//
|
||||
// See also:
|
||||
// http://stackoverflow.com/questions/1634271/url-encoding-the-space-character-or-20.
|
||||
urlData := url.QueryEscape(fmt.Sprintf(`[{"expr":%q,"tab":1}]`, expr))
|
||||
return fmt.Sprintf("/graph#%s", strings.Replace(urlData, "+", "%20", -1))
|
||||
}
|
||||
|
||||
// GraphLinkForExpression creates an escaped relative link to the graph view of
|
||||
// the provided expression.
|
||||
func GraphLinkForExpression(expr string) string {
|
||||
urlData := url.QueryEscape(fmt.Sprintf(`[{"expr":%q,"tab":0}]`, expr))
|
||||
return fmt.Sprintf("/graph#%s", strings.Replace(urlData, "+", "%20", -1))
|
||||
}
|
||||
|
||||
// SanitizeLabelName replaces anything that doesn't match
|
||||
// client_label.LabelNameRE with an underscore.
|
||||
func SanitizeLabelName(name string) string {
|
||||
return invalidLabelCharRE.ReplaceAllString(name, "_")
|
||||
}
|
121
prom/util/testutil/directory.go
Normal file
121
prom/util/testutil/directory.go
Normal file
@ -0,0 +1,121 @@
|
||||
// Copyright 2013 The Prometheus Authors
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package testutil
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
)
|
||||
|
||||
const (
|
||||
// The base directory used for test emissions, which instructs the operating
|
||||
// system to use the default temporary directory as the base or TMPDIR
|
||||
// environment variable.
|
||||
defaultDirectory = ""
|
||||
|
||||
// NilCloser is a no-op Closer.
|
||||
NilCloser = nilCloser(true)
|
||||
)
|
||||
|
||||
type (
|
||||
// Closer is the interface that wraps the Close method.
|
||||
Closer interface {
|
||||
// Close reaps the underlying directory and its children. The directory
|
||||
// could be deleted by its users already.
|
||||
Close()
|
||||
}
|
||||
|
||||
nilCloser bool
|
||||
|
||||
// TemporaryDirectory models a closeable path for transient POSIX disk
|
||||
// activities.
|
||||
TemporaryDirectory interface {
|
||||
Closer
|
||||
|
||||
// Path returns the underlying path for access.
|
||||
Path() string
|
||||
}
|
||||
|
||||
// temporaryDirectory is kept as a private type due to private fields and
|
||||
// their interactions.
|
||||
temporaryDirectory struct {
|
||||
path string
|
||||
tester T
|
||||
}
|
||||
|
||||
callbackCloser struct {
|
||||
fn func()
|
||||
}
|
||||
|
||||
// T implements the needed methods of testing.TB so that we do not need
|
||||
// to actually import testing (which has the side affect of adding all
|
||||
// the test flags, which we do not want in non-test binaries even if
|
||||
// they make use of these utilities for some reason).
|
||||
T interface {
|
||||
Fatal(args ...interface{})
|
||||
Fatalf(format string, args ...interface{})
|
||||
}
|
||||
)
|
||||
|
||||
func (c nilCloser) Close() {
|
||||
}
|
||||
|
||||
func (c callbackCloser) Close() {
|
||||
c.fn()
|
||||
}
|
||||
|
||||
// NewCallbackCloser returns a Closer that calls the provided function upon
|
||||
// closing.
|
||||
func NewCallbackCloser(fn func()) *callbackCloser {
|
||||
return &callbackCloser{
|
||||
fn: fn,
|
||||
}
|
||||
}
|
||||
|
||||
func (t temporaryDirectory) Close() {
|
||||
err := os.RemoveAll(t.path)
|
||||
if err != nil {
|
||||
switch {
|
||||
case os.IsNotExist(err):
|
||||
return
|
||||
default:
|
||||
t.tester.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (t temporaryDirectory) Path() string {
|
||||
return t.path
|
||||
}
|
||||
|
||||
// NewTemporaryDirectory creates a new temporary directory for transient POSIX
|
||||
// activities.
|
||||
func NewTemporaryDirectory(name string, t T) (handler TemporaryDirectory) {
|
||||
var (
|
||||
directory string
|
||||
err error
|
||||
)
|
||||
|
||||
directory, err = ioutil.TempDir(defaultDirectory, name)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
handler = temporaryDirectory{
|
||||
path: directory,
|
||||
tester: t,
|
||||
}
|
||||
|
||||
return
|
||||
}
|
31
prom/util/testutil/error.go
Normal file
31
prom/util/testutil/error.go
Normal file
@ -0,0 +1,31 @@
|
||||
// Copyright 2013 The Prometheus Authors
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package testutil
|
||||
|
||||
// ErrorEqual compares Go errors for equality.
|
||||
func ErrorEqual(left, right error) bool {
|
||||
if left == right {
|
||||
return true
|
||||
}
|
||||
|
||||
if left != nil && right != nil {
|
||||
if left.Error() == right.Error() {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
Reference in New Issue
Block a user