mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Merge branch 'master' into websocket
This commit is contained in:
commit
5b6754ce6c
@ -1,6 +1,13 @@
|
||||
# http://editorconfig.org
|
||||
root = true
|
||||
|
||||
[*.go]
|
||||
indent_style = tabs
|
||||
indent_size = 2
|
||||
charset = utf-8
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
||||
|
||||
[*]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
|
12
.github/ISSUE_TEMPLATE.md
vendored
Normal file
12
.github/ISSUE_TEMPLATE.md
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
Thank you! For helping us make Grafana even better.
|
||||
|
||||
To help us respond to your issues faster, please make sure to add as much information as possible.
|
||||
|
||||
If this issue is about a plugin, please open the issue in that repository.
|
||||
|
||||
Start your issues title with [Feature Request] / [Bug] / [Question] or no tag if your unsure.
|
||||
|
||||
Ex
|
||||
* What grafana version are you using?
|
||||
* What datasource are you using?
|
||||
* What OS are you running grafana on?
|
2
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
2
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
* Link the PR to an issue for new features
|
||||
* Rebase your PR if it gets out of sync with master
|
@ -25,6 +25,7 @@
|
||||
* **graph**: Template variables can now be used in TimeShift and TimeFrom, closes[#1960](https://github.com/grafana/grafana/issues/1960)
|
||||
* **Tooltip**: Optionally add milliseconds to timestamp in tool tip, closes[#2248](https://github.com/grafana/grafana/issues/2248)
|
||||
* **Opentsdb**: Support milliseconds when using openTSDB datasource, closes [#2865](https://github.com/grafana/grafana/issues/2865)
|
||||
* **Opentsdb**: Add support for annotations, closes[#664](https://github.com/grafana/grafana/issues/664)
|
||||
|
||||
### Bug fixes
|
||||
* **Playlist**: Fix for memory leak when running a playlist, closes [#3794](https://github.com/grafana/grafana/pull/3794)
|
||||
|
18
Godeps/Godeps.json
generated
18
Godeps/Godeps.json
generated
@ -64,6 +64,11 @@
|
||||
"Comment": "v1.0.0",
|
||||
"Rev": "abb928e07c4108683d6b4d0b6ca08fe6bc0eee5f"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/bmizerany/assert",
|
||||
"Comment": "release.r60-6-ge17e998",
|
||||
"Rev": "e17e99893cb6509f428e1728281c2ad60a6b31e3"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/bradfitz/gomemcache/memcache",
|
||||
"Comment": "release.r60-40-g72a6864",
|
||||
@ -123,10 +128,6 @@
|
||||
"Comment": "v0.4.4-44-gf561133",
|
||||
"Rev": "f56113384f2c63dfe4cd8e768e349f1c35122b58"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/gopherjs/gopherjs/js",
|
||||
"Rev": "14d893dca2e4adb93a5ccc9494040acc0821cd8d"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/gosimple/slug",
|
||||
"Rev": "8d258463b4459f161f51d6a357edacd3eef9d663"
|
||||
@ -160,6 +161,15 @@
|
||||
"ImportPath": "github.com/klauspost/crc32",
|
||||
"Rev": "6834731faf32e62a2dd809d99fb24d1e4ae5a92d"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/kr/pretty",
|
||||
"Comment": "go.weekly.2011-12-22-27-ge6ac2fc",
|
||||
"Rev": "e6ac2fc51e89a3249e82157fa0bb7a18ef9dd5bb"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/kr/text",
|
||||
"Rev": "bb797dc4fb8320488f47bf11de07a733d7233e1f"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/lib/pq",
|
||||
"Comment": "go1.0-cutoff-13-g19eeca3",
|
||||
|
7
Godeps/_workspace/src/github.com/bmizerany/assert/.gitignore
generated
vendored
Normal file
7
Godeps/_workspace/src/github.com/bmizerany/assert/.gitignore
generated
vendored
Normal file
@ -0,0 +1,7 @@
|
||||
_go_.*
|
||||
_gotest_.*
|
||||
_obj
|
||||
_test
|
||||
_testmain.go
|
||||
*.out
|
||||
*.[568]
|
45
Godeps/_workspace/src/github.com/bmizerany/assert/README.md
generated
vendored
Normal file
45
Godeps/_workspace/src/github.com/bmizerany/assert/README.md
generated
vendored
Normal file
@ -0,0 +1,45 @@
|
||||
# Assert (c) Blake Mizerany and Keith Rarick -- MIT LICENCE
|
||||
|
||||
## Assertions for Go tests
|
||||
|
||||
## Install
|
||||
|
||||
$ go get github.com/bmizerany/assert
|
||||
|
||||
## Use
|
||||
|
||||
**point.go**
|
||||
|
||||
package point
|
||||
|
||||
type Point struct {
|
||||
x, y int
|
||||
}
|
||||
|
||||
**point_test.go**
|
||||
|
||||
|
||||
package point
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"github.com/bmizerany/assert"
|
||||
)
|
||||
|
||||
func TestAsserts(t *testing.T) {
|
||||
p1 := Point{1, 1}
|
||||
p2 := Point{2, 1}
|
||||
|
||||
assert.Equal(t, p1, p2)
|
||||
}
|
||||
|
||||
**output**
|
||||
$ go test
|
||||
--- FAIL: TestAsserts (0.00 seconds)
|
||||
assert.go:15: /Users/flavio.barbosa/dev/stewie/src/point_test.go:12
|
||||
assert.go:24: ! X: 1 != 2
|
||||
FAIL
|
||||
|
||||
## Docs
|
||||
|
||||
http://github.com/bmizerany/assert
|
76
Godeps/_workspace/src/github.com/bmizerany/assert/assert.go
generated
vendored
Normal file
76
Godeps/_workspace/src/github.com/bmizerany/assert/assert.go
generated
vendored
Normal file
@ -0,0 +1,76 @@
|
||||
package assert
|
||||
// Testing helpers for doozer.
|
||||
|
||||
import (
|
||||
"github.com/kr/pretty"
|
||||
"reflect"
|
||||
"testing"
|
||||
"runtime"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func assert(t *testing.T, result bool, f func(), cd int) {
|
||||
if !result {
|
||||
_, file, line, _ := runtime.Caller(cd + 1)
|
||||
t.Errorf("%s:%d", file, line)
|
||||
f()
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
func equal(t *testing.T, exp, got interface{}, cd int, args ...interface{}) {
|
||||
fn := func() {
|
||||
for _, desc := range pretty.Diff(exp, got) {
|
||||
t.Error("!", desc)
|
||||
}
|
||||
if len(args) > 0 {
|
||||
t.Error("!", " -", fmt.Sprint(args...))
|
||||
}
|
||||
}
|
||||
result := reflect.DeepEqual(exp, got)
|
||||
assert(t, result, fn, cd+1)
|
||||
}
|
||||
|
||||
func tt(t *testing.T, result bool, cd int, args ...interface{}) {
|
||||
fn := func() {
|
||||
t.Errorf("! Failure")
|
||||
if len(args) > 0 {
|
||||
t.Error("!", " -", fmt.Sprint(args...))
|
||||
}
|
||||
}
|
||||
assert(t, result, fn, cd+1)
|
||||
}
|
||||
|
||||
func T(t *testing.T, result bool, args ...interface{}) {
|
||||
tt(t, result, 1, args...)
|
||||
}
|
||||
|
||||
func Tf(t *testing.T, result bool, format string, args ...interface{}) {
|
||||
tt(t, result, 1, fmt.Sprintf(format, args...))
|
||||
}
|
||||
|
||||
func Equal(t *testing.T, exp, got interface{}, args ...interface{}) {
|
||||
equal(t, exp, got, 1, args...)
|
||||
}
|
||||
|
||||
func Equalf(t *testing.T, exp, got interface{}, format string, args ...interface{}) {
|
||||
equal(t, exp, got, 1, fmt.Sprintf(format, args...))
|
||||
}
|
||||
|
||||
func NotEqual(t *testing.T, exp, got interface{}, args ...interface{}) {
|
||||
fn := func() {
|
||||
t.Errorf("! Unexpected: <%#v>", exp)
|
||||
if len(args) > 0 {
|
||||
t.Error("!", " -", fmt.Sprint(args...))
|
||||
}
|
||||
}
|
||||
result := !reflect.DeepEqual(exp, got)
|
||||
assert(t, result, fn, 1)
|
||||
}
|
||||
|
||||
func Panic(t *testing.T, err interface{}, fn func()) {
|
||||
defer func() {
|
||||
equal(t, err, recover(), 3)
|
||||
}()
|
||||
fn()
|
||||
}
|
15
Godeps/_workspace/src/github.com/bmizerany/assert/assert_test.go
generated
vendored
Normal file
15
Godeps/_workspace/src/github.com/bmizerany/assert/assert_test.go
generated
vendored
Normal file
@ -0,0 +1,15 @@
|
||||
package assert
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestLineNumbers(t *testing.T) {
|
||||
Equal(t, "foo", "foo", "msg!")
|
||||
//Equal(t, "foo", "bar", "this should blow up")
|
||||
}
|
||||
|
||||
func TestNotEqual(t *testing.T) {
|
||||
NotEqual(t, "foo", "bar", "msg!")
|
||||
//NotEqual(t, "foo", "foo", "this should blow up")
|
||||
}
|
5
Godeps/_workspace/src/github.com/bmizerany/assert/example/point.go
generated
vendored
Normal file
5
Godeps/_workspace/src/github.com/bmizerany/assert/example/point.go
generated
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
package point
|
||||
|
||||
type Point struct {
|
||||
X, Y int
|
||||
}
|
13
Godeps/_workspace/src/github.com/bmizerany/assert/example/point_test.go
generated
vendored
Normal file
13
Godeps/_workspace/src/github.com/bmizerany/assert/example/point_test.go
generated
vendored
Normal file
@ -0,0 +1,13 @@
|
||||
package point
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"assert"
|
||||
)
|
||||
|
||||
func TestAsserts(t *testing.T) {
|
||||
p1 := Point{1, 1}
|
||||
p2 := Point{2, 1}
|
||||
|
||||
assert.Equal(t, p1, p2)
|
||||
}
|
168
Godeps/_workspace/src/github.com/gopherjs/gopherjs/js/js.go
generated
vendored
168
Godeps/_workspace/src/github.com/gopherjs/gopherjs/js/js.go
generated
vendored
@ -1,168 +0,0 @@
|
||||
// Package js provides functions for interacting with native JavaScript APIs. Calls to these functions are treated specially by GopherJS and translated directly to their corresponding JavaScript syntax.
|
||||
//
|
||||
// Use MakeWrapper to expose methods to JavaScript. When passing values directly, the following type conversions are performed:
|
||||
//
|
||||
// | Go type | JavaScript type | Conversions back to interface{} |
|
||||
// | --------------------- | --------------------- | ------------------------------- |
|
||||
// | bool | Boolean | bool |
|
||||
// | integers and floats | Number | float64 |
|
||||
// | string | String | string |
|
||||
// | []int8 | Int8Array | []int8 |
|
||||
// | []int16 | Int16Array | []int16 |
|
||||
// | []int32, []int | Int32Array | []int |
|
||||
// | []uint8 | Uint8Array | []uint8 |
|
||||
// | []uint16 | Uint16Array | []uint16 |
|
||||
// | []uint32, []uint | Uint32Array | []uint |
|
||||
// | []float32 | Float32Array | []float32 |
|
||||
// | []float64 | Float64Array | []float64 |
|
||||
// | all other slices | Array | []interface{} |
|
||||
// | arrays | see slice type | see slice type |
|
||||
// | functions | Function | func(...interface{}) *js.Object |
|
||||
// | time.Time | Date | time.Time |
|
||||
// | - | instanceof Node | *js.Object |
|
||||
// | maps, structs | instanceof Object | map[string]interface{} |
|
||||
//
|
||||
// Additionally, for a struct containing a *js.Object field, only the content of the field will be passed to JavaScript and vice versa.
|
||||
package js
|
||||
|
||||
// Object is a container for a native JavaScript object. Calls to its methods are treated specially by GopherJS and translated directly to their JavaScript syntax. A nil pointer to Object is equal to JavaScript's "null". Object can not be used as a map key.
|
||||
type Object struct{ object *Object }
|
||||
|
||||
// Get returns the object's property with the given key.
|
||||
func (o *Object) Get(key string) *Object { return o.object.Get(key) }
|
||||
|
||||
// Set assigns the value to the object's property with the given key.
|
||||
func (o *Object) Set(key string, value interface{}) { o.object.Set(key, value) }
|
||||
|
||||
// Delete removes the object's property with the given key.
|
||||
func (o *Object) Delete(key string) { o.object.Delete(key) }
|
||||
|
||||
// Length returns the object's "length" property, converted to int.
|
||||
func (o *Object) Length() int { return o.object.Length() }
|
||||
|
||||
// Index returns the i'th element of an array.
|
||||
func (o *Object) Index(i int) *Object { return o.object.Index(i) }
|
||||
|
||||
// SetIndex sets the i'th element of an array.
|
||||
func (o *Object) SetIndex(i int, value interface{}) { o.object.SetIndex(i, value) }
|
||||
|
||||
// Call calls the object's method with the given name.
|
||||
func (o *Object) Call(name string, args ...interface{}) *Object { return o.object.Call(name, args...) }
|
||||
|
||||
// Invoke calls the object itself. This will fail if it is not a function.
|
||||
func (o *Object) Invoke(args ...interface{}) *Object { return o.object.Invoke(args...) }
|
||||
|
||||
// New creates a new instance of this type object. This will fail if it not a function (constructor).
|
||||
func (o *Object) New(args ...interface{}) *Object { return o.object.New(args...) }
|
||||
|
||||
// Bool returns the object converted to bool according to JavaScript type conversions.
|
||||
func (o *Object) Bool() bool { return o.object.Bool() }
|
||||
|
||||
// String returns the object converted to string according to JavaScript type conversions.
|
||||
func (o *Object) String() string { return o.object.String() }
|
||||
|
||||
// Int returns the object converted to int according to JavaScript type conversions (parseInt).
|
||||
func (o *Object) Int() int { return o.object.Int() }
|
||||
|
||||
// Int64 returns the object converted to int64 according to JavaScript type conversions (parseInt).
|
||||
func (o *Object) Int64() int64 { return o.object.Int64() }
|
||||
|
||||
// Uint64 returns the object converted to uint64 according to JavaScript type conversions (parseInt).
|
||||
func (o *Object) Uint64() uint64 { return o.object.Uint64() }
|
||||
|
||||
// Float returns the object converted to float64 according to JavaScript type conversions (parseFloat).
|
||||
func (o *Object) Float() float64 { return o.object.Float() }
|
||||
|
||||
// Interface returns the object converted to interface{}. See GopherJS' README for details.
|
||||
func (o *Object) Interface() interface{} { return o.object.Interface() }
|
||||
|
||||
// Unsafe returns the object as an uintptr, which can be converted via unsafe.Pointer. Not intended for public use.
|
||||
func (o *Object) Unsafe() uintptr { return o.object.Unsafe() }
|
||||
|
||||
// Error encapsulates JavaScript errors. Those are turned into a Go panic and may be recovered, giving an *Error that holds the JavaScript error object.
|
||||
type Error struct {
|
||||
*Object
|
||||
}
|
||||
|
||||
// Error returns the message of the encapsulated JavaScript error object.
|
||||
func (err *Error) Error() string {
|
||||
return "JavaScript error: " + err.Get("message").String()
|
||||
}
|
||||
|
||||
// Stack returns the stack property of the encapsulated JavaScript error object.
|
||||
func (err *Error) Stack() string {
|
||||
return err.Get("stack").String()
|
||||
}
|
||||
|
||||
// Global gives JavaScript's global object ("window" for browsers and "GLOBAL" for Node.js).
|
||||
var Global *Object
|
||||
|
||||
// Module gives the value of the "module" variable set by Node.js. Hint: Set a module export with 'js.Module.Get("exports").Set("exportName", ...)'.
|
||||
var Module *Object
|
||||
|
||||
// Undefined gives the JavaScript value "undefined".
|
||||
var Undefined *Object
|
||||
|
||||
// Debugger gets compiled to JavaScript's "debugger;" statement.
|
||||
func Debugger() {}
|
||||
|
||||
// InternalObject returns the internal JavaScript object that represents i. Not intended for public use.
|
||||
func InternalObject(i interface{}) *Object {
|
||||
return nil
|
||||
}
|
||||
|
||||
// MakeFunc wraps a function and gives access to the values of JavaScript's "this" and "arguments" keywords.
|
||||
func MakeFunc(func(this *Object, arguments []*Object) interface{}) *Object {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Keys returns the keys of the given JavaScript object.
|
||||
func Keys(o *Object) []string {
|
||||
if o == nil || o == Undefined {
|
||||
return nil
|
||||
}
|
||||
a := Global.Get("Object").Call("keys", o)
|
||||
s := make([]string, a.Length())
|
||||
for i := 0; i < a.Length(); i++ {
|
||||
s[i] = a.Index(i).String()
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// MakeWrapper creates a JavaScript object which has wrappers for the exported methods of i. Use explicit getter and setter methods to expose struct fields to JavaScript.
|
||||
func MakeWrapper(i interface{}) *Object {
|
||||
v := InternalObject(i)
|
||||
o := Global.Get("Object").New()
|
||||
o.Set("__internal_object__", v)
|
||||
methods := v.Get("constructor").Get("methods")
|
||||
for i := 0; i < methods.Length(); i++ {
|
||||
m := methods.Index(i)
|
||||
if m.Get("pkg").String() != "" { // not exported
|
||||
continue
|
||||
}
|
||||
o.Set(m.Get("name").String(), func(args ...*Object) *Object {
|
||||
return Global.Call("$externalizeFunction", v.Get(m.Get("prop").String()), m.Get("typ"), true).Call("apply", v, args)
|
||||
})
|
||||
}
|
||||
return o
|
||||
}
|
||||
|
||||
// NewArrayBuffer creates a JavaScript ArrayBuffer from a byte slice.
|
||||
func NewArrayBuffer(b []byte) *Object {
|
||||
slice := InternalObject(b)
|
||||
offset := slice.Get("$offset").Int()
|
||||
length := slice.Get("$length").Int()
|
||||
return slice.Get("$array").Get("buffer").Call("slice", offset, offset+length)
|
||||
}
|
||||
|
||||
// M is a simple map type. It is intended as a shorthand for JavaScript objects (before conversion).
|
||||
type M map[string]interface{}
|
||||
|
||||
// S is a simple slice type. It is intended as a shorthand for JavaScript arrays (before conversion).
|
||||
type S []interface{}
|
||||
|
||||
func init() {
|
||||
// avoid dead code elimination
|
||||
e := Error{}
|
||||
_ = e
|
||||
}
|
4
Godeps/_workspace/src/github.com/kr/pretty/.gitignore
generated
vendored
Normal file
4
Godeps/_workspace/src/github.com/kr/pretty/.gitignore
generated
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
[568].out
|
||||
_go*
|
||||
_test*
|
||||
_obj
|
21
Godeps/_workspace/src/github.com/kr/pretty/License
generated
vendored
Normal file
21
Godeps/_workspace/src/github.com/kr/pretty/License
generated
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright 2012 Keith Rarick
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
9
Godeps/_workspace/src/github.com/kr/pretty/Readme
generated
vendored
Normal file
9
Godeps/_workspace/src/github.com/kr/pretty/Readme
generated
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
package pretty
|
||||
|
||||
import "github.com/kr/pretty"
|
||||
|
||||
Package pretty provides pretty-printing for Go values.
|
||||
|
||||
Documentation
|
||||
|
||||
http://godoc.org/github.com/kr/pretty
|
158
Godeps/_workspace/src/github.com/kr/pretty/diff.go
generated
vendored
Normal file
158
Godeps/_workspace/src/github.com/kr/pretty/diff.go
generated
vendored
Normal file
@ -0,0 +1,158 @@
|
||||
package pretty
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
type sbuf []string
|
||||
|
||||
func (s *sbuf) Write(b []byte) (int, error) {
|
||||
*s = append(*s, string(b))
|
||||
return len(b), nil
|
||||
}
|
||||
|
||||
// Diff returns a slice where each element describes
|
||||
// a difference between a and b.
|
||||
func Diff(a, b interface{}) (desc []string) {
|
||||
Fdiff((*sbuf)(&desc), a, b)
|
||||
return desc
|
||||
}
|
||||
|
||||
// Fdiff writes to w a description of the differences between a and b.
|
||||
func Fdiff(w io.Writer, a, b interface{}) {
|
||||
diffWriter{w: w}.diff(reflect.ValueOf(a), reflect.ValueOf(b))
|
||||
}
|
||||
|
||||
type diffWriter struct {
|
||||
w io.Writer
|
||||
l string // label
|
||||
}
|
||||
|
||||
func (w diffWriter) printf(f string, a ...interface{}) {
|
||||
var l string
|
||||
if w.l != "" {
|
||||
l = w.l + ": "
|
||||
}
|
||||
fmt.Fprintf(w.w, l+f, a...)
|
||||
}
|
||||
|
||||
func (w diffWriter) diff(av, bv reflect.Value) {
|
||||
if !av.IsValid() && bv.IsValid() {
|
||||
w.printf("nil != %#v", bv.Interface())
|
||||
return
|
||||
}
|
||||
if av.IsValid() && !bv.IsValid() {
|
||||
w.printf("%#v != nil", av.Interface())
|
||||
return
|
||||
}
|
||||
if !av.IsValid() && !bv.IsValid() {
|
||||
return
|
||||
}
|
||||
|
||||
at := av.Type()
|
||||
bt := bv.Type()
|
||||
if at != bt {
|
||||
w.printf("%v != %v", at, bt)
|
||||
return
|
||||
}
|
||||
|
||||
// numeric types, including bool
|
||||
if at.Kind() < reflect.Array {
|
||||
a, b := av.Interface(), bv.Interface()
|
||||
if a != b {
|
||||
w.printf("%#v != %#v", a, b)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
switch at.Kind() {
|
||||
case reflect.String:
|
||||
a, b := av.Interface(), bv.Interface()
|
||||
if a != b {
|
||||
w.printf("%q != %q", a, b)
|
||||
}
|
||||
case reflect.Ptr:
|
||||
switch {
|
||||
case av.IsNil() && !bv.IsNil():
|
||||
w.printf("nil != %v", bv.Interface())
|
||||
case !av.IsNil() && bv.IsNil():
|
||||
w.printf("%v != nil", av.Interface())
|
||||
case !av.IsNil() && !bv.IsNil():
|
||||
w.diff(av.Elem(), bv.Elem())
|
||||
}
|
||||
case reflect.Struct:
|
||||
for i := 0; i < av.NumField(); i++ {
|
||||
w.relabel(at.Field(i).Name).diff(av.Field(i), bv.Field(i))
|
||||
}
|
||||
case reflect.Slice:
|
||||
lenA := av.Len()
|
||||
lenB := bv.Len()
|
||||
if lenA != lenB {
|
||||
w.printf("%s[%d] != %s[%d]", av.Type(), lenA, bv.Type(), lenB)
|
||||
break
|
||||
}
|
||||
for i := 0; i < lenA; i++ {
|
||||
w.relabel(fmt.Sprintf("[%d]", i)).diff(av.Index(i), bv.Index(i))
|
||||
}
|
||||
case reflect.Map:
|
||||
ak, both, bk := keyDiff(av.MapKeys(), bv.MapKeys())
|
||||
for _, k := range ak {
|
||||
w := w.relabel(fmt.Sprintf("[%#v]", k.Interface()))
|
||||
w.printf("%q != (missing)", av.MapIndex(k))
|
||||
}
|
||||
for _, k := range both {
|
||||
w := w.relabel(fmt.Sprintf("[%#v]", k.Interface()))
|
||||
w.diff(av.MapIndex(k), bv.MapIndex(k))
|
||||
}
|
||||
for _, k := range bk {
|
||||
w := w.relabel(fmt.Sprintf("[%#v]", k.Interface()))
|
||||
w.printf("(missing) != %q", bv.MapIndex(k))
|
||||
}
|
||||
case reflect.Interface:
|
||||
w.diff(reflect.ValueOf(av.Interface()), reflect.ValueOf(bv.Interface()))
|
||||
default:
|
||||
if !reflect.DeepEqual(av.Interface(), bv.Interface()) {
|
||||
w.printf("%# v != %# v", Formatter(av.Interface()), Formatter(bv.Interface()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (d diffWriter) relabel(name string) (d1 diffWriter) {
|
||||
d1 = d
|
||||
if d.l != "" && name[0] != '[' {
|
||||
d1.l += "."
|
||||
}
|
||||
d1.l += name
|
||||
return d1
|
||||
}
|
||||
|
||||
func keyDiff(a, b []reflect.Value) (ak, both, bk []reflect.Value) {
|
||||
for _, av := range a {
|
||||
inBoth := false
|
||||
for _, bv := range b {
|
||||
if reflect.DeepEqual(av.Interface(), bv.Interface()) {
|
||||
inBoth = true
|
||||
both = append(both, av)
|
||||
break
|
||||
}
|
||||
}
|
||||
if !inBoth {
|
||||
ak = append(ak, av)
|
||||
}
|
||||
}
|
||||
for _, bv := range b {
|
||||
inBoth := false
|
||||
for _, av := range a {
|
||||
if reflect.DeepEqual(av.Interface(), bv.Interface()) {
|
||||
inBoth = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !inBoth {
|
||||
bk = append(bk, bv)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
74
Godeps/_workspace/src/github.com/kr/pretty/diff_test.go
generated
vendored
Normal file
74
Godeps/_workspace/src/github.com/kr/pretty/diff_test.go
generated
vendored
Normal file
@ -0,0 +1,74 @@
|
||||
package pretty
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
type difftest struct {
|
||||
a interface{}
|
||||
b interface{}
|
||||
exp []string
|
||||
}
|
||||
|
||||
type S struct {
|
||||
A int
|
||||
S *S
|
||||
I interface{}
|
||||
C []int
|
||||
}
|
||||
|
||||
var diffs = []difftest{
|
||||
{a: nil, b: nil},
|
||||
{a: S{A: 1}, b: S{A: 1}},
|
||||
|
||||
{0, "", []string{`int != string`}},
|
||||
{0, 1, []string{`0 != 1`}},
|
||||
{S{}, new(S), []string{`pretty.S != *pretty.S`}},
|
||||
{"a", "b", []string{`"a" != "b"`}},
|
||||
{S{}, S{A: 1}, []string{`A: 0 != 1`}},
|
||||
{new(S), &S{A: 1}, []string{`A: 0 != 1`}},
|
||||
{S{S: new(S)}, S{S: &S{A: 1}}, []string{`S.A: 0 != 1`}},
|
||||
{S{}, S{I: 0}, []string{`I: nil != 0`}},
|
||||
{S{I: 1}, S{I: "x"}, []string{`I: int != string`}},
|
||||
{S{}, S{C: []int{1}}, []string{`C: []int[0] != []int[1]`}},
|
||||
{S{C: []int{}}, S{C: []int{1}}, []string{`C: []int[0] != []int[1]`}},
|
||||
{S{C: []int{1, 2, 3}}, S{C: []int{1, 2, 4}}, []string{`C[2]: 3 != 4`}},
|
||||
{S{}, S{A: 1, S: new(S)}, []string{`A: 0 != 1`, `S: nil != &{0 <nil> <nil> []}`}},
|
||||
}
|
||||
|
||||
func TestDiff(t *testing.T) {
|
||||
for _, tt := range diffs {
|
||||
got := Diff(tt.a, tt.b)
|
||||
eq := len(got) == len(tt.exp)
|
||||
if eq {
|
||||
for i := range got {
|
||||
eq = eq && got[i] == tt.exp[i]
|
||||
}
|
||||
}
|
||||
if !eq {
|
||||
t.Errorf("diffing % #v", tt.a)
|
||||
t.Errorf("with % #v", tt.b)
|
||||
diffdiff(t, got, tt.exp)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func diffdiff(t *testing.T, got, exp []string) {
|
||||
minus(t, "unexpected:", got, exp)
|
||||
minus(t, "missing:", exp, got)
|
||||
}
|
||||
|
||||
func minus(t *testing.T, s string, a, b []string) {
|
||||
var i, j int
|
||||
for i = 0; i < len(a); i++ {
|
||||
for j = 0; j < len(b); j++ {
|
||||
if a[i] == b[j] {
|
||||
break
|
||||
}
|
||||
}
|
||||
if j == len(b) {
|
||||
t.Error(s, a[i])
|
||||
}
|
||||
}
|
||||
}
|
20
Godeps/_workspace/src/github.com/kr/pretty/example_test.go
generated
vendored
Normal file
20
Godeps/_workspace/src/github.com/kr/pretty/example_test.go
generated
vendored
Normal file
@ -0,0 +1,20 @@
|
||||
package pretty_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/kr/pretty"
|
||||
)
|
||||
|
||||
func Example() {
|
||||
type myType struct {
|
||||
a, b int
|
||||
}
|
||||
var x = []myType{{1, 2}, {3, 4}, {5, 6}}
|
||||
fmt.Printf("%# v", pretty.Formatter(x))
|
||||
// output:
|
||||
// []pretty_test.myType{
|
||||
// {a:1, b:2},
|
||||
// {a:3, b:4},
|
||||
// {a:5, b:6},
|
||||
// }
|
||||
}
|
337
Godeps/_workspace/src/github.com/kr/pretty/formatter.go
generated
vendored
Normal file
337
Godeps/_workspace/src/github.com/kr/pretty/formatter.go
generated
vendored
Normal file
@ -0,0 +1,337 @@
|
||||
package pretty
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"text/tabwriter"
|
||||
|
||||
"github.com/kr/text"
|
||||
)
|
||||
|
||||
const (
|
||||
limit = 50
|
||||
)
|
||||
|
||||
type formatter struct {
|
||||
x interface{}
|
||||
force bool
|
||||
quote bool
|
||||
}
|
||||
|
||||
// Formatter makes a wrapper, f, that will format x as go source with line
|
||||
// breaks and tabs. Object f responds to the "%v" formatting verb when both the
|
||||
// "#" and " " (space) flags are set, for example:
|
||||
//
|
||||
// fmt.Sprintf("%# v", Formatter(x))
|
||||
//
|
||||
// If one of these two flags is not set, or any other verb is used, f will
|
||||
// format x according to the usual rules of package fmt.
|
||||
// In particular, if x satisfies fmt.Formatter, then x.Format will be called.
|
||||
func Formatter(x interface{}) (f fmt.Formatter) {
|
||||
return formatter{x: x, quote: true}
|
||||
}
|
||||
|
||||
func (fo formatter) String() string {
|
||||
return fmt.Sprint(fo.x) // unwrap it
|
||||
}
|
||||
|
||||
func (fo formatter) passThrough(f fmt.State, c rune) {
|
||||
s := "%"
|
||||
for i := 0; i < 128; i++ {
|
||||
if f.Flag(i) {
|
||||
s += string(i)
|
||||
}
|
||||
}
|
||||
if w, ok := f.Width(); ok {
|
||||
s += fmt.Sprintf("%d", w)
|
||||
}
|
||||
if p, ok := f.Precision(); ok {
|
||||
s += fmt.Sprintf(".%d", p)
|
||||
}
|
||||
s += string(c)
|
||||
fmt.Fprintf(f, s, fo.x)
|
||||
}
|
||||
|
||||
func (fo formatter) Format(f fmt.State, c rune) {
|
||||
if fo.force || c == 'v' && f.Flag('#') && f.Flag(' ') {
|
||||
w := tabwriter.NewWriter(f, 4, 4, 1, ' ', 0)
|
||||
p := &printer{tw: w, Writer: w, visited: make(map[visit]int)}
|
||||
p.printValue(reflect.ValueOf(fo.x), true, fo.quote)
|
||||
w.Flush()
|
||||
return
|
||||
}
|
||||
fo.passThrough(f, c)
|
||||
}
|
||||
|
||||
type printer struct {
|
||||
io.Writer
|
||||
tw *tabwriter.Writer
|
||||
visited map[visit]int
|
||||
depth int
|
||||
}
|
||||
|
||||
func (p *printer) indent() *printer {
|
||||
q := *p
|
||||
q.tw = tabwriter.NewWriter(p.Writer, 4, 4, 1, ' ', 0)
|
||||
q.Writer = text.NewIndentWriter(q.tw, []byte{'\t'})
|
||||
return &q
|
||||
}
|
||||
|
||||
func (p *printer) printInline(v reflect.Value, x interface{}, showType bool) {
|
||||
if showType {
|
||||
io.WriteString(p, v.Type().String())
|
||||
fmt.Fprintf(p, "(%#v)", x)
|
||||
} else {
|
||||
fmt.Fprintf(p, "%#v", x)
|
||||
}
|
||||
}
|
||||
|
||||
// printValue must keep track of already-printed pointer values to avoid
|
||||
// infinite recursion.
|
||||
type visit struct {
|
||||
v uintptr
|
||||
typ reflect.Type
|
||||
}
|
||||
|
||||
func (p *printer) printValue(v reflect.Value, showType, quote bool) {
|
||||
if p.depth > 10 {
|
||||
io.WriteString(p, "!%v(DEPTH EXCEEDED)")
|
||||
return
|
||||
}
|
||||
|
||||
switch v.Kind() {
|
||||
case reflect.Bool:
|
||||
p.printInline(v, v.Bool(), showType)
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
p.printInline(v, v.Int(), showType)
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
||||
p.printInline(v, v.Uint(), showType)
|
||||
case reflect.Float32, reflect.Float64:
|
||||
p.printInline(v, v.Float(), showType)
|
||||
case reflect.Complex64, reflect.Complex128:
|
||||
fmt.Fprintf(p, "%#v", v.Complex())
|
||||
case reflect.String:
|
||||
p.fmtString(v.String(), quote)
|
||||
case reflect.Map:
|
||||
t := v.Type()
|
||||
if showType {
|
||||
io.WriteString(p, t.String())
|
||||
}
|
||||
writeByte(p, '{')
|
||||
if nonzero(v) {
|
||||
expand := !canInline(v.Type())
|
||||
pp := p
|
||||
if expand {
|
||||
writeByte(p, '\n')
|
||||
pp = p.indent()
|
||||
}
|
||||
keys := v.MapKeys()
|
||||
for i := 0; i < v.Len(); i++ {
|
||||
showTypeInStruct := true
|
||||
k := keys[i]
|
||||
mv := v.MapIndex(k)
|
||||
pp.printValue(k, false, true)
|
||||
writeByte(pp, ':')
|
||||
if expand {
|
||||
writeByte(pp, '\t')
|
||||
}
|
||||
showTypeInStruct = t.Elem().Kind() == reflect.Interface
|
||||
pp.printValue(mv, showTypeInStruct, true)
|
||||
if expand {
|
||||
io.WriteString(pp, ",\n")
|
||||
} else if i < v.Len()-1 {
|
||||
io.WriteString(pp, ", ")
|
||||
}
|
||||
}
|
||||
if expand {
|
||||
pp.tw.Flush()
|
||||
}
|
||||
}
|
||||
writeByte(p, '}')
|
||||
case reflect.Struct:
|
||||
t := v.Type()
|
||||
if v.CanAddr() {
|
||||
addr := v.UnsafeAddr()
|
||||
vis := visit{addr, t}
|
||||
if vd, ok := p.visited[vis]; ok && vd < p.depth {
|
||||
p.fmtString(t.String()+"{(CYCLIC REFERENCE)}", false)
|
||||
break // don't print v again
|
||||
}
|
||||
p.visited[vis] = p.depth
|
||||
}
|
||||
|
||||
if showType {
|
||||
io.WriteString(p, t.String())
|
||||
}
|
||||
writeByte(p, '{')
|
||||
if nonzero(v) {
|
||||
expand := !canInline(v.Type())
|
||||
pp := p
|
||||
if expand {
|
||||
writeByte(p, '\n')
|
||||
pp = p.indent()
|
||||
}
|
||||
for i := 0; i < v.NumField(); i++ {
|
||||
showTypeInStruct := true
|
||||
if f := t.Field(i); f.Name != "" {
|
||||
io.WriteString(pp, f.Name)
|
||||
writeByte(pp, ':')
|
||||
if expand {
|
||||
writeByte(pp, '\t')
|
||||
}
|
||||
showTypeInStruct = labelType(f.Type)
|
||||
}
|
||||
pp.printValue(getField(v, i), showTypeInStruct, true)
|
||||
if expand {
|
||||
io.WriteString(pp, ",\n")
|
||||
} else if i < v.NumField()-1 {
|
||||
io.WriteString(pp, ", ")
|
||||
}
|
||||
}
|
||||
if expand {
|
||||
pp.tw.Flush()
|
||||
}
|
||||
}
|
||||
writeByte(p, '}')
|
||||
case reflect.Interface:
|
||||
switch e := v.Elem(); {
|
||||
case e.Kind() == reflect.Invalid:
|
||||
io.WriteString(p, "nil")
|
||||
case e.IsValid():
|
||||
pp := *p
|
||||
pp.depth++
|
||||
pp.printValue(e, showType, true)
|
||||
default:
|
||||
io.WriteString(p, v.Type().String())
|
||||
io.WriteString(p, "(nil)")
|
||||
}
|
||||
case reflect.Array, reflect.Slice:
|
||||
t := v.Type()
|
||||
if showType {
|
||||
io.WriteString(p, t.String())
|
||||
}
|
||||
if v.Kind() == reflect.Slice && v.IsNil() && showType {
|
||||
io.WriteString(p, "(nil)")
|
||||
break
|
||||
}
|
||||
if v.Kind() == reflect.Slice && v.IsNil() {
|
||||
io.WriteString(p, "nil")
|
||||
break
|
||||
}
|
||||
writeByte(p, '{')
|
||||
expand := !canInline(v.Type())
|
||||
pp := p
|
||||
if expand {
|
||||
writeByte(p, '\n')
|
||||
pp = p.indent()
|
||||
}
|
||||
for i := 0; i < v.Len(); i++ {
|
||||
showTypeInSlice := t.Elem().Kind() == reflect.Interface
|
||||
pp.printValue(v.Index(i), showTypeInSlice, true)
|
||||
if expand {
|
||||
io.WriteString(pp, ",\n")
|
||||
} else if i < v.Len()-1 {
|
||||
io.WriteString(pp, ", ")
|
||||
}
|
||||
}
|
||||
if expand {
|
||||
pp.tw.Flush()
|
||||
}
|
||||
writeByte(p, '}')
|
||||
case reflect.Ptr:
|
||||
e := v.Elem()
|
||||
if !e.IsValid() {
|
||||
writeByte(p, '(')
|
||||
io.WriteString(p, v.Type().String())
|
||||
io.WriteString(p, ")(nil)")
|
||||
} else {
|
||||
pp := *p
|
||||
pp.depth++
|
||||
writeByte(pp, '&')
|
||||
pp.printValue(e, true, true)
|
||||
}
|
||||
case reflect.Chan:
|
||||
x := v.Pointer()
|
||||
if showType {
|
||||
writeByte(p, '(')
|
||||
io.WriteString(p, v.Type().String())
|
||||
fmt.Fprintf(p, ")(%#v)", x)
|
||||
} else {
|
||||
fmt.Fprintf(p, "%#v", x)
|
||||
}
|
||||
case reflect.Func:
|
||||
io.WriteString(p, v.Type().String())
|
||||
io.WriteString(p, " {...}")
|
||||
case reflect.UnsafePointer:
|
||||
p.printInline(v, v.Pointer(), showType)
|
||||
case reflect.Invalid:
|
||||
io.WriteString(p, "nil")
|
||||
}
|
||||
}
|
||||
|
||||
func canInline(t reflect.Type) bool {
|
||||
switch t.Kind() {
|
||||
case reflect.Map:
|
||||
return !canExpand(t.Elem())
|
||||
case reflect.Struct:
|
||||
for i := 0; i < t.NumField(); i++ {
|
||||
if canExpand(t.Field(i).Type) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
case reflect.Interface:
|
||||
return false
|
||||
case reflect.Array, reflect.Slice:
|
||||
return !canExpand(t.Elem())
|
||||
case reflect.Ptr:
|
||||
return false
|
||||
case reflect.Chan, reflect.Func, reflect.UnsafePointer:
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func canExpand(t reflect.Type) bool {
|
||||
switch t.Kind() {
|
||||
case reflect.Map, reflect.Struct,
|
||||
reflect.Interface, reflect.Array, reflect.Slice,
|
||||
reflect.Ptr:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func labelType(t reflect.Type) bool {
|
||||
switch t.Kind() {
|
||||
case reflect.Interface, reflect.Struct:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (p *printer) fmtString(s string, quote bool) {
|
||||
if quote {
|
||||
s = strconv.Quote(s)
|
||||
}
|
||||
io.WriteString(p, s)
|
||||
}
|
||||
|
||||
func tryDeepEqual(a, b interface{}) bool {
|
||||
defer func() { recover() }()
|
||||
return reflect.DeepEqual(a, b)
|
||||
}
|
||||
|
||||
func writeByte(w io.Writer, b byte) {
|
||||
w.Write([]byte{b})
|
||||
}
|
||||
|
||||
func getField(v reflect.Value, i int) reflect.Value {
|
||||
val := v.Field(i)
|
||||
if val.Kind() == reflect.Interface && !val.IsNil() {
|
||||
val = val.Elem()
|
||||
}
|
||||
return val
|
||||
}
|
261
Godeps/_workspace/src/github.com/kr/pretty/formatter_test.go
generated
vendored
Normal file
261
Godeps/_workspace/src/github.com/kr/pretty/formatter_test.go
generated
vendored
Normal file
@ -0,0 +1,261 @@
|
||||
package pretty
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
"testing"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
type test struct {
|
||||
v interface{}
|
||||
s string
|
||||
}
|
||||
|
||||
type LongStructTypeName struct {
|
||||
longFieldName interface{}
|
||||
otherLongFieldName interface{}
|
||||
}
|
||||
|
||||
type SA struct {
|
||||
t *T
|
||||
v T
|
||||
}
|
||||
|
||||
type T struct {
|
||||
x, y int
|
||||
}
|
||||
|
||||
type F int
|
||||
|
||||
func (f F) Format(s fmt.State, c rune) {
|
||||
fmt.Fprintf(s, "F(%d)", int(f))
|
||||
}
|
||||
|
||||
var long = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
|
||||
|
||||
var gosyntax = []test{
|
||||
{nil, `nil`},
|
||||
{"", `""`},
|
||||
{"a", `"a"`},
|
||||
{1, "int(1)"},
|
||||
{1.0, "float64(1)"},
|
||||
{[]int(nil), "[]int(nil)"},
|
||||
{[0]int{}, "[0]int{}"},
|
||||
{complex(1, 0), "(1+0i)"},
|
||||
//{make(chan int), "(chan int)(0x1234)"},
|
||||
{unsafe.Pointer(uintptr(unsafe.Pointer(&long))), fmt.Sprintf("unsafe.Pointer(0x%02x)", uintptr(unsafe.Pointer(&long)))},
|
||||
{func(int) {}, "func(int) {...}"},
|
||||
{map[int]int{1: 1}, "map[int]int{1:1}"},
|
||||
{int32(1), "int32(1)"},
|
||||
{io.EOF, `&errors.errorString{s:"EOF"}`},
|
||||
{[]string{"a"}, `[]string{"a"}`},
|
||||
{
|
||||
[]string{long},
|
||||
`[]string{"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"}`,
|
||||
},
|
||||
{F(5), "pretty.F(5)"},
|
||||
{
|
||||
SA{&T{1, 2}, T{3, 4}},
|
||||
`pretty.SA{
|
||||
t: &pretty.T{x:1, y:2},
|
||||
v: pretty.T{x:3, y:4},
|
||||
}`,
|
||||
},
|
||||
{
|
||||
map[int][]byte{1: {}},
|
||||
`map[int][]uint8{
|
||||
1: {},
|
||||
}`,
|
||||
},
|
||||
{
|
||||
map[int]T{1: {}},
|
||||
`map[int]pretty.T{
|
||||
1: {},
|
||||
}`,
|
||||
},
|
||||
{
|
||||
long,
|
||||
`"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"`,
|
||||
},
|
||||
{
|
||||
LongStructTypeName{
|
||||
longFieldName: LongStructTypeName{},
|
||||
otherLongFieldName: long,
|
||||
},
|
||||
`pretty.LongStructTypeName{
|
||||
longFieldName: pretty.LongStructTypeName{},
|
||||
otherLongFieldName: "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789",
|
||||
}`,
|
||||
},
|
||||
{
|
||||
&LongStructTypeName{
|
||||
longFieldName: &LongStructTypeName{},
|
||||
otherLongFieldName: (*LongStructTypeName)(nil),
|
||||
},
|
||||
`&pretty.LongStructTypeName{
|
||||
longFieldName: &pretty.LongStructTypeName{},
|
||||
otherLongFieldName: (*pretty.LongStructTypeName)(nil),
|
||||
}`,
|
||||
},
|
||||
{
|
||||
[]LongStructTypeName{
|
||||
{nil, nil},
|
||||
{3, 3},
|
||||
{long, nil},
|
||||
},
|
||||
`[]pretty.LongStructTypeName{
|
||||
{},
|
||||
{
|
||||
longFieldName: int(3),
|
||||
otherLongFieldName: int(3),
|
||||
},
|
||||
{
|
||||
longFieldName: "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789",
|
||||
otherLongFieldName: nil,
|
||||
},
|
||||
}`,
|
||||
},
|
||||
{
|
||||
[]interface{}{
|
||||
LongStructTypeName{nil, nil},
|
||||
[]byte{1, 2, 3},
|
||||
T{3, 4},
|
||||
LongStructTypeName{long, nil},
|
||||
},
|
||||
`[]interface {}{
|
||||
pretty.LongStructTypeName{},
|
||||
[]uint8{0x1, 0x2, 0x3},
|
||||
pretty.T{x:3, y:4},
|
||||
pretty.LongStructTypeName{
|
||||
longFieldName: "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789",
|
||||
otherLongFieldName: nil,
|
||||
},
|
||||
}`,
|
||||
},
|
||||
}
|
||||
|
||||
func TestGoSyntax(t *testing.T) {
|
||||
for _, tt := range gosyntax {
|
||||
s := fmt.Sprintf("%# v", Formatter(tt.v))
|
||||
if tt.s != s {
|
||||
t.Errorf("expected %q", tt.s)
|
||||
t.Errorf("got %q", s)
|
||||
t.Errorf("expraw\n%s", tt.s)
|
||||
t.Errorf("gotraw\n%s", s)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type I struct {
|
||||
i int
|
||||
R interface{}
|
||||
}
|
||||
|
||||
func (i *I) I() *I { return i.R.(*I) }
|
||||
|
||||
func TestCycle(t *testing.T) {
|
||||
type A struct{ *A }
|
||||
v := &A{}
|
||||
v.A = v
|
||||
|
||||
// panics from stack overflow without cycle detection
|
||||
t.Logf("Example cycle:\n%# v", Formatter(v))
|
||||
|
||||
p := &A{}
|
||||
s := fmt.Sprintf("%# v", Formatter([]*A{p, p}))
|
||||
if strings.Contains(s, "CYCLIC") {
|
||||
t.Errorf("Repeated address detected as cyclic reference:\n%s", s)
|
||||
}
|
||||
|
||||
type R struct {
|
||||
i int
|
||||
*R
|
||||
}
|
||||
r := &R{
|
||||
i: 1,
|
||||
R: &R{
|
||||
i: 2,
|
||||
R: &R{
|
||||
i: 3,
|
||||
},
|
||||
},
|
||||
}
|
||||
r.R.R.R = r
|
||||
t.Logf("Example longer cycle:\n%# v", Formatter(r))
|
||||
|
||||
r = &R{
|
||||
i: 1,
|
||||
R: &R{
|
||||
i: 2,
|
||||
R: &R{
|
||||
i: 3,
|
||||
R: &R{
|
||||
i: 4,
|
||||
R: &R{
|
||||
i: 5,
|
||||
R: &R{
|
||||
i: 6,
|
||||
R: &R{
|
||||
i: 7,
|
||||
R: &R{
|
||||
i: 8,
|
||||
R: &R{
|
||||
i: 9,
|
||||
R: &R{
|
||||
i: 10,
|
||||
R: &R{
|
||||
i: 11,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
// here be pirates
|
||||
r.R.R.R.R.R.R.R.R.R.R.R = r
|
||||
t.Logf("Example very long cycle:\n%# v", Formatter(r))
|
||||
|
||||
i := &I{
|
||||
i: 1,
|
||||
R: &I{
|
||||
i: 2,
|
||||
R: &I{
|
||||
i: 3,
|
||||
R: &I{
|
||||
i: 4,
|
||||
R: &I{
|
||||
i: 5,
|
||||
R: &I{
|
||||
i: 6,
|
||||
R: &I{
|
||||
i: 7,
|
||||
R: &I{
|
||||
i: 8,
|
||||
R: &I{
|
||||
i: 9,
|
||||
R: &I{
|
||||
i: 10,
|
||||
R: &I{
|
||||
i: 11,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
iv := i.I().I().I().I().I().I().I().I().I().I()
|
||||
*iv = *i
|
||||
t.Logf("Example long interface cycle:\n%# v", Formatter(i))
|
||||
}
|
98
Godeps/_workspace/src/github.com/kr/pretty/pretty.go
generated
vendored
Normal file
98
Godeps/_workspace/src/github.com/kr/pretty/pretty.go
generated
vendored
Normal file
@ -0,0 +1,98 @@
|
||||
// Package pretty provides pretty-printing for Go values. This is
|
||||
// useful during debugging, to avoid wrapping long output lines in
|
||||
// the terminal.
|
||||
//
|
||||
// It provides a function, Formatter, that can be used with any
|
||||
// function that accepts a format string. It also provides
|
||||
// convenience wrappers for functions in packages fmt and log.
|
||||
package pretty
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
)
|
||||
|
||||
// Errorf is a convenience wrapper for fmt.Errorf.
|
||||
//
|
||||
// Calling Errorf(f, x, y) is equivalent to
|
||||
// fmt.Errorf(f, Formatter(x), Formatter(y)).
|
||||
func Errorf(format string, a ...interface{}) error {
|
||||
return fmt.Errorf(format, wrap(a, false)...)
|
||||
}
|
||||
|
||||
// Fprintf is a convenience wrapper for fmt.Fprintf.
|
||||
//
|
||||
// Calling Fprintf(w, f, x, y) is equivalent to
|
||||
// fmt.Fprintf(w, f, Formatter(x), Formatter(y)).
|
||||
func Fprintf(w io.Writer, format string, a ...interface{}) (n int, error error) {
|
||||
return fmt.Fprintf(w, format, wrap(a, false)...)
|
||||
}
|
||||
|
||||
// Log is a convenience wrapper for log.Printf.
|
||||
//
|
||||
// Calling Log(x, y) is equivalent to
|
||||
// log.Print(Formatter(x), Formatter(y)), but each operand is
|
||||
// formatted with "%# v".
|
||||
func Log(a ...interface{}) {
|
||||
log.Print(wrap(a, true)...)
|
||||
}
|
||||
|
||||
// Logf is a convenience wrapper for log.Printf.
|
||||
//
|
||||
// Calling Logf(f, x, y) is equivalent to
|
||||
// log.Printf(f, Formatter(x), Formatter(y)).
|
||||
func Logf(format string, a ...interface{}) {
|
||||
log.Printf(format, wrap(a, false)...)
|
||||
}
|
||||
|
||||
// Logln is a convenience wrapper for log.Printf.
|
||||
//
|
||||
// Calling Logln(x, y) is equivalent to
|
||||
// log.Println(Formatter(x), Formatter(y)), but each operand is
|
||||
// formatted with "%# v".
|
||||
func Logln(a ...interface{}) {
|
||||
log.Println(wrap(a, true)...)
|
||||
}
|
||||
|
||||
// Print pretty-prints its operands and writes to standard output.
|
||||
//
|
||||
// Calling Print(x, y) is equivalent to
|
||||
// fmt.Print(Formatter(x), Formatter(y)), but each operand is
|
||||
// formatted with "%# v".
|
||||
func Print(a ...interface{}) (n int, errno error) {
|
||||
return fmt.Print(wrap(a, true)...)
|
||||
}
|
||||
|
||||
// Printf is a convenience wrapper for fmt.Printf.
|
||||
//
|
||||
// Calling Printf(f, x, y) is equivalent to
|
||||
// fmt.Printf(f, Formatter(x), Formatter(y)).
|
||||
func Printf(format string, a ...interface{}) (n int, errno error) {
|
||||
return fmt.Printf(format, wrap(a, false)...)
|
||||
}
|
||||
|
||||
// Println pretty-prints its operands and writes to standard output.
|
||||
//
|
||||
// Calling Print(x, y) is equivalent to
|
||||
// fmt.Println(Formatter(x), Formatter(y)), but each operand is
|
||||
// formatted with "%# v".
|
||||
func Println(a ...interface{}) (n int, errno error) {
|
||||
return fmt.Println(wrap(a, true)...)
|
||||
}
|
||||
|
||||
// Sprintf is a convenience wrapper for fmt.Sprintf.
|
||||
//
|
||||
// Calling Sprintf(f, x, y) is equivalent to
|
||||
// fmt.Sprintf(f, Formatter(x), Formatter(y)).
|
||||
func Sprintf(format string, a ...interface{}) string {
|
||||
return fmt.Sprintf(format, wrap(a, false)...)
|
||||
}
|
||||
|
||||
func wrap(a []interface{}, force bool) []interface{} {
|
||||
w := make([]interface{}, len(a))
|
||||
for i, x := range a {
|
||||
w[i] = formatter{x: x, force: force}
|
||||
}
|
||||
return w
|
||||
}
|
41
Godeps/_workspace/src/github.com/kr/pretty/zero.go
generated
vendored
Normal file
41
Godeps/_workspace/src/github.com/kr/pretty/zero.go
generated
vendored
Normal file
@ -0,0 +1,41 @@
|
||||
package pretty
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
)
|
||||
|
||||
func nonzero(v reflect.Value) bool {
|
||||
switch v.Kind() {
|
||||
case reflect.Bool:
|
||||
return v.Bool()
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
return v.Int() != 0
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
||||
return v.Uint() != 0
|
||||
case reflect.Float32, reflect.Float64:
|
||||
return v.Float() != 0
|
||||
case reflect.Complex64, reflect.Complex128:
|
||||
return v.Complex() != complex(0, 0)
|
||||
case reflect.String:
|
||||
return v.String() != ""
|
||||
case reflect.Struct:
|
||||
for i := 0; i < v.NumField(); i++ {
|
||||
if nonzero(getField(v, i)) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
case reflect.Array:
|
||||
for i := 0; i < v.Len(); i++ {
|
||||
if nonzero(v.Index(i)) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
case reflect.Map, reflect.Interface, reflect.Slice, reflect.Ptr, reflect.Chan, reflect.Func:
|
||||
return !v.IsNil()
|
||||
case reflect.UnsafePointer:
|
||||
return v.Pointer() != 0
|
||||
}
|
||||
return true
|
||||
}
|
19
Godeps/_workspace/src/github.com/kr/text/License
generated
vendored
Normal file
19
Godeps/_workspace/src/github.com/kr/text/License
generated
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
Copyright 2012 Keith Rarick
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
3
Godeps/_workspace/src/github.com/kr/text/Readme
generated
vendored
Normal file
3
Godeps/_workspace/src/github.com/kr/text/Readme
generated
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
This is a Go package for manipulating paragraphs of text.
|
||||
|
||||
See http://go.pkgdoc.org/github.com/kr/text for full documentation.
|
5
Godeps/_workspace/src/github.com/kr/text/colwriter/Readme
generated
vendored
Normal file
5
Godeps/_workspace/src/github.com/kr/text/colwriter/Readme
generated
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
Package colwriter provides a write filter that formats
|
||||
input lines in multiple columns.
|
||||
|
||||
The package is a straightforward translation from
|
||||
/src/cmd/draw/mc.c in Plan 9 from User Space.
|
147
Godeps/_workspace/src/github.com/kr/text/colwriter/column.go
generated
vendored
Normal file
147
Godeps/_workspace/src/github.com/kr/text/colwriter/column.go
generated
vendored
Normal file
@ -0,0 +1,147 @@
|
||||
// Package colwriter provides a write filter that formats
|
||||
// input lines in multiple columns.
|
||||
//
|
||||
// The package is a straightforward translation from
|
||||
// /src/cmd/draw/mc.c in Plan 9 from User Space.
|
||||
package colwriter
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
const (
|
||||
tab = 4
|
||||
)
|
||||
|
||||
const (
|
||||
// Print each input line ending in a colon ':' separately.
|
||||
BreakOnColon uint = 1 << iota
|
||||
)
|
||||
|
||||
// A Writer is a filter that arranges input lines in as many columns as will
|
||||
// fit in its width. Tab '\t' chars in the input are translated to sequences
|
||||
// of spaces ending at multiples of 4 positions.
|
||||
//
|
||||
// If BreakOnColon is set, each input line ending in a colon ':' is written
|
||||
// separately.
|
||||
//
|
||||
// The Writer assumes that all Unicode code points have the same width; this
|
||||
// may not be true in some fonts.
|
||||
type Writer struct {
|
||||
w io.Writer
|
||||
buf []byte
|
||||
width int
|
||||
flag uint
|
||||
}
|
||||
|
||||
// NewWriter allocates and initializes a new Writer writing to w.
|
||||
// Parameter width controls the total number of characters on each line
|
||||
// across all columns.
|
||||
func NewWriter(w io.Writer, width int, flag uint) *Writer {
|
||||
return &Writer{
|
||||
w: w,
|
||||
width: width,
|
||||
flag: flag,
|
||||
}
|
||||
}
|
||||
|
||||
// Write writes p to the writer w. The only errors returned are ones
|
||||
// encountered while writing to the underlying output stream.
|
||||
func (w *Writer) Write(p []byte) (n int, err error) {
|
||||
var linelen int
|
||||
var lastWasColon bool
|
||||
for i, c := range p {
|
||||
w.buf = append(w.buf, c)
|
||||
linelen++
|
||||
if c == '\t' {
|
||||
w.buf[len(w.buf)-1] = ' '
|
||||
for linelen%tab != 0 {
|
||||
w.buf = append(w.buf, ' ')
|
||||
linelen++
|
||||
}
|
||||
}
|
||||
if w.flag&BreakOnColon != 0 && c == ':' {
|
||||
lastWasColon = true
|
||||
} else if lastWasColon {
|
||||
if c == '\n' {
|
||||
pos := bytes.LastIndex(w.buf[:len(w.buf)-1], []byte{'\n'})
|
||||
if pos < 0 {
|
||||
pos = 0
|
||||
}
|
||||
line := w.buf[pos:]
|
||||
w.buf = w.buf[:pos]
|
||||
if err = w.columnate(); err != nil {
|
||||
if len(line) < i {
|
||||
return i - len(line), err
|
||||
}
|
||||
return 0, err
|
||||
}
|
||||
if n, err := w.w.Write(line); err != nil {
|
||||
if r := len(line) - n; r < i {
|
||||
return i - r, err
|
||||
}
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
lastWasColon = false
|
||||
}
|
||||
if c == '\n' {
|
||||
linelen = 0
|
||||
}
|
||||
}
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
// Flush should be called after the last call to Write to ensure that any data
|
||||
// buffered in the Writer is written to output.
|
||||
func (w *Writer) Flush() error {
|
||||
return w.columnate()
|
||||
}
|
||||
|
||||
func (w *Writer) columnate() error {
|
||||
words := bytes.Split(w.buf, []byte{'\n'})
|
||||
w.buf = nil
|
||||
if len(words[len(words)-1]) == 0 {
|
||||
words = words[:len(words)-1]
|
||||
}
|
||||
maxwidth := 0
|
||||
for _, wd := range words {
|
||||
if n := utf8.RuneCount(wd); n > maxwidth {
|
||||
maxwidth = n
|
||||
}
|
||||
}
|
||||
maxwidth++ // space char
|
||||
wordsPerLine := w.width / maxwidth
|
||||
if wordsPerLine <= 0 {
|
||||
wordsPerLine = 1
|
||||
}
|
||||
nlines := (len(words) + wordsPerLine - 1) / wordsPerLine
|
||||
for i := 0; i < nlines; i++ {
|
||||
col := 0
|
||||
endcol := 0
|
||||
for j := i; j < len(words); j += nlines {
|
||||
endcol += maxwidth
|
||||
_, err := w.w.Write(words[j])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
col += utf8.RuneCount(words[j])
|
||||
if j+nlines < len(words) {
|
||||
for col < endcol {
|
||||
_, err := w.w.Write([]byte{' '})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
col++
|
||||
}
|
||||
}
|
||||
}
|
||||
_, err := w.w.Write([]byte{'\n'})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
90
Godeps/_workspace/src/github.com/kr/text/colwriter/column_test.go
generated
vendored
Normal file
90
Godeps/_workspace/src/github.com/kr/text/colwriter/column_test.go
generated
vendored
Normal file
@ -0,0 +1,90 @@
|
||||
package colwriter
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"testing"
|
||||
)
|
||||
|
||||
var src = `
|
||||
.git
|
||||
.gitignore
|
||||
.godir
|
||||
Procfile:
|
||||
README.md
|
||||
api.go
|
||||
apps.go
|
||||
auth.go
|
||||
darwin.go
|
||||
data.go
|
||||
dyno.go:
|
||||
env.go
|
||||
git.go
|
||||
help.go
|
||||
hkdist
|
||||
linux.go
|
||||
ls.go
|
||||
main.go
|
||||
plugin.go
|
||||
run.go
|
||||
scale.go
|
||||
ssh.go
|
||||
tail.go
|
||||
term
|
||||
unix.go
|
||||
update.go
|
||||
version.go
|
||||
windows.go
|
||||
`[1:]
|
||||
|
||||
var tests = []struct {
|
||||
wid int
|
||||
flag uint
|
||||
src string
|
||||
want string
|
||||
}{
|
||||
{80, 0, "", ""},
|
||||
{80, 0, src, `
|
||||
.git README.md darwin.go git.go ls.go scale.go unix.go
|
||||
.gitignore api.go data.go help.go main.go ssh.go update.go
|
||||
.godir apps.go dyno.go: hkdist plugin.go tail.go version.go
|
||||
Procfile: auth.go env.go linux.go run.go term windows.go
|
||||
`[1:]},
|
||||
{80, BreakOnColon, src, `
|
||||
.git .gitignore .godir
|
||||
|
||||
Procfile:
|
||||
README.md api.go apps.go auth.go darwin.go data.go
|
||||
|
||||
dyno.go:
|
||||
env.go hkdist main.go scale.go term version.go
|
||||
git.go linux.go plugin.go ssh.go unix.go windows.go
|
||||
help.go ls.go run.go tail.go update.go
|
||||
`[1:]},
|
||||
{20, 0, `
|
||||
Hello
|
||||
Γειά σου
|
||||
안녕
|
||||
今日は
|
||||
`[1:], `
|
||||
Hello 안녕
|
||||
Γειά σου 今日は
|
||||
`[1:]},
|
||||
}
|
||||
|
||||
func TestWriter(t *testing.T) {
|
||||
for _, test := range tests {
|
||||
b := new(bytes.Buffer)
|
||||
w := NewWriter(b, test.wid, test.flag)
|
||||
if _, err := w.Write([]byte(test.src)); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if err := w.Flush(); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if g := b.String(); test.want != g {
|
||||
t.Log("\n" + test.want)
|
||||
t.Log("\n" + g)
|
||||
t.Errorf("%q != %q", test.want, g)
|
||||
}
|
||||
}
|
||||
}
|
3
Godeps/_workspace/src/github.com/kr/text/doc.go
generated
vendored
Normal file
3
Godeps/_workspace/src/github.com/kr/text/doc.go
generated
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
// Package text provides rudimentary functions for manipulating text in
|
||||
// paragraphs.
|
||||
package text
|
74
Godeps/_workspace/src/github.com/kr/text/indent.go
generated
vendored
Normal file
74
Godeps/_workspace/src/github.com/kr/text/indent.go
generated
vendored
Normal file
@ -0,0 +1,74 @@
|
||||
package text
|
||||
|
||||
import (
|
||||
"io"
|
||||
)
|
||||
|
||||
// Indent inserts prefix at the beginning of each non-empty line of s. The
|
||||
// end-of-line marker is NL.
|
||||
func Indent(s, prefix string) string {
|
||||
return string(IndentBytes([]byte(s), []byte(prefix)))
|
||||
}
|
||||
|
||||
// IndentBytes inserts prefix at the beginning of each non-empty line of b.
|
||||
// The end-of-line marker is NL.
|
||||
func IndentBytes(b, prefix []byte) []byte {
|
||||
var res []byte
|
||||
bol := true
|
||||
for _, c := range b {
|
||||
if bol && c != '\n' {
|
||||
res = append(res, prefix...)
|
||||
}
|
||||
res = append(res, c)
|
||||
bol = c == '\n'
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
// Writer indents each line of its input.
|
||||
type indentWriter struct {
|
||||
w io.Writer
|
||||
bol bool
|
||||
pre [][]byte
|
||||
sel int
|
||||
off int
|
||||
}
|
||||
|
||||
// NewIndentWriter makes a new write filter that indents the input
|
||||
// lines. Each line is prefixed in order with the corresponding
|
||||
// element of pre. If there are more lines than elements, the last
|
||||
// element of pre is repeated for each subsequent line.
|
||||
func NewIndentWriter(w io.Writer, pre ...[]byte) io.Writer {
|
||||
return &indentWriter{
|
||||
w: w,
|
||||
pre: pre,
|
||||
bol: true,
|
||||
}
|
||||
}
|
||||
|
||||
// The only errors returned are from the underlying indentWriter.
|
||||
func (w *indentWriter) Write(p []byte) (n int, err error) {
|
||||
for _, c := range p {
|
||||
if w.bol {
|
||||
var i int
|
||||
i, err = w.w.Write(w.pre[w.sel][w.off:])
|
||||
w.off += i
|
||||
if err != nil {
|
||||
return n, err
|
||||
}
|
||||
}
|
||||
_, err = w.w.Write([]byte{c})
|
||||
if err != nil {
|
||||
return n, err
|
||||
}
|
||||
n++
|
||||
w.bol = c == '\n'
|
||||
if w.bol {
|
||||
w.off = 0
|
||||
if w.sel < len(w.pre)-1 {
|
||||
w.sel++
|
||||
}
|
||||
}
|
||||
}
|
||||
return n, nil
|
||||
}
|
119
Godeps/_workspace/src/github.com/kr/text/indent_test.go
generated
vendored
Normal file
119
Godeps/_workspace/src/github.com/kr/text/indent_test.go
generated
vendored
Normal file
@ -0,0 +1,119 @@
|
||||
package text
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"testing"
|
||||
)
|
||||
|
||||
type T struct {
|
||||
inp, exp, pre string
|
||||
}
|
||||
|
||||
var tests = []T{
|
||||
{
|
||||
"The quick brown fox\njumps over the lazy\ndog.\nBut not quickly.\n",
|
||||
"xxxThe quick brown fox\nxxxjumps over the lazy\nxxxdog.\nxxxBut not quickly.\n",
|
||||
"xxx",
|
||||
},
|
||||
{
|
||||
"The quick brown fox\njumps over the lazy\ndog.\n\nBut not quickly.",
|
||||
"xxxThe quick brown fox\nxxxjumps over the lazy\nxxxdog.\n\nxxxBut not quickly.",
|
||||
"xxx",
|
||||
},
|
||||
}
|
||||
|
||||
func TestIndent(t *testing.T) {
|
||||
for _, test := range tests {
|
||||
got := Indent(test.inp, test.pre)
|
||||
if got != test.exp {
|
||||
t.Errorf("mismatch %q != %q", got, test.exp)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type IndentWriterTest struct {
|
||||
inp, exp string
|
||||
pre []string
|
||||
}
|
||||
|
||||
var ts = []IndentWriterTest{
|
||||
{
|
||||
`
|
||||
The quick brown fox
|
||||
jumps over the lazy
|
||||
dog.
|
||||
But not quickly.
|
||||
`[1:],
|
||||
`
|
||||
xxxThe quick brown fox
|
||||
xxxjumps over the lazy
|
||||
xxxdog.
|
||||
xxxBut not quickly.
|
||||
`[1:],
|
||||
[]string{"xxx"},
|
||||
},
|
||||
{
|
||||
`
|
||||
The quick brown fox
|
||||
jumps over the lazy
|
||||
dog.
|
||||
But not quickly.
|
||||
`[1:],
|
||||
`
|
||||
xxaThe quick brown fox
|
||||
xxxjumps over the lazy
|
||||
xxxdog.
|
||||
xxxBut not quickly.
|
||||
`[1:],
|
||||
[]string{"xxa", "xxx"},
|
||||
},
|
||||
{
|
||||
`
|
||||
The quick brown fox
|
||||
jumps over the lazy
|
||||
dog.
|
||||
But not quickly.
|
||||
`[1:],
|
||||
`
|
||||
xxaThe quick brown fox
|
||||
xxbjumps over the lazy
|
||||
xxcdog.
|
||||
xxxBut not quickly.
|
||||
`[1:],
|
||||
[]string{"xxa", "xxb", "xxc", "xxx"},
|
||||
},
|
||||
{
|
||||
`
|
||||
The quick brown fox
|
||||
jumps over the lazy
|
||||
dog.
|
||||
|
||||
But not quickly.`[1:],
|
||||
`
|
||||
xxaThe quick brown fox
|
||||
xxxjumps over the lazy
|
||||
xxxdog.
|
||||
xxx
|
||||
xxxBut not quickly.`[1:],
|
||||
[]string{"xxa", "xxx"},
|
||||
},
|
||||
}
|
||||
|
||||
func TestIndentWriter(t *testing.T) {
|
||||
for _, test := range ts {
|
||||
b := new(bytes.Buffer)
|
||||
pre := make([][]byte, len(test.pre))
|
||||
for i := range test.pre {
|
||||
pre[i] = []byte(test.pre[i])
|
||||
}
|
||||
w := NewIndentWriter(b, pre...)
|
||||
if _, err := w.Write([]byte(test.inp)); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if got := b.String(); got != test.exp {
|
||||
t.Errorf("mismatch %q != %q", got, test.exp)
|
||||
t.Log(got)
|
||||
t.Log(test.exp)
|
||||
}
|
||||
}
|
||||
}
|
9
Godeps/_workspace/src/github.com/kr/text/mc/Readme
generated
vendored
Normal file
9
Godeps/_workspace/src/github.com/kr/text/mc/Readme
generated
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
Command mc prints in multiple columns.
|
||||
|
||||
Usage: mc [-] [-N] [file...]
|
||||
|
||||
Mc splits the input into as many columns as will fit in N
|
||||
print positions. If the output is a tty, the default N is
|
||||
the number of characters in a terminal line; otherwise the
|
||||
default N is 80. Under option - each input line ending in
|
||||
a colon ':' is printed separately.
|
62
Godeps/_workspace/src/github.com/kr/text/mc/mc.go
generated
vendored
Normal file
62
Godeps/_workspace/src/github.com/kr/text/mc/mc.go
generated
vendored
Normal file
@ -0,0 +1,62 @@
|
||||
// Command mc prints in multiple columns.
|
||||
//
|
||||
// Usage: mc [-] [-N] [file...]
|
||||
//
|
||||
// Mc splits the input into as many columns as will fit in N
|
||||
// print positions. If the output is a tty, the default N is
|
||||
// the number of characters in a terminal line; otherwise the
|
||||
// default N is 80. Under option - each input line ending in
|
||||
// a colon ':' is printed separately.
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/kr/pty"
|
||||
"github.com/kr/text/colwriter"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
func main() {
|
||||
var width int
|
||||
var flag uint
|
||||
args := os.Args[1:]
|
||||
for len(args) > 0 && len(args[0]) > 0 && args[0][0] == '-' {
|
||||
if len(args[0]) > 1 {
|
||||
width, _ = strconv.Atoi(args[0][1:])
|
||||
} else {
|
||||
flag |= colwriter.BreakOnColon
|
||||
}
|
||||
args = args[1:]
|
||||
}
|
||||
if width < 1 {
|
||||
_, width, _ = pty.Getsize(os.Stdout)
|
||||
}
|
||||
if width < 1 {
|
||||
width = 80
|
||||
}
|
||||
|
||||
w := colwriter.NewWriter(os.Stdout, width, flag)
|
||||
if len(args) > 0 {
|
||||
for _, s := range args {
|
||||
if f, err := os.Open(s); err == nil {
|
||||
copyin(w, f)
|
||||
f.Close()
|
||||
} else {
|
||||
log.Println(err)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
copyin(w, os.Stdin)
|
||||
}
|
||||
}
|
||||
|
||||
func copyin(w *colwriter.Writer, r io.Reader) {
|
||||
if _, err := io.Copy(w, r); err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
if err := w.Flush(); err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
}
|
86
Godeps/_workspace/src/github.com/kr/text/wrap.go
generated
vendored
Normal file
86
Godeps/_workspace/src/github.com/kr/text/wrap.go
generated
vendored
Normal file
@ -0,0 +1,86 @@
|
||||
package text
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"math"
|
||||
)
|
||||
|
||||
var (
|
||||
nl = []byte{'\n'}
|
||||
sp = []byte{' '}
|
||||
)
|
||||
|
||||
const defaultPenalty = 1e5
|
||||
|
||||
// Wrap wraps s into a paragraph of lines of length lim, with minimal
|
||||
// raggedness.
|
||||
func Wrap(s string, lim int) string {
|
||||
return string(WrapBytes([]byte(s), lim))
|
||||
}
|
||||
|
||||
// WrapBytes wraps b into a paragraph of lines of length lim, with minimal
|
||||
// raggedness.
|
||||
func WrapBytes(b []byte, lim int) []byte {
|
||||
words := bytes.Split(bytes.Replace(bytes.TrimSpace(b), nl, sp, -1), sp)
|
||||
var lines [][]byte
|
||||
for _, line := range WrapWords(words, 1, lim, defaultPenalty) {
|
||||
lines = append(lines, bytes.Join(line, sp))
|
||||
}
|
||||
return bytes.Join(lines, nl)
|
||||
}
|
||||
|
||||
// WrapWords is the low-level line-breaking algorithm, useful if you need more
|
||||
// control over the details of the text wrapping process. For most uses, either
|
||||
// Wrap or WrapBytes will be sufficient and more convenient.
|
||||
//
|
||||
// WrapWords splits a list of words into lines with minimal "raggedness",
|
||||
// treating each byte as one unit, accounting for spc units between adjacent
|
||||
// words on each line, and attempting to limit lines to lim units. Raggedness
|
||||
// is the total error over all lines, where error is the square of the
|
||||
// difference of the length of the line and lim. Too-long lines (which only
|
||||
// happen when a single word is longer than lim units) have pen penalty units
|
||||
// added to the error.
|
||||
func WrapWords(words [][]byte, spc, lim, pen int) [][][]byte {
|
||||
n := len(words)
|
||||
|
||||
length := make([][]int, n)
|
||||
for i := 0; i < n; i++ {
|
||||
length[i] = make([]int, n)
|
||||
length[i][i] = len(words[i])
|
||||
for j := i + 1; j < n; j++ {
|
||||
length[i][j] = length[i][j-1] + spc + len(words[j])
|
||||
}
|
||||
}
|
||||
|
||||
nbrk := make([]int, n)
|
||||
cost := make([]int, n)
|
||||
for i := range cost {
|
||||
cost[i] = math.MaxInt32
|
||||
}
|
||||
for i := n - 1; i >= 0; i-- {
|
||||
if length[i][n-1] <= lim || i == n-1 {
|
||||
cost[i] = 0
|
||||
nbrk[i] = n
|
||||
} else {
|
||||
for j := i + 1; j < n; j++ {
|
||||
d := lim - length[i][j-1]
|
||||
c := d*d + cost[j]
|
||||
if length[i][j-1] > lim {
|
||||
c += pen // too-long lines get a worse penalty
|
||||
}
|
||||
if c < cost[i] {
|
||||
cost[i] = c
|
||||
nbrk[i] = j
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var lines [][][]byte
|
||||
i := 0
|
||||
for i < n {
|
||||
lines = append(lines, words[i:nbrk[i]])
|
||||
i = nbrk[i]
|
||||
}
|
||||
return lines
|
||||
}
|
62
Godeps/_workspace/src/github.com/kr/text/wrap_test.go
generated
vendored
Normal file
62
Godeps/_workspace/src/github.com/kr/text/wrap_test.go
generated
vendored
Normal file
@ -0,0 +1,62 @@
|
||||
package text
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"testing"
|
||||
)
|
||||
|
||||
var text = "The quick brown fox jumps over the lazy dog."
|
||||
|
||||
func TestWrap(t *testing.T) {
|
||||
exp := [][]string{
|
||||
{"The", "quick", "brown", "fox"},
|
||||
{"jumps", "over", "the", "lazy", "dog."},
|
||||
}
|
||||
words := bytes.Split([]byte(text), sp)
|
||||
got := WrapWords(words, 1, 24, defaultPenalty)
|
||||
if len(exp) != len(got) {
|
||||
t.Fail()
|
||||
}
|
||||
for i := range exp {
|
||||
if len(exp[i]) != len(got[i]) {
|
||||
t.Fail()
|
||||
}
|
||||
for j := range exp[i] {
|
||||
if exp[i][j] != string(got[i][j]) {
|
||||
t.Fatal(i, exp[i][j], got[i][j])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestWrapNarrow(t *testing.T) {
|
||||
exp := "The\nquick\nbrown\nfox\njumps\nover\nthe\nlazy\ndog."
|
||||
if Wrap(text, 5) != exp {
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
|
||||
func TestWrapOneLine(t *testing.T) {
|
||||
exp := "The quick brown fox jumps over the lazy dog."
|
||||
if Wrap(text, 500) != exp {
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
|
||||
func TestWrapBug1(t *testing.T) {
|
||||
cases := []struct {
|
||||
limit int
|
||||
text string
|
||||
want string
|
||||
}{
|
||||
{4, "aaaaa", "aaaaa"},
|
||||
{4, "a aaaaa", "a\naaaaa"},
|
||||
}
|
||||
|
||||
for _, test := range cases {
|
||||
got := Wrap(test.text, test.limit)
|
||||
if got != test.want {
|
||||
t.Errorf("Wrap(%q, %d) = %q want %q", test.text, test.limit, got, test.want)
|
||||
}
|
||||
}
|
||||
}
|
@ -44,7 +44,7 @@ docs-test: docs-build
|
||||
$(DOCKER_RUN_DOCS) "$(DOCKER_DOCS_IMAGE)" ./test.sh
|
||||
|
||||
docs-build:
|
||||
git fetch https://github.com/grafana/grafana.git docs-2.5 && git diff --name-status FETCH_HEAD...HEAD -- . > changed-files
|
||||
git fetch https://github.com/grafana/grafana.git docs-2.6 && git diff --name-status FETCH_HEAD...HEAD -- . > changed-files
|
||||
echo "$(GIT_BRANCH)" > GIT_BRANCH
|
||||
echo "$(GITCOMMIT)" > GITCOMMIT
|
||||
docker build -t "$(DOCKER_DOCS_IMAGE)" .
|
||||
|
@ -1 +1 @@
|
||||
2.6.0
|
||||
3.0.0
|
||||
|
@ -25,9 +25,10 @@ google_analytics: ['UA-47280256-1', 'grafana.org']
|
||||
pages:
|
||||
|
||||
# Introduction:
|
||||
- ['index.md', 'Project', 'About Grafana']
|
||||
- ['project/cla.md', 'Project', 'Contributor License Agreement']
|
||||
# - ['index.md', 'Project', 'About Grafana']
|
||||
# - ['project/cla.md', 'Project', 'Contributor License Agreement']
|
||||
|
||||
- ['index.md', '**HIDDEN**']
|
||||
- ['installation/index.md', 'Installation', 'Installation']
|
||||
- ['installation/debian.md', 'Installation', 'Installing on Debian / Ubuntu']
|
||||
- ['installation/rpm.md', 'Installation', 'Installing on RPM-based Linux']
|
||||
@ -62,7 +63,6 @@ pages:
|
||||
- ['reference/templating.md', 'Reference', 'Templating']
|
||||
- ['reference/scripting.md', 'Reference', 'Scripting']
|
||||
- ['reference/playlist.md', 'Reference', 'Playlist']
|
||||
- ['reference/plugins.md', 'Reference', 'Plugins']
|
||||
- ['reference/export_import.md', 'Reference', 'Import & Export']
|
||||
- ['reference/admin.md', 'Reference', 'Administration']
|
||||
- ['reference/keyboard_shortcuts.md', 'Reference', 'Keyboard Shortcuts']
|
||||
@ -90,6 +90,8 @@ pages:
|
||||
- ['plugins/installation.md', 'Plugins', 'Installation']
|
||||
- ['plugins/datasources.md', 'Plugins', 'Datasource plugins']
|
||||
- ['plugins/panels.md', 'Plugins', 'Panel plugins']
|
||||
- ['plugins/development.md', 'Plugins', 'Plugin development']
|
||||
- ['plugins/plugin.json.md', 'Plugins', 'Plugin json']
|
||||
|
||||
- ['tutorials/index.md', 'Tutorials', 'Tutorials']
|
||||
- ['tutorials/hubot_howto.md', 'Tutorials', 'How To integrate Hubot and Grafana']
|
||||
|
@ -64,9 +64,19 @@ Name | Description
|
||||
`metrics(namespace)` | Returns a list of metrics in the namespace.
|
||||
`dimension_keys(namespace)` | Returns a list of dimension keys in the namespace.
|
||||
`dimension_values(region, namespace, metric, dimension_key)` | Returns a list of dimension values matching the specified `region`, `namespace`, `metric` and `dimension_key`.
|
||||
`ebs_volume_ids(region, instance_id)` | Returns a list of volume id matching the specified `region`, `instance_id`.
|
||||
`ec2_instance_attribute(region, attribute_name, filters)` | Returns a list of attribute matching the specified `region`, `attribute_name`, `filters`.
|
||||
|
||||
For details about the metrics CloudWatch provides, please refer to the [CloudWatch documentation](https://docs.aws.amazon.com/AmazonCloudWatch/latest/DeveloperGuide/CW_Support_For_AWS.html).
|
||||
|
||||
The `ec2_instance_attribute` query take `filters` in JSON format.
|
||||
You can specify [pre-defined filters of ec2:DescribeInstances](http://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_DescribeInstances.html).
|
||||
Specify like `{ filter_name1: [ filter_value1 ], filter_name2: [ filter_value2 ] }`
|
||||
|
||||
Example `ec2_instance_attribute()` query
|
||||
|
||||
ec2_instance_attribute(us-east-1, InstanceId, { "tag:Environment": [ "production" ] })
|
||||
|
||||

|
||||
|
||||
## Cost
|
||||
|
@ -74,6 +74,59 @@ page_keywords: grafana, admin, http, api, documentation, datasource
|
||||
"jsonData":null
|
||||
}
|
||||
|
||||
## Get a single data source by Name
|
||||
|
||||
`GET /api/datasources/name/:name`
|
||||
|
||||
**Example Request**:
|
||||
|
||||
GET /api/datasources/name/test_datasource HTTP/1.1
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
|
||||
|
||||
**Example Response**:
|
||||
|
||||
HTTP/1.1 200
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"id":1,
|
||||
"orgId":1,
|
||||
"name":"test_datasource",
|
||||
"type":"graphite",
|
||||
"access":"proxy",
|
||||
"url":"http://mydatasource.com",
|
||||
"password":"",
|
||||
"user":"",
|
||||
"database":"",
|
||||
"basicAuth":false,
|
||||
"basicAuthUser":"",
|
||||
"basicAuthPassword":"",
|
||||
"isDefault":false,
|
||||
"jsonData":null
|
||||
}
|
||||
|
||||
## Get data source Id by Name
|
||||
|
||||
`GET /api/datasources/id/:name`
|
||||
|
||||
**Example Request**:
|
||||
|
||||
GET /api/datasources/id/test_datasource HTTP/1.1
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
|
||||
|
||||
**Example Response**:
|
||||
|
||||
HTTP/1.1 200
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"id":1
|
||||
}
|
||||
|
||||
## Create data source
|
||||
|
||||
`POST /api/datasources`
|
||||
|
@ -1,39 +1,37 @@
|
||||
page_title: About Grafana
|
||||
page_description: Introduction to Grafana.
|
||||
page_keywords: grafana, introduction, documentation, about
|
||||
---
|
||||
page_title: Grafana Installation
|
||||
page_description: Install guide for Grafana.
|
||||
page_keywords: grafana, installation, documentation
|
||||
---
|
||||
|
||||
# About Grafana
|
||||
# Installation
|
||||
|
||||
Grafana is a leading open source application for visualizing large-scale measurement data.
|
||||
Grafana is easily installed via a Debian/Ubuntu package (.deb), via
|
||||
Redhat/Centos package (.rpm) or manually via a tarball that contains all
|
||||
required files and binaries. If you can't find a package or binary for
|
||||
your platform, you might be able to build one yourself. Read the [build
|
||||
from source](../project/building_from_source) instructions for more
|
||||
information.
|
||||
|
||||
It provides a powerful and elegant way to create, share, and explore data and dashboards from your disparate metric databases, either with your team or the world.
|
||||
## Platforms
|
||||
- [Installing on Debian / Ubuntu](installation/debian.md)
|
||||
- [Installing on RPM-based Linux (CentOS, Fedora, OpenSuse, RedHat)](installation/rpm.md)
|
||||
- [Installing on Mac OS X](installation/mac.md)
|
||||
- [Installing on Windows](installation/windows.md)
|
||||
- [Installing on Docker](installation/docker.md)
|
||||
- [Installing using Provisioning (Chef, Puppet, Salt, Ansible, etc)](installation/provisioning.md)
|
||||
- [Nightly Builds](http://grafana.org/download/builds.html)
|
||||
|
||||
Grafana is most commonly used for Internet infrastructure and application analytics, but many use it in other domains including industrial sensors, home automation, weather, and process control.
|
||||
## Configuration
|
||||
|
||||
Grafana features pluggable panels and data sources allowing easy extensibility. There is currently rich support for [Graphite](http://graphite.readthedocs.org/en/latest/), [InfluxDB](http://influxdb.org) and [OpenTSDB](http://opentsdb.net). There is also experimental support for [KairosDB](https://github.com/kairosdb/kairosdb), [Prometheus](http://prometheus.io/), and SQL is on the roadmap. Grafana has a variety of panels, including a fully featured graph panel with rich visualization options.
|
||||
The back-end web server has a number of configuration options. Go the
|
||||
[Configuration](/installation/configuration) page for details on all
|
||||
those options.
|
||||
|
||||
Version 2.0 was released in April 2015: Grafana now ships with its own backend server that brings [many changes and features](../guides/whats-new-in-v2/).
|
||||
Version 2.1 was released in July 2015 and added [even more features and enhancements](../guides/whats-new-in-v2-1/).
|
||||
## Data sources guides
|
||||
|
||||
## Community Resources, Feedback, and Support
|
||||
- [Graphite](datasources/graphite.md)
|
||||
- [Elasticsearch](datasources/elasticsearch.md)
|
||||
- [InfluxDB](datasources/influxdb.md)
|
||||
- [OpenTSDB](datasources/opentsdb.md)
|
||||
|
||||
Thousands of organizations large and small rely on Grafana, and we have a vibrant and active community that constantly inspires us.
|
||||
|
||||
Please don't hesitate to [open a new issue on Github](https://github.com/grafana/grafana/issues) with your suggestions, ideas, and bug reports.
|
||||
|
||||
Most of the new features and improvements that go into Grafana come from our users. We greatly value your feedback and suggestions; we consider them paramount to making the product better!
|
||||
|
||||
If you have any trouble with Grafana, whether you can't get it set up or you just want clarification on a feature, there are a number of ways to get help:
|
||||
|
||||
- [Troubleshooting guide](/installation/troubleshooting/)
|
||||
- \#grafana IRC channel on the freenode network (chat.freenode.net)
|
||||
- Search closed and open [issues on GitHub](https://github.com/grafana/grafana/issues)
|
||||
- [Mailing list](https://groups.io/org/groupsio/grafana)
|
||||
|
||||
## Commercial Support
|
||||
|
||||
[raintank](http://www.raintank.io), the company behind Grafana, will be launching a SaaS Grafana-based platform later this year that will also include commercial support for all your existing Grafana installations. Please sign up for [early access at raintank](http://www.raintank.io) for more information.
|
||||
|
||||
## License
|
||||
|
||||
By utilizing this software, you agree to the terms of the included license. Grafana is licensed under the Apache 2.0 agreement. See [LICENSE](https://github.com/grafana/grafana/blob/master/LICENSE.md) for the full license terms.
|
||||
|
@ -159,19 +159,19 @@ The database user's password (not applicable for `sqlite3`).
|
||||
For Postgres, use either `disable`, `require` or `verify-full`.
|
||||
For MySQL, use either `true`, `false`, or `skip-verify`.
|
||||
|
||||
### ca_cert_path
|
||||
### ca_cert_path
|
||||
|
||||
(MySQL only) The path to the CA certificate to use. On many linux systems, certs can be found in `/etc/ssl/certs`.
|
||||
|
||||
### client_key_path
|
||||
### client_key_path
|
||||
|
||||
(MySQL only) The path to the client key. Only if server requires client authentication.
|
||||
|
||||
### client_cert_path
|
||||
### client_cert_path
|
||||
|
||||
(MySQL only) The path to the client cert. Only if server requires client authentication.
|
||||
|
||||
### server_cert_name
|
||||
### server_cert_name
|
||||
|
||||
(MySQL only) The common name field of the certificate used by the `mysql` server. Not necessary if `ssl_mode` is set to `skip-verify`.
|
||||
|
||||
@ -373,7 +373,7 @@ Set to `true` to enable auto sign up of users who do not exist in Grafana DB. De
|
||||
|
||||
### provider
|
||||
|
||||
Valid values are `memory`, `file`, `mysql`, `postgres`, `memcache`. Default is `file`.
|
||||
Valid values are `memory`, `file`, `mysql`, `postgres`, `memcache` or `redis`. Default is `file`.
|
||||
|
||||
### provider_config
|
||||
|
||||
@ -384,6 +384,7 @@ session provider you have configured.
|
||||
- **mysql:** go-sql-driver/mysql dsn config string, e.g. `user:password@tcp(127.0.0.1:3306)/database_name`
|
||||
- **postgres:** ex: user=a password=b host=localhost port=5432 dbname=c sslmode=disable
|
||||
- **memcache:** ex: 127.0.0.1:11211
|
||||
- **redis:** ex: `addr=127.0.0.1:6379,pool_size=100,db=grafana`
|
||||
|
||||
If you use MySQL or Postgres as the session store you need to create the
|
||||
session table manually.
|
||||
@ -415,10 +416,10 @@ How long sessions lasts in seconds. Defaults to `86400` (24 hours).
|
||||
|
||||
### reporting_enabled
|
||||
|
||||
When enabled Grafana will send anonymous usage statistics to
|
||||
When enabled Grafana will send anonymous usage statistics to
|
||||
`stats.grafana.org`. No IP addresses are being tracked, only simple counters to
|
||||
track running instances, versions, dashboard & error counts. It is very helpful
|
||||
to us, so please leave this enabled. Counters are sent every 24 hours. Default
|
||||
to us, so please leave this enabled. Counters are sent every 24 hours. Default
|
||||
value is `true`.
|
||||
|
||||
### google_analytics_ua_id
|
||||
|
@ -13,6 +13,7 @@ your platform, you might be able to build one yourself. Read the [build
|
||||
from source](../project/building_from_source) instructions for more
|
||||
information.
|
||||
|
||||
## Platforms
|
||||
- [Installing on Debian / Ubuntu](debian.md)
|
||||
- [Installing on RPM-based Linux (CentOS, Fedora, OpenSuse, RedHat)](rpm.md)
|
||||
- [Installing on Mac OS X](mac.md)
|
||||
|
@ -6,7 +6,7 @@ page_keywords: grafana, ldap, configuration, documentation, integration
|
||||
|
||||
# LDAP Integration
|
||||
|
||||
Grafana 2.1 ships with a strong LDAP integration feature. The LDAP integration in Grafana allows your
|
||||
Grafana (2.1 and newer) ships with a strong LDAP integration feature. The LDAP integration in Grafana allows your
|
||||
Grafana users to login with their LDAP credentials. You can also specify mappings between LDAP
|
||||
group memberships and Grafana Organization user roles.
|
||||
|
||||
|
@ -4,6 +4,168 @@ page_description: Datasource plugins for Grafana
|
||||
page_keywords: grafana, plugins, documentation
|
||||
---
|
||||
|
||||
> Our goal is not to have a very extensive documentation but rather have actual code that people can look at. An example implementation of a datasource can be found in the grafana repo under /examples/datasource-plugin-genericdatasource
|
||||
|
||||
# Datasources
|
||||
|
||||
TODO
|
||||
Datasource plugins enables people to develop plugins for any database that communicates over http. Its up to the plugin to transform the data into time series data so that any grafana panel can then show it.
|
||||
|
||||
To interact with the rest of grafana the plugins module file can export 5 different components.
|
||||
|
||||
- Datasource (Required)
|
||||
- QueryCtrl (Required)
|
||||
- ConfigCtrl (Required)
|
||||
- QueryOptionsCtrl
|
||||
- AnnotationsQueryCtrl
|
||||
|
||||
## Plugin json
|
||||
There are two datasource specific settings for the plugin.json
|
||||
```javascript
|
||||
"metrics": true,
|
||||
"annotations": false,
|
||||
```
|
||||
These settings indicates what kind of data the plugin can deliver. At least one of them have to be true
|
||||
|
||||
## Datasource
|
||||
The javascript object that communicates with the database and transforms data to times series.
|
||||
|
||||
The Datasource should contain the following functions.
|
||||
```
|
||||
query(options) //used by panels to get data
|
||||
testDatasource() //used by datasource configuration page to make sure the connection is working
|
||||
annotationsQuery(options) // used dashboards to get annotations
|
||||
metricFindQuery(options) // used by query editor to get metric suggestions.
|
||||
```
|
||||
|
||||
### Query
|
||||
|
||||
Request object passed to datasource.query function
|
||||
```json
|
||||
{
|
||||
"range": { "from": "2015-12-22T03:06:13.851Z", "to": "2015-12-22T06:48:24.137Z" },
|
||||
"interval": "5s",
|
||||
"targets": [
|
||||
{ "refId": "B", "target": "upper_75" },
|
||||
{ "refId": "A", "target": "upper_90" }
|
||||
],
|
||||
"format": "json",
|
||||
"maxDataPoints": 2495 //decided by the panel
|
||||
}
|
||||
```
|
||||
|
||||
There are two different kind of results for datasources.
|
||||
Time series and table. Time series is the most common format and is supported by all datasources and panels. Table format is only support by the Influxdb datasource and table panel. But we might see more of this in the future.
|
||||
|
||||
Time series response from datasource.query
|
||||
An array of
|
||||
```json
|
||||
[
|
||||
{
|
||||
"target":"upper_75",
|
||||
"datapoints":[
|
||||
[622,1450754160000],
|
||||
[365,1450754220000]
|
||||
]
|
||||
},
|
||||
{
|
||||
"target":"upper_90",
|
||||
"datapoints":[
|
||||
[861,1450754160000],
|
||||
[767,1450754220000]
|
||||
]
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
Table response from datasource.query
|
||||
An array of
|
||||
```json
|
||||
[
|
||||
{
|
||||
"columns": [
|
||||
{
|
||||
"text": "Time",
|
||||
"type": "time",
|
||||
"sort": true,
|
||||
"desc": true,
|
||||
},
|
||||
{
|
||||
"text": "mean",
|
||||
},
|
||||
{
|
||||
"text": "sum",
|
||||
}
|
||||
],
|
||||
"rows": [
|
||||
[
|
||||
1457425380000,
|
||||
null,
|
||||
null
|
||||
],
|
||||
[
|
||||
1457425370000,
|
||||
1002.76215352,
|
||||
1002.76215352
|
||||
],
|
||||
],
|
||||
"type": "table"
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
### Annotation Query
|
||||
|
||||
Request object passed to datasource.annotationsQuery function
|
||||
```json
|
||||
{
|
||||
"range": { "from": "2016-03-04T04:07:55.144Z", "to": "2016-03-04T07:07:55.144Z" },
|
||||
"rangeRaw": { "from": "now-3h", to: "now" },
|
||||
"annotation": {
|
||||
"datasource": "generic datasource",
|
||||
"enable": true,
|
||||
"name": "annotation name"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Expected result from datasource.annotationQuery
|
||||
```json
|
||||
[
|
||||
{
|
||||
"annotation": {
|
||||
"name": "annotation name", //should match the annotation name in grafana
|
||||
"enabled": true,
|
||||
"datasource": "generic datasource",
|
||||
},
|
||||
"title": "Cluster outage",
|
||||
"time": 1457075272576,
|
||||
"text": "Joe causes brain split",
|
||||
"tags": "joe, cluster, failure"
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
|
||||
## QueryCtrl
|
||||
|
||||
A javascript class that will be instantiated and treated as an Angular controller when the user edits metrics in a panel. This class have to inherit from the app/plugins/sdk.QueryCtrl class.
|
||||
|
||||
Requires a static template or templateUrl variable which will be rendered as the view for this controller.
|
||||
|
||||
## ConfigCtrl
|
||||
|
||||
A javascript class that will be instantiated and treated as an Angular controller when a user tries to edit or create a new datasource of this type.
|
||||
|
||||
Requires a static template or templateUrl variable which will be rendered as the view for this controller.
|
||||
|
||||
## QueryOptionsCtrl
|
||||
|
||||
A javascript class that will be instantiated and treated as an Angular controller when the user edits metrics in a panel. This controller is responsible for handling panel wide settings for the datasource. Such as interval, rate and aggregations if needed.
|
||||
|
||||
Requires a static template or templateUrl variable which will be rendered as the view for this controller.
|
||||
|
||||
## AnnotationsQueryCtrl
|
||||
|
||||
A javascript class that will be instantiated and treated as an Angular controller when the user choose this type of datasource in the templating menu in the dashboard.
|
||||
|
||||
Requires a static template or templateUrl variable which will be rendered as the view for this controller. The fields that are bound to this controller is then sent to the Database objects annotationsQuery function.
|
||||
|
30
docs/sources/plugins/developing_plugins.md
Normal file
30
docs/sources/plugins/developing_plugins.md
Normal file
@ -0,0 +1,30 @@
|
||||
---
|
||||
page_title: Plugin development
|
||||
page_description: Plugin development for Grafana
|
||||
page_keywords: grafana, plugins, documentation, development
|
||||
---
|
||||
|
||||
# Plugin development
|
||||
|
||||
From grafana 3.0 it's very easy to develop your own plugins and share them with other grafana users.
|
||||
|
||||
## What languages?
|
||||
|
||||
Since everything turns into javascript its up to you to choose which language you want. That said its proberbly a good idea to choose es6 or typescript since we use es6 classes in Grafana.
|
||||
|
||||
##Buildscript
|
||||
|
||||
You can use any buildsystem you like that support systemjs. All the built content should endup in a folder named dist and commited to the repository.
|
||||
|
||||
##Loading plugins
|
||||
The easiset way to try your plugin with grafana is to [setup grafana for development](https://github.com/grafana/grafana/blob/master/DEVELOPMENT.md) and place your plugin in the /data/plugins folder in grafana. When grafana starts it will scan that folder for folders that contains a plugin.json file and mount them as plugins. If your plugin folder contains a folder named dist it will mount that folder instead of the plugin base folder.
|
||||
|
||||
## Examples / boilerplate
|
||||
We currently have three different examples that you can fork to get started developing your grafana plugin.
|
||||
|
||||
- [generic-datasource](https://github.com/grafana/grafana/tree/master/examples/datasource-plugin-genericdatasource) (small datasource plugin for quering json data from backends)
|
||||
- [panel-boilderplate-es5](https://github.com/grafana/grafana/tree/master/examples/panel-boilerplate-es5)
|
||||
- [nginx-app](https://github.com/grafana/grafana/tree/master/examples/nginx-app)
|
||||
|
||||
## Publish your plugin
|
||||
We are currently working on this.
|
@ -6,5 +6,7 @@ page_keywords: grafana, plugins, documentation
|
||||
|
||||
# Plugins
|
||||
|
||||
TODO
|
||||
From Grafana 3.0 not only datasource plugins are supported but also panel plugins and apps. Having panels as plugins make it easy to create and add any kind of panel, to show your data or improve your favorite dashboards. Apps is something new in Grafana that enables bundling of datasources, panels that belongs together.
|
||||
|
||||
Grafana already have a strong community of contributors and plugin developers. By making it easier to develop and install plugins we hope that the community can grow even stronger and develop new plugins that we would never think about.
|
||||
|
||||
|
@ -4,7 +4,26 @@ page_description: Panel plugins for Grafana
|
||||
page_keywords: grafana, plugins, documentation
|
||||
---
|
||||
|
||||
> Our goal is not to have a very extensive documentation but rather have actual code that people can look at. An example implementation of a datasource can be found in the grafana repo under /examples/panel-boilerplate-es5
|
||||
|
||||
# Panels
|
||||
|
||||
TODO
|
||||
To interact with the rest of grafana the panel plugin need to export a class in the module.js.
|
||||
This class have to inherit from sdk.PanelCtrl or sdk.MetricsPanelCtrl and be exported as PanelCtrl.
|
||||
|
||||
```javascript
|
||||
return {
|
||||
PanelCtrl: BoilerPlatePanelCtrl
|
||||
};
|
||||
```
|
||||
|
||||
This class will be instantiated once for every panel of its kind in a dashboard and treated as an AngularJs controller.
|
||||
|
||||
## MetricsPanelCtrl or PanelCtrl
|
||||
|
||||
MetricsPanelCtrl inherits from PanelCtrl and adds some common features for datasource usage. So if your Panel will be working with a datasource you should inherit from MetricsPanelCtrl. If don't need to access any datasource then you should inherit from PanelCtrl instead.
|
||||
|
||||
## Implementing a MetricsPanelCtrl
|
||||
|
||||
If you choose to inherit from MetricsPanelCtrl you should implement a function called refreshData that will take a datasource as in parameter when its time to get new data. Its recommended that the refreshData function calls the issueQueries in the base class but its not mandatory. An examples of such implementation can be found in our [example panel](https://github.com/grafana/grafana/blob/master/examples/panel-boilerplate-es5/module.js#L27-L38)
|
||||
|
||||
|
10
docs/sources/plugins/plugin.json.md
Normal file
10
docs/sources/plugins/plugin.json.md
Normal file
@ -0,0 +1,10 @@
|
||||
---
|
||||
page_title: Plugin json file
|
||||
page_description: Plugin json for Grafana
|
||||
page_keywords: grafana, plugins, documentation
|
||||
---
|
||||
|
||||
# Plugin.json
|
||||
|
||||
TODO
|
||||
|
4
examples/README.md
Normal file
4
examples/README.md
Normal file
@ -0,0 +1,4 @@
|
||||
## Example plugin implementations
|
||||
|
||||
datasource:[simple-json-datasource](https://github.com/grafana/simple-json-datasource)
|
||||
app: [example-app](https://github.com/grafana/example-app)
|
3
examples/boilerplate-es5-panel/css/styles.css
Normal file
3
examples/boilerplate-es5-panel/css/styles.css
Normal file
@ -0,0 +1,3 @@
|
||||
.panel-boilerplate-values {
|
||||
text-align: center;
|
||||
}
|
53
examples/boilerplate-es5-panel/module.js
Normal file
53
examples/boilerplate-es5-panel/module.js
Normal file
@ -0,0 +1,53 @@
|
||||
define([
|
||||
'app/plugins/sdk',
|
||||
'lodash',
|
||||
'./css/styles.css!'
|
||||
], function(sdk, _) {
|
||||
|
||||
var BoilerPlatePanelCtrl = (function(_super) {
|
||||
var self;
|
||||
|
||||
function BoilerPlatePanelCtrl($scope, $injector) {
|
||||
_super.call(this, $scope, $injector);
|
||||
|
||||
this.results = []
|
||||
|
||||
self = this;
|
||||
}
|
||||
|
||||
// you do not need a templateUrl, you can use a inline template here
|
||||
// BoilerPlatePanelCtrl.template = '<h2>boilerplate</h2>';
|
||||
|
||||
// all panel static assets can be accessed via 'public/plugins/<plugin-id>/<file>
|
||||
BoilerPlatePanelCtrl.templateUrl = 'panel.html';
|
||||
|
||||
BoilerPlatePanelCtrl.prototype = Object.create(_super.prototype);
|
||||
BoilerPlatePanelCtrl.prototype.constructor = BoilerPlatePanelCtrl;
|
||||
|
||||
BoilerPlatePanelCtrl.prototype.refreshData = function(datasource) {
|
||||
this.issueQueries(datasource)
|
||||
.then(function(result) {
|
||||
self.results = [];
|
||||
_.each(result.data, function(target) {
|
||||
var last = _.last(target.datapoints)
|
||||
self.results.push(last[0]);
|
||||
});
|
||||
|
||||
self.render();
|
||||
});
|
||||
}
|
||||
|
||||
BoilerPlatePanelCtrl.prototype.render = function() {
|
||||
this.values = this.results.join(',');
|
||||
}
|
||||
|
||||
return BoilerPlatePanelCtrl;
|
||||
|
||||
})(sdk.MetricsPanelCtrl);
|
||||
|
||||
|
||||
return {
|
||||
PanelCtrl: BoilerPlatePanelCtrl
|
||||
};
|
||||
});
|
||||
|
7
examples/boilerplate-es5-panel/panel.html
Normal file
7
examples/boilerplate-es5-panel/panel.html
Normal file
@ -0,0 +1,7 @@
|
||||
<h2 class="text-center">
|
||||
Basic panel
|
||||
</h2>
|
||||
|
||||
<p class="panel-boilerplate-values">{{ctrl.values}}</p>
|
||||
|
||||
|
@ -1,13 +0,0 @@
|
||||
{
|
||||
"disallowImplicitTypeConversion": ["string"],
|
||||
"disallowKeywords": ["with"],
|
||||
"disallowMultipleLineBreaks": true,
|
||||
"disallowMixedSpacesAndTabs": true,
|
||||
"disallowTrailingWhitespace": true,
|
||||
"requireSpacesInFunctionExpression": {
|
||||
"beforeOpeningCurlyBrace": true
|
||||
},
|
||||
"disallowSpacesInsideArrayBrackets": true,
|
||||
"disallowSpacesInsideParentheses": true,
|
||||
"validateIndentation": 2
|
||||
}
|
@ -1,36 +0,0 @@
|
||||
{
|
||||
"browser": true,
|
||||
"esnext": true,
|
||||
|
||||
"bitwise":false,
|
||||
"curly": true,
|
||||
"eqnull": true,
|
||||
"devel": true,
|
||||
"eqeqeq": true,
|
||||
"forin": false,
|
||||
"immed": true,
|
||||
"supernew": true,
|
||||
"expr": true,
|
||||
"indent": 2,
|
||||
"latedef": true,
|
||||
"newcap": true,
|
||||
"noarg": true,
|
||||
"noempty": true,
|
||||
"undef": true,
|
||||
"boss": true,
|
||||
"trailing": true,
|
||||
"laxbreak": true,
|
||||
"laxcomma": true,
|
||||
"sub": true,
|
||||
"unused": true,
|
||||
"maxdepth": 6,
|
||||
"maxlen": 140,
|
||||
|
||||
"globals": {
|
||||
"System": true,
|
||||
"define": true,
|
||||
"require": true,
|
||||
"Chromath": false,
|
||||
"setImmediate": true
|
||||
}
|
||||
}
|
@ -1,54 +0,0 @@
|
||||
module.exports = function(grunt) {
|
||||
|
||||
require('load-grunt-tasks')(grunt);
|
||||
|
||||
grunt.loadNpmTasks('grunt-execute');
|
||||
grunt.loadNpmTasks('grunt-contrib-clean');
|
||||
|
||||
grunt.initConfig({
|
||||
|
||||
clean: ["dist"],
|
||||
|
||||
copy: {
|
||||
src_to_dist: {
|
||||
cwd: 'src',
|
||||
expand: true,
|
||||
src: ['**/*', '!**/*.js', '!**/*.scss'],
|
||||
dest: 'dist'
|
||||
},
|
||||
pluginDef: {
|
||||
expand: true,
|
||||
src: 'plugin.json',
|
||||
dest: 'dist',
|
||||
}
|
||||
},
|
||||
|
||||
watch: {
|
||||
rebuild_all: {
|
||||
files: ['src/**/*', 'plugin.json'],
|
||||
tasks: ['default'],
|
||||
options: {spawn: false}
|
||||
},
|
||||
},
|
||||
|
||||
babel: {
|
||||
options: {
|
||||
sourceMap: true,
|
||||
presets: ["es2015"],
|
||||
plugins: ['transform-es2015-modules-systemjs', "transform-es2015-for-of"],
|
||||
},
|
||||
dist: {
|
||||
files: [{
|
||||
cwd: 'src',
|
||||
expand: true,
|
||||
src: ['**/*.js'],
|
||||
dest: 'dist',
|
||||
ext:'.js'
|
||||
}]
|
||||
},
|
||||
},
|
||||
|
||||
});
|
||||
|
||||
grunt.registerTask('default', ['clean', 'copy:src_to_dist', 'copy:pluginDef', 'babel']);
|
||||
};
|
@ -1,75 +0,0 @@
|
||||
#Generic backend datasource#
|
||||
|
||||
This is a very minimalistic datasource that forwards http requests in a defined format. The idea is that anybody should be able to build an api and retrieve data from any datasource without built-in support in grafana.
|
||||
|
||||
Its also serves as an living example implementation of a datasource.
|
||||
|
||||
A guide for installing plugins can be found at [placeholder for links].
|
||||
|
||||
Your backend need implement 3 urls
|
||||
* "/" Should return 200 ok. Used for "Test connection" on the datasource config page.
|
||||
* "/search" Used by the find metric options on the query tab in panels
|
||||
* "/query" Should return metrics based on input
|
||||
|
||||
## Metric discovery ##
|
||||
|
||||
### Request ###
|
||||
```
|
||||
{ refId: 'F', target: 'select metric' }
|
||||
```
|
||||
### Expected Response ###
|
||||
|
||||
An array of options based on the target input
|
||||
|
||||
####Example####
|
||||
```
|
||||
["upper_25","upper_50","upper_75","upper_90","upper_95"]
|
||||
```
|
||||
|
||||
## Metric query ##
|
||||
|
||||
### Request ###
|
||||
```
|
||||
{
|
||||
range: { from: '2015-12-22T03:06:13.851Z',to: '2015-12-22T06:48:24.137Z' },
|
||||
interval: '5s',
|
||||
targets:
|
||||
[ { refId: 'B', target: 'upper_75' },
|
||||
{ refId: 'A', target: 'upper_90' } ],
|
||||
format: 'json',
|
||||
maxDataPoints: 2495 //decided by the panel
|
||||
}
|
||||
```
|
||||
### Expected response ###
|
||||
|
||||
An array of
|
||||
```
|
||||
{
|
||||
"target":"target_name",
|
||||
"datapoints":[
|
||||
[intvalue, timestamp in epoch],
|
||||
[intvalue, timestamp in epoch]
|
||||
]
|
||||
}
|
||||
```
|
||||
###Example###
|
||||
```
|
||||
[
|
||||
{
|
||||
"target":"upper_75",
|
||||
"datapoints":[
|
||||
[622,1450754160000],
|
||||
[365,1450754220000]
|
||||
]
|
||||
},
|
||||
{
|
||||
"target":"upper_90",
|
||||
"datapoints":[
|
||||
[861,1450754160000],
|
||||
[767,1450754220000]
|
||||
]
|
||||
}
|
||||
]
|
||||
```
|
||||
## Example backend implementation ##
|
||||
https://gist.github.com/bergquist/bc4aa5baface3cffa109
|
@ -1,37 +0,0 @@
|
||||
{
|
||||
"name": "kentik-app",
|
||||
"private": true,
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/raintank/kentik-app-poc.git"
|
||||
},
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"bugs": {
|
||||
"url": "https://github.com/raintank/kentik-app-poc/issues"
|
||||
},
|
||||
"devDependencies": {
|
||||
"grunt": "~0.4.5",
|
||||
"babel": "~6.5.1",
|
||||
"grunt-babel": "~6.0.0",
|
||||
"grunt-contrib-copy": "~0.8.2",
|
||||
"grunt-contrib-watch": "^0.6.1",
|
||||
"grunt-contrib-uglify": "~0.11.0",
|
||||
"grunt-systemjs-builder": "^0.2.5",
|
||||
"load-grunt-tasks": "~3.2.0",
|
||||
"grunt-execute": "~0.2.2",
|
||||
"grunt-contrib-clean": "~0.6.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"babel-plugin-transform-es2015-modules-systemjs": "^6.5.0",
|
||||
"babel-preset-es2015": "^6.5.0",
|
||||
"lodash": "~4.0.0"
|
||||
},
|
||||
"homepage": "https://github.com/raintank/kentik-app-poc#readme"
|
||||
}
|
@ -1,27 +0,0 @@
|
||||
{
|
||||
"name": "GenericDatasource",
|
||||
"id": "datasource-plugin-genericdatasource",
|
||||
"type": "datasource",
|
||||
|
||||
"module": "plugins/genericdatasource/datasource",
|
||||
|
||||
"staticRoot": ".",
|
||||
|
||||
"metrics": true,
|
||||
"annotations": false,
|
||||
|
||||
"info": {
|
||||
"description": "generic datsource plugin",
|
||||
"author": {
|
||||
"name": "Raintank Inc.",
|
||||
"url": "http://raintank.io"
|
||||
},
|
||||
"version": "0.9.0",
|
||||
"updated": "2016-02-10"
|
||||
},
|
||||
|
||||
"dependencies": {
|
||||
"grafanaVersion": "2.6.x",
|
||||
"plugins": [ ]
|
||||
}
|
||||
}
|
@ -1,3 +0,0 @@
|
||||
.generic-datasource-query-row .query-keyword {
|
||||
width: 75px;
|
||||
}
|
@ -1,65 +0,0 @@
|
||||
export class GenericDatasource {
|
||||
|
||||
constructor(instanceSettings, $q, backendSrv) {
|
||||
this.type = instanceSettings.type;
|
||||
this.url = instanceSettings.url;
|
||||
this.name = instanceSettings.name;
|
||||
this.q = $q;
|
||||
this.backendSrv = backendSrv;
|
||||
}
|
||||
|
||||
// Called once per panel (graph)
|
||||
query(options) {
|
||||
var query = this.buildQueryParameters(options);
|
||||
|
||||
if (query.targets.length <= 0) {
|
||||
return this.q.when([]);
|
||||
}
|
||||
|
||||
return this.backendSrv.datasourceRequest({
|
||||
url: this.url + '/query',
|
||||
data: query,
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' }
|
||||
});
|
||||
}
|
||||
|
||||
// Required
|
||||
// Used for testing datasource in datasource configuration pange
|
||||
testDatasource() {
|
||||
return this.backendSrv.datasourceRequest({
|
||||
url: this.url + '/',
|
||||
method: 'GET'
|
||||
}).then(response => {
|
||||
if (response.status === 200) {
|
||||
return { status: "success", message: "Data source is working", title: "Success" };
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Optional
|
||||
// Required for templating
|
||||
metricFindQuery(options) {
|
||||
return this.backendSrv.datasourceRequest({
|
||||
url: this.url + '/search',
|
||||
data: options,
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' }
|
||||
}).then(this.mapToTextValue);
|
||||
}
|
||||
|
||||
mapToTextValue(result) {
|
||||
return _.map(result.data, (d, i) => {
|
||||
return { text: d, value: i};
|
||||
});
|
||||
}
|
||||
|
||||
buildQueryParameters(options) {
|
||||
//remove placeholder targets
|
||||
options.targets = _.filter(options.targets, target => {
|
||||
return target.target !== 'select metric';
|
||||
});
|
||||
|
||||
return options;
|
||||
}
|
||||
}
|
@ -1,15 +0,0 @@
|
||||
import {GenericDatasource} from './datasource';
|
||||
import {GenericDatasourceQueryCtrl} from './query_ctrl';
|
||||
|
||||
class GenericConfigCtrl {}
|
||||
GenericConfigCtrl.templateUrl = 'partials/config.html';
|
||||
|
||||
class GenericQueryOptionsCtrl {}
|
||||
GenericQueryOptionsCtrl.templateUrl = 'partials/query.options.html';
|
||||
|
||||
export {
|
||||
GenericDatasource as Datasource,
|
||||
GenericDatasourceQueryCtrl as QueryCtrl,
|
||||
GenericConfigCtrl as ConfigCtrl,
|
||||
GenericQueryOptionsCtrl as QueryOptionsCtrl
|
||||
};
|
@ -1,2 +0,0 @@
|
||||
<datasource-http-settings current="ctrl.current">
|
||||
</datasource-http-settings>
|
@ -1,8 +0,0 @@
|
||||
<query-editor-row ctrl="ctrl" class="generic-datasource-query-row">
|
||||
<ul class="tight-form-list">
|
||||
<li class="tight-form-item query-keyword">Query</li>
|
||||
<li>
|
||||
<metric-segment-model property="ctrl.target.target" get-options="ctrl.getOptions()" on-change="ctrl.onChangeInternal()" css-class="tight-form-item-xxlarge"></metric-segment-model>
|
||||
</li>
|
||||
</ul>
|
||||
</query-editor-row>
|
@ -1,4 +0,0 @@
|
||||
<section class="grafana-metric-options" >
|
||||
<div class="gf-form">
|
||||
</div>
|
||||
</section>
|
@ -1,26 +0,0 @@
|
||||
import {QueryCtrl} from 'app/plugins/sdk';
|
||||
import './css/query-editor.css!'
|
||||
|
||||
export class GenericDatasourceQueryCtrl extends QueryCtrl {
|
||||
|
||||
constructor($scope, $injector, uiSegmentSrv) {
|
||||
super($scope, $injector);
|
||||
|
||||
this.scope = $scope;
|
||||
this.uiSegmentSrv = uiSegmentSrv;
|
||||
this.target.target = this.target.target || 'select metric';
|
||||
}
|
||||
|
||||
getOptions() {
|
||||
return this.datasource.metricFindQuery(this.target)
|
||||
.then(this.uiSegmentSrv.transformToSegments(false));
|
||||
// Options have to be transformed by uiSegmentSrv to be usable by metric-segment-model directive
|
||||
}
|
||||
|
||||
onChangeInternal() {
|
||||
this.panelCtrl.refresh(); // Asks the panel to refresh data.
|
||||
}
|
||||
}
|
||||
|
||||
GenericDatasourceQueryCtrl.templateUrl = 'partials/query.editor.html';
|
||||
|
7
examples/nginx-app/.gitignore
vendored
7
examples/nginx-app/.gitignore
vendored
@ -1,7 +0,0 @@
|
||||
.DS_Store
|
||||
|
||||
node_modules
|
||||
tmp/*
|
||||
npm-debug.log
|
||||
dist/*
|
||||
|
@ -1,13 +0,0 @@
|
||||
{
|
||||
"disallowImplicitTypeConversion": ["string"],
|
||||
"disallowKeywords": ["with"],
|
||||
"disallowMultipleLineBreaks": true,
|
||||
"disallowMixedSpacesAndTabs": true,
|
||||
"disallowTrailingWhitespace": true,
|
||||
"requireSpacesInFunctionExpression": {
|
||||
"beforeOpeningCurlyBrace": true
|
||||
},
|
||||
"disallowSpacesInsideArrayBrackets": true,
|
||||
"disallowSpacesInsideParentheses": true,
|
||||
"validateIndentation": 2
|
||||
}
|
@ -1,36 +0,0 @@
|
||||
{
|
||||
"browser": true,
|
||||
"esnext": true,
|
||||
|
||||
"bitwise":false,
|
||||
"curly": true,
|
||||
"eqnull": true,
|
||||
"devel": true,
|
||||
"eqeqeq": true,
|
||||
"forin": false,
|
||||
"immed": true,
|
||||
"supernew": true,
|
||||
"expr": true,
|
||||
"indent": 2,
|
||||
"latedef": true,
|
||||
"newcap": true,
|
||||
"noarg": true,
|
||||
"noempty": true,
|
||||
"undef": true,
|
||||
"boss": true,
|
||||
"trailing": true,
|
||||
"laxbreak": true,
|
||||
"laxcomma": true,
|
||||
"sub": true,
|
||||
"unused": true,
|
||||
"maxdepth": 6,
|
||||
"maxlen": 140,
|
||||
|
||||
"globals": {
|
||||
"System": true,
|
||||
"define": true,
|
||||
"require": true,
|
||||
"Chromath": false,
|
||||
"setImmediate": true
|
||||
}
|
||||
}
|
@ -1,54 +0,0 @@
|
||||
module.exports = function(grunt) {
|
||||
|
||||
require('load-grunt-tasks')(grunt);
|
||||
|
||||
grunt.loadNpmTasks('grunt-execute');
|
||||
grunt.loadNpmTasks('grunt-contrib-clean');
|
||||
|
||||
grunt.initConfig({
|
||||
|
||||
clean: ["dist"],
|
||||
|
||||
copy: {
|
||||
src_to_dist: {
|
||||
cwd: 'src',
|
||||
expand: true,
|
||||
src: ['**/*', '!**/*.js', '!**/*.scss'],
|
||||
dest: 'dist'
|
||||
},
|
||||
pluginDef: {
|
||||
expand: true,
|
||||
src: 'plugin.json',
|
||||
dest: 'dist',
|
||||
}
|
||||
},
|
||||
|
||||
watch: {
|
||||
rebuild_all: {
|
||||
files: ['src/**/*', 'plugin.json'],
|
||||
tasks: ['default'],
|
||||
options: {spawn: false}
|
||||
},
|
||||
},
|
||||
|
||||
babel: {
|
||||
options: {
|
||||
sourceMap: true,
|
||||
presets: ["es2015"],
|
||||
plugins: ['transform-es2015-modules-systemjs', "transform-es2015-for-of"],
|
||||
},
|
||||
dist: {
|
||||
files: [{
|
||||
cwd: 'src',
|
||||
expand: true,
|
||||
src: ['**/*.js'],
|
||||
dest: 'dist',
|
||||
ext:'.js'
|
||||
}]
|
||||
},
|
||||
},
|
||||
|
||||
});
|
||||
|
||||
grunt.registerTask('default', ['clean', 'copy:src_to_dist', 'copy:pluginDef', 'babel']);
|
||||
};
|
@ -1,37 +0,0 @@
|
||||
{
|
||||
"name": "kentik-app",
|
||||
"private": true,
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/raintank/kentik-app-poc.git"
|
||||
},
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"bugs": {
|
||||
"url": "https://github.com/raintank/kentik-app-poc/issues"
|
||||
},
|
||||
"devDependencies": {
|
||||
"grunt": "~0.4.5",
|
||||
"babel": "~6.5.1",
|
||||
"grunt-babel": "~6.0.0",
|
||||
"grunt-contrib-copy": "~0.8.2",
|
||||
"grunt-contrib-watch": "^0.6.1",
|
||||
"grunt-contrib-uglify": "~0.11.0",
|
||||
"grunt-systemjs-builder": "^0.2.5",
|
||||
"load-grunt-tasks": "~3.2.0",
|
||||
"grunt-execute": "~0.2.2",
|
||||
"grunt-contrib-clean": "~0.6.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"babel-plugin-transform-es2015-modules-systemjs": "^6.5.0",
|
||||
"babel-preset-es2015": "^6.5.0",
|
||||
"lodash": "~4.0.0"
|
||||
},
|
||||
"homepage": "https://github.com/raintank/kentik-app-poc#readme"
|
||||
}
|
@ -1,3 +0,0 @@
|
||||
<h3>
|
||||
Nginx config!
|
||||
</h3>
|
@ -1,6 +0,0 @@
|
||||
|
||||
export class NginxAppConfigCtrl {
|
||||
}
|
||||
NginxAppConfigCtrl.templateUrl = 'components/config.html';
|
||||
|
||||
|
@ -1,3 +0,0 @@
|
||||
<h3>
|
||||
Logs page!
|
||||
</h3>
|
@ -1,6 +0,0 @@
|
||||
|
||||
export class LogsPageCtrl {
|
||||
}
|
||||
LogsPageCtrl.templateUrl = 'components/logs.html';
|
||||
|
||||
|
@ -1,3 +0,0 @@
|
||||
<h3>
|
||||
Stream page!
|
||||
</h3>
|
@ -1,6 +0,0 @@
|
||||
|
||||
export class StreamPageCtrl {
|
||||
}
|
||||
StreamPageCtrl.templateUrl = 'components/stream.html';
|
||||
|
||||
|
@ -1,17 +0,0 @@
|
||||
require([
|
||||
], function () {
|
||||
|
||||
function Dashboard() {
|
||||
|
||||
this.getInputs = function() {
|
||||
|
||||
};
|
||||
|
||||
this.buildDashboard = function() {
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
return Dashboard;
|
||||
});
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 14 KiB |
Binary file not shown.
Before Width: | Height: | Size: 6.3 KiB |
@ -1,9 +0,0 @@
|
||||
import {LogsPageCtrl} from './components/logs';
|
||||
import {StreamPageCtrl} from './components/stream';
|
||||
import {NginxAppConfigCtrl} from './components/config';
|
||||
|
||||
export {
|
||||
NginxAppConfigCtrl as ConfigCtrl,
|
||||
StreamPageCtrl,
|
||||
LogsPageCtrl
|
||||
};
|
@ -1,15 +0,0 @@
|
||||
import {PanelCtrl} from 'app/plugins/sdk';
|
||||
|
||||
class NginxPanelCtrl extends PanelCtrl {
|
||||
|
||||
constructor($scope, $injector) {
|
||||
super($scope, $injector);
|
||||
}
|
||||
|
||||
}
|
||||
NginxPanelCtrl.template = '<h2>nginx!</h2>';
|
||||
|
||||
export {
|
||||
NginxPanelCtrl as PanelCtrl
|
||||
};
|
||||
|
@ -1,5 +0,0 @@
|
||||
{
|
||||
"type": "panel",
|
||||
"name": "Nginx Panel",
|
||||
"id": "nginx-panel"
|
||||
}
|
@ -1,28 +0,0 @@
|
||||
define([
|
||||
'app/plugins/sdk'
|
||||
], function(sdk) {
|
||||
|
||||
var BoilerPlatePanel = (function(_super) {
|
||||
|
||||
function BoilerPlatePanel($scope, $injector) {
|
||||
_super.call(this, $scope, $injector);
|
||||
}
|
||||
|
||||
// you do not need a templateUrl, you can use a inline template here
|
||||
// BoilerPlatePanel.template = '<h2>boilerplate</h2>';
|
||||
|
||||
// all panel static assets can be accessed via 'public/plugins/<plugin-id>/<file>
|
||||
BoilerPlatePanel.templateUrl = 'panel.html';
|
||||
|
||||
BoilerPlatePanel.prototype = Object.create(_super.prototype);
|
||||
BoilerPlatePanel.prototype.constructor = BoilerPlatePanel;
|
||||
|
||||
return BoilerPlatePanel;
|
||||
|
||||
})(sdk.PanelCtrl);
|
||||
|
||||
|
||||
return {
|
||||
PanelCtrl: BoilerPlatePanel
|
||||
};
|
||||
});
|
@ -1,4 +0,0 @@
|
||||
<h2 class="text-center">
|
||||
Boilerplate panel
|
||||
</h2>
|
||||
|
@ -11,6 +11,7 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"angular2": "2.0.0-beta.0",
|
||||
"autoprefixer": "^6.3.3",
|
||||
"es6-promise": "^3.0.2",
|
||||
"es6-shim": "^0.33.3",
|
||||
"expect.js": "~0.2.0",
|
||||
@ -33,6 +34,7 @@
|
||||
"grunt-karma": "~0.12.1",
|
||||
"grunt-ng-annotate": "^1.0.1",
|
||||
"grunt-notify": "^0.4.3",
|
||||
"grunt-postcss": "^0.8.0",
|
||||
"grunt-sass": "^1.1.0",
|
||||
"grunt-string-replace": "~1.2.1",
|
||||
"grunt-systemjs-builder": "^0.2.5",
|
||||
@ -72,6 +74,7 @@
|
||||
"grunt-sync": "^0.4.1",
|
||||
"karma-sinon": "^1.0.3",
|
||||
"lodash": "^2.4.1",
|
||||
"remarkable": "^1.6.2",
|
||||
"sinon": "1.16.1",
|
||||
"systemjs-builder": "^0.15.7",
|
||||
"tether": "^1.2.0",
|
||||
|
@ -128,10 +128,6 @@ func Register(r *macaron.Macaron) {
|
||||
r.Post("/invites", quota("user"), bind(dtos.AddInviteForm{}), wrap(AddOrgInvite))
|
||||
r.Patch("/invites/:code/revoke", wrap(RevokeInvite))
|
||||
|
||||
// apps
|
||||
r.Get("/plugins", wrap(GetPluginList))
|
||||
r.Get("/plugins/:pluginId/settings", wrap(GetPluginSettingById))
|
||||
r.Post("/plugins/:pluginId/settings", bind(m.UpdatePluginSettingCmd{}), wrap(UpdatePluginSetting))
|
||||
}, reqOrgAdmin)
|
||||
|
||||
// create new org
|
||||
@ -173,7 +169,18 @@ func Register(r *macaron.Macaron) {
|
||||
r.Put("/:id", bind(m.UpdateDataSourceCommand{}), UpdateDataSource)
|
||||
r.Delete("/:id", DeleteDataSource)
|
||||
r.Get("/:id", wrap(GetDataSourceById))
|
||||
r.Get("/plugins", GetDataSourcePlugins)
|
||||
r.Get("/name/:name", wrap(GetDataSourceByName))
|
||||
}, reqOrgAdmin)
|
||||
|
||||
r.Get("/datasources/id/:name", wrap(GetDataSourceIdByName), reqSignedIn)
|
||||
|
||||
r.Group("/plugins", func() {
|
||||
r.Get("/", wrap(GetPluginList))
|
||||
|
||||
r.Get("/:pluginId/readme", wrap(GetPluginReadme))
|
||||
r.Get("/:pluginId/dashboards/", wrap(GetPluginDashboards))
|
||||
r.Get("/:pluginId/settings", wrap(GetPluginSettingById))
|
||||
r.Post("/:pluginId/settings", bind(m.UpdatePluginSettingCmd{}), wrap(UpdatePluginSetting))
|
||||
}, reqOrgAdmin)
|
||||
|
||||
r.Get("/frontend/settings/", GetFrontendSettings)
|
||||
@ -187,6 +194,7 @@ func Register(r *macaron.Macaron) {
|
||||
r.Get("/file/:file", GetDashboardFromJsonFile)
|
||||
r.Get("/home", GetHomeDashboard)
|
||||
r.Get("/tags", GetDashboardTags)
|
||||
r.Post("/import", bind(dtos.ImportDashboardCommand{}), wrap(ImportDashboard))
|
||||
})
|
||||
|
||||
// Dashboard snapshots
|
||||
|
@ -55,8 +55,10 @@ func init() {
|
||||
"S3BytesWritten", "S3BytesRead", "HDFSUtilization", "HDFSBytesRead", "HDFSBytesWritten", "MissingBlocks", "CorruptBlocks", "TotalLoad", "MemoryTotalMB", "MemoryReservedMB", "MemoryAvailableMB", "MemoryAllocatedMB", "PendingDeletionBlocks", "UnderReplicatedBlocks", "DfsPendingReplicationBlocks", "CapacityRemainingGB",
|
||||
"HbaseBackupFailed", "MostRecentBackupDuration", "TimeSinceLastSuccessfulBackup"},
|
||||
"AWS/ES": {"ClusterStatus.green", "ClusterStatus.yellow", "ClusterStatus.red", "Nodes", "SearchableDocuments", "DeletedDocuments", "CPUUtilization", "FreeStorageSpace", "JVMMemoryPressure", "AutomatedSnapshotFailure", "MasterCPUUtilization", "MasterFreeStorageSpace", "MasterJVMMemoryPressure", "ReadLatency", "WriteLatency", "ReadThroughput", "WriteThroughput", "DiskQueueLength", "ReadIOPS", "WriteIOPS"},
|
||||
"AWS/Events": {"Invocations", "FailedInvocations", "TriggeredRules", "MatchedEvents", "ThrottledRules"},
|
||||
"AWS/Kinesis": {"PutRecord.Bytes", "PutRecord.Latency", "PutRecord.Success", "PutRecords.Bytes", "PutRecords.Latency", "PutRecords.Records", "PutRecords.Success", "IncomingBytes", "IncomingRecords", "GetRecords.Bytes", "GetRecords.IteratorAgeMilliseconds", "GetRecords.Latency", "GetRecords.Success"},
|
||||
"AWS/Lambda": {"Invocations", "Errors", "Duration", "Throttles"},
|
||||
"AWS/Logs": {"IncomingBytes", "IncomingLogEvents", "ForwardedBytes", "ForwardedLogEvents", "DeliveryErrors", "DeliveryThrottling"},
|
||||
"AWS/ML": {"PredictCount", "PredictFailureCount"},
|
||||
"AWS/OpsWorks": {"cpu_idle", "cpu_nice", "cpu_system", "cpu_user", "cpu_waitio", "load_1", "load_5", "load_15", "memory_buffers", "memory_cached", "memory_free", "memory_swap", "memory_total", "memory_used", "procs"},
|
||||
"AWS/Redshift": {"CPUUtilization", "DatabaseConnections", "HealthStatus", "MaintenanceMode", "NetworkReceiveThroughput", "NetworkTransmitThroughput", "PercentageDiskSpaceUsed", "ReadIOPS", "ReadLatency", "ReadThroughput", "WriteIOPS", "WriteLatency", "WriteThroughput"},
|
||||
@ -85,8 +87,10 @@ func init() {
|
||||
"AWS/ELB": {"LoadBalancerName", "AvailabilityZone"},
|
||||
"AWS/ElasticMapReduce": {"ClusterId", "JobFlowId", "JobId"},
|
||||
"AWS/ES": {},
|
||||
"AWS/Events": {"RuleName"},
|
||||
"AWS/Kinesis": {"StreamName"},
|
||||
"AWS/Lambda": {"FunctionName"},
|
||||
"AWS/Logs": {"LogGroupName", "DestinationType", "FilterName"},
|
||||
"AWS/ML": {"MLModelId", "RequestMode"},
|
||||
"AWS/OpsWorks": {"StackId", "LayerId", "InstanceId"},
|
||||
"AWS/Redshift": {"NodeID", "ClusterIdentifier"},
|
||||
@ -126,11 +130,14 @@ func handleGetNamespaces(req *cwRequest, c *middleware.Context) {
|
||||
for key := range metricsMap {
|
||||
keys = append(keys, key)
|
||||
}
|
||||
if customMetricsNamespaces, ok := req.DataSource.JsonData["customMetricsNamespaces"].(string); ok {
|
||||
for _, key := range strings.Split(customMetricsNamespaces, ",") {
|
||||
|
||||
customNamespaces := req.DataSource.JsonData.Get("customMetricsNamespaces").MustString()
|
||||
if customNamespaces != "" {
|
||||
for _, key := range strings.Split(customNamespaces, ",") {
|
||||
keys = append(keys, key)
|
||||
}
|
||||
}
|
||||
|
||||
sort.Sort(sort.StringSlice(keys))
|
||||
|
||||
result := []interface{}{}
|
||||
|
@ -53,7 +53,6 @@ func CreateDashboardSnapshot(c *middleware.Context, cmd m.CreateDashboardSnapsho
|
||||
}
|
||||
|
||||
func GetDashboardSnapshot(c *middleware.Context) {
|
||||
|
||||
key := c.Params(":key")
|
||||
query := &m.GetDashboardSnapshotQuery{Key: key}
|
||||
|
||||
@ -136,5 +135,4 @@ func SearchDashboardSnapshots(c *middleware.Context) Response {
|
||||
}
|
||||
|
||||
return Json(200, dtos)
|
||||
//return Json(200, searchQuery.Result)
|
||||
}
|
||||
|
@ -6,7 +6,6 @@ import (
|
||||
//"github.com/grafana/grafana/pkg/log"
|
||||
"github.com/grafana/grafana/pkg/middleware"
|
||||
m "github.com/grafana/grafana/pkg/models"
|
||||
"github.com/grafana/grafana/pkg/plugins"
|
||||
"github.com/grafana/grafana/pkg/util"
|
||||
)
|
||||
|
||||
@ -52,24 +51,9 @@ func GetDataSourceById(c *middleware.Context) Response {
|
||||
}
|
||||
|
||||
ds := query.Result
|
||||
dtos := convertModelToDtos(ds)
|
||||
|
||||
return Json(200, &dtos.DataSource{
|
||||
Id: ds.Id,
|
||||
OrgId: ds.OrgId,
|
||||
Name: ds.Name,
|
||||
Url: ds.Url,
|
||||
Type: ds.Type,
|
||||
Access: ds.Access,
|
||||
Password: ds.Password,
|
||||
Database: ds.Database,
|
||||
User: ds.User,
|
||||
BasicAuth: ds.BasicAuth,
|
||||
BasicAuthUser: ds.BasicAuthUser,
|
||||
BasicAuthPassword: ds.BasicAuthPassword,
|
||||
WithCredentials: ds.WithCredentials,
|
||||
IsDefault: ds.IsDefault,
|
||||
JsonData: ds.JsonData,
|
||||
})
|
||||
return Json(200, &dtos)
|
||||
}
|
||||
|
||||
func DeleteDataSource(c *middleware.Context) {
|
||||
@ -115,20 +99,58 @@ func UpdateDataSource(c *middleware.Context, cmd m.UpdateDataSourceCommand) {
|
||||
c.JsonOK("Datasource updated")
|
||||
}
|
||||
|
||||
func GetDataSourcePlugins(c *middleware.Context) {
|
||||
dsList := make(map[string]*plugins.DataSourcePlugin)
|
||||
// Get /api/datasources/name/:name
|
||||
func GetDataSourceByName(c *middleware.Context) Response {
|
||||
query := m.GetDataSourceByNameQuery{Name: c.Params(":name"), OrgId: c.OrgId}
|
||||
|
||||
if enabledPlugins, err := plugins.GetEnabledPlugins(c.OrgId); err != nil {
|
||||
c.JsonApiErr(500, "Failed to get org apps", err)
|
||||
return
|
||||
} else {
|
||||
|
||||
for key, value := range enabledPlugins.DataSources {
|
||||
if !value.BuiltIn {
|
||||
dsList[key] = value
|
||||
}
|
||||
if err := bus.Dispatch(&query); err != nil {
|
||||
if err == m.ErrDataSourceNotFound {
|
||||
return ApiError(404, "Data source not found", nil)
|
||||
}
|
||||
return ApiError(500, "Failed to query datasources", err)
|
||||
}
|
||||
|
||||
c.JSON(200, dsList)
|
||||
ds := query.Result
|
||||
dtos := convertModelToDtos(ds)
|
||||
|
||||
return Json(200, &dtos)
|
||||
}
|
||||
|
||||
// Get /api/datasources/id/:name
|
||||
func GetDataSourceIdByName(c *middleware.Context) Response {
|
||||
query := m.GetDataSourceByNameQuery{Name: c.Params(":name"), OrgId: c.OrgId}
|
||||
|
||||
if err := bus.Dispatch(&query); err != nil {
|
||||
if err == m.ErrDataSourceNotFound {
|
||||
return ApiError(404, "Data source not found", nil)
|
||||
}
|
||||
return ApiError(500, "Failed to query datasources", err)
|
||||
}
|
||||
|
||||
ds := query.Result
|
||||
dtos := dtos.AnyId{
|
||||
Id: ds.Id,
|
||||
}
|
||||
|
||||
return Json(200, &dtos)
|
||||
}
|
||||
|
||||
func convertModelToDtos(ds m.DataSource) dtos.DataSource {
|
||||
return dtos.DataSource{
|
||||
Id: ds.Id,
|
||||
OrgId: ds.OrgId,
|
||||
Name: ds.Name,
|
||||
Url: ds.Url,
|
||||
Type: ds.Type,
|
||||
Access: ds.Access,
|
||||
Password: ds.Password,
|
||||
Database: ds.Database,
|
||||
User: ds.User,
|
||||
BasicAuth: ds.BasicAuth,
|
||||
BasicAuthUser: ds.BasicAuthUser,
|
||||
BasicAuthPassword: ds.BasicAuthPassword,
|
||||
WithCredentials: ds.WithCredentials,
|
||||
IsDefault: ds.IsDefault,
|
||||
JsonData: ds.JsonData,
|
||||
}
|
||||
}
|
||||
|
@ -6,10 +6,15 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/grafana/grafana/pkg/components/simplejson"
|
||||
m "github.com/grafana/grafana/pkg/models"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
)
|
||||
|
||||
type AnyId struct {
|
||||
Id int64 `json:"id"`
|
||||
}
|
||||
|
||||
type LoginCommand struct {
|
||||
User string `json:"user" binding:"Required"`
|
||||
Password string `json:"password" binding:"Required"`
|
||||
@ -48,26 +53,26 @@ type DashboardMeta struct {
|
||||
}
|
||||
|
||||
type DashboardFullWithMeta struct {
|
||||
Meta DashboardMeta `json:"meta"`
|
||||
Dashboard map[string]interface{} `json:"dashboard"`
|
||||
Meta DashboardMeta `json:"meta"`
|
||||
Dashboard *simplejson.Json `json:"dashboard"`
|
||||
}
|
||||
|
||||
type DataSource struct {
|
||||
Id int64 `json:"id"`
|
||||
OrgId int64 `json:"orgId"`
|
||||
Name string `json:"name"`
|
||||
Type string `json:"type"`
|
||||
Access m.DsAccess `json:"access"`
|
||||
Url string `json:"url"`
|
||||
Password string `json:"password"`
|
||||
User string `json:"user"`
|
||||
Database string `json:"database"`
|
||||
BasicAuth bool `json:"basicAuth"`
|
||||
BasicAuthUser string `json:"basicAuthUser"`
|
||||
BasicAuthPassword string `json:"basicAuthPassword"`
|
||||
WithCredentials bool `json:"withCredentials"`
|
||||
IsDefault bool `json:"isDefault"`
|
||||
JsonData map[string]interface{} `json:"jsonData,omitempty"`
|
||||
Id int64 `json:"id"`
|
||||
OrgId int64 `json:"orgId"`
|
||||
Name string `json:"name"`
|
||||
Type string `json:"type"`
|
||||
Access m.DsAccess `json:"access"`
|
||||
Url string `json:"url"`
|
||||
Password string `json:"password"`
|
||||
User string `json:"user"`
|
||||
Database string `json:"database"`
|
||||
BasicAuth bool `json:"basicAuth"`
|
||||
BasicAuthUser string `json:"basicAuthUser"`
|
||||
BasicAuthPassword string `json:"basicAuthPassword"`
|
||||
WithCredentials bool `json:"withCredentials"`
|
||||
IsDefault bool `json:"isDefault"`
|
||||
JsonData *simplejson.Json `json:"jsonData,omitempty"`
|
||||
}
|
||||
|
||||
type MetricQueryResultDto struct {
|
||||
|
@ -3,24 +3,32 @@ package dtos
|
||||
import "github.com/grafana/grafana/pkg/plugins"
|
||||
|
||||
type PluginSetting struct {
|
||||
Name string `json:"name"`
|
||||
Type string `json:"type"`
|
||||
PluginId string `json:"pluginId"`
|
||||
Enabled bool `json:"enabled"`
|
||||
Pinned bool `json:"pinned"`
|
||||
Module string `json:"module"`
|
||||
BaseUrl string `json:"baseUrl"`
|
||||
Info *plugins.PluginInfo `json:"info"`
|
||||
Pages []*plugins.AppPluginPage `json:"pages"`
|
||||
Includes []*plugins.AppIncludeInfo `json:"includes"`
|
||||
JsonData map[string]interface{} `json:"jsonData"`
|
||||
Name string `json:"name"`
|
||||
Type string `json:"type"`
|
||||
Id string `json:"id"`
|
||||
Enabled bool `json:"enabled"`
|
||||
Pinned bool `json:"pinned"`
|
||||
Module string `json:"module"`
|
||||
BaseUrl string `json:"baseUrl"`
|
||||
Info *plugins.PluginInfo `json:"info"`
|
||||
Pages []*plugins.AppPluginPage `json:"pages"`
|
||||
Includes []*plugins.PluginInclude `json:"includes"`
|
||||
Dependencies *plugins.PluginDependencies `json:"dependencies"`
|
||||
JsonData map[string]interface{} `json:"jsonData"`
|
||||
}
|
||||
|
||||
type PluginListItem struct {
|
||||
Name string `json:"name"`
|
||||
Type string `json:"type"`
|
||||
PluginId string `json:"pluginId"`
|
||||
Enabled bool `json:"enabled"`
|
||||
Pinned bool `json:"pinned"`
|
||||
Info *plugins.PluginInfo `json:"info"`
|
||||
Name string `json:"name"`
|
||||
Type string `json:"type"`
|
||||
Id string `json:"id"`
|
||||
Enabled bool `json:"enabled"`
|
||||
Pinned bool `json:"pinned"`
|
||||
Info *plugins.PluginInfo `json:"info"`
|
||||
}
|
||||
|
||||
type ImportDashboardCommand struct {
|
||||
PluginId string `json:"pluginId"`
|
||||
Path string `json:"path"`
|
||||
Reinstall bool `json:"reinstall"`
|
||||
Inputs []plugins.ImportDashboardInput `json:"inputs"`
|
||||
}
|
||||
|
@ -59,7 +59,7 @@ func getFrontendSettingsMap(c *middleware.Context) (map[string]interface{}, erro
|
||||
defaultDatasource = ds.Name
|
||||
}
|
||||
|
||||
if len(ds.JsonData) > 0 {
|
||||
if len(ds.JsonData.MustMap()) > 0 {
|
||||
dsMap["jsonData"] = ds.JsonData
|
||||
}
|
||||
|
||||
|
@ -48,18 +48,23 @@ func setIndexViewData(c *middleware.Context) (*dtos.IndexViewData, error) {
|
||||
data.User.LightTheme = true
|
||||
}
|
||||
|
||||
dashboardChildNavs := []*dtos.NavLink{
|
||||
{Text: "Home", Url: setting.AppSubUrl + "/"},
|
||||
{Text: "Playlists", Url: setting.AppSubUrl + "/playlists"},
|
||||
{Text: "Snapshots", Url: setting.AppSubUrl + "/dashboard/snapshots"},
|
||||
}
|
||||
|
||||
if c.OrgRole == m.ROLE_ADMIN || c.OrgRole == m.ROLE_EDITOR {
|
||||
dashboardChildNavs = append(dashboardChildNavs, &dtos.NavLink{Divider: true})
|
||||
dashboardChildNavs = append(dashboardChildNavs, &dtos.NavLink{Text: "New", Url: setting.AppSubUrl + "/dashboard/new"})
|
||||
dashboardChildNavs = append(dashboardChildNavs, &dtos.NavLink{Text: "Import", Url: setting.AppSubUrl + "/import/dashboard"})
|
||||
}
|
||||
|
||||
data.MainNavLinks = append(data.MainNavLinks, &dtos.NavLink{
|
||||
Text: "Dashboards",
|
||||
Icon: "icon-gf icon-gf-dashboard",
|
||||
Url: setting.AppSubUrl + "/",
|
||||
Children: []*dtos.NavLink{
|
||||
{Text: "Home", Url: setting.AppSubUrl + "/"},
|
||||
{Text: "Playlists", Url: setting.AppSubUrl + "/playlists"},
|
||||
{Text: "Snapshots", Url: setting.AppSubUrl + "/dashboard/snapshots"},
|
||||
{Divider: true},
|
||||
{Text: "New", Url: setting.AppSubUrl + "/dashboard/new"},
|
||||
{Text: "Import", Url: setting.AppSubUrl + "/import/dashboard"},
|
||||
},
|
||||
Text: "Dashboards",
|
||||
Icon: "icon-gf icon-gf-dashboard",
|
||||
Url: setting.AppSubUrl + "/",
|
||||
Children: dashboardChildNavs,
|
||||
})
|
||||
|
||||
if c.OrgRole == m.ROLE_ADMIN {
|
||||
|
@ -126,8 +126,10 @@ func loginUserWithUser(user *m.User, c *middleware.Context) {
|
||||
}
|
||||
|
||||
days := 86400 * setting.LogInRememberDays
|
||||
c.SetCookie(setting.CookieUserName, user.Login, days, setting.AppSubUrl+"/")
|
||||
c.SetSuperSecureCookie(util.EncodeMd5(user.Rands+user.Password), setting.CookieRememberName, user.Login, days, setting.AppSubUrl+"/")
|
||||
if days > 0 {
|
||||
c.SetCookie(setting.CookieUserName, user.Login, days, setting.AppSubUrl+"/")
|
||||
c.SetSuperSecureCookie(util.EncodeMd5(user.Rands+user.Password), setting.CookieRememberName, user.Login, days, setting.AppSubUrl+"/")
|
||||
}
|
||||
|
||||
c.Session.Set(middleware.SESS_KEY_USERID, user.Id)
|
||||
}
|
||||
|
@ -9,6 +9,10 @@ import (
|
||||
)
|
||||
|
||||
func GetPluginList(c *middleware.Context) Response {
|
||||
typeFilter := c.Query("type")
|
||||
enabledFilter := c.Query("enabled")
|
||||
embeddedFilter := c.Query("embedded")
|
||||
|
||||
pluginSettingsMap, err := plugins.GetPluginSettings(c.OrgId)
|
||||
|
||||
if err != nil {
|
||||
@ -17,11 +21,21 @@ func GetPluginList(c *middleware.Context) Response {
|
||||
|
||||
result := make([]*dtos.PluginListItem, 0)
|
||||
for _, pluginDef := range plugins.Plugins {
|
||||
// filter out app sub plugins
|
||||
if embeddedFilter == "0" && pluginDef.IncludedInAppId != "" {
|
||||
continue
|
||||
}
|
||||
|
||||
// filter on type
|
||||
if typeFilter != "" && typeFilter != pluginDef.Type {
|
||||
continue
|
||||
}
|
||||
|
||||
listItem := &dtos.PluginListItem{
|
||||
PluginId: pluginDef.Id,
|
||||
Name: pluginDef.Name,
|
||||
Type: pluginDef.Type,
|
||||
Info: &pluginDef.Info,
|
||||
Id: pluginDef.Id,
|
||||
Name: pluginDef.Name,
|
||||
Type: pluginDef.Type,
|
||||
Info: &pluginDef.Info,
|
||||
}
|
||||
|
||||
if pluginSetting, exists := pluginSettingsMap[pluginDef.Id]; exists {
|
||||
@ -29,6 +43,11 @@ func GetPluginList(c *middleware.Context) Response {
|
||||
listItem.Pinned = pluginSetting.Pinned
|
||||
}
|
||||
|
||||
// filter out disabled
|
||||
if enabledFilter == "1" && !listItem.Enabled {
|
||||
continue
|
||||
}
|
||||
|
||||
result = append(result, listItem)
|
||||
}
|
||||
|
||||
@ -41,18 +60,20 @@ func GetPluginSettingById(c *middleware.Context) Response {
|
||||
if def, exists := plugins.Plugins[pluginId]; !exists {
|
||||
return ApiError(404, "Plugin not found, no installed plugin with that id", nil)
|
||||
} else {
|
||||
|
||||
dto := &dtos.PluginSetting{
|
||||
Type: def.Type,
|
||||
PluginId: def.Id,
|
||||
Name: def.Name,
|
||||
Info: &def.Info,
|
||||
Type: def.Type,
|
||||
Id: def.Id,
|
||||
Name: def.Name,
|
||||
Info: &def.Info,
|
||||
Dependencies: &def.Dependencies,
|
||||
Includes: def.Includes,
|
||||
BaseUrl: def.BaseUrl,
|
||||
Module: def.Module,
|
||||
}
|
||||
|
||||
if app, exists := plugins.Apps[pluginId]; exists {
|
||||
dto.Pages = app.Pages
|
||||
dto.Includes = app.Includes
|
||||
dto.BaseUrl = app.BaseUrl
|
||||
dto.Module = app.Module
|
||||
}
|
||||
|
||||
query := m.GetPluginSettingByIdQuery{PluginId: pluginId, OrgId: c.OrgId}
|
||||
@ -86,3 +107,48 @@ func UpdatePluginSetting(c *middleware.Context, cmd m.UpdatePluginSettingCmd) Re
|
||||
|
||||
return ApiSuccess("Plugin settings updated")
|
||||
}
|
||||
|
||||
func GetPluginDashboards(c *middleware.Context) Response {
|
||||
pluginId := c.Params(":pluginId")
|
||||
|
||||
if list, err := plugins.GetPluginDashboards(c.OrgId, pluginId); err != nil {
|
||||
if notfound, ok := err.(plugins.PluginNotFoundError); ok {
|
||||
return ApiError(404, notfound.Error(), nil)
|
||||
}
|
||||
|
||||
return ApiError(500, "Failed to get plugin dashboards", err)
|
||||
} else {
|
||||
return Json(200, list)
|
||||
}
|
||||
}
|
||||
|
||||
func GetPluginReadme(c *middleware.Context) Response {
|
||||
pluginId := c.Params(":pluginId")
|
||||
|
||||
if content, err := plugins.GetPluginReadme(pluginId); err != nil {
|
||||
if notfound, ok := err.(plugins.PluginNotFoundError); ok {
|
||||
return ApiError(404, notfound.Error(), nil)
|
||||
}
|
||||
|
||||
return ApiError(500, "Could not get readme", err)
|
||||
} else {
|
||||
return Respond(200, content)
|
||||
}
|
||||
}
|
||||
|
||||
func ImportDashboard(c *middleware.Context, apiCmd dtos.ImportDashboardCommand) Response {
|
||||
|
||||
cmd := plugins.ImportDashboardCommand{
|
||||
OrgId: c.OrgId,
|
||||
UserId: c.UserId,
|
||||
PluginId: apiCmd.PluginId,
|
||||
Path: apiCmd.Path,
|
||||
Inputs: apiCmd.Inputs,
|
||||
}
|
||||
|
||||
if err := bus.Dispatch(&cmd); err != nil {
|
||||
return ApiError(500, "Failed to install dashboard", err)
|
||||
}
|
||||
|
||||
return Json(200, cmd.Result)
|
||||
}
|
@ -31,6 +31,7 @@ func RenderToPng(c *middleware.Context) {
|
||||
Width: queryReader.Get("width", "800"),
|
||||
Height: queryReader.Get("height", "400"),
|
||||
SessionId: c.Session.ID(),
|
||||
Timeout: queryReader.Get("timeout", "15"),
|
||||
}
|
||||
|
||||
renderOpts.Url = setting.ToAbsUrl(renderOpts.Url)
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user