2016-10-10 15:45:35 +00:00
package canned
2016-10-11 00:34:05 +00:00
//go:generate go-bindata -o apps_gen.go -ignore README|apps -pkg canned .
2016-10-10 15:45:35 +00:00
import (
"encoding/json"
2016-10-11 17:52:16 +00:00
"fmt"
2016-10-10 15:45:35 +00:00
"io/ioutil"
"os"
"path"
2016-10-11 17:52:16 +00:00
"strings"
2016-10-10 15:45:35 +00:00
2016-10-20 14:38:23 +00:00
"github.com/influxdata/chronograf"
clog "github.com/influxdata/chronograf/log"
2016-10-10 15:45:35 +00:00
"golang.org/x/net/context"
)
const AppExt = ".json"
2016-10-20 14:38:23 +00:00
var logger = clog . New ( )
2016-10-10 15:45:35 +00:00
2016-10-11 17:52:16 +00:00
// Apps are canned JSON layouts. Implements LayoutStore.
2016-10-10 15:45:35 +00:00
type Apps struct {
Dir string // Dir is the directory contained the pre-canned applications.
2016-10-20 14:38:23 +00:00
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.
2016-10-10 15:45:35 +00:00
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
2016-10-20 14:38:23 +00:00
IDs chronograf . ID // IDs generate unique ids for new application layouts
2016-10-10 15:45:35 +00:00
}
2016-10-20 14:38:23 +00:00
func NewApps ( dir string , ids chronograf . ID ) chronograf . LayoutStore {
2016-10-10 15:45:35 +00:00
return & Apps {
Dir : dir ,
Load : loadFile ,
Filename : fileName ,
Create : createLayout ,
ReadDir : ioutil . ReadDir ,
Remove : os . Remove ,
2016-10-11 00:34:05 +00:00
IDs : ids ,
2016-10-10 15:45:35 +00:00
}
}
2016-10-20 14:38:23 +00:00
func fileName ( dir string , layout chronograf . Layout ) string {
2016-10-11 17:52:16 +00:00
base := fmt . Sprintf ( "%s_%s%s" , layout . Measurement , layout . ID , AppExt )
return path . Join ( dir , base )
2016-10-10 15:45:35 +00:00
}
2016-10-20 14:38:23 +00:00
func loadFile ( name string ) ( chronograf . Layout , error ) {
2016-10-10 15:45:35 +00:00
octets , err := ioutil . ReadFile ( name )
if err != nil {
logger .
WithField ( "component" , "apps" ) .
WithField ( "name" , name ) .
2016-10-11 00:34:05 +00:00
Error ( "Unable to read file" )
2016-10-20 14:38:23 +00:00
return chronograf . Layout { } , err
2016-10-10 15:45:35 +00:00
}
2016-10-20 14:38:23 +00:00
var layout chronograf . Layout
2016-10-10 15:45:35 +00:00
if err = json . Unmarshal ( octets , & layout ) ; err != nil {
logger .
WithField ( "component" , "apps" ) .
WithField ( "name" , name ) .
Error ( "File is not a layout" )
2016-10-20 14:38:23 +00:00
return chronograf . Layout { } , err
2016-10-10 15:45:35 +00:00
}
return layout , nil
}
2016-10-20 14:38:23 +00:00
func createLayout ( file string , layout chronograf . Layout ) error {
2016-10-10 15:45:35 +00:00
h , err := os . Create ( file )
if err != nil {
return err
}
defer h . Close ( )
2016-10-11 00:34:05 +00:00
if octets , err := json . MarshalIndent ( layout , " " , " " ) ; err != nil {
2016-10-10 15:45:35 +00:00
logger .
WithField ( "component" , "apps" ) .
WithField ( "name" , file ) .
Error ( "Unable to marshal layout:" , err )
return err
} else {
if _ , err := h . Write ( octets ) ; err != nil {
logger .
WithField ( "component" , "apps" ) .
WithField ( "name" , file ) .
Error ( "Unable to write layout:" , err )
return err
}
}
return nil
}
2016-10-20 14:38:23 +00:00
func ( a * Apps ) All ( ctx context . Context ) ( [ ] chronograf . Layout , error ) {
2016-10-10 15:45:35 +00:00
files , err := a . ReadDir ( a . Dir )
if err != nil {
return nil , err
}
2016-10-20 14:38:23 +00:00
layouts := [ ] chronograf . Layout { }
2016-10-10 15:45:35 +00:00
for _ , file := range files {
if path . Ext ( file . Name ( ) ) != AppExt {
continue
}
2016-10-11 00:34:05 +00:00
if layout , err := a . Load ( path . Join ( a . Dir , file . Name ( ) ) ) ; err != nil {
2016-10-10 15:45:35 +00:00
continue // We want to load all files we can.
} else {
layouts = append ( layouts , layout )
}
}
return layouts , nil
}
2016-10-20 14:38:23 +00:00
func ( a * Apps ) Add ( ctx context . Context , layout chronograf . Layout ) ( chronograf . Layout , error ) {
2016-10-11 00:34:05 +00:00
var err error
layout . ID , err = a . IDs . Generate ( )
2016-10-10 15:45:35 +00:00
file := a . Filename ( a . Dir , layout )
if err = a . Create ( file , layout ) ; err != nil {
2016-10-20 14:38:23 +00:00
return chronograf . Layout { } , err
2016-10-10 15:45:35 +00:00
}
return layout , nil
}
2016-10-20 14:38:23 +00:00
func ( a * Apps ) Delete ( ctx context . Context , layout chronograf . Layout ) error {
2016-10-11 17:52:16 +00:00
file , err := a . idToFile ( layout . ID )
if err != nil {
return err
}
2016-10-10 15:45:35 +00:00
if err := a . Remove ( file ) ; err != nil {
logger .
WithField ( "component" , "apps" ) .
WithField ( "name" , file ) .
Error ( "Unable to remove layout:" , err )
return err
}
return nil
}
2016-10-20 14:38:23 +00:00
func ( a * Apps ) Get ( ctx context . Context , ID string ) ( chronograf . Layout , error ) {
2016-10-11 17:52:16 +00:00
file , err := a . idToFile ( ID )
if err != nil {
2016-10-20 14:38:23 +00:00
return chronograf . Layout { } , err
2016-10-11 17:52:16 +00:00
}
2016-10-10 15:45:35 +00:00
l , err := a . Load ( file )
if err != nil {
2016-10-20 14:38:23 +00:00
return chronograf . Layout { } , chronograf . ErrLayoutNotFound
2016-10-10 15:45:35 +00:00
}
return l , nil
}
2016-10-20 14:38:23 +00:00
func ( a * Apps ) Update ( ctx context . Context , layout chronograf . Layout ) error {
2016-10-11 17:52:16 +00:00
file , err := a . idToFile ( layout . ID )
if err != nil {
2016-10-10 15:45:35 +00:00
return err
}
2016-10-11 17:52:16 +00:00
l , err := a . Load ( file )
if err != nil {
2016-10-20 14:38:23 +00:00
return chronograf . ErrLayoutNotFound
2016-10-11 17:52:16 +00:00
}
if err := a . Delete ( ctx , l ) ; err != nil {
return err
}
file = a . Filename ( a . Dir , layout )
2016-10-10 15:45:35 +00:00
return a . Create ( file , layout )
}
2016-10-11 17:52:16 +00:00
// idToFile takes an id and finds the associated filename
func ( a * Apps ) idToFile ( ID string ) ( string , error ) {
// Because the entire layout information is not known at this point, we need
// to try to find the name of the file through matching.
files , err := a . ReadDir ( a . Dir )
if err != nil {
return "" , err
}
var file string
for _ , f := range files {
if strings . Contains ( f . Name ( ) , ID ) {
file = path . Join ( a . Dir , f . Name ( ) )
}
}
if file == "" {
2016-10-20 14:38:23 +00:00
return "" , chronograf . ErrLayoutNotFound
2016-10-11 17:52:16 +00:00
}
return file , nil
}