Merge branch 'master' into feature/tr-host-under-path
Conflicts: - CHANGELOG.mdpull/814/head
commit
594c1fc587
|
@ -2,13 +2,14 @@
|
|||
|
||||
### Upcoming Bug Fixes
|
||||
1. [#788](https://github.com/influxdata/chronograf/pull/788): Fix missing fields in data explorer when using non-default retention policy
|
||||
1. [#774](https://github.com/influxdata/chronograf/issues/774): Fix gaps in layouts for hosts
|
||||
2. [#774](https://github.com/influxdata/chronograf/issues/774): Fix gaps in layouts for hosts
|
||||
|
||||
### Upcoming Features
|
||||
1. [#779](https://github.com/influxdata/chronograf/issues/779): Add layout for telegraf's diskio system plugin
|
||||
1. [#810](https://github.com/influxdata/chronograf/issues/810): Add layout for telegraf's net system plugin
|
||||
1. [#811](https://github.com/influxdata/chronograf/issues/811): Add layout for telegraf's procstat plugin
|
||||
1. [#814](https://github.com/influxdata/chronograf/issues/814): Allows Chronograf to be mounted under any arbitrary URL path using the `--basepath` flag.
|
||||
2. [#810](https://github.com/influxdata/chronograf/issues/810): Add layout for telegraf's net system plugin
|
||||
3. [#811](https://github.com/influxdata/chronograf/issues/811): Add layout for telegraf's procstat plugin
|
||||
4. [#737](https://github.com/influxdata/chronograf/issues/737): Add GUI for OpsGenie kapacitor alert service
|
||||
5. [#814](https://github.com/influxdata/chronograf/issues/814): Allows Chronograf to be mounted under any arbitrary URL path using the `--basepath` flag.
|
||||
|
||||
### Upcoming UI Improvements
|
||||
|
||||
|
|
|
@ -58,8 +58,8 @@ After installing gvm you can install and set the default go version by
|
|||
running the following:
|
||||
|
||||
```bash
|
||||
gvm install go1.7.4
|
||||
gvm use go1.7.4 --default
|
||||
gvm install go1.7.5
|
||||
gvm use go1.7.5 --default
|
||||
```
|
||||
|
||||
Installing GDM
|
||||
|
|
|
@ -69,10 +69,12 @@ A UI for [Kapacitor](https://github.com/influxdata/kapacitor) alert creation and
|
|||
* Preview data and alert boundaries while creating an alert
|
||||
* Configure alert destinations - Currently, Chronograf supports sending alerts to:
|
||||
* HipChat
|
||||
* OpsGenie
|
||||
* PagerDuty
|
||||
* Sensu
|
||||
* Slack
|
||||
* SMTP
|
||||
* Talk
|
||||
* Telegram
|
||||
* VictorOps
|
||||
* View all active alerts at a glance on the alerting dashboard
|
||||
|
@ -113,7 +115,7 @@ docker pull quay.io/influxdb/chronograf:latest
|
|||
|
||||
### From Source
|
||||
|
||||
* Chronograf works with go 1.7.4, npm 3.10.7 and node v6.6.0. Additional version support of these projects will be implemented soon, but these are the only supported versions to date.
|
||||
* Chronograf works with go 1.7.x, node 6.x/7.x, and npm 3.x.
|
||||
* Chronograf requires [Kapacitor](https://github.com/influxdata/kapacitor) 1.1.x to create and store alerts.
|
||||
|
||||
1. [Install Go](https://golang.org/doc/install)
|
||||
|
|
|
@ -194,6 +194,22 @@ func UnmarshalLayout(data []byte, l *chronograf.Layout) error {
|
|||
func MarshalDashboard(d chronograf.Dashboard) ([]byte, error) {
|
||||
cells := make([]*DashboardCell, len(d.Cells))
|
||||
for i, c := range d.Cells {
|
||||
queries := make([]*Query, len(c.Queries))
|
||||
for j, q := range c.Queries {
|
||||
r := new(Range)
|
||||
if q.Range != nil {
|
||||
r.Upper, r.Lower = q.Range.Upper, q.Range.Lower
|
||||
}
|
||||
queries[j] = &Query{
|
||||
Command: q.Command,
|
||||
DB: q.DB,
|
||||
RP: q.RP,
|
||||
GroupBys: q.GroupBys,
|
||||
Wheres: q.Wheres,
|
||||
Label: q.Label,
|
||||
Range: r,
|
||||
}
|
||||
}
|
||||
|
||||
cells[i] = &DashboardCell{
|
||||
X: c.X,
|
||||
|
@ -201,7 +217,7 @@ func MarshalDashboard(d chronograf.Dashboard) ([]byte, error) {
|
|||
W: c.W,
|
||||
H: c.H,
|
||||
Name: c.Name,
|
||||
Queries: c.Queries,
|
||||
Queries: queries,
|
||||
Type: c.Type,
|
||||
}
|
||||
}
|
||||
|
@ -220,23 +236,39 @@ func UnmarshalDashboard(data []byte, d *chronograf.Dashboard) error {
|
|||
return err
|
||||
}
|
||||
|
||||
cells := make([]chronograf.DashboardCell, len(d.Cells))
|
||||
for i, c := range d.Cells {
|
||||
cells := make([]chronograf.DashboardCell, len(pb.Cells))
|
||||
for i, c := range pb.Cells {
|
||||
queries := make([]chronograf.Query, len(c.Queries))
|
||||
for j, q := range c.Queries {
|
||||
queries[j] = chronograf.Query{
|
||||
Command: q.Command,
|
||||
DB: q.DB,
|
||||
RP: q.RP,
|
||||
GroupBys: q.GroupBys,
|
||||
Wheres: q.Wheres,
|
||||
Label: q.Label,
|
||||
}
|
||||
if q.Range.Upper != q.Range.Lower {
|
||||
queries[j].Range = &chronograf.Range{
|
||||
Upper: q.Range.Upper,
|
||||
Lower: q.Range.Lower,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cells[i] = chronograf.DashboardCell{
|
||||
X: c.X,
|
||||
Y: c.Y,
|
||||
W: c.W,
|
||||
H: c.H,
|
||||
Name: c.Name,
|
||||
Queries: c.Queries,
|
||||
Queries: queries,
|
||||
Type: c.Type,
|
||||
}
|
||||
}
|
||||
|
||||
d.ID = chronograf.DashboardID(pb.ID)
|
||||
d.Cells = cells
|
||||
d.Name = pb.Name
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
@ -39,13 +39,13 @@ var _ = math.Inf
|
|||
const _ = proto.GoGoProtoPackageIsVersion2 // please upgrade the proto package
|
||||
|
||||
type Exploration struct {
|
||||
ID int64 `protobuf:"varint,1,opt,name=ID,json=iD,proto3" json:"ID,omitempty"`
|
||||
Name string `protobuf:"bytes,2,opt,name=Name,json=name,proto3" json:"Name,omitempty"`
|
||||
UserID int64 `protobuf:"varint,3,opt,name=UserID,json=userID,proto3" json:"UserID,omitempty"`
|
||||
Data string `protobuf:"bytes,4,opt,name=Data,json=data,proto3" json:"Data,omitempty"`
|
||||
CreatedAt int64 `protobuf:"varint,5,opt,name=CreatedAt,json=createdAt,proto3" json:"CreatedAt,omitempty"`
|
||||
UpdatedAt int64 `protobuf:"varint,6,opt,name=UpdatedAt,json=updatedAt,proto3" json:"UpdatedAt,omitempty"`
|
||||
Default bool `protobuf:"varint,7,opt,name=Default,json=default,proto3" json:"Default,omitempty"`
|
||||
ID int64 `protobuf:"varint,1,opt,name=ID,proto3" json:"ID,omitempty"`
|
||||
Name string `protobuf:"bytes,2,opt,name=Name,proto3" json:"Name,omitempty"`
|
||||
UserID int64 `protobuf:"varint,3,opt,name=UserID,proto3" json:"UserID,omitempty"`
|
||||
Data string `protobuf:"bytes,4,opt,name=Data,proto3" json:"Data,omitempty"`
|
||||
CreatedAt int64 `protobuf:"varint,5,opt,name=CreatedAt,proto3" json:"CreatedAt,omitempty"`
|
||||
UpdatedAt int64 `protobuf:"varint,6,opt,name=UpdatedAt,proto3" json:"UpdatedAt,omitempty"`
|
||||
Default bool `protobuf:"varint,7,opt,name=Default,proto3" json:"Default,omitempty"`
|
||||
}
|
||||
|
||||
func (m *Exploration) Reset() { *m = Exploration{} }
|
||||
|
@ -54,15 +54,15 @@ func (*Exploration) ProtoMessage() {}
|
|||
func (*Exploration) Descriptor() ([]byte, []int) { return fileDescriptorInternal, []int{0} }
|
||||
|
||||
type Source struct {
|
||||
ID int64 `protobuf:"varint,1,opt,name=ID,json=iD,proto3" json:"ID,omitempty"`
|
||||
Name string `protobuf:"bytes,2,opt,name=Name,json=name,proto3" json:"Name,omitempty"`
|
||||
Type string `protobuf:"bytes,3,opt,name=Type,json=type,proto3" json:"Type,omitempty"`
|
||||
Username string `protobuf:"bytes,4,opt,name=Username,json=username,proto3" json:"Username,omitempty"`
|
||||
Password string `protobuf:"bytes,5,opt,name=Password,json=password,proto3" json:"Password,omitempty"`
|
||||
URL string `protobuf:"bytes,6,opt,name=URL,json=uRL,proto3" json:"URL,omitempty"`
|
||||
Default bool `protobuf:"varint,7,opt,name=Default,json=default,proto3" json:"Default,omitempty"`
|
||||
Telegraf string `protobuf:"bytes,8,opt,name=Telegraf,json=telegraf,proto3" json:"Telegraf,omitempty"`
|
||||
InsecureSkipVerify bool `protobuf:"varint,9,opt,name=InsecureSkipVerify,json=insecureSkipVerify,proto3" json:"InsecureSkipVerify,omitempty"`
|
||||
ID int64 `protobuf:"varint,1,opt,name=ID,proto3" json:"ID,omitempty"`
|
||||
Name string `protobuf:"bytes,2,opt,name=Name,proto3" json:"Name,omitempty"`
|
||||
Type string `protobuf:"bytes,3,opt,name=Type,proto3" json:"Type,omitempty"`
|
||||
Username string `protobuf:"bytes,4,opt,name=Username,proto3" json:"Username,omitempty"`
|
||||
Password string `protobuf:"bytes,5,opt,name=Password,proto3" json:"Password,omitempty"`
|
||||
URL string `protobuf:"bytes,6,opt,name=URL,proto3" json:"URL,omitempty"`
|
||||
Default bool `protobuf:"varint,7,opt,name=Default,proto3" json:"Default,omitempty"`
|
||||
Telegraf string `protobuf:"bytes,8,opt,name=Telegraf,proto3" json:"Telegraf,omitempty"`
|
||||
InsecureSkipVerify bool `protobuf:"varint,9,opt,name=InsecureSkipVerify,proto3" json:"InsecureSkipVerify,omitempty"`
|
||||
}
|
||||
|
||||
func (m *Source) Reset() { *m = Source{} }
|
||||
|
@ -71,8 +71,8 @@ func (*Source) ProtoMessage() {}
|
|||
func (*Source) Descriptor() ([]byte, []int) { return fileDescriptorInternal, []int{1} }
|
||||
|
||||
type Dashboard struct {
|
||||
ID int64 `protobuf:"varint,1,opt,name=ID,json=iD,proto3" json:"ID,omitempty"`
|
||||
Name string `protobuf:"bytes,2,opt,name=Name,json=name,proto3" json:"Name,omitempty"`
|
||||
ID int64 `protobuf:"varint,1,opt,name=ID,proto3" json:"ID,omitempty"`
|
||||
Name string `protobuf:"bytes,2,opt,name=Name,proto3" json:"Name,omitempty"`
|
||||
Cells []*DashboardCell `protobuf:"bytes,3,rep,name=cells" json:"cells,omitempty"`
|
||||
}
|
||||
|
||||
|
@ -93,7 +93,7 @@ type DashboardCell struct {
|
|||
Y int32 `protobuf:"varint,2,opt,name=y,proto3" json:"y,omitempty"`
|
||||
W int32 `protobuf:"varint,3,opt,name=w,proto3" json:"w,omitempty"`
|
||||
H int32 `protobuf:"varint,4,opt,name=h,proto3" json:"h,omitempty"`
|
||||
Queries []string `protobuf:"bytes,5,rep,name=queries" json:"queries,omitempty"`
|
||||
Queries []*Query `protobuf:"bytes,5,rep,name=queries" json:"queries,omitempty"`
|
||||
Name string `protobuf:"bytes,6,opt,name=name,proto3" json:"name,omitempty"`
|
||||
Type string `protobuf:"bytes,7,opt,name=type,proto3" json:"type,omitempty"`
|
||||
}
|
||||
|
@ -103,13 +103,20 @@ func (m *DashboardCell) String() string { return proto.CompactTextStr
|
|||
func (*DashboardCell) ProtoMessage() {}
|
||||
func (*DashboardCell) Descriptor() ([]byte, []int) { return fileDescriptorInternal, []int{3} }
|
||||
|
||||
func (m *DashboardCell) GetQueries() []*Query {
|
||||
if m != nil {
|
||||
return m.Queries
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type Server struct {
|
||||
ID int64 `protobuf:"varint,1,opt,name=ID,json=iD,proto3" json:"ID,omitempty"`
|
||||
Name string `protobuf:"bytes,2,opt,name=Name,json=name,proto3" json:"Name,omitempty"`
|
||||
Username string `protobuf:"bytes,3,opt,name=Username,json=username,proto3" json:"Username,omitempty"`
|
||||
Password string `protobuf:"bytes,4,opt,name=Password,json=password,proto3" json:"Password,omitempty"`
|
||||
URL string `protobuf:"bytes,5,opt,name=URL,json=uRL,proto3" json:"URL,omitempty"`
|
||||
SrcID int64 `protobuf:"varint,6,opt,name=SrcID,json=srcID,proto3" json:"SrcID,omitempty"`
|
||||
ID int64 `protobuf:"varint,1,opt,name=ID,proto3" json:"ID,omitempty"`
|
||||
Name string `protobuf:"bytes,2,opt,name=Name,proto3" json:"Name,omitempty"`
|
||||
Username string `protobuf:"bytes,3,opt,name=Username,proto3" json:"Username,omitempty"`
|
||||
Password string `protobuf:"bytes,4,opt,name=Password,proto3" json:"Password,omitempty"`
|
||||
URL string `protobuf:"bytes,5,opt,name=URL,proto3" json:"URL,omitempty"`
|
||||
SrcID int64 `protobuf:"varint,6,opt,name=SrcID,proto3" json:"SrcID,omitempty"`
|
||||
}
|
||||
|
||||
func (m *Server) Reset() { *m = Server{} }
|
||||
|
@ -118,11 +125,11 @@ func (*Server) ProtoMessage() {}
|
|||
func (*Server) Descriptor() ([]byte, []int) { return fileDescriptorInternal, []int{4} }
|
||||
|
||||
type Layout struct {
|
||||
ID string `protobuf:"bytes,1,opt,name=ID,json=iD,proto3" json:"ID,omitempty"`
|
||||
Application string `protobuf:"bytes,2,opt,name=Application,json=application,proto3" json:"Application,omitempty"`
|
||||
Measurement string `protobuf:"bytes,3,opt,name=Measurement,json=measurement,proto3" json:"Measurement,omitempty"`
|
||||
Cells []*Cell `protobuf:"bytes,4,rep,name=Cells,json=cells" json:"Cells,omitempty"`
|
||||
Autoflow bool `protobuf:"varint,5,opt,name=Autoflow,json=autoflow,proto3" json:"Autoflow,omitempty"`
|
||||
ID string `protobuf:"bytes,1,opt,name=ID,proto3" json:"ID,omitempty"`
|
||||
Application string `protobuf:"bytes,2,opt,name=Application,proto3" json:"Application,omitempty"`
|
||||
Measurement string `protobuf:"bytes,3,opt,name=Measurement,proto3" json:"Measurement,omitempty"`
|
||||
Cells []*Cell `protobuf:"bytes,4,rep,name=Cells" json:"Cells,omitempty"`
|
||||
Autoflow bool `protobuf:"varint,5,opt,name=Autoflow,proto3" json:"Autoflow,omitempty"`
|
||||
}
|
||||
|
||||
func (m *Layout) Reset() { *m = Layout{} }
|
||||
|
@ -163,13 +170,13 @@ func (m *Cell) GetQueries() []*Query {
|
|||
}
|
||||
|
||||
type Query struct {
|
||||
Command string `protobuf:"bytes,1,opt,name=Command,json=command,proto3" json:"Command,omitempty"`
|
||||
DB string `protobuf:"bytes,2,opt,name=DB,json=dB,proto3" json:"DB,omitempty"`
|
||||
RP string `protobuf:"bytes,3,opt,name=RP,json=rP,proto3" json:"RP,omitempty"`
|
||||
GroupBys []string `protobuf:"bytes,4,rep,name=GroupBys,json=groupBys" json:"GroupBys,omitempty"`
|
||||
Wheres []string `protobuf:"bytes,5,rep,name=Wheres,json=wheres" json:"Wheres,omitempty"`
|
||||
Label string `protobuf:"bytes,6,opt,name=Label,json=label,proto3" json:"Label,omitempty"`
|
||||
Range *Range `protobuf:"bytes,7,opt,name=Range,json=range" json:"Range,omitempty"`
|
||||
Command string `protobuf:"bytes,1,opt,name=Command,proto3" json:"Command,omitempty"`
|
||||
DB string `protobuf:"bytes,2,opt,name=DB,proto3" json:"DB,omitempty"`
|
||||
RP string `protobuf:"bytes,3,opt,name=RP,proto3" json:"RP,omitempty"`
|
||||
GroupBys []string `protobuf:"bytes,4,rep,name=GroupBys" json:"GroupBys,omitempty"`
|
||||
Wheres []string `protobuf:"bytes,5,rep,name=Wheres" json:"Wheres,omitempty"`
|
||||
Label string `protobuf:"bytes,6,opt,name=Label,proto3" json:"Label,omitempty"`
|
||||
Range *Range `protobuf:"bytes,7,opt,name=Range" json:"Range,omitempty"`
|
||||
}
|
||||
|
||||
func (m *Query) Reset() { *m = Query{} }
|
||||
|
@ -185,8 +192,8 @@ func (m *Query) GetRange() *Range {
|
|||
}
|
||||
|
||||
type Range struct {
|
||||
Upper int64 `protobuf:"varint,1,opt,name=Upper,json=upper,proto3" json:"Upper,omitempty"`
|
||||
Lower int64 `protobuf:"varint,2,opt,name=Lower,json=lower,proto3" json:"Lower,omitempty"`
|
||||
Upper int64 `protobuf:"varint,1,opt,name=Upper,proto3" json:"Upper,omitempty"`
|
||||
Lower int64 `protobuf:"varint,2,opt,name=Lower,proto3" json:"Lower,omitempty"`
|
||||
}
|
||||
|
||||
func (m *Range) Reset() { *m = Range{} }
|
||||
|
@ -195,10 +202,10 @@ func (*Range) ProtoMessage() {}
|
|||
func (*Range) Descriptor() ([]byte, []int) { return fileDescriptorInternal, []int{8} }
|
||||
|
||||
type AlertRule struct {
|
||||
ID string `protobuf:"bytes,1,opt,name=ID,json=iD,proto3" json:"ID,omitempty"`
|
||||
JSON string `protobuf:"bytes,2,opt,name=JSON,json=jSON,proto3" json:"JSON,omitempty"`
|
||||
SrcID int64 `protobuf:"varint,3,opt,name=SrcID,json=srcID,proto3" json:"SrcID,omitempty"`
|
||||
KapaID int64 `protobuf:"varint,4,opt,name=KapaID,json=kapaID,proto3" json:"KapaID,omitempty"`
|
||||
ID string `protobuf:"bytes,1,opt,name=ID,proto3" json:"ID,omitempty"`
|
||||
JSON string `protobuf:"bytes,2,opt,name=JSON,proto3" json:"JSON,omitempty"`
|
||||
SrcID int64 `protobuf:"varint,3,opt,name=SrcID,proto3" json:"SrcID,omitempty"`
|
||||
KapaID int64 `protobuf:"varint,4,opt,name=KapaID,proto3" json:"KapaID,omitempty"`
|
||||
}
|
||||
|
||||
func (m *AlertRule) Reset() { *m = AlertRule{} }
|
||||
|
@ -207,8 +214,8 @@ func (*AlertRule) ProtoMessage() {}
|
|||
func (*AlertRule) Descriptor() ([]byte, []int) { return fileDescriptorInternal, []int{9} }
|
||||
|
||||
type User struct {
|
||||
ID uint64 `protobuf:"varint,1,opt,name=ID,json=iD,proto3" json:"ID,omitempty"`
|
||||
Email string `protobuf:"bytes,2,opt,name=Email,json=email,proto3" json:"Email,omitempty"`
|
||||
ID uint64 `protobuf:"varint,1,opt,name=ID,proto3" json:"ID,omitempty"`
|
||||
Email string `protobuf:"bytes,2,opt,name=Email,proto3" json:"Email,omitempty"`
|
||||
}
|
||||
|
||||
func (m *User) Reset() { *m = User{} }
|
||||
|
@ -233,52 +240,50 @@ func init() {
|
|||
func init() { proto.RegisterFile("internal.proto", fileDescriptorInternal) }
|
||||
|
||||
var fileDescriptorInternal = []byte{
|
||||
// 750 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xa4, 0x55, 0x6d, 0x6e, 0xdb, 0x46,
|
||||
0x10, 0xc5, 0x8a, 0x5c, 0x8a, 0x5c, 0xb9, 0x6e, 0xb1, 0x30, 0x5a, 0xa2, 0xe8, 0x0f, 0x81, 0x68,
|
||||
0x01, 0x15, 0x68, 0xfd, 0xc3, 0x3e, 0x81, 0x2c, 0x1a, 0x85, 0x5a, 0xd5, 0x76, 0x57, 0x56, 0xfb,
|
||||
0xab, 0x05, 0xd6, 0xe2, 0xc8, 0x62, 0xbd, 0x22, 0xd9, 0x25, 0x59, 0x99, 0x47, 0x08, 0x90, 0x33,
|
||||
0xe4, 0x10, 0xc9, 0x51, 0x72, 0x91, 0x1c, 0x21, 0xd8, 0xe1, 0xea, 0x0b, 0x4e, 0x02, 0x03, 0xf9,
|
||||
0xf9, 0x66, 0x46, 0xc3, 0x37, 0xef, 0x3d, 0x8a, 0xec, 0x38, 0xcd, 0x2a, 0xd0, 0x99, 0x54, 0xa7,
|
||||
0x85, 0xce, 0xab, 0x9c, 0xfb, 0x1b, 0x1c, 0xbd, 0x21, 0xac, 0x77, 0xf9, 0x58, 0xa8, 0x5c, 0xcb,
|
||||
0x2a, 0xcd, 0x33, 0x7e, 0xcc, 0x3a, 0xe3, 0x38, 0x24, 0x7d, 0x32, 0x70, 0x44, 0x27, 0x8d, 0x39,
|
||||
0x67, 0xee, 0x95, 0x5c, 0x41, 0xd8, 0xe9, 0x93, 0x41, 0x20, 0xdc, 0x4c, 0xae, 0x80, 0x7f, 0xcd,
|
||||
0xbc, 0x59, 0x09, 0x7a, 0x1c, 0x87, 0x0e, 0xce, 0x79, 0x35, 0x22, 0x33, 0x1b, 0xcb, 0x4a, 0x86,
|
||||
0x6e, 0x3b, 0x9b, 0xc8, 0x4a, 0xf2, 0xef, 0x58, 0x30, 0xd2, 0x20, 0x2b, 0x48, 0x86, 0x55, 0x48,
|
||||
0x71, 0x3c, 0x98, 0x6f, 0x0a, 0xa6, 0x3b, 0x2b, 0x12, 0xdb, 0xf5, 0xda, 0x6e, 0xbd, 0x29, 0xf0,
|
||||
0x90, 0x75, 0x63, 0x58, 0xc8, 0x5a, 0x55, 0x61, 0xb7, 0x4f, 0x06, 0xbe, 0xe8, 0x26, 0x2d, 0x8c,
|
||||
0xde, 0x11, 0xe6, 0x4d, 0xf3, 0x5a, 0xcf, 0xe1, 0x59, 0x84, 0x39, 0x73, 0x6f, 0x9b, 0x02, 0x90,
|
||||
0x6e, 0x20, 0xdc, 0xaa, 0x29, 0x80, 0x7f, 0xcb, 0x7c, 0x73, 0x84, 0xe9, 0x5b, 0xc2, 0x7e, 0x6d,
|
||||
0xb1, 0xe9, 0xdd, 0xc8, 0xb2, 0x5c, 0xe7, 0x3a, 0x41, 0xce, 0x81, 0xf0, 0x0b, 0x8b, 0xf9, 0x57,
|
||||
0xcc, 0x99, 0x89, 0x09, 0x92, 0x0d, 0x84, 0x53, 0x8b, 0xc9, 0xc7, 0x69, 0x9a, 0x3d, 0xb7, 0xa0,
|
||||
0xe0, 0x5e, 0xcb, 0x45, 0xe8, 0xb7, 0x7b, 0x2a, 0x8b, 0xf9, 0x29, 0xe3, 0xe3, 0xac, 0x84, 0x79,
|
||||
0xad, 0x61, 0xfa, 0x90, 0x16, 0x7f, 0x82, 0x4e, 0x17, 0x4d, 0x18, 0xe0, 0x02, 0x9e, 0x3e, 0xe9,
|
||||
0x44, 0xff, 0xb0, 0x20, 0x96, 0xe5, 0xf2, 0x2e, 0x97, 0x3a, 0x79, 0xd6, 0xd1, 0x3f, 0x33, 0x3a,
|
||||
0x07, 0xa5, 0xca, 0xd0, 0xe9, 0x3b, 0x83, 0xde, 0xd9, 0x37, 0xa7, 0xdb, 0x0c, 0x6c, 0xf7, 0x8c,
|
||||
0x40, 0x29, 0xd1, 0x4e, 0x45, 0x2f, 0x08, 0xfb, 0xe2, 0xa0, 0xc1, 0x8f, 0x18, 0x79, 0xc4, 0x67,
|
||||
0x50, 0x41, 0x1e, 0x0d, 0x6a, 0x70, 0x3f, 0x15, 0xa4, 0x31, 0x68, 0x8d, 0x72, 0x52, 0x41, 0xd6,
|
||||
0x06, 0x2d, 0x51, 0x44, 0x2a, 0xc8, 0xd2, 0xe8, 0xf1, 0x5f, 0x0d, 0x3a, 0x85, 0x32, 0xa4, 0x7d,
|
||||
0x67, 0x10, 0x88, 0x0d, 0x34, 0x34, 0x51, 0x6f, 0xef, 0xd0, 0x1b, 0xe3, 0x07, 0x4a, 0x67, 0xbd,
|
||||
0x89, 0x5e, 0x1a, 0x7b, 0x41, 0xff, 0x0f, 0xfa, 0x59, 0x97, 0xee, 0x5b, 0xe9, 0x7c, 0xc2, 0x4a,
|
||||
0xf7, 0xc3, 0x56, 0xd2, 0x9d, 0x95, 0x27, 0x8c, 0x4e, 0xf5, 0x7c, 0x1c, 0xdb, 0x2c, 0xd2, 0xd2,
|
||||
0x80, 0xe8, 0x15, 0x61, 0xde, 0x44, 0x36, 0x79, 0x5d, 0xed, 0xd1, 0x09, 0x90, 0x4e, 0x9f, 0xf5,
|
||||
0x86, 0x45, 0xa1, 0xd2, 0x39, 0xbe, 0x3d, 0x96, 0x55, 0x4f, 0xee, 0x4a, 0x66, 0xe2, 0x77, 0x90,
|
||||
0x65, 0xad, 0x61, 0x05, 0x59, 0x65, 0xf9, 0xf5, 0x56, 0xbb, 0x12, 0xff, 0x9e, 0xd1, 0x11, 0x1a,
|
||||
0xe5, 0xa2, 0x51, 0xc7, 0x3b, 0xa3, 0xf6, 0xfc, 0x31, 0x87, 0x0c, 0xeb, 0x2a, 0x5f, 0xa8, 0x7c,
|
||||
0x8d, 0x8c, 0x7d, 0xe1, 0x4b, 0x8b, 0xa3, 0xb7, 0x84, 0xb9, 0x9f, 0x65, 0xd9, 0x8f, 0x87, 0x96,
|
||||
0xf5, 0xce, 0xbe, 0xdc, 0x91, 0xf8, 0xa3, 0x06, 0xdd, 0xec, 0x3c, 0x3c, 0x62, 0x24, 0xb5, 0x06,
|
||||
0x92, 0x74, 0xeb, 0x68, 0x77, 0xcf, 0x8e, 0x90, 0x75, 0x1b, 0x2d, 0xb3, 0x7b, 0x28, 0x43, 0xbf,
|
||||
0xef, 0x0c, 0x1c, 0xb1, 0x81, 0xd8, 0x51, 0xf2, 0x0e, 0x54, 0x19, 0x06, 0x6d, 0x32, 0x2c, 0xdc,
|
||||
0xa6, 0x80, 0xed, 0xa5, 0xe0, 0x35, 0x61, 0x14, 0x1f, 0x6e, 0x7e, 0x37, 0xca, 0x57, 0x2b, 0x99,
|
||||
0x25, 0x56, 0xfa, 0xee, 0xbc, 0x85, 0xc6, 0x8f, 0xf8, 0xc2, 0xca, 0xde, 0x49, 0x2e, 0x0c, 0x16,
|
||||
0x37, 0x56, 0xe4, 0x8e, 0xbe, 0x31, 0xaa, 0xfd, 0xa2, 0xf3, 0xba, 0xb8, 0x68, 0x5a, 0x79, 0x03,
|
||||
0xe1, 0xdf, 0x5b, 0x6c, 0xfe, 0xc6, 0xfe, 0x5a, 0x82, 0xde, 0xc6, 0xd4, 0x5b, 0x23, 0x32, 0x21,
|
||||
0x98, 0x18, 0x56, 0xf6, 0x4a, 0x8a, 0x14, 0xf9, 0x0f, 0x8c, 0x0a, 0x73, 0x05, 0x9e, 0x7a, 0x20,
|
||||
0x10, 0x96, 0x05, 0xc5, 0x1b, 0xa3, 0x73, 0x3b, 0x66, 0xb6, 0xcc, 0x8a, 0x02, 0xb4, 0xcd, 0x2e,
|
||||
0xad, 0x0d, 0xc0, 0xdd, 0xf9, 0x1a, 0x34, 0x52, 0x76, 0x04, 0x55, 0x06, 0x44, 0x7f, 0xb3, 0x60,
|
||||
0xa8, 0x40, 0x57, 0xa2, 0x56, 0xf0, 0x24, 0x62, 0x9c, 0xb9, 0xbf, 0x4e, 0xaf, 0xaf, 0x36, 0x89,
|
||||
0xff, 0x77, 0x7a, 0x7d, 0xb5, 0xcb, 0xa9, 0xb3, 0x97, 0x53, 0x73, 0xd0, 0x6f, 0xb2, 0x90, 0xe3,
|
||||
0x18, 0x8d, 0x75, 0x84, 0xf7, 0x80, 0x28, 0xfa, 0x89, 0xb9, 0xe6, 0xfd, 0xd8, 0xdb, 0xec, 0xe2,
|
||||
0xe6, 0x13, 0x46, 0x2f, 0x57, 0x32, 0x55, 0x76, 0x35, 0x05, 0x03, 0xee, 0x3c, 0xfc, 0x44, 0x9c,
|
||||
0xbf, 0x0f, 0x00, 0x00, 0xff, 0xff, 0xfb, 0x83, 0xb5, 0x2a, 0x34, 0x06, 0x00, 0x00,
|
||||
// 712 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xbc, 0x55, 0xd1, 0x6e, 0xd3, 0x4a,
|
||||
0x10, 0xd5, 0xc6, 0x76, 0x12, 0x4f, 0x7b, 0x7b, 0xaf, 0x56, 0xd5, 0xc5, 0x42, 0x3c, 0x44, 0x16,
|
||||
0x48, 0x41, 0x82, 0x3e, 0xd0, 0x2f, 0x48, 0xe3, 0x0a, 0x05, 0x4a, 0x29, 0x9b, 0x06, 0x9e, 0x40,
|
||||
0xda, 0x26, 0x9b, 0xc6, 0xc2, 0xb1, 0xcd, 0xda, 0x26, 0xf5, 0x3f, 0xf0, 0x05, 0x3c, 0xf0, 0x11,
|
||||
0xf0, 0x29, 0xfc, 0x08, 0x9f, 0x80, 0x66, 0xbc, 0x76, 0x5c, 0x51, 0x50, 0x9f, 0x78, 0x9b, 0x33,
|
||||
0x33, 0x9d, 0x3d, 0x73, 0xce, 0xb8, 0x81, 0xbd, 0x30, 0xce, 0x95, 0x8e, 0x65, 0x74, 0x90, 0xea,
|
||||
0x24, 0x4f, 0x78, 0xbf, 0xc6, 0xfe, 0x37, 0x06, 0x3b, 0xc7, 0x57, 0x69, 0x94, 0x68, 0x99, 0x87,
|
||||
0x49, 0xcc, 0xf7, 0xa0, 0x33, 0x09, 0x3c, 0x36, 0x60, 0x43, 0x4b, 0x74, 0x26, 0x01, 0xe7, 0x60,
|
||||
0x9f, 0xca, 0xb5, 0xf2, 0x3a, 0x03, 0x36, 0x74, 0x05, 0xc5, 0xfc, 0x7f, 0xe8, 0xce, 0x32, 0xa5,
|
||||
0x27, 0x81, 0x67, 0x51, 0x9f, 0x41, 0xd8, 0x1b, 0xc8, 0x5c, 0x7a, 0x76, 0xd5, 0x8b, 0x31, 0xbf,
|
||||
0x07, 0xee, 0x58, 0x2b, 0x99, 0xab, 0xc5, 0x28, 0xf7, 0x1c, 0x6a, 0xdf, 0x26, 0xb0, 0x3a, 0x4b,
|
||||
0x17, 0xa6, 0xda, 0xad, 0xaa, 0x4d, 0x82, 0x7b, 0xd0, 0x0b, 0xd4, 0x52, 0x16, 0x51, 0xee, 0xf5,
|
||||
0x06, 0x6c, 0xd8, 0x17, 0x35, 0xf4, 0x7f, 0x30, 0xe8, 0x4e, 0x93, 0x42, 0xcf, 0xd5, 0xad, 0x08,
|
||||
0x73, 0xb0, 0xcf, 0xcb, 0x54, 0x11, 0x5d, 0x57, 0x50, 0xcc, 0xef, 0x42, 0x1f, 0x69, 0xc7, 0xd8,
|
||||
0x5b, 0x11, 0x6e, 0x30, 0xd6, 0xce, 0x64, 0x96, 0x6d, 0x12, 0xbd, 0x20, 0xce, 0xae, 0x68, 0x30,
|
||||
0xff, 0x0f, 0xac, 0x99, 0x38, 0x21, 0xb2, 0xae, 0xc0, 0xf0, 0xf7, 0x34, 0x71, 0xce, 0xb9, 0x8a,
|
||||
0xd4, 0xa5, 0x96, 0x4b, 0xaf, 0x5f, 0xcd, 0xa9, 0x31, 0x3f, 0x00, 0x3e, 0x89, 0x33, 0x35, 0x2f,
|
||||
0xb4, 0x9a, 0xbe, 0x0f, 0xd3, 0xd7, 0x4a, 0x87, 0xcb, 0xd2, 0x73, 0x69, 0xc0, 0x0d, 0x15, 0xff,
|
||||
0x1d, 0xb8, 0x81, 0xcc, 0x56, 0x17, 0x89, 0xd4, 0x8b, 0x5b, 0x2d, 0xfd, 0x18, 0x9c, 0xb9, 0x8a,
|
||||
0xa2, 0xcc, 0xb3, 0x06, 0xd6, 0x70, 0xe7, 0xc9, 0x9d, 0x83, 0xe6, 0x06, 0x9a, 0x39, 0x63, 0x15,
|
||||
0x45, 0xa2, 0xea, 0xf2, 0x3f, 0x33, 0xf8, 0xe7, 0x5a, 0x81, 0xef, 0x02, 0xbb, 0xa2, 0x37, 0x1c,
|
||||
0xc1, 0xae, 0x10, 0x95, 0x34, 0xdf, 0x11, 0xac, 0x44, 0xb4, 0x21, 0x39, 0x1d, 0xc1, 0x36, 0x88,
|
||||
0x56, 0x24, 0xa2, 0x23, 0xd8, 0x8a, 0x3f, 0x84, 0xde, 0x87, 0x42, 0xe9, 0x50, 0x65, 0x9e, 0x43,
|
||||
0x4f, 0xff, 0xbb, 0x7d, 0xfa, 0x55, 0xa1, 0x74, 0x29, 0xea, 0x3a, 0xf2, 0x26, 0x03, 0x2a, 0x35,
|
||||
0x29, 0xc6, 0x5c, 0x8e, 0x66, 0xf5, 0xaa, 0x1c, 0xc6, 0xfe, 0x27, 0xf4, 0x5b, 0xe9, 0x8f, 0x4a,
|
||||
0xdf, 0x6a, 0xf5, 0xb6, 0xb7, 0xd6, 0x1f, 0xbc, 0xb5, 0x6f, 0xf6, 0xd6, 0xd9, 0x7a, 0xbb, 0x0f,
|
||||
0xce, 0x54, 0xcf, 0x27, 0x81, 0x39, 0xce, 0x0a, 0xf8, 0x5f, 0x18, 0x74, 0x4f, 0x64, 0x99, 0x14,
|
||||
0x79, 0x8b, 0x8e, 0x4b, 0x74, 0x06, 0xb0, 0x33, 0x4a, 0xd3, 0x28, 0x9c, 0xd3, 0xe7, 0x64, 0x58,
|
||||
0xb5, 0x53, 0xd8, 0xf1, 0x42, 0xc9, 0xac, 0xd0, 0x6a, 0xad, 0xe2, 0xdc, 0xf0, 0x6b, 0xa7, 0xf8,
|
||||
0x7d, 0x70, 0xc6, 0xe4, 0x9c, 0x4d, 0xf2, 0xed, 0x6d, 0xe5, 0xab, 0x0c, 0xa3, 0x22, 0x2e, 0x32,
|
||||
0x2a, 0xf2, 0x64, 0x19, 0x25, 0x1b, 0x62, 0xdc, 0x17, 0x0d, 0xf6, 0xbf, 0x33, 0xb0, 0xff, 0x96,
|
||||
0x87, 0xbb, 0xc0, 0x42, 0x63, 0x20, 0x0b, 0x1b, 0x47, 0x7b, 0x2d, 0x47, 0x3d, 0xe8, 0x95, 0x5a,
|
||||
0xc6, 0x97, 0x2a, 0xf3, 0xfa, 0x03, 0x6b, 0x68, 0x89, 0x1a, 0x52, 0x25, 0x92, 0x17, 0x2a, 0xca,
|
||||
0x3c, 0x77, 0x60, 0x0d, 0x5d, 0x51, 0xc3, 0xe6, 0x0a, 0xa0, 0x75, 0x05, 0x5f, 0x19, 0x38, 0xf4,
|
||||
0x38, 0xfe, 0xdd, 0x38, 0x59, 0xaf, 0x65, 0xbc, 0x30, 0xd2, 0xd7, 0x10, 0xfd, 0x08, 0x8e, 0x8c,
|
||||
0xec, 0x9d, 0xe0, 0x08, 0xb1, 0x38, 0x33, 0x22, 0x77, 0xc4, 0x19, 0xaa, 0xf6, 0x54, 0x27, 0x45,
|
||||
0x7a, 0x54, 0x56, 0xf2, 0xba, 0xa2, 0xc1, 0xf8, 0x7f, 0xed, 0xcd, 0x4a, 0x69, 0xb3, 0xb3, 0x2b,
|
||||
0x0c, 0xc2, 0x23, 0x38, 0x41, 0x56, 0x66, 0xcb, 0x0a, 0xf0, 0x07, 0xe0, 0x08, 0xdc, 0x82, 0x56,
|
||||
0xbd, 0x26, 0x10, 0xa5, 0x45, 0x55, 0xf5, 0x0f, 0x4d, 0x1b, 0x4e, 0x99, 0xa5, 0xa9, 0xd2, 0xe6,
|
||||
0x76, 0x2b, 0x40, 0xb3, 0x93, 0x8d, 0xd2, 0x44, 0xd9, 0x12, 0x15, 0xf0, 0xdf, 0x82, 0x3b, 0x8a,
|
||||
0x94, 0xce, 0x45, 0x11, 0xa9, 0x5f, 0x4e, 0x8c, 0x83, 0xfd, 0x6c, 0xfa, 0xf2, 0xb4, 0xbe, 0x78,
|
||||
0x8c, 0xb7, 0x77, 0x6a, 0xb5, 0xee, 0x14, 0x17, 0x7a, 0x2e, 0x53, 0x39, 0x09, 0xc8, 0x58, 0x4b,
|
||||
0x18, 0xe4, 0x3f, 0x02, 0x1b, 0xbf, 0x87, 0xd6, 0x64, 0x9b, 0x26, 0xef, 0x83, 0x73, 0xbc, 0x96,
|
||||
0x61, 0x64, 0x46, 0x57, 0xe0, 0xa2, 0x4b, 0xbf, 0x19, 0x87, 0x3f, 0x03, 0x00, 0x00, 0xff, 0xff,
|
||||
0x6d, 0xf2, 0xe7, 0x54, 0x45, 0x06, 0x00, 0x00,
|
||||
}
|
||||
|
|
|
@ -34,7 +34,7 @@ message DashboardCell {
|
|||
int32 y = 2; // Y-coordinate of Cell in the Dashboard
|
||||
int32 w = 3; // Width of Cell in the Dashboard
|
||||
int32 h = 4; // Height of Cell in the Dashboard
|
||||
repeated string queries = 5; // Time-series data queries for Dashboard
|
||||
repeated Query queries = 5; // Time-series data queries for Dashboard
|
||||
string name = 6; // User-facing name for this Dashboard
|
||||
string type = 7; // Dashboard visualization type
|
||||
}
|
||||
|
|
|
@ -243,7 +243,7 @@ type DashboardCell struct {
|
|||
W int32 `json:"w"`
|
||||
H int32 `json:"h"`
|
||||
Name string `json:"name"`
|
||||
Queries []string `json:"queries"`
|
||||
Queries []Query `json:"queries"`
|
||||
Type string `json:"type"`
|
||||
}
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@ machine:
|
|||
services:
|
||||
- docker
|
||||
environment:
|
||||
DOCKER_TAG: chronograf-20161207
|
||||
DOCKER_TAG: chronograf-20170127
|
||||
|
||||
dependencies:
|
||||
override:
|
||||
|
@ -26,7 +26,7 @@ deployment:
|
|||
--package
|
||||
--platform all
|
||||
--arch all
|
||||
--upload
|
||||
--upload-overwrite
|
||||
- sudo chown -R ubuntu:ubuntu /home/ubuntu
|
||||
- cp build/linux/static_amd64/chronograf .
|
||||
- docker build -t chronograf .
|
||||
|
@ -46,7 +46,7 @@ deployment:
|
|||
--package
|
||||
--platform all
|
||||
--arch all
|
||||
--upload
|
||||
--upload-overwrite
|
||||
--bucket dl.influxdata.com/chronograf/releases
|
||||
- sudo chown -R ubuntu:ubuntu /home/ubuntu
|
||||
- cp build/linux/static_amd64/chronograf .
|
||||
|
@ -67,7 +67,7 @@ deployment:
|
|||
--package
|
||||
--platform all
|
||||
--arch all
|
||||
--upload
|
||||
--upload-overwrite
|
||||
--bucket dl.influxdata.com/chronograf/releases
|
||||
- sudo chown -R ubuntu:ubuntu /home/ubuntu
|
||||
- cp build/linux/static_amd64/chronograf .
|
||||
|
|
|
@ -16,9 +16,9 @@ RUN pip install boto requests python-jose --upgrade
|
|||
RUN gem install fpm
|
||||
|
||||
# Install node
|
||||
RUN wget -q https://nodejs.org/dist/latest-v6.x/node-v6.9.1-linux-x64.tar.gz; \
|
||||
tar -xvf node-v6.9.1-linux-x64.tar.gz -C / --strip-components=1; \
|
||||
rm -f node-v6.9.1-linux-x64.tar.gz
|
||||
RUN wget -q https://nodejs.org/dist/latest-v6.x/node-v6.9.4-linux-x64.tar.gz; \
|
||||
tar -xvf node-v6.9.4-linux-x64.tar.gz -C / --strip-components=1; \
|
||||
rm -f node-v6.9.4-linux-x64.tar.gz
|
||||
|
||||
# Update npm
|
||||
RUN cd $(npm root -g)/npm \
|
||||
|
@ -28,7 +28,7 @@ RUN npm install npm -g
|
|||
|
||||
# Install go
|
||||
ENV GOPATH /root/go
|
||||
ENV GO_VERSION 1.7.4
|
||||
ENV GO_VERSION 1.7.5
|
||||
ENV GO_ARCH amd64
|
||||
RUN wget https://storage.googleapis.com/golang/go${GO_VERSION}.linux-${GO_ARCH}.tar.gz; \
|
||||
tar -C /usr/local/ -xf /go${GO_VERSION}.linux-${GO_ARCH}.tar.gz ; \
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
## Builds
|
||||
|
||||
Builds are run from a docker build image that is configured with the node and go we support.
|
||||
Our circle.yml uses this docker container to build, test and create release packages.
|
||||
|
||||
### Updating new node/go versions
|
||||
After updating the Dockerfile_build run
|
||||
|
||||
`docker build -t quay.io/influxdb/builder:chronograf-$(date "+%Y%m%d") -f Dockerfile_build`
|
||||
|
||||
and push to quay with:
|
||||
`docker push quay.io/influxdb/builder:chronograf-$(date "+%Y%m%d")`
|
|
@ -164,10 +164,8 @@ func ValidDashboardRequest(d chronograf.Dashboard) error {
|
|||
}
|
||||
|
||||
for _, c := range d.Cells {
|
||||
for _, q := range c.Queries {
|
||||
if len(q) == 0 {
|
||||
return fmt.Errorf("query required")
|
||||
}
|
||||
if (len(c.Queries) == 0) {
|
||||
return fmt.Errorf("query required")
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -119,8 +119,8 @@ func NewMux(opts MuxOpts, service Service) http.Handler {
|
|||
router.POST("/chronograf/v1/dashboards", service.NewDashboard)
|
||||
|
||||
router.GET("/chronograf/v1/dashboards/:id", service.DashboardID)
|
||||
router.DELETE("/chronograf/v1/dashboard/:id", service.RemoveDashboard)
|
||||
router.PUT("/chronograf/v1/dashboard/:id", service.UpdateDashboard)
|
||||
router.DELETE("/chronograf/v1/dashboards/:id", service.RemoveDashboard)
|
||||
router.PUT("/chronograf/v1/dashboards/:id", service.UpdateDashboard)
|
||||
|
||||
/* Authentication */
|
||||
if opts.UseAuth {
|
||||
|
|
|
@ -6,3 +6,10 @@ export function getDashboards() {
|
|||
url: `/chronograf/v1/dashboards`,
|
||||
});
|
||||
}
|
||||
|
||||
export function getDashboard(id) {
|
||||
return AJAX({
|
||||
method: 'GET',
|
||||
url: `/chronograf/v1/dashboards/${id}`,
|
||||
});
|
||||
}
|
||||
|
|
|
@ -0,0 +1,111 @@
|
|||
import React, {PropTypes} from 'react';
|
||||
import ReactTooltip from 'react-tooltip';
|
||||
|
||||
import LayoutRenderer from 'shared/components/LayoutRenderer';
|
||||
import TimeRangeDropdown from '../../shared/components/TimeRangeDropdown';
|
||||
import timeRanges from 'hson!../../shared/data/timeRanges.hson';
|
||||
|
||||
import {getDashboard} from '../apis';
|
||||
import {getSource} from 'shared/apis';
|
||||
|
||||
const DashboardPage = React.createClass({
|
||||
propTypes: {
|
||||
params: PropTypes.shape({
|
||||
sourceID: PropTypes.string.isRequired,
|
||||
dashboardID: PropTypes.string.isRequired,
|
||||
}).isRequired,
|
||||
},
|
||||
|
||||
getInitialState() {
|
||||
const fifteenMinutesIndex = 1;
|
||||
|
||||
return {
|
||||
timeRange: timeRanges[fifteenMinutesIndex],
|
||||
};
|
||||
},
|
||||
|
||||
componentDidMount() {
|
||||
getDashboard(this.props.params.dashboardID).then((resp) => {
|
||||
getSource(this.props.params.sourceID).then(({data: source}) => {
|
||||
this.setState({
|
||||
dashboard: resp.data,
|
||||
source,
|
||||
});
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
renderDashboard(dashboard) {
|
||||
const autoRefreshMs = 15000;
|
||||
const {timeRange} = this.state;
|
||||
const {source} = this.state;
|
||||
|
||||
const cellWidth = 4;
|
||||
const cellHeight = 4;
|
||||
|
||||
const cells = dashboard.cells.map((cell, i) => {
|
||||
const dashboardCell = Object.assign(cell, {
|
||||
w: cellWidth,
|
||||
h: cellHeight,
|
||||
queries: cell.queries,
|
||||
i: i.toString(),
|
||||
});
|
||||
|
||||
dashboardCell.queries.forEach((q) => {
|
||||
q.text = q.query;
|
||||
q.database = source.telegraf;
|
||||
});
|
||||
return dashboardCell;
|
||||
});
|
||||
|
||||
return (
|
||||
<LayoutRenderer
|
||||
timeRange={timeRange}
|
||||
cells={cells}
|
||||
autoRefreshMs={autoRefreshMs}
|
||||
source={source.links.proxy}
|
||||
/>
|
||||
);
|
||||
},
|
||||
|
||||
handleChooseTimeRange({lower}) {
|
||||
const timeRange = timeRanges.find((range) => range.queryValue === lower);
|
||||
this.setState({timeRange});
|
||||
},
|
||||
|
||||
render() {
|
||||
const {dashboard, timeRange} = this.state;
|
||||
const dashboardName = dashboard ? dashboard.name : '';
|
||||
|
||||
return (
|
||||
<div className="page">
|
||||
<div className="page-header full-width">
|
||||
<div className="page-header__container">
|
||||
<div className="page-header__left">
|
||||
<div className="dropdown page-header-dropdown">
|
||||
<button className="dropdown-toggle" type="button" data-toggle="dropdown">
|
||||
<span className="button-text">{dashboardName}</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="page-header__right">
|
||||
<div className="btn btn-info btn-sm" data-for="graph-tips-tooltip" data-tip="<p><code>Click + Drag</code> Zoom in (X or Y)</p><p><code>Shift + Click</code> Pan Graph Window</p><p><code>Double Click</code> Reset Graph Window</p>">
|
||||
<span className="icon heart"></span>
|
||||
Graph Tips
|
||||
</div>
|
||||
<ReactTooltip id="graph-tips-tooltip" effect="solid" html={true} offset={{top: 2}} place="bottom" class="influx-tooltip place-bottom" />
|
||||
<TimeRangeDropdown onChooseTimeRange={this.handleChooseTimeRange} selected={timeRange.inputValue} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="page-contents">
|
||||
<div className="container-fluid full-width">
|
||||
{ dashboard ? this.renderDashboard(dashboard) : '' }
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
export default DashboardPage;
|
|
@ -1,26 +1,44 @@
|
|||
import React from 'react';
|
||||
import React, {PropTypes} from 'react';
|
||||
import {Link} from 'react-router';
|
||||
|
||||
import {getDashboards} from '../apis';
|
||||
|
||||
const DashboardsPage = React.createClass({
|
||||
propTypes: {
|
||||
source: PropTypes.shape({
|
||||
id: PropTypes.string.isRequired,
|
||||
name: PropTypes.string.isRequired,
|
||||
type: PropTypes.string,
|
||||
links: PropTypes.shape({
|
||||
proxy: PropTypes.string.isRequired,
|
||||
}).isRequired,
|
||||
telegraf: PropTypes.string.isRequired,
|
||||
}),
|
||||
addFlashMessage: PropTypes.func,
|
||||
},
|
||||
|
||||
getInitialState() {
|
||||
return {
|
||||
dashboards: [],
|
||||
waiting: true,
|
||||
};
|
||||
},
|
||||
|
||||
componentDidMount() {
|
||||
getDashboards().then((dashboards) => {
|
||||
getDashboards().then((resp) => {
|
||||
this.setState({
|
||||
dashboards,
|
||||
dashboards: resp.data.dashboards,
|
||||
waiting: false,
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
render() {
|
||||
let tableHeader;
|
||||
if (this.state.dashboards.length === 0) {
|
||||
if (this.state.waiting) {
|
||||
tableHeader = "Loading Dashboards...";
|
||||
} else if (this.state.dashboards.length === 0) {
|
||||
tableHeader = "No Dashboards";
|
||||
} else {
|
||||
tableHeader = `${this.state.dashboards.length} Dashboards`;
|
||||
}
|
||||
|
@ -56,7 +74,11 @@ const DashboardsPage = React.createClass({
|
|||
this.state.dashboards.map((dashboard) => {
|
||||
return (
|
||||
<tr key={dashboard.id}>
|
||||
<td className="monotype">{dashboard.name}</td>
|
||||
<td className="monotype">
|
||||
<Link to={`/sources/${this.props.source.id}/dashboards/${dashboard.id}`}>
|
||||
{dashboard.name}
|
||||
</Link>
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
})
|
||||
|
|
|
@ -1,2 +1,3 @@
|
|||
import DashboardsPage from './containers/DashboardsPage';
|
||||
export {DashboardsPage};
|
||||
import DashboardPage from './containers/DashboardPage';
|
||||
export {DashboardsPage, DashboardPage};
|
||||
|
|
|
@ -12,7 +12,7 @@ import {KubernetesPage} from 'src/kubernetes';
|
|||
import {Login} from 'src/auth';
|
||||
import {KapacitorPage, KapacitorRulePage, KapacitorRulesPage, KapacitorTasksPage} from 'src/kapacitor';
|
||||
import DataExplorer from 'src/data_explorer';
|
||||
import {DashboardsPage} from 'src/dashboards';
|
||||
import {DashboardsPage, DashboardPage} from 'src/dashboards';
|
||||
import {CreateSource, SourcePage, ManageSources} from 'src/sources';
|
||||
import NotFound from 'src/shared/components/NotFound';
|
||||
import configureStore from 'src/store/configureStore';
|
||||
|
@ -120,6 +120,7 @@ const Root = React.createClass({
|
|||
<Route path="kapacitor-tasks" component={KapacitorTasksPage} />
|
||||
<Route path="alerts" component={AlertsApp} />
|
||||
<Route path="dashboards" component={DashboardsPage} />
|
||||
<Route path="dashboards/:dashboardID" component={DashboardPage} />
|
||||
<Route path="alert-rules" component={KapacitorRulesPage} />
|
||||
<Route path="alert-rules/:ruleID" component={KapacitorRulePage} />
|
||||
<Route path="alert-rules/new" component={KapacitorRulePage} />
|
||||
|
|
|
@ -3,6 +3,7 @@ import _ from 'lodash';
|
|||
import {getKapacitorConfig, updateKapacitorConfigSection, testAlertOutput} from 'shared/apis';
|
||||
import AlertaConfig from './AlertaConfig';
|
||||
import HipChatConfig from './HipChatConfig';
|
||||
import OpsGenieConfig from './OpsGenieConfig';
|
||||
import PagerDutyConfig from './PagerDutyConfig';
|
||||
import SensuConfig from './SensuConfig';
|
||||
import SlackConfig from './SlackConfig';
|
||||
|
@ -111,6 +112,7 @@ const AlertOutputs = React.createClass({
|
|||
<label htmlFor="alert-endpoint" className="sr-only">Alert Enpoint</label>
|
||||
<select value={this.state.selectedEndpoint} className="form-control" id="source" onChange={this.changeSelectedEndpoint}>
|
||||
<option value="hipchat">HipChat</option>
|
||||
<option value="opsgenie">OpsGenie</option>
|
||||
<option value="pagerduty">PagerDuty</option>
|
||||
<option value="sensu">Sensu</option>
|
||||
<option value="slack">Slack</option>
|
||||
|
@ -159,6 +161,9 @@ const AlertOutputs = React.createClass({
|
|||
case 'telegram': {
|
||||
return <TelegramConfig onSave={save} config={this.getSection(configSections, endpoint)} />;
|
||||
}
|
||||
case 'opsgenie': {
|
||||
return <OpsGenieConfig onSave={save} config={this.getSection(configSections, endpoint)} />;
|
||||
}
|
||||
case 'pagerduty': {
|
||||
return <PagerDutyConfig onSave={save} config={this.getSection(configSections, endpoint)} />;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,177 @@
|
|||
import React, {PropTypes} from 'react';
|
||||
import _ from 'lodash';
|
||||
|
||||
const {
|
||||
array,
|
||||
arrayOf,
|
||||
bool,
|
||||
func,
|
||||
shape,
|
||||
string,
|
||||
} = PropTypes;
|
||||
|
||||
const OpsGenieConfig = React.createClass({
|
||||
propTypes: {
|
||||
config: shape({
|
||||
options: shape({
|
||||
'api-key': bool,
|
||||
teams: array,
|
||||
recipients: array,
|
||||
}).isRequired,
|
||||
}).isRequired,
|
||||
onSave: func.isRequired,
|
||||
},
|
||||
|
||||
getInitialState() {
|
||||
const {teams, recipients} = this.props.config.options;
|
||||
return {
|
||||
currentTeams: teams || [],
|
||||
currentRecipients: recipients || [],
|
||||
};
|
||||
},
|
||||
|
||||
handleSaveAlert(e) {
|
||||
e.preventDefault();
|
||||
|
||||
const properties = {
|
||||
'api-key': this.apiKey.value,
|
||||
teams: this.state.currentTeams,
|
||||
recipients: this.state.currentRecipients,
|
||||
};
|
||||
|
||||
this.props.onSave(properties);
|
||||
},
|
||||
|
||||
handleAddTeam(team) {
|
||||
this.setState({currentTeams: this.state.currentTeams.concat(team)});
|
||||
},
|
||||
|
||||
handleAddRecipient(recipient) {
|
||||
this.setState({currentRecipients: this.state.currentRecipients.concat(recipient)});
|
||||
},
|
||||
|
||||
handleDeleteTeam(team) {
|
||||
this.setState({currentTeams: this.state.currentTeams.filter(t => t !== team)});
|
||||
},
|
||||
|
||||
handleDeleteRecipient(recipient) {
|
||||
this.setState({currentRecipients: this.state.currentRecipients.filter(r => r !== recipient)});
|
||||
},
|
||||
|
||||
render() {
|
||||
const {options} = this.props.config;
|
||||
const apiKey = options['api-key'];
|
||||
const {currentTeams, currentRecipients} = this.state;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h4 className="text-center">OpsGenie Alert</h4>
|
||||
<br/>
|
||||
<p>Have alerts sent to OpsGenie.</p>
|
||||
<form onSubmit={this.handleSaveAlert}>
|
||||
<div className="form-group col-xs-12">
|
||||
<label htmlFor="api-key">API Key</label>
|
||||
<input className="form-control" id="api-key" type="text" ref={(r) => this.apiKey = r} defaultValue={apiKey || ''}></input>
|
||||
<label className="form-helper">Note: a value of <code>true</code> indicates the OpsGenie API key has been set</label>
|
||||
</div>
|
||||
|
||||
<TagInput title="Teams" onAddTag={this.handleAddTeam} onDeleteTag={this.handleDeleteTeam} tags={currentTeams} />
|
||||
<TagInput title="Recipients" onAddTag={this.handleAddRecipient} onDeleteTag={this.handleDeleteRecipient} tags={currentRecipients} />
|
||||
|
||||
<div className="form-group form-group-submit col-xs-12 col-sm-6 col-sm-offset-3">
|
||||
<button className="btn btn-block btn-primary" type="submit">Save</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
const TagInput = React.createClass({
|
||||
propTypes: {
|
||||
onAddTag: func.isRequired,
|
||||
onDeleteTag: func.isRequired,
|
||||
tags: arrayOf(string).isRequired,
|
||||
title: string.isRequired,
|
||||
},
|
||||
|
||||
handleAddTag(e) {
|
||||
if (e.key === 'Enter') {
|
||||
e.preventDefault();
|
||||
const newItem = e.target.value.trim();
|
||||
const {tags, onAddTag} = this.props;
|
||||
if (!this.shouldAddToList(newItem, tags)) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.input.value = '';
|
||||
onAddTag(newItem);
|
||||
}
|
||||
},
|
||||
|
||||
shouldAddToList(item, tags) {
|
||||
return (!_.isEmpty(item) && !tags.find(l => l === item));
|
||||
},
|
||||
|
||||
render() {
|
||||
const {title, tags, onDeleteTag} = this.props;
|
||||
|
||||
return (
|
||||
<div className="form-group col-xs-12">
|
||||
<label htmlFor={title}>{title}</label>
|
||||
<input
|
||||
placeholder={`Type and hit 'Enter' to add to list of ${title}`}
|
||||
autoComplete="off"
|
||||
className="form-control"
|
||||
id={title} type="text"
|
||||
ref={(r) => this.input = r}
|
||||
onKeyDown={this.handleAddTag}>
|
||||
</input>
|
||||
<Tags tags={tags} onDeleteTag={onDeleteTag}/>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
const Tags = React.createClass({
|
||||
propTypes: {
|
||||
tags: arrayOf(string),
|
||||
onDeleteTag: func,
|
||||
},
|
||||
|
||||
render() {
|
||||
const {tags, onDeleteTag} = this.props;
|
||||
return (
|
||||
<div className="input-tag-list">
|
||||
{
|
||||
tags.map((item) => {
|
||||
return (
|
||||
<Tag key={item} item={item} onDelete={onDeleteTag} />
|
||||
);
|
||||
})
|
||||
}
|
||||
</div>
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
const Tag = React.createClass({
|
||||
propTypes: {
|
||||
item: string,
|
||||
onDelete: func,
|
||||
},
|
||||
|
||||
render() {
|
||||
const {item, onDelete} = this.props;
|
||||
|
||||
return (
|
||||
<span key={item} className="input-tag-item">
|
||||
<span>{item}</span>
|
||||
<span className="icon remove" onClick={() => onDelete(item)}></span>
|
||||
</span>
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
export default OpsGenieConfig;
|
|
@ -18,11 +18,19 @@ const TelegramConfig = React.createClass({
|
|||
handleSaveAlert(e) {
|
||||
e.preventDefault();
|
||||
|
||||
let parseMode;
|
||||
if (this.parseModeHTML.checked) {
|
||||
parseMode = 'HTML';
|
||||
}
|
||||
if (this.parseModeMarkdown.checked) {
|
||||
parseMode = 'Markdown';
|
||||
}
|
||||
|
||||
const properties = {
|
||||
chatID: this.chatID.value,
|
||||
disableNotification: this.disableNotification.checked,
|
||||
disableWebPagePreview: this.disableWebPagePreview.checked,
|
||||
parseMode: this.parseMode.checked,
|
||||
'chat-id': this.chatID.value,
|
||||
'disable-notification': this.disableNotification.checked,
|
||||
'disable-web-page-preview': this.disableWebPagePreview.checked,
|
||||
'parse-mode': parseMode,
|
||||
token: this.token.value,
|
||||
url: this.url.value,
|
||||
};
|
||||
|
@ -61,9 +69,16 @@ const TelegramConfig = React.createClass({
|
|||
</div>
|
||||
|
||||
<div className="form-group col-xs-12">
|
||||
<label htmlFor="parseMode">Parse Mode</label>
|
||||
<div className="form-control-static">
|
||||
<input id="enableParseMode" type="checkbox" defaultChecked={parseMode} ref={(r) => this.parseMode = r} />
|
||||
<label htmlFor="enableParseMode">Enable Parse Mode</label>
|
||||
<div className="radio">
|
||||
<input id="parseModeHTML" type="radio" name="parseMode" value="html" defaultChecked={parseMode === 'HTML'} ref={(r) => this.parseModeHTML = r} />
|
||||
<label htmlFor="parseModeHTML">HTML</label>
|
||||
</div>
|
||||
<div className="radio">
|
||||
<input id="parseModeMarkdown" type="radio" name="parseMode" value="markdown" defaultChecked={parseMode === 'Markdown'} ref={(r) => this.parseModeMarkdown = r} />
|
||||
<label htmlFor="parseModeMarkdown">Markdown</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
@import 'layout/sidebar.scss';
|
||||
|
||||
// Components
|
||||
@import 'components/input-tag-list';
|
||||
@import 'components/group-by-time-dropdown';
|
||||
@import 'components/page-header-dropdown';
|
||||
@import 'components/multi-select-dropdown';
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
.input-tag-list {
|
||||
padding: 0 11px;
|
||||
margin: 2px 0px;
|
||||
font-size: 0;
|
||||
}
|
||||
$input-tag-item-height: 24px;
|
||||
.input-tag-item {
|
||||
display: inline-block;
|
||||
white-space: nowrap;
|
||||
height: $input-tag-item-height;
|
||||
line-height: $input-tag-item-height;
|
||||
padding: 0 9px;
|
||||
border-radius: 3px;
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
background-color: $g5-pepper;
|
||||
color: $g18-cloud;
|
||||
cursor: default;
|
||||
margin: 2px;
|
||||
}
|
||||
|
||||
.input-tag-item .icon {
|
||||
transition: color 0.25s ease;
|
||||
margin-left: 6px;
|
||||
position: relative;
|
||||
top: -0.5px;
|
||||
|
||||
&:hover {
|
||||
color: $c-dreamsicle;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
|
@ -528,6 +528,83 @@ $form-static-checkbox-size: 16px;
|
|||
&:checked + label {
|
||||
color: $g20-white;
|
||||
|
||||
&:after {
|
||||
opacity: 1;
|
||||
transform: translate(-50%,-50%) scale(1,1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.form-control-static .radio {
|
||||
margin: 0;
|
||||
|
||||
input[type="radio"] {
|
||||
position: relative;
|
||||
left: -9999px;
|
||||
visibility: hidden;
|
||||
width: 0;
|
||||
height: 0;
|
||||
margin: 0;
|
||||
|
||||
// Faux Checkbox
|
||||
& + label {
|
||||
font-size: 14px !important;
|
||||
line-height: 16px;
|
||||
color: $g11-sidewalk;
|
||||
font-weight: 500;
|
||||
transition: color 0.25s ease;
|
||||
margin: 0;
|
||||
padding: 0 0 0 (12px + $form-static-checkbox-size) !important;
|
||||
user-select: none;
|
||||
-ms-user-select: none;
|
||||
-moz-user-selct: none;
|
||||
-webkit-user-select: none;
|
||||
|
||||
&:before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 0;
|
||||
transform: translateY(-50%);
|
||||
width: $form-static-checkbox-size;
|
||||
height: $form-static-checkbox-size;
|
||||
background-color: $g2-kevlar;
|
||||
border: 2px solid $g5-pepper;
|
||||
border-radius: 50%;
|
||||
z-index: 2;
|
||||
transition:
|
||||
border-color 0.25s ease;
|
||||
}
|
||||
&:after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: ($form-static-checkbox-size / 2);
|
||||
transform: translate(-50%,-50%) scale(2,2);
|
||||
opacity: 0;
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
border-radius: 50%;
|
||||
background-color: $c-pool;
|
||||
z-index: 3;
|
||||
transition:
|
||||
opacity 0.25s ease,
|
||||
transform 0.25s cubic-bezier(0.175, 0.885, 0.32, 1.275);
|
||||
}
|
||||
&:hover {
|
||||
cursor: pointer;
|
||||
color: $g20-white;
|
||||
|
||||
&:before {
|
||||
border-color: $g6-smoke;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Faux Checkbox (Checked)
|
||||
&:checked + label {
|
||||
color: $g20-white;
|
||||
|
||||
&:after {
|
||||
opacity: 1;
|
||||
transform: translate(-50%,-50%) scale(1,1);
|
||||
|
|
Loading…
Reference in New Issue