From 8dc08278bd3243b35cb37548ef1e3370326e5654 Mon Sep 17 00:00:00 2001 From: Chris Goller Date: Fri, 14 Jul 2017 20:02:13 -0500 Subject: [PATCH 1/4] Add JWT shared secret influxdb auth Refactor --new-sources to simplify adding shared secrets --- bolt/internal/internal.go | 2 + bolt/internal/internal.pb.go | 112 +- bolt/internal/internal.proto | 19 +- bolt/internal/internal_test.go | 32 + chronograf.go | 42 +- chronograf_test.go | 88 - enterprise/enterprise.go | 16 +- influx/influx.go | 41 +- influx/influx_test.go | 150 +- influx/jwt.go | 41 + influx/jwt_test.go | 44 + server/server.go | 39 +- server/service.go | 23 +- server/sources.go | 70 +- server/sources_test.go | 180 + server/swagger.json | 8083 +++++++++++++++----------------- 16 files changed, 4528 insertions(+), 4454 deletions(-) delete mode 100644 chronograf_test.go create mode 100644 influx/jwt.go create mode 100644 influx/jwt_test.go diff --git a/bolt/internal/internal.go b/bolt/internal/internal.go index 11f35474ed..b3f677c4ae 100644 --- a/bolt/internal/internal.go +++ b/bolt/internal/internal.go @@ -17,6 +17,7 @@ func MarshalSource(s chronograf.Source) ([]byte, error) { Type: s.Type, Username: s.Username, Password: s.Password, + SharedSecret: s.SharedSecret, URL: s.URL, MetaURL: s.MetaURL, InsecureSkipVerify: s.InsecureSkipVerify, @@ -37,6 +38,7 @@ func UnmarshalSource(data []byte, s *chronograf.Source) error { s.Type = pb.Type s.Username = pb.Username s.Password = pb.Password + s.SharedSecret = pb.SharedSecret s.URL = pb.URL s.MetaURL = pb.MetaURL s.InsecureSkipVerify = pb.InsecureSkipVerify diff --git a/bolt/internal/internal.pb.go b/bolt/internal/internal.pb.go index ac2b29b1a4..994617efad 100644 --- a/bolt/internal/internal.pb.go +++ b/bolt/internal/internal.pb.go @@ -51,6 +51,7 @@ type Source struct { Telegraf string `protobuf:"bytes,8,opt,name=Telegraf,proto3" json:"Telegraf,omitempty"` InsecureSkipVerify bool `protobuf:"varint,9,opt,name=InsecureSkipVerify,proto3" json:"InsecureSkipVerify,omitempty"` MetaURL string `protobuf:"bytes,10,opt,name=MetaURL,proto3" json:"MetaURL,omitempty"` + SharedSecret string `protobuf:"bytes,11,opt,name=SharedSecret,proto3" json:"SharedSecret,omitempty"` } func (m *Source) Reset() { *m = Source{} } @@ -293,59 +294,60 @@ func init() { func init() { proto.RegisterFile("internal.proto", fileDescriptorInternal) } var fileDescriptorInternal = []byte{ - // 858 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x55, 0xdd, 0x6e, 0xe3, 0x44, - 0x14, 0xd6, 0xc4, 0x76, 0x62, 0x9f, 0xee, 0x16, 0x34, 0x5a, 0xb1, 0x06, 0x6e, 0x22, 0x0b, 0xa4, - 0x82, 0x44, 0x41, 0xec, 0x13, 0xb4, 0xb5, 0x84, 0x42, 0xbb, 0x4b, 0x99, 0xb4, 0xe5, 0x0a, 0xad, - 0x26, 0xc9, 0x49, 0x6b, 0xed, 0x24, 0x36, 0x63, 0xbb, 0x59, 0xbf, 0x02, 0x57, 0x3c, 0x01, 0x12, - 0x12, 0x57, 0x5c, 0xf2, 0x02, 0x3c, 0x04, 0x2f, 0x84, 0xce, 0xcc, 0xf8, 0x27, 0x6c, 0x41, 0x7b, - 0xb5, 0x77, 0xf3, 0x9d, 0x33, 0xf9, 0xe6, 0xfc, 0x7c, 0x9f, 0x03, 0x87, 0xd9, 0xb6, 0x42, 0xbd, - 0x95, 0xea, 0xb8, 0xd0, 0x79, 0x95, 0xf3, 0xb0, 0xc5, 0xc9, 0xcf, 0x23, 0x18, 0xcf, 0xf3, 0x5a, - 0x2f, 0x91, 0x1f, 0xc2, 0x68, 0x96, 0xc6, 0x6c, 0xca, 0x8e, 0x3c, 0x31, 0x9a, 0xa5, 0x9c, 0x83, - 0xff, 0x42, 0x6e, 0x30, 0x1e, 0x4d, 0xd9, 0x51, 0x24, 0xcc, 0x99, 0x62, 0x57, 0x4d, 0x81, 0xb1, - 0x67, 0x63, 0x74, 0xe6, 0x1f, 0x41, 0x78, 0x5d, 0x12, 0xdb, 0x06, 0x63, 0xdf, 0xc4, 0x3b, 0x4c, - 0xb9, 0x4b, 0x59, 0x96, 0xbb, 0x5c, 0xaf, 0xe2, 0xc0, 0xe6, 0x5a, 0xcc, 0xdf, 0x07, 0xef, 0x5a, - 0x5c, 0xc4, 0x63, 0x13, 0xa6, 0x23, 0x8f, 0x61, 0x92, 0xe2, 0x5a, 0xd6, 0xaa, 0x8a, 0x27, 0x53, - 0x76, 0x14, 0x8a, 0x16, 0x12, 0xcf, 0x15, 0x2a, 0xbc, 0xd5, 0x72, 0x1d, 0x87, 0x96, 0xa7, 0xc5, - 0xfc, 0x18, 0xf8, 0x6c, 0x5b, 0xe2, 0xb2, 0xd6, 0x38, 0x7f, 0x95, 0x15, 0x37, 0xa8, 0xb3, 0x75, - 0x13, 0x47, 0x86, 0xe0, 0x81, 0x0c, 0xbd, 0xf2, 0x1c, 0x2b, 0x49, 0x6f, 0x83, 0xa1, 0x6a, 0x61, - 0xf2, 0x0b, 0x83, 0x28, 0x95, 0xe5, 0xdd, 0x22, 0x97, 0x7a, 0xf5, 0x56, 0xf3, 0xf8, 0x02, 0x82, - 0x25, 0x2a, 0x55, 0xc6, 0xde, 0xd4, 0x3b, 0x3a, 0xf8, 0xfa, 0xe9, 0x71, 0x37, 0xe8, 0x8e, 0xe7, - 0x0c, 0x95, 0x12, 0xf6, 0x16, 0xff, 0x0a, 0xa2, 0x0a, 0x37, 0x85, 0x92, 0x15, 0x96, 0xb1, 0x6f, - 0x7e, 0xc2, 0xfb, 0x9f, 0x5c, 0xb9, 0x94, 0xe8, 0x2f, 0x25, 0x7f, 0x30, 0x78, 0xbc, 0x47, 0xc5, - 0x1f, 0x01, 0x7b, 0x6d, 0xaa, 0x0a, 0x04, 0x7b, 0x4d, 0xa8, 0x31, 0x15, 0x05, 0x82, 0x35, 0x84, - 0x76, 0x66, 0x37, 0x81, 0x60, 0x3b, 0x42, 0x77, 0x66, 0x23, 0x81, 0x60, 0x77, 0xfc, 0x33, 0x98, - 0xfc, 0x54, 0xa3, 0xce, 0xb0, 0x8c, 0x03, 0xf3, 0xf2, 0x7b, 0xfd, 0xcb, 0xdf, 0xd7, 0xa8, 0x1b, - 0xd1, 0xe6, 0xa9, 0x53, 0xb3, 0x4d, 0xbb, 0x1a, 0x73, 0xa6, 0x58, 0x45, 0x9b, 0x9f, 0xd8, 0x18, - 0x9d, 0xdd, 0x84, 0xec, 0x3e, 0x46, 0xb3, 0x34, 0xf9, 0x8b, 0xd1, 0x9a, 0x6c, 0xe9, 0x83, 0xf1, - 0x99, 0x24, 0xff, 0x10, 0x42, 0x6a, 0xeb, 0xe5, 0xbd, 0xd4, 0x6e, 0x84, 0x13, 0xc2, 0x37, 0x52, - 0xf3, 0x2f, 0x61, 0x7c, 0x2f, 0x55, 0x8d, 0x0f, 0x8c, 0xb1, 0xa5, 0xbb, 0xa1, 0xbc, 0x70, 0xd7, - 0xba, 0x62, 0xfc, 0x41, 0x31, 0x4f, 0x20, 0x50, 0x72, 0x81, 0xca, 0xe9, 0xcc, 0x02, 0x5a, 0x10, - 0x75, 0xd5, 0x98, 0x5e, 0x1e, 0x64, 0xb6, 0xbd, 0xdb, 0x5b, 0xc9, 0x35, 0x3c, 0xde, 0x7b, 0xb1, - 0x7b, 0x89, 0xed, 0xbf, 0x64, 0xea, 0x70, 0x6d, 0x58, 0x40, 0x12, 0x2d, 0x51, 0xe1, 0xb2, 0xc2, - 0x95, 0x59, 0x41, 0x28, 0x3a, 0x9c, 0xfc, 0xc6, 0x7a, 0x5e, 0xf3, 0x1e, 0x89, 0x70, 0x99, 0x6f, - 0x36, 0x72, 0xbb, 0x72, 0xd4, 0x2d, 0xa4, 0xb9, 0xad, 0x16, 0x8e, 0x7a, 0xb4, 0x5a, 0x10, 0xd6, - 0x85, 0x33, 0xdc, 0x48, 0x17, 0x7c, 0x0a, 0x07, 0x1b, 0x94, 0x65, 0xad, 0x71, 0x83, 0xdb, 0xca, - 0x8d, 0x60, 0x18, 0xe2, 0x4f, 0x61, 0x52, 0xc9, 0xdb, 0x97, 0xaf, 0xb0, 0x71, 0xb3, 0x18, 0x57, - 0xf2, 0xf6, 0x1c, 0x1b, 0xfe, 0x31, 0x44, 0xeb, 0x0c, 0xd5, 0xca, 0xa4, 0xec, 0x72, 0x43, 0x13, - 0x38, 0xc7, 0x26, 0xf9, 0x9d, 0xc1, 0x78, 0x8e, 0xfa, 0x1e, 0xf5, 0x5b, 0x29, 0x7f, 0xe8, 0x7a, - 0xef, 0x7f, 0x5c, 0xef, 0x3f, 0xec, 0xfa, 0xa0, 0x77, 0xfd, 0x13, 0x08, 0xe6, 0x7a, 0x39, 0x4b, - 0x4d, 0x45, 0x9e, 0xb0, 0x80, 0x7f, 0x00, 0xe3, 0x93, 0x65, 0x95, 0xdd, 0xa3, 0xfb, 0x14, 0x38, - 0x94, 0xfc, 0xca, 0x60, 0x7c, 0x21, 0x9b, 0xbc, 0xae, 0xde, 0x50, 0xd8, 0x14, 0x0e, 0x4e, 0x8a, - 0x42, 0x65, 0x4b, 0x59, 0x65, 0xf9, 0xd6, 0x55, 0x3b, 0x0c, 0xd1, 0x8d, 0xe7, 0x83, 0xd9, 0xd9, - 0xba, 0x87, 0x21, 0xfe, 0x09, 0x04, 0x67, 0xc6, 0xd0, 0xd6, 0x9d, 0x87, 0xbd, 0x5e, 0xac, 0x8f, - 0x4d, 0x92, 0x1a, 0x3c, 0xa9, 0xab, 0x7c, 0xad, 0xf2, 0x9d, 0xe9, 0x24, 0x14, 0x1d, 0x4e, 0xfe, - 0x66, 0xe0, 0xbf, 0x2b, 0xa3, 0x3e, 0x02, 0x96, 0xb9, 0x45, 0xb2, 0xac, 0xb3, 0xed, 0x64, 0x60, - 0xdb, 0x18, 0x26, 0x8d, 0x96, 0xdb, 0x5b, 0x2c, 0xe3, 0x70, 0xea, 0x1d, 0x79, 0xa2, 0x85, 0x26, - 0x63, 0x3c, 0x52, 0xc6, 0xd1, 0xd4, 0x23, 0x05, 0x3a, 0xd8, 0x69, 0x1e, 0x7a, 0xcd, 0x27, 0x7f, - 0x32, 0x08, 0x3a, 0xe5, 0x9e, 0xed, 0x2b, 0xf7, 0xac, 0x57, 0x6e, 0x7a, 0xda, 0x2a, 0x37, 0x3d, - 0x25, 0x2c, 0x2e, 0x5b, 0xe5, 0x8a, 0x4b, 0x9a, 0xda, 0x37, 0x3a, 0xaf, 0x8b, 0xd3, 0xc6, 0x8e, - 0x37, 0x12, 0x1d, 0xa6, 0x75, 0xff, 0x70, 0x87, 0xda, 0xf5, 0x1c, 0x09, 0x87, 0x48, 0x1c, 0x17, - 0xc6, 0xd5, 0xb6, 0x4b, 0x0b, 0xf8, 0xa7, 0x10, 0x08, 0xea, 0xc2, 0xb4, 0xba, 0x37, 0x20, 0x13, - 0x16, 0x36, 0x9b, 0x3c, 0x73, 0xd7, 0x88, 0xe5, 0xba, 0x28, 0x50, 0x3b, 0x4d, 0x5b, 0x60, 0xb8, - 0xf3, 0x1d, 0xda, 0xcf, 0x91, 0x27, 0x2c, 0x48, 0x7e, 0x84, 0xe8, 0x44, 0xa1, 0xae, 0x44, 0xad, - 0xde, 0xfc, 0x88, 0x71, 0xf0, 0xbf, 0x9d, 0x7f, 0xf7, 0xa2, 0x75, 0x02, 0x9d, 0x7b, 0xfd, 0x7a, - 0xff, 0xd2, 0xef, 0xb9, 0x2c, 0xe4, 0x2c, 0x35, 0x8b, 0xf5, 0x84, 0x43, 0xc9, 0xe7, 0xe0, 0x93, - 0x4f, 0x06, 0xcc, 0xfe, 0x7f, 0x79, 0x6c, 0x31, 0x36, 0xff, 0xd6, 0xcf, 0xfe, 0x09, 0x00, 0x00, - 0xff, 0xff, 0xa7, 0xc6, 0x53, 0x22, 0xbf, 0x07, 0x00, 0x00, + // 876 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xbc, 0x55, 0xdf, 0x6e, 0xe3, 0xc4, + 0x17, 0xd6, 0xc4, 0x76, 0x62, 0x9f, 0x76, 0xfb, 0xfb, 0x69, 0xb4, 0x62, 0x0d, 0xdc, 0x44, 0x16, + 0x48, 0x01, 0x89, 0x82, 0xd8, 0x27, 0x68, 0x6b, 0x09, 0x85, 0x76, 0x97, 0x32, 0x69, 0xcb, 0x15, + 0x5a, 0x4d, 0x9c, 0x93, 0xc6, 0x5a, 0x27, 0x36, 0x63, 0xbb, 0x59, 0xbf, 0x05, 0x4f, 0x80, 0x84, + 0xc4, 0x15, 0x17, 0x5c, 0xf0, 0x02, 0x3c, 0x04, 0x2f, 0x84, 0xce, 0xcc, 0xf8, 0x4f, 0xd8, 0x82, + 0xf6, 0x8a, 0xbb, 0xf9, 0xce, 0x19, 0x7f, 0xe7, 0xdf, 0x77, 0xc6, 0x70, 0x92, 0xee, 0x2a, 0x54, + 0x3b, 0x99, 0x9d, 0x16, 0x2a, 0xaf, 0x72, 0xee, 0xb7, 0x38, 0xfa, 0x6d, 0x04, 0xe3, 0x45, 0x5e, + 0xab, 0x04, 0xf9, 0x09, 0x8c, 0xe6, 0x71, 0xc8, 0xa6, 0x6c, 0xe6, 0x88, 0xd1, 0x3c, 0xe6, 0x1c, + 0xdc, 0x97, 0x72, 0x8b, 0xe1, 0x68, 0xca, 0x66, 0x81, 0xd0, 0x67, 0xb2, 0xdd, 0x34, 0x05, 0x86, + 0x8e, 0xb1, 0xd1, 0x99, 0x7f, 0x00, 0xfe, 0x6d, 0x49, 0x6c, 0x5b, 0x0c, 0x5d, 0x6d, 0xef, 0x30, + 0xf9, 0xae, 0x65, 0x59, 0xee, 0x73, 0xb5, 0x0a, 0x3d, 0xe3, 0x6b, 0x31, 0xff, 0x3f, 0x38, 0xb7, + 0xe2, 0x2a, 0x1c, 0x6b, 0x33, 0x1d, 0x79, 0x08, 0x93, 0x18, 0xd7, 0xb2, 0xce, 0xaa, 0x70, 0x32, + 0x65, 0x33, 0x5f, 0xb4, 0x90, 0x78, 0x6e, 0x30, 0xc3, 0x7b, 0x25, 0xd7, 0xa1, 0x6f, 0x78, 0x5a, + 0xcc, 0x4f, 0x81, 0xcf, 0x77, 0x25, 0x26, 0xb5, 0xc2, 0xc5, 0xeb, 0xb4, 0xb8, 0x43, 0x95, 0xae, + 0x9b, 0x30, 0xd0, 0x04, 0x8f, 0x78, 0x28, 0xca, 0x0b, 0xac, 0x24, 0xc5, 0x06, 0x4d, 0xd5, 0x42, + 0x1e, 0xc1, 0xf1, 0x62, 0x23, 0x15, 0xae, 0x16, 0x98, 0x28, 0xac, 0xc2, 0x23, 0xed, 0x3e, 0xb0, + 0x45, 0x3f, 0x32, 0x08, 0x62, 0x59, 0x6e, 0x96, 0xb9, 0x54, 0xab, 0x77, 0xea, 0xd9, 0x67, 0xe0, + 0x25, 0x98, 0x65, 0x65, 0xe8, 0x4c, 0x9d, 0xd9, 0xd1, 0x97, 0xcf, 0x4e, 0xbb, 0x61, 0x74, 0x3c, + 0x17, 0x98, 0x65, 0xc2, 0xdc, 0xe2, 0x5f, 0x40, 0x50, 0xe1, 0xb6, 0xc8, 0x64, 0x85, 0x65, 0xe8, + 0xea, 0x4f, 0x78, 0xff, 0xc9, 0x8d, 0x75, 0x89, 0xfe, 0x52, 0xf4, 0x2b, 0x83, 0x27, 0x07, 0x54, + 0xfc, 0x18, 0xd8, 0x1b, 0x9d, 0x95, 0x27, 0xd8, 0x1b, 0x42, 0x8d, 0xce, 0xc8, 0x13, 0xac, 0x21, + 0xb4, 0xd7, 0xf3, 0xf3, 0x04, 0xdb, 0x13, 0xda, 0xe8, 0xa9, 0x79, 0x82, 0x6d, 0xf8, 0x27, 0x30, + 0xf9, 0xa1, 0x46, 0x95, 0x62, 0x19, 0x7a, 0x3a, 0xf2, 0xff, 0xfa, 0xc8, 0xdf, 0xd6, 0xa8, 0x1a, + 0xd1, 0xfa, 0xa9, 0x52, 0x3d, 0x71, 0x33, 0x3e, 0x7d, 0x26, 0x5b, 0x45, 0xea, 0x98, 0x18, 0x1b, + 0x9d, 0x6d, 0x87, 0xcc, 0xcc, 0x46, 0xf3, 0x38, 0xfa, 0x83, 0xd1, 0x28, 0x4d, 0xea, 0x83, 0xf6, + 0x69, 0x27, 0x7f, 0x1f, 0x7c, 0x2a, 0xeb, 0xd5, 0x83, 0x54, 0xb6, 0x85, 0x13, 0xc2, 0x77, 0x52, + 0xf1, 0xcf, 0x61, 0xfc, 0x20, 0xb3, 0x1a, 0x1f, 0x69, 0x63, 0x4b, 0x77, 0x47, 0x7e, 0x61, 0xaf, + 0x75, 0xc9, 0xb8, 0x83, 0x64, 0x9e, 0x82, 0x97, 0xc9, 0x25, 0x66, 0x56, 0x8b, 0x06, 0xd0, 0x80, + 0xa8, 0xaa, 0x46, 0xd7, 0xf2, 0x28, 0xb3, 0xa9, 0xdd, 0xdc, 0x8a, 0x6e, 0xe1, 0xc9, 0x41, 0xc4, + 0x2e, 0x12, 0x3b, 0x8c, 0xa4, 0xf3, 0xb0, 0x65, 0x18, 0x40, 0x32, 0x2e, 0x31, 0xc3, 0xa4, 0xc2, + 0x95, 0x1e, 0x81, 0x2f, 0x3a, 0x1c, 0xfd, 0xcc, 0x7a, 0x5e, 0x1d, 0x8f, 0x84, 0x9a, 0xe4, 0xdb, + 0xad, 0xdc, 0xad, 0x2c, 0x75, 0x0b, 0xa9, 0x6f, 0xab, 0xa5, 0xa5, 0x1e, 0xad, 0x96, 0x84, 0x55, + 0x61, 0x97, 0x72, 0xa4, 0x0a, 0x3e, 0x85, 0xa3, 0x2d, 0xca, 0xb2, 0x56, 0xb8, 0xc5, 0x5d, 0x65, + 0x5b, 0x30, 0x34, 0xf1, 0x67, 0x30, 0xa9, 0xe4, 0xfd, 0xab, 0xd7, 0xd8, 0xd8, 0x5e, 0x8c, 0x2b, + 0x79, 0x7f, 0x89, 0x0d, 0xff, 0x10, 0x82, 0x75, 0x8a, 0xd9, 0x4a, 0xbb, 0xcc, 0x70, 0x7d, 0x6d, + 0xb8, 0xc4, 0x26, 0xfa, 0x85, 0xc1, 0x78, 0x81, 0xea, 0x01, 0xd5, 0x3b, 0x29, 0x7f, 0xf8, 0x32, + 0x38, 0xff, 0xf2, 0x32, 0xb8, 0x8f, 0xbf, 0x0c, 0x5e, 0xff, 0x32, 0x3c, 0x05, 0x6f, 0xa1, 0x92, + 0x79, 0xac, 0x33, 0x72, 0x84, 0x01, 0xfc, 0x3d, 0x18, 0x9f, 0x25, 0x55, 0xfa, 0x80, 0xf6, 0xb9, + 0xb0, 0x28, 0xfa, 0x89, 0xc1, 0xf8, 0x4a, 0x36, 0x79, 0x5d, 0xbd, 0xa5, 0xb0, 0x29, 0x1c, 0x9d, + 0x15, 0x45, 0x96, 0x26, 0xb2, 0x4a, 0xf3, 0x9d, 0xcd, 0x76, 0x68, 0xa2, 0x1b, 0x2f, 0x06, 0xbd, + 0x33, 0x79, 0x0f, 0x4d, 0xfc, 0x23, 0xf0, 0x2e, 0xf4, 0x42, 0x9b, 0xed, 0x3c, 0xe9, 0xf5, 0x62, + 0xf6, 0x58, 0x3b, 0xa9, 0xc0, 0xb3, 0xba, 0xca, 0xd7, 0x59, 0xbe, 0xd7, 0x95, 0xf8, 0xa2, 0xc3, + 0xd1, 0x9f, 0x0c, 0xdc, 0xff, 0x6a, 0x51, 0x8f, 0x81, 0xa5, 0x76, 0x90, 0x2c, 0xed, 0xd6, 0x76, + 0x32, 0x58, 0xdb, 0x10, 0x26, 0x8d, 0x92, 0xbb, 0x7b, 0x2c, 0x43, 0x7f, 0xea, 0xcc, 0x1c, 0xd1, + 0x42, 0xed, 0xd1, 0x3b, 0x52, 0x86, 0xc1, 0xd4, 0x21, 0x05, 0x5a, 0xd8, 0x69, 0x1e, 0x7a, 0xcd, + 0x47, 0xbf, 0x33, 0xf0, 0x3a, 0xe5, 0x5e, 0x1c, 0x2a, 0xf7, 0xa2, 0x57, 0x6e, 0x7c, 0xde, 0x2a, + 0x37, 0x3e, 0x27, 0x2c, 0xae, 0x5b, 0xe5, 0x8a, 0x6b, 0xea, 0xda, 0x57, 0x2a, 0xaf, 0x8b, 0xf3, + 0xc6, 0xb4, 0x37, 0x10, 0x1d, 0xa6, 0x71, 0x7f, 0xb7, 0x41, 0x65, 0x6b, 0x0e, 0x84, 0x45, 0x24, + 0x8e, 0x2b, 0xbd, 0xd5, 0xa6, 0x4a, 0x03, 0xf8, 0xc7, 0xe0, 0x09, 0xaa, 0x42, 0x97, 0x7a, 0xd0, + 0x20, 0x6d, 0x16, 0xc6, 0x1b, 0x3d, 0xb7, 0xd7, 0x88, 0xe5, 0xb6, 0x28, 0x50, 0x59, 0x4d, 0x1b, + 0xa0, 0xb9, 0xf3, 0x3d, 0x9a, 0xe7, 0xc8, 0x11, 0x06, 0x44, 0xdf, 0x43, 0x70, 0x96, 0xa1, 0xaa, + 0x44, 0x9d, 0xbd, 0xfd, 0x88, 0x71, 0x70, 0xbf, 0x5e, 0x7c, 0xf3, 0xb2, 0xdd, 0x04, 0x3a, 0xf7, + 0xfa, 0x75, 0xfe, 0xa6, 0xdf, 0x4b, 0x59, 0xc8, 0x79, 0xac, 0x07, 0xeb, 0x08, 0x8b, 0xa2, 0x4f, + 0xc1, 0xa5, 0x3d, 0x19, 0x30, 0xbb, 0xff, 0xb4, 0x63, 0xcb, 0xb1, 0xfe, 0xa3, 0x3f, 0xff, 0x2b, + 0x00, 0x00, 0xff, 0xff, 0x0f, 0x40, 0x84, 0xea, 0xe3, 0x07, 0x00, 0x00, } diff --git a/bolt/internal/internal.proto b/bolt/internal/internal.proto index 40a305718e..2ed54af572 100644 --- a/bolt/internal/internal.proto +++ b/bolt/internal/internal.proto @@ -2,23 +2,24 @@ syntax = "proto3"; package internal; message Source { - int64 ID = 1; // ID is the unique ID of the source - string Name = 2; // Name is the user-defined name for the source - string Type = 3; // Type specifies which kinds of source (enterprise vs oss) - string Username = 4; // Username is the username to connect to the source + int64 ID = 1; // ID is the unique ID of the source + string Name = 2; // Name is the user-defined name for the source + string Type = 3; // Type specifies which kinds of source (enterprise vs oss) + string Username = 4; // Username is the username to connect to the source string Password = 5; - string URL = 6; // URL are the connections to the source - bool Default = 7; // Flags an source as the default. - string Telegraf = 8; // Telegraf is the db telegraf is written to. By default it is "telegraf" - bool InsecureSkipVerify = 9; // InsecureSkipVerify accepts any certificate from the influx server + string URL = 6; // URL are the connections to the source + bool Default = 7; // Flags an source as the default. + string Telegraf = 8; // Telegraf is the db telegraf is written to. By default it is "telegraf" + bool InsecureSkipVerify = 9; // InsecureSkipVerify accepts any certificate from the influx server string MetaURL = 10; // MetaURL is the connection URL for the meta node. + string SharedSecret = 11; // SharedSecret signs the optional InfluxDB JWT Authorization } message Dashboard { int64 ID = 1; // ID is the unique ID of the dashboard string Name = 2; // Name is the user-defined name of the dashboard repeated DashboardCell cells = 3; // a representation of all visual data required for rendering the dashboard - repeated Template templates = 4; // Templates replace template variables within InfluxQL + repeated Template templates = 4; // Templates replace template variables within InfluxQL } message DashboardCell { diff --git a/bolt/internal/internal_test.go b/bolt/internal/internal_test.go index 77efbb9d9a..2d6966036c 100644 --- a/bolt/internal/internal_test.go +++ b/bolt/internal/internal_test.go @@ -40,6 +40,38 @@ func TestMarshalSource(t *testing.T) { t.Fatalf("source protobuf copy error: got %#v, expected %#v", vv, v) } } +func TestMarshalSourceWithSecret(t *testing.T) { + v := chronograf.Source{ + ID: 12, + Name: "Fountain of Truth", + Type: "influx", + Username: "docbrown", + SharedSecret: "hunter2s", + URL: "http://twin-pines.mall.io:8086", + MetaURL: "http://twin-pines.meta.io:8086", + Default: true, + Telegraf: "telegraf", + } + + var vv chronograf.Source + if buf, err := internal.MarshalSource(v); err != nil { + t.Fatal(err) + } else if err := internal.UnmarshalSource(buf, &vv); err != nil { + t.Fatal(err) + } else if !reflect.DeepEqual(v, vv) { + t.Fatalf("source protobuf copy error: got %#v, expected %#v", vv, v) + } + + // Test if the new insecureskipverify works + v.InsecureSkipVerify = true + if buf, err := internal.MarshalSource(v); err != nil { + t.Fatal(err) + } else if err := internal.UnmarshalSource(buf, &vv); err != nil { + t.Fatal(err) + } else if !reflect.DeepEqual(v, vv) { + t.Fatalf("source protobuf copy error: got %#v, expected %#v", vv, v) + } +} func TestMarshalServer(t *testing.T) { v := chronograf.Server{ diff --git a/chronograf.go b/chronograf.go index 233351684c..868b0aa412 100644 --- a/chronograf.go +++ b/chronograf.go @@ -346,6 +346,7 @@ type Source struct { Type string `json:"type,omitempty"` // Type specifies which kinds of source (enterprise vs oss) Username string `json:"username,omitempty"` // Username is the username to connect to the source Password string `json:"password,omitempty"` // Password is in CLEARTEXT + SharedSecret string `json:"sharedSecret,omitempty"` // ShareSecret is the optional signing secret for Influx JWT authorization URL string `json:"url"` // URL are the connections to the source MetaURL string `json:"metaUrl,omitempty"` // MetaURL is the url for the meta node InsecureSkipVerify bool `json:"insecureSkipVerify,omitempty"` // InsecureSkipVerify as true means any certificate presented by the source is accepted. @@ -625,44 +626,3 @@ type LayoutStore interface { // Update the dashboard in the store. Update(context.Context, Layout) error } - -// SourceAndKapacitor is used to parse any NewSources server flag arguments -type SourceAndKapacitor struct { - Source Source `json:"influxdb"` - Kapacitor Server `json:"kapacitor"` -} - -// NewSources adds sources to BoltDb idempotently by name, as well as respective kapacitors -func NewSources(ctx context.Context, sourcesStore SourcesStore, serversStore ServersStore, srcsKaps []SourceAndKapacitor, logger Logger) error { - srcs, err := sourcesStore.All(ctx) - if err != nil { - return err - } - -SourceLoop: - for _, srcKap := range srcsKaps { - for _, src := range srcs { - // If source already exists, do nothing - if src.Name == srcKap.Source.Name { - logger. - WithField("component", "server"). - WithField("NewSources", src.Name). - Info("Source already exists") - continue SourceLoop - } - } - - src, err := sourcesStore.Add(ctx, srcKap.Source) - if err != nil { - return err - } - - srcKap.Kapacitor.SrcID = src.ID - _, err = serversStore.Add(ctx, srcKap.Kapacitor) - if err != nil { - return err - } - } - - return nil -} diff --git a/chronograf_test.go b/chronograf_test.go deleted file mode 100644 index bed0c4f40d..0000000000 --- a/chronograf_test.go +++ /dev/null @@ -1,88 +0,0 @@ -package chronograf_test - -import ( - "context" - "reflect" - "testing" - - "github.com/influxdata/chronograf" - "github.com/influxdata/chronograf/mocks" -) - -func Test_NewSources(t *testing.T) { - t.Parallel() - - srcsKaps := []chronograf.SourceAndKapacitor{ - { - Source: chronograf.Source{ - Default: true, - InsecureSkipVerify: false, - MetaURL: "http://metaurl.com", - Name: "Influx 1", - Password: "pass1", - Telegraf: "telegraf", - URL: "http://localhost:8086", - Username: "user1", - }, - Kapacitor: chronograf.Server{ - Active: true, - Name: "Kapa 1", - URL: "http://localhost:9092", - }, - }, - } - saboteurSrcsKaps := []chronograf.SourceAndKapacitor{ - { - Source: chronograf.Source{ - Name: "Influx 1", - }, - Kapacitor: chronograf.Server{ - Name: "Kapa Aspiring Saboteur", - }, - }, - } - - ctx := context.Background() - srcs := []chronograf.Source{} - srcsStore := mocks.SourcesStore{ - AllF: func(ctx context.Context) ([]chronograf.Source, error) { - return srcs, nil - }, - AddF: func(ctx context.Context, src chronograf.Source) (chronograf.Source, error) { - srcs = append(srcs, src) - return src, nil - }, - } - srvs := []chronograf.Server{} - srvsStore := mocks.ServersStore{ - AddF: func(ctx context.Context, srv chronograf.Server) (chronograf.Server, error) { - srvs = append(srvs, srv) - return srv, nil - }, - } - - err := chronograf.NewSources(ctx, &srcsStore, &srvsStore, srcsKaps, &mocks.TestLogger{}) - if err != nil { - t.Fatal("Expected no error when creating New Sources. Error:", err) - } - if len(srcs) != 1 { - t.Error("Expected one source in sourcesStore") - } - if len(srvs) != 1 { - t.Error("Expected one source in serversStore") - } - - err = chronograf.NewSources(ctx, &srcsStore, &srvsStore, saboteurSrcsKaps, &mocks.TestLogger{}) - if err != nil { - t.Fatal("Expected no error when creating New Sources. Error:", err) - } - if len(srcs) != 1 { - t.Error("Expected one source in sourcesStore") - } - if len(srvs) != 1 { - t.Error("Expected one source in serversStore") - } - if !reflect.DeepEqual(srcs[0], srcsKaps[0].Source) { - t.Error("Expected source in sourceStore to remain unchanged") - } -} diff --git a/enterprise/enterprise.go b/enterprise/enterprise.go index 189e5a1507..7776b89de1 100644 --- a/enterprise/enterprise.go +++ b/enterprise/enterprise.go @@ -120,13 +120,17 @@ func (c *Client) Connect(ctx context.Context, src *chronograf.Source) error { c.dataNodes = ring.New(len(cluster.DataNodes)) for _, dn := range cluster.DataNodes { - cl, err := influx.NewClient(dn.HTTPAddr, c.Logger) - if err != nil { - continue - } else { - c.dataNodes.Value = cl - c.dataNodes = c.dataNodes.Next() + cl := &influx.Client{ + Logger: c.Logger, } + dataSrc := &chronograf.Source{} + *dataSrc = *src + dataSrc.URL = dn.HTTPAddr + if err := cl.Connect(ctx, dataSrc); err != nil { + continue + } + c.dataNodes.Value = cl + c.dataNodes = c.dataNodes.Next() } return nil } diff --git a/influx/influx.go b/influx/influx.go index a66176dee6..c1f0baef9e 100644 --- a/influx/influx.go +++ b/influx/influx.go @@ -28,25 +28,9 @@ var ( // Client is a device for retrieving time series data from an InfluxDB instance type Client struct { URL *url.URL + Bearer Bearer InsecureSkipVerify bool - - Logger chronograf.Logger -} - -// NewClient initializes an HTTP Client for InfluxDB. UDP, although supported -// for querying InfluxDB, is not supported here to remove the need to -// explicitly Close the client. -func NewClient(host string, lg chronograf.Logger) (*Client, error) { - l := lg.WithField("host", host) - u, err := url.Parse(host) - if err != nil { - l.Error("Error initialize influx client: err:", err) - return nil, err - } - return &Client{ - URL: u, - Logger: l, - }, nil + Logger chronograf.Logger } // Response is a partial JSON decoded InfluxQL response used @@ -88,6 +72,15 @@ func (c *Client) query(u *url.URL, q chronograf.Query) (chronograf.Response, err params.Set("epoch", "ms") // TODO(timraymond): set this based on analysis req.URL.RawQuery = params.Encode() + if c.Bearer != nil && u.User != nil { + token, err := c.Bearer.Token(u.User.Username()) + if err != nil { + logs.Error("Error creating token", err) + return nil, fmt.Errorf("Unable to create token") + } + req.Header.Set("Authorization", "Bearer "+token) + } + hc := &http.Client{} if c.InsecureSkipVerify { hc.Transport = skipVerifyTransport @@ -157,7 +150,7 @@ func (c *Client) Query(ctx context.Context, q chronograf.Query) (chronograf.Resp } } -// Connect caches the URL for the data source +// Connect caches the URL and optional Bearer Authorization for the data source func (c *Client) Connect(ctx context.Context, src *chronograf.Source) error { u, err := url.Parse(src.URL) if err != nil { @@ -169,6 +162,16 @@ func (c *Client) Connect(ctx context.Context, src *chronograf.Source) error { c.InsecureSkipVerify = src.InsecureSkipVerify } c.URL = u + + // Optionally, add the shared secret JWT token creation + if src.Username != "" && src.SharedSecret != "" { + c.Bearer = &BearerJWT{ + src.SharedSecret, + } + } else { + // Clear out the bearer if not needed + c.Bearer = nil + } return nil } diff --git a/influx/influx_test.go b/influx/influx_test.go index 99cdc76ef2..3218681a18 100644 --- a/influx/influx_test.go +++ b/influx/influx_test.go @@ -2,16 +2,34 @@ package influx_test import ( "context" + "fmt" "net/http" "net/http/httptest" + "net/url" + "strings" "testing" "time" + gojwt "github.com/dgrijalva/jwt-go" "github.com/influxdata/chronograf" "github.com/influxdata/chronograf/influx" "github.com/influxdata/chronograf/log" ) +// NewClient initializes an HTTP Client for InfluxDB. +func NewClient(host string, lg chronograf.Logger) (*influx.Client, error) { + l := lg.WithField("host", host) + u, err := url.Parse(host) + if err != nil { + l.Error("Error initialize influx client: err:", err) + return nil, err + } + return &influx.Client{ + URL: u, + Logger: l, + }, nil +} + func Test_Influx_MakesRequestsToQueryEndpoint(t *testing.T) { t.Parallel() called := false @@ -26,7 +44,7 @@ func Test_Influx_MakesRequestsToQueryEndpoint(t *testing.T) { defer ts.Close() var series chronograf.TimeSeries - series, err := influx.NewClient(ts.URL, log.New(log.DebugLevel)) + series, err := NewClient(ts.URL, log.New(log.DebugLevel)) if err != nil { t.Fatal("Unexpected error initializing client: err:", err) } @@ -44,6 +62,126 @@ func Test_Influx_MakesRequestsToQueryEndpoint(t *testing.T) { } } +type MockBearer struct { + Bearer string + Error error +} + +func (m *MockBearer) Token(username string) (string, error) { + return m.Bearer, m.Error +} +func Test_Influx_AuthorizationBearer(t *testing.T) { + t.Parallel() + want := "Bearer ********" + ts := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { + rw.WriteHeader(http.StatusOK) + rw.Write([]byte(`{}`)) + got := r.Header.Get("Authorization") + if got != want { + t.Errorf("Test_Influx_AuthorizationBearer got %s want %s", got, want) + } + })) + defer ts.Close() + + bearer := &MockBearer{ + Bearer: "********", + } + + u, _ := url.Parse(ts.URL) + u.User = url.UserPassword("AzureDiamond", "hunter2") + series := &influx.Client{ + URL: u, + Bearer: bearer, + Logger: log.New(log.DebugLevel), + } + + query := chronograf.Query{ + Command: "show databases", + } + _, err := series.Query(context.Background(), query) + if err != nil { + t.Fatal("Expected no error but was", err) + } +} + +func Test_Influx_AuthorizationBearerCtx(t *testing.T) { + t.Parallel() + ts := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { + rw.WriteHeader(http.StatusOK) + rw.Write([]byte(`{}`)) + got := r.Header.Get("Authorization") + if got == "" { + t.Error("Test_Influx_AuthorizationBearerCtx got empty string") + } + incomingToken := strings.Split(got, " ")[1] + + alg := func(token *gojwt.Token) (interface{}, error) { + if _, ok := token.Method.(*gojwt.SigningMethodHMAC); !ok { + return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"]) + } + return []byte("hunter2"), nil + } + claims := &gojwt.MapClaims{} + token, err := gojwt.ParseWithClaims(string(incomingToken), claims, alg) + if err != nil { + t.Errorf("Test_Influx_AuthorizationBearerCtx unexpected claims error %v", err) + } + if !token.Valid { + t.Error("Test_Influx_AuthorizationBearerCtx unexpected valid claim") + } + if err := claims.Valid(); err != nil { + t.Errorf("Test_Influx_AuthorizationBearerCtx not expires already %v", err) + } + user := (*claims)["username"].(string) + if user != "AzureDiamond" { + t.Errorf("Test_Influx_AuthorizationBearerCtx expected username AzureDiamond but got %s", user) + } + })) + defer ts.Close() + + series := &influx.Client{ + Logger: log.New(log.DebugLevel), + } + + err := series.Connect(context.Background(), &chronograf.Source{ + Username: "AzureDiamond", + SharedSecret: "hunter2", + URL: ts.URL, + InsecureSkipVerify: true, + }) + + query := chronograf.Query{ + Command: "show databases", + } + _, err = series.Query(context.Background(), query) + if err != nil { + t.Fatal("Expected no error but was", err) + } +} + +func Test_Influx_AuthorizationBearerFailure(t *testing.T) { + t.Parallel() + bearer := &MockBearer{ + Error: fmt.Errorf("cracked1337"), + } + + u, _ := url.Parse("http://haxored.net") + u.User = url.UserPassword("AzureDiamond", "hunter2") + series := &influx.Client{ + URL: u, + Bearer: bearer, + Logger: log.New(log.DebugLevel), + } + + query := chronograf.Query{ + Command: "show databases", + } + _, err := series.Query(context.Background(), query) + if err == nil { + t.Fatal("Test_Influx_AuthorizationBearerFailure Expected error but received nil") + } +} + func Test_Influx_HTTPS_Failure(t *testing.T) { called := false ts := httptest.NewTLSServer(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { @@ -53,7 +191,7 @@ func Test_Influx_HTTPS_Failure(t *testing.T) { ctx := context.Background() var series chronograf.TimeSeries - series, err := influx.NewClient(ts.URL, log.New(log.DebugLevel)) + series, err := NewClient(ts.URL, log.New(log.DebugLevel)) if err != nil { t.Fatal("Unexpected error initializing client: err:", err) } @@ -97,7 +235,7 @@ func Test_Influx_HTTPS_InsecureSkipVerify(t *testing.T) { ctx := context.Background() var series chronograf.TimeSeries - series, err := influx.NewClient(ts.URL, log.New(log.DebugLevel)) + series, err := NewClient(ts.URL, log.New(log.DebugLevel)) if err != nil { t.Fatal("Unexpected error initializing client: err:", err) } @@ -166,7 +304,7 @@ func Test_Influx_CancelsInFlightRequests(t *testing.T) { ts.Close() }() - series, _ := influx.NewClient(ts.URL, log.New(log.DebugLevel)) + series, _ := NewClient(ts.URL, log.New(log.DebugLevel)) ctx, cancel := context.WithCancel(context.Background()) errs := make(chan (error)) @@ -209,7 +347,7 @@ func Test_Influx_CancelsInFlightRequests(t *testing.T) { } func Test_Influx_RejectsInvalidHosts(t *testing.T) { - _, err := influx.NewClient(":", log.New(log.DebugLevel)) + _, err := NewClient(":", log.New(log.DebugLevel)) if err == nil { t.Fatal("Expected err but was nil") } @@ -221,7 +359,7 @@ func Test_Influx_ReportsInfluxErrs(t *testing.T) { })) defer ts.Close() - cl, err := influx.NewClient(ts.URL, log.New(log.DebugLevel)) + cl, err := NewClient(ts.URL, log.New(log.DebugLevel)) if err != nil { t.Fatal("Encountered unexpected error while initializing influx client: err:", err) } diff --git a/influx/jwt.go b/influx/jwt.go new file mode 100644 index 0000000000..7d89b21392 --- /dev/null +++ b/influx/jwt.go @@ -0,0 +1,41 @@ +package influx + +import ( + "time" + + jwt "github.com/dgrijalva/jwt-go" +) + +// Bearer generates tokens for Authorization: Bearer +type Bearer interface { + Token(username string) (string, error) +} + +// BearerJWT is the default Bearer for InfluxDB +type BearerJWT struct { + SharedSecret string +} + +// Token returns the expected InfluxDB JWT signed with the sharedSecret +func (b *BearerJWT) Token(username string) (string, error) { + return JWT(username, b.SharedSecret, time.Now) +} + +// Now returns the current time +type Now func() time.Time + +// JWT returns a token string accepted by InfluxDB using the sharedSecret as an Authorization: Bearer header +func JWT(username, sharedSecret string, now Now) (string, error) { + token := &jwt.Token{ + Header: map[string]interface{}{ + "typ": "JWT", + "alg": jwt.SigningMethodHS512.Alg(), + }, + Claims: jwt.MapClaims{ + "username": username, + "exp": now().Add(time.Minute).Unix(), + }, + Method: jwt.SigningMethodHS512, + } + return token.SignedString([]byte(sharedSecret)) +} diff --git a/influx/jwt_test.go b/influx/jwt_test.go new file mode 100644 index 0000000000..a3553a50b0 --- /dev/null +++ b/influx/jwt_test.go @@ -0,0 +1,44 @@ +package influx + +import ( + "testing" + "time" +) + +func TestJWT(t *testing.T) { + type args struct { + username string + sharedSecret string + now Now + } + tests := []struct { + name string + args args + want string + wantErr bool + }{ + { + name: "", + args: args{ + username: "AzureDiamond", + sharedSecret: "hunter2", + now: func() time.Time { + return time.Unix(0, 0) + }, + }, + want: "eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJleHAiOjYwLCJ1c2VybmFtZSI6IkF6dXJlRGlhbW9uZCJ9.kUWGwcpCPwV7MEk7luO1rt8036LyvG4bRL_CfseQGmz4b0S34gATx30g4xvqVAV6bwwYE0YU3P8FjG8ij4kc5g", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := JWT(tt.args.username, tt.args.sharedSecret, tt.args.now) + if (err != nil) != tt.wantErr { + t.Errorf("JWT() error = %v, wantErr %v", err, tt.wantErr) + return + } + if got != tt.want { + t.Errorf("JWT() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/server/server.go b/server/server.go index 39e1fcfc07..ae0d51c693 100644 --- a/server/server.go +++ b/server/server.go @@ -3,7 +3,6 @@ package server import ( "context" "crypto/tls" - "encoding/json" "log" "math/rand" "net" @@ -51,7 +50,7 @@ type Server struct { KapacitorUsername string `long:"kapacitor-username" description:"Username of your Kapacitor instance" env:"KAPACITOR_USERNAME"` KapacitorPassword string `long:"kapacitor-password" description:"Password of your Kapacitor instance" env:"KAPACITOR_PASSWORD"` - NewSources string `long:"new-sources" description:"Config for adding a new InfluxDb source and Kapacitor server, in JSON as an array of objects, and surrounded by single quotes. E.g. --new-sources='[{\"influxdb\":{\"name\":\"Influx 1\",\"username\":\"user1\",\"password\":\"pass1\",\"url\":\"http://localhost:8086\",\"metaUrl\":\"http://metaurl.com\",\"insecureSkipVerify\":false,\"default\":true,\"telegraf\":\"telegraf\"},\"kapacitor\":{\"name\":\"Kapa 1\",\"url\":\"http://localhost:9092\",\"active\":true}}]'" env:"NEW_SOURCES"` + NewSources string `long:"new-sources" description:"Config for adding a new InfluxDB source and Kapacitor server, in JSON as an array of objects, and surrounded by single quotes. E.g. --new-sources='[{\"influxdb\":{\"name\":\"Influx 1\",\"username\":\"user1\",\"password\":\"pass1\",\"url\":\"http://localhost:8086\",\"metaUrl\":\"http://metaurl.com\",\"insecureSkipVerify\":false,\"default\":true,\"telegraf\":\"telegraf\",\"sharedSecret\":\"hunter2\"},\"kapacitor\":{\"name\":\"Kapa 1\",\"url\":\"http://localhost:9092\",\"active\":true}}]'" env:"NEW_SOURCES" hidden:"true"` Develop bool `short:"d" long:"develop" description:"Run server in develop mode."` BoltPath string `short:"b" long:"bolt-path" description:"Full path to boltDB file (/var/lib/chronograf/chronograf-v1.db)" env:"BOLT_PATH" default:"chronograf-v1.db"` @@ -301,8 +300,13 @@ func (s *Server) Serve(ctx context.Context) error { return err } service := openService(ctx, s.BoltPath, layoutBuilder, sourcesBuilder, kapacitorBuilder, logger, s.useAuth()) - - go processNewSources(ctx, service, s.NewSources, logger) + if err := service.HandleNewSources(ctx, s.NewSources); err != nil { + logger. + WithField("component", "server"). + WithField("new-sources", "invalid"). + Error(err) + return err + } basepath = s.Basepath if basepath != "" && s.PrefixRoutes == false { @@ -437,33 +441,6 @@ func openService(ctx context.Context, boltPath string, lBuilder LayoutBuilder, s } } -// processNewSources parses and persists new sources passed in via server flag -func processNewSources(ctx context.Context, service Service, newSources string, logger chronograf.Logger) error { - if newSources == "" { - return nil - } - - var srcsKaps []chronograf.SourceAndKapacitor - // On JSON unmarshal error, continue server process without new source and write error to log - if err := json.Unmarshal([]byte(newSources), &srcsKaps); err != nil { - logger. - WithField("component", "server"). - WithField("NewSources", "invalid"). - Error(err) - } - - // Add any new sources and kapacitors as specified via server flag - if err := chronograf.NewSources(ctx, service.SourcesStore, service.ServersStore, srcsKaps, logger); err != nil { - // Continue with server run even if adding NewSources fails - logger. - WithField("component", "server"). - WithField("NewSources", "invalid"). - Error(err) - } - - return nil -} - // reportUsageStats starts periodic server reporting. func reportUsageStats(bi BuildInfo, logger chronograf.Logger) { rand.Seed(time.Now().UTC().UnixNano()) diff --git a/server/service.go b/server/service.go index be027a5546..50bb32d0c0 100644 --- a/server/service.go +++ b/server/service.go @@ -43,18 +43,15 @@ type InfluxClient struct{} // New creates a client to connect to OSS or enterprise func (c *InfluxClient) New(src chronograf.Source, logger chronograf.Logger) (chronograf.TimeSeries, error) { - if src.Type == chronograf.InfluxEnterprise && src.MetaURL != "" { - dataNode := &influx.Client{ - Logger: logger, - } - if err := dataNode.Connect(context.TODO(), &src); err != nil { - return nil, err - } - - tls := strings.Contains(src.MetaURL, "https") - return enterprise.NewClientWithTimeSeries(logger, src.MetaURL, src.Username, src.Password, tls, dataNode) - } - return &influx.Client{ + client := &influx.Client{ Logger: logger, - }, nil + } + if err := client.Connect(context.TODO(), &src); err != nil { + return nil, err + } + if src.Type == chronograf.InfluxEnterprise && src.MetaURL != "" { + tls := strings.Contains(src.MetaURL, "https") + return enterprise.NewClientWithTimeSeries(logger, src.MetaURL, src.Username, src.Password, tls, client) + } + return client, nil } diff --git a/server/sources.go b/server/sources.go index a70bb3df95..9b784818bf 100644 --- a/server/sources.go +++ b/server/sources.go @@ -34,8 +34,9 @@ func newSourceResponse(src chronograf.Source) sourceResponse { src.Telegraf = "telegraf" } - // Omit the password on response + // Omit the password and shared secret on response src.Password = "" + src.SharedSecret = "" httpAPISrcs := "/chronograf/v1/sources" res := sourceResponse{ @@ -99,6 +100,7 @@ func (h *Service) tsdbType(ctx context.Context, src *chronograf.Source) (string, cli := &influx.Client{ Logger: h.Logger, } + if err := cli.Connect(ctx, src); err != nil { return "", err } @@ -293,3 +295,69 @@ func ValidSourceRequest(s chronograf.Source) error { } return nil } + +// HandleNewSources parses and persists new sources passed in via server flag +func (h *Service) HandleNewSources(ctx context.Context, input string) error { + if input == "" { + return nil + } + + var srcsKaps []struct { + Source chronograf.Source `json:"influxdb"` + Kapacitor chronograf.Server `json:"kapacitor"` + } + if err := json.Unmarshal([]byte(input), &srcsKaps); err != nil { + h.Logger. + WithField("component", "server"). + WithField("NewSources", "invalid"). + Error(err) + return err + } + + for _, sk := range srcsKaps { + if err := ValidSourceRequest(sk.Source); err != nil { + return err + } + // Add any new sources and kapacitors as specified via server flag + if err := h.newSourceKapacitor(ctx, sk.Source, sk.Kapacitor); err != nil { + // Continue with server run even if adding NewSource fails + h.Logger. + WithField("component", "server"). + WithField("NewSource", "invalid"). + Error(err) + return err + } + } + return nil +} + +// newSourceKapacitor adds sources to BoltDB idempotently by name, as well as respective kapacitors +func (h *Service) newSourceKapacitor(ctx context.Context, src chronograf.Source, kapa chronograf.Server) error { + srcs, err := h.SourcesStore.All(ctx) + if err != nil { + return err + } + + for _, s := range srcs { + // If source already exists, do nothing + if s.Name == src.Name { + h.Logger. + WithField("component", "server"). + WithField("NewSource", s.Name). + Info("Source already exists") + return nil + } + } + + src, err = h.SourcesStore.Add(ctx, src) + if err != nil { + return err + } + + kapa.SrcID = src.ID + if _, err := h.ServersStore.Add(ctx, kapa); err != nil { + return err + } + + return nil +} diff --git a/server/sources_test.go b/server/sources_test.go index 02f426a912..b85524e1a8 100644 --- a/server/sources_test.go +++ b/server/sources_test.go @@ -1,10 +1,13 @@ package server import ( + "context" + "fmt" "reflect" "testing" "github.com/influxdata/chronograf" + "github.com/influxdata/chronograf/mocks" ) func Test_newSourceResponse(t *testing.T) { @@ -66,3 +69,180 @@ func Test_newSourceResponse(t *testing.T) { } } } + +func TestService_newSourceKapacitor(t *testing.T) { + type fields struct { + SourcesStore chronograf.SourcesStore + ServersStore chronograf.ServersStore + Logger chronograf.Logger + } + type args struct { + ctx context.Context + src chronograf.Source + kapa chronograf.Server + } + srcCount := 0 + srvCount := 0 + tests := []struct { + name string + fields fields + args args + wantSrc int + wantSrv int + wantErr bool + }{ + { + name: "Add when no existing sources", + fields: fields{ + SourcesStore: &mocks.SourcesStore{ + AllF: func(ctx context.Context) ([]chronograf.Source, error) { + return []chronograf.Source{}, nil + }, + AddF: func(ctx context.Context, src chronograf.Source) (chronograf.Source, error) { + srcCount++ + src.ID = srcCount + return src, nil + }, + }, + ServersStore: &mocks.ServersStore{ + AddF: func(ctx context.Context, srv chronograf.Server) (chronograf.Server, error) { + srvCount++ + return srv, nil + }, + }, + }, + args: args{ + ctx: context.Background(), + src: chronograf.Source{ + Name: "Influx 1", + }, + kapa: chronograf.Server{ + Name: "Kapa 1", + }, + }, + wantSrc: 1, + wantSrv: 1, + }, + { + name: "Should not add if existing source", + fields: fields{ + SourcesStore: &mocks.SourcesStore{ + AllF: func(ctx context.Context) ([]chronograf.Source, error) { + return []chronograf.Source{ + { + Name: "Influx 1", + }, + }, nil + }, + AddF: func(ctx context.Context, src chronograf.Source) (chronograf.Source, error) { + srcCount++ + src.ID = srcCount + return src, nil + }, + }, + ServersStore: &mocks.ServersStore{ + AddF: func(ctx context.Context, srv chronograf.Server) (chronograf.Server, error) { + srvCount++ + return srv, nil + }, + }, + Logger: &mocks.TestLogger{}, + }, + args: args{ + ctx: context.Background(), + src: chronograf.Source{ + Name: "Influx 1", + }, + kapa: chronograf.Server{ + Name: "Kapa 1", + }, + }, + wantSrc: 0, + wantSrv: 0, + }, + { + name: "Error if All returns error", + fields: fields{ + SourcesStore: &mocks.SourcesStore{ + AllF: func(ctx context.Context) ([]chronograf.Source, error) { + return nil, fmt.Errorf("error") + }, + }, + Logger: &mocks.TestLogger{}, + }, + args: args{ + ctx: context.Background(), + }, + wantErr: true, + }, + { + name: "Error if Add returns error", + fields: fields{ + SourcesStore: &mocks.SourcesStore{ + AllF: func(ctx context.Context) ([]chronograf.Source, error) { + return []chronograf.Source{}, nil + }, + AddF: func(ctx context.Context, src chronograf.Source) (chronograf.Source, error) { + return chronograf.Source{}, fmt.Errorf("error") + }, + }, + Logger: &mocks.TestLogger{}, + }, + args: args{ + ctx: context.Background(), + }, + wantErr: true, + }, + { + name: "Error if kapa add is error", + fields: fields{ + SourcesStore: &mocks.SourcesStore{ + AllF: func(ctx context.Context) ([]chronograf.Source, error) { + return []chronograf.Source{}, nil + }, + AddF: func(ctx context.Context, src chronograf.Source) (chronograf.Source, error) { + srcCount++ + src.ID = srcCount + return src, nil + }, + }, + ServersStore: &mocks.ServersStore{ + AddF: func(ctx context.Context, srv chronograf.Server) (chronograf.Server, error) { + srvCount++ + return chronograf.Server{}, fmt.Errorf("error") + }, + }, + Logger: &mocks.TestLogger{}, + }, + args: args{ + ctx: context.Background(), + src: chronograf.Source{ + Name: "Influx 1", + }, + kapa: chronograf.Server{ + Name: "Kapa 1", + }, + }, + wantSrc: 1, + wantSrv: 1, + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + srcCount = 0 + srvCount = 0 + h := &Service{ + SourcesStore: tt.fields.SourcesStore, + ServersStore: tt.fields.ServersStore, + Logger: tt.fields.Logger, + } + if err := h.newSourceKapacitor(tt.args.ctx, tt.args.src, tt.args.kapa); (err != nil) != tt.wantErr { + t.Errorf("Service.newSourceKapacitor() error = %v, wantErr %v", err, tt.wantErr) + } + if tt.wantSrc != srcCount { + t.Errorf("Service.newSourceKapacitor() count = %d, wantSrc %d", srcCount, tt.wantSrc) + } + }) + } +} diff --git a/server/swagger.json b/server/swagger.json index 769a6be2c9..ddd452ea7a 100644 --- a/server/swagger.json +++ b/server/swagger.json @@ -1,4187 +1,3900 @@ { - "swagger": "2.0", - "info": { - "title": "Chronograf", - "description": "API endpoints for Chronograf", - "version": "1.3.2.0" - }, - "schemes": [ - "http" - ], - "basePath": "/chronograf/v1", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "paths": { - "/": { - "get": { - "tags": [ - "routes" - ], - "summary": "Lists all the endpoints", - "description": "List of the endpoints.", - "responses": { - "200": { - "description": "Returns the links to the top level endpoints.", - "schema": { - "$ref": "#/definitions/Routes" - } - }, - "default": { - "description": "Unexpected internal service error", - "schema": { - "$ref": "#/definitions/Error" - } - } - } - } - }, - "/sources": { - "get": { - "tags": [ - "sources" - ], - "summary": "Configured data sources", - "description": "These data sources store time series data.", - "responses": { - "200": { - "description": "An array of data sources", - "schema": { - "$ref": "#/definitions/Sources" - } - }, - "default": { - "description": "Unexpected internal service error", - "schema": { - "$ref": "#/definitions/Error" - } - } - } - }, - "post": { - "tags": [ - "sources" - ], - "summary": "Create new data source", - "parameters": [ - { - "name": "source", - "in": "body", - "description": "Configuration options for data source", - "schema": { - "$ref": "#/definitions/Source" - } - } - ], - "responses": { - "201": { - "description": "Successfully create data source", - "headers": { - "Location": { - "type": "string", - "format": "url", - "description": "Location of the newly created data source resource." - } - }, - "schema": { - "$ref": "#/definitions/Source" - } - }, - "default": { - "description": "A processing or an unexpected error.", - "schema": { - "$ref": "#/definitions/Error" - } - } - } - } - }, - "/sources/{id}": { - "get": { - "tags": [ - "sources" - ], - "parameters": [ - { - "name": "id", - "in": "path", - "type": "string", - "description": "ID of the data source", - "required": true - } - ], - "summary": "Configured data sources", - "description": "These data sources store time series data.", - "responses": { - "200": { - "description": "Data source used to supply time series information.", - "schema": { - "$ref": "#/definitions/Source" - } - }, - "404": { - "description": "Unknown source id", - "schema": { - "$ref": "#/definitions/Error" - } - }, - "default": { - "description": "Unexpected internal service error", - "schema": { - "$ref": "#/definitions/Error" - } - } - } - }, - "patch": { - "tags": [ - "sources" - ], - "summary": "Update data source configuration", - "parameters": [ - { - "name": "id", - "in": "path", - "type": "string", - "description": "ID of a data source", - "required": true - }, - { - "name": "config", - "in": "body", - "description": "data source configuration", - "schema": { - "$ref": "#/definitions/Source" - }, - "required": true - } - ], - "responses": { - "200": { - "description": "Data source's configuration was changed", - "schema": { - "$ref": "#/definitions/Source" - } - }, - "404": { - "description": "Happens when trying to access a non-existent data source.", - "schema": { - "$ref": "#/definitions/Error" - } - }, - "default": { - "description": "A processing or an unexpected error.", - "schema": { - "$ref": "#/definitions/Error" - } - } - } - }, - "delete": { - "tags": [ - "sources" - ], - "parameters": [ - { - "name": "id", - "in": "path", - "type": "string", - "description": "ID of the source", - "required": true - } - ], - "summary": "This specific data source will be removed from the data store. All associated kapacitor resources and kapacitor rules resources are also removed.", - "responses": { - "204": { - "description": "data source has been removed" - }, - "404": { - "description": "Unknown data source id", - "schema": { - "$ref": "#/definitions/Error" - } - }, - "default": { - "description": "Unexpected internal service error", - "schema": { - "$ref": "#/definitions/Error" - } - } - } - } - }, - "/sources/{id}/queries": { - "post": { - "tags": [ - "sources", - "queries" - ], - "description": "Used to analyze queries for structure`", - "parameters": [ - { - "name": "id", - "in": "path", - "type": "string", - "description": "ID of the data source", - "required": true - }, - { - "name": "queries", - "in": "body", - "description": "Query Parameters", - "schema": { - "$ref": "#/definitions/Queries" - }, - "required": true - } - ], - "responses": { - "200": { - "description": "Result of the analysis of the query.", - "schema": { - "$ref": "#/definitions/QueriesResponse" - } - }, - "404": { - "description": "Data source id does not exist.", - "schema": { - "$ref": "#/definitions/Error" - } - }, - "default": { - "description": "Unexpected internal service error", - "schema": { - "$ref": "#/definitions/Error" - } - } - } - } - }, - "/sources/{id}/proxy": { - "post": { - "tags": [ - "sources", - "proxy" - ], - "description": "Query the backend time series data source and return the response according to `format`", - "parameters": [ - { - "name": "id", - "in": "path", - "type": "string", - "description": "ID of the data source", - "required": true - }, - { - "name": "query", - "in": "body", - "description": "Query Parameters", - "schema": { - "$ref": "#/definitions/Proxy" - }, - "required": true - } - ], - "responses": { - "200": { - "description": "Result of the query from the backend time series data source.", - "schema": { - "$ref": "#/definitions/ProxyResponse" - } - }, - "400": { - "description": "Any query that results in a data source error (syntax error, etc) will cause this response. The error message will be passed back in the body", - "schema": { - "$ref": "#/definitions/Error" - } - }, - "404": { - "description": "Data source id does not exist.", - "schema": { - "$ref": "#/definitions/Error" - } - }, - "408": { - "description": "Timeout trying to query data source.", - "schema": { - "$ref": "#/definitions/Error" - } - }, - "default": { - "description": "Unexpected internal service error", - "schema": { - "$ref": "#/definitions/Error" - } - } - } - } - }, - "/sources/{id}/write": { - "post": { - "tags": [ - "sources", - "write" - ], - "description": "Write points to the backend time series data source", - "parameters": [ - { - "name": "id", - "in": "path", - "type": "string", - "description": "ID of the data source", - "required": true - }, - { - "name": "query", - "in": "body", - "description": "Write Parameters", - "schema": { - "type": "string", - "format": "byte" - }, - "required": true - }, - { - "name": "db", - "in": "query", - "description": "Sets the target database for the write.", - "type": "string", - "required": true - }, - { - "name": "rp", - "in": "query", - "description": "Sets the target retention policy for the write. InfluxDB writes to the DEFAULT retention policy if you do not specify a retention policy.", - "type": "string" - }, - { - "name": "precision", - "in": "query", - "description": "Sets the precision for the supplied Unix time values. InfluxDB assumes that timestamps are in nanoseconds if you do not specify precision.", - "type": "string", - "enum": [ - "ns","u","ms","s","m","h" - ] - }, - { - "name": "consistency", - "in": "query", - "description": "Sets the write consistency for the point. InfluxDB assumes that the write consistency is one if you do not specify consistency. See the InfluxEnterprise documentation for detailed descriptions of each consistency option.", - "type": "string", - "enum": [ - "any","one","quorum","all" - ] - } - ], - "responses": { - "204": { - "description": "Points written successfuly to database." - }, - "400": { - "description": "Any query that results in a data source error (syntax error, etc) will cause this response. The error message will be passed back in the body", - "schema": { - "$ref": "#/definitions/Error" - } - }, - "404": { - "description": "Data source id does not exist.", - "schema": { - "$ref": "#/definitions/Error" - } - }, - "408": { - "description": "Timeout trying to query data source.", - "schema": { - "$ref": "#/definitions/Error" - } - }, - "default": { - "description": "Unexpected internal service error", - "schema": { - "$ref": "#/definitions/Error" - } - } - } - } - }, - "/sources/{id}/permissions": { - "get": { - "tags": [ - "sources", - "users" - ], - "summary": "Retrieve possible permissions for this data source", - "parameters": [ - { - "name": "id", - "in": "path", - "type": "string", - "description": "ID of the data source", - "required": true - } - ], - "responses": { - "200": { - "description": "Listing of all possible permissions", - "schema": { - "$ref": "#/definitions/AllPermissions" - } - }, - "404": { - "description": "Data source id does not exist.", - "schema": { - "$ref": "#/definitions/Error" - } - }, - "default": { - "description": "A processing or an unexpected error.", - "schema": { - "$ref": "#/definitions/Error" - } - } - } - } - }, - "/sources/{id}/users": { - "get": { - "tags": [ - "sources", - "users" - ], - "summary": "Retrieve all data sources users", - "parameters": [ - { - "name": "id", - "in": "path", - "type": "string", - "description": "ID of the data source", - "required": true - } - ], - "responses": { - "200": { - "description": "Listing of all users", - "schema": { - "$ref": "#/definitions/Users" - } - }, - "404": { - "description": "Data source id does not exist.", - "schema": { - "$ref": "#/definitions/Error" - } - }, - "default": { - "description": "A processing or an unexpected error.", - "schema": { - "$ref": "#/definitions/Error" - } - } - } - }, - "post": { - "tags": [ - "sources", - "users" - ], - "summary": "Create new user for this data source", - "parameters": [ - { - "name": "id", - "in": "path", - "type": "string", - "description": "ID of the data source", - "required": true - }, - { - "name": "user", - "in": "body", - "description": "Configuration options for new user", - "schema": { - "$ref": "#/definitions/User" - } - } - ], - "responses": { - "201": { - "description": "Successfully created new user", - "headers": { - "Location": { - "type": "string", - "format": "url", - "description": "Location of the newly created user resource." - } - }, - "schema": { - "$ref": "#/definitions/User" - } - }, - "404": { - "description": "Data source id does not exist.", - "schema": { - "$ref": "#/definitions/Error" - } - }, - "default": { - "description": "A processing or an unexpected error.", - "schema": { - "$ref": "#/definitions/Error" - } - } - } - } - }, - "/sources/{id}/users/{user_id}": { - "get": { - "tags": [ - "sources", - "users" - ], - "parameters": [ - { - "name": "id", - "in": "path", - "type": "string", - "description": "ID of the data source", - "required": true - }, - { - "name": "user_id", - "in": "path", - "type": "string", - "description": "ID of the specific user", - "required": true - } - ], - "summary": "Returns information about a specific user", - "description": "Specific User within a data source", - "responses": { - "200": { - "description": "Information relating to the user", - "schema": { - "$ref": "#/definitions/User" - } - }, - "404": { - "description": "Unknown user or unknown source", - "schema": { - "$ref": "#/definitions/Error" - } - }, - "default": { - "description": "Unexpected internal service error", - "schema": { - "$ref": "#/definitions/Error" - } - } - } - }, - "patch": { - "tags": [ - "sources", - "users" - ], - "summary": "Update user configuration", - "parameters": [ - { - "name": "id", - "in": "path", - "type": "string", - "description": "ID of the data source", - "required": true - }, - { - "name": "user_id", - "in": "path", - "type": "string", - "description": "ID of the specific user", - "required": true - }, - { - "name": "config", - "in": "body", - "description": "user configuration", - "schema": { - "$ref": "#/definitions/User" - }, - "required": true - } - ], - "responses": { - "200": { - "description": "Users's configuration was changed", - "schema": { - "$ref": "#/definitions/User" - } - }, - "404": { - "description": "Happens when trying to access a non-existent user or source.", - "schema": { - "$ref": "#/definitions/Error" - } - }, - "default": { - "description": "A processing or an unexpected error.", - "schema": { - "$ref": "#/definitions/Error" - } - } - } - }, - "delete": { - "tags": [ - "sources", - "users" - ], - "parameters": [ - { - "name": "id", - "in": "path", - "type": "string", - "description": "ID of the data source", - "required": true - }, - { - "name": "user_id", - "in": "path", - "type": "string", - "description": "ID of the specific user", - "required": true - } - ], - "summary": "This specific user will be removed from the data source", - "responses": { - "204": { - "description": "User has been removed" - }, - "404": { - "description": "Unknown user id or data source", - "schema": { - "$ref": "#/definitions/Error" - } - }, - "default": { - "description": "Unexpected internal service error", - "schema": { - "$ref": "#/definitions/Error" - } - } - } - } - }, - "/sources/{id}/roles": { - "get": { - "tags": [ - "sources", - "users", - "roles" - ], - "summary": "Retrieve all data sources roles. Available only in Influx Enterprise", - "parameters": [ - { - "name": "id", - "in": "path", - "type": "string", - "description": "ID of the data source", - "required": true - } - ], - "responses": { - "200": { - "description": "Listing of all roles", - "schema": { - "$ref": "#/definitions/Roles" - } - }, - "404": { - "description": "Data source id does not exist.", - "schema": { - "$ref": "#/definitions/Error" - } - }, - "default": { - "description": "A processing or an unexpected error.", - "schema": { - "$ref": "#/definitions/Error" - } - } - } - }, - "post": { - "tags": [ - "sources", - "users", - "roles" - ], - "summary": "Create new role for this data source", - "parameters": [ - { - "name": "id", - "in": "path", - "type": "string", - "description": "ID of the data source", - "required": true - }, - { - "name": "roleuser", - "in": "body", - "description": "Configuration options for new role", - "schema": { - "$ref": "#/definitions/Role" - } - } - ], - "responses": { - "201": { - "description": "Successfully created new role", - "headers": { - "Location": { - "type": "string", - "format": "url", - "description": "Location of the newly created role resource." - } - }, - "schema": { - "$ref": "#/definitions/Role" - } - }, - "404": { - "description": "Data source id does not exist.", - "schema": { - "$ref": "#/definitions/Error" - } - }, - "default": { - "description": "A processing or an unexpected error.", - "schema": { - "$ref": "#/definitions/Error" - } - } - } - } - }, - "/sources/{id}/roles/{role_id}": { - "get": { - "tags": [ - "sources", - "users", - "roles" - ], - "parameters": [ - { - "name": "id", - "in": "path", - "type": "string", - "description": "ID of the data source", - "required": true - }, - { - "name": "role_id", - "in": "path", - "type": "string", - "description": "ID of the specific role", - "required": true - } - ], - "summary": "Returns information about a specific role", - "description": "Specific role within a data source", - "responses": { - "200": { - "description": "Information relating to the role", - "schema": { - "$ref": "#/definitions/Role" - } - }, - "404": { - "description": "Unknown role or unknown source", - "schema": { - "$ref": "#/definitions/Error" - } - }, - "default": { - "description": "Unexpected internal service error", - "schema": { - "$ref": "#/definitions/Error" - } - } - } - }, - "patch": { - "tags": [ - "sources", - "users", - "roles" - ], - "summary": "Update role configuration", - "parameters": [ - { - "name": "id", - "in": "path", - "type": "string", - "description": "ID of the data source", - "required": true - }, - { - "name": "role_id", - "in": "path", - "type": "string", - "description": "ID of the specific role", - "required": true - }, - { - "name": "config", - "in": "body", - "description": "role configuration", - "schema": { - "$ref": "#/definitions/Role" - }, - "required": true - } - ], - "responses": { - "200": { - "description": "Roles's configuration was changed", - "schema": { - "$ref": "#/definitions/Role" - } - }, - "404": { - "description": "Happens when trying to access a non-existent role or source.", - "schema": { - "$ref": "#/definitions/Error" - } - }, - "default": { - "description": "A processing or an unexpected error.", - "schema": { - "$ref": "#/definitions/Error" - } - } - } - }, - "delete": { - "tags": [ - "sources", - "users", - "roles" - ], - "parameters": [ - { - "name": "id", - "in": "path", - "type": "string", - "description": "ID of the data source", - "required": true - }, - { - "name": "role_id", - "in": "path", - "type": "string", - "description": "ID of the specific role", - "required": true - } - ], - "summary": "This specific role will be removed from the data source", - "responses": { - "204": { - "description": "Role has been removed" - }, - "404": { - "description": "Unknown role id or data source", - "schema": { - "$ref": "#/definitions/Error" - } - }, - "default": { - "description": "Unexpected internal service error", - "schema": { - "$ref": "#/definitions/Error" - } - } - } - } - }, - "/sources/{id}/dbs/": { - "get": { - "tags": [ - "databases" - ], - "summary": "Retrieve all databases for a source", - "parameters": [ - { - "name": "id", - "in": "path", - "type": "string", - "description": "ID of the data source", - "required": true - } - ], - "responses": { - "200": { - "description": "Listing of all databases for a source", - "schema": { - "$ref": "#/definitions/Databases" - } - }, - "404": { - "description": "Data source id does not exist.", - "schema": { - "$ref": "#/definitions/Error" - } - }, - "default": { - "description": "A processing or an unexpected error.", - "schema": { - "$ref": "#/definitions/Error" - } - } - } - }, - "post": { - "tags": [ - "databases" - ], - "summary": "Create new database for a source", - "parameters": [ - { - "name": "id", - "in": "path", - "type": "string", - "description": "ID of the data source", - "required": true - }, - { - "name": "database", - "in": "body", - "description": "Configuration options for a database", - "schema": { - "$ref": "#/definitions/Database" - }, - "required": true - } - ], - "responses": { - "201": { - "description": "Database successfully created.", - "schema": { - "$ref": "#/definitions/Database" - } - }, - "404": { - "description": "Data source id does not exist.", - "schema": { - "$ref": "#/definitions/Error" - } - }, - "default": { - "description": "A processing or an unexpected error.", - "schema": { - "$ref": "#/definitions/Error" - } - } - } - } - }, - "/sources/{id}/dbs/{db_id}": { - "delete": { - "tags": [ - "databases" - ], - "summary": "Delete database for a source", - "parameters": [ - { - "name": "id", - "in": "path", - "type": "string", - "description": "ID of the data source", - "required": true - }, - { - "name": "db_id", - "in": "path", - "type": "string", - "description": "ID of the database", - "required": true - } - ], - "responses": { - "204": { - "description": "Database has been deleted" - }, - "404": { - "description": "Data source id does not exist.", - "schema": { - "$ref": "#/definitions/Error" - } - }, - "default": { - "description": "A processing or an unexpected error.", - "schema": { - "$ref": "#/definitions/Error" - } - } - } - } - }, - "/sources/{id}/dbs/{db_id}/rps": { - "get": { - "tags": [ - "retention policies" - ], - "summary": "Retrieve all retention policies for a database", - "parameters": [ - { - "name": "id", - "in": "path", - "type": "string", - "description": "ID of the data source", - "required": true - }, - { - "name": "db_id", - "in": "path", - "type": "string", - "description": "ID of the database", - "required": true - } - ], - "responses": { - "200": { - "description": "Listing of all retention policies for a database", - "schema": { - "$ref": "#/definitions/RetentionPolicies" - } - }, - "404": { - "description": "Specified retention policy does not exist.", - "schema": { - "$ref": "#/definitions/Error" - } - }, - "default": { - "description": "A processing or an unexpected error.", - "schema": { - "$ref": "#/definitions/Error" - } - } - } - }, - "post": { - "tags": [ - "retention policies" - ], - "summary": "Create new retention policy for a database", - "parameters": [ - { - "name": "id", - "in": "path", - "type": "string", - "description": "ID of the data source", - "required": true - }, - { - "name": "db_id", - "in": "path", - "type": "string", - "description": "ID of the database", - "required": true - }, - { - "name": "rp", - "in": "body", - "description": "Configuration options for the retention policy", - "schema": { - "$ref": "#/definitions/RetentionPolicy" - }, - "required": true - } - ], - "responses": { - "201": { - "description": "Retention Policy successfully created.", - "schema": { - "$ref": "#/definitions/RetentionPolicy" - } - }, - "404": { - "description": "Data source id does not exist.", - "schema": { - "$ref": "#/definitions/Error" - } - }, - "default": { - "description": "A processing or an unexpected error.", - "schema": { - "$ref": "#/definitions/Error" - } - } - } - } - }, - "/sources/{id}/dbs/{db_id}/rps/{rp_id}": { - "patch": { - "tags": [ - "retention policies" - ], - "summary": "Alter retention policy for a database", - "parameters": [ - { - "name": "id", - "in": "path", - "type": "string", - "description": "ID of the data source", - "required": true - }, - { - "name": "db_id", - "in": "path", - "type": "string", - "description": "ID of the database", - "required": true - }, - { - "name": "rp_id", - "in": "path", - "type": "string", - "description": "ID of the retention policy", - "required": true - }, - { - "name": "rp", - "in": "body", - "description": "Configuration options for the retention policy", - "schema": { - "$ref": "#/definitions/RetentionPolicy" - }, - "required": true - } - ], - "responses": { - "200": { - "description": "Retention Policy was altered", - "schema": { - "$ref": "#/definitions/RetentionPolicy" - } - }, - "404": { - "description": "Database or source does not exist.", - "schema": { - "$ref": "#/definitions/Error" - } - }, - "default": { - "description": "A processing or an unexpected error.", - "schema": { - "$ref": "#/definitions/Error" - } - } - } - }, - "delete": { - "tags": [ - "retention policies" - ], - "summary": "Delete retention policy for a database", - "parameters": [ - { - "name": "id", - "in": "path", - "type": "string", - "description": "ID of the data source", - "required": true - }, - { - "name": "db_id", - "in": "path", - "type": "string", - "description": "ID of the database", - "required": true - }, - { - "name": "rp_id", - "in": "path", - "type": "string", - "description": "ID of the retention policy", - "required": true - } - ], - "responses": { - "204": { - "description": "Retention Policy has been deleted" - }, - "404": { - "description": "Data source id does not exist.", - "schema": { - "$ref": "#/definitions/Error" - } - }, - "default": { - "description": "A processing or an unexpected error.", - "schema": { - "$ref": "#/definitions/Error" - } - } - } - } - }, - "/sources/{id}/kapacitors": { - "get": { - "tags": [ - "sources", - "kapacitors" - ], - "parameters": [ - { - "name": "id", - "in": "path", - "type": "string", - "description": "ID of the source", - "required": true - } - ], - "summary": "Retrieve list of configured kapacitors", - "responses": { - "200": { - "description": "An array of kapacitors", - "schema": { - "$ref": "#/definitions/Kapacitors" - } - }, - "default": { - "description": "Unexpected internal service error", - "schema": { - "$ref": "#/definitions/Error" - } - } - } - }, - "post": { - "tags": [ - "sources", - "kapacitors" - ], - "summary": "Create new kapacitor backend", - "parameters": [ - { - "name": "id", - "in": "path", - "type": "string", - "description": "ID of the source", - "required": true - }, - { - "name": "kapacitor", - "in": "body", - "description": "Configuration options for kapacitor", - "schema": { - "$ref": "#/definitions/Kapacitor" - } - } - ], - "responses": { - "201": { - "description": "Successfully created kapacitor source", - "headers": { - "Location": { - "type": "string", - "format": "url", - "description": "Location of the newly created kapacitor resource." - } - }, - "schema": { - "$ref": "#/definitions/Kapacitor" - } - }, - "default": { - "description": "A processing or an unexpected error.", - "schema": { - "$ref": "#/definitions/Error" - } - } - } - } - }, - "/sources/{id}/kapacitors/{kapa_id}": { - "get": { - "tags": [ - "sources", - "kapacitors" - ], - "parameters": [ - { - "name": "id", - "in": "path", - "type": "string", - "description": "ID of the source", - "required": true - }, - { - "name": "kapa_id", - "in": "path", - "type": "string", - "description": "ID of the kapacitor", - "required": true - } - ], - "summary": "Configured kapacitors", - "description": "Retrieve information on a single kapacitor instance", - "responses": { - "200": { - "description": "Kapacitor connection information", - "schema": { - "$ref": "#/definitions/Kapacitor" - } - }, - "404": { - "description": "Unknown data source or kapacitor id", - "schema": { - "$ref": "#/definitions/Error" - } - }, - "default": { - "description": "Unexpected internal service error", - "schema": { - "$ref": "#/definitions/Error" - } - } - } - }, - "patch": { - "tags": [ - "sources", - "kapacitors" - ], - "summary": "Update kapacitor configuration", - "parameters": [ - { - "name": "id", - "in": "path", - "type": "string", - "description": "ID of the source", - "required": true - }, - { - "name": "kapa_id", - "in": "path", - "type": "string", - "description": "ID of a kapacitor backend", - "required": true - }, - { - "name": "config", - "in": "body", - "description": "kapacitor configuration", - "schema": { - "$ref": "#/definitions/Kapacitor" - }, - "required": true - } - ], - "responses": { - "200": { - "description": "Kapacitor's configuration was changed", - "schema": { - "$ref": "#/definitions/Kapacitor" - } - }, - "404": { - "description": "Happens when trying to access a non-existent data source or kapacitor.", - "schema": { - "$ref": "#/definitions/Error" - } - }, - "default": { - "description": "A processing or an unexpected error.", - "schema": { - "$ref": "#/definitions/Error" - } - } - } - }, - "delete": { - "tags": [ - "sources", - "kapacitors" - ], - "parameters": [ - { - "name": "id", - "in": "path", - "type": "string", - "description": "ID of the source", - "required": true - }, - { - "name": "kapa_id", - "in": "path", - "type": "string", - "description": "ID of the kapacitor", - "required": true - } - ], - "summary": "Remove Kapacitor backend", - "description": "This specific kapacitor will be removed. All associated rule resources will also be removed from the store.", - "responses": { - "204": { - "description": "kapacitor has been removed." - }, - "404": { - "description": "Unknown Data source or Kapacitor id", - "schema": { - "$ref": "#/definitions/Error" - } - }, - "default": { - "description": "Unexpected internal service error", - "schema": { - "$ref": "#/definitions/Error" - } - } - } - } - }, - "/sources/{id}/kapacitors/{kapa_id}/rules": { - "get": { - "tags": [ - "sources", - "kapacitors", - "rules" - ], - "description": "Get all defined alert rules.", - "parameters": [ - { - "name": "id", - "in": "path", - "type": "string", - "description": "ID of the source", - "required": true - }, - { - "name": "kapa_id", - "in": "path", - "type": "string", - "description": "ID of the kapacitor backend.", - "required": true - } - ], - "responses": { - "200": { - "description": "All alert rules for this specific kapacitor are returned", - "schema": { - "$ref": "#/definitions/Rules" - } - }, - "404": { - "description": "Data source or Kapacitor ID does not exist.", - "schema": { - "$ref": "#/definitions/Error" - } - }, - "default": { - "description": "Internal server error", - "schema": { - "$ref": "#/definitions/Error" - } - } - } - }, - "post": { - "tags": [ - "sources", - "kapacitors", - "rules" - ], - "description": "Create kapacitor alert rule", - "parameters": [ - { - "name": "id", - "in": "path", - "type": "string", - "description": "ID of the source", - "required": true - }, - { - "name": "kapa_id", - "in": "path", - "type": "string", - "description": "ID of the kapacitor backend.", - "required": true - }, - { - "name": "rule", - "in": "body", - "description": "Rule to generate alert rule", - "schema": { - "$ref": "#/definitions/Rule" - }, - "required": true - } - ], - "responses": { - "201": { - "description": "Successfully created new kapacitor alert rule", - "headers": { - "Location": { - "type": "string", - "format": "url", - "description": "Location of the newly created kapacitor rule resource." - } - }, - "schema": { - "$ref": "#/definitions/Rule" - } - }, - "404": { - "description": "Source ID or Kapacitor ID does not exist.", - "schema": { - "$ref": "#/definitions/Error" - } - }, - "422": { - "description": "Source ID , Kapacitor ID or alert are unprocessable", - "schema": { - "$ref": "#/definitions/Error" - } - }, - "default": { - "description": "Internal server error; generally a problem creating alert in kapacitor", - "schema": { - "$ref": "#/definitions/Error" - } - } - } - } - }, - "/sources/{id}/kapacitors/{kapa_id}/rules/{rule_id}": { - "get": { - "tags": [ - "sources", - "kapacitors", - "rules" - ], - "parameters": [ - { - "name": "id", - "in": "path", - "type": "string", - "description": "ID of the source", - "required": true - }, - { - "name": "kapa_id", - "in": "path", - "type": "string", - "description": "ID of the kapacitor", - "required": true - }, - { - "name": "rule_id", - "in": "path", - "type": "string", - "description": "ID of the rule", - "required": true - } - ], - "summary": "Specific kapacitor alert rule", - "description": "Alerting rule for kapacitor", - "responses": { - "200": { - "description": "Alert exists and has a specific TICKscript", - "schema": { - "$ref": "#/definitions/Rule" - } - }, - "404": { - "description": "Unknown data source, kapacitor id, or rule id", - "schema": { - "$ref": "#/definitions/Error" - } - }, - "default": { - "description": "Unexpected internal service error", - "schema": { - "$ref": "#/definitions/Error" - } - } - } - }, - "put": { - "tags": [ - "sources", - "kapacitors", - "rules" - ], - "summary": "Update rule alert rule configuration", - "parameters": [ - { - "name": "id", - "in": "path", - "type": "string", - "description": "ID of the source", - "required": true - }, - { - "name": "kapa_id", - "in": "path", - "type": "string", - "description": "ID of a kapacitor backend", - "required": true - }, - { - "name": "rule_id", - "in": "path", - "type": "string", - "description": "ID of a rule", - "required": true - }, - { - "name": "rule", - "in": "body", - "description": "Rule update", - "schema": { - "$ref": "#/definitions/Rule" - }, - "required": true - } - ], - "responses": { - "200": { - "description": "Alert configuration was changed", - "schema": { - "$ref": "#/definitions/Rule" - } - }, - "404": { - "description": "Happens when trying to access a non-existent data source, kapacitor, or rule.", - "schema": { - "$ref": "#/definitions/Error" - } - }, - "default": { - "description": "A processing or an unexpected error.", - "schema": { - "$ref": "#/definitions/Error" - } - } - } - }, - "delete": { - "tags": [ - "sources", - "kapacitors", - "rules" - ], - "parameters": [ - { - "name": "id", - "in": "path", - "type": "string", - "description": "ID of the source", - "required": true - }, - { - "name": "kapa_id", - "in": "path", - "type": "string", - "description": "ID of the kapacitor", - "required": true - }, - { - "name": "rule_id", - "in": "path", - "type": "string", - "description": "ID of the rule", - "required": true - } - ], - "summary": "This specific alert rule will be removed.", - "responses": { - "204": { - "description": "Alert rule has been removed." - }, - "404": { - "description": "Unknown Data source, Kapacitor id, or alert rule", - "schema": { - "$ref": "#/definitions/Error" - } - }, - "default": { - "description": "Unexpected internal service error", - "schema": { - "$ref": "#/definitions/Error" - } - } - } - } - }, - "/sources/{id}/kapacitors/{kapa_id}/proxy": { - "get": { - "tags": [ - "sources", - "kapacitors", - "proxy" - ], - "description": "GET to `path` of kapacitor. The response and status code from kapacitor is directly returned.", - "parameters": [ - { - "name": "id", - "in": "path", - "type": "string", - "description": "ID of the source", - "required": true - }, - { - "name": "kapa_id", - "in": "path", - "type": "string", - "description": "ID of the kapacitor backend.", - "required": true - }, - { - "name": "path", - "in": "query", - "type": "string", - "description": "The kapacitor API path to use in the proxy redirect", - "required": true - } - ], - "responses": { - "204": { - "description": "Kapacitor returned no content" - }, - "404": { - "description": "Data source or Kapacitor ID does not exist.", - "schema": { - "$ref": "#/definitions/Error" - } - }, - "default": { - "description": "Response directly from kapacitor", - "schema": { - "$ref": "#/definitions/KapacitorProxyResponse" - } - } - } - }, - "delete": { - "tags": [ - "sources", - "kapacitors", - "proxy" - ], - "description": "DELETE to `path` of kapacitor. The response and status code from kapacitor is directly returned.", - "parameters": [ - { - "name": "id", - "in": "path", - "type": "string", - "description": "ID of the source", - "required": true - }, - { - "name": "kapa_id", - "in": "path", - "type": "string", - "description": "ID of the kapacitor backend.", - "required": true - }, - { - "name": "path", - "in": "query", - "type": "string", - "description": "The kapacitor API path to use in the proxy redirect", - "required": true - } - ], - "responses": { - "204": { - "description": "Kapacitor returned no content" - }, - "404": { - "description": "Data source or Kapacitor ID does not exist.", - "schema": { - "$ref": "#/definitions/Error" - } - }, - "default": { - "description": "Response directly from kapacitor", - "schema": { - "$ref": "#/definitions/KapacitorProxyResponse" - } - } - } - }, - "patch": { - "tags": [ - "sources", - "kapacitors", - "proxy" - ], - "description": "PATCH body directly to configured kapacitor. The response and status code from kapacitor is directly returned.", - "parameters": [ - { - "name": "id", - "in": "path", - "type": "string", - "description": "ID of the source", - "required": true - }, - { - "name": "kapa_id", - "in": "path", - "type": "string", - "description": "ID of the kapacitor backend.", - "required": true - }, - { - "name": "path", - "in": "query", - "type": "string", - "description": "The kapacitor API path to use in the proxy redirect", - "required": true - }, - { - "name": "query", - "in": "body", - "description": "Kapacitor body", - "schema": { - "$ref": "#/definitions/KapacitorProxy" - }, - "required": true - } - ], - "responses": { - "204": { - "description": "Kapacitor returned no content" - }, - "404": { - "description": "Data source or Kapacitor ID does not exist.", - "schema": { - "$ref": "#/definitions/Error" - } - }, - "default": { - "description": "Response directly from kapacitor", - "schema": { - "$ref": "#/definitions/KapacitorProxyResponse" - } - } - } - }, - "post": { - "tags": [ - "sources", - "kapacitors", - "proxy" - ], - "description": "POST body directly to configured kapacitor. The response and status code from kapacitor is directly returned.", - "parameters": [ - { - "name": "id", - "in": "path", - "type": "string", - "description": "ID of the source", - "required": true - }, - { - "name": "kapa_id", - "in": "path", - "type": "string", - "description": "ID of the kapacitor backend.", - "required": true - }, - { - "name": "path", - "in": "query", - "type": "string", - "description": "The kapacitor API path to use in the proxy redirect", - "required": true - }, - { - "name": "query", - "in": "body", - "description": "Kapacitor body", - "schema": { - "$ref": "#/definitions/KapacitorProxy" - }, - "required": true - } - ], - "responses": { - "204": { - "description": "Kapacitor returned no content" - }, - "404": { - "description": "Kapacitor ID does not exist.", - "schema": { - "$ref": "#/definitions/Error" - } - }, - "default": { - "description": "Response directly from kapacitor", - "schema": { - "$ref": "#/definitions/KapacitorProxyResponse" - } - } - } - } - }, - "/mappings": { - "get": { - "tags": [ - "layouts", - "mappings" - ], - "summary": "Mappings between app names and measurements", - "description": "Mappings provide a means to alias measurement names found within a telegraf database and application layouts found within Chronograf\n", - "responses": { - "200": { - "description": "An array of mappings", - "schema": { - "$ref": "#/definitions/Mappings" - } - }, - "default": { - "description": "Unexpected internal service error", - "schema": { - "$ref": "#/definitions/Error" - } - } - } - } - }, - "/layouts": { - "get": { - "tags": [ - "layouts" - ], - "summary": "Pre-configured layouts", - "parameters": [ - { - "name": "measurement", - "in": "query", - "description": "Returns layouts with this measurement", - "required": false, - "type": "array", - "items": { - "type": "string" - }, - "collectionFormat": "multi" - }, - { - "name": "app", - "in": "query", - "description": "Returns layouts with this app", - "required": false, - "type": "array", - "items": { - "type": "string" - }, - "collectionFormat": "multi" - } - ], - "description": "Layouts are a collection of `Cells` that visualize time-series data.\n", - "responses": { - "200": { - "description": "An array of layouts", - "schema": { - "$ref": "#/definitions/Layouts" - } - }, - "default": { - "description": "Unexpected internal service error", - "schema": { - "$ref": "#/definitions/Error" - } - } - } - }, - "post": { - "tags": [ - "layouts" - ], - "summary": "Create new layout", - "parameters": [ - { - "name": "layout", - "in": "body", - "description": "Defines the layout and queries of the cells within the layout.", - "schema": { - "$ref": "#/definitions/Layout" - } - } - ], - "responses": { - "201": { - "description": "Successfully created new layout", - "headers": { - "Location": { - "type": "string", - "format": "url", - "description": "Location of the newly created layout" - } - }, - "schema": { - "$ref": "#/definitions/Layout" - } - }, - "default": { - "description": "A processing or an unexpected error.", - "schema": { - "$ref": "#/definitions/Error" - } - } - } - } - }, - "/layouts/{id}": { - "get": { - "tags": [ - "layouts" - ], - "parameters": [ - { - "name": "id", - "in": "path", - "type": "string", - "description": "ID of the layout", - "required": true - } - ], - "summary": "Specific pre-configured layout containing cells and queries.", - "description": "layouts will hold information about how to layout the page of graphs.\n", - "responses": { - "200": { - "description": "Returns the specified layout containing `cells`.", - "schema": { - "$ref": "#/definitions/Layout" - } - }, - "404": { - "description": "Unknown layout id", - "schema": { - "$ref": "#/definitions/Error" - } - }, - "default": { - "description": "Unexpected internal service error", - "schema": { - "$ref": "#/definitions/Error" - } - } - } - }, - "delete": { - "tags": [ - "layouts" - ], - "parameters": [ - { - "name": "id", - "in": "path", - "type": "string", - "description": "ID of the layout", - "required": true - } - ], - "summary": "This specific layout will be removed from the data store", - "responses": { - "204": { - "description": "Layout has been removed." - }, - "404": { - "description": "Unknown layout id", - "schema": { - "$ref": "#/definitions/Error" - } - }, - "default": { - "description": "Unexpected internal service error", - "schema": { - "$ref": "#/definitions/Error" - } - } - } - }, - "put": { - "tags": [ - "layouts" - ], - "summary": "Replace layout configuration.", - "parameters": [ - { - "name": "id", - "in": "path", - "type": "string", - "description": "ID of a layout", - "required": true - }, - { - "name": "config", - "in": "body", - "description": "layout configuration update parameters", - "schema": { - "$ref": "#/definitions/Layout" - }, - "required": true - } - ], - "responses": { - "200": { - "description": "Layout has been replaced and the new layout is returned.", - "schema": { - "$ref": "#/definitions/Layout" - } - }, - "404": { - "description": "Happens when trying to access a non-existent layout.", - "schema": { - "$ref": "#/definitions/Error" - } - }, - "default": { - "description": "A processing or an unexpected error.", - "schema": { - "$ref": "#/definitions/Error" - } - } - } - } - }, - "/dashboards": { - "get": { - "tags": [ - "dashboards" - ], - "summary": "List of all dashboards", - "responses": { - "200": { - "description": "An array of dashboards", - "schema": { - "$ref": "#/definitions/Dashboards" - } - }, - "default": { - "description": "Unexpected internal service error", - "schema": { - "$ref": "#/definitions/Error" - } - } - } - }, - "post": { - "tags": [ - "dashboards" - ], - "summary": "Create new dashboard", - "parameters": [ - { - "name": "dashboard", - "in": "body", - "description": "Configuration options for new dashboard", - "schema": { - "$ref": "#/definitions/Dashboard" - } - } - ], - "responses": { - "201": { - "description": "Successfully created new dashboard", - "headers": { - "Location": { - "type": "string", - "format": "url", - "description": "Location of the newly created dashboard resource." - } - }, - "schema": { - "$ref": "#/definitions/Dashboard" - } - }, - "default": { - "description": "A processing or an unexpected error.", - "schema": { - "$ref": "#/definitions/Error" - } - } - } - } - }, - "/dashboards/{id}": { - "get": { - "tags": [ - "dashboards" - ], - "parameters": [ - { - "name": "id", - "in": "path", - "type": "integer", - "description": "ID of the dashboard", - "required": true - } - ], - "summary": "Specific dashboard", - "description": "Dashboards contain visual display information as well as links to queries", - "responses": { - "200": { - "description": "Returns the specified dashboard with links to queries.", - "schema": { - "$ref": "#/definitions/Dashboard" - } - }, - "404": { - "description": "Unknown dashboard id", - "schema": { - "$ref": "#/definitions/Error" - } - }, - "default": { - "description": "Unexpected internal service error", - "schema": { - "$ref": "#/definitions/Error" - } - } - } - }, - "delete": { - "tags": [ - "dashboards" - ], - "parameters": [ - { - "name": "id", - "in": "path", - "type": "integer", - "description": "ID of the layout", - "required": true - } - ], - "summary": "Deletes the specified dashboard", - "responses": { - "204": { - "description": "Dashboard has been removed." - }, - "404": { - "description": "Unknown dashboard id", - "schema": { - "$ref": "#/definitions/Error" - } - }, - "default": { - "description": "Unexpected internal service error", - "schema": { - "$ref": "#/definitions/Error" - } - } - } - }, - "put": { - "tags": [ - "dashboards" - ], - "summary": "Replace dashboard information.", - "parameters": [ - { - "name": "id", - "in": "path", - "type": "integer", - "description": "ID of a dashboard", - "required": true - }, - { - "name": "config", - "in": "body", - "description": "dashboard configuration update parameters", - "schema": { - "$ref": "#/definitions/Dashboard" - }, - "required": true - } - ], - "responses": { - "200": { - "description": "Dashboard has been replaced and the new dashboard is returned.", - "schema": { - "$ref": "#/definitions/Dashboard" - } - }, - "404": { - "description": "Happens when trying to access a non-existent dashboard.", - "schema": { - "$ref": "#/definitions/Error" - } - }, - "default": { - "description": "A processing or an unexpected error.", - "schema": { - "$ref": "#/definitions/Error" - } - } - } - }, - "patch": { - "tags": [ - "layouts" - ], - "summary": "Update dashboard information.", - "description": "Update either the dashboard name or the dashboard cells", - "parameters": [ - { - "name": "id", - "in": "path", - "type": "integer", - "description": "ID of a dashboard", - "required": true - }, - { - "name": "config", - "in": "body", - "description": "dashboard configuration update parameters. Must be either name or cells", - "schema": { - "$ref": "#/definitions/Dashboard" - }, - "required": true - } - ], - "responses": { - "200": { - "description": "Dashboard has been updated and the new dashboard is returned.", - "schema": { - "$ref": "#/definitions/Dashboard" - } - }, - "404": { - "description": "Happens when trying to access a non-existent dashboard.", - "schema": { - "$ref": "#/definitions/Error" - } - }, - "default": { - "description": "A processing or an unexpected error.", - "schema": { - "$ref": "#/definitions/Error" - } - } - } - } - } - }, - "definitions": { - "Databases": { - "type": "object", - "required": [ - "databases" - ], - "properties": { - "databases": { - "type": "array", - "items": { - "$ref": "#/definitions/Database" - } - } - } - }, - "Database": { - "type": "object", - "required": [ - "name" - ], - "example": { - "name": "NOAA_water_database", - "duration": "3d", - "replication": 3, - "shardDuration": "3h", - "retentionPolicies": [ - { - "name": "weekly", - "duration": "7d", - "replication": 1, - "shardDuration": "7d", - "default": true, - "links": { - "self": "/chronograf/v1/ousrces/1/dbs/NOAA_water_database/rps/liquid" - } - } - ], - "links": { - "self": "/chronograf/v1/sources/1/dbs/NOAA_water_database", - "rps": "/chronograf/v1/sources/1/dbs/NOAA_water_database/rps" - } - }, - "properties": { - "name": { - "type": "string", - "description": "The identifying name of the database" - }, - "duration": { - "type": "string", - "description": "the duration of the default retention policy" - }, - "replication": { - "type": "integer", - "format": "int32", - "description": "how many copies of the data are stored in the cluster" - }, - "shardDuration": { - "type": "string", - "description": "the interval spanned by each shard group" - }, - "retentionPolicies": { - "type": "array", - "items": { - "$ref": "#/definitions/RetentionPolicy" - } - }, - "links": { - "type": "object", - "properties": { - "self": { - "type": "string", - "description": "Self link mapping to this resource", - "format": "url" - }, - "rps": { - "type": "string", - "description": "Link to retention policies for this database", - "format": "url" - } - } - } - } - }, - "Kapacitors": { - "type": "object", - "required": [ - "kapacitors" - ], - "properties": { - "kapacitors": { - "type": "array", - "items": { - "$ref": "#/definitions/Kapacitor" - } - } - } - }, - "Kapacitor": { - "type": "object", - "required": [ - "name", - "url" - ], - "example": { - "id": "4", - "name": "kapa", - "url": "http://localhost:9092", - "active": false, - "links": { - "proxy": "/chronograf/v1/sources/4/kapacitors/4/proxy", - "self": "/chronograf/v1/sources/4/kapacitors/4", - "rules": "/chronograf/v1/sources/4/kapacitors/4/rules" - } - }, - "properties": { - "id": { - "type": "string", - "description": "Unique identifier representing a kapacitor instance.", - "readOnly": true - }, - "name": { - "type": "string", - "description": "User facing name of kapacitor instance." - }, - "username": { - "type": "string", - "description": "Username for authentication to kapacitor." - }, - "password": { - "type": "string", - "description": "Password is in cleartext." - }, - "url": { - "type": "string", - "format": "url", - "description": "URL for the kapacitor backend (e.g. http://localhost:9092)" - }, - "active": { - "type": "boolean", - "description": "Indicates whether the kapacitor is the current kapacitor being used for a source" - }, - "links": { - "type": "object", - "properties": { - "self": { - "type": "string", - "description": "Self link mapping to this resource", - "format": "url" - }, - "proxy": { - "type": "string", - "description": "URL location of proxy endpoint for this kapacitor", - "format": "url" - }, - "rules": { - "type": "string", - "description": "URL location of rules endpoint for this kapacitor", - "format": "url" - } - } - } - } - }, - "KapacitorProxy": { - "description": "Entirely used as the body for the request to the kapacitor backend.", - "type": "object" - }, - "KapacitorProxyResponse": { - "description": "Entire response from the kapacitor backend.", - "type": "object" - }, - "Rules": { - "type": "object", - "required": [ - "rules" - ], - "properties": { - "rules": { - "type": "array", - "items": { - "$ref": "#/definitions/Rule" - } - } - } - }, - "Query": { - "type": "object", - "properties": { - "query": { - "type": "string", - "description": "InfluxQL statement to be analyzed", - "example": { - "query": "select max(usage_system) from telegraf.autogen.cpu group by time(10m)" - } - } - } - }, - "QueryResponse": { - "type": "object", - "properties": { - "query": { - "type": "string", - "example": { - "query": "select max(usage_system) from telegraf.autogen.cpu group by time(10m)" - }, - "description": "InfluxQL statement to be analyzed" - }, - "queryConfig": { - "$ref": "#/definitions/QueryConfig" - } - } - }, - "Queries": { - "type": "object", - "properties": { - "queries": { - "type": "array", - "items": { - "$ref": "#/definitions/Query" - } - } - } - }, - "QueriesResponse": { - "type": "object", - "properties": { - "queries": { - "type": "array", - "items": { - "$ref": "#/definitions/QueryResponse" - } - } - } - }, - "QueryConfig": { - "type": "object", - "example": { - "id": "ce72917d-1ecb-45ea-a6cb-4c122deb93c7", - "database": "telegraf", - "measurement": "cpu", - "retentionPolicy": "autogen", - "fields": [ - { - "field": "usage_system", - "funcs": [ - "max" - ] - } - ], - "tags": {}, - "groupBy": { - "time": "10m", - "tags": [] - }, - "areTagsAccepted": true, - "range": { - "lower": "15m", - "upper": "now" - } - }, - "properties": { - "id": { - "type": "string" - }, - "database": { - "type": "string" - }, - "measurement": { - "type": "string" - }, - "retentionPolicy": { - "type": "string" - }, - "areTagsAccepted": { - "type": "boolean" - }, - "rawText": { - "type": "string" - }, - "tags": { - "type": "object" - }, - "groupBy": { - "type": "object", - "properties": { - "time": { - "type": "string" - }, - "tags": { - "type": "array", - "items": { - "type": "string" - } - } - }, - "required": [ - "time", - "tags" - ] - }, - "fields": { - "type": "array", - "items": { - "type": "object", - "properties": { - "field": { - "type": "string" - }, - "funcs": { - "type": "array", - "items": { - "type": "string" - } - } - }, - "required": [ - "field", - "funcs" - ] - } - }, - "range": { - "type": "object", - "properties": { - "lower": { - "type": "string" - }, - "upper": { - "type": "string" - } - }, - "required": [ - "lower", - "upper" - ] - } - }, - "required": [ - "database", - "measurement", - "retentionPolicy", - "areTagsAccepted", - "tags", - "groupBy", - "fields" - ] - }, - "KapacitorNode": { - "type": "object", - "description": "Represents a node in the kapacitor TICKscript graph", - "required": [ - "name" - ], - "properties": { - "name": { - "type": "string", - "description": "Name of the kapacitor node e.g. slack" - }, - "args": { - "type": "array", - "description": "All arguments to the named node", - "items": { - "type": "string" - } - }, - "properties": { - "type": "array", - "description": "All properties attached to the kapacitor node", - "items": { - "$ref": "#/definitions/KapacitorProperty" - } - } - } - }, - "KapacitorProperty": { - "type": "object", - "description": "Represents a property attached to a node in the kapacitor TICKscript graph", - "required": [ - "name" - ], - "properties": { - "name": { - "type": "string", - "description": "Name of the kapacitor property e.g. channel for a slack ndoe" - }, - "args": { - "type": "array", - "description": "All arguments to the named property", - "items": { - "type": "string" - } - } - } - }, - "RetentionPolicies": { - "type": "object", - "required": [ - "retentionPolicies" - ], - "properties": { - "retentionPolicies": { - "type": "array", - "items": { - "$ref": "#/definitions/RetentionPolicy" - } - } - } - }, - "RetentionPolicy": { - "type": "object", - "required": [ - "name", - "duration", - "replication" - ], - "example": { - "name": "weekly", - "duration": "7d", - "replication": 1, - "shardDuration": "7d", - "default": true, - "links": { - "self": "/chronograf/v1/ousrces/1/dbs/NOAA_water_database/rps/liquid" - } - }, - "properties": { - "name": { - "type": "string", - "description": "The identifying name of the retention policy" - }, - "duration": { - "type": "string", - "description": "the duration of the retention policy" - }, - "replication": { - "type": "integer", - "format": "int32", - "description": "how many copies of the data are stored in the cluster" - }, - "shardDuration": { - "type": "string", - "description": "the interval spanned by each shard group" - }, - "default": { - "type": "boolean", - "description": "Indicates whether this retention policy should be the default" - }, - "links": { - "type": "object", - "properties": { - "self": { - "type": "string", - "description": "Self link mapping to this resource", - "format": "url" - } - } - } - } - }, - "Rule": { - "type": "object", - "example": { - "id": "chronograf-v1-b2b065ea-79bd-4e4f-8c0d-d0ef68477d38", - "query": { - "id": "ce72917d-1ecb-45ea-a6cb-4c122deb93c7", - "database": "telegraf", - "measurement": "cpu", - "retentionPolicy": "autogen", - "fields": [ - { - "field": "usage_system", - "funcs": [ - "max" - ] - } - ], - "tags": {}, - "groupBy": { - "time": "10m", - "tags": [] - }, - "areTagsAccepted": true - }, - "every": "30s", - "alerts": [ - "alerta" - ], - "alertNodes": [ - { - "name": "alerta", - "args": [], - "properties": [] - } - ], - "message": "too much spam", - "details": "muh body", - "trigger": "threshold", - "values": { - "operator": "greater than", - "value": "10" - }, - "name": "Untitled Rule", - "tickscript": "var db = 'telegraf'\n\nvar rp = 'autogen'\n\nvar measurement = 'cpu'\n\nvar groupBy = []\n\nvar whereFilter = lambda: TRUE\n\nvar period = 10m\n\nvar every = 30s\n\nvar name = 'Untitled Rule'\n\nvar idVar = name + ':{{.Group}}'\n\nvar message = 'too much spam'\n\nvar idTag = 'alertID'\n\nvar levelTag = 'level'\n\nvar messageField = 'message'\n\nvar durationField = 'duration'\n\nvar outputDB = 'chronograf'\n\nvar outputRP = 'autogen'\n\nvar outputMeasurement = 'alerts'\n\nvar triggerType = 'threshold'\n\nvar details = 'muh body'\n\nvar crit = 10\n\nvar data = stream\n |from()\n .database(db)\n .retentionPolicy(rp)\n .measurement(measurement)\n .groupBy(groupBy)\n .where(whereFilter)\n |window()\n .period(period)\n .every(every)\n .align()\n |max('usage_system')\n .as('value')\n\nvar trigger = data\n |alert()\n .crit(lambda: \"value\" > crit)\n .stateChangesOnly()\n .message(message)\n .id(idVar)\n .idTag(idTag)\n .levelTag(levelTag)\n .messageField(messageField)\n .durationField(durationField)\n .details(details)\n .alerta()\n\ntrigger\n |influxDBOut()\n .create()\n .database(outputDB)\n .retentionPolicy(outputRP)\n .measurement(outputMeasurement)\n .tag('alertName', name)\n .tag('triggerType', triggerType)\n\ntrigger\n |httpOut('output')\n", - "status": "enabled", - "links": { - "self": "/chronograf/v1/sources/5/kapacitors/5/rules/chronograf-v1-b2b065ea-79bd-4e4f-8c0d-d0ef68477d38", - "kapacitor": "/chronograf/v1/sources/5/kapacitors/5/proxy?path=%2Fkapacitor%2Fv1%2Ftasks%2Fchronograf-v1-b2b065ea-79bd-4e4f-8c0d-d0ef68477d38", - "output": "/chronograf/v1/sources/5/kapacitors/5/proxy?path=%2Fkapacitor%2Fv1%2Ftasks%2Fchronograf-v1-b2b065ea-79bd-4e4f-8c0d-d0ef68477d38%2Foutput" - } - }, - "required": [ - "query", - "every", - "trigger" - ], - "properties": { - "id": { - "type": "string", - "description": "ID for this rule; the ID is shared with kapacitor" - }, - "query": { - "$ref": "#/definitions/QueryConfig" - }, - "name": { - "type": "string", - "description": "User facing name of the alerting rule" - }, - "every": { - "type": "string", - "description": "Golang duration string specifying how often the alert condition is checked" - }, - "alerts": { - "type": "array", - "description": "Array of alerting services to warn if the alert is triggered", - "items": { - "type": "string", - "enum": [ - "alerta", - "post", - "http", - "hipchat", - "opsgenie", - "pagerduty", - "victorops", - "smtp", - "email", - "exec", - "sensu", - "slack", - "talk", - "telegram", - "tcp" - ] - } - }, - "alertNodes": { - "type": "array", - "description": "Arguments and properties to add to alert", - "items": { - "$ref": "#/definitions/KapacitorNode" - } - }, - "message": { - "type": "string", - "description": "Message to send when alert occurs." - }, - "details": { - "type": "string", - "description": "Template for constructing a detailed HTML message for the alert. (Currently, only used for email/smtp" - }, - "trigger": { - "type": "string", - "description": "Trigger defines the alerting structure; deadman alert if no data are received for the specified time range; relative alert if the data change relative to the data in a different time range; threshold alert if the data cross a boundary", - "enum": [ - "deadman", - "relative", - "threshold" - ] - }, - "values": { - "type": "object", - "description": "Alerting logic for trigger type", - "properties": { - "change": { - "description": "Specifies if the change is percent or absolute", - "type": "string", - "enum": [ - "% change", - "change" - ] - }, - "period": { - "description": "Length of time before deadman is alerted (golang duration)", - "type": "string" - }, - "shift": { - "description": "Amount of time to look into the past to compare to the present (golang duration)", - "type": "string" - }, - "operator": { - "description": "Operator for alert comparison", - "type": "string", - "enum": [ - "greater than", - "less than", - "equal to or less than", - "equal to or greater", - "equal to", - "not equal to", - "inside range", - "outside range" - ] - }, - "value": { - "description": "Value is the boundary value when alert goes critical", - "type": "string" - }, - "rangeValue": { - "description": "Optional value for range comparisions", - "type": "string" - } - } - }, - "tickscript": { - "type": "string", - "description": "TICKscript representing this rule" - }, - "status": { - "type": "string", - "description": "Represents if this rule is enabled or disabled in kapacitor", - "enum": [ - "enabled", - "disabled" - ] - }, - "links": { - "type": "object", - "required": [ - "self", - "kapacitor" - ], - "properties": { - "self": { - "description": "Self link pointing to this rule resource", - "type": "string", - "format": "uri" - }, - "kapacitor": { - "description": "Link pointing to the kapacitor proxy for this rule including the path query parameter.", - "type": "string", - "format": "uri" - }, - "output": { - "description": "Link pointing to the kapacitor httpOut node of the tickscript; includes the path query argument", - "type": "string", - "format": "uri" - } - } - } - } - }, - "Sources": { - "type": "object", - "required": [ - "sources" - ], - "properties": { - "sources": { - "type": "array", - "items": { - "$ref": "#/definitions/Source" - } - } - } - }, - "Source": { - "type": "object", - "example": { - "id": "4", - "name": "Influx 1", - "url": "http://localhost:8086", - "default": false, - "telegraf": "telegraf", - "links": { - "self": "/chronograf/v1/sources/4", - "kapacitors": "/chronograf/v1/sources/4/kapacitors", - "proxy": "/chronograf/v1/sources/4/proxy", - "write": "/chronograf/v1/sources/4/write", - "queries": "/chronograf/v1/sources/4/queries", - "permissions": "/chronograf/v1/sources/4/permissions", - "users": "/chronograf/v1/sources/4/users", - "roles": "/chronograf/v1/sources/4/roles" - } - }, - "required": [ - "url" - ], - "properties": { - "id": { - "type": "string", - "description": "Unique identifier representing a specific data source.", - "readOnly": true - }, - "name": { - "type": "string", - "description": "User facing name of data source" - }, - "type": { - "type": "string", - "description": "Format of the data source", - "readOnly": true, - "enum": [ - "influx", - "influx-enterprise", - "influx-relay" - ] - }, - "username": { - "type": "string", - "description": "Username for authentication to data source" - }, - "password": { - "type": "string", - "description": "Password is in cleartext." - }, - "url": { - "type": "string", - "format": "url", - "description": "URL for the time series data source backend (e.g. http://localhost:8086)" - }, - "metaUrl": { - "type": "string", - "format": "url", - "description": "URL for the influxdb meta node" - }, - "insecureSkipVerify": { - "type": "boolean", - "description": "True means any certificate presented by the source is accepted. Typically used for self-signed certs. Probably should only be used for testing." - }, - "default": { - "type": "boolean", - "description": "Indicates whether this source is the default source" - }, - "telegraf": { - "type": "string", - "description": "Database where telegraf information is stored for this source", - "default": "telegraf" - }, - "links": { - "type": "object", - "properties": { - "self": { - "type": "string", - "description": "Self link mapping to this resource", - "format": "url" - }, - "proxy": { - "type": "string", - "description": "URL location of proxy endpoint for this source", - "format": "url" - }, - "write": { - "type": "string", - "description": "URL location of write endpoint for this source", - "format": "url" - }, - "queries": { - "type": "string", - "description": "URL location of the queries endpoint for this source", - "format": "url" - }, - "kapacitors": { - "type": "string", - "description": "URL location of the kapacitors endpoint for this source", - "format": "url" - }, - "users": { - "type": "string", - "description": "URL location of the users endpoint for this source", - "format": "url" - }, - "permissions": { - "type": "string", - "description": "URL location of the permissions endpoint for this source", - "format": "url" - }, - "roles": { - "type": "string", - "description": "Optional path to the roles endpoint IFF it is supported on this source", - "format": "url" - } - } - } - } - }, - "Proxy": { - "type": "object", - "example": { - "query": "select $myfield from cpu where time > now() - 10m", - "db": "telegraf", - "rp": "autogen", - "tempVars": [ - { - "tempVar": "$myfield", - "values": [ - { - "type": "fieldKey", - "value": "usage_user" - } - ] - } - ] - }, - "required": [ - "query" - ], - "properties": { - "query": { - "type": "string" - }, - "db": { - "type": "string" - }, - "rp": { - "type": "string" - }, - "tempVars": { - "type": "array", - "description": "Template variables to replace within an InfluxQL query", - "items": { - "$ref": "#/definitions/TemplateVariable" - } - } - } - }, - "TemplateVariable": { - "type": "object", - "description": "Named variable within an InfluxQL query to be replaced with values", - "properties": { - "tempVar": { - "type": "string", - "description": "String to replace within an InfluxQL statement" - }, - "values": { - "type": "array", - "description": "Values used to replace tempVar.", - "items": { - "$ref": "#/definitions/TemplateValue" - } - } - } - }, - "TemplateValue": { - "type": "object", - "description": "Value use to replace a template in an InfluxQL query. The type governs the output format", - "properties": { - "value": { - "type": "string", - "description": "Specific value that will be encoded based on type" - }, - "type": { - "type": "string", - "enum": [ - "csv", - "tagKey", - "tagValue", - "fieldKey", - "timeStamp" - ], - "description": "The type will change the format of the output value. tagKey/fieldKey are double quoted; tagValue are single quoted; csv and timeStamp are not quoted." - } - } - }, - "ProxyResponse": { - "type": "object", - "example": { - "results": [ - { - "statement_id": 0, - "series": [ - { - "name": "cpu", - "columns": [ - "time", - "cpu", - "host", - "usage_guest", - "usage_guest_nice", - "usage_idle", - "usage_iowait", - "usage_irq", - "usage_nice", - "usage_softirq", - "usage_steal", - "usage_system", - "usage_user" - ], - "values": [ - [ - 1487785510000, - "cpu-total", - "ChristohersMBP2.lan", - 0, - 0, - 76.6916354556804, - 0, - 0, - 0, - 0, - 0, - 4.781523096129837, - 18.526841448189764 - ] - ] - } - ] - } - ] - }, - "properties": { - "results": { - "description": "results from influx", - "type": "object" - } - } - }, - "Roles": { - "type": "array", - "items": { - "$ref": "#/definitions/Role" - }, - "example": { - "roles": [ - { - "users": [ - { - "name": "admin", - "links": { - "self": "/chronograf/v1/sources/3/users/admin" - } - } - ], - "name": "timetravelers", - "permissions": [ - { - "scope": "database", - "name": "telegraf", - "allowed": [ - "ReadData", - "WriteData" - ] - } - ], - "links": { - "self": "/chronograf/v1/sources/3/roles/timetravelers" - } - } - ] - } - }, - "Role": { - "type": "object", - "required": [ - "name" - ], - "properties": { - "name": { - "type": "string", - "description": "Unique name of the role", - "maxLength": 254, - "minLength": 1 - }, - "users": { - "$ref": "#/definitions/Users" - }, - "permissions": { - "$ref": "#/definitions/Permissions" - }, - "links": { - "type": "object", - "description": "URL relations of this role", - "properties": { - "self": { - "type": "string", - "format": "url", - "description": "URI of resource." - } - } - } - }, - "example": { - "users": [ - { - "name": "admin", - "links": { - "self": "/chronograf/v1/sources/3/users/admin" - } - } - ], - "name": "timetravelers", - "permissions": [ - { - "scope": "database", - "name": "telegraf", - "allowed": [ - "ReadData", - "WriteData" - ] - } - ], - "links": { - "self": "/chronograf/v1/sources/3/roles/timetravelers" - } - } - }, - "Users": { - "type": "object", - "properties": { - "users": { - "type": "array", - "items": { - "$ref": "#/definitions/User" - } - } - }, - "example": { - "users": [ - { - "name": "docbrown", - "permissions": [ - { - "scope": "all", - "allowed": [ - "ViewAdmin", - "ViewChronograf", - "CreateDatabase", - "CreateUserAndRole", - "DropDatabase", - "DropData", - "ReadData", - "WriteData", - "ManageShard", - "ManageContinuousQuery", - "ManageQuery", - "ManageSubscription", - "Monitor", - "KapacitorAPI" - ] - } - ], - "roles": [ - { - "name": "timetravelers", - "permissions": [ - { - "scope": "database", - "name": "telegraf", - "allowed": [ - "ReadData", - "WriteData" - ] - } - ], - "links": { - "self": "/chronograf/v1/sources/3/roles/timetravelers" - } - } - ], - "links": { - "self": "/chronograf/v1/sources/3/users/docbrown" - } - } - ] - } - }, - "User": { - "type": "object", - "properties": { - "name": { - "type": "string", - "description": "Unique name of the user", - "maxLength": 254, - "minLength": 1 - }, - "password": { - "type": "string" - }, - "permissions": { - "$ref": "#/definitions/Permissions" - }, - "roles": { - "$ref": "#/definitions/Roles" - }, - "links": { - "type": "object", - "description": "URL relations of this user", - "properties": { - "self": { - "type": "string", - "format": "url", - "description": "URI of resource." - } - } - } - }, - "example": { - "name": "docbrown", - "permissions": [ - { - "scope": "all", - "allowed": [ - "ViewAdmin", - "ViewChronograf", - "CreateDatabase", - "CreateUserAndRole", - "DropDatabase", - "DropData", - "ReadData", - "WriteData", - "ManageShard", - "ManageContinuousQuery", - "ManageQuery", - "ManageSubscription", - "Monitor", - "KapacitorAPI" - ] - } - ], - "roles": [ - { - "name": "timetravelers", - "permissions": [ - { - "scope": "database", - "name": "telegraf", - "allowed": [ - "ReadData", - "WriteData" - ] - } - ], - "links": { - "self": "/chronograf/v1/sources/3/roles/timetravelers" - } - } - ], - "links": { - "self": "/chronograf/v1/sources/3/users/docbrown" - } - } - }, - "Permissions": { - "description": "Permissions represent the entire set of permissions a User or Role may have", - "type": "array", - "items": { - "$ref": "#/definitions/Permission" - } - }, - "Permission": { - "description": "Permission is a specific allowance for User or Role bound to a scope of the data source", - "type": "object", - "required": [ - "scope", - "allowed" - ], - "properties": { - "scope": { - "type": "string", - "description": "Describes if the permission is for all databases or restricted to one database", - "enum": [ - "all", - "database" - ] - }, - "name": { - "type": "string", - "description": "If the scope is database this identifies the name of the database" - }, - "allowed": { - "$ref": "#/definitions/Allowances" - } - }, - "example": { - "scope": "database", - "name": "telegraf", - "allowed": [ - "READ", - "WRITE" - ] - } - }, - "AllPermissions": { - "description": "All possible permissions for this particular datasource. Used as a static list", - "type": "object", - "properties": { - "permissions": { - "$ref": "#/definitions/Permissions" - }, - "links": { - "type": "object", - "properties": { - "self": { - "description": "Relative link back to the permissions endpoint", - "type": "string", - "format": "uri" - }, - "source": { - "description": "Relative link to host with these permissiosn", - "type": "string", - "format": "uri" - } - } - } - } - }, - "Allowances": { - "description": "Allowances defines what actions a user can have on a scoped permission", - "type": "array", - "items": { - "type": "string", - "description": "OSS InfluxDB is READ and WRITE. Enterprise is all others", - "enum": [ - "READ", - "WRITE", - "NoPermissions", - "ViewAdmin", - "ViewChronograf", - "CreateDatabase", - "CreateUserAndRole", - "AddRemoveNode", - "DropDatabase", - "DropData", - "ReadData", - "WriteData", - "Rebalance", - "ManageShard", - "ManageContinuousQuery", - "ManageQuery", - "ManageSubscription", - "Monitor", - "CopyShard", - "KapacitorAPI", - "KapacitorConfigAPI" - ] - } - }, - "Layouts": { - "required": [ - "layouts" - ], - "type": "object", - "properties": { - "layouts": { - "type": "array", - "items": { - "$ref": "#/definitions/Layout" - } - } - } - }, - "Layout": { - "type": "object", - "required": [ - "cells", - "app", - "measurement" - ], - "properties": { - "id": { - "type": "string", - "description": "ID is an opaque string that uniquely identifies this layout." - }, - "app": { - "type": "string", - "description": "App is the user facing name of this Layout" - }, - "measurement": { - "type": "string", - "description": "Measurement is the descriptive name of the time series data." - }, - "cells": { - "type": "array", - "description": "Cells are the individual visualization elements.", - "items": { - "$ref": "#/definitions/Cell" - } - }, - "link": { - "$ref": "#/definitions/Link" - } - }, - "example": { - "id": "0e980b97-c162-487b-a815-3f955df62430", - "app": "docker", - "measurement": "docker_container_net", - "autoflow": true, - "cells": [ - { - "x": 0, - "y": 0, - "w": 4, - "h": 4, - "i": "4c79cefb-5152-410c-9b88-74f9bff7ef23", - "name": "Docker - Container Network", - "queries": [ - { - "query": "SELECT derivative(mean(\"tx_bytes\"), 10s) AS \"net_tx_bytes\" FROM \"docker_container_net\"", - "groupbys": [ - "\"container_name\"" - ] - }, - { - "query": "SELECT derivative(mean(\"rx_bytes\"), 10s) AS \"net_rx_bytes\" FROM \"docker_container_net\"", - "groupbys": [ - "\"container_name\"" - ] - } - ], - "type": "" - } - ], - "link": { - "href": "/chronograf/v1/layouts/0e980b97-c162-487b-a815-3f955df62430", - "rel": "self" - } - } - }, - "Mappings": { - "type": "object", - "required": [ - "mappings" - ], - "properties": { - "mappings": { - "type": "array", - "items": { - "$ref": "#/definitions/Mapping" - } - } - } - }, - "Mapping": { - "type": "object", - "required": [ - "measurement", - "name" - ], - "properties": { - "measurement": { - "description": "The measurement where data for this mapping is found", - "type": "string" - }, - "name": { - "description": "The application name which will be assigned to the corresponding measurement", - "type": "string" - } - }, - "example": { - "measurement": "riak", - "name": "riak" - } - }, - "Cell": { - "type": "object", - "required": [ - "i", - "x", - "y", - "w", - "h" - ], - "properties": { - "i": { - "description": "Unique ID of Cell", - "type": "string", - "format": "uuid4" - }, - "x": { - "description": "X-coordinate of Cell in the Layout", - "type": "integer", - "format": "int32" - }, - "y": { - "description": "Y-coordinate of Cell in the Layout", - "type": "integer", - "format": "int32" - }, - "w": { - "description": "Width of Cell in the Layout", - "type": "integer", - "format": "int32" - }, - "h": { - "description": "Height of Cell in the Layout", - "type": "integer", - "format": "int32" - }, - "name": { - "description": "Cell name", - "type": "string" - }, - "queries": { - "description": "Time-series data queries for Cell.", - "type": "array", - "items": { - "$ref": "#/definitions/LayoutQuery" - } - }, - "type": { - "description": "Cell visualization type", - "type": "string" - } - }, - "example": { - "x": 5, - "y": 5, - "w": 4, - "h": 4, - "name": "usage_user", - "queries": [ - { - "query": "SELECT mean(\"usage_user\") AS \"usage_user\" FROM \"cpu\"", - "label": "%" - } - ], - "type": "line" - } - }, - "LayoutQuery": { - "type": "object", - "required": [ - "query" - ], - "properties": { - "label": { - "description": "Optional Y-axis user-facing label for this query", - "type": "string" - }, - "range": { - "description": "Optional default range of the Y-axis", - "type": "object", - "required": [ - "upper", - "lower" - ], - "properties": { - "upper": { - "description": "Upper bound of the display range of the Y-axis", - "type": "integer", - "format": "int64" - }, - "lower": { - "description": "Lower bound of the display range of the Y-axis", - "type": "integer", - "format": "int64" - } - } - }, - "query": { - "type": "string" - }, - "wheres": { - "description": "Defines the condition clauses for influxdb", - "type": "array", - "items": { - "type": "string" - } - }, - "groupbys": { - "description": "Defines the group by clauses for influxdb", - "type": "array", - "items": { - "type": "string" - } - } - }, - "example": { - "label": "# warnings", - "query": "SELECT count(\"check_id\") as \"Number Warning\" FROM consul_health_checks", - "wheres": [ - "\"status\" = 'warning'" - ], - "groupbys": [ - "\"service_name\"" - ] - } - }, - "DashboardQuery": { - "type": "object", - "required": [ - "query" - ], - "properties": { - "label": { - "description": "Optional Y-axis user-facing label for this query", - "type": "string" - }, - "range": { - "description": "Optional default range of the Y-axis", - "type": "object", - "required": [ - "upper", - "lower" - ], - "properties": { - "upper": { - "description": "Upper bound of the display range of the Y-axis", - "type": "integer", - "format": "int64" - }, - "lower": { - "description": "Lower bound of the display range of the Y-axis", - "type": "integer", - "format": "int64" - } - } - }, - "query": { - "type": "string" - }, - "queryConfig": { - "$ref": "#/definitions/QueryConfig" - } - }, - "example": { - "id": 4, - "cells": [ - { - "x": 0, - "y": 0, - "w": 4, - "h": 4, - "name": "", - "queries": [ - { - "query": "SELECT mean(\"usage_user\") AS \"usage_user\" FROM \"cpu\"", - "label": "%", - "queryConfig": { - "database": "", - "measurement": "cpu", - "retentionPolicy": "", - "fields": [ - { - "field": "usage_user", - "funcs": [ - "mean" - ] - } - ], - "tags": {}, - "groupBy": { - "time": "", - "tags": [] - }, - "areTagsAccepted": false - } - } - ], - "type": "line" - } - ], - "name": "dashboard name", - "links": { - "self": "/chronograf/v1/dashboards/4" - } - } - }, - "Dashboards": { - "description": "a list of dashboards", - "type": "object", - "properties": { - "dashboards": { - "type": "array", - "items": { - "$ref": "#/definitions/Dashboard" - } - } - } - }, - "Dashboard": { - "type": "object", - "properties": { - "id": { - "description": "the unique dashboard id", - "type": "integer", - "format": "int64" - }, - "cells": { - "description": "a list of dashboard visualizations", - "type": "array", - "items": { - "description": "cell visualization information", - "type": "object", - "properties": { - "x": { - "description": "X-coordinate of Cell in the Dashboard", - "type": "integer", - "format": "int32" - }, - "y": { - "description": "Y-coordinate of Cell in the Dashboard", - "type": "integer", - "format": "int32" - }, - "w": { - "description": "Width of Cell in the Dashboard", - "type": "integer", - "format": "int32", - "minimum": 1, - "default": 4 - }, - "h": { - "description": "Height of Cell in the Dashboard", - "type": "integer", - "format": "int32", - "minimum": 1, - "default": 4 - }, - "name": { - "description": "Name of Cell in the Dashboard", - "type": "string" - }, - "queries": { - "description": "Time-series data queries for Cell.", - "type": "array", - "items": { - "$ref": "#/definitions/DashboardQuery" - } - }, - "type": { - "description": "Cell visualization type", - "type": "string", - "enum": [ - "single-stat", - "line", - "line-plus-single-stat", - "line-stacked", - "line-stepplot", - "bar" - ], - "default": "line" - }, - "links": { - "type": "object", - "properties": { - "self": { - "type": "string", - "description": "Self link mapping to this resource", - "format": "url" - } - } - } - } - } - }, - "name": { - "description": "the user-facing name of the dashboard", - "type": "string" - }, - "links": { - "type": "object", - "properties": { - "self": { - "type": "string", - "description": "Self link mapping to this resource", - "format": "url" - } - } - } - }, - "example": { - "id": 4, - "cells": [ - { - "x": 5, - "y": 5, - "w": 4, - "h": 4, - "name": "usage_user", - "queries": [ - { - "query": "SELECT mean(\"usage_user\") AS \"usage_user\" FROM \"cpu\"", - "db": "telegraf", - "label": "%" - } - ], - "type": "line" - }, - { - "x": 0, - "y": 0, - "w": 4, - "h": 4, - "name": "usage_system", - "queries": [ - { - "query": "SELECT mean(\"usage_system\") AS \"usage_system\" FROM \"cpu\"", - "db": "telegraf", - "label": "%" - } - ], - "type": "line" - } - ], - "name": "lalalalala", - "links": { - "self": "/chronograf/v1/dashboards/4" - } - } - }, - "Routes": { - "type": "object", - "properties": { - "me": { - "description": "Location of the me endpoint.", - "type": "string", - "format": "url" - }, - "layouts": { - "description": "Location of the layouts endpoint", - "type": "string", - "format": "url" - }, - "sources": { - "description": "Location of the sources endpoint", - "type": "string", - "format": "url" - }, - "mappings": { - "description": "Location of the application mappings endpoint", - "type": "string", - "format": "url" - }, - "dashboards": { - "description": "location of the dashboards endpoint", - "type": "string", - "format": "url" - }, - "external": { - "description": "external links provided to client, ex. status feed URL", - "type": "object", - "properties": { - "statusFeed": { - "description": "link to a JSON Feed for the News Feed on client's Status Page", - "type": "string", - "format": "url" - }, - "custom": { - "description": "a collection of custom links set by the user to be rendered in the client User menu", - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "url": { - "type": "string", - "format": "url" - } - } - } - } - } - } - }, - "example": { - "layouts": "/chronograf/v1/layouts", - "mappings": "/chronograf/v1/mappings", - "sources": "/chronograf/v1/sources", - "me": "/chronograf/v1/me", - "dashboards": "/chronograf/v1/dashboards", - "external": { - "statusFeed": "http://news.influxdata.com/feed.json", - "custom": [ - { - "name": "InfluxData", - "url": "https://www.influxdata.com" - } - ] - } - } - }, - "Link": { - "type": "object", - "required": [ - "rel", - "href" - ], - "readOnly": true, - "description": "URI of resource.", - "properties": { - "rel": { - "type": "string" - }, - "href": { - "type": "string", - "format": "url" - } - } - }, - "Error": { - "type": "object", - "properties": { - "code": { - "type": "integer", - "format": "int32" - }, - "message": { - "type": "string" - } - } - } - } + "swagger": "2.0", + "info": { + "title": "Chronograf", + "description": "API endpoints for Chronograf", + "version": "1.3.2.0" + }, + "schemes": ["http"], + "basePath": "/chronograf/v1", + "consumes": ["application/json"], + "produces": ["application/json"], + "paths": { + "/": { + "get": { + "tags": ["routes"], + "summary": "Lists all the endpoints", + "description": "List of the endpoints.", + "responses": { + "200": { + "description": "Returns the links to the top level endpoints.", + "schema": { + "$ref": "#/definitions/Routes" + } + }, + "default": { + "description": "Unexpected internal service error", + "schema": { + "$ref": "#/definitions/Error" + } + } + } + } + }, + "/sources": { + "get": { + "tags": ["sources"], + "summary": "Configured data sources", + "description": "These data sources store time series data.", + "responses": { + "200": { + "description": "An array of data sources", + "schema": { + "$ref": "#/definitions/Sources" + } + }, + "default": { + "description": "Unexpected internal service error", + "schema": { + "$ref": "#/definitions/Error" + } + } + } + }, + "post": { + "tags": ["sources"], + "summary": "Create new data source", + "parameters": [ + { + "name": "source", + "in": "body", + "description": "Configuration options for data source", + "schema": { + "$ref": "#/definitions/Source" + } + } + ], + "responses": { + "201": { + "description": "Successfully create data source", + "headers": { + "Location": { + "type": "string", + "format": "url", + "description": "Location of the newly created data source resource." + } + }, + "schema": { + "$ref": "#/definitions/Source" + } + }, + "default": { + "description": "A processing or an unexpected error.", + "schema": { + "$ref": "#/definitions/Error" + } + } + } + } + }, + "/sources/{id}": { + "get": { + "tags": ["sources"], + "parameters": [ + { + "name": "id", + "in": "path", + "type": "string", + "description": "ID of the data source", + "required": true + } + ], + "summary": "Configured data sources", + "description": "These data sources store time series data.", + "responses": { + "200": { + "description": "Data source used to supply time series information.", + "schema": { + "$ref": "#/definitions/Source" + } + }, + "404": { + "description": "Unknown source id", + "schema": { + "$ref": "#/definitions/Error" + } + }, + "default": { + "description": "Unexpected internal service error", + "schema": { + "$ref": "#/definitions/Error" + } + } + } + }, + "patch": { + "tags": ["sources"], + "summary": "Update data source configuration", + "parameters": [ + { + "name": "id", + "in": "path", + "type": "string", + "description": "ID of a data source", + "required": true + }, + { + "name": "config", + "in": "body", + "description": "data source configuration", + "schema": { + "$ref": "#/definitions/Source" + }, + "required": true + } + ], + "responses": { + "200": { + "description": "Data source's configuration was changed", + "schema": { + "$ref": "#/definitions/Source" + } + }, + "404": { + "description": "Happens when trying to access a non-existent data source.", + "schema": { + "$ref": "#/definitions/Error" + } + }, + "default": { + "description": "A processing or an unexpected error.", + "schema": { + "$ref": "#/definitions/Error" + } + } + } + }, + "delete": { + "tags": ["sources"], + "parameters": [ + { + "name": "id", + "in": "path", + "type": "string", + "description": "ID of the source", + "required": true + } + ], + "summary": "This specific data source will be removed from the data store. All associated kapacitor resources and kapacitor rules resources are also removed.", + "responses": { + "204": { + "description": "data source has been removed" + }, + "404": { + "description": "Unknown data source id", + "schema": { + "$ref": "#/definitions/Error" + } + }, + "default": { + "description": "Unexpected internal service error", + "schema": { + "$ref": "#/definitions/Error" + } + } + } + } + }, + "/sources/{id}/queries": { + "post": { + "tags": ["sources", "queries"], + "description": "Used to analyze queries for structure`", + "parameters": [ + { + "name": "id", + "in": "path", + "type": "string", + "description": "ID of the data source", + "required": true + }, + { + "name": "queries", + "in": "body", + "description": "Query Parameters", + "schema": { + "$ref": "#/definitions/Queries" + }, + "required": true + } + ], + "responses": { + "200": { + "description": "Result of the analysis of the query.", + "schema": { + "$ref": "#/definitions/QueriesResponse" + } + }, + "404": { + "description": "Data source id does not exist.", + "schema": { + "$ref": "#/definitions/Error" + } + }, + "default": { + "description": "Unexpected internal service error", + "schema": { + "$ref": "#/definitions/Error" + } + } + } + } + }, + "/sources/{id}/proxy": { + "post": { + "tags": ["sources", "proxy"], + "description": "Query the backend time series data source and return the response according to `format`", + "parameters": [ + { + "name": "id", + "in": "path", + "type": "string", + "description": "ID of the data source", + "required": true + }, + { + "name": "query", + "in": "body", + "description": "Query Parameters", + "schema": { + "$ref": "#/definitions/Proxy" + }, + "required": true + } + ], + "responses": { + "200": { + "description": "Result of the query from the backend time series data source.", + "schema": { + "$ref": "#/definitions/ProxyResponse" + } + }, + "400": { + "description": "Any query that results in a data source error (syntax error, etc) will cause this response. The error message will be passed back in the body", + "schema": { + "$ref": "#/definitions/Error" + } + }, + "404": { + "description": "Data source id does not exist.", + "schema": { + "$ref": "#/definitions/Error" + } + }, + "408": { + "description": "Timeout trying to query data source.", + "schema": { + "$ref": "#/definitions/Error" + } + }, + "default": { + "description": "Unexpected internal service error", + "schema": { + "$ref": "#/definitions/Error" + } + } + } + } + }, + "/sources/{id}/write": { + "post": { + "tags": ["sources", "write"], + "description": "Write points to the backend time series data source", + "parameters": [ + { + "name": "id", + "in": "path", + "type": "string", + "description": "ID of the data source", + "required": true + }, + { + "name": "query", + "in": "body", + "description": "Write Parameters", + "schema": { + "type": "string", + "format": "byte" + }, + "required": true + }, + { + "name": "db", + "in": "query", + "description": "Sets the target database for the write.", + "type": "string", + "required": true + }, + { + "name": "rp", + "in": "query", + "description": "Sets the target retention policy for the write. InfluxDB writes to the DEFAULT retention policy if you do not specify a retention policy.", + "type": "string" + }, + { + "name": "precision", + "in": "query", + "description": "Sets the precision for the supplied Unix time values. InfluxDB assumes that timestamps are in nanoseconds if you do not specify precision.", + "type": "string", + "enum": ["ns", "u", "ms", "s", "m", "h"] + }, + { + "name": "consistency", + "in": "query", + "description": "Sets the write consistency for the point. InfluxDB assumes that the write consistency is one if you do not specify consistency. See the InfluxEnterprise documentation for detailed descriptions of each consistency option.", + "type": "string", + "enum": ["any", "one", "quorum", "all"] + } + ], + "responses": { + "204": { + "description": "Points written successfuly to database." + }, + "400": { + "description": "Any query that results in a data source error (syntax error, etc) will cause this response. The error message will be passed back in the body", + "schema": { + "$ref": "#/definitions/Error" + } + }, + "404": { + "description": "Data source id does not exist.", + "schema": { + "$ref": "#/definitions/Error" + } + }, + "408": { + "description": "Timeout trying to query data source.", + "schema": { + "$ref": "#/definitions/Error" + } + }, + "default": { + "description": "Unexpected internal service error", + "schema": { + "$ref": "#/definitions/Error" + } + } + } + } + }, + "/sources/{id}/permissions": { + "get": { + "tags": ["sources", "users"], + "summary": "Retrieve possible permissions for this data source", + "parameters": [ + { + "name": "id", + "in": "path", + "type": "string", + "description": "ID of the data source", + "required": true + } + ], + "responses": { + "200": { + "description": "Listing of all possible permissions", + "schema": { + "$ref": "#/definitions/AllPermissions" + } + }, + "404": { + "description": "Data source id does not exist.", + "schema": { + "$ref": "#/definitions/Error" + } + }, + "default": { + "description": "A processing or an unexpected error.", + "schema": { + "$ref": "#/definitions/Error" + } + } + } + } + }, + "/sources/{id}/users": { + "get": { + "tags": ["sources", "users"], + "summary": "Retrieve all data sources users", + "parameters": [ + { + "name": "id", + "in": "path", + "type": "string", + "description": "ID of the data source", + "required": true + } + ], + "responses": { + "200": { + "description": "Listing of all users", + "schema": { + "$ref": "#/definitions/Users" + } + }, + "404": { + "description": "Data source id does not exist.", + "schema": { + "$ref": "#/definitions/Error" + } + }, + "default": { + "description": "A processing or an unexpected error.", + "schema": { + "$ref": "#/definitions/Error" + } + } + } + }, + "post": { + "tags": ["sources", "users"], + "summary": "Create new user for this data source", + "parameters": [ + { + "name": "id", + "in": "path", + "type": "string", + "description": "ID of the data source", + "required": true + }, + { + "name": "user", + "in": "body", + "description": "Configuration options for new user", + "schema": { + "$ref": "#/definitions/User" + } + } + ], + "responses": { + "201": { + "description": "Successfully created new user", + "headers": { + "Location": { + "type": "string", + "format": "url", + "description": "Location of the newly created user resource." + } + }, + "schema": { + "$ref": "#/definitions/User" + } + }, + "404": { + "description": "Data source id does not exist.", + "schema": { + "$ref": "#/definitions/Error" + } + }, + "default": { + "description": "A processing or an unexpected error.", + "schema": { + "$ref": "#/definitions/Error" + } + } + } + } + }, + "/sources/{id}/users/{user_id}": { + "get": { + "tags": ["sources", "users"], + "parameters": [ + { + "name": "id", + "in": "path", + "type": "string", + "description": "ID of the data source", + "required": true + }, + { + "name": "user_id", + "in": "path", + "type": "string", + "description": "ID of the specific user", + "required": true + } + ], + "summary": "Returns information about a specific user", + "description": "Specific User within a data source", + "responses": { + "200": { + "description": "Information relating to the user", + "schema": { + "$ref": "#/definitions/User" + } + }, + "404": { + "description": "Unknown user or unknown source", + "schema": { + "$ref": "#/definitions/Error" + } + }, + "default": { + "description": "Unexpected internal service error", + "schema": { + "$ref": "#/definitions/Error" + } + } + } + }, + "patch": { + "tags": ["sources", "users"], + "summary": "Update user configuration", + "parameters": [ + { + "name": "id", + "in": "path", + "type": "string", + "description": "ID of the data source", + "required": true + }, + { + "name": "user_id", + "in": "path", + "type": "string", + "description": "ID of the specific user", + "required": true + }, + { + "name": "config", + "in": "body", + "description": "user configuration", + "schema": { + "$ref": "#/definitions/User" + }, + "required": true + } + ], + "responses": { + "200": { + "description": "Users's configuration was changed", + "schema": { + "$ref": "#/definitions/User" + } + }, + "404": { + "description": "Happens when trying to access a non-existent user or source.", + "schema": { + "$ref": "#/definitions/Error" + } + }, + "default": { + "description": "A processing or an unexpected error.", + "schema": { + "$ref": "#/definitions/Error" + } + } + } + }, + "delete": { + "tags": ["sources", "users"], + "parameters": [ + { + "name": "id", + "in": "path", + "type": "string", + "description": "ID of the data source", + "required": true + }, + { + "name": "user_id", + "in": "path", + "type": "string", + "description": "ID of the specific user", + "required": true + } + ], + "summary": "This specific user will be removed from the data source", + "responses": { + "204": { + "description": "User has been removed" + }, + "404": { + "description": "Unknown user id or data source", + "schema": { + "$ref": "#/definitions/Error" + } + }, + "default": { + "description": "Unexpected internal service error", + "schema": { + "$ref": "#/definitions/Error" + } + } + } + } + }, + "/sources/{id}/roles": { + "get": { + "tags": ["sources", "users", "roles"], + "summary": "Retrieve all data sources roles. Available only in Influx Enterprise", + "parameters": [ + { + "name": "id", + "in": "path", + "type": "string", + "description": "ID of the data source", + "required": true + } + ], + "responses": { + "200": { + "description": "Listing of all roles", + "schema": { + "$ref": "#/definitions/Roles" + } + }, + "404": { + "description": "Data source id does not exist.", + "schema": { + "$ref": "#/definitions/Error" + } + }, + "default": { + "description": "A processing or an unexpected error.", + "schema": { + "$ref": "#/definitions/Error" + } + } + } + }, + "post": { + "tags": ["sources", "users", "roles"], + "summary": "Create new role for this data source", + "parameters": [ + { + "name": "id", + "in": "path", + "type": "string", + "description": "ID of the data source", + "required": true + }, + { + "name": "roleuser", + "in": "body", + "description": "Configuration options for new role", + "schema": { + "$ref": "#/definitions/Role" + } + } + ], + "responses": { + "201": { + "description": "Successfully created new role", + "headers": { + "Location": { + "type": "string", + "format": "url", + "description": "Location of the newly created role resource." + } + }, + "schema": { + "$ref": "#/definitions/Role" + } + }, + "404": { + "description": "Data source id does not exist.", + "schema": { + "$ref": "#/definitions/Error" + } + }, + "default": { + "description": "A processing or an unexpected error.", + "schema": { + "$ref": "#/definitions/Error" + } + } + } + } + }, + "/sources/{id}/roles/{role_id}": { + "get": { + "tags": ["sources", "users", "roles"], + "parameters": [ + { + "name": "id", + "in": "path", + "type": "string", + "description": "ID of the data source", + "required": true + }, + { + "name": "role_id", + "in": "path", + "type": "string", + "description": "ID of the specific role", + "required": true + } + ], + "summary": "Returns information about a specific role", + "description": "Specific role within a data source", + "responses": { + "200": { + "description": "Information relating to the role", + "schema": { + "$ref": "#/definitions/Role" + } + }, + "404": { + "description": "Unknown role or unknown source", + "schema": { + "$ref": "#/definitions/Error" + } + }, + "default": { + "description": "Unexpected internal service error", + "schema": { + "$ref": "#/definitions/Error" + } + } + } + }, + "patch": { + "tags": ["sources", "users", "roles"], + "summary": "Update role configuration", + "parameters": [ + { + "name": "id", + "in": "path", + "type": "string", + "description": "ID of the data source", + "required": true + }, + { + "name": "role_id", + "in": "path", + "type": "string", + "description": "ID of the specific role", + "required": true + }, + { + "name": "config", + "in": "body", + "description": "role configuration", + "schema": { + "$ref": "#/definitions/Role" + }, + "required": true + } + ], + "responses": { + "200": { + "description": "Roles's configuration was changed", + "schema": { + "$ref": "#/definitions/Role" + } + }, + "404": { + "description": "Happens when trying to access a non-existent role or source.", + "schema": { + "$ref": "#/definitions/Error" + } + }, + "default": { + "description": "A processing or an unexpected error.", + "schema": { + "$ref": "#/definitions/Error" + } + } + } + }, + "delete": { + "tags": ["sources", "users", "roles"], + "parameters": [ + { + "name": "id", + "in": "path", + "type": "string", + "description": "ID of the data source", + "required": true + }, + { + "name": "role_id", + "in": "path", + "type": "string", + "description": "ID of the specific role", + "required": true + } + ], + "summary": "This specific role will be removed from the data source", + "responses": { + "204": { + "description": "Role has been removed" + }, + "404": { + "description": "Unknown role id or data source", + "schema": { + "$ref": "#/definitions/Error" + } + }, + "default": { + "description": "Unexpected internal service error", + "schema": { + "$ref": "#/definitions/Error" + } + } + } + } + }, + "/sources/{id}/dbs/": { + "get": { + "tags": ["databases"], + "summary": "Retrieve all databases for a source", + "parameters": [ + { + "name": "id", + "in": "path", + "type": "string", + "description": "ID of the data source", + "required": true + } + ], + "responses": { + "200": { + "description": "Listing of all databases for a source", + "schema": { + "$ref": "#/definitions/Databases" + } + }, + "404": { + "description": "Data source id does not exist.", + "schema": { + "$ref": "#/definitions/Error" + } + }, + "default": { + "description": "A processing or an unexpected error.", + "schema": { + "$ref": "#/definitions/Error" + } + } + } + }, + "post": { + "tags": ["databases"], + "summary": "Create new database for a source", + "parameters": [ + { + "name": "id", + "in": "path", + "type": "string", + "description": "ID of the data source", + "required": true + }, + { + "name": "database", + "in": "body", + "description": "Configuration options for a database", + "schema": { + "$ref": "#/definitions/Database" + }, + "required": true + } + ], + "responses": { + "201": { + "description": "Database successfully created.", + "schema": { + "$ref": "#/definitions/Database" + } + }, + "404": { + "description": "Data source id does not exist.", + "schema": { + "$ref": "#/definitions/Error" + } + }, + "default": { + "description": "A processing or an unexpected error.", + "schema": { + "$ref": "#/definitions/Error" + } + } + } + } + }, + "/sources/{id}/dbs/{db_id}": { + "delete": { + "tags": ["databases"], + "summary": "Delete database for a source", + "parameters": [ + { + "name": "id", + "in": "path", + "type": "string", + "description": "ID of the data source", + "required": true + }, + { + "name": "db_id", + "in": "path", + "type": "string", + "description": "ID of the database", + "required": true + } + ], + "responses": { + "204": { + "description": "Database has been deleted" + }, + "404": { + "description": "Data source id does not exist.", + "schema": { + "$ref": "#/definitions/Error" + } + }, + "default": { + "description": "A processing or an unexpected error.", + "schema": { + "$ref": "#/definitions/Error" + } + } + } + } + }, + "/sources/{id}/dbs/{db_id}/rps": { + "get": { + "tags": ["retention policies"], + "summary": "Retrieve all retention policies for a database", + "parameters": [ + { + "name": "id", + "in": "path", + "type": "string", + "description": "ID of the data source", + "required": true + }, + { + "name": "db_id", + "in": "path", + "type": "string", + "description": "ID of the database", + "required": true + } + ], + "responses": { + "200": { + "description": "Listing of all retention policies for a database", + "schema": { + "$ref": "#/definitions/RetentionPolicies" + } + }, + "404": { + "description": "Specified retention policy does not exist.", + "schema": { + "$ref": "#/definitions/Error" + } + }, + "default": { + "description": "A processing or an unexpected error.", + "schema": { + "$ref": "#/definitions/Error" + } + } + } + }, + "post": { + "tags": ["retention policies"], + "summary": "Create new retention policy for a database", + "parameters": [ + { + "name": "id", + "in": "path", + "type": "string", + "description": "ID of the data source", + "required": true + }, + { + "name": "db_id", + "in": "path", + "type": "string", + "description": "ID of the database", + "required": true + }, + { + "name": "rp", + "in": "body", + "description": "Configuration options for the retention policy", + "schema": { + "$ref": "#/definitions/RetentionPolicy" + }, + "required": true + } + ], + "responses": { + "201": { + "description": "Retention Policy successfully created.", + "schema": { + "$ref": "#/definitions/RetentionPolicy" + } + }, + "404": { + "description": "Data source id does not exist.", + "schema": { + "$ref": "#/definitions/Error" + } + }, + "default": { + "description": "A processing or an unexpected error.", + "schema": { + "$ref": "#/definitions/Error" + } + } + } + } + }, + "/sources/{id}/dbs/{db_id}/rps/{rp_id}": { + "patch": { + "tags": ["retention policies"], + "summary": "Alter retention policy for a database", + "parameters": [ + { + "name": "id", + "in": "path", + "type": "string", + "description": "ID of the data source", + "required": true + }, + { + "name": "db_id", + "in": "path", + "type": "string", + "description": "ID of the database", + "required": true + }, + { + "name": "rp_id", + "in": "path", + "type": "string", + "description": "ID of the retention policy", + "required": true + }, + { + "name": "rp", + "in": "body", + "description": "Configuration options for the retention policy", + "schema": { + "$ref": "#/definitions/RetentionPolicy" + }, + "required": true + } + ], + "responses": { + "200": { + "description": "Retention Policy was altered", + "schema": { + "$ref": "#/definitions/RetentionPolicy" + } + }, + "404": { + "description": "Database or source does not exist.", + "schema": { + "$ref": "#/definitions/Error" + } + }, + "default": { + "description": "A processing or an unexpected error.", + "schema": { + "$ref": "#/definitions/Error" + } + } + } + }, + "delete": { + "tags": ["retention policies"], + "summary": "Delete retention policy for a database", + "parameters": [ + { + "name": "id", + "in": "path", + "type": "string", + "description": "ID of the data source", + "required": true + }, + { + "name": "db_id", + "in": "path", + "type": "string", + "description": "ID of the database", + "required": true + }, + { + "name": "rp_id", + "in": "path", + "type": "string", + "description": "ID of the retention policy", + "required": true + } + ], + "responses": { + "204": { + "description": "Retention Policy has been deleted" + }, + "404": { + "description": "Data source id does not exist.", + "schema": { + "$ref": "#/definitions/Error" + } + }, + "default": { + "description": "A processing or an unexpected error.", + "schema": { + "$ref": "#/definitions/Error" + } + } + } + } + }, + "/sources/{id}/kapacitors": { + "get": { + "tags": ["sources", "kapacitors"], + "parameters": [ + { + "name": "id", + "in": "path", + "type": "string", + "description": "ID of the source", + "required": true + } + ], + "summary": "Retrieve list of configured kapacitors", + "responses": { + "200": { + "description": "An array of kapacitors", + "schema": { + "$ref": "#/definitions/Kapacitors" + } + }, + "default": { + "description": "Unexpected internal service error", + "schema": { + "$ref": "#/definitions/Error" + } + } + } + }, + "post": { + "tags": ["sources", "kapacitors"], + "summary": "Create new kapacitor backend", + "parameters": [ + { + "name": "id", + "in": "path", + "type": "string", + "description": "ID of the source", + "required": true + }, + { + "name": "kapacitor", + "in": "body", + "description": "Configuration options for kapacitor", + "schema": { + "$ref": "#/definitions/Kapacitor" + } + } + ], + "responses": { + "201": { + "description": "Successfully created kapacitor source", + "headers": { + "Location": { + "type": "string", + "format": "url", + "description": "Location of the newly created kapacitor resource." + } + }, + "schema": { + "$ref": "#/definitions/Kapacitor" + } + }, + "default": { + "description": "A processing or an unexpected error.", + "schema": { + "$ref": "#/definitions/Error" + } + } + } + } + }, + "/sources/{id}/kapacitors/{kapa_id}": { + "get": { + "tags": ["sources", "kapacitors"], + "parameters": [ + { + "name": "id", + "in": "path", + "type": "string", + "description": "ID of the source", + "required": true + }, + { + "name": "kapa_id", + "in": "path", + "type": "string", + "description": "ID of the kapacitor", + "required": true + } + ], + "summary": "Configured kapacitors", + "description": "Retrieve information on a single kapacitor instance", + "responses": { + "200": { + "description": "Kapacitor connection information", + "schema": { + "$ref": "#/definitions/Kapacitor" + } + }, + "404": { + "description": "Unknown data source or kapacitor id", + "schema": { + "$ref": "#/definitions/Error" + } + }, + "default": { + "description": "Unexpected internal service error", + "schema": { + "$ref": "#/definitions/Error" + } + } + } + }, + "patch": { + "tags": ["sources", "kapacitors"], + "summary": "Update kapacitor configuration", + "parameters": [ + { + "name": "id", + "in": "path", + "type": "string", + "description": "ID of the source", + "required": true + }, + { + "name": "kapa_id", + "in": "path", + "type": "string", + "description": "ID of a kapacitor backend", + "required": true + }, + { + "name": "config", + "in": "body", + "description": "kapacitor configuration", + "schema": { + "$ref": "#/definitions/Kapacitor" + }, + "required": true + } + ], + "responses": { + "200": { + "description": "Kapacitor's configuration was changed", + "schema": { + "$ref": "#/definitions/Kapacitor" + } + }, + "404": { + "description": "Happens when trying to access a non-existent data source or kapacitor.", + "schema": { + "$ref": "#/definitions/Error" + } + }, + "default": { + "description": "A processing or an unexpected error.", + "schema": { + "$ref": "#/definitions/Error" + } + } + } + }, + "delete": { + "tags": ["sources", "kapacitors"], + "parameters": [ + { + "name": "id", + "in": "path", + "type": "string", + "description": "ID of the source", + "required": true + }, + { + "name": "kapa_id", + "in": "path", + "type": "string", + "description": "ID of the kapacitor", + "required": true + } + ], + "summary": "Remove Kapacitor backend", + "description": "This specific kapacitor will be removed. All associated rule resources will also be removed from the store.", + "responses": { + "204": { + "description": "kapacitor has been removed." + }, + "404": { + "description": "Unknown Data source or Kapacitor id", + "schema": { + "$ref": "#/definitions/Error" + } + }, + "default": { + "description": "Unexpected internal service error", + "schema": { + "$ref": "#/definitions/Error" + } + } + } + } + }, + "/sources/{id}/kapacitors/{kapa_id}/rules": { + "get": { + "tags": ["sources", "kapacitors", "rules"], + "description": "Get all defined alert rules.", + "parameters": [ + { + "name": "id", + "in": "path", + "type": "string", + "description": "ID of the source", + "required": true + }, + { + "name": "kapa_id", + "in": "path", + "type": "string", + "description": "ID of the kapacitor backend.", + "required": true + } + ], + "responses": { + "200": { + "description": "All alert rules for this specific kapacitor are returned", + "schema": { + "$ref": "#/definitions/Rules" + } + }, + "404": { + "description": "Data source or Kapacitor ID does not exist.", + "schema": { + "$ref": "#/definitions/Error" + } + }, + "default": { + "description": "Internal server error", + "schema": { + "$ref": "#/definitions/Error" + } + } + } + }, + "post": { + "tags": ["sources", "kapacitors", "rules"], + "description": "Create kapacitor alert rule", + "parameters": [ + { + "name": "id", + "in": "path", + "type": "string", + "description": "ID of the source", + "required": true + }, + { + "name": "kapa_id", + "in": "path", + "type": "string", + "description": "ID of the kapacitor backend.", + "required": true + }, + { + "name": "rule", + "in": "body", + "description": "Rule to generate alert rule", + "schema": { + "$ref": "#/definitions/Rule" + }, + "required": true + } + ], + "responses": { + "201": { + "description": "Successfully created new kapacitor alert rule", + "headers": { + "Location": { + "type": "string", + "format": "url", + "description": "Location of the newly created kapacitor rule resource." + } + }, + "schema": { + "$ref": "#/definitions/Rule" + } + }, + "404": { + "description": "Source ID or Kapacitor ID does not exist.", + "schema": { + "$ref": "#/definitions/Error" + } + }, + "422": { + "description": "Source ID , Kapacitor ID or alert are unprocessable", + "schema": { + "$ref": "#/definitions/Error" + } + }, + "default": { + "description": "Internal server error; generally a problem creating alert in kapacitor", + "schema": { + "$ref": "#/definitions/Error" + } + } + } + } + }, + "/sources/{id}/kapacitors/{kapa_id}/rules/{rule_id}": { + "get": { + "tags": ["sources", "kapacitors", "rules"], + "parameters": [ + { + "name": "id", + "in": "path", + "type": "string", + "description": "ID of the source", + "required": true + }, + { + "name": "kapa_id", + "in": "path", + "type": "string", + "description": "ID of the kapacitor", + "required": true + }, + { + "name": "rule_id", + "in": "path", + "type": "string", + "description": "ID of the rule", + "required": true + } + ], + "summary": "Specific kapacitor alert rule", + "description": "Alerting rule for kapacitor", + "responses": { + "200": { + "description": "Alert exists and has a specific TICKscript", + "schema": { + "$ref": "#/definitions/Rule" + } + }, + "404": { + "description": "Unknown data source, kapacitor id, or rule id", + "schema": { + "$ref": "#/definitions/Error" + } + }, + "default": { + "description": "Unexpected internal service error", + "schema": { + "$ref": "#/definitions/Error" + } + } + } + }, + "put": { + "tags": ["sources", "kapacitors", "rules"], + "summary": "Update rule alert rule configuration", + "parameters": [ + { + "name": "id", + "in": "path", + "type": "string", + "description": "ID of the source", + "required": true + }, + { + "name": "kapa_id", + "in": "path", + "type": "string", + "description": "ID of a kapacitor backend", + "required": true + }, + { + "name": "rule_id", + "in": "path", + "type": "string", + "description": "ID of a rule", + "required": true + }, + { + "name": "rule", + "in": "body", + "description": "Rule update", + "schema": { + "$ref": "#/definitions/Rule" + }, + "required": true + } + ], + "responses": { + "200": { + "description": "Alert configuration was changed", + "schema": { + "$ref": "#/definitions/Rule" + } + }, + "404": { + "description": "Happens when trying to access a non-existent data source, kapacitor, or rule.", + "schema": { + "$ref": "#/definitions/Error" + } + }, + "default": { + "description": "A processing or an unexpected error.", + "schema": { + "$ref": "#/definitions/Error" + } + } + } + }, + "delete": { + "tags": ["sources", "kapacitors", "rules"], + "parameters": [ + { + "name": "id", + "in": "path", + "type": "string", + "description": "ID of the source", + "required": true + }, + { + "name": "kapa_id", + "in": "path", + "type": "string", + "description": "ID of the kapacitor", + "required": true + }, + { + "name": "rule_id", + "in": "path", + "type": "string", + "description": "ID of the rule", + "required": true + } + ], + "summary": "This specific alert rule will be removed.", + "responses": { + "204": { + "description": "Alert rule has been removed." + }, + "404": { + "description": "Unknown Data source, Kapacitor id, or alert rule", + "schema": { + "$ref": "#/definitions/Error" + } + }, + "default": { + "description": "Unexpected internal service error", + "schema": { + "$ref": "#/definitions/Error" + } + } + } + } + }, + "/sources/{id}/kapacitors/{kapa_id}/proxy": { + "get": { + "tags": ["sources", "kapacitors", "proxy"], + "description": "GET to `path` of kapacitor. The response and status code from kapacitor is directly returned.", + "parameters": [ + { + "name": "id", + "in": "path", + "type": "string", + "description": "ID of the source", + "required": true + }, + { + "name": "kapa_id", + "in": "path", + "type": "string", + "description": "ID of the kapacitor backend.", + "required": true + }, + { + "name": "path", + "in": "query", + "type": "string", + "description": "The kapacitor API path to use in the proxy redirect", + "required": true + } + ], + "responses": { + "204": { + "description": "Kapacitor returned no content" + }, + "404": { + "description": "Data source or Kapacitor ID does not exist.", + "schema": { + "$ref": "#/definitions/Error" + } + }, + "default": { + "description": "Response directly from kapacitor", + "schema": { + "$ref": "#/definitions/KapacitorProxyResponse" + } + } + } + }, + "delete": { + "tags": ["sources", "kapacitors", "proxy"], + "description": "DELETE to `path` of kapacitor. The response and status code from kapacitor is directly returned.", + "parameters": [ + { + "name": "id", + "in": "path", + "type": "string", + "description": "ID of the source", + "required": true + }, + { + "name": "kapa_id", + "in": "path", + "type": "string", + "description": "ID of the kapacitor backend.", + "required": true + }, + { + "name": "path", + "in": "query", + "type": "string", + "description": "The kapacitor API path to use in the proxy redirect", + "required": true + } + ], + "responses": { + "204": { + "description": "Kapacitor returned no content" + }, + "404": { + "description": "Data source or Kapacitor ID does not exist.", + "schema": { + "$ref": "#/definitions/Error" + } + }, + "default": { + "description": "Response directly from kapacitor", + "schema": { + "$ref": "#/definitions/KapacitorProxyResponse" + } + } + } + }, + "patch": { + "tags": ["sources", "kapacitors", "proxy"], + "description": "PATCH body directly to configured kapacitor. The response and status code from kapacitor is directly returned.", + "parameters": [ + { + "name": "id", + "in": "path", + "type": "string", + "description": "ID of the source", + "required": true + }, + { + "name": "kapa_id", + "in": "path", + "type": "string", + "description": "ID of the kapacitor backend.", + "required": true + }, + { + "name": "path", + "in": "query", + "type": "string", + "description": "The kapacitor API path to use in the proxy redirect", + "required": true + }, + { + "name": "query", + "in": "body", + "description": "Kapacitor body", + "schema": { + "$ref": "#/definitions/KapacitorProxy" + }, + "required": true + } + ], + "responses": { + "204": { + "description": "Kapacitor returned no content" + }, + "404": { + "description": "Data source or Kapacitor ID does not exist.", + "schema": { + "$ref": "#/definitions/Error" + } + }, + "default": { + "description": "Response directly from kapacitor", + "schema": { + "$ref": "#/definitions/KapacitorProxyResponse" + } + } + } + }, + "post": { + "tags": ["sources", "kapacitors", "proxy"], + "description": "POST body directly to configured kapacitor. The response and status code from kapacitor is directly returned.", + "parameters": [ + { + "name": "id", + "in": "path", + "type": "string", + "description": "ID of the source", + "required": true + }, + { + "name": "kapa_id", + "in": "path", + "type": "string", + "description": "ID of the kapacitor backend.", + "required": true + }, + { + "name": "path", + "in": "query", + "type": "string", + "description": "The kapacitor API path to use in the proxy redirect", + "required": true + }, + { + "name": "query", + "in": "body", + "description": "Kapacitor body", + "schema": { + "$ref": "#/definitions/KapacitorProxy" + }, + "required": true + } + ], + "responses": { + "204": { + "description": "Kapacitor returned no content" + }, + "404": { + "description": "Kapacitor ID does not exist.", + "schema": { + "$ref": "#/definitions/Error" + } + }, + "default": { + "description": "Response directly from kapacitor", + "schema": { + "$ref": "#/definitions/KapacitorProxyResponse" + } + } + } + } + }, + "/mappings": { + "get": { + "tags": ["layouts", "mappings"], + "summary": "Mappings between app names and measurements", + "description": "Mappings provide a means to alias measurement names found within a telegraf database and application layouts found within Chronograf\n", + "responses": { + "200": { + "description": "An array of mappings", + "schema": { + "$ref": "#/definitions/Mappings" + } + }, + "default": { + "description": "Unexpected internal service error", + "schema": { + "$ref": "#/definitions/Error" + } + } + } + } + }, + "/layouts": { + "get": { + "tags": ["layouts"], + "summary": "Pre-configured layouts", + "parameters": [ + { + "name": "measurement", + "in": "query", + "description": "Returns layouts with this measurement", + "required": false, + "type": "array", + "items": { + "type": "string" + }, + "collectionFormat": "multi" + }, + { + "name": "app", + "in": "query", + "description": "Returns layouts with this app", + "required": false, + "type": "array", + "items": { + "type": "string" + }, + "collectionFormat": "multi" + } + ], + "description": "Layouts are a collection of `Cells` that visualize time-series data.\n", + "responses": { + "200": { + "description": "An array of layouts", + "schema": { + "$ref": "#/definitions/Layouts" + } + }, + "default": { + "description": "Unexpected internal service error", + "schema": { + "$ref": "#/definitions/Error" + } + } + } + }, + "post": { + "tags": ["layouts"], + "summary": "Create new layout", + "parameters": [ + { + "name": "layout", + "in": "body", + "description": "Defines the layout and queries of the cells within the layout.", + "schema": { + "$ref": "#/definitions/Layout" + } + } + ], + "responses": { + "201": { + "description": "Successfully created new layout", + "headers": { + "Location": { + "type": "string", + "format": "url", + "description": "Location of the newly created layout" + } + }, + "schema": { + "$ref": "#/definitions/Layout" + } + }, + "default": { + "description": "A processing or an unexpected error.", + "schema": { + "$ref": "#/definitions/Error" + } + } + } + } + }, + "/layouts/{id}": { + "get": { + "tags": ["layouts"], + "parameters": [ + { + "name": "id", + "in": "path", + "type": "string", + "description": "ID of the layout", + "required": true + } + ], + "summary": "Specific pre-configured layout containing cells and queries.", + "description": "layouts will hold information about how to layout the page of graphs.\n", + "responses": { + "200": { + "description": "Returns the specified layout containing `cells`.", + "schema": { + "$ref": "#/definitions/Layout" + } + }, + "404": { + "description": "Unknown layout id", + "schema": { + "$ref": "#/definitions/Error" + } + }, + "default": { + "description": "Unexpected internal service error", + "schema": { + "$ref": "#/definitions/Error" + } + } + } + }, + "delete": { + "tags": ["layouts"], + "parameters": [ + { + "name": "id", + "in": "path", + "type": "string", + "description": "ID of the layout", + "required": true + } + ], + "summary": "This specific layout will be removed from the data store", + "responses": { + "204": { + "description": "Layout has been removed." + }, + "404": { + "description": "Unknown layout id", + "schema": { + "$ref": "#/definitions/Error" + } + }, + "default": { + "description": "Unexpected internal service error", + "schema": { + "$ref": "#/definitions/Error" + } + } + } + }, + "put": { + "tags": ["layouts"], + "summary": "Replace layout configuration.", + "parameters": [ + { + "name": "id", + "in": "path", + "type": "string", + "description": "ID of a layout", + "required": true + }, + { + "name": "config", + "in": "body", + "description": "layout configuration update parameters", + "schema": { + "$ref": "#/definitions/Layout" + }, + "required": true + } + ], + "responses": { + "200": { + "description": "Layout has been replaced and the new layout is returned.", + "schema": { + "$ref": "#/definitions/Layout" + } + }, + "404": { + "description": "Happens when trying to access a non-existent layout.", + "schema": { + "$ref": "#/definitions/Error" + } + }, + "default": { + "description": "A processing or an unexpected error.", + "schema": { + "$ref": "#/definitions/Error" + } + } + } + } + }, + "/dashboards": { + "get": { + "tags": ["dashboards"], + "summary": "List of all dashboards", + "responses": { + "200": { + "description": "An array of dashboards", + "schema": { + "$ref": "#/definitions/Dashboards" + } + }, + "default": { + "description": "Unexpected internal service error", + "schema": { + "$ref": "#/definitions/Error" + } + } + } + }, + "post": { + "tags": ["dashboards"], + "summary": "Create new dashboard", + "parameters": [ + { + "name": "dashboard", + "in": "body", + "description": "Configuration options for new dashboard", + "schema": { + "$ref": "#/definitions/Dashboard" + } + } + ], + "responses": { + "201": { + "description": "Successfully created new dashboard", + "headers": { + "Location": { + "type": "string", + "format": "url", + "description": "Location of the newly created dashboard resource." + } + }, + "schema": { + "$ref": "#/definitions/Dashboard" + } + }, + "default": { + "description": "A processing or an unexpected error.", + "schema": { + "$ref": "#/definitions/Error" + } + } + } + } + }, + "/dashboards/{id}": { + "get": { + "tags": ["dashboards"], + "parameters": [ + { + "name": "id", + "in": "path", + "type": "integer", + "description": "ID of the dashboard", + "required": true + } + ], + "summary": "Specific dashboard", + "description": "Dashboards contain visual display information as well as links to queries", + "responses": { + "200": { + "description": "Returns the specified dashboard with links to queries.", + "schema": { + "$ref": "#/definitions/Dashboard" + } + }, + "404": { + "description": "Unknown dashboard id", + "schema": { + "$ref": "#/definitions/Error" + } + }, + "default": { + "description": "Unexpected internal service error", + "schema": { + "$ref": "#/definitions/Error" + } + } + } + }, + "delete": { + "tags": ["dashboards"], + "parameters": [ + { + "name": "id", + "in": "path", + "type": "integer", + "description": "ID of the layout", + "required": true + } + ], + "summary": "Deletes the specified dashboard", + "responses": { + "204": { + "description": "Dashboard has been removed." + }, + "404": { + "description": "Unknown dashboard id", + "schema": { + "$ref": "#/definitions/Error" + } + }, + "default": { + "description": "Unexpected internal service error", + "schema": { + "$ref": "#/definitions/Error" + } + } + } + }, + "put": { + "tags": ["dashboards"], + "summary": "Replace dashboard information.", + "parameters": [ + { + "name": "id", + "in": "path", + "type": "integer", + "description": "ID of a dashboard", + "required": true + }, + { + "name": "config", + "in": "body", + "description": "dashboard configuration update parameters", + "schema": { + "$ref": "#/definitions/Dashboard" + }, + "required": true + } + ], + "responses": { + "200": { + "description": "Dashboard has been replaced and the new dashboard is returned.", + "schema": { + "$ref": "#/definitions/Dashboard" + } + }, + "404": { + "description": "Happens when trying to access a non-existent dashboard.", + "schema": { + "$ref": "#/definitions/Error" + } + }, + "default": { + "description": "A processing or an unexpected error.", + "schema": { + "$ref": "#/definitions/Error" + } + } + } + }, + "patch": { + "tags": ["layouts"], + "summary": "Update dashboard information.", + "description": "Update either the dashboard name or the dashboard cells", + "parameters": [ + { + "name": "id", + "in": "path", + "type": "integer", + "description": "ID of a dashboard", + "required": true + }, + { + "name": "config", + "in": "body", + "description": "dashboard configuration update parameters. Must be either name or cells", + "schema": { + "$ref": "#/definitions/Dashboard" + }, + "required": true + } + ], + "responses": { + "200": { + "description": "Dashboard has been updated and the new dashboard is returned.", + "schema": { + "$ref": "#/definitions/Dashboard" + } + }, + "404": { + "description": "Happens when trying to access a non-existent dashboard.", + "schema": { + "$ref": "#/definitions/Error" + } + }, + "default": { + "description": "A processing or an unexpected error.", + "schema": { + "$ref": "#/definitions/Error" + } + } + } + } + } + }, + "definitions": { + "Databases": { + "type": "object", + "required": ["databases"], + "properties": { + "databases": { + "type": "array", + "items": { + "$ref": "#/definitions/Database" + } + } + } + }, + "Database": { + "type": "object", + "required": ["name"], + "example": { + "name": "NOAA_water_database", + "duration": "3d", + "replication": 3, + "shardDuration": "3h", + "retentionPolicies": [ + { + "name": "weekly", + "duration": "7d", + "replication": 1, + "shardDuration": "7d", + "default": true, + "links": { + "self": "/chronograf/v1/ousrces/1/dbs/NOAA_water_database/rps/liquid" + } + } + ], + "links": { + "self": "/chronograf/v1/sources/1/dbs/NOAA_water_database", + "rps": "/chronograf/v1/sources/1/dbs/NOAA_water_database/rps" + } + }, + "properties": { + "name": { + "type": "string", + "description": "The identifying name of the database" + }, + "duration": { + "type": "string", + "description": "the duration of the default retention policy" + }, + "replication": { + "type": "integer", + "format": "int32", + "description": "how many copies of the data are stored in the cluster" + }, + "shardDuration": { + "type": "string", + "description": "the interval spanned by each shard group" + }, + "retentionPolicies": { + "type": "array", + "items": { + "$ref": "#/definitions/RetentionPolicy" + } + }, + "links": { + "type": "object", + "properties": { + "self": { + "type": "string", + "description": "Self link mapping to this resource", + "format": "url" + }, + "rps": { + "type": "string", + "description": "Link to retention policies for this database", + "format": "url" + } + } + } + } + }, + "Kapacitors": { + "type": "object", + "required": ["kapacitors"], + "properties": { + "kapacitors": { + "type": "array", + "items": { + "$ref": "#/definitions/Kapacitor" + } + } + } + }, + "Kapacitor": { + "type": "object", + "required": ["name", "url"], + "example": { + "id": "4", + "name": "kapa", + "url": "http://localhost:9092", + "active": false, + "links": { + "proxy": "/chronograf/v1/sources/4/kapacitors/4/proxy", + "self": "/chronograf/v1/sources/4/kapacitors/4", + "rules": "/chronograf/v1/sources/4/kapacitors/4/rules" + } + }, + "properties": { + "id": { + "type": "string", + "description": "Unique identifier representing a kapacitor instance.", + "readOnly": true + }, + "name": { + "type": "string", + "description": "User facing name of kapacitor instance." + }, + "username": { + "type": "string", + "description": "Username for authentication to kapacitor." + }, + "password": { + "type": "string", + "description": "Password is in cleartext." + }, + "url": { + "type": "string", + "format": "url", + "description": "URL for the kapacitor backend (e.g. http://localhost:9092)" + }, + "active": { + "type": "boolean", + "description": "Indicates whether the kapacitor is the current kapacitor being used for a source" + }, + "links": { + "type": "object", + "properties": { + "self": { + "type": "string", + "description": "Self link mapping to this resource", + "format": "url" + }, + "proxy": { + "type": "string", + "description": "URL location of proxy endpoint for this kapacitor", + "format": "url" + }, + "rules": { + "type": "string", + "description": "URL location of rules endpoint for this kapacitor", + "format": "url" + } + } + } + } + }, + "KapacitorProxy": { + "description": "Entirely used as the body for the request to the kapacitor backend.", + "type": "object" + }, + "KapacitorProxyResponse": { + "description": "Entire response from the kapacitor backend.", + "type": "object" + }, + "Rules": { + "type": "object", + "required": ["rules"], + "properties": { + "rules": { + "type": "array", + "items": { + "$ref": "#/definitions/Rule" + } + } + } + }, + "Query": { + "type": "object", + "properties": { + "query": { + "type": "string", + "description": "InfluxQL statement to be analyzed", + "example": { + "query": "select max(usage_system) from telegraf.autogen.cpu group by time(10m)" + } + } + } + }, + "QueryResponse": { + "type": "object", + "properties": { + "query": { + "type": "string", + "example": { + "query": "select max(usage_system) from telegraf.autogen.cpu group by time(10m)" + }, + "description": "InfluxQL statement to be analyzed" + }, + "queryConfig": { + "$ref": "#/definitions/QueryConfig" + } + } + }, + "Queries": { + "type": "object", + "properties": { + "queries": { + "type": "array", + "items": { + "$ref": "#/definitions/Query" + } + } + } + }, + "QueriesResponse": { + "type": "object", + "properties": { + "queries": { + "type": "array", + "items": { + "$ref": "#/definitions/QueryResponse" + } + } + } + }, + "QueryConfig": { + "type": "object", + "example": { + "id": "ce72917d-1ecb-45ea-a6cb-4c122deb93c7", + "database": "telegraf", + "measurement": "cpu", + "retentionPolicy": "autogen", + "fields": [ + { + "field": "usage_system", + "funcs": ["max"] + } + ], + "tags": {}, + "groupBy": { + "time": "10m", + "tags": [] + }, + "areTagsAccepted": true, + "range": { + "lower": "15m", + "upper": "now" + } + }, + "properties": { + "id": { + "type": "string" + }, + "database": { + "type": "string" + }, + "measurement": { + "type": "string" + }, + "retentionPolicy": { + "type": "string" + }, + "areTagsAccepted": { + "type": "boolean" + }, + "rawText": { + "type": "string" + }, + "tags": { + "type": "object" + }, + "groupBy": { + "type": "object", + "properties": { + "time": { + "type": "string" + }, + "tags": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "required": ["time", "tags"] + }, + "fields": { + "type": "array", + "items": { + "type": "object", + "properties": { + "field": { + "type": "string" + }, + "funcs": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "required": ["field", "funcs"] + } + }, + "range": { + "type": "object", + "properties": { + "lower": { + "type": "string" + }, + "upper": { + "type": "string" + } + }, + "required": ["lower", "upper"] + } + }, + "required": [ + "database", + "measurement", + "retentionPolicy", + "areTagsAccepted", + "tags", + "groupBy", + "fields" + ] + }, + "KapacitorNode": { + "type": "object", + "description": "Represents a node in the kapacitor TICKscript graph", + "required": ["name"], + "properties": { + "name": { + "type": "string", + "description": "Name of the kapacitor node e.g. slack" + }, + "args": { + "type": "array", + "description": "All arguments to the named node", + "items": { + "type": "string" + } + }, + "properties": { + "type": "array", + "description": "All properties attached to the kapacitor node", + "items": { + "$ref": "#/definitions/KapacitorProperty" + } + } + } + }, + "KapacitorProperty": { + "type": "object", + "description": "Represents a property attached to a node in the kapacitor TICKscript graph", + "required": ["name"], + "properties": { + "name": { + "type": "string", + "description": "Name of the kapacitor property e.g. channel for a slack ndoe" + }, + "args": { + "type": "array", + "description": "All arguments to the named property", + "items": { + "type": "string" + } + } + } + }, + "RetentionPolicies": { + "type": "object", + "required": ["retentionPolicies"], + "properties": { + "retentionPolicies": { + "type": "array", + "items": { + "$ref": "#/definitions/RetentionPolicy" + } + } + } + }, + "RetentionPolicy": { + "type": "object", + "required": ["name", "duration", "replication"], + "example": { + "name": "weekly", + "duration": "7d", + "replication": 1, + "shardDuration": "7d", + "default": true, + "links": { + "self": "/chronograf/v1/ousrces/1/dbs/NOAA_water_database/rps/liquid" + } + }, + "properties": { + "name": { + "type": "string", + "description": "The identifying name of the retention policy" + }, + "duration": { + "type": "string", + "description": "the duration of the retention policy" + }, + "replication": { + "type": "integer", + "format": "int32", + "description": "how many copies of the data are stored in the cluster" + }, + "shardDuration": { + "type": "string", + "description": "the interval spanned by each shard group" + }, + "default": { + "type": "boolean", + "description": "Indicates whether this retention policy should be the default" + }, + "links": { + "type": "object", + "properties": { + "self": { + "type": "string", + "description": "Self link mapping to this resource", + "format": "url" + } + } + } + } + }, + "Rule": { + "type": "object", + "example": { + "id": "chronograf-v1-b2b065ea-79bd-4e4f-8c0d-d0ef68477d38", + "query": { + "id": "ce72917d-1ecb-45ea-a6cb-4c122deb93c7", + "database": "telegraf", + "measurement": "cpu", + "retentionPolicy": "autogen", + "fields": [ + { + "field": "usage_system", + "funcs": ["max"] + } + ], + "tags": {}, + "groupBy": { + "time": "10m", + "tags": [] + }, + "areTagsAccepted": true + }, + "every": "30s", + "alerts": ["alerta"], + "alertNodes": [ + { + "name": "alerta", + "args": [], + "properties": [] + } + ], + "message": "too much spam", + "details": "muh body", + "trigger": "threshold", + "values": { + "operator": "greater than", + "value": "10" + }, + "name": "Untitled Rule", + "tickscript": "var db = 'telegraf'\n\nvar rp = 'autogen'\n\nvar measurement = 'cpu'\n\nvar groupBy = []\n\nvar whereFilter = lambda: TRUE\n\nvar period = 10m\n\nvar every = 30s\n\nvar name = 'Untitled Rule'\n\nvar idVar = name + ':{{.Group}}'\n\nvar message = 'too much spam'\n\nvar idTag = 'alertID'\n\nvar levelTag = 'level'\n\nvar messageField = 'message'\n\nvar durationField = 'duration'\n\nvar outputDB = 'chronograf'\n\nvar outputRP = 'autogen'\n\nvar outputMeasurement = 'alerts'\n\nvar triggerType = 'threshold'\n\nvar details = 'muh body'\n\nvar crit = 10\n\nvar data = stream\n |from()\n .database(db)\n .retentionPolicy(rp)\n .measurement(measurement)\n .groupBy(groupBy)\n .where(whereFilter)\n |window()\n .period(period)\n .every(every)\n .align()\n |max('usage_system')\n .as('value')\n\nvar trigger = data\n |alert()\n .crit(lambda: \"value\" > crit)\n .stateChangesOnly()\n .message(message)\n .id(idVar)\n .idTag(idTag)\n .levelTag(levelTag)\n .messageField(messageField)\n .durationField(durationField)\n .details(details)\n .alerta()\n\ntrigger\n |influxDBOut()\n .create()\n .database(outputDB)\n .retentionPolicy(outputRP)\n .measurement(outputMeasurement)\n .tag('alertName', name)\n .tag('triggerType', triggerType)\n\ntrigger\n |httpOut('output')\n", + "status": "enabled", + "links": { + "self": "/chronograf/v1/sources/5/kapacitors/5/rules/chronograf-v1-b2b065ea-79bd-4e4f-8c0d-d0ef68477d38", + "kapacitor": "/chronograf/v1/sources/5/kapacitors/5/proxy?path=%2Fkapacitor%2Fv1%2Ftasks%2Fchronograf-v1-b2b065ea-79bd-4e4f-8c0d-d0ef68477d38", + "output": "/chronograf/v1/sources/5/kapacitors/5/proxy?path=%2Fkapacitor%2Fv1%2Ftasks%2Fchronograf-v1-b2b065ea-79bd-4e4f-8c0d-d0ef68477d38%2Foutput" + } + }, + "required": ["query", "every", "trigger"], + "properties": { + "id": { + "type": "string", + "description": "ID for this rule; the ID is shared with kapacitor" + }, + "query": { + "$ref": "#/definitions/QueryConfig" + }, + "name": { + "type": "string", + "description": "User facing name of the alerting rule" + }, + "every": { + "type": "string", + "description": "Golang duration string specifying how often the alert condition is checked" + }, + "alerts": { + "type": "array", + "description": "Array of alerting services to warn if the alert is triggered", + "items": { + "type": "string", + "enum": [ + "alerta", + "post", + "http", + "hipchat", + "opsgenie", + "pagerduty", + "victorops", + "smtp", + "email", + "exec", + "sensu", + "slack", + "talk", + "telegram", + "tcp" + ] + } + }, + "alertNodes": { + "type": "array", + "description": "Arguments and properties to add to alert", + "items": { + "$ref": "#/definitions/KapacitorNode" + } + }, + "message": { + "type": "string", + "description": "Message to send when alert occurs." + }, + "details": { + "type": "string", + "description": "Template for constructing a detailed HTML message for the alert. (Currently, only used for email/smtp" + }, + "trigger": { + "type": "string", + "description": "Trigger defines the alerting structure; deadman alert if no data are received for the specified time range; relative alert if the data change relative to the data in a different time range; threshold alert if the data cross a boundary", + "enum": ["deadman", "relative", "threshold"] + }, + "values": { + "type": "object", + "description": "Alerting logic for trigger type", + "properties": { + "change": { + "description": "Specifies if the change is percent or absolute", + "type": "string", + "enum": ["% change", "change"] + }, + "period": { + "description": "Length of time before deadman is alerted (golang duration)", + "type": "string" + }, + "shift": { + "description": "Amount of time to look into the past to compare to the present (golang duration)", + "type": "string" + }, + "operator": { + "description": "Operator for alert comparison", + "type": "string", + "enum": [ + "greater than", + "less than", + "equal to or less than", + "equal to or greater", + "equal to", + "not equal to", + "inside range", + "outside range" + ] + }, + "value": { + "description": "Value is the boundary value when alert goes critical", + "type": "string" + }, + "rangeValue": { + "description": "Optional value for range comparisions", + "type": "string" + } + } + }, + "tickscript": { + "type": "string", + "description": "TICKscript representing this rule" + }, + "status": { + "type": "string", + "description": "Represents if this rule is enabled or disabled in kapacitor", + "enum": ["enabled", "disabled"] + }, + "links": { + "type": "object", + "required": ["self", "kapacitor"], + "properties": { + "self": { + "description": "Self link pointing to this rule resource", + "type": "string", + "format": "uri" + }, + "kapacitor": { + "description": "Link pointing to the kapacitor proxy for this rule including the path query parameter.", + "type": "string", + "format": "uri" + }, + "output": { + "description": "Link pointing to the kapacitor httpOut node of the tickscript; includes the path query argument", + "type": "string", + "format": "uri" + } + } + } + } + }, + "Sources": { + "type": "object", + "required": ["sources"], + "properties": { + "sources": { + "type": "array", + "items": { + "$ref": "#/definitions/Source" + } + } + } + }, + "Source": { + "type": "object", + "example": { + "id": "4", + "name": "Influx 1", + "url": "http://localhost:8086", + "default": false, + "telegraf": "telegraf", + "links": { + "self": "/chronograf/v1/sources/4", + "kapacitors": "/chronograf/v1/sources/4/kapacitors", + "proxy": "/chronograf/v1/sources/4/proxy", + "write": "/chronograf/v1/sources/4/write", + "queries": "/chronograf/v1/sources/4/queries", + "permissions": "/chronograf/v1/sources/4/permissions", + "users": "/chronograf/v1/sources/4/users", + "roles": "/chronograf/v1/sources/4/roles" + } + }, + "required": ["url"], + "properties": { + "id": { + "type": "string", + "description": "Unique identifier representing a specific data source.", + "readOnly": true + }, + "name": { + "type": "string", + "description": "User facing name of data source" + }, + "type": { + "type": "string", + "description": "Format of the data source", + "readOnly": true, + "enum": ["influx", "influx-enterprise", "influx-relay"] + }, + "username": { + "type": "string", + "description": "Username for authentication to data source" + }, + "password": { + "type": "string", + "description": "Password is in cleartext." + }, + "sharedSecret": { + "type": "string", + "description": "JWT signing secret for optional Authorization: Bearer to InfluxDB" + }, + "url": { + "type": "string", + "format": "url", + "description": "URL for the time series data source backend (e.g. http://localhost:8086)" + }, + "metaUrl": { + "type": "string", + "format": "url", + "description": "URL for the influxdb meta node" + }, + "insecureSkipVerify": { + "type": "boolean", + "description": "True means any certificate presented by the source is accepted. Typically used for self-signed certs. Probably should only be used for testing." + }, + "default": { + "type": "boolean", + "description": "Indicates whether this source is the default source" + }, + "telegraf": { + "type": "string", + "description": "Database where telegraf information is stored for this source", + "default": "telegraf" + }, + "links": { + "type": "object", + "properties": { + "self": { + "type": "string", + "description": "Self link mapping to this resource", + "format": "url" + }, + "proxy": { + "type": "string", + "description": "URL location of proxy endpoint for this source", + "format": "url" + }, + "write": { + "type": "string", + "description": "URL location of write endpoint for this source", + "format": "url" + }, + "queries": { + "type": "string", + "description": "URL location of the queries endpoint for this source", + "format": "url" + }, + "kapacitors": { + "type": "string", + "description": "URL location of the kapacitors endpoint for this source", + "format": "url" + }, + "users": { + "type": "string", + "description": "URL location of the users endpoint for this source", + "format": "url" + }, + "permissions": { + "type": "string", + "description": "URL location of the permissions endpoint for this source", + "format": "url" + }, + "roles": { + "type": "string", + "description": "Optional path to the roles endpoint IFF it is supported on this source", + "format": "url" + } + } + } + } + }, + "Proxy": { + "type": "object", + "example": { + "query": "select $myfield from cpu where time > now() - 10m", + "db": "telegraf", + "rp": "autogen", + "tempVars": [ + { + "tempVar": "$myfield", + "values": [ + { + "type": "fieldKey", + "value": "usage_user" + } + ] + } + ] + }, + "required": ["query"], + "properties": { + "query": { + "type": "string" + }, + "db": { + "type": "string" + }, + "rp": { + "type": "string" + }, + "tempVars": { + "type": "array", + "description": "Template variables to replace within an InfluxQL query", + "items": { + "$ref": "#/definitions/TemplateVariable" + } + } + } + }, + "TemplateVariable": { + "type": "object", + "description": "Named variable within an InfluxQL query to be replaced with values", + "properties": { + "tempVar": { + "type": "string", + "description": "String to replace within an InfluxQL statement" + }, + "values": { + "type": "array", + "description": "Values used to replace tempVar.", + "items": { + "$ref": "#/definitions/TemplateValue" + } + } + } + }, + "TemplateValue": { + "type": "object", + "description": "Value use to replace a template in an InfluxQL query. The type governs the output format", + "properties": { + "value": { + "type": "string", + "description": "Specific value that will be encoded based on type" + }, + "type": { + "type": "string", + "enum": ["csv", "tagKey", "tagValue", "fieldKey", "timeStamp"], + "description": "The type will change the format of the output value. tagKey/fieldKey are double quoted; tagValue are single quoted; csv and timeStamp are not quoted." + } + } + }, + "ProxyResponse": { + "type": "object", + "example": { + "results": [ + { + "statement_id": 0, + "series": [ + { + "name": "cpu", + "columns": [ + "time", + "cpu", + "host", + "usage_guest", + "usage_guest_nice", + "usage_idle", + "usage_iowait", + "usage_irq", + "usage_nice", + "usage_softirq", + "usage_steal", + "usage_system", + "usage_user" + ], + "values": [ + [ + 1487785510000, + "cpu-total", + "ChristohersMBP2.lan", + 0, + 0, + 76.6916354556804, + 0, + 0, + 0, + 0, + 0, + 4.781523096129837, + 18.526841448189764 + ] + ] + } + ] + } + ] + }, + "properties": { + "results": { + "description": "results from influx", + "type": "object" + } + } + }, + "Roles": { + "type": "array", + "items": { + "$ref": "#/definitions/Role" + }, + "example": { + "roles": [ + { + "users": [ + { + "name": "admin", + "links": { + "self": "/chronograf/v1/sources/3/users/admin" + } + } + ], + "name": "timetravelers", + "permissions": [ + { + "scope": "database", + "name": "telegraf", + "allowed": ["ReadData", "WriteData"] + } + ], + "links": { + "self": "/chronograf/v1/sources/3/roles/timetravelers" + } + } + ] + } + }, + "Role": { + "type": "object", + "required": ["name"], + "properties": { + "name": { + "type": "string", + "description": "Unique name of the role", + "maxLength": 254, + "minLength": 1 + }, + "users": { + "$ref": "#/definitions/Users" + }, + "permissions": { + "$ref": "#/definitions/Permissions" + }, + "links": { + "type": "object", + "description": "URL relations of this role", + "properties": { + "self": { + "type": "string", + "format": "url", + "description": "URI of resource." + } + } + } + }, + "example": { + "users": [ + { + "name": "admin", + "links": { + "self": "/chronograf/v1/sources/3/users/admin" + } + } + ], + "name": "timetravelers", + "permissions": [ + { + "scope": "database", + "name": "telegraf", + "allowed": ["ReadData", "WriteData"] + } + ], + "links": { + "self": "/chronograf/v1/sources/3/roles/timetravelers" + } + } + }, + "Users": { + "type": "object", + "properties": { + "users": { + "type": "array", + "items": { + "$ref": "#/definitions/User" + } + } + }, + "example": { + "users": [ + { + "name": "docbrown", + "permissions": [ + { + "scope": "all", + "allowed": [ + "ViewAdmin", + "ViewChronograf", + "CreateDatabase", + "CreateUserAndRole", + "DropDatabase", + "DropData", + "ReadData", + "WriteData", + "ManageShard", + "ManageContinuousQuery", + "ManageQuery", + "ManageSubscription", + "Monitor", + "KapacitorAPI" + ] + } + ], + "roles": [ + { + "name": "timetravelers", + "permissions": [ + { + "scope": "database", + "name": "telegraf", + "allowed": ["ReadData", "WriteData"] + } + ], + "links": { + "self": "/chronograf/v1/sources/3/roles/timetravelers" + } + } + ], + "links": { + "self": "/chronograf/v1/sources/3/users/docbrown" + } + } + ] + } + }, + "User": { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "Unique name of the user", + "maxLength": 254, + "minLength": 1 + }, + "password": { + "type": "string" + }, + "permissions": { + "$ref": "#/definitions/Permissions" + }, + "roles": { + "$ref": "#/definitions/Roles" + }, + "links": { + "type": "object", + "description": "URL relations of this user", + "properties": { + "self": { + "type": "string", + "format": "url", + "description": "URI of resource." + } + } + } + }, + "example": { + "name": "docbrown", + "permissions": [ + { + "scope": "all", + "allowed": [ + "ViewAdmin", + "ViewChronograf", + "CreateDatabase", + "CreateUserAndRole", + "DropDatabase", + "DropData", + "ReadData", + "WriteData", + "ManageShard", + "ManageContinuousQuery", + "ManageQuery", + "ManageSubscription", + "Monitor", + "KapacitorAPI" + ] + } + ], + "roles": [ + { + "name": "timetravelers", + "permissions": [ + { + "scope": "database", + "name": "telegraf", + "allowed": ["ReadData", "WriteData"] + } + ], + "links": { + "self": "/chronograf/v1/sources/3/roles/timetravelers" + } + } + ], + "links": { + "self": "/chronograf/v1/sources/3/users/docbrown" + } + } + }, + "Permissions": { + "description": "Permissions represent the entire set of permissions a User or Role may have", + "type": "array", + "items": { + "$ref": "#/definitions/Permission" + } + }, + "Permission": { + "description": "Permission is a specific allowance for User or Role bound to a scope of the data source", + "type": "object", + "required": ["scope", "allowed"], + "properties": { + "scope": { + "type": "string", + "description": "Describes if the permission is for all databases or restricted to one database", + "enum": ["all", "database"] + }, + "name": { + "type": "string", + "description": "If the scope is database this identifies the name of the database" + }, + "allowed": { + "$ref": "#/definitions/Allowances" + } + }, + "example": { + "scope": "database", + "name": "telegraf", + "allowed": ["READ", "WRITE"] + } + }, + "AllPermissions": { + "description": "All possible permissions for this particular datasource. Used as a static list", + "type": "object", + "properties": { + "permissions": { + "$ref": "#/definitions/Permissions" + }, + "links": { + "type": "object", + "properties": { + "self": { + "description": "Relative link back to the permissions endpoint", + "type": "string", + "format": "uri" + }, + "source": { + "description": "Relative link to host with these permissiosn", + "type": "string", + "format": "uri" + } + } + } + } + }, + "Allowances": { + "description": "Allowances defines what actions a user can have on a scoped permission", + "type": "array", + "items": { + "type": "string", + "description": "OSS InfluxDB is READ and WRITE. Enterprise is all others", + "enum": [ + "READ", + "WRITE", + "NoPermissions", + "ViewAdmin", + "ViewChronograf", + "CreateDatabase", + "CreateUserAndRole", + "AddRemoveNode", + "DropDatabase", + "DropData", + "ReadData", + "WriteData", + "Rebalance", + "ManageShard", + "ManageContinuousQuery", + "ManageQuery", + "ManageSubscription", + "Monitor", + "CopyShard", + "KapacitorAPI", + "KapacitorConfigAPI" + ] + } + }, + "Layouts": { + "required": ["layouts"], + "type": "object", + "properties": { + "layouts": { + "type": "array", + "items": { + "$ref": "#/definitions/Layout" + } + } + } + }, + "Layout": { + "type": "object", + "required": ["cells", "app", "measurement"], + "properties": { + "id": { + "type": "string", + "description": "ID is an opaque string that uniquely identifies this layout." + }, + "app": { + "type": "string", + "description": "App is the user facing name of this Layout" + }, + "measurement": { + "type": "string", + "description": "Measurement is the descriptive name of the time series data." + }, + "cells": { + "type": "array", + "description": "Cells are the individual visualization elements.", + "items": { + "$ref": "#/definitions/Cell" + } + }, + "link": { + "$ref": "#/definitions/Link" + } + }, + "example": { + "id": "0e980b97-c162-487b-a815-3f955df62430", + "app": "docker", + "measurement": "docker_container_net", + "autoflow": true, + "cells": [ + { + "x": 0, + "y": 0, + "w": 4, + "h": 4, + "i": "4c79cefb-5152-410c-9b88-74f9bff7ef23", + "name": "Docker - Container Network", + "queries": [ + { + "query": "SELECT derivative(mean(\"tx_bytes\"), 10s) AS \"net_tx_bytes\" FROM \"docker_container_net\"", + "groupbys": ["\"container_name\""] + }, + { + "query": "SELECT derivative(mean(\"rx_bytes\"), 10s) AS \"net_rx_bytes\" FROM \"docker_container_net\"", + "groupbys": ["\"container_name\""] + } + ], + "type": "" + } + ], + "link": { + "href": "/chronograf/v1/layouts/0e980b97-c162-487b-a815-3f955df62430", + "rel": "self" + } + } + }, + "Mappings": { + "type": "object", + "required": ["mappings"], + "properties": { + "mappings": { + "type": "array", + "items": { + "$ref": "#/definitions/Mapping" + } + } + } + }, + "Mapping": { + "type": "object", + "required": ["measurement", "name"], + "properties": { + "measurement": { + "description": "The measurement where data for this mapping is found", + "type": "string" + }, + "name": { + "description": "The application name which will be assigned to the corresponding measurement", + "type": "string" + } + }, + "example": { + "measurement": "riak", + "name": "riak" + } + }, + "Cell": { + "type": "object", + "required": ["i", "x", "y", "w", "h"], + "properties": { + "i": { + "description": "Unique ID of Cell", + "type": "string", + "format": "uuid4" + }, + "x": { + "description": "X-coordinate of Cell in the Layout", + "type": "integer", + "format": "int32" + }, + "y": { + "description": "Y-coordinate of Cell in the Layout", + "type": "integer", + "format": "int32" + }, + "w": { + "description": "Width of Cell in the Layout", + "type": "integer", + "format": "int32" + }, + "h": { + "description": "Height of Cell in the Layout", + "type": "integer", + "format": "int32" + }, + "name": { + "description": "Cell name", + "type": "string" + }, + "queries": { + "description": "Time-series data queries for Cell.", + "type": "array", + "items": { + "$ref": "#/definitions/LayoutQuery" + } + }, + "type": { + "description": "Cell visualization type", + "type": "string" + } + }, + "example": { + "x": 5, + "y": 5, + "w": 4, + "h": 4, + "name": "usage_user", + "queries": [ + { + "query": "SELECT mean(\"usage_user\") AS \"usage_user\" FROM \"cpu\"", + "label": "%" + } + ], + "type": "line" + } + }, + "LayoutQuery": { + "type": "object", + "required": ["query"], + "properties": { + "label": { + "description": "Optional Y-axis user-facing label for this query", + "type": "string" + }, + "range": { + "description": "Optional default range of the Y-axis", + "type": "object", + "required": ["upper", "lower"], + "properties": { + "upper": { + "description": "Upper bound of the display range of the Y-axis", + "type": "integer", + "format": "int64" + }, + "lower": { + "description": "Lower bound of the display range of the Y-axis", + "type": "integer", + "format": "int64" + } + } + }, + "query": { + "type": "string" + }, + "wheres": { + "description": "Defines the condition clauses for influxdb", + "type": "array", + "items": { + "type": "string" + } + }, + "groupbys": { + "description": "Defines the group by clauses for influxdb", + "type": "array", + "items": { + "type": "string" + } + } + }, + "example": { + "label": "# warnings", + "query": "SELECT count(\"check_id\") as \"Number Warning\" FROM consul_health_checks", + "wheres": ["\"status\" = 'warning'"], + "groupbys": ["\"service_name\""] + } + }, + "DashboardQuery": { + "type": "object", + "required": ["query"], + "properties": { + "label": { + "description": "Optional Y-axis user-facing label for this query", + "type": "string" + }, + "range": { + "description": "Optional default range of the Y-axis", + "type": "object", + "required": ["upper", "lower"], + "properties": { + "upper": { + "description": "Upper bound of the display range of the Y-axis", + "type": "integer", + "format": "int64" + }, + "lower": { + "description": "Lower bound of the display range of the Y-axis", + "type": "integer", + "format": "int64" + } + } + }, + "query": { + "type": "string" + }, + "queryConfig": { + "$ref": "#/definitions/QueryConfig" + } + }, + "example": { + "id": 4, + "cells": [ + { + "x": 0, + "y": 0, + "w": 4, + "h": 4, + "name": "", + "queries": [ + { + "query": "SELECT mean(\"usage_user\") AS \"usage_user\" FROM \"cpu\"", + "label": "%", + "queryConfig": { + "database": "", + "measurement": "cpu", + "retentionPolicy": "", + "fields": [ + { + "field": "usage_user", + "funcs": ["mean"] + } + ], + "tags": {}, + "groupBy": { + "time": "", + "tags": [] + }, + "areTagsAccepted": false + } + } + ], + "type": "line" + } + ], + "name": "dashboard name", + "links": { + "self": "/chronograf/v1/dashboards/4" + } + } + }, + "Dashboards": { + "description": "a list of dashboards", + "type": "object", + "properties": { + "dashboards": { + "type": "array", + "items": { + "$ref": "#/definitions/Dashboard" + } + } + } + }, + "Dashboard": { + "type": "object", + "properties": { + "id": { + "description": "the unique dashboard id", + "type": "integer", + "format": "int64" + }, + "cells": { + "description": "a list of dashboard visualizations", + "type": "array", + "items": { + "description": "cell visualization information", + "type": "object", + "properties": { + "x": { + "description": "X-coordinate of Cell in the Dashboard", + "type": "integer", + "format": "int32" + }, + "y": { + "description": "Y-coordinate of Cell in the Dashboard", + "type": "integer", + "format": "int32" + }, + "w": { + "description": "Width of Cell in the Dashboard", + "type": "integer", + "format": "int32", + "minimum": 1, + "default": 4 + }, + "h": { + "description": "Height of Cell in the Dashboard", + "type": "integer", + "format": "int32", + "minimum": 1, + "default": 4 + }, + "name": { + "description": "Name of Cell in the Dashboard", + "type": "string" + }, + "queries": { + "description": "Time-series data queries for Cell.", + "type": "array", + "items": { + "$ref": "#/definitions/DashboardQuery" + } + }, + "type": { + "description": "Cell visualization type", + "type": "string", + "enum": [ + "single-stat", + "line", + "line-plus-single-stat", + "line-stacked", + "line-stepplot", + "bar" + ], + "default": "line" + }, + "links": { + "type": "object", + "properties": { + "self": { + "type": "string", + "description": "Self link mapping to this resource", + "format": "url" + } + } + } + } + } + }, + "name": { + "description": "the user-facing name of the dashboard", + "type": "string" + }, + "links": { + "type": "object", + "properties": { + "self": { + "type": "string", + "description": "Self link mapping to this resource", + "format": "url" + } + } + } + }, + "example": { + "id": 4, + "cells": [ + { + "x": 5, + "y": 5, + "w": 4, + "h": 4, + "name": "usage_user", + "queries": [ + { + "query": "SELECT mean(\"usage_user\") AS \"usage_user\" FROM \"cpu\"", + "db": "telegraf", + "label": "%" + } + ], + "type": "line" + }, + { + "x": 0, + "y": 0, + "w": 4, + "h": 4, + "name": "usage_system", + "queries": [ + { + "query": "SELECT mean(\"usage_system\") AS \"usage_system\" FROM \"cpu\"", + "db": "telegraf", + "label": "%" + } + ], + "type": "line" + } + ], + "name": "lalalalala", + "links": { + "self": "/chronograf/v1/dashboards/4" + } + } + }, + "Routes": { + "type": "object", + "properties": { + "me": { + "description": "Location of the me endpoint.", + "type": "string", + "format": "url" + }, + "layouts": { + "description": "Location of the layouts endpoint", + "type": "string", + "format": "url" + }, + "sources": { + "description": "Location of the sources endpoint", + "type": "string", + "format": "url" + }, + "mappings": { + "description": "Location of the application mappings endpoint", + "type": "string", + "format": "url" + }, + "dashboards": { + "description": "location of the dashboards endpoint", + "type": "string", + "format": "url" + }, + "external": { + "description": "external links provided to client, ex. status feed URL", + "type": "object", + "properties": { + "statusFeed": { + "description": "link to a JSON Feed for the News Feed on client's Status Page", + "type": "string", + "format": "url" + }, + "custom": { + "description": "a collection of custom links set by the user to be rendered in the client User menu", + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "url": { + "type": "string", + "format": "url" + } + } + } + } + } + } + }, + "example": { + "layouts": "/chronograf/v1/layouts", + "mappings": "/chronograf/v1/mappings", + "sources": "/chronograf/v1/sources", + "me": "/chronograf/v1/me", + "dashboards": "/chronograf/v1/dashboards", + "external": { + "statusFeed": "http://news.influxdata.com/feed.json", + "custom": [ + { + "name": "InfluxData", + "url": "https://www.influxdata.com" + } + ] + } + } + }, + "Link": { + "type": "object", + "required": ["rel", "href"], + "readOnly": true, + "description": "URI of resource.", + "properties": { + "rel": { + "type": "string" + }, + "href": { + "type": "string", + "format": "url" + } + } + }, + "Error": { + "type": "object", + "properties": { + "code": { + "type": "integer", + "format": "int32" + }, + "message": { + "type": "string" + } + } + } + } } From a4be2a25b06db4d5afdeadb7bf9a846a9c53b79b Mon Sep 17 00:00:00 2001 From: Chris Goller Date: Wed, 19 Jul 2017 01:45:31 -0500 Subject: [PATCH 2/4] Update CHANGELOG to mention adding shared secret JWT to InfluxDB --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7613754c15..b650ec82d7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ ### Features 1. [#1717](https://github.com/influxdata/chronograf/pull/1717): View server generated TICKscripts 1. [#1681](https://github.com/influxdata/chronograf/pull/1681): Add the ability to select Custom Time Ranges in the Hostpages, Data Explorer, and Dashboards. +1. [#1738](https://github.com/influxdata/chronograf/pull/1738): Add shared secret JWT authorization to InfluxDB ### UI Improvements From 9031493abb5d73236c708336b9fcdb34e948f2a2 Mon Sep 17 00:00:00 2001 From: Hunter Trujillo Date: Thu, 20 Jul 2017 10:34:27 -0600 Subject: [PATCH 3/4] Formatted with Prettier 1.5.3 CLI and Prettier 1.5.2 Atom --- ui/src/admin/components/AdminTabs.js | 10 ++++- ui/src/admin/components/DatabaseTable.js | 4 +- .../admin/components/DatabaseTableHeader.js | 4 +- ui/src/admin/components/EmptyRow.js | 4 +- ui/src/admin/components/QueriesTable.js | 4 +- ui/src/admin/components/QueryRow.js | 6 ++- ui/src/admin/components/RoleRow.js | 4 +- ui/src/admin/components/UserRow.js | 4 +- ui/src/admin/containers/AdminPage.js | 4 +- ui/src/alerts/components/AlertsTable.js | 13 +++--- ui/src/alerts/containers/AlertsApp.js | 4 +- ui/src/auth/Login.js | 4 +- ui/src/dashboards/actions/index.js | 4 +- ui/src/dashboards/components/Dashboard.js | 23 +++++----- .../dashboards/components/DashboardHeader.js | 4 +- .../dashboards/components/DashboardsHeader.js | 4 +- .../components/DashboardsPageContents.js | 4 +- .../dashboards/components/DashboardsTable.js | 4 +- .../components/TemplateQueryBuilder.js | 6 ++- .../components/TemplateVariableRow.js | 4 +- ui/src/dashboards/reducers/ui.js | 8 ++-- ui/src/data_explorer/actions/view/index.js | 6 ++- ui/src/data_explorer/components/FieldList.js | 11 ++--- .../components/GroupByTimeDropdown.js | 11 ++++- .../components/MeasurementList.js | 4 +- .../components/NoDataNodeError.js | 8 +++- .../data_explorer/components/QueryBuilder.js | 11 ++++- .../data_explorer/components/QueryEditor.js | 4 +- .../data_explorer/components/QueryMakerTab.js | 4 +- ui/src/data_explorer/components/Table.js | 27 ++++++++---- ui/src/data_explorer/components/VisHeader.js | 4 +- .../data_explorer/components/WriteDataBody.js | 3 +- .../components/WriteDataFooter.js | 3 +- .../data_explorer/components/WriteDataForm.js | 2 +- .../components/WriteDataHeader.js | 3 +- ui/src/data_explorer/containers/Header.js | 4 +- ui/src/data_explorer/reducers/queryConfigs.js | 6 ++- ui/src/hosts/components/HostsTable.js | 17 ++++---- ui/src/hosts/containers/HostsPage.js | 4 +- ui/src/kapacitor/components/AlertTabs.js | 10 ++++- ui/src/kapacitor/components/KapacitorForm.js | 4 +- ui/src/kapacitor/components/KapacitorRules.js | 12 +++--- .../components/KapacitorRulesTable.js | 20 +++++++-- ui/src/kapacitor/components/RuleGraph.js | 11 +++-- .../components/RuleMessageAlertConfig.js | 4 +- ui/src/kapacitor/components/ValuesSection.js | 4 +- .../components/config/OpsGenieConfig.js | 15 +++---- .../components/config/PagerDutyConfig.js | 7 +--- .../components/config/TelegramConfig.js | 12 ++---- .../containers/KapacitorTasksPage.js | 6 +-- ui/src/shared/apis/index.js | 8 ++-- .../shared/components/AutoRefreshDropdown.js | 4 +- ui/src/shared/components/ContextMenu.js | 42 ++++++++----------- .../shared/components/CustomTimeIndicator.js | 6 ++- ui/src/shared/components/Dropdown.js | 10 ++++- ui/src/shared/components/DygraphLegend.js | 8 +++- ui/src/shared/components/LineGraph.js | 4 +- ui/src/shared/components/MiniGraph.js | 10 ++++- .../shared/components/MultiSelectDropdown.js | 4 +- ui/src/shared/components/NoKapacitorError.js | 3 +- ui/src/shared/components/Notifications.js | 3 +- .../shared/components/OverlayTechnologies.js | 4 +- ui/src/shared/components/Tabs.js | 22 ++++++++-- ui/src/shared/components/Tooltip.js | 4 +- ui/src/shared/graphs/helpers.js | 7 ++-- ui/src/shared/parsing/parseAlerta.js | 5 +-- ui/src/side_nav/components/NavItems.js | 6 ++- ui/src/side_nav/modals/RenameCluster.js | 1 - ui/src/sources/components/InfluxTable.js | 8 +++- ui/src/sources/components/SourceForm.js | 4 +- ui/src/sources/containers/ManageSources.js | 4 +- ui/src/status/components/GettingStarted.js | 14 ++++--- ui/src/status/components/JSONFeedReader.js | 12 ++++-- ui/src/utils/queryTransitions.js | 20 +++++++-- 74 files changed, 355 insertions(+), 221 deletions(-) diff --git a/ui/src/admin/components/AdminTabs.js b/ui/src/admin/components/AdminTabs.js index 2c78b292f3..4067a4e43d 100644 --- a/ui/src/admin/components/AdminTabs.js +++ b/ui/src/admin/components/AdminTabs.js @@ -88,11 +88,17 @@ const AdminTabs = ({ return ( - {tabs.map((t, i) => {tabs[i].type})} + {tabs.map((t, i) => + + {tabs[i].type} + + )} {tabs.map((t, i) => - {t.component} + + {t.component} + )} diff --git a/ui/src/admin/components/DatabaseTable.js b/ui/src/admin/components/DatabaseTable.js index 018836e35a..a763fa82fa 100644 --- a/ui/src/admin/components/DatabaseTable.js +++ b/ui/src/admin/components/DatabaseTable.js @@ -52,9 +52,7 @@ const DatabaseTable = ({ - + diff --git a/ui/src/admin/components/DatabaseTableHeader.js b/ui/src/admin/components/DatabaseTableHeader.js index 6348eae16d..29577460a3 100644 --- a/ui/src/admin/components/DatabaseTableHeader.js +++ b/ui/src/admin/components/DatabaseTableHeader.js @@ -105,7 +105,9 @@ const Header = ({ return (
-

{database.name}

+

+ {database.name} +

{database.hasOwnProperty('deleteCode') ? deleteConfirmation : buttons}
) diff --git a/ui/src/admin/components/EmptyRow.js b/ui/src/admin/components/EmptyRow.js index e6ce4050ac..e26587e1f6 100644 --- a/ui/src/admin/components/EmptyRow.js +++ b/ui/src/admin/components/EmptyRow.js @@ -3,7 +3,9 @@ import React, {PropTypes} from 'react' const EmptyRow = ({tableName}) => diff --git a/ui/src/admin/components/QueriesTable.js b/ui/src/admin/components/QueriesTable.js index e7ca13022e..9f90af4eec 100644 --- a/ui/src/admin/components/QueriesTable.js +++ b/ui/src/admin/components/QueriesTable.js @@ -14,9 +14,7 @@ const QueriesTable = ({queries, onKillQuery}) => Database - + diff --git a/ui/src/admin/components/QueryRow.js b/ui/src/admin/components/QueryRow.js index 08bc009f93..1bb4436e52 100644 --- a/ui/src/admin/components/QueryRow.js +++ b/ui/src/admin/components/QueryRow.js @@ -39,7 +39,11 @@ class QueryRow extends Component { > {database} - + + + ) default: - return
n/a
+ return ( +
+ n/a +
+ ) } } diff --git a/ui/src/dashboards/components/TemplateVariableRow.js b/ui/src/dashboards/components/TemplateVariableRow.js index 0f91627d78..ef89cd172f 100644 --- a/ui/src/dashboards/components/TemplateVariableRow.js +++ b/ui/src/dashboards/components/TemplateVariableRow.js @@ -198,7 +198,9 @@ const TableInput = ({ /> :
onStartEdit(name)}> -
{defaultValue}
+
+ {defaultValue} +
} diff --git a/ui/src/dashboards/reducers/ui.js b/ui/src/dashboards/reducers/ui.js index 92ae13c688..7ec6557877 100644 --- a/ui/src/dashboards/reducers/ui.js +++ b/ui/src/dashboards/reducers/ui.js @@ -217,12 +217,12 @@ export default function ui(state = initialState, action) { const dashboards = state.dashboards.map( dashboard => - (dashboard.id === dashboardID + dashboard.id === dashboardID ? { ...dashboard, templates: dashboard.templates.map( template => - (template.id === templateID + template.id === templateID ? { ...template, values: values.map((value, i) => ({ @@ -231,10 +231,10 @@ export default function ui(state = initialState, action) { type: TEMPLATE_VARIABLE_TYPES[template.type], })), } - : template) + : template ), } - : dashboard) + : dashboard ) return {...state, dashboards} diff --git a/ui/src/data_explorer/actions/view/index.js b/ui/src/data_explorer/actions/view/index.js index e4c896d277..8ecc8b786b 100644 --- a/ui/src/data_explorer/actions/view/index.js +++ b/ui/src/data_explorer/actions/view/index.js @@ -39,7 +39,11 @@ export function toggleField(queryId, fieldFunc, isKapacitorRule) { // all fields implicitly have a function applied to them, so consequently // we need to set the auto group by time -export const toggleFieldWithGroupByInterval = (queryID, fieldFunc, isKapacitorRule) => (dispatch) => { +export const toggleFieldWithGroupByInterval = ( + queryID, + fieldFunc, + isKapacitorRule +) => dispatch => { dispatch(toggleField(queryID, fieldFunc, isKapacitorRule)) dispatch(groupByTime(queryID, DEFAULT_DATA_EXPLORER_GROUP_BY_INTERVAL)) } diff --git a/ui/src/data_explorer/components/FieldList.js b/ui/src/data_explorer/components/FieldList.js index 75d41f7418..161e42e343 100644 --- a/ui/src/data_explorer/components/FieldList.js +++ b/ui/src/data_explorer/components/FieldList.js @@ -7,12 +7,7 @@ import FancyScrollbar from 'shared/components/FancyScrollbar' import {showFieldKeys} from 'shared/apis/metaQuery' import showFieldKeysParser from 'shared/parsing/showFieldKeys' -const { - bool, - func, - shape, - string, -} = PropTypes +const {bool, func, shape, string} = PropTypes const FieldList = React.createClass({ propTypes: { @@ -112,7 +107,9 @@ const FieldList = React.createClass({ if (!database || !measurement) { return (
- No Measurement selected + + No Measurement selected +
) } diff --git a/ui/src/data_explorer/components/GroupByTimeDropdown.js b/ui/src/data_explorer/components/GroupByTimeDropdown.js index 81ea8e7e03..d0c3400fd0 100644 --- a/ui/src/data_explorer/components/GroupByTimeDropdown.js +++ b/ui/src/data_explorer/components/GroupByTimeDropdown.js @@ -17,11 +17,18 @@ const GroupByTimeDropdown = React.createClass({ }, render() { - const {selected, onChooseGroupByTime, isInRuleBuilder, isInDataExplorer} = this.props + const { + selected, + onChooseGroupByTime, + isInRuleBuilder, + isInDataExplorer, + } = this.props let validOptions = groupByTimeOptions if (isInDataExplorer) { - validOptions = validOptions.filter(({menuOption}) => menuOption !== DEFAULT_DASHBOARD_GROUP_BY_INTERVAL) + validOptions = validOptions.filter( + ({menuOption}) => menuOption !== DEFAULT_DASHBOARD_GROUP_BY_INTERVAL + ) } return ( diff --git a/ui/src/data_explorer/components/MeasurementList.js b/ui/src/data_explorer/components/MeasurementList.js index fba817da4c..d69d751989 100644 --- a/ui/src/data_explorer/components/MeasurementList.js +++ b/ui/src/data_explorer/components/MeasurementList.js @@ -112,7 +112,9 @@ const MeasurementList = React.createClass({ if (!this.props.query.database) { return (
- No Database selected + + No Database selected +
) } diff --git a/ui/src/data_explorer/components/NoDataNodeError.js b/ui/src/data_explorer/components/NoDataNodeError.js index 3410e71f4c..51eade84f4 100644 --- a/ui/src/data_explorer/components/NoDataNodeError.js +++ b/ui/src/data_explorer/components/NoDataNodeError.js @@ -8,8 +8,12 @@ const NoDataNodeError = React.createClass({ render() { return ( - {errorCopy.noData.head} - {errorCopy.noData.body} + + {errorCopy.noData.head} + + + {errorCopy.noData.body} + ) }, diff --git a/ui/src/data_explorer/components/QueryBuilder.js b/ui/src/data_explorer/components/QueryBuilder.js index 00b214dac7..a7613c724c 100644 --- a/ui/src/data_explorer/components/QueryBuilder.js +++ b/ui/src/data_explorer/components/QueryBuilder.js @@ -52,7 +52,10 @@ const QueryBuilder = React.createClass({ }, handleToggleField(field) { - this.props.actions.toggleFieldWithGroupByInterval(this.props.query.id, field) + this.props.actions.toggleFieldWithGroupByInterval( + this.props.query.id, + field + ) }, handleGroupByTime(time) { @@ -60,7 +63,11 @@ const QueryBuilder = React.createClass({ }, handleApplyFuncsToField(fieldFunc) { - this.props.actions.applyFuncsToField(this.props.query.id, fieldFunc, this.props.isInDataExplorer) + this.props.actions.applyFuncsToField( + this.props.query.id, + fieldFunc, + this.props.isInDataExplorer + ) }, handleChooseTag(tag) { diff --git a/ui/src/data_explorer/components/QueryEditor.js b/ui/src/data_explorer/components/QueryEditor.js index 9f2b628528..4e6f49431d 100644 --- a/ui/src/data_explorer/components/QueryEditor.js +++ b/ui/src/data_explorer/components/QueryEditor.js @@ -225,7 +225,9 @@ class QueryEditor extends Component { className={classnames('varmoji', {'varmoji-rotated': isTemplating})} >
-
{this.renderStatus(status)}
+
+ {this.renderStatus(status)} +
{isTemplating ? - +
) diff --git a/ui/src/data_explorer/components/Table.js b/ui/src/data_explorer/components/Table.js index 027dbd3acc..4f13a53e53 100644 --- a/ui/src/data_explorer/components/Table.js +++ b/ui/src/data_explorer/components/Table.js @@ -27,10 +27,18 @@ const CustomCell = React.createClass({ if (columnName === 'time') { const date = moment(new Date(data)).format('MM/DD/YY hh:mm:ssA') - return {date} + return ( + + {date} + + ) } - return {data} + return ( + + {data} + + ) }, }) @@ -137,9 +145,8 @@ const ChronoTable = React.createClass({ const headerHeight = 30 const minWidth = 70 const styleAdjustedHeight = height - stylePixelOffset - const width = columns && columns.length > 1 - ? defaultColumnWidth - : containerWidth + const width = + columns && columns.length > 1 ? defaultColumnWidth : containerWidth if (!query) { return
Please add a query below
@@ -172,9 +179,7 @@ const ChronoTable = React.createClass({ />}
{(columns && !columns.length) || (values && !values.length) - ?
- This series is empty -
+ ?
This series is empty
:
- Retention Policy - Retention Policy Duration
-

You don't have any {tableName},
why not create one?

+

+ You don't have any {tableName},
why not create one? +

Query - Running - Running
{query} + + {query} + + - {name} + {name} + {allPermissions && allPermissions.length ? - {name} + {name} +
-

- Admin -

+

Admin

diff --git a/ui/src/alerts/components/AlertsTable.js b/ui/src/alerts/components/AlertsTable.js index 0ddc64877c..f5dfce8932 100644 --- a/ui/src/alerts/components/AlertsTable.js +++ b/ui/src/alerts/components/AlertsTable.js @@ -48,9 +48,8 @@ class AlertsTable extends Component { changeSort(key) { // if we're using the key, reverse order; otherwise, set it with ascending if (this.state.sortKey === key) { - const reverseDirection = this.state.sortDirection === 'asc' - ? 'desc' - : 'asc' + const reverseDirection = + this.state.sortDirection === 'asc' ? 'desc' : 'asc' this.setState({sortDirection: reverseDirection}) } else { this.setState({sortKey: key, sortDirection: 'asc'}) @@ -192,9 +191,7 @@ class AlertsTable extends Component {

:
-

- There are no Alerts to display -

+

There are no Alerts to display


Try changing the Time Range or @@ -236,7 +233,9 @@ class AlertsTable extends Component {
:
-

{this.props.alerts.length} Alerts

+

+ {this.props.alerts.length} Alerts +

{this.props.alerts.length ? : null} diff --git a/ui/src/alerts/containers/AlertsApp.js b/ui/src/alerts/containers/AlertsApp.js index a1bb9e138a..34140b11f4 100644 --- a/ui/src/alerts/containers/AlertsApp.js +++ b/ui/src/alerts/containers/AlertsApp.js @@ -153,9 +153,7 @@ class AlertsApp extends Component {
-

- Alert History -

+

Alert History

diff --git a/ui/src/auth/Login.js b/ui/src/auth/Login.js index bdcedfbbf0..4edbf15b30 100644 --- a/ui/src/auth/Login.js +++ b/ui/src/auth/Login.js @@ -15,7 +15,9 @@ const Login = ({authData: {auth}}) => {

Chronograf

-

{VERSION} / Time-Series Data Visualization

+

+ {VERSION} / Time-Series Data Visualization +

{auth.links && auth.links.map(({name, login, label}) => diff --git a/ui/src/dashboards/actions/index.js b/ui/src/dashboards/actions/index.js index 1ee23908f2..71f9c3ba2b 100644 --- a/ui/src/dashboards/actions/index.js +++ b/ui/src/dashboards/actions/index.js @@ -14,9 +14,7 @@ import {errorThrown} from 'shared/actions/errors' import {NEW_DEFAULT_DASHBOARD_CELL} from 'src/dashboards/constants' import {TEMPLATE_VARIABLE_SELECTED} from 'shared/constants/actionTypes' -import { - makeQueryForTemplate, -} from 'src/dashboards/utils/templateVariableQueryGenerator' +import {makeQueryForTemplate} from 'src/dashboards/utils/templateVariableQueryGenerator' import parsers from 'shared/parsing' export const loadDashboards = (dashboards, dashboardID) => ({ diff --git a/ui/src/dashboards/components/Dashboard.js b/ui/src/dashboards/components/Dashboard.js index 2c64b71ebb..0afd0e5f8d 100644 --- a/ui/src/dashboards/components/Dashboard.js +++ b/ui/src/dashboards/components/Dashboard.js @@ -26,19 +26,16 @@ const Dashboard = ({ }) => { const cells = dashboard.cells.map(cell => { const dashboardCell = {...cell} - dashboardCell.queries = dashboardCell.queries.map(({ - label, - query, - queryConfig, - db, - }) => ({ - label, - query, - queryConfig, - db, - database: db, - text: query, - })) + dashboardCell.queries = dashboardCell.queries.map( + ({label, query, queryConfig, db}) => ({ + label, + query, + queryConfig, + db, + database: db, + text: query, + }) + ) return dashboardCell }) diff --git a/ui/src/dashboards/components/DashboardHeader.js b/ui/src/dashboards/components/DashboardHeader.js index 9dec62d47a..6977d98d19 100644 --- a/ui/src/dashboards/components/DashboardHeader.js +++ b/ui/src/dashboards/components/DashboardHeader.js @@ -35,7 +35,9 @@ const DashboardHeader = ({ type="button" data-toggle="dropdown" > - {buttonText} + + {buttonText} +
    diff --git a/ui/src/dashboards/components/DashboardsHeader.js b/ui/src/dashboards/components/DashboardsHeader.js index 3774f7293a..a66d4021e5 100644 --- a/ui/src/dashboards/components/DashboardsHeader.js +++ b/ui/src/dashboards/components/DashboardsHeader.js @@ -7,9 +7,7 @@ const DashboardsHeader = ({sourceName}) => {
    -

    - Dashboards -

    +

    Dashboards

    diff --git a/ui/src/dashboards/components/DashboardsPageContents.js b/ui/src/dashboards/components/DashboardsPageContents.js index be859ec9ef..02d477b4be 100644 --- a/ui/src/dashboards/components/DashboardsPageContents.js +++ b/ui/src/dashboards/components/DashboardsPageContents.js @@ -25,7 +25,9 @@ const DashboardsPageContents = ({
    -

    {tableHeader}

    +

    + {tableHeader} +

{columnName}} + header={ + + {columnName} + + } cell={({rowIndex}) => )} : null} -
{name}
+
+ {name} +
const {arrayOf, func, string} = PropTypes diff --git a/ui/src/data_explorer/components/WriteDataBody.js b/ui/src/data_explorer/components/WriteDataBody.js index b0d14ec07f..a8aa651c76 100644 --- a/ui/src/data_explorer/components/WriteDataBody.js +++ b/ui/src/data_explorer/components/WriteDataBody.js @@ -12,7 +12,7 @@ const WriteDataBody = ({ isManual, fileInput, handleFileOpen, -}) => ( +}) =>
{isManual ?