Initial commit
This commit is contained in:
commit
30c24998be
|
@ -0,0 +1,121 @@
|
||||||
|
package confection
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type (
|
||||||
|
config struct {
|
||||||
|
config interface{}
|
||||||
|
}
|
||||||
|
configField struct {
|
||||||
|
Path string `json:"path"`
|
||||||
|
IsRequired bool `json:"is_required"`
|
||||||
|
IsReadonly bool `json:"is_readonly"`
|
||||||
|
Title string `json:"title"`
|
||||||
|
Description string `json:"description"`
|
||||||
|
Options []string `json:"options"`
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
tJson = "json"
|
||||||
|
tTitle = "title"
|
||||||
|
tDescription = "description"
|
||||||
|
tAttrs = "attrs"
|
||||||
|
tOptions = "options"
|
||||||
|
aRequired = "required"
|
||||||
|
aReadonly = "readonly"
|
||||||
|
sep = ","
|
||||||
|
)
|
||||||
|
|
||||||
|
func (c *config) dump() ([]byte, error) {
|
||||||
|
var (
|
||||||
|
out bytes.Buffer
|
||||||
|
b []byte
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
|
||||||
|
if b, err = json.Marshal(c.config); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
// Indent with empty prefix and four spaces
|
||||||
|
if err = json.Indent(&out, b, "", " "); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return out.Bytes(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *config) load(b []byte) (err error) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: function draft, needs refactor
|
||||||
|
func (c *config) meta(prefix string) []*configField {
|
||||||
|
var (
|
||||||
|
fields = []*configField{}
|
||||||
|
cval = reflect.ValueOf(c.config)
|
||||||
|
typ = reflect.TypeOf(c.config)
|
||||||
|
kind = cval.Kind()
|
||||||
|
)
|
||||||
|
|
||||||
|
if kind != reflect.Struct {
|
||||||
|
panic(fmt.Errorf("Config is expected to be a Struct, not %s", kind.String()))
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < cval.NumField(); i++ {
|
||||||
|
var (
|
||||||
|
field = typ.Field(i)
|
||||||
|
val = cval.Field(i)
|
||||||
|
|
||||||
|
jsonKey = field.Tag.Get(tJson)
|
||||||
|
path = strings.Join([]string{prefix, jsonKey}, "/")
|
||||||
|
title = field.Tag.Get(tTitle)
|
||||||
|
description = field.Tag.Get(tDescription)
|
||||||
|
attrs = strings.Split(field.Tag.Get(tAttrs), sep)
|
||||||
|
options = strings.Split(field.Tag.Get(tOptions), sep)
|
||||||
|
|
||||||
|
cf = &configField{
|
||||||
|
Path: path,
|
||||||
|
Title: title,
|
||||||
|
Description: description,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// Skip field if no tags are set
|
||||||
|
if title == "" && len(attrs) == 0 && len(options) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Substitute field name for title if none set
|
||||||
|
if title == "" {
|
||||||
|
cf.Title = field.Name
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, attr := range attrs {
|
||||||
|
if attr == aRequired {
|
||||||
|
cf.IsRequired = true
|
||||||
|
}
|
||||||
|
if attr == aReadonly {
|
||||||
|
cf.IsReadonly = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fields = append(fields, cf)
|
||||||
|
|
||||||
|
// Recursion here
|
||||||
|
if val.Kind() == reflect.Struct {
|
||||||
|
subconf := &config{
|
||||||
|
config: val.Interface(),
|
||||||
|
}
|
||||||
|
fields = append(fields, subconf.meta(path)...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return fields
|
||||||
|
}
|
|
@ -0,0 +1,60 @@
|
||||||
|
package confection
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
|
)
|
||||||
|
|
||||||
|
type (
|
||||||
|
configFile struct {
|
||||||
|
path string
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func (cf *configFile) write(b []byte) (err error) {
|
||||||
|
var (
|
||||||
|
fd *os.File
|
||||||
|
n int // Bytes written successfully
|
||||||
|
)
|
||||||
|
|
||||||
|
if cf.isExist() {
|
||||||
|
if err = cf.mkdirp(); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fd, err = os.Create(cf.path)
|
||||||
|
} else {
|
||||||
|
fd, err = os.Open(cf.path)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer fd.Close()
|
||||||
|
|
||||||
|
n, err = fd.Write(b)
|
||||||
|
if err == nil && n != len(b) {
|
||||||
|
return fmt.Errorf("Failed to write config file: written %d/%d bytes", n, len(b))
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cf *configFile) read() ([]byte, error) {
|
||||||
|
if cf.isExist() {
|
||||||
|
return ioutil.ReadFile(cf.path)
|
||||||
|
} else {
|
||||||
|
return nil, fmt.Errorf("Config file does not exist")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cf *configFile) isExist() bool {
|
||||||
|
_, err := os.Stat(cf.path)
|
||||||
|
return os.IsExist(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cf *configFile) mkdirp() error {
|
||||||
|
dir := path.Dir(cf.path)
|
||||||
|
return os.MkdirAll(dir, 0755)
|
||||||
|
}
|
|
@ -0,0 +1,35 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"flag"
|
||||||
|
|
||||||
|
"github.com/localhots/confection"
|
||||||
|
)
|
||||||
|
|
||||||
|
type (
|
||||||
|
Config struct {
|
||||||
|
AppName string `json:"app_name" attrs:"required" title:"Application Name"`
|
||||||
|
BuildNumber int `json:"build_number" attrs:"readonly" title:"Build Number"`
|
||||||
|
EnableSignIn bool `json:"enable_sign_in" title:"Enable Sign-In"`
|
||||||
|
DatabaseDriver string `json:"database_driver" title:"Database Driver" options:"mysql,postgresql,mssql"`
|
||||||
|
DatabaseConfig DatabaseConfig `json:"database_config"`
|
||||||
|
}
|
||||||
|
DatabaseConfig struct {
|
||||||
|
Hostname string `json:"hostname"`
|
||||||
|
Port int `json:"port"`
|
||||||
|
Username string `json:"username"`
|
||||||
|
Password string `json:"password"`
|
||||||
|
Database string `json:"database" attrs:"required"`
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
confection.Setup()
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
conf := Config{
|
||||||
|
DatabaseConfig: DatabaseConfig{},
|
||||||
|
}
|
||||||
|
manager := confection.New(conf)
|
||||||
|
manager.StartServer()
|
||||||
|
}
|
|
@ -0,0 +1,46 @@
|
||||||
|
package confection
|
||||||
|
|
||||||
|
import (
|
||||||
|
"flag"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
type (
|
||||||
|
Manager struct {
|
||||||
|
mux *sync.Mutex
|
||||||
|
conf *config
|
||||||
|
file *configFile
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
configPath string
|
||||||
|
serverPort int
|
||||||
|
)
|
||||||
|
|
||||||
|
func Setup() {
|
||||||
|
flag.StringVar(&configPath, "config", "config.json", "Path to config file")
|
||||||
|
flag.IntVar(&serverPort, "config-port", 5050, "Config manager http port")
|
||||||
|
}
|
||||||
|
|
||||||
|
func New(conf interface{}) *Manager {
|
||||||
|
mgr := &Manager{
|
||||||
|
mux: &sync.Mutex{},
|
||||||
|
conf: &config{
|
||||||
|
config: conf,
|
||||||
|
},
|
||||||
|
file: &configFile{
|
||||||
|
path: configPath,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
return mgr
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Manager) StartServer() {
|
||||||
|
srv := &server{
|
||||||
|
manager: m,
|
||||||
|
port: serverPort,
|
||||||
|
}
|
||||||
|
srv.start()
|
||||||
|
}
|
|
@ -0,0 +1,33 @@
|
||||||
|
package confection
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
type (
|
||||||
|
server struct {
|
||||||
|
manager *Manager
|
||||||
|
port int
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func (s *server) start() {
|
||||||
|
portStr := ":" + strconv.Itoa(s.port)
|
||||||
|
|
||||||
|
fmt.Println("Configuration server is available at http://127.0.0.1" + portStr)
|
||||||
|
if err := http.ListenAndServe(portStr, s); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *server) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||||
|
jsn, err := json.Marshal(s.manager.conf.meta(""))
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
w.Write(jsn)
|
||||||
|
}
|
Loading…
Reference in New Issue