Add OrganizationConfigStore & refactor org config to be per org
As previously implemented, OrganizationConfig was a global object. This refactor adds the organization id to context for every request, even when auth is disabled, so that org id can be used to get/update an organization config. Along those lines, this also removes OrganizationConfigStore .Initialize and replaces .Get with .FindOrCreate, handling the creation of organization configs upon first attempted access. Co-authored-by: Jared Scheib <jared.scheib@gmail.com>pull/10616/head
parent
9909bed41f
commit
429fe214e6
|
@ -33,15 +33,16 @@ type Client struct {
|
||||||
Now func() time.Time
|
Now func() time.Time
|
||||||
LayoutIDs chronograf.ID
|
LayoutIDs chronograf.ID
|
||||||
|
|
||||||
BuildStore *BuildStore
|
BuildStore *BuildStore
|
||||||
SourcesStore *SourcesStore
|
SourcesStore *SourcesStore
|
||||||
ServersStore *ServersStore
|
ServersStore *ServersStore
|
||||||
LayoutsStore *LayoutsStore
|
LayoutsStore *LayoutsStore
|
||||||
DashboardsStore *DashboardsStore
|
DashboardsStore *DashboardsStore
|
||||||
UsersStore *UsersStore
|
UsersStore *UsersStore
|
||||||
OrganizationsStore *OrganizationsStore
|
OrganizationsStore *OrganizationsStore
|
||||||
ConfigStore *ConfigStore
|
ConfigStore *ConfigStore
|
||||||
MappingsStore *MappingsStore
|
MappingsStore *MappingsStore
|
||||||
|
OrganizationConfigStore *OrganizationConfigStore
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewClient initializes all stores
|
// NewClient initializes all stores
|
||||||
|
@ -62,6 +63,7 @@ func NewClient() *Client {
|
||||||
c.OrganizationsStore = &OrganizationsStore{client: c}
|
c.OrganizationsStore = &OrganizationsStore{client: c}
|
||||||
c.ConfigStore = &ConfigStore{client: c}
|
c.ConfigStore = &ConfigStore{client: c}
|
||||||
c.MappingsStore = &MappingsStore{client: c}
|
c.MappingsStore = &MappingsStore{client: c}
|
||||||
|
c.OrganizationConfigStore = &OrganizationConfigStore{client: c}
|
||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -161,6 +163,10 @@ func (c *Client) initialize(ctx context.Context) error {
|
||||||
if _, err := tx.CreateBucketIfNotExists(MappingsBucket); err != nil {
|
if _, err := tx.CreateBucketIfNotExists(MappingsBucket); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
// Always create OrganizationConfig bucket.
|
||||||
|
if _, err := tx.CreateBucketIfNotExists(OrganizationConfigBucket); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -197,6 +203,9 @@ func (c *Client) migrate(ctx context.Context, build chronograf.BuildInfo) error
|
||||||
if err := c.MappingsStore.Migrate(ctx); err != nil {
|
if err := c.MappingsStore.Migrate(ctx); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if err := c.OrganizationConfigStore.Migrate(ctx); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
MigrateAll(c)
|
MigrateAll(c)
|
||||||
}
|
}
|
||||||
|
|
|
@ -749,9 +749,9 @@ func UnmarshalConfigPB(data []byte, c *Config) error {
|
||||||
|
|
||||||
// MarshalOrganizationConfig encodes a config to binary protobuf format.
|
// MarshalOrganizationConfig encodes a config to binary protobuf format.
|
||||||
func MarshalOrganizationConfig(c *chronograf.OrganizationConfig) ([]byte, error) {
|
func MarshalOrganizationConfig(c *chronograf.OrganizationConfig) ([]byte, error) {
|
||||||
var lv chronograf.LogViewerConfig
|
columns := make([]*LogViewerColumn, len(c.LogViewer.Columns))
|
||||||
columns := make([]*LogViewerColumn, len(lv.Columns))
|
|
||||||
for i, column := range lv.Columns {
|
for i, column := range c.LogViewer.Columns {
|
||||||
encodings := make([]*ColumnEncoding, len(column.Encodings))
|
encodings := make([]*ColumnEncoding, len(column.Encodings))
|
||||||
|
|
||||||
for j, e := range column.Encodings {
|
for j, e := range column.Encodings {
|
||||||
|
@ -768,7 +768,9 @@ func MarshalOrganizationConfig(c *chronograf.OrganizationConfig) ([]byte, error)
|
||||||
Encodings: encodings,
|
Encodings: encodings,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return MarshalOrganizationConfigPB(&OrganizationConfig{
|
return MarshalOrganizationConfigPB(&OrganizationConfig{
|
||||||
|
OrganizationID: c.OrganizationID,
|
||||||
LogViewer: &LogViewerConfig{
|
LogViewer: &LogViewerConfig{
|
||||||
Columns: columns,
|
Columns: columns,
|
||||||
},
|
},
|
||||||
|
@ -783,13 +785,22 @@ func MarshalOrganizationConfigPB(c *OrganizationConfig) ([]byte, error) {
|
||||||
// UnmarshalOrganizationConfig decodes a config from binary protobuf data.
|
// UnmarshalOrganizationConfig decodes a config from binary protobuf data.
|
||||||
func UnmarshalOrganizationConfig(data []byte, c *chronograf.OrganizationConfig) error {
|
func UnmarshalOrganizationConfig(data []byte, c *chronograf.OrganizationConfig) error {
|
||||||
var pb OrganizationConfig
|
var pb OrganizationConfig
|
||||||
|
|
||||||
if err := UnmarshalOrganizationConfigPB(data, &pb); err != nil {
|
if err := UnmarshalOrganizationConfigPB(data, &pb); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if pb.OrganizationID == "" {
|
||||||
|
return fmt.Errorf("Organization ID on organization config is nil")
|
||||||
|
}
|
||||||
if pb.LogViewer == nil {
|
if pb.LogViewer == nil {
|
||||||
return fmt.Errorf("Log Viewer config is nil")
|
return fmt.Errorf("Log Viewer config is nil")
|
||||||
}
|
}
|
||||||
|
if pb.LogViewer.Columns == nil {
|
||||||
|
return fmt.Errorf("Log Viewer config Columns is nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
c.OrganizationID = pb.OrganizationID
|
||||||
|
|
||||||
columns := make([]chronograf.LogViewerColumn, len(pb.LogViewer.Columns))
|
columns := make([]chronograf.LogViewerColumn, len(pb.LogViewer.Columns))
|
||||||
|
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -215,7 +215,8 @@ message AuthConfig {
|
||||||
}
|
}
|
||||||
|
|
||||||
message OrganizationConfig {
|
message OrganizationConfig {
|
||||||
LogViewerConfig LogViewer = 1; // LogViewer is the organization configuration for log viewer
|
string OrganizationID = 1; // OrganizationID is the ID of the organization this config belogs to
|
||||||
|
LogViewerConfig LogViewer = 2; // LogViewer is the organization configuration for log viewer
|
||||||
}
|
}
|
||||||
|
|
||||||
message LogViewerConfig {
|
message LogViewerConfig {
|
||||||
|
|
|
@ -12,145 +12,26 @@ import (
|
||||||
// Ensure OrganizationConfigStore implements chronograf.OrganizationConfigStore.
|
// Ensure OrganizationConfigStore implements chronograf.OrganizationConfigStore.
|
||||||
var _ chronograf.OrganizationConfigStore = &OrganizationConfigStore{}
|
var _ chronograf.OrganizationConfigStore = &OrganizationConfigStore{}
|
||||||
|
|
||||||
// ConfigBucket is used to store chronograf application state
|
// OrganizationConfigBucket is used to store chronograf organization configurations
|
||||||
var ConfigBucket = []byte("ConfigV1")
|
var OrganizationConfigBucket = []byte("OrganizationConfigV1")
|
||||||
|
|
||||||
// configID is the boltDB key where the configuration object is stored
|
// OrganizationConfigStore uses bolt to store and retrieve organization configurations
|
||||||
var configID = []byte("config/v1")
|
|
||||||
|
|
||||||
// OrganizationConfigStore uses bolt to store and retrieve global
|
|
||||||
// application configuration
|
|
||||||
type OrganizationConfigStore struct {
|
type OrganizationConfigStore struct {
|
||||||
client *Client
|
client *Client
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *OrganizationConfigStore) Migrate(ctx context.Context) error {
|
func (s *OrganizationConfigStore) Migrate(ctx context.Context) error {
|
||||||
if _, err := s.Get(ctx); err != nil {
|
|
||||||
return s.Initialize(ctx)
|
|
||||||
}
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *OrganizationConfigStore) Initialize(ctx context.Context) error {
|
// FindOrCreate gets an OrganizationConfig from the store or creates one if none exists for this organization
|
||||||
cfg := chronograf.OrganizationConfig{
|
func (s *OrganizationConfigStore) FindOrCreate(ctx context.Context, orgID string) (*chronograf.OrganizationConfig, error) {
|
||||||
OrganizationConfig: chronograf.OrganizationConfig{
|
|
||||||
|
|
||||||
LogViewer: chronograf.LogViewer{
|
|
||||||
Columns: []chronograf.LogViewerColumn{
|
|
||||||
{
|
|
||||||
Name: "time",
|
|
||||||
Position: 0,
|
|
||||||
Encodings: []chronograf.ColumnEncoding{
|
|
||||||
{
|
|
||||||
Type: "visibility",
|
|
||||||
Value: "hidden",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "severity",
|
|
||||||
Position: 1,
|
|
||||||
Encodings: []chronograf.ColumnEncoding{
|
|
||||||
|
|
||||||
{
|
|
||||||
Type: "visibility",
|
|
||||||
Value: "visible",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Type: "label",
|
|
||||||
Value: "icon",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Type: "label",
|
|
||||||
Value: "text",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "timestamp",
|
|
||||||
Position: 2,
|
|
||||||
Encodings: []chronograf.ColumnEncoding{
|
|
||||||
|
|
||||||
{
|
|
||||||
Type: "visibility",
|
|
||||||
Value: "visible",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "message",
|
|
||||||
Position: 3,
|
|
||||||
Encodings: []chronograf.ColumnEncoding{
|
|
||||||
|
|
||||||
{
|
|
||||||
Type: "visibility",
|
|
||||||
Value: "visible",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "facility",
|
|
||||||
Position: 4,
|
|
||||||
Encodings: []chronograf.ColumnEncoding{
|
|
||||||
|
|
||||||
{
|
|
||||||
Type: "visibility",
|
|
||||||
Value: "visible",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "procid",
|
|
||||||
Position: 5,
|
|
||||||
Encodings: []chronograf.ColumnEncoding{
|
|
||||||
|
|
||||||
{
|
|
||||||
Type: "visibility",
|
|
||||||
Value: "visible",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Type: "displayName",
|
|
||||||
Value: "Proc ID",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "appname",
|
|
||||||
Position: 6,
|
|
||||||
Encodings: []chronograf.ColumnEncoding{
|
|
||||||
{
|
|
||||||
Type: "visibility",
|
|
||||||
Value: "visible",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Type: "displayName",
|
|
||||||
Value: "Application",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "host",
|
|
||||||
Position: 7,
|
|
||||||
Encodings: []chronograf.ColumnEncoding{
|
|
||||||
{
|
|
||||||
Type: "visibility",
|
|
||||||
Value: "visible",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
return s.Update(ctx, &cfg)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *OrganizationConfigStore) Get(ctx context.Context) (*chronograf.OrganizationConfig, error) {
|
|
||||||
var cfg chronograf.OrganizationConfig
|
var cfg chronograf.OrganizationConfig
|
||||||
err := s.client.db.View(func(tx *bolt.Tx) error {
|
err := s.client.db.View(func(tx *bolt.Tx) error {
|
||||||
v := tx.Bucket(ConfigBucket).Get(configID)
|
v := tx.Bucket(OrganizationConfigBucket).Get([]byte(orgID))
|
||||||
if v == nil {
|
if v == nil {
|
||||||
return chronograf.ErrConfigNotFound
|
cfg = newOrganizationConfig(orgID)
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
return internal.UnmarshalOrganizationConfig(v, &cfg)
|
return internal.UnmarshalOrganizationConfig(v, &cfg)
|
||||||
})
|
})
|
||||||
|
@ -161,6 +42,7 @@ func (s *OrganizationConfigStore) Get(ctx context.Context) (*chronograf.Organiza
|
||||||
return &cfg, nil
|
return &cfg, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Update replaces the OrganizationConfig in the store
|
||||||
func (s *OrganizationConfigStore) Update(ctx context.Context, cfg *chronograf.OrganizationConfig) error {
|
func (s *OrganizationConfigStore) Update(ctx context.Context, cfg *chronograf.OrganizationConfig) error {
|
||||||
if cfg == nil {
|
if cfg == nil {
|
||||||
return fmt.Errorf("config provided was nil")
|
return fmt.Errorf("config provided was nil")
|
||||||
|
@ -168,9 +50,120 @@ func (s *OrganizationConfigStore) Update(ctx context.Context, cfg *chronograf.Or
|
||||||
return s.client.db.Update(func(tx *bolt.Tx) error {
|
return s.client.db.Update(func(tx *bolt.Tx) error {
|
||||||
if v, err := internal.MarshalOrganizationConfig(cfg); err != nil {
|
if v, err := internal.MarshalOrganizationConfig(cfg); err != nil {
|
||||||
return err
|
return err
|
||||||
} else if err := tx.Bucket(ConfigBucket).Put(configID, v); err != nil {
|
} else if err := tx.Bucket(OrganizationConfigBucket).Put([]byte(cfg.OrganizationID), v); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func newOrganizationConfig(orgID string) chronograf.OrganizationConfig {
|
||||||
|
return chronograf.OrganizationConfig{
|
||||||
|
OrganizationID: orgID,
|
||||||
|
LogViewer: chronograf.LogViewerConfig{
|
||||||
|
Columns: []chronograf.LogViewerColumn{
|
||||||
|
{
|
||||||
|
Name: "time",
|
||||||
|
Position: 0,
|
||||||
|
Encodings: []chronograf.ColumnEncoding{
|
||||||
|
{
|
||||||
|
Type: "visibility",
|
||||||
|
Value: "hidden",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "severity",
|
||||||
|
Position: 1,
|
||||||
|
Encodings: []chronograf.ColumnEncoding{
|
||||||
|
|
||||||
|
{
|
||||||
|
Type: "visibility",
|
||||||
|
Value: "visible",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Type: "label",
|
||||||
|
Value: "icon",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Type: "label",
|
||||||
|
Value: "text",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "timestamp",
|
||||||
|
Position: 2,
|
||||||
|
Encodings: []chronograf.ColumnEncoding{
|
||||||
|
|
||||||
|
{
|
||||||
|
Type: "visibility",
|
||||||
|
Value: "visible",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "message",
|
||||||
|
Position: 3,
|
||||||
|
Encodings: []chronograf.ColumnEncoding{
|
||||||
|
|
||||||
|
{
|
||||||
|
Type: "visibility",
|
||||||
|
Value: "visible",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "facility",
|
||||||
|
Position: 4,
|
||||||
|
Encodings: []chronograf.ColumnEncoding{
|
||||||
|
|
||||||
|
{
|
||||||
|
Type: "visibility",
|
||||||
|
Value: "visible",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "procid",
|
||||||
|
Position: 5,
|
||||||
|
Encodings: []chronograf.ColumnEncoding{
|
||||||
|
|
||||||
|
{
|
||||||
|
Type: "visibility",
|
||||||
|
Value: "visible",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Type: "displayName",
|
||||||
|
Value: "Proc ID",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "appname",
|
||||||
|
Position: 6,
|
||||||
|
Encodings: []chronograf.ColumnEncoding{
|
||||||
|
{
|
||||||
|
Type: "visibility",
|
||||||
|
Value: "visible",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Type: "displayName",
|
||||||
|
Value: "Application",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "host",
|
||||||
|
Position: 7,
|
||||||
|
Encodings: []chronograf.ColumnEncoding{
|
||||||
|
{
|
||||||
|
Type: "visibility",
|
||||||
|
Value: "visible",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -8,19 +8,29 @@ import (
|
||||||
"github.com/influxdata/chronograf"
|
"github.com/influxdata/chronograf"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestConfig_Get(t *testing.T) {
|
func TestOrganizationConfig_FindOrCreate(t *testing.T) {
|
||||||
|
type args struct {
|
||||||
|
organizationID string
|
||||||
|
}
|
||||||
type wants struct {
|
type wants struct {
|
||||||
config *chronograf.OrganizationConfig
|
organizationConfig *chronograf.OrganizationConfig
|
||||||
err error
|
err error
|
||||||
}
|
}
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
wants wants
|
args args
|
||||||
|
addFirst bool
|
||||||
|
wants wants
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "Get config",
|
name: "Get non-existent default config from default org",
|
||||||
|
args: args{
|
||||||
|
organizationID: "default",
|
||||||
|
},
|
||||||
|
addFirst: false,
|
||||||
wants: wants{
|
wants: wants{
|
||||||
config: &chronograf.OrganizationConfig{
|
organizationConfig: &chronograf.OrganizationConfig{
|
||||||
|
OrganizationID: "default",
|
||||||
LogViewer: chronograf.LogViewerConfig{
|
LogViewer: chronograf.LogViewerConfig{
|
||||||
Columns: []chronograf.LogViewerColumn{
|
Columns: []chronograf.LogViewerColumn{
|
||||||
{
|
{
|
||||||
|
@ -129,64 +139,40 @@ func TestConfig_Get(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
|
||||||
for _, tt := range tests {
|
|
||||||
client, err := NewTestClient()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
defer client.Close()
|
|
||||||
|
|
||||||
s := client.OrganizationConfigStore
|
|
||||||
got, err := s.Get(context.Background())
|
|
||||||
if (tt.wants.err != nil) != (err != nil) {
|
|
||||||
t.Errorf("%q. ConfigStore.Get() error = %v, wantErr %v", tt.name, err, tt.wants.err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if diff := cmp.Diff(got, tt.wants.config); diff != "" {
|
|
||||||
t.Errorf("%q. ConfigStore.Get():\n-got/+want\ndiff %s", tt.name, diff)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestConfig_Update(t *testing.T) {
|
|
||||||
type args struct {
|
|
||||||
config *chronograf.OrganizationConfig
|
|
||||||
}
|
|
||||||
type wants struct {
|
|
||||||
config *chronograf.OrganizationConfig
|
|
||||||
err error
|
|
||||||
}
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
args args
|
|
||||||
wants wants
|
|
||||||
}{
|
|
||||||
{
|
{
|
||||||
name: "Set config",
|
name: "Get non-existent default config from non-default org",
|
||||||
args: args{
|
args: args{
|
||||||
config: &chronograf.OrganizationConfig{
|
organizationID: "1",
|
||||||
|
},
|
||||||
|
addFirst: false,
|
||||||
|
wants: wants{
|
||||||
|
organizationConfig: &chronograf.OrganizationConfig{
|
||||||
|
OrganizationID: "1",
|
||||||
LogViewer: chronograf.LogViewerConfig{
|
LogViewer: chronograf.LogViewerConfig{
|
||||||
Columns: []chronograf.LogViewerColumn{
|
Columns: []chronograf.LogViewerColumn{
|
||||||
{
|
{
|
||||||
Name: "time",
|
Name: "time",
|
||||||
Position: 1,
|
Position: 0,
|
||||||
Encodings: []chronograf.ColumnEncoding{
|
Encodings: []chronograf.ColumnEncoding{
|
||||||
{
|
{
|
||||||
Type: "visibility",
|
Type: "visibility",
|
||||||
Value: "visible",
|
Value: "hidden",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "severity",
|
Name: "severity",
|
||||||
Position: 0,
|
Position: 1,
|
||||||
Encodings: []chronograf.ColumnEncoding{
|
Encodings: []chronograf.ColumnEncoding{
|
||||||
|
|
||||||
{
|
{
|
||||||
Type: "visibility",
|
Type: "visibility",
|
||||||
Value: "visible",
|
Value: "visible",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
Type: "label",
|
||||||
|
Value: "icon",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
Type: "label",
|
Type: "label",
|
||||||
Value: "text",
|
Value: "text",
|
||||||
|
@ -237,7 +223,7 @@ func TestConfig_Update(t *testing.T) {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Type: "displayName",
|
Type: "displayName",
|
||||||
Value: "Milkshake",
|
Value: "Proc ID",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -269,8 +255,16 @@ func TestConfig_Update(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Get existing/modified config from default org",
|
||||||
|
args: args{
|
||||||
|
organizationID: "default",
|
||||||
|
},
|
||||||
|
addFirst: true,
|
||||||
wants: wants{
|
wants: wants{
|
||||||
config: &chronograf.OrganizationConfig{
|
organizationConfig: &chronograf.OrganizationConfig{
|
||||||
|
OrganizationID: "default",
|
||||||
LogViewer: chronograf.LogViewerConfig{
|
LogViewer: chronograf.LogViewerConfig{
|
||||||
Columns: []chronograf.LogViewerColumn{
|
Columns: []chronograf.LogViewerColumn{
|
||||||
{
|
{
|
||||||
|
@ -279,7 +273,7 @@ func TestConfig_Update(t *testing.T) {
|
||||||
Encodings: []chronograf.ColumnEncoding{
|
Encodings: []chronograf.ColumnEncoding{
|
||||||
{
|
{
|
||||||
Type: "visibility",
|
Type: "visibility",
|
||||||
Value: "visible",
|
Value: "hidden",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -290,7 +284,11 @@ func TestConfig_Update(t *testing.T) {
|
||||||
|
|
||||||
{
|
{
|
||||||
Type: "visibility",
|
Type: "visibility",
|
||||||
Value: "visible",
|
Value: "hidden",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Type: "label",
|
||||||
|
Value: "icon",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Type: "label",
|
Type: "label",
|
||||||
|
@ -342,7 +340,124 @@ func TestConfig_Update(t *testing.T) {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Type: "displayName",
|
Type: "displayName",
|
||||||
Value: "Milkshake",
|
Value: "Proc ID",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "appname",
|
||||||
|
Position: 6,
|
||||||
|
Encodings: []chronograf.ColumnEncoding{
|
||||||
|
{
|
||||||
|
Type: "visibility",
|
||||||
|
Value: "visible",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Type: "displayName",
|
||||||
|
Value: "Application",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "host",
|
||||||
|
Position: 7,
|
||||||
|
Encodings: []chronograf.ColumnEncoding{
|
||||||
|
{
|
||||||
|
Type: "visibility",
|
||||||
|
Value: "visible",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Get existing/modified config from non-default org",
|
||||||
|
args: args{
|
||||||
|
organizationID: "1",
|
||||||
|
},
|
||||||
|
addFirst: true,
|
||||||
|
wants: wants{
|
||||||
|
organizationConfig: &chronograf.OrganizationConfig{
|
||||||
|
OrganizationID: "1",
|
||||||
|
LogViewer: chronograf.LogViewerConfig{
|
||||||
|
Columns: []chronograf.LogViewerColumn{
|
||||||
|
{
|
||||||
|
Name: "time",
|
||||||
|
Position: 1,
|
||||||
|
Encodings: []chronograf.ColumnEncoding{
|
||||||
|
{
|
||||||
|
Type: "visibility",
|
||||||
|
Value: "hidden",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "severity",
|
||||||
|
Position: 0,
|
||||||
|
Encodings: []chronograf.ColumnEncoding{
|
||||||
|
|
||||||
|
{
|
||||||
|
Type: "visibility",
|
||||||
|
Value: "hidden",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Type: "label",
|
||||||
|
Value: "icon",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Type: "label",
|
||||||
|
Value: "text",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "timestamp",
|
||||||
|
Position: 2,
|
||||||
|
Encodings: []chronograf.ColumnEncoding{
|
||||||
|
|
||||||
|
{
|
||||||
|
Type: "visibility",
|
||||||
|
Value: "visible",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "message",
|
||||||
|
Position: 3,
|
||||||
|
Encodings: []chronograf.ColumnEncoding{
|
||||||
|
|
||||||
|
{
|
||||||
|
Type: "visibility",
|
||||||
|
Value: "visible",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "facility",
|
||||||
|
Position: 4,
|
||||||
|
Encodings: []chronograf.ColumnEncoding{
|
||||||
|
|
||||||
|
{
|
||||||
|
Type: "visibility",
|
||||||
|
Value: "visible",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "procid",
|
||||||
|
Position: 5,
|
||||||
|
Encodings: []chronograf.ColumnEncoding{
|
||||||
|
|
||||||
|
{
|
||||||
|
Type: "visibility",
|
||||||
|
Value: "visible",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Type: "displayName",
|
||||||
|
Value: "Proc ID",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -384,20 +499,494 @@ func TestConfig_Update(t *testing.T) {
|
||||||
defer client.Close()
|
defer client.Close()
|
||||||
|
|
||||||
s := client.OrganizationConfigStore
|
s := client.OrganizationConfigStore
|
||||||
err = s.Update(context.Background(), tt.args.config)
|
|
||||||
if (tt.wants.err != nil) != (err != nil) {
|
if tt.addFirst {
|
||||||
t.Errorf("%q. ConfigStore.Get() error = %v, wantErr %v", tt.name, err, tt.wants.err)
|
if err := s.Update(context.Background(), tt.wants.organizationConfig); err != nil {
|
||||||
continue
|
t.Fatal(err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
got, _ := s.Get(context.Background())
|
got, err := s.FindOrCreate(context.Background(), tt.args.organizationID)
|
||||||
|
|
||||||
if (tt.wants.err != nil) != (err != nil) {
|
if (tt.wants.err != nil) != (err != nil) {
|
||||||
t.Errorf("%q. ConfigStore.Get() error = %v, wantErr %v", tt.name, err, tt.wants.err)
|
t.Errorf("%q. OrganizationConfigStore.FindOrCreate() error = %v, wantErr %v", tt.name, err, tt.wants.err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
if diff := cmp.Diff(got, tt.wants.organizationConfig); diff != "" {
|
||||||
if diff := cmp.Diff(got, tt.wants.config); diff != "" {
|
t.Errorf("%q. OrganizationConfigStore.FindOrCreate():\n-got/+want\ndiff %s", tt.name, diff)
|
||||||
t.Errorf("%q. ConfigStore.Get():\n-got/+want\ndiff %s", tt.name, diff)
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestOrganizationConfig_Update(t *testing.T) {
|
||||||
|
type args struct {
|
||||||
|
organizationConfig *chronograf.OrganizationConfig
|
||||||
|
organizationID string
|
||||||
|
}
|
||||||
|
type wants struct {
|
||||||
|
organizationConfig *chronograf.OrganizationConfig
|
||||||
|
err error
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args args
|
||||||
|
wants wants
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "Set default org config",
|
||||||
|
args: args{
|
||||||
|
organizationConfig: &chronograf.OrganizationConfig{
|
||||||
|
OrganizationID: "default",
|
||||||
|
LogViewer: chronograf.LogViewerConfig{
|
||||||
|
Columns: []chronograf.LogViewerColumn{
|
||||||
|
{
|
||||||
|
Name: "time",
|
||||||
|
Position: 1,
|
||||||
|
Encodings: []chronograf.ColumnEncoding{
|
||||||
|
{
|
||||||
|
Type: "visibility",
|
||||||
|
Value: "visible",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "severity",
|
||||||
|
Position: 0,
|
||||||
|
Encodings: []chronograf.ColumnEncoding{
|
||||||
|
|
||||||
|
{
|
||||||
|
Type: "visibility",
|
||||||
|
Value: "visible",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Type: "label",
|
||||||
|
Value: "text",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "timestamp",
|
||||||
|
Position: 2,
|
||||||
|
Encodings: []chronograf.ColumnEncoding{
|
||||||
|
|
||||||
|
{
|
||||||
|
Type: "visibility",
|
||||||
|
Value: "visible",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "message",
|
||||||
|
Position: 3,
|
||||||
|
Encodings: []chronograf.ColumnEncoding{
|
||||||
|
|
||||||
|
{
|
||||||
|
Type: "visibility",
|
||||||
|
Value: "visible",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "facility",
|
||||||
|
Position: 4,
|
||||||
|
Encodings: []chronograf.ColumnEncoding{
|
||||||
|
|
||||||
|
{
|
||||||
|
Type: "visibility",
|
||||||
|
Value: "visible",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "procid",
|
||||||
|
Position: 5,
|
||||||
|
Encodings: []chronograf.ColumnEncoding{
|
||||||
|
|
||||||
|
{
|
||||||
|
Type: "visibility",
|
||||||
|
Value: "visible",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Type: "displayName",
|
||||||
|
Value: "Milkshake",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "appname",
|
||||||
|
Position: 6,
|
||||||
|
Encodings: []chronograf.ColumnEncoding{
|
||||||
|
{
|
||||||
|
Type: "visibility",
|
||||||
|
Value: "visible",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Type: "displayName",
|
||||||
|
Value: "Application",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "host",
|
||||||
|
Position: 7,
|
||||||
|
Encodings: []chronograf.ColumnEncoding{
|
||||||
|
{
|
||||||
|
Type: "visibility",
|
||||||
|
Value: "visible",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
organizationID: "default",
|
||||||
|
},
|
||||||
|
wants: wants{
|
||||||
|
organizationConfig: &chronograf.OrganizationConfig{
|
||||||
|
LogViewer: chronograf.LogViewerConfig{
|
||||||
|
Columns: []chronograf.LogViewerColumn{
|
||||||
|
{
|
||||||
|
Name: "time",
|
||||||
|
Position: 1,
|
||||||
|
Encodings: []chronograf.ColumnEncoding{
|
||||||
|
{
|
||||||
|
Type: "visibility",
|
||||||
|
Value: "visible",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "severity",
|
||||||
|
Position: 0,
|
||||||
|
Encodings: []chronograf.ColumnEncoding{
|
||||||
|
|
||||||
|
{
|
||||||
|
Type: "visibility",
|
||||||
|
Value: "visible",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Type: "label",
|
||||||
|
Value: "text",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "timestamp",
|
||||||
|
Position: 2,
|
||||||
|
Encodings: []chronograf.ColumnEncoding{
|
||||||
|
|
||||||
|
{
|
||||||
|
Type: "visibility",
|
||||||
|
Value: "visible",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "message",
|
||||||
|
Position: 3,
|
||||||
|
Encodings: []chronograf.ColumnEncoding{
|
||||||
|
|
||||||
|
{
|
||||||
|
Type: "visibility",
|
||||||
|
Value: "visible",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "facility",
|
||||||
|
Position: 4,
|
||||||
|
Encodings: []chronograf.ColumnEncoding{
|
||||||
|
|
||||||
|
{
|
||||||
|
Type: "visibility",
|
||||||
|
Value: "visible",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "procid",
|
||||||
|
Position: 5,
|
||||||
|
Encodings: []chronograf.ColumnEncoding{
|
||||||
|
|
||||||
|
{
|
||||||
|
Type: "visibility",
|
||||||
|
Value: "visible",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Type: "displayName",
|
||||||
|
Value: "Milkshake",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "appname",
|
||||||
|
Position: 6,
|
||||||
|
Encodings: []chronograf.ColumnEncoding{
|
||||||
|
{
|
||||||
|
Type: "visibility",
|
||||||
|
Value: "visible",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Type: "displayName",
|
||||||
|
Value: "Application",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "host",
|
||||||
|
Position: 7,
|
||||||
|
Encodings: []chronograf.ColumnEncoding{
|
||||||
|
{
|
||||||
|
Type: "visibility",
|
||||||
|
Value: "visible",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
OrganizationID: "default",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Set non-default org config",
|
||||||
|
args: args{
|
||||||
|
organizationConfig: &chronograf.OrganizationConfig{
|
||||||
|
OrganizationID: "1337",
|
||||||
|
LogViewer: chronograf.LogViewerConfig{
|
||||||
|
Columns: []chronograf.LogViewerColumn{
|
||||||
|
{
|
||||||
|
Name: "time",
|
||||||
|
Position: 1,
|
||||||
|
Encodings: []chronograf.ColumnEncoding{
|
||||||
|
{
|
||||||
|
Type: "visibility",
|
||||||
|
Value: "visible",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "severity",
|
||||||
|
Position: 0,
|
||||||
|
Encodings: []chronograf.ColumnEncoding{
|
||||||
|
|
||||||
|
{
|
||||||
|
Type: "visibility",
|
||||||
|
Value: "visible",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Type: "label",
|
||||||
|
Value: "text",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "timestamp",
|
||||||
|
Position: 2,
|
||||||
|
Encodings: []chronograf.ColumnEncoding{
|
||||||
|
|
||||||
|
{
|
||||||
|
Type: "visibility",
|
||||||
|
Value: "visible",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "message",
|
||||||
|
Position: 3,
|
||||||
|
Encodings: []chronograf.ColumnEncoding{
|
||||||
|
|
||||||
|
{
|
||||||
|
Type: "visibility",
|
||||||
|
Value: "visible",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "facility",
|
||||||
|
Position: 4,
|
||||||
|
Encodings: []chronograf.ColumnEncoding{
|
||||||
|
|
||||||
|
{
|
||||||
|
Type: "visibility",
|
||||||
|
Value: "visible",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "procid",
|
||||||
|
Position: 5,
|
||||||
|
Encodings: []chronograf.ColumnEncoding{
|
||||||
|
|
||||||
|
{
|
||||||
|
Type: "visibility",
|
||||||
|
Value: "visible",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Type: "displayName",
|
||||||
|
Value: "Milkshake",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "appname",
|
||||||
|
Position: 6,
|
||||||
|
Encodings: []chronograf.ColumnEncoding{
|
||||||
|
{
|
||||||
|
Type: "visibility",
|
||||||
|
Value: "visible",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Type: "displayName",
|
||||||
|
Value: "Application",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "host",
|
||||||
|
Position: 7,
|
||||||
|
Encodings: []chronograf.ColumnEncoding{
|
||||||
|
{
|
||||||
|
Type: "visibility",
|
||||||
|
Value: "visible",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
organizationID: "1337",
|
||||||
|
},
|
||||||
|
wants: wants{
|
||||||
|
organizationConfig: &chronograf.OrganizationConfig{
|
||||||
|
LogViewer: chronograf.LogViewerConfig{
|
||||||
|
Columns: []chronograf.LogViewerColumn{
|
||||||
|
{
|
||||||
|
Name: "time",
|
||||||
|
Position: 1,
|
||||||
|
Encodings: []chronograf.ColumnEncoding{
|
||||||
|
{
|
||||||
|
Type: "visibility",
|
||||||
|
Value: "visible",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "severity",
|
||||||
|
Position: 0,
|
||||||
|
Encodings: []chronograf.ColumnEncoding{
|
||||||
|
|
||||||
|
{
|
||||||
|
Type: "visibility",
|
||||||
|
Value: "visible",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Type: "label",
|
||||||
|
Value: "text",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "timestamp",
|
||||||
|
Position: 2,
|
||||||
|
Encodings: []chronograf.ColumnEncoding{
|
||||||
|
|
||||||
|
{
|
||||||
|
Type: "visibility",
|
||||||
|
Value: "visible",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "message",
|
||||||
|
Position: 3,
|
||||||
|
Encodings: []chronograf.ColumnEncoding{
|
||||||
|
|
||||||
|
{
|
||||||
|
Type: "visibility",
|
||||||
|
Value: "visible",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "facility",
|
||||||
|
Position: 4,
|
||||||
|
Encodings: []chronograf.ColumnEncoding{
|
||||||
|
|
||||||
|
{
|
||||||
|
Type: "visibility",
|
||||||
|
Value: "visible",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "procid",
|
||||||
|
Position: 5,
|
||||||
|
Encodings: []chronograf.ColumnEncoding{
|
||||||
|
|
||||||
|
{
|
||||||
|
Type: "visibility",
|
||||||
|
Value: "visible",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Type: "displayName",
|
||||||
|
Value: "Milkshake",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "appname",
|
||||||
|
Position: 6,
|
||||||
|
Encodings: []chronograf.ColumnEncoding{
|
||||||
|
{
|
||||||
|
Type: "visibility",
|
||||||
|
Value: "visible",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Type: "displayName",
|
||||||
|
Value: "Application",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "host",
|
||||||
|
Position: 7,
|
||||||
|
Encodings: []chronograf.ColumnEncoding{
|
||||||
|
{
|
||||||
|
Type: "visibility",
|
||||||
|
Value: "visible",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
OrganizationID: "1337",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
client, err := NewTestClient()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer client.Close()
|
||||||
|
|
||||||
|
s := client.OrganizationConfigStore
|
||||||
|
err = s.Update(context.Background(), tt.args.organizationConfig)
|
||||||
|
if (tt.wants.err != nil) != (err != nil) {
|
||||||
|
t.Errorf("%q. OrganizationConfigStore.Update() error = %v, wantErr %v", tt.name, err, tt.wants.err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
got, _ := s.FindOrCreate(context.Background(), tt.args.organizationID)
|
||||||
|
if (tt.wants.err != nil) != (err != nil) {
|
||||||
|
t.Errorf("%q. OrganizationConfigStore.Update() error = %v, wantErr %v", tt.name, err, tt.wants.err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if diff := cmp.Diff(got, tt.wants.organizationConfig); diff != "" {
|
||||||
|
t.Errorf("%q. OrganizationConfigStore.Update():\n-got/+want\ndiff %s", tt.name, diff)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,35 +9,36 @@ import (
|
||||||
|
|
||||||
// General errors.
|
// General errors.
|
||||||
const (
|
const (
|
||||||
ErrUpstreamTimeout = Error("request to backend timed out")
|
ErrUpstreamTimeout = Error("request to backend timed out")
|
||||||
ErrSourceNotFound = Error("source not found")
|
ErrSourceNotFound = Error("source not found")
|
||||||
ErrServerNotFound = Error("server not found")
|
ErrServerNotFound = Error("server not found")
|
||||||
ErrLayoutNotFound = Error("layout not found")
|
ErrLayoutNotFound = Error("layout not found")
|
||||||
ErrDashboardNotFound = Error("dashboard not found")
|
ErrDashboardNotFound = Error("dashboard not found")
|
||||||
ErrUserNotFound = Error("user not found")
|
ErrUserNotFound = Error("user not found")
|
||||||
ErrLayoutInvalid = Error("layout is invalid")
|
ErrLayoutInvalid = Error("layout is invalid")
|
||||||
ErrDashboardInvalid = Error("dashboard is invalid")
|
ErrDashboardInvalid = Error("dashboard is invalid")
|
||||||
ErrSourceInvalid = Error("source is invalid")
|
ErrSourceInvalid = Error("source is invalid")
|
||||||
ErrServerInvalid = Error("server is invalid")
|
ErrServerInvalid = Error("server is invalid")
|
||||||
ErrAlertNotFound = Error("alert not found")
|
ErrAlertNotFound = Error("alert not found")
|
||||||
ErrAuthentication = Error("user not authenticated")
|
ErrAuthentication = Error("user not authenticated")
|
||||||
ErrUninitialized = Error("client uninitialized. Call Open() method")
|
ErrUninitialized = Error("client uninitialized. Call Open() method")
|
||||||
ErrInvalidAxis = Error("Unexpected axis in cell. Valid axes are 'x', 'y', and 'y2'")
|
ErrInvalidAxis = Error("Unexpected axis in cell. Valid axes are 'x', 'y', and 'y2'")
|
||||||
ErrInvalidColorType = Error("Invalid color type. Valid color types are 'min', 'max', 'threshold', 'text', and 'background'")
|
ErrInvalidColorType = Error("Invalid color type. Valid color types are 'min', 'max', 'threshold', 'text', and 'background'")
|
||||||
ErrInvalidColor = Error("Invalid color. Accepted color format is #RRGGBB")
|
ErrInvalidColor = Error("Invalid color. Accepted color format is #RRGGBB")
|
||||||
ErrInvalidLegend = Error("Invalid legend. Both type and orientation must be set")
|
ErrInvalidLegend = Error("Invalid legend. Both type and orientation must be set")
|
||||||
ErrInvalidLegendType = Error("Invalid legend type. Valid legend type is 'static'")
|
ErrInvalidLegendType = Error("Invalid legend type. Valid legend type is 'static'")
|
||||||
ErrInvalidLegendOrient = Error("Invalid orientation type. Valid orientation types are 'top', 'bottom', 'right', 'left'")
|
ErrInvalidLegendOrient = Error("Invalid orientation type. Valid orientation types are 'top', 'bottom', 'right', 'left'")
|
||||||
ErrUserAlreadyExists = Error("user already exists")
|
ErrUserAlreadyExists = Error("user already exists")
|
||||||
ErrOrganizationNotFound = Error("organization not found")
|
ErrOrganizationNotFound = Error("organization not found")
|
||||||
ErrMappingNotFound = Error("mapping not found")
|
ErrMappingNotFound = Error("mapping not found")
|
||||||
ErrOrganizationAlreadyExists = Error("organization already exists")
|
ErrOrganizationAlreadyExists = Error("organization already exists")
|
||||||
ErrCannotDeleteDefaultOrganization = Error("cannot delete default organization")
|
ErrCannotDeleteDefaultOrganization = Error("cannot delete default organization")
|
||||||
ErrConfigNotFound = Error("cannot find configuration")
|
ErrConfigNotFound = Error("cannot find configuration")
|
||||||
ErrAnnotationNotFound = Error("annotation not found")
|
ErrAnnotationNotFound = Error("annotation not found")
|
||||||
ErrInvalidCellOptionsText = Error("invalid text wrapping option. Valid wrappings are 'truncate', 'wrap', and 'single line'")
|
ErrInvalidCellOptionsText = Error("invalid text wrapping option. Valid wrappings are 'truncate', 'wrap', and 'single line'")
|
||||||
ErrInvalidCellOptionsSort = Error("cell options sortby cannot be empty'")
|
ErrInvalidCellOptionsSort = Error("cell options sortby cannot be empty'")
|
||||||
ErrInvalidCellOptionsColumns = Error("cell options columns cannot be empty'")
|
ErrInvalidCellOptionsColumns = Error("cell options columns cannot be empty'")
|
||||||
|
ErrOrganizationConfigFindOrCreateFailed = Error("failed to find or create organization config")
|
||||||
)
|
)
|
||||||
|
|
||||||
// Error is a domain error encountered while processing chronograf requests
|
// Error is a domain error encountered while processing chronograf requests
|
||||||
|
@ -760,7 +761,8 @@ type ConfigStore interface {
|
||||||
// OrganizationConfig is the organization config for parameters that can
|
// OrganizationConfig is the organization config for parameters that can
|
||||||
// be set via API, with different sections, such as LogViewer
|
// be set via API, with different sections, such as LogViewer
|
||||||
type OrganizationConfig struct {
|
type OrganizationConfig struct {
|
||||||
LogViewer LogViewerConfig `json:"logViewer"`
|
OrganizationID string `json:"organization"`
|
||||||
|
LogViewer LogViewerConfig `json:"logViewer"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// LogViewerConfig is the configuration settings for the Log Viewer UI
|
// LogViewerConfig is the configuration settings for the Log Viewer UI
|
||||||
|
@ -784,12 +786,10 @@ type ColumnEncoding struct {
|
||||||
|
|
||||||
// OrganizationConfigStore is the storage and retrieval of organization Configs
|
// OrganizationConfigStore is the storage and retrieval of organization Configs
|
||||||
type OrganizationConfigStore interface {
|
type OrganizationConfigStore interface {
|
||||||
// Initialize creates the initial configuration
|
// FindOrCreate gets an existing OrganizationConfig and creates one if none exists
|
||||||
Initialize(context.Context) error
|
FindOrCreate(ctx context.Context, orgID string) (*OrganizationConfig, error)
|
||||||
// Get retrieves the whole Config from the OrganizationConfigStore
|
// Update updates the whole organization config in the OrganizationConfigStore
|
||||||
Get(context.Context) (*Config, error)
|
Update(context.Context, *OrganizationConfig) error
|
||||||
// Update updates the whole Config in the OrganizationConfigStore
|
|
||||||
Update(context.Context, *Config) error
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// BuildInfo is sent to the usage client to track versions and commits
|
// BuildInfo is sent to the usage client to track versions and commits
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
package mocks
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/influxdata/chronograf"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ chronograf.OrganizationConfigStore = &OrganizationConfigStore{}
|
||||||
|
|
||||||
|
type OrganizationConfigStore struct {
|
||||||
|
//InitializeF func(ctx context.Context) error
|
||||||
|
//GetF func(ctx context.Context, id chronograf.OrganizationID) (*chronograf.OrganizationConfig, error)
|
||||||
|
FindOrCreateF func(ctx context.Context, id string) (*chronograf.OrganizationConfig, error)
|
||||||
|
UpdateF func(ctx context.Context, target *chronograf.OrganizationConfig) error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (oc *OrganizationConfigStore) FindOrCreate(ctx context.Context, id string) (*chronograf.OrganizationConfig, error) {
|
||||||
|
return oc.FindOrCreateF(ctx, id)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (oc *OrganizationConfigStore) Update(ctx context.Context, target *chronograf.OrganizationConfig) error {
|
||||||
|
return oc.UpdateF(ctx, target)
|
||||||
|
}
|
|
@ -8,14 +8,15 @@ import (
|
||||||
|
|
||||||
// Store is a server.DataStore
|
// Store is a server.DataStore
|
||||||
type Store struct {
|
type Store struct {
|
||||||
SourcesStore chronograf.SourcesStore
|
SourcesStore chronograf.SourcesStore
|
||||||
MappingsStore chronograf.MappingsStore
|
MappingsStore chronograf.MappingsStore
|
||||||
ServersStore chronograf.ServersStore
|
ServersStore chronograf.ServersStore
|
||||||
LayoutsStore chronograf.LayoutsStore
|
LayoutsStore chronograf.LayoutsStore
|
||||||
UsersStore chronograf.UsersStore
|
UsersStore chronograf.UsersStore
|
||||||
DashboardsStore chronograf.DashboardsStore
|
DashboardsStore chronograf.DashboardsStore
|
||||||
OrganizationsStore chronograf.OrganizationsStore
|
OrganizationsStore chronograf.OrganizationsStore
|
||||||
ConfigStore chronograf.ConfigStore
|
ConfigStore chronograf.ConfigStore
|
||||||
|
OrganizationConfigStore chronograf.OrganizationConfigStore
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Store) Sources(ctx context.Context) chronograf.SourcesStore {
|
func (s *Store) Sources(ctx context.Context) chronograf.SourcesStore {
|
||||||
|
@ -48,3 +49,7 @@ func (s *Store) Dashboards(ctx context.Context) chronograf.DashboardsStore {
|
||||||
func (s *Store) Config(ctx context.Context) chronograf.ConfigStore {
|
func (s *Store) Config(ctx context.Context) chronograf.ConfigStore {
|
||||||
return s.ConfigStore
|
return s.ConfigStore
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *Store) OrganizationConfig(ctx context.Context) chronograf.OrganizationConfigStore {
|
||||||
|
return s.OrganizationConfigStore
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
package noop
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/influxdata/chronograf"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ensure OrganizationConfigStore implements chronograf.OrganizationConfigStore
|
||||||
|
var _ chronograf.OrganizationConfigStore = &OrganizationConfigStore{}
|
||||||
|
|
||||||
|
type OrganizationConfigStore struct{}
|
||||||
|
|
||||||
|
func (s *OrganizationConfigStore) FindOrCreate(context.Context, string) (*chronograf.OrganizationConfig, error) {
|
||||||
|
return nil, chronograf.ErrOrganizationConfigFindOrCreateFailed
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *OrganizationConfigStore) Update(context.Context, *chronograf.OrganizationConfig) error {
|
||||||
|
return fmt.Errorf("cannot update conifg")
|
||||||
|
}
|
|
@ -0,0 +1,56 @@
|
||||||
|
package organizations
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/influxdata/chronograf"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ensure that OrganizationConfig implements chronograf.OrganizationConfigStore
|
||||||
|
var _ chronograf.OrganizationConfigStore = &OrganizationConfigStore{}
|
||||||
|
|
||||||
|
// OrganizationConfigStore facade on a OrganizationConfig that filters OrganizationConfigs by organization.
|
||||||
|
type OrganizationConfigStore struct {
|
||||||
|
store chronograf.OrganizationConfigStore
|
||||||
|
organization string
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewOrganizationConfigStore creates a new OrganizationConfigStore from an existing
|
||||||
|
// chronograf.OrganizationConfigStore and an organization string
|
||||||
|
func NewOrganizationConfigStore(s chronograf.OrganizationConfigStore, orgID string) *OrganizationConfigStore {
|
||||||
|
return &OrganizationConfigStore{
|
||||||
|
store: s,
|
||||||
|
organization: orgID,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FindOrCreate gets an organization's config or creates one if none exists
|
||||||
|
func (s *OrganizationConfigStore) FindOrCreate(ctx context.Context, orgID string) (*chronograf.OrganizationConfig, error) {
|
||||||
|
var err = validOrganization(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
oc, err := s.store.FindOrCreate(ctx, orgID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return oc, nil
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the OrganizationConfig in OrganizationConfigStore.
|
||||||
|
func (s *OrganizationConfigStore) Update(ctx context.Context, oc *chronograf.OrganizationConfig) error {
|
||||||
|
err := validOrganization(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = s.store.FindOrCreate(ctx, oc.OrganizationID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return s.store.Update(ctx, oc)
|
||||||
|
}
|
|
@ -95,13 +95,8 @@ func AuthorizedUser(
|
||||||
next http.HandlerFunc,
|
next http.HandlerFunc,
|
||||||
) http.HandlerFunc {
|
) http.HandlerFunc {
|
||||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
if !useAuth {
|
ctx := r.Context()
|
||||||
ctx := r.Context()
|
serverCtx := serverContext(ctx)
|
||||||
// If there is no auth, then give the user raw access to the DataStore
|
|
||||||
r = r.WithContext(serverContext(ctx))
|
|
||||||
next(w, r)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
log := logger.
|
log := logger.
|
||||||
WithField("component", "role_auth").
|
WithField("component", "role_auth").
|
||||||
|
@ -109,8 +104,24 @@ func AuthorizedUser(
|
||||||
WithField("method", r.Method).
|
WithField("method", r.Method).
|
||||||
WithField("url", r.URL)
|
WithField("url", r.URL)
|
||||||
|
|
||||||
ctx := r.Context()
|
defaultOrg, err := store.Organizations(serverCtx).DefaultOrganization(serverCtx)
|
||||||
serverCtx := serverContext(ctx)
|
if err != nil {
|
||||||
|
log.Error(fmt.Sprintf("Failed to retrieve the default organization: %v", err))
|
||||||
|
Error(w, http.StatusForbidden, "User is not authorized", logger)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !useAuth {
|
||||||
|
// If there is no auth, then set the organization id to be the default org id on context
|
||||||
|
// so that calls like hasOrganizationContext as used in Organization Config service
|
||||||
|
// method OrganizationConfig can successfully get the organization id
|
||||||
|
ctx = context.WithValue(ctx, organizations.ContextKey, defaultOrg.ID)
|
||||||
|
|
||||||
|
// And if there is no auth, then give the user raw access to the DataStore
|
||||||
|
r = r.WithContext(serverContext(ctx))
|
||||||
|
next(w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
p, err := getValidPrincipal(ctx)
|
p, err := getValidPrincipal(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -127,12 +138,6 @@ func AuthorizedUser(
|
||||||
|
|
||||||
// This is as if the user was logged into the default organization
|
// This is as if the user was logged into the default organization
|
||||||
if p.Organization == "" {
|
if p.Organization == "" {
|
||||||
defaultOrg, err := store.Organizations(serverCtx).DefaultOrganization(serverCtx)
|
|
||||||
if err != nil {
|
|
||||||
log.Error(fmt.Sprintf("Failed to retrieve the default organization: %v", err))
|
|
||||||
Error(w, http.StatusForbidden, "User is not authorized", logger)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
p.Organization = defaultOrg.ID
|
p.Organization = defaultOrg.ID
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -63,7 +63,6 @@ func TestAuthorizedToken(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAuthorizedUser(t *testing.T) {
|
func TestAuthorizedUser(t *testing.T) {
|
||||||
type fields struct {
|
type fields struct {
|
||||||
UsersStore chronograf.UsersStore
|
UsersStore chronograf.UsersStore
|
||||||
|
@ -102,7 +101,7 @@ func TestAuthorizedUser(t *testing.T) {
|
||||||
args: args{
|
args: args{
|
||||||
useAuth: false,
|
useAuth: false,
|
||||||
},
|
},
|
||||||
hasOrganizationContext: false,
|
hasOrganizationContext: true,
|
||||||
hasSuperAdminContext: false,
|
hasSuperAdminContext: false,
|
||||||
hasRoleContext: false,
|
hasRoleContext: false,
|
||||||
hasServerContext: true,
|
hasServerContext: true,
|
||||||
|
@ -1047,6 +1046,11 @@ func TestAuthorizedUser(t *testing.T) {
|
||||||
Name: "The ShillBillThrilliettas",
|
Name: "The ShillBillThrilliettas",
|
||||||
}, nil
|
}, nil
|
||||||
},
|
},
|
||||||
|
DefaultOrganizationF: func(ctx context.Context) (*chronograf.Organization, error) {
|
||||||
|
return &chronograf.Organization{
|
||||||
|
ID: "0",
|
||||||
|
}, nil
|
||||||
|
},
|
||||||
},
|
},
|
||||||
Logger: clog.New(clog.DebugLevel),
|
Logger: clog.New(clog.DebugLevel),
|
||||||
},
|
},
|
||||||
|
|
|
@ -22,17 +22,17 @@ func newOrganizationConfigResponse(config chronograf.OrganizationConfig) *organi
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type logViewerOrganizationConfigResponse struct {
|
type logViewerConfigResponse struct {
|
||||||
Links selfLinks `json:"links"`
|
Links selfLinks `json:"links"`
|
||||||
chronograf.LogViewerOrganizationConfig
|
chronograf.LogViewerConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
func newAuthConfigResponse(config chronograf.OrganizationConfig) *logViewerOrganizationConfigResponse {
|
func newLogViewerConfigResponse(config chronograf.OrganizationConfig) *logViewerConfigResponse {
|
||||||
return &logViewerOrganizationConfigResponse{
|
return &logViewerConfigResponse{
|
||||||
Links: selfLinks{
|
Links: selfLinks{
|
||||||
Self: "/chronograf/v1/org_config/logviewer",
|
Self: "/chronograf/v1/org_config/logviewer",
|
||||||
},
|
},
|
||||||
LogViewerOrganizationConfig: config.LogViewer,
|
LogViewerConfig: config.LogViewer,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -40,14 +40,20 @@ func newAuthConfigResponse(config chronograf.OrganizationConfig) *logViewerOrgan
|
||||||
func (s *Service) OrganizationConfig(w http.ResponseWriter, r *http.Request) {
|
func (s *Service) OrganizationConfig(w http.ResponseWriter, r *http.Request) {
|
||||||
ctx := r.Context()
|
ctx := r.Context()
|
||||||
|
|
||||||
config, err := s.Store.OrganizationConfig(ctx).Get(ctx)
|
orgID, ok := hasOrganizationContext(ctx)
|
||||||
|
if !ok {
|
||||||
|
Error(w, http.StatusBadRequest, "Organization not found on context", s.Logger)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
config, err := s.Store.OrganizationConfig(ctx).FindOrCreate(ctx, orgID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
Error(w, http.StatusBadRequest, err.Error(), s.Logger)
|
Error(w, http.StatusBadRequest, err.Error(), s.Logger)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if config == nil {
|
if config == nil {
|
||||||
Error(w, http.StatusBadRequest, "Configuration object was nil", s.Logger)
|
Error(w, http.StatusBadRequest, "Organization configuration object was nil", s.Logger)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
res := newOrganizationConfigResponse(*config)
|
res := newOrganizationConfigResponse(*config)
|
||||||
|
@ -59,14 +65,20 @@ func (s *Service) OrganizationConfig(w http.ResponseWriter, r *http.Request) {
|
||||||
func (s *Service) LogViewerOrganizationConfig(w http.ResponseWriter, r *http.Request) {
|
func (s *Service) LogViewerOrganizationConfig(w http.ResponseWriter, r *http.Request) {
|
||||||
ctx := r.Context()
|
ctx := r.Context()
|
||||||
|
|
||||||
config, err := s.Store.OrganizationConfig(ctx).Get(ctx)
|
orgID, ok := hasOrganizationContext(ctx)
|
||||||
|
if !ok {
|
||||||
|
Error(w, http.StatusBadRequest, "Organization not found on context", s.Logger)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
config, err := s.Store.OrganizationConfig(ctx).FindOrCreate(ctx, orgID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
Error(w, http.StatusBadRequest, err.Error(), s.Logger)
|
Error(w, http.StatusBadRequest, err.Error(), s.Logger)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if config == nil {
|
if config == nil {
|
||||||
Error(w, http.StatusBadRequest, "Configuration object was nil", s.Logger)
|
Error(w, http.StatusBadRequest, "Organization configuration object was nil", s.Logger)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -79,6 +91,12 @@ func (s *Service) LogViewerOrganizationConfig(w http.ResponseWriter, r *http.Req
|
||||||
func (s *Service) ReplaceLogViewerOrganizationConfig(w http.ResponseWriter, r *http.Request) {
|
func (s *Service) ReplaceLogViewerOrganizationConfig(w http.ResponseWriter, r *http.Request) {
|
||||||
ctx := r.Context()
|
ctx := r.Context()
|
||||||
|
|
||||||
|
orgID, ok := hasOrganizationContext(ctx)
|
||||||
|
if !ok {
|
||||||
|
Error(w, http.StatusBadRequest, "Organization not found on context", s.Logger)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
var logViewerConfig chronograf.LogViewerConfig
|
var logViewerConfig chronograf.LogViewerConfig
|
||||||
if err := json.NewDecoder(r.Body).Decode(&logViewerConfig); err != nil {
|
if err := json.NewDecoder(r.Body).Decode(&logViewerConfig); err != nil {
|
||||||
invalidJSON(w, s.Logger)
|
invalidJSON(w, s.Logger)
|
||||||
|
@ -89,13 +107,13 @@ func (s *Service) ReplaceLogViewerOrganizationConfig(w http.ResponseWriter, r *h
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
config, err := s.Store.OrganizationConfig(ctx).Get(ctx)
|
config, err := s.Store.OrganizationConfig(ctx).FindOrCreate(ctx, orgID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
Error(w, http.StatusBadRequest, err.Error(), s.Logger)
|
Error(w, http.StatusBadRequest, err.Error(), s.Logger)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if config == nil {
|
if config == nil {
|
||||||
Error(w, http.StatusBadRequest, "Configuration object was nil", s.Logger)
|
Error(w, http.StatusBadRequest, "Organization configuration object was nil", s.Logger)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
config.LogViewer = logViewerConfig
|
config.LogViewer = logViewerConfig
|
||||||
|
|
|
@ -2,6 +2,7 @@ package server
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
|
@ -10,11 +11,15 @@ import (
|
||||||
"github.com/influxdata/chronograf"
|
"github.com/influxdata/chronograf"
|
||||||
"github.com/influxdata/chronograf/log"
|
"github.com/influxdata/chronograf/log"
|
||||||
"github.com/influxdata/chronograf/mocks"
|
"github.com/influxdata/chronograf/mocks"
|
||||||
|
"github.com/influxdata/chronograf/organizations"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestOrganizationConfig(t *testing.T) {
|
func TestOrganizationConfig(t *testing.T) {
|
||||||
|
type args struct {
|
||||||
|
organizationID string
|
||||||
|
}
|
||||||
type fields struct {
|
type fields struct {
|
||||||
OrganizationConfigStore chronograf.OrganizationConfigStore
|
organizationConfigStore chronograf.OrganizationConfigStore
|
||||||
}
|
}
|
||||||
type wants struct {
|
type wants struct {
|
||||||
statusCode int
|
statusCode int
|
||||||
|
@ -24,126 +29,138 @@ func TestOrganizationConfig(t *testing.T) {
|
||||||
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
|
args args
|
||||||
fields fields
|
fields fields
|
||||||
wants wants
|
wants wants
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "Get organization configuration settings",
|
name: "Get organization configuration",
|
||||||
|
args: args{
|
||||||
|
organizationID: "default",
|
||||||
|
},
|
||||||
fields: fields{
|
fields: fields{
|
||||||
OrganizationConfigStore: &mocks.OrganizationConfigStore{
|
organizationConfigStore: &mocks.OrganizationConfigStore{
|
||||||
OrganizationConfig: &chronograf.OrganizationConfig{
|
FindOrCreateF: func(ctx context.Context, orgID string) (*chronograf.OrganizationConfig, error) {
|
||||||
LogViewer: chronograf.LogViewer{
|
switch orgID {
|
||||||
Columns: []chronograf.LogViewerColumn{
|
case "default":
|
||||||
{
|
return &chronograf.OrganizationConfig{
|
||||||
Name: "time",
|
OrganizationID: "default",
|
||||||
Position: 0,
|
LogViewer: chronograf.LogViewerConfig{
|
||||||
Encodings: []chronograf.ColumnEncoding{
|
Columns: []chronograf.LogViewerColumn{
|
||||||
{
|
{
|
||||||
Type: "visibility",
|
Name: "time",
|
||||||
Value: "hidden",
|
Position: 0,
|
||||||
|
Encodings: []chronograf.ColumnEncoding{
|
||||||
|
{
|
||||||
|
Type: "visibility",
|
||||||
|
Value: "hidden",
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
{
|
||||||
},
|
Name: "severity",
|
||||||
{
|
Position: 1,
|
||||||
Name: "severity",
|
Encodings: []chronograf.ColumnEncoding{
|
||||||
Position: 1,
|
|
||||||
Encodings: []chronograf.ColumnEncoding{
|
|
||||||
|
|
||||||
{
|
{
|
||||||
Type: "visibility",
|
Type: "visibility",
|
||||||
Value: "visible",
|
Value: "visible",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Type: "label",
|
||||||
|
Value: "icon",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Type: "label",
|
||||||
|
Value: "text",
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Type: "label",
|
Name: "timestamp",
|
||||||
Value: "icon",
|
Position: 2,
|
||||||
},
|
Encodings: []chronograf.ColumnEncoding{
|
||||||
{
|
|
||||||
Type: "label",
|
|
||||||
Value: "text",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "timestamp",
|
|
||||||
Position: 2,
|
|
||||||
Encodings: []chronograf.ColumnEncoding{
|
|
||||||
|
|
||||||
{
|
{
|
||||||
Type: "visibility",
|
Type: "visibility",
|
||||||
Value: "visible",
|
Value: "visible",
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
{
|
||||||
},
|
Name: "message",
|
||||||
{
|
Position: 3,
|
||||||
Name: "message",
|
Encodings: []chronograf.ColumnEncoding{
|
||||||
Position: 3,
|
|
||||||
Encodings: []chronograf.ColumnEncoding{
|
|
||||||
|
|
||||||
{
|
{
|
||||||
Type: "visibility",
|
Type: "visibility",
|
||||||
Value: "visible",
|
Value: "visible",
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
{
|
||||||
},
|
Name: "facility",
|
||||||
{
|
Position: 4,
|
||||||
Name: "facility",
|
Encodings: []chronograf.ColumnEncoding{
|
||||||
Position: 4,
|
|
||||||
Encodings: []chronograf.ColumnEncoding{
|
|
||||||
|
|
||||||
{
|
{
|
||||||
Type: "visibility",
|
Type: "visibility",
|
||||||
Value: "visible",
|
Value: "visible",
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
{
|
||||||
},
|
Name: "procid",
|
||||||
{
|
Position: 5,
|
||||||
Name: "procid",
|
Encodings: []chronograf.ColumnEncoding{
|
||||||
Position: 5,
|
|
||||||
Encodings: []chronograf.ColumnEncoding{
|
|
||||||
|
|
||||||
{
|
{
|
||||||
Type: "visibility",
|
Type: "visibility",
|
||||||
Value: "visible",
|
Value: "visible",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Type: "displayName",
|
||||||
|
Value: "Proc ID",
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Type: "displayName",
|
Name: "appname",
|
||||||
Value: "Proc ID",
|
Position: 6,
|
||||||
|
Encodings: []chronograf.ColumnEncoding{
|
||||||
|
{
|
||||||
|
Type: "visibility",
|
||||||
|
Value: "visible",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Type: "displayName",
|
||||||
|
Value: "Application",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "host",
|
||||||
|
Position: 7,
|
||||||
|
Encodings: []chronograf.ColumnEncoding{
|
||||||
|
{
|
||||||
|
Type: "visibility",
|
||||||
|
Value: "visible",
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
}, nil
|
||||||
Name: "appname",
|
default:
|
||||||
Position: 6,
|
return nil, chronograf.ErrOrganizationConfigFindOrCreateFailed
|
||||||
Encodings: []chronograf.ColumnEncoding{
|
}
|
||||||
{
|
|
||||||
Type: "visibility",
|
|
||||||
Value: "visible",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Type: "displayName",
|
|
||||||
Value: "Application",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "host",
|
|
||||||
Position: 7,
|
|
||||||
Encodings: []chronograf.ColumnEncoding{
|
|
||||||
{
|
|
||||||
Type: "visibility",
|
|
||||||
Value: "visible",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
wants: wants{
|
wants: wants{
|
||||||
statusCode: 200,
|
statusCode: 200,
|
||||||
contentType: "application/json",
|
contentType: "application/json",
|
||||||
body: `{"links":{"self":"/chronograf/v1/config"},"auth":{"superAdminNewUsers":false}}`,
|
body: `{"links":{"self":"/chronograf/v1/org_config"},"organization":"default","logViewer":{"columns":[{"name":"time","position":0,"encodings":[{"type":"visibility","value":"hidden"}]},{"name":"severity","position":1,"encodings":[{"type":"visibility","value":"visible"},{"type":"label","value":"icon"},{"type":"label","value":"text"}]},{"name":"timestamp","position":2,"encodings":[{"type":"visibility","value":"visible"}]},{"name":"message","position":3,"encodings":[{"type":"visibility","value":"visible"}]},{"name":"facility","position":4,"encodings":[{"type":"visibility","value":"visible"}]},{"name":"procid","position":5,"encodings":[{"type":"visibility","value":"visible"},{"type":"displayName","value":"Proc ID"}]},{"name":"appname","position":6,"encodings":[{"type":"visibility","value":"visible"},{"type":"displayName","value":"Application"}]},{"name":"host","position":7,"encodings":[{"type":"visibility","value":"visible"}]}]}}`,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -152,36 +169,41 @@ func TestOrganizationConfig(t *testing.T) {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
s := &Service{
|
s := &Service{
|
||||||
Store: &mocks.Store{
|
Store: &mocks.Store{
|
||||||
OrganizationConfigStore: tt.fields.OrganizationConfigStore,
|
OrganizationConfigStore: tt.fields.organizationConfigStore,
|
||||||
},
|
},
|
||||||
Logger: log.New(log.DebugLevel),
|
Logger: log.New(log.DebugLevel),
|
||||||
}
|
}
|
||||||
|
|
||||||
w := httptest.NewRecorder()
|
w := httptest.NewRecorder()
|
||||||
r := httptest.NewRequest("GET", "http://any.url", nil)
|
r := httptest.NewRequest("GET", "http://any.url", nil)
|
||||||
|
ctx := context.WithValue(r.Context(), organizations.ContextKey, tt.args.organizationID)
|
||||||
|
r = r.WithContext(ctx)
|
||||||
|
|
||||||
s.Config(w, r)
|
s.OrganizationConfig(w, r)
|
||||||
|
|
||||||
resp := w.Result()
|
resp := w.Result()
|
||||||
content := resp.Header.Get("Content-Type")
|
content := resp.Header.Get("Content-Type")
|
||||||
body, _ := ioutil.ReadAll(resp.Body)
|
body, _ := ioutil.ReadAll(resp.Body)
|
||||||
|
|
||||||
if resp.StatusCode != tt.wants.statusCode {
|
if resp.StatusCode != tt.wants.statusCode {
|
||||||
t.Errorf("%q. Config() = %v, want %v", tt.name, resp.StatusCode, tt.wants.statusCode)
|
t.Errorf("%q. OrganizationConfig() = %v, want %v", tt.name, resp.StatusCode, tt.wants.statusCode)
|
||||||
}
|
}
|
||||||
if tt.wants.contentType != "" && content != tt.wants.contentType {
|
if tt.wants.contentType != "" && content != tt.wants.contentType {
|
||||||
t.Errorf("%q. Config() = %v, want %v", tt.name, content, tt.wants.contentType)
|
t.Errorf("%q. OrganizationConfig() = %v, want %v", tt.name, content, tt.wants.contentType)
|
||||||
}
|
}
|
||||||
if eq, _ := jsonEqual(string(body), tt.wants.body); tt.wants.body != "" && !eq {
|
if eq, _ := jsonEqual(string(body), tt.wants.body); tt.wants.body != "" && !eq {
|
||||||
t.Errorf("%q. Config() = \n***%v***\n,\nwant\n***%v***", tt.name, string(body), tt.wants.body)
|
t.Errorf("%q. OrganizationConfig() = \n***%v***\n,\nwant\n***%v***", tt.name, string(body), tt.wants.body)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestLogViewerOrganizationConfig(t *testing.T) {
|
func TestLogViewerOrganizationConfig(t *testing.T) {
|
||||||
|
type args struct {
|
||||||
|
organizationID string
|
||||||
|
}
|
||||||
type fields struct {
|
type fields struct {
|
||||||
OrganizationConfigStore chronograf.OrganizationConfigStore
|
organizationConfigStore chronograf.OrganizationConfigStore
|
||||||
}
|
}
|
||||||
type wants struct {
|
type wants struct {
|
||||||
statusCode int
|
statusCode int
|
||||||
|
@ -191,45 +213,56 @@ func TestLogViewerOrganizationConfig(t *testing.T) {
|
||||||
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
|
args args
|
||||||
fields fields
|
fields fields
|
||||||
wants wants
|
wants wants
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "Get log viewer configuration",
|
name: "Get log viewer configuration",
|
||||||
|
args: args{
|
||||||
|
organizationID: "default",
|
||||||
|
},
|
||||||
fields: fields{
|
fields: fields{
|
||||||
OrganizationConfigStore: &mocks.OrganizationConfigStore{
|
organizationConfigStore: &mocks.OrganizationConfigStore{
|
||||||
OrganizationConfig: &chronograf.OrganizationConfig{
|
FindOrCreateF: func(ctx context.Context, orgID string) (*chronograf.OrganizationConfig, error) {
|
||||||
LogViewer: chronograf.LogViewerConfig{
|
switch orgID {
|
||||||
Columns: []chronograf.LogViewerColumn{
|
case "default":
|
||||||
{
|
return &chronograf.OrganizationConfig{
|
||||||
Name: "severity",
|
LogViewer: chronograf.LogViewerConfig{
|
||||||
Position: 0,
|
Columns: []chronograf.LogViewerColumn{
|
||||||
Encodings: []chronograf.ColumnEncoding{
|
|
||||||
{
|
{
|
||||||
Type: "color",
|
Name: "severity",
|
||||||
Value: "emergency",
|
Position: 0,
|
||||||
Name: "ruby",
|
Encodings: []chronograf.ColumnEncoding{
|
||||||
},
|
{
|
||||||
{
|
Type: "color",
|
||||||
Type: "color",
|
Value: "emergency",
|
||||||
Value: "info",
|
Name: "ruby",
|
||||||
Name: "rainforest",
|
},
|
||||||
},
|
{
|
||||||
{
|
Type: "color",
|
||||||
Type: "displayName",
|
Value: "info",
|
||||||
Value: "Log Severity",
|
Name: "rainforest",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Type: "displayName",
|
||||||
|
Value: "Log Severity",
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
}, nil
|
||||||
},
|
default:
|
||||||
|
return nil, chronograf.ErrOrganizationConfigFindOrCreateFailed
|
||||||
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
wants: wants{
|
wants: wants{
|
||||||
statusCode: 200,
|
statusCode: 200,
|
||||||
contentType: "application/json",
|
contentType: "application/json",
|
||||||
body: `{"links":{"self":"/chronograf/v1/config/logviewer"},"columns":[{"name":"severity","position":0,"encodings":[{"type":"color","value":"emergency","name":"ruby"},{"type":"color","value":"info","name":"rainforest"},{"type":"displayName","value":"Log Severity"}]}]}`,
|
body: `{"links":{"self":"/chronograf/v1/org_config/logviewer"},"columns":[{"name":"severity","position":0,"encodings":[{"type":"color","value":"emergency","name":"ruby"},{"type":"color","value":"info","name":"rainforest"},{"type":"displayName","value":"Log Severity"}]}]}`,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -238,13 +271,15 @@ func TestLogViewerOrganizationConfig(t *testing.T) {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
s := &Service{
|
s := &Service{
|
||||||
Store: &mocks.Store{
|
Store: &mocks.Store{
|
||||||
ConfigStore: tt.fields.ConfigStore,
|
OrganizationConfigStore: tt.fields.organizationConfigStore,
|
||||||
},
|
},
|
||||||
Logger: log.New(log.DebugLevel),
|
Logger: log.New(log.DebugLevel),
|
||||||
}
|
}
|
||||||
|
|
||||||
w := httptest.NewRecorder()
|
w := httptest.NewRecorder()
|
||||||
r := httptest.NewRequest("GET", "http://any.url", nil)
|
r := httptest.NewRequest("GET", "http://any.url", nil)
|
||||||
|
ctx := context.WithValue(r.Context(), organizations.ContextKey, tt.args.organizationID)
|
||||||
|
r = r.WithContext(ctx)
|
||||||
|
|
||||||
s.LogViewerOrganizationConfig(w, r)
|
s.LogViewerOrganizationConfig(w, r)
|
||||||
|
|
||||||
|
@ -267,10 +302,11 @@ func TestLogViewerOrganizationConfig(t *testing.T) {
|
||||||
|
|
||||||
func TestReplaceLogViewerOrganizationConfig(t *testing.T) {
|
func TestReplaceLogViewerOrganizationConfig(t *testing.T) {
|
||||||
type fields struct {
|
type fields struct {
|
||||||
ConfigStore chronograf.OrganizationConfigStore
|
organizationConfigStore chronograf.OrganizationConfigStore
|
||||||
}
|
}
|
||||||
type args struct {
|
type args struct {
|
||||||
payload interface{} // expects JSON serializable struct
|
payload interface{} // expects JSON serializable struct
|
||||||
|
organizationID string
|
||||||
}
|
}
|
||||||
type wants struct {
|
type wants struct {
|
||||||
statusCode int
|
statusCode int
|
||||||
|
@ -287,31 +323,41 @@ func TestReplaceLogViewerOrganizationConfig(t *testing.T) {
|
||||||
{
|
{
|
||||||
name: "Set log viewer configuration",
|
name: "Set log viewer configuration",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
ConfigStore: &mocks.OrganizationConfigStore{
|
organizationConfigStore: &mocks.OrganizationConfigStore{
|
||||||
OrganizationConfig: &chronograf.OrganizationConfig{
|
FindOrCreateF: func(ctx context.Context, orgID string) (*chronograf.OrganizationConfig, error) {
|
||||||
LogViewer: chronograf.LogViewerConfig{
|
switch orgID {
|
||||||
Columns: []chronograf.LogViewerColumn{
|
case "1337":
|
||||||
{
|
return &chronograf.OrganizationConfig{
|
||||||
Name: "severity",
|
LogViewer: chronograf.LogViewerConfig{
|
||||||
Position: 0,
|
Columns: []chronograf.LogViewerColumn{
|
||||||
Encodings: []chronograf.ColumnEncoding{
|
|
||||||
{
|
{
|
||||||
Type: "color",
|
Name: "severity",
|
||||||
Value: "info",
|
Position: 0,
|
||||||
Name: "rainforest",
|
Encodings: []chronograf.ColumnEncoding{
|
||||||
},
|
{
|
||||||
{
|
Type: "color",
|
||||||
Type: "visibility",
|
Value: "info",
|
||||||
Value: "visible",
|
Name: "rainforest",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Type: "label",
|
Type: "visibility",
|
||||||
Value: "icon",
|
Value: "visible",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Type: "label",
|
||||||
|
Value: "icon",
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
}, nil
|
||||||
},
|
default:
|
||||||
|
return nil, chronograf.ErrOrganizationConfigFindOrCreateFailed
|
||||||
|
}
|
||||||
|
},
|
||||||
|
UpdateF: func(ctx context.Context, target *chronograf.OrganizationConfig) error {
|
||||||
|
return nil
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -358,41 +404,52 @@ func TestReplaceLogViewerOrganizationConfig(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
organizationID: "1337",
|
||||||
},
|
},
|
||||||
wants: wants{
|
wants: wants{
|
||||||
statusCode: 200,
|
statusCode: 200,
|
||||||
contentType: "application/json",
|
contentType: "application/json",
|
||||||
body: `{"links":{"self":"/chronograf/v1/config/logviewer"},"columns":[{"name":"severity","position":1,"encodings":[{"type":"color","value":"info","name":"pineapple"},{"type":"color","value":"emergency","name":"ruby"},{"type":"visibility","value":"visible"},{"type":"label","value":"icon"}]},{"name":"messages","position":0,"encodings":[{"type":"displayName","value":"Log Messages"},{"type":"visibility","value":"visible"}]}]}`,
|
body: `{"links":{"self":"/chronograf/v1/org_config/logviewer"},"columns":[{"name":"severity","position":1,"encodings":[{"type":"color","value":"info","name":"pineapple"},{"type":"color","value":"emergency","name":"ruby"},{"type":"visibility","value":"visible"},{"type":"label","value":"icon"}]},{"name":"messages","position":0,"encodings":[{"type":"displayName","value":"Log Messages"},{"type":"visibility","value":"visible"}]}]}`,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Set invalid log viewer configuration – empty",
|
name: "Set invalid log viewer configuration – empty",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
ConfigStore: &mocks.OrganizationConfigStore{
|
organizationConfigStore: &mocks.OrganizationConfigStore{
|
||||||
OrganizationConfig: &chronograf.OrganizationConfig{
|
FindOrCreateF: func(ctx context.Context, orgID string) (*chronograf.OrganizationConfig, error) {
|
||||||
LogViewer: chronograf.LogViewerConfig{
|
switch orgID {
|
||||||
Columns: []chronograf.LogViewerColumn{
|
case "1337":
|
||||||
{
|
return &chronograf.OrganizationConfig{
|
||||||
Name: "severity",
|
LogViewer: chronograf.LogViewerConfig{
|
||||||
Position: 0,
|
Columns: []chronograf.LogViewerColumn{
|
||||||
Encodings: []chronograf.ColumnEncoding{
|
|
||||||
{
|
{
|
||||||
Type: "color",
|
Name: "severity",
|
||||||
Value: "info",
|
Position: 0,
|
||||||
Name: "rainforest",
|
Encodings: []chronograf.ColumnEncoding{
|
||||||
},
|
{
|
||||||
{
|
Type: "color",
|
||||||
Type: "label",
|
Value: "info",
|
||||||
Value: "icon",
|
Name: "rainforest",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Type: "visibility",
|
Type: "label",
|
||||||
Value: "visible",
|
Value: "icon",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Type: "visibility",
|
||||||
|
Value: "visible",
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
}, nil
|
||||||
},
|
default:
|
||||||
|
return nil, chronograf.ErrOrganizationConfigFindOrCreateFailed
|
||||||
|
}
|
||||||
|
},
|
||||||
|
UpdateF: func(ctx context.Context, target *chronograf.OrganizationConfig) error {
|
||||||
|
return nil
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -400,6 +457,7 @@ func TestReplaceLogViewerOrganizationConfig(t *testing.T) {
|
||||||
payload: chronograf.LogViewerConfig{
|
payload: chronograf.LogViewerConfig{
|
||||||
Columns: []chronograf.LogViewerColumn{},
|
Columns: []chronograf.LogViewerColumn{},
|
||||||
},
|
},
|
||||||
|
organizationID: "1337",
|
||||||
},
|
},
|
||||||
wants: wants{
|
wants: wants{
|
||||||
statusCode: 400,
|
statusCode: 400,
|
||||||
|
@ -410,22 +468,32 @@ func TestReplaceLogViewerOrganizationConfig(t *testing.T) {
|
||||||
{
|
{
|
||||||
name: "Set invalid log viewer configuration - duplicate column name",
|
name: "Set invalid log viewer configuration - duplicate column name",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
ConfigStore: &mocks.OrganizationConfigStore{
|
organizationConfigStore: &mocks.OrganizationConfigStore{
|
||||||
OrganizationConfig: &chronograf.OrganizationConfig{
|
FindOrCreateF: func(ctx context.Context, orgID string) (*chronograf.OrganizationConfig, error) {
|
||||||
LogViewer: chronograf.LogViewerConfig{
|
switch orgID {
|
||||||
Columns: []chronograf.LogViewerColumn{
|
case "1337":
|
||||||
{
|
return &chronograf.OrganizationConfig{
|
||||||
Name: "procid",
|
LogViewer: chronograf.LogViewerConfig{
|
||||||
Position: 0,
|
Columns: []chronograf.LogViewerColumn{
|
||||||
Encodings: []chronograf.ColumnEncoding{
|
|
||||||
{
|
{
|
||||||
Type: "visibility",
|
Name: "procid",
|
||||||
Value: "hidden",
|
Position: 0,
|
||||||
|
Encodings: []chronograf.ColumnEncoding{
|
||||||
|
{
|
||||||
|
Type: "visibility",
|
||||||
|
Value: "hidden",
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
}, nil
|
||||||
},
|
default:
|
||||||
|
return nil, chronograf.ErrOrganizationConfigFindOrCreateFailed
|
||||||
|
}
|
||||||
|
},
|
||||||
|
UpdateF: func(ctx context.Context, target *chronograf.OrganizationConfig) error {
|
||||||
|
return nil
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -454,6 +522,7 @@ func TestReplaceLogViewerOrganizationConfig(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
organizationID: "1337",
|
||||||
},
|
},
|
||||||
wants: wants{
|
wants: wants{
|
||||||
statusCode: 400,
|
statusCode: 400,
|
||||||
|
@ -464,22 +533,32 @@ func TestReplaceLogViewerOrganizationConfig(t *testing.T) {
|
||||||
{
|
{
|
||||||
name: "Set invalid log viewer configuration - multiple columns with same position value",
|
name: "Set invalid log viewer configuration - multiple columns with same position value",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
ConfigStore: &mocks.OrganizationConfigStore{
|
organizationConfigStore: &mocks.OrganizationConfigStore{
|
||||||
OrganizationConfig: &chronograf.OrganizationConfig{
|
FindOrCreateF: func(ctx context.Context, orgID string) (*chronograf.OrganizationConfig, error) {
|
||||||
LogViewer: chronograf.LogViewerConfig{
|
switch orgID {
|
||||||
Columns: []chronograf.LogViewerColumn{
|
case "1337":
|
||||||
{
|
return &chronograf.OrganizationConfig{
|
||||||
Name: "procid",
|
LogViewer: chronograf.LogViewerConfig{
|
||||||
Position: 0,
|
Columns: []chronograf.LogViewerColumn{
|
||||||
Encodings: []chronograf.ColumnEncoding{
|
|
||||||
{
|
{
|
||||||
Type: "visibility",
|
Name: "procid",
|
||||||
Value: "hidden",
|
Position: 0,
|
||||||
|
Encodings: []chronograf.ColumnEncoding{
|
||||||
|
{
|
||||||
|
Type: "visibility",
|
||||||
|
Value: "hidden",
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
}, nil
|
||||||
},
|
default:
|
||||||
|
return nil, chronograf.ErrOrganizationConfigFindOrCreateFailed
|
||||||
|
}
|
||||||
|
},
|
||||||
|
UpdateF: func(ctx context.Context, target *chronograf.OrganizationConfig) error {
|
||||||
|
return nil
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -508,6 +587,7 @@ func TestReplaceLogViewerOrganizationConfig(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
organizationID: "1337",
|
||||||
},
|
},
|
||||||
wants: wants{
|
wants: wants{
|
||||||
statusCode: 400,
|
statusCode: 400,
|
||||||
|
@ -518,27 +598,37 @@ func TestReplaceLogViewerOrganizationConfig(t *testing.T) {
|
||||||
{
|
{
|
||||||
name: "Set invalid log viewer configuration – no visibility",
|
name: "Set invalid log viewer configuration – no visibility",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
ConfigStore: &mocks.OrganizationConfigStore{
|
organizationConfigStore: &mocks.OrganizationConfigStore{
|
||||||
OrganizationConfig: &chronograf.OrganizationConfig{
|
FindOrCreateF: func(ctx context.Context, orgID string) (*chronograf.OrganizationConfig, error) {
|
||||||
LogViewer: chronograf.LogViewerConfig{
|
switch orgID {
|
||||||
Columns: []chronograf.LogViewerColumn{
|
case "1337":
|
||||||
{
|
return &chronograf.OrganizationConfig{
|
||||||
Name: "severity",
|
LogViewer: chronograf.LogViewerConfig{
|
||||||
Position: 0,
|
Columns: []chronograf.LogViewerColumn{
|
||||||
Encodings: []chronograf.ColumnEncoding{
|
|
||||||
{
|
{
|
||||||
Type: "color",
|
Name: "severity",
|
||||||
Value: "info",
|
Position: 0,
|
||||||
Name: "rainforest",
|
Encodings: []chronograf.ColumnEncoding{
|
||||||
},
|
{
|
||||||
{
|
Type: "color",
|
||||||
Type: "label",
|
Value: "info",
|
||||||
Value: "icon",
|
Name: "rainforest",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Type: "label",
|
||||||
|
Value: "icon",
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
}, nil
|
||||||
},
|
default:
|
||||||
|
return nil, chronograf.ErrOrganizationConfigFindOrCreateFailed
|
||||||
|
}
|
||||||
|
},
|
||||||
|
UpdateF: func(ctx context.Context, target *chronograf.OrganizationConfig) error {
|
||||||
|
return nil
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -567,6 +657,7 @@ func TestReplaceLogViewerOrganizationConfig(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
organizationID: "1337",
|
||||||
},
|
},
|
||||||
wants: wants{
|
wants: wants{
|
||||||
statusCode: 400,
|
statusCode: 400,
|
||||||
|
@ -580,13 +671,15 @@ func TestReplaceLogViewerOrganizationConfig(t *testing.T) {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
s := &Service{
|
s := &Service{
|
||||||
Store: &mocks.Store{
|
Store: &mocks.Store{
|
||||||
OrganizationConfigStore: tt.fields.OrganizationConfigStore,
|
OrganizationConfigStore: tt.fields.organizationConfigStore,
|
||||||
},
|
},
|
||||||
Logger: log.New(log.DebugLevel),
|
Logger: log.New(log.DebugLevel),
|
||||||
}
|
}
|
||||||
|
|
||||||
w := httptest.NewRecorder()
|
w := httptest.NewRecorder()
|
||||||
r := httptest.NewRequest("GET", "http://any.url", nil)
|
r := httptest.NewRequest("GET", "http://any.url", nil)
|
||||||
|
ctx := context.WithValue(r.Context(), organizations.ContextKey, tt.args.organizationID)
|
||||||
|
r = r.WithContext(ctx)
|
||||||
buf, _ := json.Marshal(tt.args.payload)
|
buf, _ := json.Marshal(tt.args.payload)
|
||||||
r.Body = ioutil.NopCloser(bytes.NewReader(buf))
|
r.Body = ioutil.NopCloser(bytes.NewReader(buf))
|
||||||
|
|
||||||
|
|
|
@ -486,14 +486,15 @@ func openService(ctx context.Context, buildInfo chronograf.BuildInfo, boltPath s
|
||||||
return Service{
|
return Service{
|
||||||
TimeSeriesClient: &InfluxClient{},
|
TimeSeriesClient: &InfluxClient{},
|
||||||
Store: &Store{
|
Store: &Store{
|
||||||
LayoutsStore: layouts,
|
LayoutsStore: layouts,
|
||||||
DashboardsStore: dashboards,
|
DashboardsStore: dashboards,
|
||||||
SourcesStore: sources,
|
SourcesStore: sources,
|
||||||
ServersStore: kapacitors,
|
ServersStore: kapacitors,
|
||||||
OrganizationsStore: organizations,
|
OrganizationsStore: organizations,
|
||||||
UsersStore: db.UsersStore,
|
UsersStore: db.UsersStore,
|
||||||
ConfigStore: db.ConfigStore,
|
ConfigStore: db.ConfigStore,
|
||||||
MappingsStore: db.MappingsStore,
|
MappingsStore: db.MappingsStore,
|
||||||
|
OrganizationConfigStore: db.OrganizationConfigStore,
|
||||||
},
|
},
|
||||||
Logger: logger,
|
Logger: logger,
|
||||||
UseAuth: useAuth,
|
UseAuth: useAuth,
|
||||||
|
|
|
@ -91,6 +91,7 @@ type DataStore interface {
|
||||||
Mappings(ctx context.Context) chronograf.MappingsStore
|
Mappings(ctx context.Context) chronograf.MappingsStore
|
||||||
Dashboards(ctx context.Context) chronograf.DashboardsStore
|
Dashboards(ctx context.Context) chronograf.DashboardsStore
|
||||||
Config(ctx context.Context) chronograf.ConfigStore
|
Config(ctx context.Context) chronograf.ConfigStore
|
||||||
|
OrganizationConfig(ctx context.Context) chronograf.OrganizationConfigStore
|
||||||
}
|
}
|
||||||
|
|
||||||
// ensure that Store implements a DataStore
|
// ensure that Store implements a DataStore
|
||||||
|
@ -98,18 +99,19 @@ var _ DataStore = &Store{}
|
||||||
|
|
||||||
// Store implements the DataStore interface
|
// Store implements the DataStore interface
|
||||||
type Store struct {
|
type Store struct {
|
||||||
SourcesStore chronograf.SourcesStore
|
SourcesStore chronograf.SourcesStore
|
||||||
ServersStore chronograf.ServersStore
|
ServersStore chronograf.ServersStore
|
||||||
LayoutsStore chronograf.LayoutsStore
|
LayoutsStore chronograf.LayoutsStore
|
||||||
UsersStore chronograf.UsersStore
|
UsersStore chronograf.UsersStore
|
||||||
DashboardsStore chronograf.DashboardsStore
|
DashboardsStore chronograf.DashboardsStore
|
||||||
MappingsStore chronograf.MappingsStore
|
MappingsStore chronograf.MappingsStore
|
||||||
OrganizationsStore chronograf.OrganizationsStore
|
OrganizationsStore chronograf.OrganizationsStore
|
||||||
ConfigStore chronograf.ConfigStore
|
ConfigStore chronograf.ConfigStore
|
||||||
|
OrganizationConfigStore chronograf.OrganizationConfigStore
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sources returns a noop.SourcesStore if the context has no organization specified
|
// Sources returns a noop.SourcesStore if the context has no organization specified
|
||||||
// and a organization.SourcesStore otherwise.
|
// and an organization.SourcesStore otherwise.
|
||||||
func (s *Store) Sources(ctx context.Context) chronograf.SourcesStore {
|
func (s *Store) Sources(ctx context.Context) chronograf.SourcesStore {
|
||||||
if isServer := hasServerContext(ctx); isServer {
|
if isServer := hasServerContext(ctx); isServer {
|
||||||
return s.SourcesStore
|
return s.SourcesStore
|
||||||
|
@ -122,7 +124,7 @@ func (s *Store) Sources(ctx context.Context) chronograf.SourcesStore {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Servers returns a noop.ServersStore if the context has no organization specified
|
// Servers returns a noop.ServersStore if the context has no organization specified
|
||||||
// and a organization.ServersStore otherwise.
|
// and an organization.ServersStore otherwise.
|
||||||
func (s *Store) Servers(ctx context.Context) chronograf.ServersStore {
|
func (s *Store) Servers(ctx context.Context) chronograf.ServersStore {
|
||||||
if isServer := hasServerContext(ctx); isServer {
|
if isServer := hasServerContext(ctx); isServer {
|
||||||
return s.ServersStore
|
return s.ServersStore
|
||||||
|
@ -157,7 +159,7 @@ func (s *Store) Users(ctx context.Context) chronograf.UsersStore {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Dashboards returns a noop.DashboardsStore if the context has no organization specified
|
// Dashboards returns a noop.DashboardsStore if the context has no organization specified
|
||||||
// and a organization.DashboardsStore otherwise.
|
// and an organization.DashboardsStore otherwise.
|
||||||
func (s *Store) Dashboards(ctx context.Context) chronograf.DashboardsStore {
|
func (s *Store) Dashboards(ctx context.Context) chronograf.DashboardsStore {
|
||||||
if isServer := hasServerContext(ctx); isServer {
|
if isServer := hasServerContext(ctx); isServer {
|
||||||
return s.DashboardsStore
|
return s.DashboardsStore
|
||||||
|
@ -169,17 +171,17 @@ func (s *Store) Dashboards(ctx context.Context) chronograf.DashboardsStore {
|
||||||
return &noop.DashboardsStore{}
|
return &noop.DashboardsStore{}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Settings returns a noop.SettingsStore if the context has no organization specified
|
// OrganizationConfig returns a noop.OrganizationConfigStore if the context has no organization specified
|
||||||
// and a organization.SettingsStore otherwise.
|
// and an organization.OrganizationConfigStore otherwise.
|
||||||
func (s *Store) Settings(ctx context.Context) chronograf.SettingsStore {
|
func (s *Store) OrganizationConfig(ctx context.Context) chronograf.OrganizationConfigStore {
|
||||||
if isServer := hasServerContext(ctx); isServer {
|
if isServer := hasServerContext(ctx); isServer {
|
||||||
return s.SettingsStore
|
return s.OrganizationConfigStore
|
||||||
}
|
}
|
||||||
if org, ok := hasOrganizationContext(ctx); ok {
|
if orgID, ok := hasOrganizationContext(ctx); ok {
|
||||||
return organizations.NewSettingsStore(s.SettingsStore, org)
|
return organizations.NewOrganizationConfigStore(s.OrganizationConfigStore, orgID)
|
||||||
}
|
}
|
||||||
|
|
||||||
return &noop.SettingsStore{}
|
return &noop.OrganizationConfigStore{}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Organizations returns the underlying OrganizationsStore.
|
// Organizations returns the underlying OrganizationsStore.
|
||||||
|
|
Loading…
Reference in New Issue