Merge branch 'multitenancy' into multitenancy_reset_current_org

pull/2399/head
Jared Scheib 2017-12-05 18:47:05 -08:00
commit bbe1330cbf
19 changed files with 472 additions and 773 deletions

View File

@ -125,12 +125,11 @@ func MarshalLayout(l chronograf.Layout) ([]byte, error) {
}
}
return proto.Marshal(&Layout{
ID: l.ID,
Measurement: l.Measurement,
Application: l.Application,
Autoflow: l.Autoflow,
Cells: cells,
Organization: l.Organization,
ID: l.ID,
Measurement: l.Measurement,
Application: l.Application,
Autoflow: l.Autoflow,
Cells: cells,
})
}
@ -145,7 +144,6 @@ func UnmarshalLayout(data []byte, l *chronograf.Layout) error {
l.Measurement = pb.Measurement
l.Application = pb.Application
l.Autoflow = pb.Autoflow
l.Organization = pb.Organization
cells := make([]chronograf.Cell, len(pb.Cells))
for i, c := range pb.Cells {
queries := make([]chronograf.Query, len(c.Queries))

View File

@ -208,12 +208,11 @@ func (*Server) ProtoMessage() {}
func (*Server) Descriptor() ([]byte, []int) { return fileDescriptorInternal, []int{7} }
type Layout struct {
ID string `protobuf:"bytes,1,opt,name=ID,proto3" json:"ID,omitempty"`
Application string `protobuf:"bytes,2,opt,name=Application,proto3" json:"Application,omitempty"`
Measurement string `protobuf:"bytes,3,opt,name=Measurement,proto3" json:"Measurement,omitempty"`
Cells []*Cell `protobuf:"bytes,4,rep,name=Cells" json:"Cells,omitempty"`
Autoflow bool `protobuf:"varint,5,opt,name=Autoflow,proto3" json:"Autoflow,omitempty"`
Organization string `protobuf:"bytes,6,opt,name=Organization,proto3" json:"Organization,omitempty"`
ID string `protobuf:"bytes,1,opt,name=ID,proto3" json:"ID,omitempty"`
Application string `protobuf:"bytes,2,opt,name=Application,proto3" json:"Application,omitempty"`
Measurement string `protobuf:"bytes,3,opt,name=Measurement,proto3" json:"Measurement,omitempty"`
Cells []*Cell `protobuf:"bytes,4,rep,name=Cells" json:"Cells,omitempty"`
Autoflow bool `protobuf:"varint,5,opt,name=Autoflow,proto3" json:"Autoflow,omitempty"`
}
func (m *Layout) Reset() { *m = Layout{} }
@ -391,81 +390,81 @@ func init() {
func init() { proto.RegisterFile("internal.proto", fileDescriptorInternal) }
var fileDescriptorInternal = []byte{
// 1211 bytes of a gzipped FileDescriptorProto
// 1207 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xbc, 0x56, 0x5f, 0x8f, 0xdb, 0x44,
0x10, 0xd7, 0xc6, 0x71, 0x12, 0x4f, 0xae, 0x05, 0x2d, 0x15, 0x35, 0x45, 0x42, 0xc1, 0x02, 0xe9,
0x10, 0xd7, 0xc6, 0x71, 0x62, 0x4f, 0xae, 0x05, 0x2d, 0x15, 0x35, 0x45, 0x42, 0xc1, 0x02, 0xe9,
0x10, 0xf4, 0x40, 0xad, 0x90, 0x10, 0x0f, 0x48, 0xb9, 0x0b, 0xaa, 0x8e, 0xfe, 0xbb, 0x6e, 0x7a,
0xe5, 0x09, 0x55, 0x1b, 0x67, 0x72, 0xb1, 0xea, 0xd8, 0x66, 0x6d, 0xdf, 0xc5, 0x7c, 0x1b, 0x24,
0x9e, 0x78, 0x44, 0xbc, 0x23, 0xf1, 0x84, 0xf8, 0x20, 0x7c, 0x05, 0x5e, 0xd1, 0xec, 0xae, 0x1d,
0xa7, 0x09, 0x55, 0x5f, 0xe0, 0x6d, 0x7f, 0x33, 0xeb, 0xd9, 0x9d, 0x99, 0xdf, 0xfc, 0xbc, 0x70,
0x3d, 0x4a, 0x0a, 0x54, 0x89, 0x8c, 0x8f, 0x32, 0x95, 0x16, 0x29, 0x1f, 0xd4, 0x38, 0xf8, 0xab,
0x03, 0xbd, 0x69, 0x5a, 0xaa, 0x10, 0xf9, 0x75, 0xe8, 0x9c, 0x4e, 0x7c, 0x36, 0x62, 0x87, 0x8e,
0xe8, 0x9c, 0x4e, 0x38, 0x87, 0xee, 0x23, 0xb9, 0x42, 0xbf, 0x33, 0x62, 0x87, 0x9e, 0xd0, 0x6b,
0xb2, 0x3d, 0xad, 0x32, 0xf4, 0x1d, 0x63, 0xa3, 0x35, 0xbf, 0x05, 0x83, 0xf3, 0x9c, 0xa2, 0xad,
0xd0, 0xef, 0x6a, 0x7b, 0x83, 0xc9, 0x77, 0x26, 0xf3, 0xfc, 0x2a, 0x55, 0x73, 0xdf, 0x35, 0xbe,
0x1a, 0xf3, 0x37, 0xc1, 0x39, 0x17, 0x0f, 0xfc, 0x9e, 0x36, 0xd3, 0x92, 0xfb, 0xd0, 0x9f, 0xe0,
0x42, 0x96, 0x71, 0xe1, 0xf7, 0x47, 0xec, 0x70, 0x20, 0x6a, 0x48, 0x71, 0x9e, 0x62, 0x8c, 0x17,
0x4a, 0x2e, 0xfc, 0x81, 0x89, 0x53, 0x63, 0x7e, 0x04, 0xfc, 0x34, 0xc9, 0x31, 0x2c, 0x15, 0x4e,
0x5f, 0x44, 0xd9, 0x33, 0x54, 0xd1, 0xa2, 0xf2, 0x3d, 0x1d, 0x60, 0x8f, 0x87, 0x4e, 0x79, 0x88,
0x85, 0xa4, 0xb3, 0x41, 0x87, 0xaa, 0x21, 0x0f, 0xe0, 0x60, 0xba, 0x94, 0x0a, 0xe7, 0x53, 0x0c,
0x15, 0x16, 0xfe, 0x50, 0xbb, 0xb7, 0x6c, 0xb4, 0xe7, 0xb1, 0xba, 0x90, 0x49, 0xf4, 0x83, 0x2c,
0xa2, 0x34, 0xf1, 0x0f, 0xcc, 0x9e, 0xb6, 0x8d, 0xaa, 0x24, 0xd2, 0x18, 0xfd, 0x6b, 0xa6, 0x4a,
0xb4, 0x0e, 0x7e, 0x65, 0xe0, 0x4d, 0x64, 0xbe, 0x9c, 0xa5, 0x52, 0xcd, 0x5f, 0xab, 0xd6, 0xb7,
0xc1, 0x0d, 0x31, 0x8e, 0x73, 0xdf, 0x19, 0x39, 0x87, 0xc3, 0x3b, 0x37, 0x8f, 0x9a, 0x26, 0x36,
0x71, 0x4e, 0x30, 0x8e, 0x85, 0xd9, 0xc5, 0x3f, 0x03, 0xaf, 0xc0, 0x55, 0x16, 0xcb, 0x02, 0x73,
0xbf, 0xab, 0x3f, 0xe1, 0x9b, 0x4f, 0x9e, 0x5a, 0x97, 0xd8, 0x6c, 0xda, 0x49, 0xc5, 0xdd, 0x4d,
0x25, 0xf8, 0xb9, 0x03, 0xd7, 0xb6, 0x8e, 0xe3, 0x07, 0xc0, 0xd6, 0xfa, 0xe6, 0xae, 0x60, 0x6b,
0x42, 0x95, 0xbe, 0xb5, 0x2b, 0x58, 0x45, 0xe8, 0x4a, 0x73, 0xc3, 0x15, 0xec, 0x8a, 0xd0, 0x52,
0x33, 0xc2, 0x15, 0x6c, 0xc9, 0x3f, 0x82, 0xfe, 0xf7, 0x25, 0xaa, 0x08, 0x73, 0xdf, 0xd5, 0xb7,
0x7b, 0x63, 0x73, 0xbb, 0x27, 0x25, 0xaa, 0x4a, 0xd4, 0x7e, 0xaa, 0x86, 0x66, 0x93, 0xa1, 0x86,
0x5e, 0x93, 0xad, 0x20, 0xe6, 0xf5, 0x8d, 0x8d, 0xd6, 0xb6, 0x8a, 0x86, 0x0f, 0x54, 0xc5, 0xcf,
0xa1, 0x2b, 0xd7, 0x98, 0xfb, 0x9e, 0x8e, 0xff, 0xfe, 0xbf, 0x14, 0xec, 0x68, 0xbc, 0xc6, 0xfc,
0xeb, 0xa4, 0x50, 0x95, 0xd0, 0xdb, 0x6f, 0xdd, 0x03, 0xaf, 0x31, 0x11, 0x2b, 0x5f, 0x60, 0xa5,
0x13, 0xf4, 0x04, 0x2d, 0xf9, 0x07, 0xe0, 0x5e, 0xca, 0xb8, 0x34, 0xcd, 0x19, 0xde, 0xb9, 0xbe,
0x09, 0x3b, 0x5e, 0x47, 0xb9, 0x30, 0xce, 0x2f, 0x3b, 0x5f, 0xb0, 0xe0, 0x17, 0x06, 0x5d, 0xb2,
0x51, 0x65, 0x63, 0xbc, 0x90, 0x61, 0x75, 0x9c, 0x96, 0xc9, 0x3c, 0xf7, 0xd9, 0xc8, 0x39, 0x74,
0xc4, 0x96, 0x8d, 0xbf, 0x0d, 0xbd, 0x99, 0xf1, 0x76, 0x46, 0xce, 0xa1, 0x27, 0x2c, 0xe2, 0x37,
0xc0, 0x8d, 0xe5, 0x0c, 0x63, 0x3b, 0x63, 0x06, 0xd0, 0xee, 0x4c, 0xe1, 0x22, 0x5a, 0xdb, 0x11,
0xb3, 0x88, 0xec, 0x79, 0xb9, 0x20, 0xbb, 0xe9, 0x9e, 0x45, 0x54, 0xae, 0x99, 0xcc, 0x9b, 0x12,
0xd2, 0x9a, 0x22, 0xe7, 0xa1, 0x8c, 0xeb, 0x1a, 0x1a, 0x10, 0xfc, 0xc6, 0x68, 0xb6, 0x0c, 0x27,
0x5a, 0xbc, 0x34, 0x15, 0x7d, 0x07, 0x06, 0xc4, 0x97, 0xe7, 0x97, 0x52, 0x59, 0x6e, 0xf6, 0x09,
0x3f, 0x93, 0x8a, 0x7f, 0x0a, 0x3d, 0x9d, 0xf9, 0x1e, 0x7e, 0xd6, 0xe1, 0x9e, 0x91, 0x5f, 0xd8,
0x6d, 0x4d, 0x07, 0xbb, 0xad, 0x0e, 0x36, 0xc9, 0xba, 0xed, 0x64, 0x6f, 0x83, 0x4b, 0x54, 0xa8,
0xf4, 0xed, 0xf7, 0x46, 0x36, 0x84, 0x31, 0xbb, 0x82, 0x73, 0xb8, 0xb6, 0x75, 0x62, 0x73, 0x12,
0xdb, 0x3e, 0x69, 0xd3, 0x45, 0xcf, 0x76, 0x8d, 0x74, 0x25, 0xc7, 0x18, 0xc3, 0x02, 0xe7, 0xba,
0xde, 0x03, 0xd1, 0xe0, 0xe0, 0x47, 0xb6, 0x89, 0xab, 0xcf, 0x23, 0xe5, 0x08, 0xd3, 0xd5, 0x4a,
0x26, 0x73, 0x1b, 0xba, 0x86, 0x54, 0xb7, 0xf9, 0xcc, 0x86, 0xee, 0xcc, 0x67, 0x84, 0x55, 0x66,
0x3b, 0xd8, 0x51, 0x19, 0x1f, 0xc1, 0x70, 0x85, 0x32, 0x2f, 0x15, 0xae, 0x30, 0x29, 0x6c, 0x09,
0xda, 0x26, 0x7e, 0x13, 0xfa, 0x85, 0xbc, 0x78, 0x4e, 0xdc, 0xb3, 0x9d, 0x2c, 0xe4, 0xc5, 0x7d,
0xac, 0xf8, 0xbb, 0xe0, 0x2d, 0x22, 0x8c, 0xe7, 0xda, 0x65, 0xda, 0x39, 0xd0, 0x86, 0xfb, 0x58,
0x05, 0x7f, 0x30, 0xe8, 0x4d, 0x51, 0x5d, 0xa2, 0x7a, 0x2d, 0x49, 0x69, 0x4b, 0xb5, 0xf3, 0x0a,
0xa9, 0xee, 0xee, 0x97, 0x6a, 0x77, 0x23, 0xd5, 0x37, 0xc0, 0x9d, 0xaa, 0xf0, 0x74, 0xa2, 0x6f,
0xe4, 0x08, 0x03, 0x88, 0x8d, 0xe3, 0xb0, 0x88, 0x2e, 0xd1, 0xea, 0xb7, 0x45, 0x3b, 0x4a, 0x33,
0xd8, 0xa3, 0x34, 0xbf, 0x33, 0xe8, 0x3d, 0x90, 0x55, 0x5a, 0x16, 0x3b, 0x2c, 0x1c, 0xc1, 0x70,
0x9c, 0x65, 0x71, 0x14, 0x9a, 0xaf, 0x4d, 0x46, 0x6d, 0x13, 0xed, 0x78, 0xd8, 0xaa, 0xaf, 0xc9,
0xad, 0x6d, 0xa2, 0x29, 0x3e, 0xd1, 0x6a, 0x6a, 0xa4, 0xb1, 0x35, 0xc5, 0x46, 0x44, 0xb5, 0x93,
0x8a, 0x30, 0x2e, 0x8b, 0x74, 0x11, 0xa7, 0x57, 0x3a, 0xdb, 0x81, 0x68, 0xf0, 0x4e, 0x12, 0xbd,
0x3d, 0x49, 0xfc, 0xd9, 0x81, 0xee, 0xff, 0xa5, 0x92, 0x07, 0xc0, 0x22, 0x7b, 0x09, 0x16, 0x35,
0x9a, 0xd9, 0x6f, 0x69, 0xa6, 0x0f, 0xfd, 0x4a, 0xc9, 0xe4, 0x02, 0x73, 0x7f, 0xa0, 0x15, 0xa8,
0x86, 0xda, 0xa3, 0x67, 0xcd, 0x88, 0xa5, 0x27, 0x6a, 0xd8, 0xcc, 0x0e, 0xb4, 0x66, 0xe7, 0x13,
0xab, 0xab, 0x43, 0x7d, 0x23, 0x7f, 0xbb, 0x74, 0xff, 0x9d, 0x9c, 0xfe, 0xcd, 0xc0, 0x6d, 0x06,
0xef, 0x64, 0x7b, 0xf0, 0x4e, 0x36, 0x83, 0x37, 0x39, 0xae, 0x07, 0x6f, 0x72, 0x4c, 0x58, 0x9c,
0xd5, 0x83, 0x27, 0xce, 0xa8, 0xa1, 0xf7, 0x54, 0x5a, 0x66, 0xc7, 0x95, 0xe9, 0xbc, 0x27, 0x1a,
0x4c, 0x6c, 0xfd, 0x76, 0x89, 0xca, 0x96, 0xda, 0x13, 0x16, 0x11, 0xb7, 0x1f, 0x68, 0x51, 0x32,
0xc5, 0x35, 0x80, 0x7f, 0x08, 0xae, 0xa0, 0xe2, 0xe9, 0x0a, 0x6f, 0xf5, 0x45, 0x9b, 0x85, 0xf1,
0x52, 0x50, 0xf3, 0x9e, 0xb2, 0x24, 0xaf, 0x5f, 0x57, 0x1f, 0x43, 0x6f, 0xba, 0x8c, 0x16, 0x45,
0xfd, 0x77, 0x7a, 0xab, 0x25, 0x6a, 0xd1, 0x0a, 0xb5, 0x4f, 0xd8, 0x2d, 0xc1, 0x13, 0xf0, 0x1a,
0xe3, 0xe6, 0x3a, 0xac, 0x7d, 0x1d, 0x0e, 0xdd, 0xf3, 0x24, 0x2a, 0xea, 0xf1, 0xa6, 0x35, 0x25,
0xfb, 0xa4, 0x94, 0x49, 0x11, 0x15, 0x55, 0x3d, 0xde, 0x35, 0x0e, 0xee, 0xda, 0xeb, 0x53, 0xb8,
0xf3, 0x2c, 0x43, 0x65, 0xa5, 0xc2, 0x00, 0x7d, 0x48, 0x7a, 0x85, 0x46, 0xe5, 0x1d, 0x61, 0x40,
0xf0, 0x1d, 0x78, 0xe3, 0x18, 0x55, 0x21, 0xca, 0x78, 0xf7, 0xdf, 0xc0, 0xa1, 0xfb, 0xcd, 0xf4,
0xf1, 0xa3, 0xfa, 0x06, 0xb4, 0xde, 0xc8, 0x82, 0xf3, 0x92, 0x2c, 0xdc, 0x97, 0x99, 0x3c, 0x9d,
0x68, 0x9e, 0x3b, 0xc2, 0xa2, 0xe0, 0x27, 0x06, 0x5d, 0xd2, 0x9f, 0x56, 0xe8, 0xee, 0xab, 0xb4,
0xeb, 0x4c, 0xa5, 0x97, 0xd1, 0x1c, 0x55, 0x9d, 0x5c, 0x8d, 0x75, 0xd1, 0xc3, 0x25, 0x36, 0x0f,
0x50, 0x8b, 0x88, 0x6b, 0xf4, 0xf8, 0xaa, 0x67, 0xa9, 0xc5, 0x35, 0x32, 0x0b, 0xe3, 0xe4, 0xef,
0x01, 0x4c, 0xcb, 0x0c, 0xd5, 0x78, 0xbe, 0x8a, 0xcc, 0x58, 0x0f, 0x44, 0xcb, 0x12, 0x7c, 0x65,
0x9e, 0x73, 0x3b, 0x02, 0xc0, 0xf6, 0x3f, 0xfd, 0x5e, 0xbe, 0x79, 0x10, 0x6f, 0x7f, 0xf7, 0x5a,
0xd9, 0x8e, 0x60, 0x68, 0xdf, 0xbe, 0xfa, 0x25, 0x69, 0x05, 0xad, 0x65, 0xa2, 0x9c, 0xcf, 0xca,
0x59, 0x1c, 0x85, 0x3a, 0xe7, 0x81, 0xb0, 0x68, 0xd6, 0xd3, 0x4f, 0xfc, 0xbb, 0xff, 0x04, 0x00,
0x00, 0xff, 0xff, 0xe3, 0xb7, 0x65, 0x8a, 0xf4, 0x0b, 0x00, 0x00,
0xe5, 0x09, 0x55, 0x1b, 0x67, 0x72, 0xb1, 0xea, 0xd8, 0x66, 0x6d, 0xdf, 0x9d, 0xf9, 0x30, 0x48,
0x48, 0x3c, 0xf1, 0x88, 0x78, 0xe7, 0x15, 0xf1, 0x41, 0xf8, 0x0a, 0xbc, 0xa2, 0xd9, 0x5d, 0x3b,
0x4e, 0x2f, 0x54, 0x7d, 0x81, 0xb7, 0xfd, 0xcd, 0xac, 0x67, 0x77, 0x66, 0x7e, 0xf3, 0xf3, 0xc2,
0xf5, 0x38, 0x2d, 0x51, 0xa5, 0x32, 0x39, 0xc8, 0x55, 0x56, 0x66, 0xdc, 0x6b, 0x70, 0xf8, 0x57,
0x0f, 0x06, 0xb3, 0xac, 0x52, 0x11, 0xf2, 0xeb, 0xd0, 0x3b, 0x9e, 0x06, 0x6c, 0xcc, 0xf6, 0x1d,
0xd1, 0x3b, 0x9e, 0x72, 0x0e, 0xfd, 0x47, 0x72, 0x8d, 0x41, 0x6f, 0xcc, 0xf6, 0x7d, 0xa1, 0xd7,
0x64, 0x7b, 0x5a, 0xe7, 0x18, 0x38, 0xc6, 0x46, 0x6b, 0x7e, 0x0b, 0xbc, 0xd3, 0x82, 0xa2, 0xad,
0x31, 0xe8, 0x6b, 0x7b, 0x8b, 0xc9, 0x77, 0x22, 0x8b, 0xe2, 0x22, 0x53, 0x8b, 0xc0, 0x35, 0xbe,
0x06, 0xf3, 0x37, 0xc1, 0x39, 0x15, 0x0f, 0x82, 0x81, 0x36, 0xd3, 0x92, 0x07, 0x30, 0x9c, 0xe2,
0x52, 0x56, 0x49, 0x19, 0x0c, 0xc7, 0x6c, 0xdf, 0x13, 0x0d, 0xa4, 0x38, 0x4f, 0x31, 0xc1, 0x33,
0x25, 0x97, 0x81, 0x67, 0xe2, 0x34, 0x98, 0x1f, 0x00, 0x3f, 0x4e, 0x0b, 0x8c, 0x2a, 0x85, 0xb3,
0x17, 0x71, 0xfe, 0x0c, 0x55, 0xbc, 0xac, 0x03, 0x5f, 0x07, 0xd8, 0xe1, 0xa1, 0x53, 0x1e, 0x62,
0x29, 0xe9, 0x6c, 0xd0, 0xa1, 0x1a, 0xc8, 0x43, 0xd8, 0x9b, 0xad, 0xa4, 0xc2, 0xc5, 0x0c, 0x23,
0x85, 0x65, 0x30, 0xd2, 0xee, 0x2d, 0x1b, 0xed, 0x79, 0xac, 0xce, 0x64, 0x1a, 0xff, 0x20, 0xcb,
0x38, 0x4b, 0x83, 0x3d, 0xb3, 0xa7, 0x6b, 0xa3, 0x2a, 0x89, 0x2c, 0xc1, 0xe0, 0x9a, 0xa9, 0x12,
0xad, 0xc3, 0xdf, 0x18, 0xf8, 0x53, 0x59, 0xac, 0xe6, 0x99, 0x54, 0x8b, 0xd7, 0xaa, 0xf5, 0x6d,
0x70, 0x23, 0x4c, 0x92, 0x22, 0x70, 0xc6, 0xce, 0xfe, 0xe8, 0xce, 0xcd, 0x83, 0xb6, 0x89, 0x6d,
0x9c, 0x23, 0x4c, 0x12, 0x61, 0x76, 0xf1, 0xcf, 0xc0, 0x2f, 0x71, 0x9d, 0x27, 0xb2, 0xc4, 0x22,
0xe8, 0xeb, 0x4f, 0xf8, 0xe6, 0x93, 0xa7, 0xd6, 0x25, 0x36, 0x9b, 0xae, 0xa4, 0xe2, 0x5e, 0x4d,
0x25, 0xfc, 0xa5, 0x07, 0xd7, 0xb6, 0x8e, 0xe3, 0x7b, 0xc0, 0x2e, 0xf5, 0xcd, 0x5d, 0xc1, 0x2e,
0x09, 0xd5, 0xfa, 0xd6, 0xae, 0x60, 0x35, 0xa1, 0x0b, 0xcd, 0x0d, 0x57, 0xb0, 0x0b, 0x42, 0x2b,
0xcd, 0x08, 0x57, 0xb0, 0x15, 0xff, 0x08, 0x86, 0xdf, 0x57, 0xa8, 0x62, 0x2c, 0x02, 0x57, 0xdf,
0xee, 0x8d, 0xcd, 0xed, 0x9e, 0x54, 0xa8, 0x6a, 0xd1, 0xf8, 0xa9, 0x1a, 0x9a, 0x4d, 0x86, 0x1a,
0x7a, 0x4d, 0xb6, 0x92, 0x98, 0x37, 0x34, 0x36, 0x5a, 0xdb, 0x2a, 0x1a, 0x3e, 0x50, 0x15, 0x3f,
0x87, 0xbe, 0xbc, 0xc4, 0x22, 0xf0, 0x75, 0xfc, 0xf7, 0xff, 0xa5, 0x60, 0x07, 0x93, 0x4b, 0x2c,
0xbe, 0x4e, 0x4b, 0x55, 0x0b, 0xbd, 0xfd, 0xd6, 0x3d, 0xf0, 0x5b, 0x13, 0xb1, 0xf2, 0x05, 0xd6,
0x3a, 0x41, 0x5f, 0xd0, 0x92, 0x7f, 0x00, 0xee, 0xb9, 0x4c, 0x2a, 0xd3, 0x9c, 0xd1, 0x9d, 0xeb,
0x9b, 0xb0, 0x93, 0xcb, 0xb8, 0x10, 0xc6, 0xf9, 0x65, 0xef, 0x0b, 0x16, 0xfe, 0xca, 0xa0, 0x4f,
0x36, 0xaa, 0x6c, 0x82, 0x67, 0x32, 0xaa, 0x0f, 0xb3, 0x2a, 0x5d, 0x14, 0x01, 0x1b, 0x3b, 0xfb,
0x8e, 0xd8, 0xb2, 0xf1, 0xb7, 0x61, 0x30, 0x37, 0xde, 0xde, 0xd8, 0xd9, 0xf7, 0x85, 0x45, 0xfc,
0x06, 0xb8, 0x89, 0x9c, 0x63, 0x62, 0x67, 0xcc, 0x00, 0xda, 0x9d, 0x2b, 0x5c, 0xc6, 0x97, 0x76,
0xc4, 0x2c, 0x22, 0x7b, 0x51, 0x2d, 0xc9, 0x6e, 0xba, 0x67, 0x11, 0x95, 0x6b, 0x2e, 0x8b, 0xb6,
0x84, 0xb4, 0xa6, 0xc8, 0x45, 0x24, 0x93, 0xa6, 0x86, 0x06, 0x84, 0xbf, 0x33, 0x9a, 0x2d, 0xc3,
0x89, 0x0e, 0x2f, 0x4d, 0x45, 0xdf, 0x01, 0x8f, 0xf8, 0xf2, 0xfc, 0x5c, 0x2a, 0xcb, 0xcd, 0x21,
0xe1, 0x67, 0x52, 0xf1, 0x4f, 0x61, 0xa0, 0x33, 0xdf, 0xc1, 0xcf, 0x26, 0xdc, 0x33, 0xf2, 0x0b,
0xbb, 0xad, 0xed, 0x60, 0xbf, 0xd3, 0xc1, 0x36, 0x59, 0xb7, 0x9b, 0xec, 0x6d, 0x70, 0x89, 0x0a,
0xb5, 0xbe, 0xfd, 0xce, 0xc8, 0x86, 0x30, 0x66, 0x57, 0x78, 0x0a, 0xd7, 0xb6, 0x4e, 0x6c, 0x4f,
0x62, 0xdb, 0x27, 0x6d, 0xba, 0xe8, 0xdb, 0xae, 0x91, 0xae, 0x14, 0x98, 0x60, 0x54, 0xe2, 0x42,
0xd7, 0xdb, 0x13, 0x2d, 0x0e, 0x7f, 0x62, 0x9b, 0xb8, 0xfa, 0x3c, 0x52, 0x8e, 0x28, 0x5b, 0xaf,
0x65, 0xba, 0xb0, 0xa1, 0x1b, 0x48, 0x75, 0x5b, 0xcc, 0x6d, 0xe8, 0xde, 0x62, 0x4e, 0x58, 0xe5,
0xb6, 0x83, 0x3d, 0x95, 0xf3, 0x31, 0x8c, 0xd6, 0x28, 0x8b, 0x4a, 0xe1, 0x1a, 0xd3, 0xd2, 0x96,
0xa0, 0x6b, 0xe2, 0x37, 0x61, 0x58, 0xca, 0xb3, 0xe7, 0xc4, 0x3d, 0xdb, 0xc9, 0x52, 0x9e, 0xdd,
0xc7, 0x9a, 0xbf, 0x0b, 0xfe, 0x32, 0xc6, 0x64, 0xa1, 0x5d, 0xa6, 0x9d, 0x9e, 0x36, 0xdc, 0xc7,
0x3a, 0xfc, 0x83, 0xc1, 0x60, 0x86, 0xea, 0x1c, 0xd5, 0x6b, 0x49, 0x4a, 0x57, 0xaa, 0x9d, 0x57,
0x48, 0x75, 0x7f, 0xb7, 0x54, 0xbb, 0x1b, 0xa9, 0xbe, 0x01, 0xee, 0x4c, 0x45, 0xc7, 0x53, 0x7d,
0x23, 0x47, 0x18, 0x40, 0x6c, 0x9c, 0x44, 0x65, 0x7c, 0x8e, 0x56, 0xbf, 0x2d, 0xba, 0xa2, 0x34,
0xde, 0x0e, 0xa5, 0xf9, 0x91, 0xc1, 0xe0, 0x81, 0xac, 0xb3, 0xaa, 0xbc, 0xc2, 0xc2, 0x31, 0x8c,
0x26, 0x79, 0x9e, 0xc4, 0x91, 0xf9, 0xda, 0x64, 0xd4, 0x35, 0xd1, 0x8e, 0x87, 0x9d, 0xfa, 0x9a,
0xdc, 0xba, 0x26, 0x9a, 0xe2, 0x23, 0xad, 0xa6, 0x46, 0x1a, 0x3b, 0x53, 0x6c, 0x44, 0x54, 0x3b,
0xa9, 0x08, 0x93, 0xaa, 0xcc, 0x96, 0x49, 0x76, 0xa1, 0xb3, 0xf5, 0x44, 0x8b, 0xc3, 0x3f, 0x7b,
0xd0, 0xff, 0xbf, 0x14, 0x70, 0x0f, 0x58, 0x6c, 0x9b, 0xcd, 0xe2, 0x56, 0x0f, 0x87, 0x1d, 0x3d,
0x0c, 0x60, 0x58, 0x2b, 0x99, 0x9e, 0x61, 0x11, 0x78, 0x5a, 0x5d, 0x1a, 0xa8, 0x3d, 0x7a, 0x8e,
0x8c, 0x10, 0xfa, 0xa2, 0x81, 0xed, 0x5c, 0x40, 0x67, 0x2e, 0x3e, 0xb1, 0x9a, 0x39, 0xd2, 0x37,
0x0a, 0xb6, 0xcb, 0xf2, 0xdf, 0x49, 0xe5, 0xdf, 0x0c, 0xdc, 0x76, 0xa8, 0x8e, 0xb6, 0x87, 0xea,
0x68, 0x33, 0x54, 0xd3, 0xc3, 0x66, 0xa8, 0xa6, 0x87, 0x84, 0xc5, 0x49, 0x33, 0x54, 0xe2, 0x84,
0x9a, 0x75, 0x4f, 0x65, 0x55, 0x7e, 0x58, 0x9b, 0xae, 0xfa, 0xa2, 0xc5, 0xc4, 0xc4, 0x6f, 0x57,
0xa8, 0x6c, 0xa9, 0x7d, 0x61, 0x11, 0xf1, 0xf6, 0x81, 0x16, 0x1c, 0x53, 0x5c, 0x03, 0xf8, 0x87,
0xe0, 0x0a, 0x2a, 0x9e, 0xae, 0xf0, 0x56, 0x5f, 0xb4, 0x59, 0x18, 0x2f, 0x05, 0x35, 0x6f, 0x25,
0x4b, 0xe0, 0xe6, 0xe5, 0xf4, 0x31, 0x0c, 0x66, 0xab, 0x78, 0x59, 0x36, 0x7f, 0x9e, 0xb7, 0x3a,
0x82, 0x15, 0xaf, 0x51, 0xfb, 0x84, 0xdd, 0x12, 0x3e, 0x01, 0xbf, 0x35, 0x6e, 0xae, 0xc3, 0xba,
0xd7, 0xe1, 0xd0, 0x3f, 0x4d, 0xe3, 0xb2, 0x19, 0x5d, 0x5a, 0x53, 0xb2, 0x4f, 0x2a, 0x99, 0x96,
0x71, 0x59, 0x37, 0xa3, 0xdb, 0xe0, 0xf0, 0xae, 0xbd, 0x3e, 0x85, 0x3b, 0xcd, 0x73, 0x54, 0x56,
0x06, 0x0c, 0xd0, 0x87, 0x64, 0x17, 0x68, 0x14, 0xdc, 0x11, 0x06, 0x84, 0xdf, 0x81, 0x3f, 0x49,
0x50, 0x95, 0xa2, 0x4a, 0xae, 0xea, 0x3e, 0x87, 0xfe, 0x37, 0xb3, 0xc7, 0x8f, 0x9a, 0x1b, 0xd0,
0x7a, 0x33, 0xf2, 0xce, 0x4b, 0x23, 0x7f, 0x5f, 0xe6, 0xf2, 0x78, 0xaa, 0x79, 0xee, 0x08, 0x8b,
0xc2, 0x9f, 0x19, 0xf4, 0x49, 0x5b, 0x3a, 0xa1, 0xfb, 0xaf, 0xd2, 0xa5, 0x13, 0x95, 0x9d, 0xc7,
0x0b, 0x54, 0x4d, 0x72, 0x0d, 0xd6, 0x45, 0x8f, 0x56, 0xd8, 0x3e, 0x2e, 0x2d, 0x22, 0xae, 0xd1,
0xc3, 0xaa, 0x99, 0xa5, 0x0e, 0xd7, 0xc8, 0x2c, 0x8c, 0x93, 0xbf, 0x07, 0x30, 0xab, 0x72, 0x54,
0x93, 0xc5, 0x3a, 0x4e, 0x75, 0xd3, 0x3d, 0xd1, 0xb1, 0x84, 0x5f, 0x99, 0xa7, 0xda, 0x15, 0x85,
0x62, 0xbb, 0x9f, 0x75, 0x2f, 0xdf, 0x3c, 0x4c, 0xb6, 0xbf, 0x7b, 0xad, 0x6c, 0xc7, 0x30, 0xb2,
0xef, 0x5a, 0xfd, 0x4a, 0xb4, 0x62, 0xd5, 0x31, 0x51, 0xce, 0x27, 0xd5, 0x3c, 0x89, 0x23, 0x9d,
0xb3, 0x27, 0x2c, 0x9a, 0x0f, 0xf4, 0xf3, 0xfd, 0xee, 0x3f, 0x01, 0x00, 0x00, 0xff, 0xff, 0xaa,
0x43, 0x90, 0xf1, 0xd0, 0x0b, 0x00, 0x00,
}

View File

@ -88,7 +88,6 @@ message Layout {
string Measurement = 3; // Measurement is the descriptive name of the time series data.
repeated Cell Cells = 4; // Cells are the individual visualization elements.
bool Autoflow = 5; // Autoflow indicates whether the frontend should layout the cells automatically.
string Organization = 6; // Organization is the organization ID that resource belongs to
}
message Cell {

View File

@ -2,7 +2,6 @@ package bolt
import (
"context"
"fmt"
"github.com/boltdb/bolt"
"github.com/influxdata/chronograf"
@ -22,27 +21,6 @@ type LayoutsStore struct {
}
func (s *LayoutsStore) Migrate(ctx context.Context) error {
layouts, err := s.All(ctx)
if err != nil {
return err
}
defaultOrg, err := s.client.OrganizationsStore.DefaultOrganization(ctx)
if err != nil {
return err
}
defaultOrgID := fmt.Sprintf("%d", defaultOrg.ID)
for _, layout := range layouts {
if layout.Organization == "" {
layout.Organization = defaultOrgID
if err := s.Update(ctx, layout); err != nil {
return nil
}
}
}
return nil
}

View File

@ -168,17 +168,6 @@ func (s *OrganizationsStore) Delete(ctx context.Context, o *chronograf.Organizat
}
}
layoutsStore := organizations.NewLayoutsStore(s.client.LayoutsStore, org)
layouts, err := layoutsStore.All(ctx)
if err != nil {
return err
}
for _, layout := range layouts {
if err := layoutsStore.Delete(ctx, layout); err != nil {
return err
}
}
dashboardsStore := organizations.NewDashboardsStore(s.client.DashboardsStore, org)
dashboards, err := dashboardsStore.All(ctx)
if err != nil {

View File

@ -526,12 +526,11 @@ type Cell struct {
// Layout is a collection of Cells for visualization
type Layout struct {
ID string `json:"id"`
Application string `json:"app"`
Measurement string `json:"measurement"`
Autoflow bool `json:"autoflow"`
Cells []Cell `json:"cells"`
Organization string `json:"organization"` // Organization is the organization ID that resource belongs to
ID string `json:"id"`
Application string `json:"app"`
Measurement string `json:"measurement"`
Autoflow bool `json:"autoflow"`
Cells []Cell `json:"cells"`
}
// LayoutsStore stores dashboards and associated Cells

296
integrations/server_test.go Normal file
View File

@ -0,0 +1,296 @@
package integrations
// This was intentionally added under the integrations package and not the integrations test package
// so that changes in other parts of the code base that may have an effect on these test will not
// compile until they are fixed.
import (
"bytes"
"context"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"testing"
"time"
"github.com/influxdata/chronograf"
"github.com/influxdata/chronograf/bolt"
"github.com/influxdata/chronograf/oauth2"
"github.com/influxdata/chronograf/server"
)
func TestServer(t *testing.T) {
type fields struct {
Organizations []chronograf.Organization
Users []chronograf.User
Sources []chronograf.Source
Servers []chronograf.Server
Layouts []chronograf.Layout
Dashboards []chronograf.Dashboard
}
type args struct {
server *server.Server
method string
path string
payload interface{} // Expects this to be a json serializable struct
principal oauth2.Principal
}
type wants struct {
statusCode int
contentType string
body string
}
tests := []struct {
name string
subName string
fields fields
args args
wants wants
}{
{
name: "GET /users",
subName: "User Not Found in the Default Organization",
fields: fields{
Users: []chronograf.User{},
},
args: args{
server: &server.Server{
GithubClientID: "not empty",
GithubClientSecret: "not empty",
},
method: "GET",
path: "/chronograf/v1/users",
principal: oauth2.Principal{
Organization: "0",
Subject: "billibob",
Issuer: "github",
},
},
wants: wants{
statusCode: 401,
body: `{"code":401,"message":"User is not authorized"}`,
},
},
{
name: "GET /users",
subName: "Single User in the Default Organization as SuperAdmin",
fields: fields{
Users: []chronograf.User{
{
ID: 1, // This is artificial, but should be reflective of the users actual ID
Name: "billibob",
Provider: "github",
Scheme: "oauth2",
SuperAdmin: true,
Roles: []chronograf.Role{
{
Name: "admin",
Organization: "0",
},
},
},
},
},
args: args{
server: &server.Server{
GithubClientID: "not empty",
GithubClientSecret: "not empty",
},
method: "GET",
path: "/chronograf/v1/users",
principal: oauth2.Principal{
Organization: "0",
Subject: "billibob",
Issuer: "github",
},
},
wants: wants{
statusCode: 200,
body: `
{
"links": {
"self": "/chronograf/v1/users"
},
"users": [
{
"links": {
"self": "/chronograf/v1/users/1"
},
"id": "1",
"name": "billibob",
"provider": "github",
"scheme": "oauth2",
"superAdmin": true,
"roles": [
{
"name": "admin",
"organization": "0"
}
]
}
]
}`,
},
},
}
for _, tt := range tests {
testName := fmt.Sprintf("%s: %s", tt.name, tt.subName)
t.Run(testName, func(t *testing.T) {
ctx := context.TODO()
// Create Test Server
host, port := hostAndPort()
tt.args.server.Host = host
tt.args.server.Port = port
// This is so that we can use staticly generate jwts
tt.args.server.TokenSecret = "secret"
boltFile := newBoltFile()
tt.args.server.BoltPath = boltFile
// Prepopulate BoltDB Database for Server
boltdb := bolt.NewClient()
boltdb.Path = boltFile
_ = boltdb.Open(ctx)
// Populate Organizations
for i, organization := range tt.fields.Organizations {
o, err := boltdb.OrganizationsStore.Add(ctx, &organization)
if err != nil {
t.Fatalf("failed to add organization: %v", err)
return
}
tt.fields.Organizations[i] = *o
}
// Populate Users
for i, user := range tt.fields.Users {
u, err := boltdb.UsersStore.Add(ctx, &user)
if err != nil {
t.Fatalf("failed to add user: %v", err)
return
}
tt.fields.Users[i] = *u
}
// Populate Sources
for i, source := range tt.fields.Sources {
s, err := boltdb.SourcesStore.Add(ctx, source)
if err != nil {
t.Fatalf("failed to add source: %v", err)
return
}
tt.fields.Sources[i] = s
}
// Populate Servers
for i, server := range tt.fields.Servers {
s, err := boltdb.ServersStore.Add(ctx, server)
if err != nil {
t.Fatalf("failed to add server: %v", err)
return
}
tt.fields.Servers[i] = s
}
// Populate Layouts
for i, layout := range tt.fields.Layouts {
l, err := boltdb.LayoutsStore.Add(ctx, layout)
if err != nil {
t.Fatalf("failed to add layout: %v", err)
return
}
tt.fields.Layouts[i] = l
}
// Populate Dashboards
for i, dashboard := range tt.fields.Dashboards {
d, err := boltdb.DashboardsStore.Add(ctx, dashboard)
if err != nil {
t.Fatalf("failed to add dashboard: %v", err)
return
}
tt.fields.Dashboards[i] = d
}
_ = boltdb.Close()
go tt.args.server.Serve(ctx)
serverURL := fmt.Sprintf("http://%v:%v%v", host, port, tt.args.path)
// Wait for the server to come online
timeout := time.Now().Add(100 * time.Millisecond)
for {
_, err := http.Get(serverURL + "/swagger.json")
if err == nil {
break
}
if time.Now().After(timeout) {
t.Fatalf("failed to start server")
return
}
}
// Set the Expiry time on the principal
tt.args.principal.IssuedAt = time.Now()
tt.args.principal.ExpiresAt = time.Now().Add(10 * time.Second)
// Construct HTTP Request
buf, _ := json.Marshal(tt.args.payload)
reqBody := ioutil.NopCloser(bytes.NewReader(buf))
req, _ := http.NewRequest(tt.args.method, serverURL, reqBody)
token, _ := oauth2.NewJWT(tt.args.server.TokenSecret).Create(ctx, tt.args.principal)
req.AddCookie(&http.Cookie{
Name: "session",
Value: string(token),
HttpOnly: true,
Path: "/",
})
// Make actual http request
resp, err := http.DefaultClient.Do(req)
if err != nil {
t.Fatalf("failed to make httprequest: %v", err)
return
}
content := resp.Header.Get("Content-Type")
body, _ := ioutil.ReadAll(resp.Body)
if resp.StatusCode != tt.wants.statusCode {
t.Errorf(
"%s %s Status Code = %v, want %v",
tt.args.method,
tt.args.path,
resp.StatusCode,
tt.wants.statusCode,
)
}
if tt.wants.contentType != "" && content != tt.wants.contentType {
t.Errorf(
"%s %s Content Type = %v, want %v",
tt.args.method,
tt.args.path,
content,
tt.wants.contentType,
)
}
if eq, err := jsonEqual(tt.wants.body, string(body)); err != nil || !eq {
t.Errorf(
"%s %s Body = %v, want %v",
tt.args.method,
tt.args.path,
string(body),
tt.wants.body,
)
}
tt.args.server.Listener.Close()
})
}
}

54
integrations/utils.go Normal file
View File

@ -0,0 +1,54 @@
package integrations
import (
"encoding/json"
"io/ioutil"
"net/http/httptest"
"net/url"
"strconv"
"strings"
"github.com/google/go-cmp/cmp"
)
func hostAndPort() (string, int) {
s := httptest.NewServer(nil)
defer s.Close()
u, err := url.Parse(s.URL)
if err != nil {
panic(err)
}
xs := strings.Split(u.Host, ":")
host := xs[0]
portStr := xs[1]
port, err := strconv.Atoi(portStr)
if err != nil {
panic(err)
}
return host, port
}
func newBoltFile() string {
f, err := ioutil.TempFile("", "chronograf-bolt-")
if err != nil {
panic(err)
}
f.Close()
return f.Name()
}
func jsonEqual(s1, s2 string) (eq bool, err error) {
var o1, o2 interface{}
if err = json.Unmarshal([]byte(s1), &o1); err != nil {
return
}
if err = json.Unmarshal([]byte(s2), &o2); err != nil {
return
}
return cmp.Equal(o1, o2), nil
}

View File

@ -1,112 +0,0 @@
package organizations
import (
"context"
"github.com/influxdata/chronograf"
)
// ensure that LayoutsStore implements chronograf.LayoutStore
var _ chronograf.LayoutsStore = &LayoutsStore{}
// LayoutsStore facade on a LayoutStore that filters layouts
// by organization.
type LayoutsStore struct {
store chronograf.LayoutsStore
organization string
}
// NewLayoutsStore creates a new LayoutsStore from an existing
// chronograf.LayoutStore and an organization string
func NewLayoutsStore(s chronograf.LayoutsStore, org string) *LayoutsStore {
return &LayoutsStore{
store: s,
organization: org,
}
}
// All retrieves all layouts from the underlying LayoutStore and filters them
// by organization.
func (s *LayoutsStore) All(ctx context.Context) ([]chronograf.Layout, error) {
err := validOrganization(ctx)
if err != nil {
return nil, err
}
ds, err := s.store.All(ctx)
if err != nil {
return nil, err
}
// This filters layouts without allocating
// https://github.com/golang/go/wiki/SliceTricks#filtering-without-allocating
layouts := ds[:0]
for _, d := range ds {
if d.Organization == s.organization {
layouts = append(layouts, d)
}
}
return layouts, nil
}
// Add creates a new Layout in the LayoutsStore with layout.Organization set to be the
// organization from the layout store.
func (s *LayoutsStore) Add(ctx context.Context, d chronograf.Layout) (chronograf.Layout, error) {
err := validOrganization(ctx)
if err != nil {
return chronograf.Layout{}, err
}
d.Organization = s.organization
return s.store.Add(ctx, d)
}
// Delete the layout from LayoutsStore
func (s *LayoutsStore) Delete(ctx context.Context, d chronograf.Layout) error {
err := validOrganization(ctx)
if err != nil {
return err
}
d, err = s.store.Get(ctx, d.ID)
if err != nil {
return err
}
return s.store.Delete(ctx, d)
}
// Get returns a Layout if the id exists and belongs to the organization that is set.
func (s *LayoutsStore) Get(ctx context.Context, id string) (chronograf.Layout, error) {
err := validOrganization(ctx)
if err != nil {
return chronograf.Layout{}, err
}
d, err := s.store.Get(ctx, id)
if err != nil {
return chronograf.Layout{}, err
}
if d.Organization != s.organization {
return chronograf.Layout{}, chronograf.ErrLayoutNotFound
}
return d, nil
}
// Update the layout in LayoutsStore.
func (s *LayoutsStore) Update(ctx context.Context, d chronograf.Layout) error {
err := validOrganization(ctx)
if err != nil {
return err
}
_, err = s.store.Get(ctx, d.ID)
if err != nil {
return err
}
return s.store.Update(ctx, d)
}

View File

@ -1,339 +0,0 @@
package organizations_test
import (
"context"
"fmt"
"testing"
"github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts"
"github.com/influxdata/chronograf"
"github.com/influxdata/chronograf/mocks"
"github.com/influxdata/chronograf/organizations"
)
// IgnoreFields is used because ID cannot be predicted reliably
// EquateEmpty is used because we want nil slices, arrays, and maps to be equal to the empty map
var layoutCmpOptions = cmp.Options{
cmpopts.EquateEmpty(),
cmpopts.IgnoreFields(chronograf.Layout{}, "ID"),
}
func TestLayouts_All(t *testing.T) {
type fields struct {
LayoutsStore chronograf.LayoutsStore
}
type args struct {
organization string
ctx context.Context
}
tests := []struct {
name string
args args
fields fields
want []chronograf.Layout
wantRaw []chronograf.Layout
wantErr bool
}{
{
name: "No Layouts",
fields: fields{
LayoutsStore: &mocks.LayoutsStore{
AllF: func(ctx context.Context) ([]chronograf.Layout, error) {
return nil, fmt.Errorf("No Layouts")
},
},
},
wantErr: true,
},
{
name: "All Layouts",
fields: fields{
LayoutsStore: &mocks.LayoutsStore{
AllF: func(ctx context.Context) ([]chronograf.Layout, error) {
return []chronograf.Layout{
{
Application: "howdy",
Organization: "1337",
},
{
Application: "doody",
Organization: "1338",
},
}, nil
},
},
},
args: args{
organization: "1337",
ctx: context.Background(),
},
want: []chronograf.Layout{
{
Application: "howdy",
Organization: "1337",
},
},
},
}
for _, tt := range tests {
s := organizations.NewLayoutsStore(tt.fields.LayoutsStore, tt.args.organization)
tt.args.ctx = context.WithValue(tt.args.ctx, organizations.ContextKey, tt.args.organization)
gots, err := s.All(tt.args.ctx)
if (err != nil) != tt.wantErr {
t.Errorf("%q. LayoutsStore.All() error = %v, wantErr %v", tt.name, err, tt.wantErr)
continue
}
for i, got := range gots {
if diff := cmp.Diff(got, tt.want[i], layoutCmpOptions...); diff != "" {
t.Errorf("%q. LayoutsStore.All():\n-got/+want\ndiff %s", tt.name, diff)
}
}
}
}
func TestLayouts_Add(t *testing.T) {
type fields struct {
LayoutsStore chronograf.LayoutsStore
}
type args struct {
organization string
ctx context.Context
layout chronograf.Layout
}
tests := []struct {
name string
args args
fields fields
want chronograf.Layout
wantErr bool
}{
{
name: "Add Layout",
fields: fields{
LayoutsStore: &mocks.LayoutsStore{
AddF: func(ctx context.Context, s chronograf.Layout) (chronograf.Layout, error) {
return s, nil
},
GetF: func(ctx context.Context, id string) (chronograf.Layout, error) {
return chronograf.Layout{
ID: "1229",
Application: "howdy",
Organization: "1337",
}, nil
},
},
},
args: args{
organization: "1337",
ctx: context.Background(),
layout: chronograf.Layout{
ID: "1229",
Application: "howdy",
},
},
want: chronograf.Layout{
Application: "howdy",
Organization: "1337",
},
},
}
for _, tt := range tests {
s := organizations.NewLayoutsStore(tt.fields.LayoutsStore, tt.args.organization)
tt.args.ctx = context.WithValue(tt.args.ctx, organizations.ContextKey, tt.args.organization)
d, err := s.Add(tt.args.ctx, tt.args.layout)
if (err != nil) != tt.wantErr {
t.Errorf("%q. LayoutsStore.Add() error = %v, wantErr %v", tt.name, err, tt.wantErr)
continue
}
got, err := s.Get(tt.args.ctx, d.ID)
if diff := cmp.Diff(got, tt.want, layoutCmpOptions...); diff != "" {
t.Errorf("%q. LayoutsStore.Add():\n-got/+want\ndiff %s", tt.name, diff)
}
}
}
func TestLayouts_Delete(t *testing.T) {
type fields struct {
LayoutsStore chronograf.LayoutsStore
}
type args struct {
organization string
ctx context.Context
layout chronograf.Layout
}
tests := []struct {
name string
fields fields
args args
want []chronograf.Layout
addFirst bool
wantErr bool
}{
{
name: "Delete layout",
fields: fields{
LayoutsStore: &mocks.LayoutsStore{
DeleteF: func(ctx context.Context, s chronograf.Layout) error {
return nil
},
GetF: func(ctx context.Context, id string) (chronograf.Layout, error) {
return chronograf.Layout{
ID: "1229",
Application: "howdy",
Organization: "1337",
}, nil
},
},
},
args: args{
organization: "1337",
ctx: context.Background(),
layout: chronograf.Layout{
ID: "1229",
Application: "howdy",
Organization: "1337",
},
},
addFirst: true,
},
}
for _, tt := range tests {
s := organizations.NewLayoutsStore(tt.fields.LayoutsStore, tt.args.organization)
tt.args.ctx = context.WithValue(tt.args.ctx, organizations.ContextKey, tt.args.organization)
err := s.Delete(tt.args.ctx, tt.args.layout)
if (err != nil) != tt.wantErr {
t.Errorf("%q. LayoutsStore.All() error = %v, wantErr %v", tt.name, err, tt.wantErr)
continue
}
}
}
func TestLayouts_Get(t *testing.T) {
type fields struct {
LayoutsStore chronograf.LayoutsStore
}
type args struct {
organization string
ctx context.Context
layout chronograf.Layout
}
tests := []struct {
name string
fields fields
args args
want chronograf.Layout
addFirst bool
wantErr bool
}{
{
name: "Get Layout",
fields: fields{
LayoutsStore: &mocks.LayoutsStore{
GetF: func(ctx context.Context, id string) (chronograf.Layout, error) {
return chronograf.Layout{
ID: "1229",
Application: "howdy",
Organization: "1337",
}, nil
},
},
},
args: args{
organization: "1337",
ctx: context.Background(),
layout: chronograf.Layout{
ID: "1229",
Application: "howdy",
Organization: "1337",
},
},
want: chronograf.Layout{
ID: "1229",
Application: "howdy",
Organization: "1337",
},
},
}
for _, tt := range tests {
s := organizations.NewLayoutsStore(tt.fields.LayoutsStore, tt.args.organization)
tt.args.ctx = context.WithValue(tt.args.ctx, organizations.ContextKey, tt.args.organization)
got, err := s.Get(tt.args.ctx, tt.args.layout.ID)
if (err != nil) != tt.wantErr {
t.Errorf("%q. LayoutsStore.Get() error = %v, wantErr %v", tt.name, err, tt.wantErr)
continue
}
if diff := cmp.Diff(got, tt.want, layoutCmpOptions...); diff != "" {
t.Errorf("%q. LayoutsStore.Get():\n-got/+want\ndiff %s", tt.name, diff)
}
}
}
func TestLayouts_Update(t *testing.T) {
type fields struct {
LayoutsStore chronograf.LayoutsStore
}
type args struct {
organization string
ctx context.Context
layout chronograf.Layout
name string
}
tests := []struct {
name string
fields fields
args args
want chronograf.Layout
addFirst bool
wantErr bool
}{
{
name: "Update Layout Application",
fields: fields{
LayoutsStore: &mocks.LayoutsStore{
UpdateF: func(ctx context.Context, s chronograf.Layout) error {
return nil
},
GetF: func(ctx context.Context, id string) (chronograf.Layout, error) {
return chronograf.Layout{
ID: "1229",
Application: "doody",
Organization: "1337",
}, nil
},
},
},
args: args{
organization: "1337",
ctx: context.Background(),
layout: chronograf.Layout{
ID: "1229",
Application: "howdy",
Organization: "1337",
},
name: "doody",
},
want: chronograf.Layout{
Application: "doody",
Organization: "1337",
},
addFirst: true,
},
}
for _, tt := range tests {
if tt.args.name != "" {
tt.args.layout.Application = tt.args.name
}
s := organizations.NewLayoutsStore(tt.fields.LayoutsStore, tt.args.organization)
tt.args.ctx = context.WithValue(tt.args.ctx, organizations.ContextKey, tt.args.organization)
err := s.Update(tt.args.ctx, tt.args.layout)
if (err != nil) != tt.wantErr {
t.Errorf("%q. LayoutsStore.Update() error = %v, wantErr %v", tt.name, err, tt.wantErr)
continue
}
got, err := s.Get(tt.args.ctx, tt.args.layout.ID)
if diff := cmp.Diff(got, tt.want, layoutCmpOptions...); diff != "" {
t.Errorf("%q. LayoutsStore.Update():\n-got/+want\ndiff %s", tt.name, diff)
}
}
}

View File

@ -1,7 +1,6 @@
package server
import (
"encoding/json"
"fmt"
"net/http"
@ -49,38 +48,6 @@ func newLayoutResponse(layout chronograf.Layout) layoutResponse {
}
}
// NewLayout adds a valid layout to store.
func (s *Service) NewLayout(w http.ResponseWriter, r *http.Request) {
var layout chronograf.Layout
var err error
if err := json.NewDecoder(r.Body).Decode(&layout); err != nil {
invalidJSON(w, s.Logger)
return
}
ctx := r.Context()
defaultOrg, err := s.Store.Organizations(ctx).DefaultOrganization(ctx)
if err != nil {
unknownErrorWithMessage(w, err, s.Logger)
return
}
if err := ValidLayoutRequest(layout, fmt.Sprintf("%d", defaultOrg.ID)); err != nil {
invalidData(w, err, s.Logger)
return
}
if layout, err = s.Store.Layouts(ctx).Add(r.Context(), layout); err != nil {
msg := fmt.Errorf("Error storing layout %v: %v", layout, err)
unknownErrorWithMessage(w, msg, s.Logger)
return
}
res := newLayoutResponse(layout)
location(w, res.Link.Href)
encodeJSON(w, http.StatusCreated, res, s.Logger)
}
type getLayoutsResponse struct {
Layouts []layoutResponse `json:"layouts"`
}
@ -117,7 +84,14 @@ func (s *Service) Layouts(w http.ResponseWriter, r *http.Request) {
res := getLayoutsResponse{
Layouts: []layoutResponse{},
}
seen := make(map[string]bool)
for _, layout := range layouts {
// remove duplicates
if seen[layout.Measurement+layout.ID] {
continue
}
// filter for data that belongs to provided application or measurement
if filter(&layout) {
res.Layouts = append(res.Layouts, newLayoutResponse(layout))
}
@ -139,82 +113,3 @@ func (s *Service) LayoutsID(w http.ResponseWriter, r *http.Request) {
res := newLayoutResponse(layout)
encodeJSON(w, http.StatusOK, res, s.Logger)
}
// RemoveLayout deletes layout from store.
func (s *Service) RemoveLayout(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
id := httprouter.GetParamFromContext(ctx, "id")
layout := chronograf.Layout{
ID: id,
}
if err := s.Store.Layouts(ctx).Delete(ctx, layout); err != nil {
unknownErrorWithMessage(w, err, s.Logger)
return
}
w.WriteHeader(http.StatusNoContent)
}
// UpdateLayout replaces the layout of ID with new valid layout.
func (s *Service) UpdateLayout(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
id := httprouter.GetParamFromContext(ctx, "id")
_, err := s.Store.Layouts(ctx).Get(ctx, id)
if err != nil {
Error(w, http.StatusNotFound, fmt.Sprintf("ID %s not found", id), s.Logger)
return
}
var req chronograf.Layout
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
invalidJSON(w, s.Logger)
return
}
req.ID = id
defaultOrg, err := s.Store.Organizations(ctx).DefaultOrganization(ctx)
if err != nil {
unknownErrorWithMessage(w, err, s.Logger)
return
}
if err := ValidLayoutRequest(req, fmt.Sprintf("%d", defaultOrg.ID)); err != nil {
invalidData(w, err, s.Logger)
return
}
if err := s.Store.Layouts(ctx).Update(ctx, req); err != nil {
msg := fmt.Sprintf("Error updating layout ID %s: %v", id, err)
Error(w, http.StatusInternalServerError, msg, s.Logger)
return
}
res := newLayoutResponse(req)
encodeJSON(w, http.StatusOK, res, s.Logger)
}
// ValidLayoutRequest checks if the layout has valid application, measurement and cells.
func ValidLayoutRequest(l chronograf.Layout, defaultOrgID string) error {
if l.Application == "" || l.Measurement == "" || len(l.Cells) == 0 {
return fmt.Errorf("app, measurement, and cells required")
}
if l.Organization == "" {
l.Organization = defaultOrgID
}
for _, c := range l.Cells {
if c.W == 0 || c.H == 0 {
return fmt.Errorf("w, and h required")
}
for _, q := range c.Queries {
if q.Command == "" {
return fmt.Errorf("query required")
}
}
}
return nil
}

View File

@ -1,38 +0,0 @@
package server
import "net/http"
type getMappingsResponse struct {
Mappings []mapping `json:"mappings"`
}
type mapping struct {
Measurement string `json:"measurement"` // The measurement where data for this mapping is found
Name string `json:"name"` // The application name which will be assigned to the corresponding measurement
}
// GetMappings returns the known mappings of measurements to applications
func (s *Service) GetMappings(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
layouts, err := s.Store.Layouts(ctx).All(ctx)
if err != nil {
Error(w, http.StatusInternalServerError, "Error loading layouts", s.Logger)
return
}
mp := getMappingsResponse{
Mappings: []mapping{},
}
seen := make(map[string]bool)
for _, layout := range layouts {
if seen[layout.Measurement+layout.ID] {
continue
}
mp.Mappings = append(mp.Mappings, mapping{layout.Measurement, layout.Application})
seen[layout.Measurement+layout.ID] = true
}
encodeJSON(w, http.StatusOK, mp, s.Logger)
}

View File

@ -183,16 +183,9 @@ func NewMux(opts MuxOpts, service Service) http.Handler {
router.PATCH("/chronograf/v1/sources/:id/kapacitors/:kid/proxy", EnsureEditor(service.KapacitorProxyPatch))
router.DELETE("/chronograf/v1/sources/:id/kapacitors/:kid/proxy", EnsureEditor(service.KapacitorProxyDelete))
// Mappings
router.GET("/chronograf/v1/mappings", EnsureViewer(service.GetMappings))
// Layouts
router.GET("/chronograf/v1/layouts", EnsureViewer(service.Layouts))
router.POST("/chronograf/v1/layouts", EnsureEditor(service.NewLayout))
router.GET("/chronograf/v1/layouts/:id", EnsureViewer(service.LayoutsID))
router.PUT("/chronograf/v1/layouts/:id", EnsureEditor(service.UpdateLayout))
router.DELETE("/chronograf/v1/layouts/:id", EnsureEditor(service.RemoveLayout))
// Users associated with Chronograf
router.GET("/chronograf/v1/me", service.Me)

View File

@ -130,17 +130,9 @@ func (s *Store) Servers(ctx context.Context) chronograf.ServersStore {
return &noop.ServersStore{}
}
// Layouts returns a noop.LayoutsStore if the context has no organization specified
// and a organization.LayoutsStore otherwise.
// Layouts returns all layouts in the underlying layouts store.
func (s *Store) Layouts(ctx context.Context) chronograf.LayoutsStore {
if isServer := hasServerContext(ctx); isServer {
return s.LayoutsStore
}
if org, ok := hasOrganizationContext(ctx); ok {
return organizations.NewLayoutsStore(s.LayoutsStore, org)
}
return &noop.LayoutsStore{}
return s.LayoutsStore
}
// Users returns a chronograf.UsersStore.

View File

@ -110,18 +110,17 @@ export async function getAllHosts(proxyLink, telegrafDB) {
}
}
export function getMappings() {
return AJAX({
export const getLayouts = () =>
AJAX({
method: 'GET',
resource: 'mappings',
resource: 'layouts',
})
}
export function getAppsForHosts(proxyLink, hosts, appMappings, telegrafDB) {
const measurements = appMappings.map(m => `^${m.measurement}$`).join('|')
export function getAppsForHosts(proxyLink, hosts, appLayouts, telegrafDB) {
const measurements = appLayouts.map(m => `^${m.measurement}$`).join('|')
const measurementsToApps = _.zipObject(
appMappings.map(m => m.measurement),
appMappings.map(m => m.name)
appLayouts.map(m => m.measurement),
appLayouts.map(({app}) => app)
)
return proxy({

View File

@ -13,12 +13,11 @@ import ManualRefresh from 'src/shared/components/ManualRefresh'
import timeRanges from 'hson!shared/data/timeRanges.hson'
import {
getMappings,
getLayouts,
getAppsForHosts,
getMeasurementsForHost,
getAllHosts,
} from 'src/hosts/apis'
import {fetchLayouts} from 'shared/apis'
import {setAutoRefresh} from 'shared/actions/app'
import {presentationButtonDispatcher} from 'shared/dispatchers'
@ -38,13 +37,12 @@ class HostPage extends Component {
const {source, params, location} = this.props
// fetching layouts and mappings can be done at the same time
const {data: {layouts}} = await fetchLayouts()
const {data: {mappings}} = await getMappings()
const {data: {layouts}} = await getLayouts()
const hosts = await getAllHosts(source.links.proxy, source.telegraf)
const newHosts = await getAppsForHosts(
source.links.proxy,
hosts,
mappings,
layouts,
source.telegraf
)

View File

@ -5,7 +5,7 @@ import HostsTable from 'src/hosts/components/HostsTable'
import FancyScrollbar from 'shared/components/FancyScrollbar'
import SourceIndicator from 'shared/components/SourceIndicator'
import {getCpuAndLoadForHosts, getMappings, getAppsForHosts} from '../apis'
import {getCpuAndLoadForHosts, getLayouts, getAppsForHosts} from '../apis'
class HostsPage extends Component {
constructor(props) {
@ -22,18 +22,18 @@ class HostsPage extends Component {
const {source, addFlashMessage} = this.props
Promise.all([
getCpuAndLoadForHosts(source.links.proxy, source.telegraf),
getMappings(),
getLayouts(),
new Promise(resolve => {
this.setState({hostsLoading: true})
resolve()
}),
])
.then(([hosts, {data: {mappings}}]) => {
.then(([hosts, {data: {layouts}}]) => {
this.setState({
hosts,
hostsLoading: false,
})
getAppsForHosts(source.links.proxy, hosts, mappings, source.telegraf)
getAppsForHosts(source.links.proxy, hosts, layouts, source.telegraf)
.then(newHosts => {
this.setState({
hosts: newHosts,

View File

@ -1,13 +1,5 @@
import AJAX from 'utils/ajax'
export function fetchLayouts() {
return AJAX({
url: '/chronograf/v1/layouts',
method: 'GET',
resource: 'layouts',
})
}
export function getSources() {
return AJAX({
resource: 'sources',

View File

@ -49,8 +49,15 @@
}
> .btn {
margin-top: ($sidebar--width / 2);
margin-bottom: $sidebar--width;
margin-top: 6px;
margin-bottom: 6px;
&:first-of-type {
margin-top: ($sidebar--width / 2);
}
&:last-child {
margin-bottom: $sidebar--width;
}
> .icon {
font-size: 20px;