Merge pull request #2593 from influxdata/feature/dashboard-filestore

Add filesystem backed dashboard and organization stores
pull/10616/head
Nathan Haugo 2017-12-19 14:36:19 -08:00 committed by GitHub
commit ce45f13cdc
59 changed files with 2904 additions and 700 deletions

View File

@ -1,9 +1,12 @@
## v1.4.0.0-beta2 [unreleased]
### Features
1. [#2593](https://github.com/influxdata/chronograf/pull/2593): Add option to use files for dashboards, organizations, data sources, and kapacitors
### UI Improvements
1. [#2502](https://github.com/influxdata/chronograf/pull/2502): Fix cursor flashing between default and pointer
### Bug Fixes
1. [#2528](https://github.com/influxdata/chronograf/pull/2528): Fix template rendering to ignore template if not in query
1. [#2528](https://github.com/influxdata/chronograf/pull/2528): Fix template rendering to ignore template if not in query
1. [#2563](https://github.com/influxdata/chronograf/pull/2563): Fix graph inversion if user input y-axis min greater than max
1. [#2616](https://github.com/influxdata/chronograf/pull/2616): Fix cell editing so query data choices are kept when updating a cell

View File

@ -93,10 +93,10 @@ internal.pb.go: bolt/internal/internal.proto
test: jstest gotest gotestrace
gotest:
go test `go list ./... | grep -v /vendor/`
go test ./...
gotestrace:
go test -race `go list ./... | grep -v /vendor/`
go test -race ./...
jstest:
cd ui && yarn test

View File

@ -10,7 +10,7 @@ import (
"github.com/boltdb/bolt"
"github.com/influxdata/chronograf"
"github.com/influxdata/chronograf/uuid"
"github.com/influxdata/chronograf/id"
)
const (
@ -51,11 +51,11 @@ func NewClient() *Client {
c.ServersStore = &ServersStore{client: c}
c.LayoutsStore = &LayoutsStore{
client: c,
IDs: &uuid.V4{},
IDs: &id.UUID{},
}
c.DashboardsStore = &DashboardsStore{
client: c,
IDs: &uuid.V4{},
IDs: &id.UUID{},
}
c.UsersStore = &UsersStore{client: c}
c.OrganizationsStore = &OrganizationsStore{client: c}

View File

@ -2,7 +2,6 @@ package bolt
import (
"context"
"fmt"
"strconv"
"github.com/boltdb/bolt"
@ -64,11 +63,9 @@ func (d *DashboardsStore) Migrate(ctx context.Context) error {
return err
}
defaultOrgID := fmt.Sprintf("%d", defaultOrg.ID)
for _, board := range boards {
if board.Organization == "" {
board.Organization = defaultOrgID
board.Organization = defaultOrg.ID
if err := d.Update(ctx, board); err != nil {
return nil
}

View File

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

View File

@ -158,7 +158,7 @@ message Role {
}
message Organization {
uint64 ID = 1; // ID is the unique ID of the organization
string ID = 1; // ID is the unique ID of the organization
string Name = 2; // Name is the organization's name
string DefaultRole = 3; // DefaultRole is the name of the role that is the default for any users added to the organization
bool Public = 4; // Public specifies that users must be explicitly added to the organization

View File

@ -13,12 +13,14 @@ import (
// Ensure OrganizationsStore implements chronograf.OrganizationsStore.
var _ chronograf.OrganizationsStore = &OrganizationsStore{}
// OrganizationsBucket is the bucket where organizations are stored.
var OrganizationsBucket = []byte("OrganizationsV1")
var (
// OrganizationsBucket is the bucket where organizations are stored.
OrganizationsBucket = []byte("OrganizationsV1")
// DefaultOrganizationID is the ID of the default organization.
DefaultOrganizationID = []byte("default")
)
const (
// DefaultOrganizationID is the ID of the default organization.
DefaultOrganizationID uint64 = 0
// DefaultOrganizationName is the Name of the default organization
DefaultOrganizationName string = "Default"
// DefaultOrganizationRole is the DefaultRole for the Default organization
@ -40,20 +42,20 @@ func (s *OrganizationsStore) Migrate(ctx context.Context) error {
// CreateDefault does a findOrCreate on the default organization
func (s *OrganizationsStore) CreateDefault(ctx context.Context) error {
o := chronograf.Organization{
ID: DefaultOrganizationID,
ID: string(DefaultOrganizationID),
Name: DefaultOrganizationName,
DefaultRole: DefaultOrganizationRole,
Public: DefaultOrganizationPublic,
}
return s.client.db.Update(func(tx *bolt.Tx) error {
b := tx.Bucket(OrganizationsBucket)
v := b.Get(u64tob(o.ID))
v := b.Get(DefaultOrganizationID)
if v != nil {
return nil
}
if v, err := internal.MarshalOrganization(&o); err != nil {
return err
} else if err := b.Put(u64tob(o.ID), v); err != nil {
} else if err := b.Put(DefaultOrganizationID, v); err != nil {
return err
}
@ -75,7 +77,7 @@ func (s *OrganizationsStore) nameIsUnique(ctx context.Context, name string) bool
func (s *OrganizationsStore) DefaultOrganization(ctx context.Context) (*chronograf.Organization, error) {
var org chronograf.Organization
if err := s.client.db.View(func(tx *bolt.Tx) error {
v := tx.Bucket(OrganizationsBucket).Get(u64tob(DefaultOrganizationID))
v := tx.Bucket(OrganizationsBucket).Get(DefaultOrganizationID)
return internal.UnmarshalOrganization(v, &org)
}); err != nil {
return nil, err
@ -89,25 +91,23 @@ func (s *OrganizationsStore) Add(ctx context.Context, o *chronograf.Organization
if !s.nameIsUnique(ctx, o.Name) {
return nil, chronograf.ErrOrganizationAlreadyExists
}
if err := s.client.db.Update(func(tx *bolt.Tx) error {
err := s.client.db.Update(func(tx *bolt.Tx) error {
b := tx.Bucket(OrganizationsBucket)
seq, err := b.NextSequence()
if err != nil {
return err
}
o.ID = seq
if v, err := internal.MarshalOrganization(o); err != nil {
return err
} else if err := b.Put(u64tob(seq), v); err != nil {
o.ID = fmt.Sprintf("%d", seq)
v, err := internal.MarshalOrganization(o)
if err != nil {
return err
}
return nil
}); err != nil {
return nil, err
}
return b.Put([]byte(o.ID), v)
})
return o, nil
return o, err
}
// All returns all known organizations
@ -126,7 +126,7 @@ func (s *OrganizationsStore) All(ctx context.Context) ([]chronograf.Organization
// Delete the organization from OrganizationsStore
func (s *OrganizationsStore) Delete(ctx context.Context, o *chronograf.Organization) error {
if o.ID == DefaultOrganizationID {
if o.ID == string(DefaultOrganizationID) {
return chronograf.ErrCannotDeleteDefaultOrganization
}
_, err := s.get(ctx, o.ID)
@ -134,19 +134,18 @@ func (s *OrganizationsStore) Delete(ctx context.Context, o *chronograf.Organizat
return err
}
if err := s.client.db.Update(func(tx *bolt.Tx) error {
return tx.Bucket(OrganizationsBucket).Delete(u64tob(o.ID))
return tx.Bucket(OrganizationsBucket).Delete([]byte(o.ID))
}); err != nil {
return err
}
// Dependent Delete of all resources
org := fmt.Sprintf("%d", o.ID)
// Each of the associated organization stores expects organization to be
// set on the context.
ctx = context.WithValue(ctx, organizations.ContextKey, org)
ctx = context.WithValue(ctx, organizations.ContextKey, o.ID)
sourcesStore := organizations.NewSourcesStore(s.client.SourcesStore, org)
sourcesStore := organizations.NewSourcesStore(s.client.SourcesStore, o.ID)
sources, err := sourcesStore.All(ctx)
if err != nil {
return err
@ -157,7 +156,7 @@ func (s *OrganizationsStore) Delete(ctx context.Context, o *chronograf.Organizat
}
}
serversStore := organizations.NewServersStore(s.client.ServersStore, org)
serversStore := organizations.NewServersStore(s.client.ServersStore, o.ID)
servers, err := serversStore.All(ctx)
if err != nil {
return err
@ -168,7 +167,7 @@ func (s *OrganizationsStore) Delete(ctx context.Context, o *chronograf.Organizat
}
}
dashboardsStore := organizations.NewDashboardsStore(s.client.DashboardsStore, org)
dashboardsStore := organizations.NewDashboardsStore(s.client.DashboardsStore, o.ID)
dashboards, err := dashboardsStore.All(ctx)
if err != nil {
return err
@ -179,7 +178,7 @@ func (s *OrganizationsStore) Delete(ctx context.Context, o *chronograf.Organizat
}
}
usersStore := organizations.NewUsersStore(s.client.UsersStore, org)
usersStore := organizations.NewUsersStore(s.client.UsersStore, o.ID)
users, err := usersStore.All(ctx)
if err != nil {
return err
@ -193,10 +192,10 @@ func (s *OrganizationsStore) Delete(ctx context.Context, o *chronograf.Organizat
return nil
}
func (s *OrganizationsStore) get(ctx context.Context, id uint64) (*chronograf.Organization, error) {
func (s *OrganizationsStore) get(ctx context.Context, id string) (*chronograf.Organization, error) {
var o chronograf.Organization
err := s.client.db.View(func(tx *bolt.Tx) error {
v := tx.Bucket(OrganizationsBucket).Get(u64tob(id))
v := tx.Bucket(OrganizationsBucket).Get([]byte(id))
if v == nil {
return chronograf.ErrOrganizationNotFound
}
@ -221,7 +220,6 @@ func (s *OrganizationsStore) each(ctx context.Context, fn func(*chronograf.Organ
return nil
})
})
return nil
}
// Get returns a Organization if the id exists.
@ -270,7 +268,7 @@ func (s *OrganizationsStore) Update(ctx context.Context, o *chronograf.Organizat
return s.client.db.Update(func(tx *bolt.Tx) error {
if v, err := internal.MarshalOrganization(o); err != nil {
return err
} else if err := tx.Bucket(OrganizationsBucket).Put(u64tob(o.ID), v); err != nil {
} else if err := tx.Bucket(OrganizationsBucket).Put([]byte(o.ID), v); err != nil {
return err
}
return nil

View File

@ -99,7 +99,7 @@ func TestOrganizationsStore_GetWithID(t *testing.T) {
args: args{
ctx: context.Background(),
org: &chronograf.Organization{
ID: 1234,
ID: "1234",
},
},
wantErr: true,
@ -180,11 +180,6 @@ func TestOrganizationsStore_All(t *testing.T) {
},
},
want: []chronograf.Organization{
{
Name: bolt.DefaultOrganizationName,
DefaultRole: bolt.DefaultOrganizationRole,
Public: bolt.DefaultOrganizationPublic,
},
{
Name: "EE - Evil Empire",
DefaultRole: roles.MemberRoleName,
@ -195,6 +190,11 @@ func TestOrganizationsStore_All(t *testing.T) {
DefaultRole: roles.EditorRoleName,
Public: true,
},
{
Name: bolt.DefaultOrganizationName,
DefaultRole: bolt.DefaultOrganizationRole,
Public: bolt.DefaultOrganizationPublic,
},
},
addFirst: true,
},
@ -253,7 +253,7 @@ func TestOrganizationsStore_Update(t *testing.T) {
args: args{
ctx: context.Background(),
initial: &chronograf.Organization{
ID: 1234,
ID: "1234",
Name: "The Okay Place",
},
updates: &chronograf.Organization{},
@ -448,7 +448,7 @@ func TestOrganizationStore_Delete(t *testing.T) {
args: args{
ctx: context.Background(),
org: &chronograf.Organization{
ID: 10,
ID: "10",
},
},
wantErr: true,
@ -615,7 +615,7 @@ func TestOrganizationsStore_DefaultOrganization(t *testing.T) {
ctx: context.Background(),
},
want: &chronograf.Organization{
ID: bolt.DefaultOrganizationID,
ID: string(bolt.DefaultOrganizationID),
Name: bolt.DefaultOrganizationName,
DefaultRole: bolt.DefaultOrganizationRole,
Public: bolt.DefaultOrganizationPublic,

View File

@ -2,7 +2,6 @@ package bolt
import (
"context"
"fmt"
"github.com/boltdb/bolt"
"github.com/influxdata/chronograf"
@ -32,11 +31,9 @@ func (s *ServersStore) Migrate(ctx context.Context) error {
return err
}
defaultOrgID := fmt.Sprintf("%d", defaultOrg.ID)
for _, server := range servers {
if server.Organization == "" {
server.Organization = defaultOrgID
server.Organization = defaultOrg.ID
if err := s.Update(ctx, server); err != nil {
return nil
}

View File

@ -2,7 +2,6 @@ package bolt
import (
"context"
"fmt"
"github.com/boltdb/bolt"
"github.com/influxdata/chronograf"
@ -32,11 +31,9 @@ func (s *SourcesStore) Migrate(ctx context.Context) error {
return err
}
defaultOrgID := fmt.Sprintf("%d", defaultOrg.ID)
for _, source := range sources {
if source.Organization == "" {
source.Organization = defaultOrgID
source.Organization = defaultOrg.ID
}
if source.Role == "" {
source.Role = roles.ViewerRoleName

View File

@ -2,7 +2,6 @@ package bolt_test
import (
"context"
"fmt"
"testing"
"github.com/google/go-cmp/cmp"
@ -301,9 +300,7 @@ func TestUsersStore_Delete(t *testing.T) {
s := client.UsersStore
if tt.addFirst {
var err error
tt.args.user, err = s.Add(tt.args.ctx, tt.args.user)
fmt.Println(err)
tt.args.user, _ = s.Add(tt.args.ctx, tt.args.user)
}
if err := s.Delete(tt.args.ctx, tt.args.user); (err != nil) != tt.wantErr {
t.Errorf("%q. UsersStore.Delete() error = %v, wantErr %v", tt.name, err, tt.wantErr)

View File

@ -16,6 +16,9 @@ const (
ErrDashboardNotFound = Error("dashboard not found")
ErrUserNotFound = Error("user not found")
ErrLayoutInvalid = Error("layout is invalid")
ErrDashboardInvalid = Error("dashboard is invalid")
ErrSourceInvalid = Error("source is invalid")
ErrServerInvalid = Error("server is invalid")
ErrAlertNotFound = Error("alert not found")
ErrAuthentication = Error("user not authenticated")
ErrUninitialized = Error("client uninitialized. Call Open() method")
@ -562,7 +565,7 @@ type LayoutsStore interface {
// Organization is a group of resources under a common name
type Organization struct {
ID uint64 `json:"id,string"`
ID string `json:"id"`
Name string `json:"name"`
// DefaultRole is the name of the role that is the default for any users added to the organization
DefaultRole string `json:"defaultRole,omitempty"`
@ -576,7 +579,7 @@ type Organization struct {
// It is expected that only one of ID or Name will be specified, but will prefer ID over Name if both are specified.
type OrganizationQuery struct {
// If an ID is provided in the query, the lookup time for an organization will be O(1).
ID *uint64
ID *string
// If Name is provided, the lookup time will be O(n).
Name *string
}

View File

@ -1,4 +1,4 @@
package canned
package filestore
import (
"context"

View File

@ -1,4 +1,4 @@
package canned_test
package filestore_test
import (
"context"
@ -13,7 +13,7 @@ import (
"time"
"github.com/influxdata/chronograf"
"github.com/influxdata/chronograf/canned"
"github.com/influxdata/chronograf/filestore"
clog "github.com/influxdata/chronograf/log"
)
@ -47,7 +47,7 @@ func TestAll(t *testing.T) {
apps, _ := MockApps(test.Existing, test.Err)
layouts, err := apps.All(context.Background())
if err != test.Err {
t.Errorf("Test %d: Canned all error expected: %v; actual: %v", i, test.Err, err)
t.Errorf("Test %d: apps all error expected: %v; actual: %v", i, test.Err, err)
}
if !reflect.DeepEqual(layouts, test.Existing) {
t.Errorf("Test %d: Layouts should be equal; expected %v; actual %v", i, test.Existing, layouts)
@ -99,7 +99,7 @@ func TestAdd(t *testing.T) {
apps, _ := MockApps(test.Existing, test.Err)
layout, err := apps.Add(context.Background(), test.Add)
if err != test.Err {
t.Errorf("Test %d: Canned add error expected: %v; actual: %v", i, test.Err, err)
t.Errorf("Test %d: apps add error expected: %v; actual: %v", i, test.Err, err)
}
if layout.ID != test.ExpectedID {
@ -150,7 +150,7 @@ func TestDelete(t *testing.T) {
apps, actual := MockApps(test.Existing, test.Err)
err := apps.Delete(context.Background(), chronograf.Layout{ID: test.DeleteID})
if err != test.Err {
t.Errorf("Test %d: Canned delete error expected: %v; actual: %v", i, test.Err, err)
t.Errorf("Test %d: apps delete error expected: %v; actual: %v", i, test.Err, err)
}
if !reflect.DeepEqual(*actual, test.Expected) {
t.Errorf("Test %d: Layouts should be equal; expected %v; actual %v", i, test.Expected, actual)
@ -199,7 +199,7 @@ func TestGet(t *testing.T) {
apps, _ := MockApps(test.Existing, test.Err)
layout, err := apps.Get(context.Background(), test.ID)
if err != test.Err {
t.Errorf("Test %d: Canned get error expected: %v; actual: %v", i, test.Err, err)
t.Errorf("Test %d: Layouts get error expected: %v; actual: %v", i, test.Err, err)
}
if !reflect.DeepEqual(layout, test.Expected) {
t.Errorf("Test %d: Layouts should be equal; expected %v; actual %v", i, test.Expected, layout)
@ -261,7 +261,7 @@ func TestUpdate(t *testing.T) {
apps, actual := MockApps(test.Existing, test.Err)
err := apps.Update(context.Background(), test.Update)
if err != test.Err {
t.Errorf("Test %d: Canned get error expected: %v; actual: %v", i, test.Err, err)
t.Errorf("Test %d: Layouts get error expected: %v; actual: %v", i, test.Err, err)
}
if !reflect.DeepEqual(*actual, test.Expected) {
t.Errorf("Test %d: Layouts should be equal; expected %v; actual %v", i, test.Expected, actual)
@ -312,7 +312,7 @@ func (m *MockID) Generate() (string, error) {
return strconv.Itoa(m.id), nil
}
func MockApps(existing []chronograf.Layout, expected error) (canned.Apps, *map[string]chronograf.Layout) {
func MockApps(existing []chronograf.Layout, expected error) (filestore.Apps, *map[string]chronograf.Layout) {
layouts := map[string]chronograf.Layout{}
fileName := func(dir string, layout chronograf.Layout) string {
return path.Join(dir, layout.ID+".json")
@ -326,11 +326,11 @@ func MockApps(existing []chronograf.Layout, expected error) (canned.Apps, *map[s
return chronograf.Layout{}, expected
}
if l, ok := layouts[file]; !ok {
l, ok := layouts[file]
if !ok {
return chronograf.Layout{}, chronograf.ErrLayoutNotFound
} else {
return l, nil
}
return l, nil
}
create := func(file string, layout chronograf.Layout) error {
@ -346,7 +346,7 @@ func MockApps(existing []chronograf.Layout, expected error) (canned.Apps, *map[s
return nil, expected
}
info := []os.FileInfo{}
for k, _ := range layouts {
for k := range layouts {
info = append(info, &MockFileInfo{filepath.Base(k)})
}
sort.Sort(MockFileInfos(info))
@ -364,7 +364,7 @@ func MockApps(existing []chronograf.Layout, expected error) (canned.Apps, *map[s
return nil
}
return canned.Apps{
return filestore.Apps{
Dir: dir,
Load: load,
Filename: fileName,

210
filestore/dashboards.go Normal file
View File

@ -0,0 +1,210 @@
package filestore
import (
"context"
"encoding/json"
"fmt"
"io/ioutil"
"os"
"path"
"strconv"
"github.com/influxdata/chronograf"
)
// DashExt is the the file extension searched for in the directory for dashboard files
const DashExt = ".dashboard"
var _ chronograf.DashboardsStore = &Dashboards{}
// Dashboards are JSON dashboards stored in the filesystem
type Dashboards struct {
Dir string // Dir is the directory containing the dashboards.
Load func(string, interface{}) error // Load loads string name and dashbaord passed in as interface
Create func(string, interface{}) error // Create will write dashboard to file.
ReadDir func(dirname string) ([]os.FileInfo, error) // ReadDir reads the directory named by dirname and returns a list of directory entries sorted by filename.
Remove func(name string) error // Remove file
IDs chronograf.ID // IDs generate unique ids for new dashboards
Logger chronograf.Logger
}
// NewDashboards constructs a dashboard store wrapping a file system directory
func NewDashboards(dir string, ids chronograf.ID, logger chronograf.Logger) chronograf.DashboardsStore {
return &Dashboards{
Dir: dir,
Load: load,
Create: create,
ReadDir: ioutil.ReadDir,
Remove: os.Remove,
IDs: ids,
Logger: logger,
}
}
func dashboardFile(dir string, dashboard chronograf.Dashboard) string {
base := fmt.Sprintf("%s%s", dashboard.Name, DashExt)
return path.Join(dir, base)
}
func load(name string, resource interface{}) error {
octets, err := templatedFromEnv(name)
if err != nil {
return fmt.Errorf("resource %s not found", name)
}
return json.Unmarshal(octets, resource)
}
func create(file string, resource interface{}) error {
h, err := os.Create(file)
if err != nil {
return err
}
defer h.Close()
octets, err := json.MarshalIndent(resource, " ", " ")
if err != nil {
return err
}
_, err = h.Write(octets)
return err
}
// All returns all dashboards from the directory
func (d *Dashboards) All(ctx context.Context) ([]chronograf.Dashboard, error) {
files, err := d.ReadDir(d.Dir)
if err != nil {
return nil, err
}
dashboards := []chronograf.Dashboard{}
for _, file := range files {
if path.Ext(file.Name()) != DashExt {
continue
}
var dashboard chronograf.Dashboard
if err := d.Load(path.Join(d.Dir, file.Name()), &dashboard); err != nil {
continue // We want to load all files we can.
} else {
dashboards = append(dashboards, dashboard)
}
}
return dashboards, nil
}
// Add creates a new dashboard within the directory
func (d *Dashboards) Add(ctx context.Context, dashboard chronograf.Dashboard) (chronograf.Dashboard, error) {
genID, err := d.IDs.Generate()
if err != nil {
d.Logger.
WithField("component", "dashboard").
Error("Unable to generate ID")
return chronograf.Dashboard{}, err
}
id, err := strconv.Atoi(genID)
if err != nil {
d.Logger.
WithField("component", "dashboard").
Error("Unable to convert ID")
return chronograf.Dashboard{}, err
}
dashboard.ID = chronograf.DashboardID(id)
file := dashboardFile(d.Dir, dashboard)
if err = d.Create(file, dashboard); err != nil {
if err == chronograf.ErrDashboardInvalid {
d.Logger.
WithField("component", "dashboard").
WithField("name", file).
Error("Invalid Dashboard: ", err)
} else {
d.Logger.
WithField("component", "dashboard").
WithField("name", file).
Error("Unable to write dashboard:", err)
}
return chronograf.Dashboard{}, err
}
return dashboard, nil
}
// Delete removes a dashboard file from the directory
func (d *Dashboards) Delete(ctx context.Context, dashboard chronograf.Dashboard) error {
_, file, err := d.idToFile(dashboard.ID)
if err != nil {
return err
}
if err := d.Remove(file); err != nil {
d.Logger.
WithField("component", "dashboard").
WithField("name", file).
Error("Unable to remove dashboard:", err)
return err
}
return nil
}
// Get returns a dashboard file from the dashboard directory
func (d *Dashboards) Get(ctx context.Context, id chronograf.DashboardID) (chronograf.Dashboard, error) {
board, file, err := d.idToFile(id)
if err != nil {
if err == chronograf.ErrDashboardNotFound {
d.Logger.
WithField("component", "dashboard").
WithField("name", file).
Error("Unable to read file")
} else if err == chronograf.ErrDashboardInvalid {
d.Logger.
WithField("component", "dashboard").
WithField("name", file).
Error("File is not a dashboard")
}
return chronograf.Dashboard{}, err
}
return board, nil
}
// Update replaces a dashboard from the file system directory
func (d *Dashboards) Update(ctx context.Context, dashboard chronograf.Dashboard) error {
board, _, err := d.idToFile(dashboard.ID)
if err != nil {
return err
}
if err := d.Delete(ctx, board); err != nil {
return err
}
file := dashboardFile(d.Dir, dashboard)
return d.Create(file, dashboard)
}
// idToFile takes an id and finds the associated filename
func (d *Dashboards) idToFile(id chronograf.DashboardID) (chronograf.Dashboard, string, error) {
// Because the entire dashboard information is not known at this point, we need
// to try to find the name of the file through matching the ID in the dashboard
// content with the ID passed.
files, err := d.ReadDir(d.Dir)
if err != nil {
return chronograf.Dashboard{}, "", err
}
for _, f := range files {
if path.Ext(f.Name()) != DashExt {
continue
}
file := path.Join(d.Dir, f.Name())
var dashboard chronograf.Dashboard
if err := d.Load(file, &dashboard); err != nil {
return chronograf.Dashboard{}, "", err
}
if dashboard.ID == id {
return dashboard, file, nil
}
}
return chronograf.Dashboard{}, "", chronograf.ErrDashboardNotFound
}

24
filestore/environ.go Normal file
View File

@ -0,0 +1,24 @@
package filestore
import (
"os"
"strings"
)
var env map[string]string
// environ returns a map of all environment variables in the running process
func environ() map[string]string {
if env == nil {
env = make(map[string]string)
envVars := os.Environ()
for _, envVar := range envVars {
kv := strings.SplitN(envVar, "=", 2)
if len(kv) != 2 {
continue
}
env[kv[0]] = kv[1]
}
}
return env
}

29
filestore/environ_test.go Normal file
View File

@ -0,0 +1,29 @@
package filestore
import (
"os"
"testing"
)
func Test_environ(t *testing.T) {
tests := []struct {
name string
key string
value string
}{
{
name: "environment variable is returned",
key: "CHRONOGRAF_TEST_ENVIRON",
value: "howdy",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
os.Setenv(tt.key, tt.value)
got := environ()
if v, ok := got[tt.key]; !ok || v != tt.value {
t.Errorf("environ() = %v, want %v", v, tt.value)
}
})
}
}

184
filestore/kapacitors.go Normal file
View File

@ -0,0 +1,184 @@
package filestore
import (
"context"
"fmt"
"io/ioutil"
"os"
"path"
"strconv"
"github.com/influxdata/chronograf"
)
// KapExt is the the file extension searched for in the directory for kapacitor files
const KapExt = ".kap"
var _ chronograf.ServersStore = &Kapacitors{}
// Kapacitors are JSON kapacitors stored in the filesystem
type Kapacitors struct {
Dir string // Dir is the directory containing the kapacitors.
Load func(string, interface{}) error // Load loads string name and dashbaord passed in as interface
Create func(string, interface{}) error // Create will write kapacitor to file.
ReadDir func(dirname string) ([]os.FileInfo, error) // ReadDir reads the directory named by dirname and returns a list of directory entries sorted by filename.
Remove func(name string) error // Remove file
IDs chronograf.ID // IDs generate unique ids for new kapacitors
Logger chronograf.Logger
}
// NewKapacitors constructs a kapacitor store wrapping a file system directory
func NewKapacitors(dir string, ids chronograf.ID, logger chronograf.Logger) chronograf.ServersStore {
return &Kapacitors{
Dir: dir,
Load: load,
Create: create,
ReadDir: ioutil.ReadDir,
Remove: os.Remove,
IDs: ids,
Logger: logger,
}
}
func kapacitorFile(dir string, kapacitor chronograf.Server) string {
base := fmt.Sprintf("%s%s", kapacitor.Name, KapExt)
return path.Join(dir, base)
}
// All returns all kapacitors from the directory
func (d *Kapacitors) All(ctx context.Context) ([]chronograf.Server, error) {
files, err := d.ReadDir(d.Dir)
if err != nil {
return nil, err
}
kapacitors := []chronograf.Server{}
for _, file := range files {
if path.Ext(file.Name()) != KapExt {
continue
}
var kapacitor chronograf.Server
if err := d.Load(path.Join(d.Dir, file.Name()), &kapacitor); err != nil {
continue // We want to load all files we can.
} else {
kapacitors = append(kapacitors, kapacitor)
}
}
return kapacitors, nil
}
// Add creates a new kapacitor within the directory
func (d *Kapacitors) Add(ctx context.Context, kapacitor chronograf.Server) (chronograf.Server, error) {
genID, err := d.IDs.Generate()
if err != nil {
d.Logger.
WithField("component", "kapacitor").
Error("Unable to generate ID")
return chronograf.Server{}, err
}
id, err := strconv.Atoi(genID)
if err != nil {
d.Logger.
WithField("component", "kapacitor").
Error("Unable to convert ID")
return chronograf.Server{}, err
}
kapacitor.ID = id
file := kapacitorFile(d.Dir, kapacitor)
if err = d.Create(file, kapacitor); err != nil {
if err == chronograf.ErrServerInvalid {
d.Logger.
WithField("component", "kapacitor").
WithField("name", file).
Error("Invalid Server: ", err)
} else {
d.Logger.
WithField("component", "kapacitor").
WithField("name", file).
Error("Unable to write kapacitor:", err)
}
return chronograf.Server{}, err
}
return kapacitor, nil
}
// Delete removes a kapacitor file from the directory
func (d *Kapacitors) Delete(ctx context.Context, kapacitor chronograf.Server) error {
_, file, err := d.idToFile(kapacitor.ID)
if err != nil {
return err
}
if err := d.Remove(file); err != nil {
d.Logger.
WithField("component", "kapacitor").
WithField("name", file).
Error("Unable to remove kapacitor:", err)
return err
}
return nil
}
// Get returns a kapacitor file from the kapacitor directory
func (d *Kapacitors) Get(ctx context.Context, id int) (chronograf.Server, error) {
board, file, err := d.idToFile(id)
if err != nil {
if err == chronograf.ErrServerNotFound {
d.Logger.
WithField("component", "kapacitor").
WithField("name", file).
Error("Unable to read file")
} else if err == chronograf.ErrServerInvalid {
d.Logger.
WithField("component", "kapacitor").
WithField("name", file).
Error("File is not a kapacitor")
}
return chronograf.Server{}, err
}
return board, nil
}
// Update replaces a kapacitor from the file system directory
func (d *Kapacitors) Update(ctx context.Context, kapacitor chronograf.Server) error {
board, _, err := d.idToFile(kapacitor.ID)
if err != nil {
return err
}
if err := d.Delete(ctx, board); err != nil {
return err
}
file := kapacitorFile(d.Dir, kapacitor)
return d.Create(file, kapacitor)
}
// idToFile takes an id and finds the associated filename
func (d *Kapacitors) idToFile(id int) (chronograf.Server, string, error) {
// Because the entire kapacitor information is not known at this point, we need
// to try to find the name of the file through matching the ID in the kapacitor
// content with the ID passed.
files, err := d.ReadDir(d.Dir)
if err != nil {
return chronograf.Server{}, "", err
}
for _, f := range files {
if path.Ext(f.Name()) != KapExt {
continue
}
file := path.Join(d.Dir, f.Name())
var kapacitor chronograf.Server
if err := d.Load(file, &kapacitor); err != nil {
return chronograf.Server{}, "", err
}
if kapacitor.ID == id {
return kapacitor, file, nil
}
}
return chronograf.Server{}, "", chronograf.ErrServerNotFound
}

122
filestore/organizations.go Normal file
View File

@ -0,0 +1,122 @@
package filestore
import (
"context"
"fmt"
"io/ioutil"
"os"
"path"
"github.com/influxdata/chronograf"
)
// OrgExt is the the file extension searched for in the directory for org files
const OrgExt = ".org"
var _ chronograf.OrganizationsStore = &Organizations{}
// Organizations are JSON orgs stored in the filesystem
type Organizations struct {
Dir string // Dir is the directory containing the orgs.
Load func(string, interface{}) error // Load loads string name and org passed in as interface
ReadDir func(dirname string) ([]os.FileInfo, error) // ReadDir reads the directory named by dirname and returns a list of directory entries sorted by filename.
Logger chronograf.Logger
}
// NewOrganizations constructs a org store wrapping a file system directory
func NewOrganizations(dir string, logger chronograf.Logger) chronograf.OrganizationsStore {
return &Organizations{
Dir: dir,
Load: load,
ReadDir: ioutil.ReadDir,
Logger: logger,
}
}
func orgFile(dir string, org chronograf.Organization) string {
base := fmt.Sprintf("%s%s", org.Name, OrgExt)
return path.Join(dir, base)
}
// All returns all orgs from the directory
func (o *Organizations) All(ctx context.Context) ([]chronograf.Organization, error) {
files, err := o.ReadDir(o.Dir)
if err != nil {
return nil, err
}
orgs := []chronograf.Organization{}
for _, file := range files {
if path.Ext(file.Name()) != OrgExt {
continue
}
var org chronograf.Organization
if err := o.Load(path.Join(o.Dir, file.Name()), &org); err != nil {
continue // We want to load all files we can.
} else {
orgs = append(orgs, org)
}
}
return orgs, nil
}
// Get returns a org file from the org directory
func (o *Organizations) Get(ctx context.Context, query chronograf.OrganizationQuery) (*chronograf.Organization, error) {
org, _, err := o.findOrg(query)
return org, err
}
// Add is not allowed for the filesystem organization store
func (o *Organizations) Add(ctx context.Context, org *chronograf.Organization) (*chronograf.Organization, error) {
return nil, fmt.Errorf("unable to add organizations to the filesystem")
}
// Delete is not allowed for the filesystem organization store
func (o *Organizations) Delete(ctx context.Context, org *chronograf.Organization) error {
return fmt.Errorf("unable to delete an organization from the filesystem")
}
// Update is not allowed for the filesystem organization store
func (o *Organizations) Update(ctx context.Context, org *chronograf.Organization) error {
return fmt.Errorf("unable to update organizations on the filesystem")
}
// CreateDefault is not allowed for the filesystem organization store
func (o *Organizations) CreateDefault(ctx context.Context) error {
return fmt.Errorf("unable to create default organizations on the filesystem")
}
// DefaultOrganization is not allowed for the filesystem organization store
func (o *Organizations) DefaultOrganization(ctx context.Context) (*chronograf.Organization, error) {
return nil, fmt.Errorf("unable to get default organizations from the filestore")
}
// findOrg takes an OrganizationQuery and finds the associated filename
func (o *Organizations) findOrg(query chronograf.OrganizationQuery) (*chronograf.Organization, string, error) {
// Because the entire org information is not known at this point, we need
// to try to find the name of the file through matching the ID or name in the org
// content with the ID passed.
files, err := o.ReadDir(o.Dir)
if err != nil {
return nil, "", err
}
for _, f := range files {
if path.Ext(f.Name()) != OrgExt {
continue
}
file := path.Join(o.Dir, f.Name())
var org chronograf.Organization
if err := o.Load(file, &org); err != nil {
return nil, "", err
}
if query.ID != nil && org.ID == *query.ID {
return &org, file, nil
}
if query.Name != nil && org.Name == *query.Name {
return &org, file, nil
}
}
return nil, "", chronograf.ErrOrganizationNotFound
}

184
filestore/sources.go Normal file
View File

@ -0,0 +1,184 @@
package filestore
import (
"context"
"fmt"
"io/ioutil"
"os"
"path"
"strconv"
"github.com/influxdata/chronograf"
)
// SrcExt is the the file extension searched for in the directory for source files
const SrcExt = ".src"
var _ chronograf.SourcesStore = &Sources{}
// Sources are JSON sources stored in the filesystem
type Sources struct {
Dir string // Dir is the directory containing the sources.
Load func(string, interface{}) error // Load loads string name and dashbaord passed in as interface
Create func(string, interface{}) error // Create will write source to file.
ReadDir func(dirname string) ([]os.FileInfo, error) // ReadDir reads the directory named by dirname and returns a list of directory entries sorted by filename.
Remove func(name string) error // Remove file
IDs chronograf.ID // IDs generate unique ids for new sources
Logger chronograf.Logger
}
// NewSources constructs a source store wrapping a file system directory
func NewSources(dir string, ids chronograf.ID, logger chronograf.Logger) chronograf.SourcesStore {
return &Sources{
Dir: dir,
Load: load,
Create: create,
ReadDir: ioutil.ReadDir,
Remove: os.Remove,
IDs: ids,
Logger: logger,
}
}
func sourceFile(dir string, source chronograf.Source) string {
base := fmt.Sprintf("%s%s", source.Name, SrcExt)
return path.Join(dir, base)
}
// All returns all sources from the directory
func (d *Sources) All(ctx context.Context) ([]chronograf.Source, error) {
files, err := d.ReadDir(d.Dir)
if err != nil {
return nil, err
}
sources := []chronograf.Source{}
for _, file := range files {
if path.Ext(file.Name()) != SrcExt {
continue
}
var source chronograf.Source
if err := d.Load(path.Join(d.Dir, file.Name()), &source); err != nil {
continue // We want to load all files we can.
} else {
sources = append(sources, source)
}
}
return sources, nil
}
// Add creates a new source within the directory
func (d *Sources) Add(ctx context.Context, source chronograf.Source) (chronograf.Source, error) {
genID, err := d.IDs.Generate()
if err != nil {
d.Logger.
WithField("component", "source").
Error("Unable to generate ID")
return chronograf.Source{}, err
}
id, err := strconv.Atoi(genID)
if err != nil {
d.Logger.
WithField("component", "source").
Error("Unable to convert ID")
return chronograf.Source{}, err
}
source.ID = id
file := sourceFile(d.Dir, source)
if err = d.Create(file, source); err != nil {
if err == chronograf.ErrSourceInvalid {
d.Logger.
WithField("component", "source").
WithField("name", file).
Error("Invalid Source: ", err)
} else {
d.Logger.
WithField("component", "source").
WithField("name", file).
Error("Unable to write source:", err)
}
return chronograf.Source{}, err
}
return source, nil
}
// Delete removes a source file from the directory
func (d *Sources) Delete(ctx context.Context, source chronograf.Source) error {
_, file, err := d.idToFile(source.ID)
if err != nil {
return err
}
if err := d.Remove(file); err != nil {
d.Logger.
WithField("component", "source").
WithField("name", file).
Error("Unable to remove source:", err)
return err
}
return nil
}
// Get returns a source file from the source directory
func (d *Sources) Get(ctx context.Context, id int) (chronograf.Source, error) {
board, file, err := d.idToFile(id)
if err != nil {
if err == chronograf.ErrSourceNotFound {
d.Logger.
WithField("component", "source").
WithField("name", file).
Error("Unable to read file")
} else if err == chronograf.ErrSourceInvalid {
d.Logger.
WithField("component", "source").
WithField("name", file).
Error("File is not a source")
}
return chronograf.Source{}, err
}
return board, nil
}
// Update replaces a source from the file system directory
func (d *Sources) Update(ctx context.Context, source chronograf.Source) error {
board, _, err := d.idToFile(source.ID)
if err != nil {
return err
}
if err := d.Delete(ctx, board); err != nil {
return err
}
file := sourceFile(d.Dir, source)
return d.Create(file, source)
}
// idToFile takes an id and finds the associated filename
func (d *Sources) idToFile(id int) (chronograf.Source, string, error) {
// Because the entire source information is not known at this point, we need
// to try to find the name of the file through matching the ID in the source
// content with the ID passed.
files, err := d.ReadDir(d.Dir)
if err != nil {
return chronograf.Source{}, "", err
}
for _, f := range files {
if path.Ext(f.Name()) != SrcExt {
continue
}
file := path.Join(d.Dir, f.Name())
var source chronograf.Source
if err := d.Load(file, &source); err != nil {
return chronograf.Source{}, "", err
}
if source.ID == id {
return source, file, nil
}
}
return chronograf.Source{}, "", chronograf.ErrSourceNotFound
}

28
filestore/templates.go Normal file
View File

@ -0,0 +1,28 @@
package filestore
import (
"bytes"
"html/template"
)
// templated returns all files templated using data
func templated(data interface{}, filenames ...string) ([]byte, error) {
t, err := template.ParseFiles(filenames...)
if err != nil {
return nil, err
}
var b bytes.Buffer
// If a key in the file exists but is not in the data we
// immediately fail with a missing key error
err = t.Option("missingkey=error").Execute(&b, data)
if err != nil {
return nil, err
}
return b.Bytes(), nil
}
// templatedFromEnv returns all files templated against environment variables
func templatedFromEnv(filenames ...string) ([]byte, error) {
return templated(environ(), filenames...)
}

View File

@ -0,0 +1,64 @@
package filestore
import (
"io/ioutil"
"os"
"reflect"
"testing"
)
func Test_templated(t *testing.T) {
tests := []struct {
name string
content []string
data interface{}
want []byte
wantErr bool
}{
{
name: "files with templates are rendered correctly",
content: []string{
"{{ .MYVAR }}",
},
data: map[string]string{
"MYVAR": "howdy",
},
want: []byte("howdy"),
},
{
name: "missing key gives an error",
content: []string{
"{{ .MYVAR }}",
},
wantErr: true,
},
{
name: "no files make me an error!",
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
filenames := make([]string, len(tt.content))
for i, c := range tt.content {
f, err := ioutil.TempFile("", "")
if err != nil {
t.Fatal(err)
}
if _, err := f.Write([]byte(c)); err != nil {
t.Fatal(err)
}
filenames[i] = f.Name()
defer os.Remove(f.Name())
}
got, err := templated(tt.data, filenames...)
if (err != nil) != tt.wantErr {
t.Errorf("templated() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("templated() = %v, want %v", got, tt.want)
}
})
}
}

25
id/time.go Normal file
View File

@ -0,0 +1,25 @@
package id
import (
"strconv"
"time"
"github.com/influxdata/chronograf"
)
// tm generates an id based on current time
type tm struct {
Now func() time.Time
}
// NewTime builds a chronograf.ID generator based on current time
func NewTime() chronograf.ID {
return &tm{
Now: time.Now,
}
}
// Generate creates a string based on the current time as an integer
func (i *tm) Generate() (string, error) {
return strconv.Itoa(int(i.Now().Unix())), nil
}

16
id/uuid.go Normal file
View File

@ -0,0 +1,16 @@
package id
import (
"github.com/influxdata/chronograf"
uuid "github.com/satori/go.uuid"
)
var _ chronograf.ID = &UUID{}
// UUID generates a V4 uuid
type UUID struct{}
// Generate creates a UUID v4 string
func (i *UUID) Generate() (string, error) {
return uuid.NewV4().String(), nil
}

File diff suppressed because it is too large Load Diff

8
integrations/testdata/example.kap vendored Normal file
View File

@ -0,0 +1,8 @@
{
"id": 5000,
"srcID": 5000,
"name": "Kapa 1",
"url": "http://localhost:9092",
"active": true,
"organization": "howdy"
}

5
integrations/testdata/example.org vendored Normal file
View File

@ -0,0 +1,5 @@
{
"id": "howdy",
"name": "An Organization",
"defaultRole": "viewer"
}

14
integrations/testdata/example.src vendored Normal file
View File

@ -0,0 +1,14 @@
{
"id": "5000",
"name": "Influx 1",
"username": "user1",
"password": "pass1",
"url": "http://localhost:8086",
"metaUrl": "http://metaurl.com",
"type": "influx-enterprise",
"insecureSkipVerify": false,
"default": true,
"telegraf": "telegraf",
"sharedSecret": "cubeapples",
"organization": "howdy"
}

185
integrations/testdata/mydash.dashboard vendored Normal file
View File

@ -0,0 +1,185 @@
{
"id": 1000,
"cells": [
{
"i": "8f61c619-dd9b-4761-8aa8-577f27247093",
"x": 0,
"y": 0,
"w": 11,
"h": 5,
"name": "Untitled Cell",
"queries": [
{
"query": "SELECT mean(\"value\") AS \"mean_value\" FROM \"telegraf\".\"autogen\".\"cpg\" WHERE time \u003e :dashboardTime: GROUP BY :interval: FILL(null)",
"queryConfig": {
"id": "b20baa61-bacb-4a17-b27d-b904a0d18114",
"database": "telegraf",
"measurement": "cpg",
"retentionPolicy": "autogen",
"fields": [
{
"value": "mean",
"type": "func",
"alias": "mean_value",
"args": [
{
"value": "value",
"type": "field",
"alias": ""
}
]
}
],
"tags": {},
"groupBy": {
"time": "auto",
"tags": []
},
"areTagsAccepted": true,
"fill": "null",
"rawText": null,
"range": null,
"shifts": []
},
"source": "/chronograf/v1/sources/2"
}
],
"axes": {
"x": {
"bounds": [],
"label": "",
"prefix": "",
"suffix": "",
"base": "10",
"scale": "linear"
},
"y": {
"bounds": [],
"label": "",
"prefix": "",
"suffix": "",
"base": "10",
"scale": "linear"
},
"y2": {
"bounds": [],
"label": "",
"prefix": "",
"suffix": "",
"base": "10",
"scale": "linear"
}
},
"type": "line",
"colors": [
{
"id": "0",
"type": "min",
"hex": "#00C9FF",
"name": "laser",
"value": "0"
},
{
"id": "1",
"type": "max",
"hex": "#9394FF",
"name": "comet",
"value": "100"
}
]
}
],
"templates": [
{
"tempVar": ":dbs:",
"values": [
{
"value": "_internal",
"type": "database",
"selected": true
},
{
"value": "telegraf",
"type": "database",
"selected": false
},
{
"value": "tensorflowdb",
"type": "database",
"selected": false
},
{
"value": "pushgateway",
"type": "database",
"selected": false
},
{
"value": "node_exporter",
"type": "database",
"selected": false
},
{
"value": "mydb",
"type": "database",
"selected": false
},
{
"value": "tiny",
"type": "database",
"selected": false
},
{
"value": "blah",
"type": "database",
"selected": false
},
{
"value": "test",
"type": "database",
"selected": false
},
{
"value": "chronograf",
"type": "database",
"selected": false
},
{
"value": "db_name",
"type": "database",
"selected": false
},
{
"value": "demo",
"type": "database",
"selected": false
},
{
"value": "eeg",
"type": "database",
"selected": false
},
{
"value": "solaredge",
"type": "database",
"selected": false
},
{
"value": "zipkin",
"type": "database",
"selected": false
}
],
"id": "e7e498bf-5869-4874-9071-24628a2cda63",
"type": "databases",
"label": "",
"query": {
"influxql": "SHOW DATABASES",
"measurement": "",
"tagKey": "",
"fieldKey": ""
}
}
],
"name": "Name This Dashboard",
"organization": "howdy"
}

View File

@ -5,7 +5,7 @@ import (
"fmt"
"github.com/influxdata/chronograf"
"github.com/influxdata/chronograf/uuid"
"github.com/influxdata/chronograf/id"
client "github.com/influxdata/kapacitor/client/v1"
)
@ -44,7 +44,7 @@ func NewClient(url, username, password string, insecureSkipVerify bool) *Client
Username: username,
Password: password,
InsecureSkipVerify: insecureSkipVerify,
ID: &uuid.V4{},
ID: &id.UUID{},
Ticker: &Alert{},
kapaClient: NewKapaClient,
}

View File

@ -7,9 +7,8 @@ import (
"github.com/influxdata/chronograf"
)
// Ensure KapacitorStore and MultiKapacitorStore implements chronograf.ServersStore.
// Ensure KapacitorStore implements chronograf.ServersStore.
var _ chronograf.ServersStore = &KapacitorStore{}
var _ chronograf.ServersStore = &MultiKapacitorStore{}
// KapacitorStore implements the chronograf.ServersStore interface, and keeps
// an in-memory Kapacitor according to startup configuration
@ -55,90 +54,3 @@ func (store *KapacitorStore) Update(ctx context.Context, kap chronograf.Server)
store.Kapacitor = &kap
return nil
}
// MultiKapacitorStore implements the chronograf.ServersStore interface, and
// delegates to all contained KapacitorStores
type MultiKapacitorStore struct {
Stores []chronograf.ServersStore
}
// All concatenates the Kapacitors of all contained Stores
func (multi *MultiKapacitorStore) All(ctx context.Context) ([]chronograf.Server, error) {
all := []chronograf.Server{}
kapSet := map[int]struct{}{}
ok := false
var err error
for _, store := range multi.Stores {
var kaps []chronograf.Server
kaps, err = store.All(ctx)
if err != nil {
// If this Store is unable to return an array of kapacitors, skip to the
// next Store.
continue
}
ok = true // We've received a response from at least one Store
for _, kap := range kaps {
// Enforce that the kapacitor has a unique ID
// If the ID has been seen before, ignore the kapacitor
if _, okay := kapSet[kap.ID]; !okay { // We have a new kapacitor
kapSet[kap.ID] = struct{}{} // We just care that the ID is unique
all = append(all, kap)
}
}
}
if !ok {
return nil, err
}
return all, nil
}
// Add the kap to the first responsive Store
func (multi *MultiKapacitorStore) Add(ctx context.Context, kap chronograf.Server) (chronograf.Server, error) {
var err error
for _, store := range multi.Stores {
var k chronograf.Server
k, err = store.Add(ctx, kap)
if err == nil {
return k, nil
}
}
return chronograf.Server{}, nil
}
// Delete delegates to all Stores, returns success if one Store is successful
func (multi *MultiKapacitorStore) Delete(ctx context.Context, kap chronograf.Server) error {
var err error
for _, store := range multi.Stores {
err = store.Delete(ctx, kap)
if err == nil {
return nil
}
}
return err
}
// Get finds the Source by id among all contained Stores
func (multi *MultiKapacitorStore) Get(ctx context.Context, id int) (chronograf.Server, error) {
var err error
for _, store := range multi.Stores {
var k chronograf.Server
k, err = store.Get(ctx, id)
if err == nil {
return k, nil
}
}
return chronograf.Server{}, nil
}
// Update the first responsive Store
func (multi *MultiKapacitorStore) Update(ctx context.Context, kap chronograf.Server) error {
var err error
for _, store := range multi.Stores {
err = store.Update(ctx, kap)
if err == nil {
return nil
}
}
return err
}

View File

@ -9,7 +9,6 @@ import (
func TestInterfaceImplementation(t *testing.T) {
var _ chronograf.ServersStore = &KapacitorStore{}
var _ chronograf.ServersStore = &MultiKapacitorStore{}
}
func TestKapacitorStoreAll(t *testing.T) {

View File

@ -7,95 +7,8 @@ import (
"github.com/influxdata/chronograf"
)
// Ensure MultiSourcesStore and SourcesStore implements chronograf.SourcesStore.
// Ensure SourcesStore implements chronograf.SourcesStore.
var _ chronograf.SourcesStore = &SourcesStore{}
var _ chronograf.SourcesStore = &MultiSourcesStore{}
// MultiSourcesStore delegates to the SourcesStores that compose it
type MultiSourcesStore struct {
Stores []chronograf.SourcesStore
}
// All concatenates the Sources of all contained Stores
func (multi *MultiSourcesStore) All(ctx context.Context) ([]chronograf.Source, error) {
all := []chronograf.Source{}
sourceSet := map[int]struct{}{}
ok := false
var err error
for _, store := range multi.Stores {
var sources []chronograf.Source
sources, err = store.All(ctx)
if err != nil {
// If this Store is unable to return an array of sources, skip to the
// next Store.
continue
}
ok = true // We've received a response from at least one Store
for _, s := range sources {
// Enforce that the source has a unique ID
// If the source has been seen before, don't override what we already have
if _, okay := sourceSet[s.ID]; !okay { // We have a new Source!
sourceSet[s.ID] = struct{}{} // We just care that the ID is unique
all = append(all, s)
}
}
}
if !ok {
return nil, err
}
return all, nil
}
// Add the src to the first Store to respond successfully
func (multi *MultiSourcesStore) Add(ctx context.Context, src chronograf.Source) (chronograf.Source, error) {
var err error
for _, store := range multi.Stores {
var s chronograf.Source
s, err = store.Add(ctx, src)
if err == nil {
return s, nil
}
}
return chronograf.Source{}, nil
}
// Delete delegates to all stores, returns success if one Store is successful
func (multi *MultiSourcesStore) Delete(ctx context.Context, src chronograf.Source) error {
var err error
for _, store := range multi.Stores {
err = store.Delete(ctx, src)
if err == nil {
return nil
}
}
return err
}
// Get finds the Source by id among all contained Stores
func (multi *MultiSourcesStore) Get(ctx context.Context, id int) (chronograf.Source, error) {
var err error
for _, store := range multi.Stores {
var s chronograf.Source
s, err = store.Get(ctx, id)
if err == nil {
return s, nil
}
}
return chronograf.Source{}, err
}
// Update the first store to return a successful response
func (multi *MultiSourcesStore) Update(ctx context.Context, src chronograf.Source) error {
var err error
for _, store := range multi.Stores {
err = store.Update(ctx, src)
if err == nil {
return nil
}
}
return err
}
// SourcesStore implements the chronograf.SourcesStore interface
type SourcesStore struct {

97
multistore/dashboards.go Normal file
View File

@ -0,0 +1,97 @@
package multistore
import (
"context"
"github.com/influxdata/chronograf"
)
// Ensure DashboardsStore implements chronograf.DashboardsStore.
var _ chronograf.DashboardsStore = &DashboardsStore{}
// DashboardsStore implements the chronograf.DashboardsStore interface, and
// delegates to all contained DashboardsStores
type DashboardsStore struct {
Stores []chronograf.DashboardsStore
}
// All concatenates the Dashboards of all contained Stores
func (multi *DashboardsStore) All(ctx context.Context) ([]chronograf.Dashboard, error) {
all := []chronograf.Dashboard{}
boardSet := map[chronograf.DashboardID]struct{}{}
ok := false
var err error
for _, store := range multi.Stores {
var boards []chronograf.Dashboard
boards, err = store.All(ctx)
if err != nil {
// If this Store is unable to return an array of dashboards, skip to the
// next Store.
continue
}
ok = true // We've received a response from at least one Store
for _, board := range boards {
// Enforce that the dashboard has a unique ID
// If the ID has been seen before, ignore the dashboard
if _, okay := boardSet[board.ID]; !okay { // We have a new dashboard
boardSet[board.ID] = struct{}{} // We just care that the ID is unique
all = append(all, board)
}
}
}
if !ok {
return nil, err
}
return all, nil
}
// Add the dashboard to the first responsive Store
func (multi *DashboardsStore) Add(ctx context.Context, dashboard chronograf.Dashboard) (chronograf.Dashboard, error) {
var err error
for _, store := range multi.Stores {
var d chronograf.Dashboard
d, err = store.Add(ctx, dashboard)
if err == nil {
return d, nil
}
}
return chronograf.Dashboard{}, nil
}
// Delete delegates to all Stores, returns success if one Store is successful
func (multi *DashboardsStore) Delete(ctx context.Context, dashboard chronograf.Dashboard) error {
var err error
for _, store := range multi.Stores {
err = store.Delete(ctx, dashboard)
if err == nil {
return nil
}
}
return err
}
// Get finds the Dashboard by id among all contained Stores
func (multi *DashboardsStore) Get(ctx context.Context, id chronograf.DashboardID) (chronograf.Dashboard, error) {
var err error
for _, store := range multi.Stores {
var d chronograf.Dashboard
d, err = store.Get(ctx, id)
if err == nil {
return d, nil
}
}
return chronograf.Dashboard{}, nil
}
// Update the first responsive Store
func (multi *DashboardsStore) Update(ctx context.Context, dashboard chronograf.Dashboard) error {
var err error
for _, store := range multi.Stores {
err = store.Update(ctx, dashboard)
if err == nil {
return nil
}
}
return err
}

97
multistore/kapacitors.go Normal file
View File

@ -0,0 +1,97 @@
package multistore
import (
"context"
"github.com/influxdata/chronograf"
)
// Ensure KapacitorStore implements chronograf.ServersStore.
var _ chronograf.ServersStore = &KapacitorStore{}
// KapacitorStore implements the chronograf.ServersStore interface, and
// delegates to all contained KapacitorStores
type KapacitorStore struct {
Stores []chronograf.ServersStore
}
// All concatenates the Kapacitors of all contained Stores
func (multi *KapacitorStore) All(ctx context.Context) ([]chronograf.Server, error) {
all := []chronograf.Server{}
kapSet := map[int]struct{}{}
ok := false
var err error
for _, store := range multi.Stores {
var kaps []chronograf.Server
kaps, err = store.All(ctx)
if err != nil {
// If this Store is unable to return an array of kapacitors, skip to the
// next Store.
continue
}
ok = true // We've received a response from at least one Store
for _, kap := range kaps {
// Enforce that the kapacitor has a unique ID
// If the ID has been seen before, ignore the kapacitor
if _, okay := kapSet[kap.ID]; !okay { // We have a new kapacitor
kapSet[kap.ID] = struct{}{} // We just care that the ID is unique
all = append(all, kap)
}
}
}
if !ok {
return nil, err
}
return all, nil
}
// Add the kap to the first responsive Store
func (multi *KapacitorStore) Add(ctx context.Context, kap chronograf.Server) (chronograf.Server, error) {
var err error
for _, store := range multi.Stores {
var k chronograf.Server
k, err = store.Add(ctx, kap)
if err == nil {
return k, nil
}
}
return chronograf.Server{}, nil
}
// Delete delegates to all Stores, returns success if one Store is successful
func (multi *KapacitorStore) Delete(ctx context.Context, kap chronograf.Server) error {
var err error
for _, store := range multi.Stores {
err = store.Delete(ctx, kap)
if err == nil {
return nil
}
}
return err
}
// Get finds the Source by id among all contained Stores
func (multi *KapacitorStore) Get(ctx context.Context, id int) (chronograf.Server, error) {
var err error
for _, store := range multi.Stores {
var k chronograf.Server
k, err = store.Get(ctx, id)
if err == nil {
return k, nil
}
}
return chronograf.Server{}, nil
}
// Update the first responsive Store
func (multi *KapacitorStore) Update(ctx context.Context, kap chronograf.Server) error {
var err error
for _, store := range multi.Stores {
err = store.Update(ctx, kap)
if err == nil {
return nil
}
}
return err
}

View File

@ -0,0 +1,11 @@
package multistore
import (
"testing"
"github.com/influxdata/chronograf"
)
func TestInterfaceImplementation(t *testing.T) {
var _ chronograf.ServersStore = &KapacitorStore{}
}

View File

@ -1,4 +1,4 @@
package layouts
package multistore
import (
"context"
@ -6,15 +6,15 @@ import (
"github.com/influxdata/chronograf"
)
// MultiLayoutsStore is a LayoutsStore that contains multiple LayoutsStores
// Layouts is a LayoutsStore that contains multiple LayoutsStores
// The All method will return the set of all Layouts.
// Each method will be tried against the Stores slice serially.
type MultiLayoutsStore struct {
type Layouts struct {
Stores []chronograf.LayoutsStore
}
// All returns the set of all layouts
func (s *MultiLayoutsStore) All(ctx context.Context) ([]chronograf.Layout, error) {
func (s *Layouts) All(ctx context.Context) ([]chronograf.Layout, error) {
all := []chronograf.Layout{}
layoutSet := map[string]chronograf.Layout{}
ok := false
@ -43,7 +43,7 @@ func (s *MultiLayoutsStore) All(ctx context.Context) ([]chronograf.Layout, error
}
// Add creates a new dashboard in the LayoutsStore. Tries each store sequentially until success.
func (s *MultiLayoutsStore) Add(ctx context.Context, layout chronograf.Layout) (chronograf.Layout, error) {
func (s *Layouts) Add(ctx context.Context, layout chronograf.Layout) (chronograf.Layout, error) {
var err error
for _, store := range s.Stores {
var l chronograf.Layout
@ -57,7 +57,7 @@ func (s *MultiLayoutsStore) Add(ctx context.Context, layout chronograf.Layout) (
// Delete the dashboard from the store. Searches through all stores to find Layout and
// then deletes from that store.
func (s *MultiLayoutsStore) Delete(ctx context.Context, layout chronograf.Layout) error {
func (s *Layouts) Delete(ctx context.Context, layout chronograf.Layout) error {
var err error
for _, store := range s.Stores {
err = store.Delete(ctx, layout)
@ -69,7 +69,7 @@ func (s *MultiLayoutsStore) Delete(ctx context.Context, layout chronograf.Layout
}
// Get retrieves Layout if `ID` exists. Searches through each store sequentially until success.
func (s *MultiLayoutsStore) Get(ctx context.Context, ID string) (chronograf.Layout, error) {
func (s *Layouts) Get(ctx context.Context, ID string) (chronograf.Layout, error) {
var err error
for _, store := range s.Stores {
var l chronograf.Layout
@ -82,7 +82,7 @@ func (s *MultiLayoutsStore) Get(ctx context.Context, ID string) (chronograf.Layo
}
// Update the dashboard in the store. Searches through each store sequentially until success.
func (s *MultiLayoutsStore) Update(ctx context.Context, layout chronograf.Layout) error {
func (s *Layouts) Update(ctx context.Context, layout chronograf.Layout) error {
var err error
for _, store := range s.Stores {
err = store.Update(ctx, layout)

129
multistore/organizations.go Normal file
View File

@ -0,0 +1,129 @@
package multistore
import (
"context"
"fmt"
"strings"
"github.com/influxdata/chronograf"
)
// Ensure OrganizationsStore implements chronograf.OrganizationsStore.
var _ chronograf.OrganizationsStore = &OrganizationsStore{}
// OrganizationsStore implements the chronograf.OrganizationsStore interface, and
// delegates to all contained OrganizationsStores
type OrganizationsStore struct {
Stores []chronograf.OrganizationsStore
}
// All concatenates the Organizations of all contained Stores
func (multi *OrganizationsStore) All(ctx context.Context) ([]chronograf.Organization, error) {
all := []chronograf.Organization{}
orgSet := map[string]struct{}{}
ok := false
var err error
for _, store := range multi.Stores {
var orgs []chronograf.Organization
orgs, err = store.All(ctx)
if err != nil {
// If this Store is unable to return an array of orgs, skip to the
// next Store.
continue
}
ok = true // We've received a response from at least one Store
for _, org := range orgs {
// Enforce that the org has a unique ID
// If the ID has been seen before, ignore the org
if _, okay := orgSet[org.ID]; !okay { // We have a new org
orgSet[org.ID] = struct{}{} // We just care that the ID is unique
all = append(all, org)
}
}
}
if !ok {
return nil, err
}
return all, nil
}
// Add the org to the first responsive Store
func (multi *OrganizationsStore) Add(ctx context.Context, org *chronograf.Organization) (*chronograf.Organization, error) {
errors := []string{}
for _, store := range multi.Stores {
var o *chronograf.Organization
o, err := store.Add(ctx, org)
if err == nil {
return o, nil
}
errors = append(errors, err.Error())
}
return nil, fmt.Errorf("Unknown error while adding organization: %s", strings.Join(errors, " "))
}
// Delete delegates to all Stores, returns success if one Store is successful
func (multi *OrganizationsStore) Delete(ctx context.Context, org *chronograf.Organization) error {
errors := []string{}
for _, store := range multi.Stores {
err := store.Delete(ctx, org)
if err == nil {
return nil
}
errors = append(errors, err.Error())
}
return fmt.Errorf("Unknown error while deleting organization: %s", strings.Join(errors, " "))
}
// Get finds the Organization by id among all contained Stores
func (multi *OrganizationsStore) Get(ctx context.Context, query chronograf.OrganizationQuery) (*chronograf.Organization, error) {
var err error
for _, store := range multi.Stores {
var o *chronograf.Organization
o, err = store.Get(ctx, query)
if err == nil {
return o, nil
}
}
return nil, chronograf.ErrOrganizationNotFound
}
// Update the first responsive Store
func (multi *OrganizationsStore) Update(ctx context.Context, org *chronograf.Organization) error {
errors := []string{}
for _, store := range multi.Stores {
err := store.Update(ctx, org)
if err == nil {
return nil
}
errors = append(errors, err.Error())
}
return fmt.Errorf("Unknown error while updating organization: %s", strings.Join(errors, " "))
}
// CreateDefault makes a default organization in the first responsive Store
func (multi *OrganizationsStore) CreateDefault(ctx context.Context) error {
errors := []string{}
for _, store := range multi.Stores {
err := store.CreateDefault(ctx)
if err == nil {
return nil
}
errors = append(errors, err.Error())
}
return fmt.Errorf("Unknown error while creating default organization: %s", strings.Join(errors, " "))
}
// DefaultOrganization returns the first successful DefaultOrganization
func (multi *OrganizationsStore) DefaultOrganization(ctx context.Context) (*chronograf.Organization, error) {
errors := []string{}
for _, store := range multi.Stores {
org, err := store.DefaultOrganization(ctx)
if err == nil {
return org, nil
}
errors = append(errors, err.Error())
}
return nil, fmt.Errorf("Unknown error while getting default organization: %s", strings.Join(errors, " "))
}

96
multistore/sources.go Normal file
View File

@ -0,0 +1,96 @@
package multistore
import (
"context"
"github.com/influxdata/chronograf"
)
// Ensure SourcesStore implements chronograf.SourcesStore.
var _ chronograf.SourcesStore = &SourcesStore{}
// SourcesStore delegates to the SourcesStores that compose it
type SourcesStore struct {
Stores []chronograf.SourcesStore
}
// All concatenates the Sources of all contained Stores
func (multi *SourcesStore) All(ctx context.Context) ([]chronograf.Source, error) {
all := []chronograf.Source{}
sourceSet := map[int]struct{}{}
ok := false
var err error
for _, store := range multi.Stores {
var sources []chronograf.Source
sources, err = store.All(ctx)
if err != nil {
// If this Store is unable to return an array of sources, skip to the
// next Store.
continue
}
ok = true // We've received a response from at least one Store
for _, s := range sources {
// Enforce that the source has a unique ID
// If the source has been seen before, don't override what we already have
if _, okay := sourceSet[s.ID]; !okay { // We have a new Source!
sourceSet[s.ID] = struct{}{} // We just care that the ID is unique
all = append(all, s)
}
}
}
if !ok {
return nil, err
}
return all, nil
}
// Add the src to the first Store to respond successfully
func (multi *SourcesStore) Add(ctx context.Context, src chronograf.Source) (chronograf.Source, error) {
var err error
for _, store := range multi.Stores {
var s chronograf.Source
s, err = store.Add(ctx, src)
if err == nil {
return s, nil
}
}
return chronograf.Source{}, nil
}
// Delete delegates to all stores, returns success if one Store is successful
func (multi *SourcesStore) Delete(ctx context.Context, src chronograf.Source) error {
var err error
for _, store := range multi.Stores {
err = store.Delete(ctx, src)
if err == nil {
return nil
}
}
return err
}
// Get finds the Source by id among all contained Stores
func (multi *SourcesStore) Get(ctx context.Context, id int) (chronograf.Source, error) {
var err error
for _, store := range multi.Stores {
var s chronograf.Source
s, err = store.Get(ctx, id)
if err == nil {
return s, nil
}
}
return chronograf.Source{}, err
}
// Update the first store to return a successful response
func (multi *SourcesStore) Update(ctx context.Context, src chronograf.Source) error {
var err error
for _, store := range multi.Stores {
err = store.Update(ctx, src)
if err == nil {
return nil
}
}
return err
}

View File

@ -66,13 +66,13 @@ func (s *OrganizationsStore) All(ctx context.Context) ([]chronograf.Organization
return nil, err
}
defaultOrgID := fmt.Sprintf("%d", defaultOrg.ID)
defaultOrgID := defaultOrg.ID
// This filters organizations without allocating
// https://github.com/golang/go/wiki/SliceTricks#filtering-without-allocating
organizations := ds[:0]
for _, d := range ds {
id := fmt.Sprintf("%d", d.ID)
id := d.ID
switch id {
case s.organization, defaultOrgID:
organizations = append(organizations, d)
@ -117,7 +117,7 @@ func (s *OrganizationsStore) Get(ctx context.Context, q chronograf.OrganizationQ
return nil, err
}
if fmt.Sprintf("%d", d.ID) != s.organization {
if d.ID != s.organization {
return nil, chronograf.ErrOrganizationNotFound
}

View File

@ -44,7 +44,7 @@ func TestOrganizations_All(t *testing.T) {
},
DefaultOrganizationF: func(ctx context.Context) (*chronograf.Organization, error) {
return &chronograf.Organization{
ID: 0,
ID: "0",
Name: "Default",
}, nil
},
@ -58,7 +58,7 @@ func TestOrganizations_All(t *testing.T) {
OrganizationsStore: &mocks.OrganizationsStore{
DefaultOrganizationF: func(ctx context.Context) (*chronograf.Organization, error) {
return &chronograf.Organization{
ID: 0,
ID: "0",
Name: "Default",
}, nil
},
@ -66,11 +66,11 @@ func TestOrganizations_All(t *testing.T) {
return []chronograf.Organization{
{
Name: "howdy",
ID: 1337,
ID: "1337",
},
{
Name: "doody",
ID: 1447,
ID: "1447",
},
}, nil
},
@ -83,11 +83,11 @@ func TestOrganizations_All(t *testing.T) {
want: []chronograf.Organization{
{
Name: "howdy",
ID: 1337,
ID: "1337",
},
{
Name: "Default",
ID: 0,
ID: "0",
},
},
},
@ -133,7 +133,7 @@ func TestOrganizations_Add(t *testing.T) {
},
GetF: func(ctx context.Context, q chronograf.OrganizationQuery) (*chronograf.Organization, error) {
return &chronograf.Organization{
ID: 1229,
ID: "1229",
Name: "howdy",
}, nil
},
@ -193,7 +193,7 @@ func TestOrganizations_Delete(t *testing.T) {
},
GetF: func(ctx context.Context, q chronograf.OrganizationQuery) (*chronograf.Organization, error) {
return &chronograf.Organization{
ID: 1229,
ID: "1229",
Name: "howdy",
}, nil
},
@ -203,7 +203,7 @@ func TestOrganizations_Delete(t *testing.T) {
organizationID: "1229",
ctx: context.Background(),
organization: &chronograf.Organization{
ID: 1229,
ID: "1229",
Name: "howdy",
},
},
@ -244,7 +244,7 @@ func TestOrganizations_Get(t *testing.T) {
OrganizationsStore: &mocks.OrganizationsStore{
GetF: func(ctx context.Context, q chronograf.OrganizationQuery) (*chronograf.Organization, error) {
return &chronograf.Organization{
ID: 1337,
ID: "1337",
Name: "howdy",
}, nil
},
@ -254,12 +254,12 @@ func TestOrganizations_Get(t *testing.T) {
organizationID: "1337",
ctx: context.Background(),
organization: &chronograf.Organization{
ID: 1337,
ID: "1337",
Name: "howdy",
},
},
want: &chronograf.Organization{
ID: 1337,
ID: "1337",
Name: "howdy",
},
},
@ -305,7 +305,7 @@ func TestOrganizations_Update(t *testing.T) {
},
GetF: func(ctx context.Context, q chronograf.OrganizationQuery) (*chronograf.Organization, error) {
return &chronograf.Organization{
ID: 1229,
ID: "1229",
Name: "doody",
}, nil
},
@ -315,7 +315,7 @@ func TestOrganizations_Update(t *testing.T) {
organizationID: "1229",
ctx: context.Background(),
organization: &chronograf.Organization{
ID: 1229,
ID: "1229",
Name: "howdy",
},
name: "doody",

View File

@ -99,19 +99,13 @@ func AuthorizedUser(
Error(w, http.StatusForbidden, "User is not authorized", logger)
return
}
p.Organization = fmt.Sprintf("%d", defaultOrg.ID)
p.Organization = defaultOrg.ID
}
// validate that the organization exists
orgID, err := parseOrganizationID(p.Organization)
_, err = store.Organizations(serverCtx).Get(serverCtx, chronograf.OrganizationQuery{ID: &p.Organization})
if err != nil {
log.Error("Failed to validate organization on context")
Error(w, http.StatusForbidden, "User is not authorized", logger)
return
}
_, err = store.Organizations(serverCtx).Get(serverCtx, chronograf.OrganizationQuery{ID: &orgID})
if err != nil {
log.Error(fmt.Sprintf("Failed to retrieve organization %d from organizations store", orgID))
log.Error(fmt.Sprintf("Failed to retrieve organization %s from organizations store", p.Organization))
Error(w, http.StatusForbidden, "User is not authorized", logger)
return
}

View File

@ -93,7 +93,7 @@ func TestAuthorizedUser(t *testing.T) {
OrganizationsStore: &mocks.OrganizationsStore{
DefaultOrganizationF: func(ctx context.Context) (*chronograf.Organization, error) {
return &chronograf.Organization{
ID: 0,
ID: "0",
}, nil
},
},
@ -133,7 +133,7 @@ func TestAuthorizedUser(t *testing.T) {
OrganizationsStore: &mocks.OrganizationsStore{
DefaultOrganizationF: func(ctx context.Context) (*chronograf.Organization, error) {
return &chronograf.Organization{
ID: 0,
ID: "0",
}, nil
},
GetF: func(ctx context.Context, q chronograf.OrganizationQuery) (*chronograf.Organization, error) {
@ -141,7 +141,7 @@ func TestAuthorizedUser(t *testing.T) {
return nil, fmt.Errorf("Invalid organization query: missing ID")
}
return &chronograf.Organization{
ID: 1337,
ID: "1337",
Name: "The ShillBillThrilliettas",
}, nil
},
@ -189,7 +189,7 @@ func TestAuthorizedUser(t *testing.T) {
OrganizationsStore: &mocks.OrganizationsStore{
DefaultOrganizationF: func(ctx context.Context) (*chronograf.Organization, error) {
return &chronograf.Organization{
ID: 0,
ID: "0",
}, nil
},
GetF: func(ctx context.Context, q chronograf.OrganizationQuery) (*chronograf.Organization, error) {
@ -197,7 +197,7 @@ func TestAuthorizedUser(t *testing.T) {
return nil, fmt.Errorf("Invalid organization query: missing ID")
}
return &chronograf.Organization{
ID: 1337,
ID: "1337",
Name: "The ShillBillThrilliettas",
}, nil
},
@ -245,7 +245,7 @@ func TestAuthorizedUser(t *testing.T) {
OrganizationsStore: &mocks.OrganizationsStore{
DefaultOrganizationF: func(ctx context.Context) (*chronograf.Organization, error) {
return &chronograf.Organization{
ID: 0,
ID: "0",
}, nil
},
GetF: func(ctx context.Context, q chronograf.OrganizationQuery) (*chronograf.Organization, error) {
@ -253,7 +253,7 @@ func TestAuthorizedUser(t *testing.T) {
return nil, fmt.Errorf("Invalid organization query: missing ID")
}
return &chronograf.Organization{
ID: 1337,
ID: "1337",
Name: "The ShillBillThrilliettas",
}, nil
},
@ -301,7 +301,7 @@ func TestAuthorizedUser(t *testing.T) {
OrganizationsStore: &mocks.OrganizationsStore{
DefaultOrganizationF: func(ctx context.Context) (*chronograf.Organization, error) {
return &chronograf.Organization{
ID: 0,
ID: "0",
}, nil
},
GetF: func(ctx context.Context, q chronograf.OrganizationQuery) (*chronograf.Organization, error) {
@ -309,7 +309,7 @@ func TestAuthorizedUser(t *testing.T) {
return nil, fmt.Errorf("Invalid organization query: missing ID")
}
return &chronograf.Organization{
ID: 1337,
ID: "1337",
Name: "The ShillBillThrilliettas",
}, nil
},
@ -353,7 +353,7 @@ func TestAuthorizedUser(t *testing.T) {
OrganizationsStore: &mocks.OrganizationsStore{
DefaultOrganizationF: func(ctx context.Context) (*chronograf.Organization, error) {
return &chronograf.Organization{
ID: 0,
ID: "0",
}, nil
},
GetF: func(ctx context.Context, q chronograf.OrganizationQuery) (*chronograf.Organization, error) {
@ -361,7 +361,7 @@ func TestAuthorizedUser(t *testing.T) {
return nil, fmt.Errorf("Invalid organization query: missing ID")
}
return &chronograf.Organization{
ID: 1337,
ID: "1337",
Name: "The ShillBillThrilliettas",
}, nil
},
@ -409,7 +409,7 @@ func TestAuthorizedUser(t *testing.T) {
OrganizationsStore: &mocks.OrganizationsStore{
DefaultOrganizationF: func(ctx context.Context) (*chronograf.Organization, error) {
return &chronograf.Organization{
ID: 0,
ID: "0",
}, nil
},
GetF: func(ctx context.Context, q chronograf.OrganizationQuery) (*chronograf.Organization, error) {
@ -417,7 +417,7 @@ func TestAuthorizedUser(t *testing.T) {
return nil, fmt.Errorf("Invalid organization query: missing ID")
}
return &chronograf.Organization{
ID: 1337,
ID: "1337",
Name: "The ShillBillThrilliettas",
}, nil
},
@ -465,7 +465,7 @@ func TestAuthorizedUser(t *testing.T) {
OrganizationsStore: &mocks.OrganizationsStore{
DefaultOrganizationF: func(ctx context.Context) (*chronograf.Organization, error) {
return &chronograf.Organization{
ID: 0,
ID: "0",
}, nil
},
GetF: func(ctx context.Context, q chronograf.OrganizationQuery) (*chronograf.Organization, error) {
@ -473,7 +473,7 @@ func TestAuthorizedUser(t *testing.T) {
return nil, fmt.Errorf("Invalid organization query: missing ID")
}
return &chronograf.Organization{
ID: 1337,
ID: "1337",
Name: "The ShillBillThrilliettas",
}, nil
},
@ -517,7 +517,7 @@ func TestAuthorizedUser(t *testing.T) {
OrganizationsStore: &mocks.OrganizationsStore{
DefaultOrganizationF: func(ctx context.Context) (*chronograf.Organization, error) {
return &chronograf.Organization{
ID: 0,
ID: "0",
}, nil
},
GetF: func(ctx context.Context, q chronograf.OrganizationQuery) (*chronograf.Organization, error) {
@ -525,7 +525,7 @@ func TestAuthorizedUser(t *testing.T) {
return nil, fmt.Errorf("Invalid organization query: missing ID")
}
return &chronograf.Organization{
ID: 1337,
ID: "1337",
Name: "The ShillBillThrilliettas",
}, nil
},
@ -569,7 +569,7 @@ func TestAuthorizedUser(t *testing.T) {
OrganizationsStore: &mocks.OrganizationsStore{
DefaultOrganizationF: func(ctx context.Context) (*chronograf.Organization, error) {
return &chronograf.Organization{
ID: 0,
ID: "0",
}, nil
},
GetF: func(ctx context.Context, q chronograf.OrganizationQuery) (*chronograf.Organization, error) {
@ -577,7 +577,7 @@ func TestAuthorizedUser(t *testing.T) {
return nil, fmt.Errorf("Invalid organization query: missing ID")
}
return &chronograf.Organization{
ID: 1337,
ID: "1337",
Name: "The ShillBillThrilliettas",
}, nil
},
@ -620,7 +620,7 @@ func TestAuthorizedUser(t *testing.T) {
OrganizationsStore: &mocks.OrganizationsStore{
DefaultOrganizationF: func(ctx context.Context) (*chronograf.Organization, error) {
return &chronograf.Organization{
ID: 0,
ID: "0",
}, nil
},
GetF: func(ctx context.Context, q chronograf.OrganizationQuery) (*chronograf.Organization, error) {
@ -628,7 +628,7 @@ func TestAuthorizedUser(t *testing.T) {
return nil, fmt.Errorf("Invalid organization query: missing ID")
}
return &chronograf.Organization{
ID: 1337,
ID: "1337",
Name: "The ShillBillThrilliettas",
}, nil
},
@ -667,7 +667,7 @@ func TestAuthorizedUser(t *testing.T) {
OrganizationsStore: &mocks.OrganizationsStore{
DefaultOrganizationF: func(ctx context.Context) (*chronograf.Organization, error) {
return &chronograf.Organization{
ID: 0,
ID: "0",
}, nil
},
GetF: func(ctx context.Context, q chronograf.OrganizationQuery) (*chronograf.Organization, error) {
@ -675,7 +675,7 @@ func TestAuthorizedUser(t *testing.T) {
return nil, fmt.Errorf("Invalid organization query: missing ID")
}
return &chronograf.Organization{
ID: 1337,
ID: "1337",
Name: "The ShillBillThrilliettas",
}, nil
},
@ -714,7 +714,7 @@ func TestAuthorizedUser(t *testing.T) {
OrganizationsStore: &mocks.OrganizationsStore{
DefaultOrganizationF: func(ctx context.Context) (*chronograf.Organization, error) {
return &chronograf.Organization{
ID: 0,
ID: "0",
}, nil
},
GetF: func(ctx context.Context, q chronograf.OrganizationQuery) (*chronograf.Organization, error) {
@ -722,7 +722,7 @@ func TestAuthorizedUser(t *testing.T) {
return nil, fmt.Errorf("Invalid organization query: missing ID")
}
return &chronograf.Organization{
ID: 1337,
ID: "1337",
Name: "The ShillBillThrilliettas",
}, nil
},
@ -765,7 +765,7 @@ func TestAuthorizedUser(t *testing.T) {
OrganizationsStore: &mocks.OrganizationsStore{
DefaultOrganizationF: func(ctx context.Context) (*chronograf.Organization, error) {
return &chronograf.Organization{
ID: 0,
ID: "0",
}, nil
},
GetF: func(ctx context.Context, q chronograf.OrganizationQuery) (*chronograf.Organization, error) {
@ -773,7 +773,7 @@ func TestAuthorizedUser(t *testing.T) {
return nil, fmt.Errorf("Invalid organization query: missing ID")
}
return &chronograf.Organization{
ID: 1337,
ID: "1337",
Name: "The ShillBillThrilliettas",
}, nil
},
@ -819,7 +819,7 @@ func TestAuthorizedUser(t *testing.T) {
return nil, fmt.Errorf("Invalid organization query: missing ID")
}
return &chronograf.Organization{
ID: 1337,
ID: "1337",
Name: "The ShillBillThrilliettas",
}, nil
},
@ -862,7 +862,7 @@ func TestAuthorizedUser(t *testing.T) {
OrganizationsStore: &mocks.OrganizationsStore{
DefaultOrganizationF: func(ctx context.Context) (*chronograf.Organization, error) {
return &chronograf.Organization{
ID: 0,
ID: "0",
}, nil
},
GetF: func(ctx context.Context, q chronograf.OrganizationQuery) (*chronograf.Organization, error) {
@ -870,7 +870,7 @@ func TestAuthorizedUser(t *testing.T) {
return nil, fmt.Errorf("Invalid organization query: missing ID")
}
return &chronograf.Organization{
ID: 1337,
ID: "1337",
Name: "The ShillBillThrilliettas",
}, nil
},
@ -914,7 +914,7 @@ func TestAuthorizedUser(t *testing.T) {
OrganizationsStore: &mocks.OrganizationsStore{
DefaultOrganizationF: func(ctx context.Context) (*chronograf.Organization, error) {
return &chronograf.Organization{
ID: 0,
ID: "0",
}, nil
},
GetF: func(ctx context.Context, q chronograf.OrganizationQuery) (*chronograf.Organization, error) {
@ -922,7 +922,7 @@ func TestAuthorizedUser(t *testing.T) {
return nil, fmt.Errorf("Invalid organization query: missing ID")
}
return &chronograf.Organization{
ID: 1337,
ID: "1337",
Name: "The ShillBillThrilliettas",
}, nil
},
@ -966,7 +966,7 @@ func TestAuthorizedUser(t *testing.T) {
OrganizationsStore: &mocks.OrganizationsStore{
DefaultOrganizationF: func(ctx context.Context) (*chronograf.Organization, error) {
return &chronograf.Organization{
ID: 0,
ID: "0",
}, nil
},
GetF: func(ctx context.Context, q chronograf.OrganizationQuery) (*chronograf.Organization, error) {
@ -974,7 +974,7 @@ func TestAuthorizedUser(t *testing.T) {
return nil, fmt.Errorf("Invalid organization query: missing ID")
}
return &chronograf.Organization{
ID: 1337,
ID: "1337",
Name: "The ShillBillThrilliettas",
}, nil
},
@ -1018,7 +1018,7 @@ func TestAuthorizedUser(t *testing.T) {
OrganizationsStore: &mocks.OrganizationsStore{
DefaultOrganizationF: func(ctx context.Context) (*chronograf.Organization, error) {
return &chronograf.Organization{
ID: 0,
ID: "0",
}, nil
},
GetF: func(ctx context.Context, q chronograf.OrganizationQuery) (*chronograf.Organization, error) {
@ -1026,7 +1026,7 @@ func TestAuthorizedUser(t *testing.T) {
return nil, fmt.Errorf("Invalid organization query: missing ID")
}
return &chronograf.Organization{
ID: 1337,
ID: "1337",
Name: "The ShillBillThrilliettas",
}, nil
},
@ -1071,7 +1071,7 @@ func TestAuthorizedUser(t *testing.T) {
OrganizationsStore: &mocks.OrganizationsStore{
DefaultOrganizationF: func(ctx context.Context) (*chronograf.Organization, error) {
return &chronograf.Organization{
ID: 0,
ID: "0",
}, nil
},
GetF: func(ctx context.Context, q chronograf.OrganizationQuery) (*chronograf.Organization, error) {
@ -1079,7 +1079,7 @@ func TestAuthorizedUser(t *testing.T) {
return nil, fmt.Errorf("Invalid organization query: missing ID")
}
return &chronograf.Organization{
ID: 1337,
ID: "1337",
Name: "The ShillBillThrilliettas",
}, nil
},
@ -1128,7 +1128,7 @@ func TestAuthorizedUser(t *testing.T) {
OrganizationsStore: &mocks.OrganizationsStore{
DefaultOrganizationF: func(ctx context.Context) (*chronograf.Organization, error) {
return &chronograf.Organization{
ID: 0,
ID: "0",
}, nil
},
GetF: func(ctx context.Context, q chronograf.OrganizationQuery) (*chronograf.Organization, error) {
@ -1136,7 +1136,7 @@ func TestAuthorizedUser(t *testing.T) {
return nil, fmt.Errorf("Invalid organization query: missing ID")
}
return &chronograf.Organization{
ID: 1337,
ID: "1337",
Name: "The ShillBillThrilliettas",
}, nil
},
@ -1185,7 +1185,7 @@ func TestAuthorizedUser(t *testing.T) {
OrganizationsStore: &mocks.OrganizationsStore{
DefaultOrganizationF: func(ctx context.Context) (*chronograf.Organization, error) {
return &chronograf.Organization{
ID: 0,
ID: "0",
}, nil
},
GetF: func(ctx context.Context, q chronograf.OrganizationQuery) (*chronograf.Organization, error) {
@ -1193,7 +1193,7 @@ func TestAuthorizedUser(t *testing.T) {
return nil, fmt.Errorf("Invalid organization query: missing ID")
}
return &chronograf.Organization{
ID: 1337,
ID: "1337",
Name: "The ShillBillThrilliettas",
}, nil
},
@ -1242,7 +1242,7 @@ func TestAuthorizedUser(t *testing.T) {
OrganizationsStore: &mocks.OrganizationsStore{
DefaultOrganizationF: func(ctx context.Context) (*chronograf.Organization, error) {
return &chronograf.Organization{
ID: 0,
ID: "0",
}, nil
},
GetF: func(ctx context.Context, q chronograf.OrganizationQuery) (*chronograf.Organization, error) {
@ -1250,7 +1250,7 @@ func TestAuthorizedUser(t *testing.T) {
return nil, fmt.Errorf("Invalid organization query: missing ID")
}
return &chronograf.Organization{
ID: 1337,
ID: "1337",
Name: "The ShillBillThrilliettas",
}, nil
},
@ -1298,7 +1298,7 @@ func TestAuthorizedUser(t *testing.T) {
OrganizationsStore: &mocks.OrganizationsStore{
DefaultOrganizationF: func(ctx context.Context) (*chronograf.Organization, error) {
return &chronograf.Organization{
ID: 0,
ID: "0",
}, nil
},
GetF: func(ctx context.Context, q chronograf.OrganizationQuery) (*chronograf.Organization, error) {
@ -1306,7 +1306,7 @@ func TestAuthorizedUser(t *testing.T) {
return nil, fmt.Errorf("Invalid organization query: missing ID")
}
return &chronograf.Organization{
ID: 1337,
ID: "1337",
Name: "The ShillBillThrilliettas",
}, nil
},
@ -1346,7 +1346,7 @@ func TestAuthorizedUser(t *testing.T) {
OrganizationsStore: &mocks.OrganizationsStore{
DefaultOrganizationF: func(ctx context.Context) (*chronograf.Organization, error) {
return &chronograf.Organization{
ID: 0,
ID: "0",
}, nil
},
GetF: func(ctx context.Context, q chronograf.OrganizationQuery) (*chronograf.Organization, error) {
@ -1354,7 +1354,7 @@ func TestAuthorizedUser(t *testing.T) {
return nil, fmt.Errorf("Invalid organization query: missing ID")
}
return &chronograf.Organization{
ID: 1337,
ID: "1337",
Name: "The ShillBillThrilliettas",
}, nil
},
@ -1397,7 +1397,7 @@ func TestAuthorizedUser(t *testing.T) {
OrganizationsStore: &mocks.OrganizationsStore{
DefaultOrganizationF: func(ctx context.Context) (*chronograf.Organization, error) {
return &chronograf.Organization{
ID: 0,
ID: "0",
}, nil
},
GetF: func(ctx context.Context, q chronograf.OrganizationQuery) (*chronograf.Organization, error) {
@ -1405,7 +1405,7 @@ func TestAuthorizedUser(t *testing.T) {
return nil, fmt.Errorf("Invalid organization query: missing ID")
}
return &chronograf.Organization{
ID: 1337,
ID: "1337",
Name: "The ShillBillThrilliettas",
}, nil
},
@ -1449,7 +1449,7 @@ func TestAuthorizedUser(t *testing.T) {
OrganizationsStore: &mocks.OrganizationsStore{
DefaultOrganizationF: func(ctx context.Context) (*chronograf.Organization, error) {
return &chronograf.Organization{
ID: 0,
ID: "0",
}, nil
},
GetF: func(ctx context.Context, q chronograf.OrganizationQuery) (*chronograf.Organization, error) {
@ -1457,9 +1457,9 @@ func TestAuthorizedUser(t *testing.T) {
return nil, fmt.Errorf("Invalid organization query: missing ID")
}
switch *q.ID {
case 1338:
case "1338":
return &chronograf.Organization{
ID: 1338,
ID: "1338",
Name: "The ShillBillThrilliettas",
}, nil
default:
@ -1511,7 +1511,7 @@ func TestAuthorizedUser(t *testing.T) {
OrganizationsStore: &mocks.OrganizationsStore{
DefaultOrganizationF: func(ctx context.Context) (*chronograf.Organization, error) {
return &chronograf.Organization{
ID: 0,
ID: "0",
}, nil
},
GetF: func(ctx context.Context, q chronograf.OrganizationQuery) (*chronograf.Organization, error) {
@ -1519,7 +1519,7 @@ func TestAuthorizedUser(t *testing.T) {
return nil, fmt.Errorf("Invalid organization query: missing ID")
}
return &chronograf.Organization{
ID: 1337,
ID: "1337",
Name: "The ShillBillThrilliettas",
}, nil
},

View File

@ -3,27 +3,28 @@ package server
import (
"github.com/influxdata/chronograf"
"github.com/influxdata/chronograf/canned"
"github.com/influxdata/chronograf/layouts"
"github.com/influxdata/chronograf/filestore"
"github.com/influxdata/chronograf/memdb"
"github.com/influxdata/chronograf/multistore"
)
// LayoutBuilder is responsible for building Layouts
type LayoutBuilder interface {
Build(chronograf.LayoutsStore) (*layouts.MultiLayoutsStore, error)
Build(chronograf.LayoutsStore) (*multistore.Layouts, error)
}
// MultiLayoutBuilder implements LayoutBuilder and will return a MultiLayoutsStore
// MultiLayoutBuilder implements LayoutBuilder and will return a Layouts
type MultiLayoutBuilder struct {
Logger chronograf.Logger
UUID chronograf.ID
CannedPath string
}
// Build will construct a MultiLayoutsStore of canned and db-backed personalized
// Build will construct a Layouts of canned and db-backed personalized
// layouts
func (builder *MultiLayoutBuilder) Build(db chronograf.LayoutsStore) (*layouts.MultiLayoutsStore, error) {
func (builder *MultiLayoutBuilder) Build(db chronograf.LayoutsStore) (*multistore.Layouts, error) {
// These apps are those handled from a directory
apps := canned.NewApps(builder.CannedPath, builder.UUID, builder.Logger)
apps := filestore.NewApps(builder.CannedPath, builder.UUID, builder.Logger)
// These apps are statically compiled into chronograf
binApps := &canned.BinLayoutsStore{
Logger: builder.Logger,
@ -31,7 +32,7 @@ func (builder *MultiLayoutBuilder) Build(db chronograf.LayoutsStore) (*layouts.M
// Acts as a front-end to both the bolt layouts, filesystem layouts and binary statically compiled layouts.
// The idea here is that these stores form a hierarchy in which each is tried sequentially until
// the operation has success. So, the database is preferred over filesystem over binary data.
layouts := &layouts.MultiLayoutsStore{
layouts := &multistore.Layouts{
Stores: []chronograf.LayoutsStore{
db,
apps,
@ -42,9 +43,38 @@ func (builder *MultiLayoutBuilder) Build(db chronograf.LayoutsStore) (*layouts.M
return layouts, nil
}
// DashboardBuilder is responsible for building dashboards
type DashboardBuilder interface {
Build(chronograf.DashboardsStore) (*multistore.DashboardsStore, error)
}
// MultiDashboardBuilder builds a DashboardsStore backed by bolt and the filesystem
type MultiDashboardBuilder struct {
Logger chronograf.Logger
ID chronograf.ID
Path string
}
// Build will construct a Dashboard store of filesystem and db-backed dashboards
func (builder *MultiDashboardBuilder) Build(db chronograf.DashboardsStore) (*multistore.DashboardsStore, error) {
// These dashboards are those handled from a directory
files := filestore.NewDashboards(builder.Path, builder.ID, builder.Logger)
// Acts as a front-end to both the bolt dashboard and filesystem dashboards.
// The idea here is that these stores form a hierarchy in which each is tried sequentially until
// the operation has success. So, the database is preferred over filesystem
dashboards := &multistore.DashboardsStore{
Stores: []chronograf.DashboardsStore{
db,
files,
},
}
return dashboards, nil
}
// SourcesBuilder builds a MultiSourceStore
type SourcesBuilder interface {
Build(chronograf.SourcesStore) (*memdb.MultiSourcesStore, error)
Build(chronograf.SourcesStore) (*multistore.SourcesStore, error)
}
// MultiSourceBuilder implements SourcesBuilder
@ -52,11 +82,18 @@ type MultiSourceBuilder struct {
InfluxDBURL string
InfluxDBUsername string
InfluxDBPassword string
Logger chronograf.Logger
ID chronograf.ID
Path string
}
// Build will return a MultiSourceStore
func (fs *MultiSourceBuilder) Build(db chronograf.SourcesStore) (*memdb.MultiSourcesStore, error) {
stores := []chronograf.SourcesStore{db}
func (fs *MultiSourceBuilder) Build(db chronograf.SourcesStore) (*multistore.SourcesStore, error) {
// These dashboards are those handled from a directory
files := filestore.NewSources(fs.Path, fs.ID, fs.Logger)
stores := []chronograf.SourcesStore{db, files}
if fs.InfluxDBURL != "" {
influxStore := &memdb.SourcesStore{
@ -71,7 +108,7 @@ func (fs *MultiSourceBuilder) Build(db chronograf.SourcesStore) (*memdb.MultiSou
}}
stores = append([]chronograf.SourcesStore{influxStore}, stores...)
}
sources := &memdb.MultiSourcesStore{
sources := &multistore.SourcesStore{
Stores: stores,
}
@ -80,7 +117,7 @@ func (fs *MultiSourceBuilder) Build(db chronograf.SourcesStore) (*memdb.MultiSou
// KapacitorBuilder builds a KapacitorStore
type KapacitorBuilder interface {
Build(chronograf.ServersStore) (*memdb.MultiKapacitorStore, error)
Build(chronograf.ServersStore) (*multistore.KapacitorStore, error)
}
// MultiKapacitorBuilder implements KapacitorBuilder
@ -88,11 +125,19 @@ type MultiKapacitorBuilder struct {
KapacitorURL string
KapacitorUsername string
KapacitorPassword string
Logger chronograf.Logger
ID chronograf.ID
Path string
}
// Build will return a MultiKapacitorStore
func (builder *MultiKapacitorBuilder) Build(db chronograf.ServersStore) (*memdb.MultiKapacitorStore, error) {
stores := []chronograf.ServersStore{db}
// Build will return a multistore facade KapacitorStore over memdb and bolt
func (builder *MultiKapacitorBuilder) Build(db chronograf.ServersStore) (*multistore.KapacitorStore, error) {
// These dashboards are those handled from a directory
files := filestore.NewKapacitors(builder.Path, builder.ID, builder.Logger)
stores := []chronograf.ServersStore{db, files}
if builder.KapacitorURL != "" {
memStore := &memdb.KapacitorStore{
Kapacitor: &chronograf.Server{
@ -106,8 +151,36 @@ func (builder *MultiKapacitorBuilder) Build(db chronograf.ServersStore) (*memdb.
}
stores = append([]chronograf.ServersStore{memStore}, stores...)
}
kapacitors := &memdb.MultiKapacitorStore{
kapacitors := &multistore.KapacitorStore{
Stores: stores,
}
return kapacitors, nil
}
// OrganizationBuilder is responsible for building dashboards
type OrganizationBuilder interface {
Build(chronograf.OrganizationsStore) (*multistore.OrganizationsStore, error)
}
// MultiOrganizationBuilder builds a OrganizationsStore backed by bolt and the filesystem
type MultiOrganizationBuilder struct {
Logger chronograf.Logger
Path string
}
// Build will construct a Organization store of filesystem and db-backed dashboards
func (builder *MultiOrganizationBuilder) Build(db chronograf.OrganizationsStore) (*multistore.OrganizationsStore, error) {
// These organization are those handled from a directory
files := filestore.NewOrganizations(builder.Path, builder.Logger)
// Acts as a front-end to both the bolt org and filesystem orgs.
// The idea here is that these stores form a hierarchy in which each is tried sequentially until
// the operation has success. So, the database is preferred over filesystem
orgs := &multistore.OrganizationsStore{
Stores: []chronograf.OrganizationsStore{
db,
files,
},
}
return orgs, nil
}

View File

@ -7,7 +7,7 @@ import (
"github.com/bouk/httprouter"
"github.com/influxdata/chronograf"
"github.com/influxdata/chronograf/uuid"
idgen "github.com/influxdata/chronograf/id"
)
const (
@ -214,7 +214,7 @@ func (s *Service) NewDashboardCell(w http.ResponseWriter, r *http.Request) {
return
}
ids := uuid.V4{}
ids := &idgen.UUID{}
cid, err := ids.Generate()
if err != nil {
msg := fmt.Sprintf("Error creating cell ID of dashboard %d: %v", id, err)

View File

@ -101,7 +101,7 @@ func (s *Service) NewDashboard(w http.ResponseWriter, r *http.Request) {
return
}
if err := ValidDashboardRequest(&dashboard, fmt.Sprintf("%d", defaultOrg.ID)); err != nil {
if err := ValidDashboardRequest(&dashboard, defaultOrg.ID); err != nil {
invalidData(w, err, s.Logger)
return
}
@ -168,7 +168,7 @@ func (s *Service) ReplaceDashboard(w http.ResponseWriter, r *http.Request) {
return
}
if err := ValidDashboardRequest(&req, fmt.Sprintf("%d", defaultOrg.ID)); err != nil {
if err := ValidDashboardRequest(&req, defaultOrg.ID); err != nil {
invalidData(w, err, s.Logger)
return
}
@ -215,7 +215,7 @@ func (s *Service) UpdateDashboard(w http.ResponseWriter, r *http.Request) {
unknownErrorWithMessage(w, err, s.Logger)
return
}
if err := ValidDashboardRequest(&req, fmt.Sprintf("%d", defaultOrg.ID)); err != nil {
if err := ValidDashboardRequest(&req, defaultOrg.ID); err != nil {
invalidData(w, err, s.Logger)
return
}

View File

@ -87,7 +87,7 @@ func (s *Service) NewKapacitor(w http.ResponseWriter, r *http.Request) {
return
}
if err := req.Valid(fmt.Sprintf("%d", defaultOrg.ID)); err != nil {
if err := req.Valid(defaultOrg.ID); err != nil {
invalidData(w, err, s.Logger)
return
}

View File

@ -96,12 +96,7 @@ func (s *Service) UpdateMe(auth oauth2.Authenticator) func(http.ResponseWriter,
}
// validate that the organization exists
orgID, err := parseOrganizationID(req.Organization)
if err != nil {
Error(w, http.StatusInternalServerError, err.Error(), s.Logger)
return
}
_, err = s.Store.Organizations(serverCtx).Get(serverCtx, chronograf.OrganizationQuery{ID: &orgID})
_, err = s.Store.Organizations(serverCtx).Get(serverCtx, chronograf.OrganizationQuery{ID: &req.Organization})
if err != nil {
Error(w, http.StatusBadRequest, err.Error(), s.Logger)
return
@ -121,7 +116,7 @@ func (s *Service) UpdateMe(auth oauth2.Authenticator) func(http.ResponseWriter,
unknownErrorWithMessage(w, err, s.Logger)
return
}
p.Organization = fmt.Sprintf("%d", defaultOrg.ID)
p.Organization = defaultOrg.ID
}
scheme, err := getScheme(ctx)
if err != nil {
@ -212,7 +207,7 @@ func (s *Service) Me(w http.ResponseWriter, r *http.Request) {
unknownErrorWithMessage(w, err, s.Logger)
return
}
p.Organization = fmt.Sprintf("%d", defaultOrg.ID)
p.Organization = defaultOrg.ID
}
usr, err := s.Store.Users(serverCtx).Get(serverCtx, chronograf.UserQuery{
@ -236,7 +231,7 @@ func (s *Service) Me(w http.ResponseWriter, r *http.Request) {
if defaultOrg.Public || usr.SuperAdmin == true {
// If the default organization is public, or the user is a super admin
// they will always have a role in the default organization
defaultOrgID := fmt.Sprintf("%d", defaultOrg.ID)
defaultOrgID := defaultOrg.ID
if !hasRoleInDefaultOrganization(usr, defaultOrgID) {
usr.Roles = append(usr.Roles, chronograf.Role{
Organization: defaultOrgID,
@ -254,12 +249,7 @@ func (s *Service) Me(w http.ResponseWriter, r *http.Request) {
Error(w, http.StatusForbidden, "This organization is private. To gain access, you must be explicitly added by an administrator.", s.Logger)
return
}
orgID, err := parseOrganizationID(p.Organization)
if err != nil {
unknownErrorWithMessage(w, err, s.Logger)
return
}
currentOrg, err := s.Store.Organizations(serverCtx).Get(serverCtx, chronograf.OrganizationQuery{ID: &orgID})
currentOrg, err := s.Store.Organizations(serverCtx).Get(serverCtx, chronograf.OrganizationQuery{ID: &p.Organization})
if err == chronograf.ErrOrganizationNotFound {
// The intent is to force a the user to go through another auth flow
Error(w, http.StatusForbidden, "user's current organization was not found", s.Logger)
@ -301,7 +291,7 @@ func (s *Service) Me(w http.ResponseWriter, r *http.Request) {
{
Name: defaultOrg.DefaultRole,
// This is the ID of the default organization
Organization: fmt.Sprintf("%d", defaultOrg.ID),
Organization: defaultOrg.ID,
},
},
// TODO(desa): this needs a better name
@ -320,12 +310,7 @@ func (s *Service) Me(w http.ResponseWriter, r *http.Request) {
unknownErrorWithMessage(w, err, s.Logger)
return
}
orgID, err := parseOrganizationID(p.Organization)
if err != nil {
unknownErrorWithMessage(w, err, s.Logger)
return
}
currentOrg, err := s.Store.Organizations(serverCtx).Get(serverCtx, chronograf.OrganizationQuery{ID: &orgID})
currentOrg, err := s.Store.Organizations(serverCtx).Get(serverCtx, chronograf.OrganizationQuery{ID: &p.Organization})
if err != nil {
unknownErrorWithMessage(w, err, s.Logger)
return
@ -376,8 +361,7 @@ func (s *Service) usersOrganizations(ctx context.Context, u *chronograf.User) ([
orgs := []chronograf.Organization{}
for orgID, _ := range orgIDs {
id, err := parseOrganizationID(orgID)
org, err := s.Store.Organizations(ctx).Get(ctx, chronograf.OrganizationQuery{ID: &id})
org, err := s.Store.Organizations(ctx).Get(ctx, chronograf.OrganizationQuery{ID: &orgID})
if err != nil {
return nil, err
}

View File

@ -59,7 +59,7 @@ func TestService_Me(t *testing.T) {
OrganizationsStore: &mocks.OrganizationsStore{
DefaultOrganizationF: func(ctx context.Context) (*chronograf.Organization, error) {
return &chronograf.Organization{
ID: 0,
ID: "0",
Name: "Default",
DefaultRole: roles.ViewerRoleName,
Public: false,
@ -67,16 +67,16 @@ func TestService_Me(t *testing.T) {
},
GetF: func(ctx context.Context, q chronograf.OrganizationQuery) (*chronograf.Organization, error) {
switch *q.ID {
case 0:
case "0":
return &chronograf.Organization{
ID: 0,
ID: "0",
Name: "Default",
DefaultRole: roles.ViewerRoleName,
Public: false,
}, nil
case 1:
case "1":
return &chronograf.Organization{
ID: 1,
ID: "1",
Name: "The Bad Place",
Public: false,
}, nil
@ -124,7 +124,7 @@ func TestService_Me(t *testing.T) {
OrganizationsStore: &mocks.OrganizationsStore{
DefaultOrganizationF: func(ctx context.Context) (*chronograf.Organization, error) {
return &chronograf.Organization{
ID: 0,
ID: "0",
Name: "Default",
DefaultRole: roles.ViewerRoleName,
Public: false,
@ -132,16 +132,16 @@ func TestService_Me(t *testing.T) {
},
GetF: func(ctx context.Context, q chronograf.OrganizationQuery) (*chronograf.Organization, error) {
switch *q.ID {
case 0:
case "0":
return &chronograf.Organization{
ID: 0,
ID: "0",
Name: "Default",
DefaultRole: roles.ViewerRoleName,
Public: true,
}, nil
case 1:
case "1":
return &chronograf.Organization{
ID: 1,
ID: "1",
Name: "The Bad Place",
Public: true,
}, nil
@ -190,7 +190,7 @@ func TestService_Me(t *testing.T) {
OrganizationsStore: &mocks.OrganizationsStore{
DefaultOrganizationF: func(ctx context.Context) (*chronograf.Organization, error) {
return &chronograf.Organization{
ID: 0,
ID: "0",
Name: "Default",
DefaultRole: roles.ViewerRoleName,
Public: false,
@ -198,16 +198,16 @@ func TestService_Me(t *testing.T) {
},
GetF: func(ctx context.Context, q chronograf.OrganizationQuery) (*chronograf.Organization, error) {
switch *q.ID {
case 0:
case "0":
return &chronograf.Organization{
ID: 0,
ID: "0",
Name: "Default",
DefaultRole: roles.ViewerRoleName,
Public: true,
}, nil
case 1:
case "1":
return &chronograf.Organization{
ID: 1,
ID: "1",
Name: "The Bad Place",
Public: true,
}, nil
@ -255,7 +255,7 @@ func TestService_Me(t *testing.T) {
OrganizationsStore: &mocks.OrganizationsStore{
DefaultOrganizationF: func(ctx context.Context) (*chronograf.Organization, error) {
return &chronograf.Organization{
ID: 0,
ID: "0",
Name: "Default",
DefaultRole: roles.ViewerRoleName,
Public: true,
@ -263,16 +263,16 @@ func TestService_Me(t *testing.T) {
},
GetF: func(ctx context.Context, q chronograf.OrganizationQuery) (*chronograf.Organization, error) {
switch *q.ID {
case 0:
case "0":
return &chronograf.Organization{
ID: 0,
ID: "0",
Name: "Default",
DefaultRole: roles.ViewerRoleName,
Public: true,
}, nil
case 1:
case "1":
return &chronograf.Organization{
ID: 1,
ID: "1",
Name: "The Bad Place",
Public: true,
}, nil
@ -320,7 +320,7 @@ func TestService_Me(t *testing.T) {
OrganizationsStore: &mocks.OrganizationsStore{
DefaultOrganizationF: func(ctx context.Context) (*chronograf.Organization, error) {
return &chronograf.Organization{
ID: 0,
ID: "0",
Name: "Default",
DefaultRole: roles.ViewerRoleName,
Public: true,
@ -328,9 +328,9 @@ func TestService_Me(t *testing.T) {
},
GetF: func(ctx context.Context, q chronograf.OrganizationQuery) (*chronograf.Organization, error) {
switch *q.ID {
case 0:
case "0":
return &chronograf.Organization{
ID: 0,
ID: "0",
Name: "Default",
DefaultRole: roles.ViewerRoleName,
Public: true,
@ -383,7 +383,7 @@ func TestService_Me(t *testing.T) {
OrganizationsStore: &mocks.OrganizationsStore{
DefaultOrganizationF: func(ctx context.Context) (*chronograf.Organization, error) {
return &chronograf.Organization{
ID: 0,
ID: "0",
Name: "The Gnarly Default",
DefaultRole: roles.ViewerRoleName,
Public: true,
@ -391,7 +391,7 @@ func TestService_Me(t *testing.T) {
},
GetF: func(ctx context.Context, q chronograf.OrganizationQuery) (*chronograf.Organization, error) {
return &chronograf.Organization{
ID: 0,
ID: "0",
Name: "The Gnarly Default",
DefaultRole: roles.ViewerRoleName,
Public: true,
@ -445,7 +445,7 @@ func TestService_Me(t *testing.T) {
OrganizationsStore: &mocks.OrganizationsStore{
DefaultOrganizationF: func(ctx context.Context) (*chronograf.Organization, error) {
return &chronograf.Organization{
ID: 0,
ID: "0",
Name: "The Gnarly Default",
DefaultRole: roles.ViewerRoleName,
Public: true,
@ -453,7 +453,7 @@ func TestService_Me(t *testing.T) {
},
GetF: func(ctx context.Context, q chronograf.OrganizationQuery) (*chronograf.Organization, error) {
return &chronograf.Organization{
ID: 0,
ID: "0",
Name: "The Gnarly Default",
DefaultRole: roles.ViewerRoleName,
Public: true,
@ -507,7 +507,7 @@ func TestService_Me(t *testing.T) {
OrganizationsStore: &mocks.OrganizationsStore{
DefaultOrganizationF: func(ctx context.Context) (*chronograf.Organization, error) {
return &chronograf.Organization{
ID: 0,
ID: "0",
Name: "The Gnarly Default",
DefaultRole: roles.ViewerRoleName,
Public: true,
@ -515,7 +515,7 @@ func TestService_Me(t *testing.T) {
},
GetF: func(ctx context.Context, q chronograf.OrganizationQuery) (*chronograf.Organization, error) {
return &chronograf.Organization{
ID: 0,
ID: "0",
Name: "The Gnarly Default",
DefaultRole: roles.ViewerRoleName,
Public: true,
@ -568,13 +568,13 @@ func TestService_Me(t *testing.T) {
OrganizationsStore: &mocks.OrganizationsStore{
DefaultOrganizationF: func(ctx context.Context) (*chronograf.Organization, error) {
return &chronograf.Organization{
ID: 0,
ID: "0",
Public: true,
}, nil
},
GetF: func(ctx context.Context, q chronograf.OrganizationQuery) (*chronograf.Organization, error) {
return &chronograf.Organization{
ID: 0,
ID: "0",
Name: "The Bad Place",
Public: true,
}, nil
@ -662,7 +662,7 @@ func TestService_Me(t *testing.T) {
OrganizationsStore: &mocks.OrganizationsStore{
DefaultOrganizationF: func(ctx context.Context) (*chronograf.Organization, error) {
return &chronograf.Organization{
ID: 0,
ID: "0",
Name: "The Bad Place",
DefaultRole: roles.MemberRoleName,
Public: false,
@ -789,7 +789,7 @@ func TestService_UpdateMe(t *testing.T) {
OrganizationsStore: &mocks.OrganizationsStore{
DefaultOrganizationF: func(ctx context.Context) (*chronograf.Organization, error) {
return &chronograf.Organization{
ID: 0,
ID: "0",
Name: "Default",
DefaultRole: roles.AdminRoleName,
Public: true,
@ -800,16 +800,16 @@ func TestService_UpdateMe(t *testing.T) {
return nil, fmt.Errorf("Invalid organization query: missing ID")
}
switch *q.ID {
case 0:
case "0":
return &chronograf.Organization{
ID: 0,
ID: "0",
Name: "Default",
DefaultRole: roles.AdminRoleName,
Public: true,
}, nil
case 1337:
case "1337":
return &chronograf.Organization{
ID: 1337,
ID: "1337",
Name: "The ShillBillThrilliettas",
Public: true,
}, nil
@ -863,7 +863,7 @@ func TestService_UpdateMe(t *testing.T) {
OrganizationsStore: &mocks.OrganizationsStore{
DefaultOrganizationF: func(ctx context.Context) (*chronograf.Organization, error) {
return &chronograf.Organization{
ID: 0,
ID: "0",
Name: "Default",
DefaultRole: roles.EditorRoleName,
Public: true,
@ -874,15 +874,15 @@ func TestService_UpdateMe(t *testing.T) {
return nil, fmt.Errorf("Invalid organization query: missing ID")
}
switch *q.ID {
case 1337:
case "1337":
return &chronograf.Organization{
ID: 1337,
ID: "1337",
Name: "The ThrillShilliettos",
Public: false,
}, nil
case 0:
case "0":
return &chronograf.Organization{
ID: 0,
ID: "0",
Name: "Default",
DefaultRole: roles.EditorRoleName,
Public: true,
@ -938,7 +938,7 @@ func TestService_UpdateMe(t *testing.T) {
OrganizationsStore: &mocks.OrganizationsStore{
DefaultOrganizationF: func(ctx context.Context) (*chronograf.Organization, error) {
return &chronograf.Organization{
ID: 0,
ID: "0",
}, nil
},
GetF: func(ctx context.Context, q chronograf.OrganizationQuery) (*chronograf.Organization, error) {
@ -946,7 +946,7 @@ func TestService_UpdateMe(t *testing.T) {
return nil, fmt.Errorf("Invalid organization query: missing ID")
}
return &chronograf.Organization{
ID: 1337,
ID: "1337",
Name: "The ShillBillThrilliettas",
Public: true,
}, nil
@ -999,7 +999,7 @@ func TestService_UpdateMe(t *testing.T) {
OrganizationsStore: &mocks.OrganizationsStore{
DefaultOrganizationF: func(ctx context.Context) (*chronograf.Organization, error) {
return &chronograf.Organization{
ID: 0,
ID: "0",
}, nil
},
GetF: func(ctx context.Context, q chronograf.OrganizationQuery) (*chronograf.Organization, error) {

View File

@ -5,7 +5,6 @@ import (
"encoding/json"
"fmt"
"net/http"
"strconv"
"github.com/bouk/httprouter"
"github.com/influxdata/chronograf"
@ -13,10 +12,6 @@ import (
"github.com/influxdata/chronograf/roles"
)
func parseOrganizationID(id string) (uint64, error) {
return strconv.ParseUint(id, 10, 64)
}
type organizationRequest struct {
Name string `json:"name"`
DefaultRole string `json:"defaultRole"`
@ -68,7 +63,7 @@ func newOrganizationResponse(o *chronograf.Organization) *organizationResponse {
return &organizationResponse{
Organization: *o,
Links: selfLinks{
Self: fmt.Sprintf("/chronograf/v1/organizations/%d", o.ID),
Self: fmt.Sprintf("/chronograf/v1/organizations/%s", o.ID),
},
}
}
@ -144,15 +139,14 @@ func (s *Service) NewOrganization(w http.ResponseWriter, r *http.Request) {
return
}
orgID := fmt.Sprintf("%d", res.ID)
user.Roles = []chronograf.Role{
{
Organization: orgID,
Organization: res.ID,
Name: roles.AdminRoleName,
},
}
orgCtx := context.WithValue(ctx, organizations.ContextKey, orgID)
orgCtx := context.WithValue(ctx, organizations.ContextKey, res.ID)
_, err = s.Store.Users(orgCtx).Add(orgCtx, user)
if err != nil {
// Best attempt at cleanup the organization if there were any errors adding user to org
@ -171,12 +165,7 @@ func (s *Service) NewOrganization(w http.ResponseWriter, r *http.Request) {
func (s *Service) OrganizationID(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
idStr := httprouter.GetParamFromContext(ctx, "id")
id, err := parseOrganizationID(idStr)
if err != nil {
Error(w, http.StatusBadRequest, fmt.Sprintf("invalid organization id: %s", err.Error()), s.Logger)
return
}
id := httprouter.GetParamFromContext(ctx, "id")
org, err := s.Store.Organizations(ctx).Get(ctx, chronograf.OrganizationQuery{ID: &id})
if err != nil {
@ -202,12 +191,7 @@ func (s *Service) UpdateOrganization(w http.ResponseWriter, r *http.Request) {
}
ctx := r.Context()
idStr := httprouter.GetParamFromContext(ctx, "id")
id, err := parseOrganizationID(idStr)
if err != nil {
Error(w, http.StatusBadRequest, fmt.Sprintf("invalid organization id: %s", err.Error()), s.Logger)
return
}
id := httprouter.GetParamFromContext(ctx, "id")
org, err := s.Store.Organizations(ctx).Get(ctx, chronograf.OrganizationQuery{ID: &id})
if err != nil {
@ -242,12 +226,7 @@ func (s *Service) UpdateOrganization(w http.ResponseWriter, r *http.Request) {
// RemoveOrganization removes an organization in the organizations store
func (s *Service) RemoveOrganization(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
idStr := httprouter.GetParamFromContext(ctx, "id")
id, err := parseOrganizationID(idStr)
if err != nil {
Error(w, http.StatusBadRequest, fmt.Sprintf("invalid organization id: %s", err.Error()), s.Logger)
return
}
id := httprouter.GetParamFromContext(ctx, "id")
org, err := s.Store.Organizations(ctx).Get(ctx, chronograf.OrganizationQuery{ID: &id})
if err != nil {

View File

@ -50,9 +50,9 @@ func TestService_OrganizationID(t *testing.T) {
OrganizationsStore: &mocks.OrganizationsStore{
GetF: func(ctx context.Context, q chronograf.OrganizationQuery) (*chronograf.Organization, error) {
switch *q.ID {
case 1337:
case "1337":
return &chronograf.Organization{
ID: 1337,
ID: "1337",
Name: "The Good Place",
Public: false,
}, nil
@ -139,12 +139,12 @@ func TestService_Organizations(t *testing.T) {
AllF: func(ctx context.Context) ([]chronograf.Organization, error) {
return []chronograf.Organization{
chronograf.Organization{
ID: 1337,
ID: "1337",
Name: "The Good Place",
Public: false,
},
chronograf.Organization{
ID: 100,
ID: "100",
Name: "The Bad Place",
Public: false,
},
@ -228,7 +228,7 @@ func TestService_UpdateOrganization(t *testing.T) {
},
GetF: func(ctx context.Context, q chronograf.OrganizationQuery) (*chronograf.Organization, error) {
return &chronograf.Organization{
ID: 1337,
ID: "1337",
Name: "The Good Place",
DefaultRole: roles.ViewerRoleName,
Public: false,
@ -262,7 +262,7 @@ func TestService_UpdateOrganization(t *testing.T) {
},
GetF: func(ctx context.Context, q chronograf.OrganizationQuery) (*chronograf.Organization, error) {
return &chronograf.Organization{
ID: 0,
ID: "0",
Name: "The Good Place",
DefaultRole: roles.ViewerRoleName,
Public: true,
@ -294,7 +294,7 @@ func TestService_UpdateOrganization(t *testing.T) {
},
GetF: func(ctx context.Context, q chronograf.OrganizationQuery) (*chronograf.Organization, error) {
return &chronograf.Organization{
ID: 1337,
ID: "1337",
Name: "The Good Place",
DefaultRole: roles.ViewerRoleName,
Public: true,
@ -328,7 +328,7 @@ func TestService_UpdateOrganization(t *testing.T) {
},
GetF: func(ctx context.Context, q chronograf.OrganizationQuery) (*chronograf.Organization, error) {
return &chronograf.Organization{
ID: 1337,
ID: "1337",
Name: "The Good Place",
DefaultRole: roles.MemberRoleName,
Public: false,
@ -475,9 +475,9 @@ func TestService_RemoveOrganization(t *testing.T) {
},
GetF: func(ctx context.Context, q chronograf.OrganizationQuery) (*chronograf.Organization, error) {
switch *q.ID {
case 1337:
case "1337":
return &chronograf.Organization{
ID: 1337,
ID: "1337",
Name: "The Good Place",
}, nil
default:
@ -573,7 +573,7 @@ func TestService_NewOrganization(t *testing.T) {
OrganizationsStore: &mocks.OrganizationsStore{
AddF: func(ctx context.Context, o *chronograf.Organization) (*chronograf.Organization, error) {
return &chronograf.Organization{
ID: 1337,
ID: "1337",
Name: "The Good Place",
Public: false,
}, nil
@ -612,7 +612,7 @@ func TestService_NewOrganization(t *testing.T) {
OrganizationsStore: &mocks.OrganizationsStore{
AddF: func(ctx context.Context, o *chronograf.Organization) (*chronograf.Organization, error) {
return &chronograf.Organization{
ID: 1337,
ID: "1337",
Name: "The Good Place",
}, nil
},
@ -654,7 +654,7 @@ func TestService_NewOrganization(t *testing.T) {
OrganizationsStore: &mocks.OrganizationsStore{
AddF: func(ctx context.Context, o *chronograf.Organization) (*chronograf.Organization, error) {
return &chronograf.Organization{
ID: 1337,
ID: "1337",
Name: "The Good Place",
}, nil
},

View File

@ -16,10 +16,10 @@ import (
"github.com/influxdata/chronograf"
"github.com/influxdata/chronograf/bolt"
idgen "github.com/influxdata/chronograf/id"
"github.com/influxdata/chronograf/influx"
clog "github.com/influxdata/chronograf/log"
"github.com/influxdata/chronograf/oauth2"
"github.com/influxdata/chronograf/uuid"
client "github.com/influxdata/usage-client/v1"
flags "github.com/jessevdk/go-flags"
"github.com/tylerb/graceful"
@ -54,7 +54,7 @@ type Server struct {
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 (e.g. './chronograf-v1.db')" env:"BOLT_PATH" default:"chronograf-v1.db"`
CannedPath string `short:"c" long:"canned-path" description:"Path to directory of pre-canned application layouts (/usr/share/chronograf/canned)" env:"CANNED_PATH" default:"canned"`
CannedPath string `short:"c" long:"canned-path" description:"Path to directory of pre-canned dashboards and application layouts (/usr/share/chronograf/canned)" env:"CANNED_PATH" default:"canned"`
TokenSecret string `short:"t" long:"token-secret" description:"Secret to sign tokens" env:"TOKEN_SECRET"`
AuthDuration time.Duration `long:"auth-duration" default:"720h" description:"Total duration of cookie life for authentication (in hours). 0 means authentication expires on browser close." env:"AUTH_DURATION"`
@ -122,6 +122,7 @@ func (s *Server) UseHeroku() bool {
return s.TokenSecret != "" && s.HerokuClientID != "" && s.HerokuSecret != ""
}
// UseAuth0 validates the CLI parameters to enable Auth0 oauth support
func (s *Server) UseAuth0() bool {
return s.Auth0ClientID != "" && s.Auth0ClientSecret != ""
}
@ -269,24 +270,52 @@ func (s *Server) NewListener() (net.Listener, error) {
return listener, nil
}
type builders struct {
Layouts LayoutBuilder
Sources SourcesBuilder
Kapacitors KapacitorBuilder
Dashboards DashboardBuilder
Organizations OrganizationBuilder
}
func (s *Server) newBuilders(logger chronograf.Logger) builders {
return builders{
Layouts: &MultiLayoutBuilder{
Logger: logger,
UUID: &idgen.UUID{},
CannedPath: s.CannedPath,
},
Dashboards: &MultiDashboardBuilder{
Logger: logger,
ID: idgen.NewTime(),
Path: s.CannedPath,
},
Sources: &MultiSourceBuilder{
InfluxDBURL: s.InfluxDBURL,
InfluxDBUsername: s.InfluxDBUsername,
InfluxDBPassword: s.InfluxDBPassword,
Logger: logger,
ID: idgen.NewTime(),
Path: s.CannedPath,
},
Kapacitors: &MultiKapacitorBuilder{
KapacitorURL: s.KapacitorURL,
KapacitorUsername: s.KapacitorUsername,
KapacitorPassword: s.KapacitorPassword,
Logger: logger,
ID: idgen.NewTime(),
Path: s.CannedPath,
},
Organizations: &MultiOrganizationBuilder{
Logger: logger,
Path: s.CannedPath,
},
}
}
// Serve starts and runs the chronograf server
func (s *Server) Serve(ctx context.Context) error {
logger := clog.New(clog.ParseLevel(s.LogLevel))
layoutBuilder := &MultiLayoutBuilder{
Logger: logger,
UUID: &uuid.V4{},
CannedPath: s.CannedPath,
}
sourcesBuilder := &MultiSourceBuilder{
InfluxDBURL: s.InfluxDBURL,
InfluxDBUsername: s.InfluxDBUsername,
InfluxDBPassword: s.InfluxDBPassword,
}
kapacitorBuilder := &MultiKapacitorBuilder{
KapacitorURL: s.KapacitorURL,
KapacitorUsername: s.KapacitorUsername,
KapacitorPassword: s.KapacitorPassword,
}
_, err := NewCustomLinks(s.CustomLinks)
if err != nil {
logger.
@ -295,7 +324,7 @@ func (s *Server) Serve(ctx context.Context) error {
Error(err)
return err
}
service := openService(ctx, s, layoutBuilder, sourcesBuilder, kapacitorBuilder, logger)
service := openService(ctx, s.BuildInfo, s.BoltPath, s.newBuilders(logger), logger, s.useAuth())
if err := service.HandleNewSources(ctx, s.NewSources); err != nil {
logger.
WithField("component", "server").
@ -390,18 +419,18 @@ func (s *Server) Serve(ctx context.Context) error {
return nil
}
func openService(ctx context.Context, s *Server, lBuilder LayoutBuilder, sBuilder SourcesBuilder, kapBuilder KapacitorBuilder, logger chronograf.Logger) Service {
func openService(ctx context.Context, buildInfo chronograf.BuildInfo, boltPath string, builder builders, logger chronograf.Logger, useAuth bool) Service {
db := bolt.NewClient()
db.Path = s.BoltPath
db.Path = boltPath
if err := db.Open(ctx, logger, s.BuildInfo, bolt.WithBackup()); err != nil {
if err := db.Open(ctx, logger, buildInfo, bolt.WithBackup()); err != nil {
logger.
WithField("component", "boltstore").
Error(err)
os.Exit(1)
}
layouts, err := lBuilder.Build(db.LayoutsStore)
layouts, err := builder.Layouts.Build(db.LayoutsStore)
if err != nil {
logger.
WithField("component", "LayoutsStore").
@ -409,7 +438,14 @@ func openService(ctx context.Context, s *Server, lBuilder LayoutBuilder, sBuilde
os.Exit(1)
}
sources, err := sBuilder.Build(db.SourcesStore)
dashboards, err := builder.Dashboards.Build(db.DashboardsStore)
if err != nil {
logger.
WithField("component", "DashboardsStore").
Error("Unable to construct a MultiDashboardsStore", err)
os.Exit(1)
}
sources, err := builder.Sources.Build(db.SourcesStore)
if err != nil {
logger.
WithField("component", "SourcesStore").
@ -417,7 +453,7 @@ func openService(ctx context.Context, s *Server, lBuilder LayoutBuilder, sBuilde
os.Exit(1)
}
kapacitors, err := kapBuilder.Build(db.ServersStore)
kapacitors, err := builder.Kapacitors.Build(db.ServersStore)
if err != nil {
logger.
WithField("component", "KapacitorStore").
@ -425,19 +461,27 @@ func openService(ctx context.Context, s *Server, lBuilder LayoutBuilder, sBuilde
os.Exit(1)
}
organizations, err := builder.Organizations.Build(db.OrganizationsStore)
if err != nil {
logger.
WithField("component", "OrganizationsStore").
Error("Unable to construct a MultiOrganizationStore", err)
os.Exit(1)
}
return Service{
TimeSeriesClient: &InfluxClient{},
Store: &Store{
LayoutsStore: layouts,
DashboardsStore: dashboards,
SourcesStore: sources,
ServersStore: kapacitors,
OrganizationsStore: organizations,
UsersStore: db.UsersStore,
OrganizationsStore: db.OrganizationsStore,
LayoutsStore: layouts,
DashboardsStore: db.DashboardsStore,
ConfigStore: db.ConfigStore,
},
Logger: logger,
UseAuth: s.useAuth(),
UseAuth: useAuth,
Databases: &influx.Client{Logger: logger},
}
}

View File

@ -78,7 +78,7 @@ func (s *Service) NewSource(w http.ResponseWriter, r *http.Request) {
return
}
if err := ValidSourceRequest(&src, fmt.Sprintf("%d", defaultOrg.ID)); err != nil {
if err := ValidSourceRequest(&src, defaultOrg.ID); err != nil {
invalidData(w, err, s.Logger)
return
}
@ -271,7 +271,7 @@ func (s *Service) UpdateSource(w http.ResponseWriter, r *http.Request) {
return
}
if err := ValidSourceRequest(&src, fmt.Sprintf("%d", defaultOrg.ID)); err != nil {
if err := ValidSourceRequest(&src, defaultOrg.ID); err != nil {
invalidData(w, err, s.Logger)
return
}
@ -346,7 +346,7 @@ func (s *Service) HandleNewSources(ctx context.Context, input string) error {
}
for _, sk := range srcsKaps {
if err := ValidSourceRequest(&sk.Source, fmt.Sprintf("%d", defaultOrg.ID)); err != nil {
if err := ValidSourceRequest(&sk.Source, defaultOrg.ID); err != nil {
return err
}
// Add any new sources and kapacitors as specified via server flag

View File

@ -237,7 +237,7 @@ func TestStore_OrganizationsAdd(t *testing.T) {
OrganizationsStore chronograf.OrganizationsStore
}
type args struct {
orgID uint64
orgID string
serverContext bool
organization string
user *chronograf.User
@ -259,7 +259,7 @@ func TestStore_OrganizationsAdd(t *testing.T) {
OrganizationsStore: &mocks.OrganizationsStore{
GetF: func(ctx context.Context, q chronograf.OrganizationQuery) (*chronograf.Organization, error) {
return &chronograf.Organization{
ID: 21,
ID: "21",
Name: "my sweet name",
DefaultRole: "viewer",
}, nil
@ -268,11 +268,11 @@ func TestStore_OrganizationsAdd(t *testing.T) {
},
args: args{
serverContext: true,
orgID: 21,
orgID: "21",
},
wants: wants{
organization: &chronograf.Organization{
ID: 21,
ID: "21",
Name: "my sweet name",
DefaultRole: "viewer",
},
@ -284,7 +284,7 @@ func TestStore_OrganizationsAdd(t *testing.T) {
OrganizationsStore: &mocks.OrganizationsStore{
GetF: func(ctx context.Context, q chronograf.OrganizationQuery) (*chronograf.Organization, error) {
return &chronograf.Organization{
ID: 21,
ID: "21",
Name: "my sweet name",
DefaultRole: "viewer",
}, nil
@ -299,11 +299,11 @@ func TestStore_OrganizationsAdd(t *testing.T) {
Scheme: "oauth2",
SuperAdmin: true,
},
orgID: 21,
orgID: "21",
},
wants: wants{
organization: &chronograf.Organization{
ID: 21,
ID: "21",
Name: "my sweet name",
DefaultRole: "viewer",
},
@ -315,7 +315,7 @@ func TestStore_OrganizationsAdd(t *testing.T) {
OrganizationsStore: &mocks.OrganizationsStore{
GetF: func(ctx context.Context, q chronograf.OrganizationQuery) (*chronograf.Organization, error) {
return &chronograf.Organization{
ID: 21,
ID: "21",
Name: "my sweet name",
DefaultRole: "viewer",
}, nil
@ -329,7 +329,7 @@ func TestStore_OrganizationsAdd(t *testing.T) {
Provider: "github",
Scheme: "oauth2",
},
orgID: 21,
orgID: "21",
},
wants: wants{
err: true,
@ -341,7 +341,7 @@ func TestStore_OrganizationsAdd(t *testing.T) {
OrganizationsStore: &mocks.OrganizationsStore{
GetF: func(ctx context.Context, q chronograf.OrganizationQuery) (*chronograf.Organization, error) {
return &chronograf.Organization{
ID: 21,
ID: "22",
Name: "my sweet name",
DefaultRole: "viewer",
}, nil
@ -355,12 +355,12 @@ func TestStore_OrganizationsAdd(t *testing.T) {
Provider: "github",
Scheme: "oauth2",
},
organization: "21",
orgID: 21,
organization: "22",
orgID: "22",
},
wants: wants{
organization: &chronograf.Organization{
ID: 21,
ID: "22",
Name: "my sweet name",
DefaultRole: "viewer",
},
@ -372,7 +372,7 @@ func TestStore_OrganizationsAdd(t *testing.T) {
OrganizationsStore: &mocks.OrganizationsStore{
GetF: func(ctx context.Context, q chronograf.OrganizationQuery) (*chronograf.Organization, error) {
return &chronograf.Organization{
ID: 22,
ID: "22",
Name: "my sweet name",
DefaultRole: "viewer",
}, nil
@ -387,7 +387,7 @@ func TestStore_OrganizationsAdd(t *testing.T) {
Scheme: "oauth2",
},
organization: "21",
orgID: 21,
orgID: "21",
},
wants: wants{
err: true,

View File

@ -7,7 +7,7 @@ import (
"github.com/bouk/httprouter"
"github.com/influxdata/chronograf"
"github.com/influxdata/chronograf/uuid"
idgen "github.com/influxdata/chronograf/id"
)
// ValidTemplateRequest checks if the request sent to the server is the correct format.
@ -111,7 +111,7 @@ func (s *Service) NewTemplate(w http.ResponseWriter, r *http.Request) {
return
}
ids := uuid.V4{}
ids := idgen.UUID{}
tid, err := ids.Generate()
if err != nil {
msg := fmt.Sprintf("Error creating template ID for dashboard %d: %v", id, err)

View File

@ -54,9 +54,6 @@ func (r *userRequest) ValidRoles() error {
if r.Organization == "" {
return fmt.Errorf("no organization was provided")
}
if _, err := parseOrganizationID(r.Organization); err != nil {
return fmt.Errorf("failed to parse organization ID: %v", err)
}
if _, ok := orgs[r.Organization]; ok {
return fmt.Errorf("duplicate organization %q in roles", r.Organization)
}

View File

@ -1156,25 +1156,6 @@ func TestUserRequest_ValidCreate(t *testing.T) {
wantErr: false,
err: nil,
},
{
name: "Invalid - bad organization",
args: args{
u: &userRequest{
ID: 1337,
Name: "billietta",
Provider: "auth0",
Scheme: "oauth2",
Roles: []chronograf.Role{
{
Name: roles.EditorRoleName,
Organization: "l", // this is the character L not integer One
},
},
},
},
wantErr: true,
err: fmt.Errorf("failed to parse organization ID: strconv.ParseUint: parsing \"l\": invalid syntax"),
},
{
name: "Invalid Name missing",
args: args{
@ -1319,25 +1300,6 @@ func TestUserRequest_ValidUpdate(t *testing.T) {
wantErr: true,
err: fmt.Errorf("No Roles to update"),
},
{
name: "Invalid - bad organization",
args: args{
u: &userRequest{
ID: 1337,
Name: "billietta",
Provider: "auth0",
Scheme: "oauth2",
Roles: []chronograf.Role{
{
Name: roles.EditorRoleName,
Organization: "l", // this is the character L not integer One
},
},
},
},
wantErr: true,
err: fmt.Errorf("failed to parse organization ID: strconv.ParseUint: parsing \"l\": invalid syntax"),
},
{
name: "Invalid - bad role name",
args: args{

View File

@ -12,4 +12,4 @@ export const USER_ROLES = [
{name: ADMIN_ROLE},
]
export const DEFAULT_ORG_ID = '0'
export const DEFAULT_ORG_ID = 'default'

View File

@ -1,11 +0,0 @@
package uuid
import uuid "github.com/satori/go.uuid"
// V4 implements chronograf.ID
type V4 struct{}
// Generate creates a UUID v4 string
func (i *V4) Generate() (string, error) {
return uuid.NewV4().String(), nil
}