chore: tidy and document filestore functionality
parent
3e5e8c5c55
commit
d470723faf
|
@ -700,14 +700,8 @@ type Layout struct {
|
|||
type LayoutsStore interface {
|
||||
// All returns all dashboards in the store
|
||||
All(context.Context) ([]Layout, error)
|
||||
// Add creates a new dashboard in the LayoutsStore
|
||||
Add(context.Context, Layout) (Layout, error)
|
||||
// Delete the dashboard from the store
|
||||
Delete(context.Context, Layout) error
|
||||
// Get retrieves Layout if `ID` exists
|
||||
Get(ctx context.Context, ID string) (Layout, error)
|
||||
// Update the dashboard in the store.
|
||||
Update(context.Context, Layout) error
|
||||
}
|
||||
|
||||
// ProtoboardMeta is the metadata of a Protoboard
|
||||
|
|
|
@ -3,7 +3,6 @@ package filestore
|
|||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
|
@ -14,64 +13,43 @@ import (
|
|||
// AppExt is the the file extension searched for in the directory for layout files
|
||||
const AppExt = ".json"
|
||||
|
||||
// Verify apps (layouts) implements layoutsStore interface.
|
||||
var _ chronograf.LayoutsStore = (*Apps)(nil)
|
||||
|
||||
// Apps are canned JSON layouts. Implements LayoutsStore.
|
||||
type Apps struct {
|
||||
Dir string // Dir is the directory contained the pre-canned applications.
|
||||
Load func(string) (chronograf.Layout, error) // Load loads string name and return a Layout
|
||||
Filename func(string, chronograf.Layout) string // Filename takes dir and layout and returns loadable file
|
||||
Create func(string, chronograf.Layout) error // Create will write layout 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 application layouts
|
||||
Logger chronograf.Logger
|
||||
Dir string // Dir is the directory contained the pre-canned applications.
|
||||
Load func(string) (chronograf.Layout, error) // Load loads string name and return a Layout
|
||||
ReadDir func(dirname string) ([]os.FileInfo, error) // ReadDir reads the directory named by dirname and returns a list of directory entries sorted by filename.
|
||||
IDs chronograf.ID // IDs generate unique ids for new application layouts
|
||||
Logger chronograf.Logger
|
||||
}
|
||||
|
||||
// NewApps constructs a layout store wrapping a file system directory
|
||||
func NewApps(dir string, ids chronograf.ID, logger chronograf.Logger) chronograf.LayoutsStore {
|
||||
return &Apps{
|
||||
Dir: dir,
|
||||
Load: loadFile,
|
||||
Filename: fileName,
|
||||
Create: createLayout,
|
||||
ReadDir: ioutil.ReadDir,
|
||||
Remove: os.Remove,
|
||||
IDs: ids,
|
||||
Logger: logger,
|
||||
Dir: dir,
|
||||
Load: loadFile,
|
||||
ReadDir: ioutil.ReadDir,
|
||||
IDs: ids,
|
||||
Logger: logger,
|
||||
}
|
||||
}
|
||||
|
||||
func fileName(dir string, layout chronograf.Layout) string {
|
||||
base := fmt.Sprintf("%s%s", layout.Measurement, AppExt)
|
||||
return path.Join(dir, base)
|
||||
}
|
||||
|
||||
func loadFile(name string) (chronograf.Layout, error) {
|
||||
octets, err := ioutil.ReadFile(name)
|
||||
if err != nil {
|
||||
return chronograf.Layout{}, chronograf.ErrLayoutNotFound
|
||||
}
|
||||
|
||||
var layout chronograf.Layout
|
||||
if err = json.Unmarshal(octets, &layout); err != nil {
|
||||
return chronograf.Layout{}, chronograf.ErrLayoutInvalid
|
||||
}
|
||||
|
||||
return layout, nil
|
||||
}
|
||||
|
||||
func createLayout(file string, layout chronograf.Layout) error {
|
||||
h, err := os.Create(file)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer h.Close()
|
||||
if octets, err := json.MarshalIndent(layout, " ", " "); err != nil {
|
||||
return chronograf.ErrLayoutInvalid
|
||||
} else if _, err := h.Write(octets); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// All returns all layouts from the directory
|
||||
func (a *Apps) All(ctx context.Context) ([]chronograf.Layout, error) {
|
||||
files, err := a.ReadDir(a.Dir)
|
||||
|
@ -93,51 +71,6 @@ func (a *Apps) All(ctx context.Context) ([]chronograf.Layout, error) {
|
|||
return layouts, nil
|
||||
}
|
||||
|
||||
// Add creates a new layout within the directory
|
||||
func (a *Apps) Add(ctx context.Context, layout chronograf.Layout) (chronograf.Layout, error) {
|
||||
var err error
|
||||
layout.ID, err = a.IDs.Generate()
|
||||
if err != nil {
|
||||
a.Logger.
|
||||
WithField("component", "apps").
|
||||
Error("Unable to generate ID")
|
||||
return chronograf.Layout{}, err
|
||||
}
|
||||
file := a.Filename(a.Dir, layout)
|
||||
if err = a.Create(file, layout); err != nil {
|
||||
if err == chronograf.ErrLayoutInvalid {
|
||||
a.Logger.
|
||||
WithField("component", "apps").
|
||||
WithField("name", file).
|
||||
Error("Invalid Layout: ", err)
|
||||
} else {
|
||||
a.Logger.
|
||||
WithField("component", "apps").
|
||||
WithField("name", file).
|
||||
Error("Unable to write layout:", err)
|
||||
}
|
||||
return chronograf.Layout{}, err
|
||||
}
|
||||
return layout, nil
|
||||
}
|
||||
|
||||
// Delete removes a layout file from the directory
|
||||
func (a *Apps) Delete(ctx context.Context, layout chronograf.Layout) error {
|
||||
_, file, err := a.idToFile(layout.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := a.Remove(file); err != nil {
|
||||
a.Logger.
|
||||
WithField("component", "apps").
|
||||
WithField("name", file).
|
||||
Error("Unable to remove layout:", err)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Get returns an app file from the layout directory
|
||||
func (a *Apps) Get(ctx context.Context, ID string) (chronograf.Layout, error) {
|
||||
l, file, err := a.idToFile(ID)
|
||||
|
@ -162,20 +95,6 @@ func (a *Apps) Get(ctx context.Context, ID string) (chronograf.Layout, error) {
|
|||
return l, nil
|
||||
}
|
||||
|
||||
// Update replaces a layout from the file system directory
|
||||
func (a *Apps) Update(ctx context.Context, layout chronograf.Layout) error {
|
||||
l, _, err := a.idToFile(layout.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := a.Delete(ctx, l); err != nil {
|
||||
return err
|
||||
}
|
||||
file := a.Filename(a.Dir, layout)
|
||||
return a.Create(file, layout)
|
||||
}
|
||||
|
||||
// idToFile takes an id and finds the associated filename
|
||||
func (a *Apps) idToFile(ID string) (chronograf.Layout, string, error) {
|
||||
// Because the entire layout information is not known at this point, we need
|
||||
|
|
|
@ -55,109 +55,6 @@ func TestAll(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestAdd(t *testing.T) {
|
||||
t.Parallel()
|
||||
var tests = []struct {
|
||||
Existing []chronograf.Layout
|
||||
Add chronograf.Layout
|
||||
ExpectedID string
|
||||
Err error
|
||||
}{
|
||||
{
|
||||
Existing: []chronograf.Layout{
|
||||
{ID: "1",
|
||||
Application: "howdy",
|
||||
},
|
||||
{ID: "2",
|
||||
Application: "doody",
|
||||
},
|
||||
},
|
||||
Add: chronograf.Layout{
|
||||
Application: "newbie",
|
||||
},
|
||||
ExpectedID: "3",
|
||||
Err: nil,
|
||||
},
|
||||
{
|
||||
Existing: []chronograf.Layout{},
|
||||
Add: chronograf.Layout{
|
||||
Application: "newbie",
|
||||
},
|
||||
ExpectedID: "1",
|
||||
Err: nil,
|
||||
},
|
||||
{
|
||||
Existing: nil,
|
||||
Add: chronograf.Layout{
|
||||
Application: "newbie",
|
||||
},
|
||||
ExpectedID: "",
|
||||
Err: errors.New("Error"),
|
||||
},
|
||||
}
|
||||
for i, test := range tests {
|
||||
apps, _ := MockApps(test.Existing, test.Err)
|
||||
layout, err := apps.Add(context.Background(), test.Add)
|
||||
if err != test.Err {
|
||||
t.Errorf("Test %d: apps add error expected: %v; actual: %v", i, test.Err, err)
|
||||
}
|
||||
|
||||
if layout.ID != test.ExpectedID {
|
||||
t.Errorf("Test %d: Layout ID should be equal; expected %s; actual %s", i, test.ExpectedID, layout.ID)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDelete(t *testing.T) {
|
||||
t.Parallel()
|
||||
var tests = []struct {
|
||||
Existing []chronograf.Layout
|
||||
DeleteID string
|
||||
Expected map[string]chronograf.Layout
|
||||
Err error
|
||||
}{
|
||||
{
|
||||
Existing: []chronograf.Layout{
|
||||
{ID: "1",
|
||||
Application: "howdy",
|
||||
},
|
||||
{ID: "2",
|
||||
Application: "doody",
|
||||
},
|
||||
},
|
||||
DeleteID: "1",
|
||||
Expected: map[string]chronograf.Layout{
|
||||
"dir/2.json": {ID: "2",
|
||||
Application: "doody",
|
||||
},
|
||||
},
|
||||
Err: nil,
|
||||
},
|
||||
{
|
||||
Existing: []chronograf.Layout{},
|
||||
DeleteID: "1",
|
||||
Expected: map[string]chronograf.Layout{},
|
||||
Err: chronograf.ErrLayoutNotFound,
|
||||
},
|
||||
{
|
||||
Existing: nil,
|
||||
DeleteID: "1",
|
||||
Expected: map[string]chronograf.Layout{},
|
||||
Err: errors.New("Error"),
|
||||
},
|
||||
}
|
||||
for i, test := range tests {
|
||||
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: 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestGet(t *testing.T) {
|
||||
t.Parallel()
|
||||
var tests = []struct {
|
||||
|
@ -207,68 +104,6 @@ func TestGet(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestUpdate(t *testing.T) {
|
||||
t.Parallel()
|
||||
var tests = []struct {
|
||||
Existing []chronograf.Layout
|
||||
Update chronograf.Layout
|
||||
Expected map[string]chronograf.Layout
|
||||
Err error
|
||||
}{
|
||||
{
|
||||
Existing: []chronograf.Layout{
|
||||
{ID: "1",
|
||||
Application: "howdy",
|
||||
},
|
||||
{ID: "2",
|
||||
Application: "doody",
|
||||
},
|
||||
},
|
||||
Update: chronograf.Layout{
|
||||
ID: "1",
|
||||
Application: "hello",
|
||||
Measurement: "measurement",
|
||||
},
|
||||
Expected: map[string]chronograf.Layout{
|
||||
"dir/1.json": {ID: "1",
|
||||
Application: "hello",
|
||||
Measurement: "measurement",
|
||||
},
|
||||
"dir/2.json": {ID: "2",
|
||||
Application: "doody",
|
||||
},
|
||||
},
|
||||
Err: nil,
|
||||
},
|
||||
{
|
||||
Existing: []chronograf.Layout{},
|
||||
Update: chronograf.Layout{
|
||||
ID: "1",
|
||||
},
|
||||
Expected: map[string]chronograf.Layout{},
|
||||
Err: chronograf.ErrLayoutNotFound,
|
||||
},
|
||||
{
|
||||
Existing: nil,
|
||||
Update: chronograf.Layout{
|
||||
ID: "1",
|
||||
},
|
||||
Expected: map[string]chronograf.Layout{},
|
||||
Err: chronograf.ErrLayoutNotFound,
|
||||
},
|
||||
}
|
||||
for i, test := range tests {
|
||||
apps, actual := MockApps(test.Existing, test.Err)
|
||||
err := apps.Update(context.Background(), test.Update)
|
||||
if err != test.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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type MockFileInfo struct {
|
||||
name string
|
||||
}
|
||||
|
@ -321,7 +156,7 @@ func MockApps(existing []chronograf.Layout, expected error) (filestore.Apps, *ma
|
|||
for _, l := range existing {
|
||||
layouts[fileName(dir, l)] = l
|
||||
}
|
||||
load := func(file string) (chronograf.Layout, error) {
|
||||
loadLayout := func(file string) (chronograf.Layout, error) {
|
||||
if expected != nil {
|
||||
return chronograf.Layout{}, expected
|
||||
}
|
||||
|
@ -333,14 +168,6 @@ func MockApps(existing []chronograf.Layout, expected error) (filestore.Apps, *ma
|
|||
return l, nil
|
||||
}
|
||||
|
||||
create := func(file string, layout chronograf.Layout) error {
|
||||
if expected != nil {
|
||||
return expected
|
||||
}
|
||||
layouts[file] = layout
|
||||
return nil
|
||||
}
|
||||
|
||||
readDir := func(dirname string) ([]os.FileInfo, error) {
|
||||
if expected != nil {
|
||||
return nil, expected
|
||||
|
@ -353,27 +180,17 @@ func MockApps(existing []chronograf.Layout, expected error) (filestore.Apps, *ma
|
|||
return info, nil
|
||||
}
|
||||
|
||||
remove := func(name string) error {
|
||||
if expected != nil {
|
||||
return expected
|
||||
}
|
||||
if _, ok := layouts[name]; !ok {
|
||||
return chronograf.ErrLayoutNotFound
|
||||
}
|
||||
delete(layouts, name)
|
||||
return nil
|
||||
}
|
||||
|
||||
return filestore.Apps{
|
||||
Dir: dir,
|
||||
Load: load,
|
||||
Filename: fileName,
|
||||
Create: create,
|
||||
ReadDir: readDir,
|
||||
Remove: remove,
|
||||
Dir: dir,
|
||||
Load: loadLayout,
|
||||
ReadDir: readDir,
|
||||
IDs: &MockID{
|
||||
id: len(existing),
|
||||
},
|
||||
Logger: clog.New(clog.ParseLevel("debug")),
|
||||
}, &layouts
|
||||
}
|
||||
|
||||
type apps struct {
|
||||
filestore.Apps
|
||||
}
|
||||
|
|
|
@ -2,12 +2,10 @@ package filestore
|
|||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"errors"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"strconv"
|
||||
|
||||
"github.com/influxdata/chronograf"
|
||||
)
|
||||
|
@ -15,13 +13,12 @@ import (
|
|||
// DashExt is the the file extension searched for in the directory for dashboard files
|
||||
const DashExt = ".dashboard"
|
||||
|
||||
var _ chronograf.DashboardsStore = &Dashboards{}
|
||||
// Verify dashboards implements dashboardsStore interface.
|
||||
var _ chronograf.DashboardsStore = (*Dashboards)(nil)
|
||||
|
||||
// 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
|
||||
|
@ -32,8 +29,6 @@ type Dashboards struct {
|
|||
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,
|
||||
|
@ -41,36 +36,6 @@ func NewDashboards(dir string, ids chronograf.ID, logger chronograf.Logger) chro
|
|||
}
|
||||
}
|
||||
|
||||
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)
|
||||
|
@ -84,7 +49,7 @@ func (d *Dashboards) All(ctx context.Context) ([]chronograf.Dashboard, error) {
|
|||
continue
|
||||
}
|
||||
var dashboard chronograf.Dashboard
|
||||
if err := d.Load(path.Join(d.Dir, file.Name()), &dashboard); err != nil {
|
||||
if err := load(path.Join(d.Dir, file.Name()), &dashboard); err != nil {
|
||||
continue // We want to load all files we can.
|
||||
} else {
|
||||
dashboards = append(dashboards, dashboard)
|
||||
|
@ -93,61 +58,6 @@ func (d *Dashboards) All(ctx context.Context) ([]chronograf.Dashboard, error) {
|
|||
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)
|
||||
|
@ -168,20 +78,6 @@ func (d *Dashboards) Get(ctx context.Context, id chronograf.DashboardID) (chrono
|
|||
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
|
||||
|
@ -198,7 +94,7 @@ func (d *Dashboards) idToFile(id chronograf.DashboardID) (chronograf.Dashboard,
|
|||
}
|
||||
file := path.Join(d.Dir, f.Name())
|
||||
var dashboard chronograf.Dashboard
|
||||
if err := d.Load(file, &dashboard); err != nil {
|
||||
if err := load(file, &dashboard); err != nil {
|
||||
return chronograf.Dashboard{}, "", err
|
||||
}
|
||||
if dashboard.ID == id {
|
||||
|
@ -208,3 +104,39 @@ func (d *Dashboards) idToFile(id chronograf.DashboardID) (chronograf.Dashboard,
|
|||
|
||||
return chronograf.Dashboard{}, "", chronograf.ErrDashboardNotFound
|
||||
}
|
||||
|
||||
// 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 := file(d.Dir, dashboard.Name, DashExt)
|
||||
return create(file, dashboard)
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// Add creates a new dashboard within the directory
|
||||
func (d *Dashboards) Add(ctx context.Context, dashboard chronograf.Dashboard) (chronograf.Dashboard, error) {
|
||||
return chronograf.Dashboard{}, errors.New("adding a dashboard to a filestore is not supported")
|
||||
}
|
||||
|
|
|
@ -1,24 +0,0 @@
|
|||
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
|
||||
}
|
|
@ -1,29 +0,0 @@
|
|||
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)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
|
@ -0,0 +1,100 @@
|
|||
// Package filestore provides the ability to read pre-defined resources from
|
||||
// a specified directory (--canned-path). This also provides a means to make
|
||||
// updates to certain resources. Adding new resources is not supported as it
|
||||
// paves the way for too much unexpected behaviors. Filestore is used in
|
||||
// conjunction with a 'multistore' and is usually tried last. By supporting
|
||||
// add functionality, a resource may mistakenly be saved to the filesystem
|
||||
// if the write to the db fails.
|
||||
//
|
||||
// Resources that are storable in a file are:
|
||||
// (CRUD refers to create, read, update, delete. An '_' means not supported)
|
||||
// Apps(layouts) - _R__
|
||||
// Dashboards - _RUD
|
||||
// Kapacitors - _RUD
|
||||
// Organizations - _R__
|
||||
// Protoboards - _R__
|
||||
// Sources - _RUD
|
||||
//
|
||||
// Caution should be taken when editing resources provided via the filestore,
|
||||
// especially in a distributed environment as unexpected behavior may occur.
|
||||
package filestore
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"html/template"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
)
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
func file(dir, name, ext string) string {
|
||||
base := fmt.Sprintf("%s%s", name, ext)
|
||||
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)
|
||||
}
|
||||
|
||||
var env map[string]string
|
||||
|
||||
// templatedFromEnv returns all files templated against environment variables
|
||||
func templatedFromEnv(filenames ...string) ([]byte, error) {
|
||||
return templated(environ(), filenames...)
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
|
@ -2,11 +2,11 @@ package filestore
|
|||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"strconv"
|
||||
|
||||
"github.com/influxdata/chronograf"
|
||||
)
|
||||
|
@ -14,13 +14,12 @@ import (
|
|||
// KapExt is the the file extension searched for in the directory for kapacitor files
|
||||
const KapExt = ".kap"
|
||||
|
||||
var _ chronograf.ServersStore = &Kapacitors{}
|
||||
// Verify Kapacitors implements serverStore interface.
|
||||
var _ chronograf.ServersStore = (*Kapacitors)(nil)
|
||||
|
||||
// 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
|
||||
|
@ -31,8 +30,6 @@ type Kapacitors struct {
|
|||
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,
|
||||
|
@ -40,11 +37,6 @@ func NewKapacitors(dir string, ids chronograf.ID, logger chronograf.Logger) chro
|
|||
}
|
||||
}
|
||||
|
||||
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)
|
||||
|
@ -58,7 +50,7 @@ func (d *Kapacitors) All(ctx context.Context) ([]chronograf.Server, error) {
|
|||
continue
|
||||
}
|
||||
var kapacitor chronograf.Server
|
||||
if err := d.Load(path.Join(d.Dir, file.Name()), &kapacitor); err != nil {
|
||||
if err := load(path.Join(d.Dir, file.Name()), &kapacitor); err != nil {
|
||||
var fmtErr = fmt.Errorf("Error loading kapacitor configuration from %v:\n%v", path.Join(d.Dir, file.Name()), err)
|
||||
d.Logger.Error(fmtErr)
|
||||
continue // We want to load all files we can.
|
||||
|
@ -69,61 +61,6 @@ func (d *Kapacitors) All(ctx context.Context) ([]chronograf.Server, error) {
|
|||
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)
|
||||
|
@ -144,20 +81,6 @@ func (d *Kapacitors) Get(ctx context.Context, id int) (chronograf.Server, error)
|
|||
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
|
||||
|
@ -174,7 +97,7 @@ func (d *Kapacitors) idToFile(id int) (chronograf.Server, string, error) {
|
|||
}
|
||||
file := path.Join(d.Dir, f.Name())
|
||||
var kapacitor chronograf.Server
|
||||
if err := d.Load(file, &kapacitor); err != nil {
|
||||
if err := load(file, &kapacitor); err != nil {
|
||||
return chronograf.Server{}, "", err
|
||||
}
|
||||
if kapacitor.ID == id {
|
||||
|
@ -184,3 +107,39 @@ func (d *Kapacitors) idToFile(id int) (chronograf.Server, string, error) {
|
|||
|
||||
return chronograf.Server{}, "", chronograf.ErrServerNotFound
|
||||
}
|
||||
|
||||
// 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 := file(d.Dir, kapacitor.Name, KapExt)
|
||||
return create(file, kapacitor)
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// Add creates a new kapacitor within the directory
|
||||
func (d *Kapacitors) Add(ctx context.Context, kapacitor chronograf.Server) (chronograf.Server, error) {
|
||||
return chronograf.Server{}, errors.New("adding a server to a filestore is not supported")
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@ package filestore
|
|||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"errors"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
|
@ -13,7 +13,8 @@ import (
|
|||
// OrgExt is the the file extension searched for in the directory for org files
|
||||
const OrgExt = ".org"
|
||||
|
||||
var _ chronograf.OrganizationsStore = &Organizations{}
|
||||
// Verify organizations implements organizationsStore interface.
|
||||
var _ chronograf.OrganizationsStore = (*Organizations)(nil)
|
||||
|
||||
// Organizations are JSON orgs stored in the filesystem
|
||||
type Organizations struct {
|
||||
|
@ -33,11 +34,6 @@ func NewOrganizations(dir string, logger chronograf.Logger) chronograf.Organizat
|
|||
}
|
||||
}
|
||||
|
||||
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)
|
||||
|
@ -66,31 +62,6 @@ func (o *Organizations) Get(ctx context.Context, query chronograf.OrganizationQu
|
|||
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
|
||||
|
@ -120,3 +91,28 @@ func (o *Organizations) findOrg(query chronograf.OrganizationQuery) (*chronograf
|
|||
|
||||
return nil, "", chronograf.ErrOrganizationNotFound
|
||||
}
|
||||
|
||||
// Add is not allowed for the filesystem organization store
|
||||
func (o *Organizations) Add(ctx context.Context, org *chronograf.Organization) (*chronograf.Organization, error) {
|
||||
return nil, errors.New("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 errors.New("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 errors.New("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 errors.New("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, errors.New("unable to get default organizations from the filestore")
|
||||
}
|
||||
|
|
|
@ -13,6 +13,9 @@ import (
|
|||
// ProtoboardExt is the the file extension searched for in the directory for protoboard files
|
||||
const ProtoboardExt = ".json"
|
||||
|
||||
// Verify Protoboards implements protoboardStore interface.
|
||||
var _ chronograf.ProtoboardsStore = (*Protoboards)(nil)
|
||||
|
||||
// Protoboards are instantiable JSON representation of dashbards. Implements ProtoboardsStore.
|
||||
type Protoboards struct {
|
||||
Dir string // Dir is the directory containing protoboard json definitions
|
||||
|
|
|
@ -2,11 +2,11 @@ package filestore
|
|||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"strconv"
|
||||
|
||||
"github.com/influxdata/chronograf"
|
||||
)
|
||||
|
@ -14,13 +14,12 @@ import (
|
|||
// SrcExt is the the file extension searched for in the directory for source files
|
||||
const SrcExt = ".src"
|
||||
|
||||
var _ chronograf.SourcesStore = &Sources{}
|
||||
// Verify sources implements sourcesStore interface.
|
||||
var _ chronograf.SourcesStore = (*Sources)(nil)
|
||||
|
||||
// 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
|
||||
|
@ -31,8 +30,6 @@ type Sources struct {
|
|||
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,
|
||||
|
@ -40,11 +37,6 @@ func NewSources(dir string, ids chronograf.ID, logger chronograf.Logger) chronog
|
|||
}
|
||||
}
|
||||
|
||||
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)
|
||||
|
@ -58,7 +50,7 @@ func (d *Sources) All(ctx context.Context) ([]chronograf.Source, error) {
|
|||
continue
|
||||
}
|
||||
var source chronograf.Source
|
||||
if err := d.Load(path.Join(d.Dir, file.Name()), &source); err != nil {
|
||||
if err := load(path.Join(d.Dir, file.Name()), &source); err != nil {
|
||||
var fmtErr = fmt.Errorf("Error loading source configuration from %v:\n%v", path.Join(d.Dir, file.Name()), err)
|
||||
d.Logger.Error(fmtErr)
|
||||
continue // We want to load all files we can.
|
||||
|
@ -69,61 +61,6 @@ func (d *Sources) All(ctx context.Context) ([]chronograf.Source, error) {
|
|||
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)
|
||||
|
@ -154,8 +91,25 @@ func (d *Sources) Update(ctx context.Context, source chronograf.Source) error {
|
|||
if err := d.Delete(ctx, board); err != nil {
|
||||
return err
|
||||
}
|
||||
file := sourceFile(d.Dir, source)
|
||||
return d.Create(file, source)
|
||||
file := file(d.Dir, source.Name, SrcExt)
|
||||
return create(file, source)
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// idToFile takes an id and finds the associated filename
|
||||
|
@ -174,7 +128,7 @@ func (d *Sources) idToFile(id int) (chronograf.Source, string, error) {
|
|||
}
|
||||
file := path.Join(d.Dir, f.Name())
|
||||
var source chronograf.Source
|
||||
if err := d.Load(file, &source); err != nil {
|
||||
if err := load(file, &source); err != nil {
|
||||
return chronograf.Source{}, "", err
|
||||
}
|
||||
if source.ID == id {
|
||||
|
@ -184,3 +138,8 @@ func (d *Sources) idToFile(id int) (chronograf.Source, string, error) {
|
|||
|
||||
return chronograf.Source{}, "", chronograf.ErrSourceNotFound
|
||||
}
|
||||
|
||||
// Add creates a new source within the directory
|
||||
func (d *Sources) Add(ctx context.Context, source chronograf.Source) (chronograf.Source, error) {
|
||||
return chronograf.Source{}, errors.New("adding a source to a filestore is not supported")
|
||||
}
|
||||
|
|
|
@ -1,28 +0,0 @@
|
|||
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...)
|
||||
}
|
|
@ -62,3 +62,26 @@ func Test_templated(t *testing.T) {
|
|||
})
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,15 +6,11 @@ import (
|
|||
"io/ioutil"
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/influxdata/chronograf"
|
||||
)
|
||||
|
||||
// TestNow is a set time for testing.
|
||||
var TestNow = time.Date(2000, time.January, 1, 0, 0, 0, 0, time.UTC)
|
||||
|
||||
// TestClient wraps *bolt.Client.
|
||||
type TestClient struct {
|
||||
Client *client
|
||||
|
@ -32,7 +28,6 @@ func NewTestClient() (*TestClient, error) {
|
|||
ctx := context.TODO()
|
||||
b, err := NewClient(ctx,
|
||||
WithPath(f.Name()),
|
||||
WithNow(func() time.Time { return TestNow }),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
|
@ -39,7 +39,6 @@ type client struct {
|
|||
db *bolt.DB
|
||||
isNew bool
|
||||
logger chronograf.Logger
|
||||
now func() time.Time
|
||||
path string
|
||||
}
|
||||
|
||||
|
@ -49,7 +48,6 @@ func NewClient(ctx context.Context, opts ...Option) (*client, error) {
|
|||
buildInfo: defaultBuildInfo,
|
||||
path: defaultBoltPath,
|
||||
logger: mocks.NewLogger(),
|
||||
now: time.Now,
|
||||
}
|
||||
|
||||
for i := range opts {
|
||||
|
@ -90,14 +88,6 @@ func WithPath(path string) Option {
|
|||
}
|
||||
}
|
||||
|
||||
// WithNow sets the function to use for the current time.
|
||||
func WithNow(fn func() time.Time) Option {
|
||||
return func(c *client) error {
|
||||
c.now = fn
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// Open opens or creates the boltDB file.
|
||||
func (c *client) open(ctx context.Context) error {
|
||||
if _, err := os.Stat(c.path); os.IsNotExist(err) {
|
||||
|
|
|
@ -4,16 +4,12 @@ import (
|
|||
"context"
|
||||
"errors"
|
||||
"io/ioutil"
|
||||
"time"
|
||||
|
||||
"github.com/influxdata/chronograf"
|
||||
"github.com/influxdata/chronograf/kv"
|
||||
"github.com/influxdata/chronograf/kv/bolt"
|
||||
)
|
||||
|
||||
// TestNow is a set time for testing.
|
||||
var TestNow = time.Date(2000, time.January, 1, 0, 0, 0, 0, time.UTC)
|
||||
|
||||
// NewTestClient creates new *bolt.Client with a set time and temp path.
|
||||
func NewTestClient() (*kv.Service, error) {
|
||||
f, err := ioutil.TempFile("", "chronograf-bolt-")
|
||||
|
@ -30,7 +26,6 @@ func NewTestClient() (*kv.Service, error) {
|
|||
ctx := context.TODO()
|
||||
b, err := bolt.NewClient(ctx,
|
||||
bolt.WithPath(f.Name()),
|
||||
bolt.WithNow(func() time.Time { return TestNow }),
|
||||
bolt.WithBuildInfo(build),
|
||||
)
|
||||
if err != nil {
|
||||
|
|
|
@ -39,47 +39,6 @@ func (s *layoutsStore) All(ctx context.Context) ([]chronograf.Layout, error) {
|
|||
|
||||
}
|
||||
|
||||
// Add creates a new Layout in the layoutsStore.
|
||||
func (s *layoutsStore) Add(ctx context.Context, src chronograf.Layout) (chronograf.Layout, error) {
|
||||
if err := s.client.kv.Update(ctx, func(tx Tx) error {
|
||||
b := tx.Bucket(layoutsBucket)
|
||||
id, err := s.IDs.Generate()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
src.ID = id
|
||||
if v, err := internal.MarshalLayout(src); err != nil {
|
||||
return err
|
||||
} else if err := b.Put([]byte(src.ID), v); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}); err != nil {
|
||||
return chronograf.Layout{}, err
|
||||
}
|
||||
|
||||
return src, nil
|
||||
}
|
||||
|
||||
// Delete removes the Layout from the layoutsStore
|
||||
func (s *layoutsStore) Delete(ctx context.Context, src chronograf.Layout) error {
|
||||
_, err := s.Get(ctx, src.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.client.kv.Update(ctx, func(tx Tx) error {
|
||||
if err := tx.Bucket(layoutsBucket).Delete([]byte(src.ID)); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Get returns a Layout if the id exists.
|
||||
func (s *layoutsStore) Get(ctx context.Context, id string) (chronograf.Layout, error) {
|
||||
var src chronograf.Layout
|
||||
|
@ -96,25 +55,3 @@ func (s *layoutsStore) Get(ctx context.Context, id string) (chronograf.Layout, e
|
|||
|
||||
return src, nil
|
||||
}
|
||||
|
||||
// Update a Layout
|
||||
func (s *layoutsStore) Update(ctx context.Context, src chronograf.Layout) error {
|
||||
if err := s.client.kv.Update(ctx, func(tx Tx) error {
|
||||
// Get an existing layout with the same ID.
|
||||
b := tx.Bucket(layoutsBucket)
|
||||
if v, err := b.Get([]byte(src.ID)); v == nil || err != nil {
|
||||
return chronograf.ErrLayoutNotFound
|
||||
}
|
||||
|
||||
if v, err := internal.MarshalLayout(src); err != nil {
|
||||
return err
|
||||
} else if err := b.Put([]byte(src.ID), v); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -2,10 +2,12 @@ package kv_test
|
|||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/influxdata/chronograf"
|
||||
"github.com/influxdata/chronograf/mocks"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
|
@ -45,21 +47,10 @@ func TestLayoutStore_All(t *testing.T) {
|
|||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
client, err := NewTestClient()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer client.Close()
|
||||
|
||||
s := client.LayoutsStore()
|
||||
s := mocksLayout(&tt.fields.layouts)
|
||||
ctx := context.Background()
|
||||
|
||||
for _, layout := range tt.fields.layouts {
|
||||
s.Add(ctx, layout)
|
||||
}
|
||||
|
||||
got, err := s.All(ctx)
|
||||
|
||||
if (err != nil) != (tt.wants.err != nil) {
|
||||
t.Errorf("LayoutsStore.All() error = %v, want error %v", err, tt.wants.err)
|
||||
return
|
||||
|
@ -75,137 +66,6 @@ func TestLayoutStore_All(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestLayoutStore_Add(t *testing.T) {
|
||||
type args struct {
|
||||
layout chronograf.Layout
|
||||
}
|
||||
type wants struct {
|
||||
layout chronograf.Layout
|
||||
err error
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
wants wants
|
||||
}{
|
||||
{
|
||||
name: "simple",
|
||||
args: args{
|
||||
layout: chronograf.Layout{
|
||||
Application: "test",
|
||||
Measurement: "test",
|
||||
},
|
||||
},
|
||||
wants: wants{
|
||||
layout: chronograf.Layout{
|
||||
Application: "test",
|
||||
Measurement: "test",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
client, err := NewTestClient()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer client.Close()
|
||||
|
||||
s := client.LayoutsStore()
|
||||
ctx := context.Background()
|
||||
|
||||
l, err := s.Add(ctx, tt.args.layout)
|
||||
|
||||
if (err != nil) != (tt.wants.err != nil) {
|
||||
t.Errorf("LayoutsStore.Add() error = %v, want error %v", err, tt.wants.err)
|
||||
return
|
||||
}
|
||||
|
||||
got, err := s.Get(ctx, l.ID)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to get layout: %v", err)
|
||||
return
|
||||
}
|
||||
if diff := cmp.Diff(got.Application, tt.wants.layout.Application); diff != "" {
|
||||
t.Errorf("LayoutStore.Add():\n-got/+want\ndiff %s", diff)
|
||||
return
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestLayoutStore_Delete(t *testing.T) {
|
||||
type fields struct {
|
||||
layouts []chronograf.Layout
|
||||
}
|
||||
type wants struct {
|
||||
err error
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
wants wants
|
||||
}{
|
||||
{
|
||||
name: "simple",
|
||||
fields: fields{
|
||||
layouts: []chronograf.Layout{
|
||||
{
|
||||
Application: "test",
|
||||
Measurement: "test",
|
||||
},
|
||||
},
|
||||
},
|
||||
wants: wants{
|
||||
err: nil,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "layout not found",
|
||||
fields: fields{
|
||||
layouts: []chronograf.Layout{
|
||||
{
|
||||
Application: "test",
|
||||
Measurement: "test",
|
||||
},
|
||||
},
|
||||
},
|
||||
wants: wants{
|
||||
err: chronograf.ErrLayoutNotFound,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
client, err := NewTestClient()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer client.Close()
|
||||
|
||||
s := client.LayoutsStore()
|
||||
ctx := context.Background()
|
||||
|
||||
var l chronograf.Layout
|
||||
for _, layout := range tt.fields.layouts {
|
||||
l, _ = s.Add(ctx, layout)
|
||||
}
|
||||
|
||||
err = s.Delete(ctx, l)
|
||||
if (err != nil) != (tt.wants.err != nil) {
|
||||
err = s.Delete(ctx, l)
|
||||
if (err != nil) != (tt.wants.err != nil) {
|
||||
t.Errorf("LayoutsStore.Delete() error = %v, want error %v", err, tt.wants.err)
|
||||
return
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestLayoutStore_Get(t *testing.T) {
|
||||
type fields struct {
|
||||
layouts []chronograf.Layout
|
||||
|
@ -224,10 +84,12 @@ func TestLayoutStore_Get(t *testing.T) {
|
|||
fields: fields{
|
||||
layouts: []chronograf.Layout{
|
||||
{
|
||||
ID: "A",
|
||||
Application: "test",
|
||||
Measurement: "test",
|
||||
},
|
||||
{
|
||||
ID: "B",
|
||||
Application: "test2",
|
||||
Measurement: "test2",
|
||||
},
|
||||
|
@ -235,6 +97,7 @@ func TestLayoutStore_Get(t *testing.T) {
|
|||
},
|
||||
wants: wants{
|
||||
layout: chronograf.Layout{
|
||||
ID: "B",
|
||||
Application: "test2",
|
||||
Measurement: "test2",
|
||||
},
|
||||
|
@ -259,25 +122,15 @@ func TestLayoutStore_Get(t *testing.T) {
|
|||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
client, err := NewTestClient()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer client.Close()
|
||||
|
||||
s := client.LayoutsStore()
|
||||
s := mocksLayout(&tt.fields.layouts)
|
||||
ctx := context.Background()
|
||||
|
||||
var l chronograf.Layout
|
||||
for _, layout := range tt.fields.layouts {
|
||||
l, _ = s.Add(ctx, layout)
|
||||
}
|
||||
|
||||
id := tt.fields.layouts[len(tt.fields.layouts)-1].ID
|
||||
if tt.wants.err != nil {
|
||||
s.Delete(ctx, l)
|
||||
tt.fields.layouts = tt.fields.layouts[:len(tt.fields.layouts)-1]
|
||||
}
|
||||
|
||||
got, err := s.Get(ctx, l.ID)
|
||||
got, err := s.Get(ctx, id)
|
||||
if (err != nil) != (tt.wants.err != nil) {
|
||||
t.Errorf("LayoutsStore.Get() error = %v, want error %v", err, tt.wants.err)
|
||||
return
|
||||
|
@ -291,71 +144,18 @@ func TestLayoutStore_Get(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestLayoutStore_Update(t *testing.T) {
|
||||
type fields struct {
|
||||
layouts []chronograf.Layout
|
||||
}
|
||||
type wants struct {
|
||||
layout chronograf.Layout
|
||||
err error
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
wants wants
|
||||
}{
|
||||
{
|
||||
name: "simple",
|
||||
fields: fields{
|
||||
layouts: []chronograf.Layout{
|
||||
{
|
||||
Application: "test",
|
||||
Measurement: "test",
|
||||
},
|
||||
{
|
||||
Application: "test2",
|
||||
Measurement: "test2",
|
||||
},
|
||||
},
|
||||
},
|
||||
wants: wants{
|
||||
layout: chronograf.Layout{
|
||||
Application: "test3",
|
||||
Measurement: "test3",
|
||||
},
|
||||
err: nil,
|
||||
},
|
||||
func mocksLayout(layouts *[]chronograf.Layout) mocks.LayoutsStore {
|
||||
return mocks.LayoutsStore{
|
||||
AllF: func(ctx context.Context) ([]chronograf.Layout, error) {
|
||||
return *layouts, nil
|
||||
},
|
||||
GetF: func(ctx context.Context, id string) (chronograf.Layout, error) {
|
||||
for _, l := range *layouts {
|
||||
if l.ID == id {
|
||||
return l, nil
|
||||
}
|
||||
}
|
||||
return chronograf.Layout{}, errors.New("no layout found")
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
client, err := NewTestClient()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer client.Close()
|
||||
|
||||
s := client.LayoutsStore()
|
||||
ctx := context.Background()
|
||||
|
||||
var l chronograf.Layout
|
||||
for _, layout := range tt.fields.layouts {
|
||||
l, _ = s.Add(ctx, layout)
|
||||
}
|
||||
|
||||
l.Application = "test3"
|
||||
l.Measurement = "test3"
|
||||
|
||||
err = s.Update(ctx, l)
|
||||
if (err != nil) != (tt.wants.err != nil) {
|
||||
t.Errorf("LayoutsStore.Update() error = %v, want error %v", err, tt.wants.err)
|
||||
return
|
||||
}
|
||||
if diff := cmp.Diff(l.Application, tt.wants.layout.Application); diff != "" {
|
||||
t.Errorf("LayoutStore.Update():\n-got/+want\ndiff %s", diff)
|
||||
return
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +1,9 @@
|
|||
// Package memdb provides a transient layer to store the InfluxDB and Kapacitor
|
||||
// configured via flags at Chronograf start.
|
||||
// Caution should be taken when editing resources generated from cli flags,
|
||||
// especially in a distributed environment as unexpected behavior may occur.
|
||||
// Instead, it is suggested that chronograf be restarted to pick up the new
|
||||
// flag/evar changes.
|
||||
package memdb
|
||||
|
||||
import (
|
||||
|
|
|
@ -9,29 +9,14 @@ import (
|
|||
var _ chronograf.LayoutsStore = &LayoutsStore{}
|
||||
|
||||
type LayoutsStore struct {
|
||||
AddF func(ctx context.Context, layout chronograf.Layout) (chronograf.Layout, error)
|
||||
AllF func(ctx context.Context) ([]chronograf.Layout, error)
|
||||
DeleteF func(ctx context.Context, layout chronograf.Layout) error
|
||||
GetF func(ctx context.Context, id string) (chronograf.Layout, error)
|
||||
UpdateF func(ctx context.Context, layout chronograf.Layout) error
|
||||
}
|
||||
|
||||
func (s *LayoutsStore) Add(ctx context.Context, layout chronograf.Layout) (chronograf.Layout, error) {
|
||||
return s.AddF(ctx, layout)
|
||||
AllF func(ctx context.Context) ([]chronograf.Layout, error)
|
||||
GetF func(ctx context.Context, id string) (chronograf.Layout, error)
|
||||
}
|
||||
|
||||
func (s *LayoutsStore) All(ctx context.Context) ([]chronograf.Layout, error) {
|
||||
return s.AllF(ctx)
|
||||
}
|
||||
|
||||
func (s *LayoutsStore) Delete(ctx context.Context, layout chronograf.Layout) error {
|
||||
return s.DeleteF(ctx, layout)
|
||||
}
|
||||
|
||||
func (s *LayoutsStore) Get(ctx context.Context, id string) (chronograf.Layout, error) {
|
||||
return s.GetF(ctx, id)
|
||||
}
|
||||
|
||||
func (s *LayoutsStore) Update(ctx context.Context, layout chronograf.Layout) error {
|
||||
return s.UpdateF(ctx, layout)
|
||||
}
|
||||
|
|
|
@ -42,32 +42,6 @@ func (s *Layouts) All(ctx context.Context) ([]chronograf.Layout, error) {
|
|||
return all, nil
|
||||
}
|
||||
|
||||
// Add creates a new dashboard in the LayoutsStore. Tries each store sequentially until success.
|
||||
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
|
||||
l, err = store.Add(ctx, layout)
|
||||
if err == nil {
|
||||
return l, nil
|
||||
}
|
||||
}
|
||||
return chronograf.Layout{}, err
|
||||
}
|
||||
|
||||
// Delete the dashboard from the store. Searches through all stores to find Layout and
|
||||
// then deletes from that store.
|
||||
func (s *Layouts) Delete(ctx context.Context, layout chronograf.Layout) error {
|
||||
var err error
|
||||
for _, store := range s.Stores {
|
||||
err = store.Delete(ctx, layout)
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// Get retrieves Layout if `ID` exists. Searches through each store sequentially until success.
|
||||
func (s *Layouts) Get(ctx context.Context, ID string) (chronograf.Layout, error) {
|
||||
var err error
|
||||
|
@ -80,15 +54,3 @@ func (s *Layouts) Get(ctx context.Context, ID string) (chronograf.Layout, error)
|
|||
}
|
||||
return chronograf.Layout{}, err
|
||||
}
|
||||
|
||||
// Update the dashboard in the store. Searches through each store sequentially until success.
|
||||
func (s *Layouts) Update(ctx context.Context, layout chronograf.Layout) error {
|
||||
var err error
|
||||
for _, store := range s.Stores {
|
||||
err = store.Update(ctx, layout)
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -125,5 +125,4 @@ func (multi *OrganizationsStore) DefaultOrganization(ctx context.Context) (*chro
|
|||
errors = append(errors, err.Error())
|
||||
}
|
||||
return nil, fmt.Errorf("Unknown error while getting default organization: %s", strings.Join(errors, " "))
|
||||
|
||||
}
|
||||
|
|
|
@ -2,7 +2,6 @@ package multistore
|
|||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/influxdata/chronograf"
|
||||
)
|
||||
|
@ -55,18 +54,3 @@ func (s *Protoboards) Get(ctx context.Context, ID string) (chronograf.Protoboard
|
|||
}
|
||||
return chronograf.Protoboard{}, err
|
||||
}
|
||||
|
||||
// Add creates a new protoboard in the protoboardsStore.
|
||||
func (s *Protoboards) Add(ctx context.Context, protoboard chronograf.Protoboard) (chronograf.Protoboard, error) {
|
||||
return chronograf.Protoboard{}, fmt.Errorf("Add to multistore/protoboards not supported")
|
||||
}
|
||||
|
||||
// Delete the protoboard from the store.
|
||||
func (s *Protoboards) Delete(ctx context.Context, protoboard chronograf.Protoboard) error {
|
||||
return fmt.Errorf("Delete to multistore/protoboards not supported")
|
||||
}
|
||||
|
||||
// Update the protoboard in the store.
|
||||
func (s *Protoboards) Update(ctx context.Context, protoboard chronograf.Protoboard) error {
|
||||
return fmt.Errorf("Update to multistore/protoboards not supported")
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue