Files
matchbox/api/store.go
2016-02-01 22:09:39 -08:00

115 lines
2.7 KiB
Go

package api
import (
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"path/filepath"
)
// Store provides Group, Spec, Ignition, and Cloud config resources.
type Store interface {
BootstrapGroups([]Group) error
ListGroups() ([]Group, error)
Spec(id string) (*Spec, error)
CloudConfig(id string) (string, error)
IgnitionConfig(id string) (string, error)
}
// fileStore provides configs from an http.Filesystem.
type fileStore struct {
root http.FileSystem
groups []Group
}
// NewFileStore returns a Store backed by a filesystem directory.
func NewFileStore(root http.FileSystem) Store {
return &fileStore{
root: root,
}
}
// BootstrapGroups loads an initial collection of groups.
func (s *fileStore) BootstrapGroups(groups []Group) error {
s.groups = groups
return nil
}
// ListGroups returns the list of groups with matchers.
func (s *fileStore) ListGroups() ([]Group, error) {
return s.groups, nil
}
// Spec returns the Spec with the given id.
func (s *fileStore) Spec(id string) (*Spec, error) {
file, err := openFile(s.root, filepath.Join("specs", id, "spec.json"))
if err != nil {
log.Debugf("no spec %s", id)
return nil, err
}
defer file.Close()
spec := new(Spec)
err = json.NewDecoder(file).Decode(spec)
if err != nil {
log.Errorf("error decoding spec: %s", err)
return nil, err
}
return spec, err
}
// CloudConfig returns the cloud config with the given id.
func (s *fileStore) CloudConfig(id string) (string, error) {
file, err := openFile(s.root, filepath.Join("cloud", id))
if err != nil {
log.Debugf("no cloud config %s", id)
return "", err
}
defer file.Close()
b, err := ioutil.ReadAll(file)
if err != nil {
log.Errorf("error reading cloud config: %s", err)
return "", err
}
return string(b), err
}
// IgnitionConfig returns the ignition template with the given id.
func (s *fileStore) IgnitionConfig(id string) (string, error) {
file, err := openFile(s.root, filepath.Join("ignition", id))
if err != nil {
log.Debugf("no ignition config %s", id)
return "", err
}
defer file.Close()
b, err := ioutil.ReadAll(file)
if err != nil {
log.Errorf("error reading ignition config: %s", err)
return "", err
}
return string(b), err
}
// openFile attempts to open the file within the specified Filesystem. If
// successful, the http.File is returned and must be closed by the caller.
// Otherwise, the path was not a regular file that could be opened and an
// error is returned.
func openFile(fs http.FileSystem, path string) (http.File, error) {
file, err := fs.Open(path)
if err != nil {
return nil, err
}
info, err := file.Stat()
if err != nil {
file.Close()
return nil, err
}
if info.Mode().IsRegular() {
return file, nil
}
file.Close()
return nil, fmt.Errorf("%s is not a file on the given filesystem", path)
}