chronograf/filestore/filestore.go

123 lines
3.1 KiB
Go

// 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"
htmlTemplate "html/template"
"os"
"path"
"strings"
textTemplate "text/template"
)
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 := templated(environ(), name)
if err != nil {
return fmt.Errorf("resource %s not found", name)
}
return json.Unmarshal(octets, resource)
}
func loadText(name string, resource interface{}) error {
octets, err := textTemplated(environ(), name)
if err != nil {
return fmt.Errorf("resource %s not found", name)
}
return json.Unmarshal(octets, resource)
}
var env map[string]string
// templated returns all files html-templated using data
func templated(data interface{}, filenames ...string) ([]byte, error) {
t, err := htmlTemplate.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
}
// textTemplated returns all files text-templated using data
func textTemplated(data interface{}, filenames ...string) ([]byte, error) {
t, err := textTemplate.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
}