feat(config): add config loading functions
This commit is contained in:
parent
32a003d789
commit
48a239bef2
5 changed files with 576 additions and 0 deletions
107
src/server/config_load.go
Normal file
107
src/server/config_load.go
Normal file
|
@ -0,0 +1,107 @@
|
|||
package server
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/ihexxa/gocfg"
|
||||
|
||||
"github.com/ihexxa/quickshare/src/db/sitestore"
|
||||
"github.com/ihexxa/quickshare/src/kvstore/boltdbpvd"
|
||||
)
|
||||
|
||||
type Opts struct {
|
||||
Host string `short:"h" long:"host" description:"server hostname"`
|
||||
Port int `short:"p" long:"port" description:"server port"`
|
||||
DbPath string `short:"d" long:"db" description:"path of the quickshare.db"`
|
||||
Configs []string `short:"c" long:"configs" description:"config path"`
|
||||
}
|
||||
|
||||
func LoadCfg(opts *Opts) (*gocfg.Cfg, error) {
|
||||
defaultCfg, err := DefaultConfig()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cfg, err := gocfg.New(NewConfig()).Load(gocfg.JSONStr(defaultCfg))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if opts.DbPath != "" {
|
||||
cfg, err = mergeDbConfig(cfg, opts.DbPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
_, err := os.Stat(boltdbpvd.DBName)
|
||||
if err == nil {
|
||||
cfg, err = mergeDbConfig(cfg, boltdbpvd.DBName)
|
||||
} else if err != nil {
|
||||
if !os.IsNotExist(err) {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cfg, err = mergeConfigFiles(cfg, opts.Configs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return mergeArgs(cfg, opts)
|
||||
}
|
||||
|
||||
func mergeDbConfig(cfg *gocfg.Cfg, dbPath string) (*gocfg.Cfg, error) {
|
||||
kv := boltdbpvd.New(dbPath, 1024)
|
||||
defer kv.Close()
|
||||
|
||||
siteStore, err := sitestore.NewSiteStore(kv)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("fail to new site config store: %s", err)
|
||||
}
|
||||
|
||||
clientCfg, err := siteStore.GetCfg()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
clientCfgBytes, err := json.Marshal(clientCfg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cfgStr := fmt.Sprintf(`{"site": %s}`, string(clientCfgBytes))
|
||||
return cfg.Load(gocfg.JSONStr(cfgStr))
|
||||
}
|
||||
|
||||
func mergeConfigFiles(cfg *gocfg.Cfg, configPaths []string) (*gocfg.Cfg, error) {
|
||||
var err error
|
||||
|
||||
for _, configPath := range configPaths {
|
||||
if strings.HasSuffix(configPath, ".yml") || strings.HasSuffix(configPath, ".yaml") {
|
||||
cfg, err = cfg.Load(gocfg.YAML(configPath))
|
||||
} else if strings.HasSuffix(configPath, ".json") {
|
||||
cfg, err = cfg.Load(gocfg.JSON(configPath))
|
||||
} else {
|
||||
return nil, fmt.Errorf("unknown config file type (.yml .yaml .json are supported): %s", configPath)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return cfg, nil
|
||||
}
|
||||
|
||||
func mergeArgs(cfg *gocfg.Cfg, opts *Opts) (*gocfg.Cfg, error) {
|
||||
if opts.Host != "" {
|
||||
cfg.SetString("Server.Host", opts.Host)
|
||||
}
|
||||
if opts.Port != 0 {
|
||||
cfg.SetInt("Server.Port", opts.Port)
|
||||
}
|
||||
|
||||
return cfg, nil
|
||||
}
|
352
src/server/config_load_test.go
Normal file
352
src/server/config_load_test.go
Normal file
|
@ -0,0 +1,352 @@
|
|||
package server
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/ihexxa/gocfg"
|
||||
|
||||
"github.com/ihexxa/quickshare/src/db/sitestore"
|
||||
"github.com/ihexxa/quickshare/src/db/userstore"
|
||||
)
|
||||
|
||||
func TestLoadCfg(t *testing.T) {
|
||||
argsList := []*Opts{
|
||||
// default config
|
||||
&Opts{
|
||||
Host: "",
|
||||
Port: 0,
|
||||
DbPath: "",
|
||||
Configs: []string{},
|
||||
},
|
||||
// default config + db
|
||||
&Opts{
|
||||
Host: "",
|
||||
Port: 0,
|
||||
DbPath: "testdata",
|
||||
Configs: []string{},
|
||||
},
|
||||
// default config + db + config_1
|
||||
&Opts{
|
||||
Host: "",
|
||||
Port: 0,
|
||||
DbPath: "testdata",
|
||||
Configs: []string{"testdata/config_1.yml"},
|
||||
},
|
||||
// default config + db + config_1 + config_2
|
||||
&Opts{
|
||||
Host: "",
|
||||
Port: 0,
|
||||
DbPath: "testdata",
|
||||
Configs: []string{"testdata/config_1.yml", "testdata/config_4.yml"},
|
||||
},
|
||||
// config partial overwrite
|
||||
&Opts{
|
||||
Host: "",
|
||||
Port: 0,
|
||||
DbPath: "testdata",
|
||||
Configs: []string{
|
||||
"testdata/config_1.yml",
|
||||
"testdata/config_4.yml",
|
||||
"testdata/config_partial_users.yml",
|
||||
},
|
||||
},
|
||||
// arg overwrite
|
||||
}
|
||||
|
||||
cfg1 := DefaultConfigStruct()
|
||||
|
||||
cfg2 := DefaultConfigStruct()
|
||||
cfg2.Site.ClientCfg.SiteName = "Quickshare"
|
||||
cfg2.Site.ClientCfg.SiteDesc = "Quickshare"
|
||||
cfg2.Site.ClientCfg.Bg.Url = "test.png"
|
||||
cfg2.Site.ClientCfg.Bg.Repeat = "no-repeat"
|
||||
cfg2.Site.ClientCfg.Bg.Position = "top"
|
||||
cfg2.Site.ClientCfg.Bg.Align = "scroll"
|
||||
|
||||
cfg3 := &Config{
|
||||
Fs: &FSConfig{
|
||||
Root: "1",
|
||||
OpensLimit: 1,
|
||||
OpenTTL: 1,
|
||||
},
|
||||
Users: &UsersCfg{
|
||||
EnableAuth: true,
|
||||
DefaultAdmin: "1",
|
||||
DefaultAdminPwd: "1",
|
||||
CookieTTL: 1,
|
||||
CookieSecure: true,
|
||||
CookieHttpOnly: true,
|
||||
MinUserNameLen: 1,
|
||||
MinPwdLen: 1,
|
||||
CaptchaWidth: 1,
|
||||
CaptchaHeight: 1,
|
||||
CaptchaEnabled: true,
|
||||
UploadSpeedLimit: 1,
|
||||
DownloadSpeedLimit: 1,
|
||||
SpaceLimit: 1,
|
||||
LimiterCapacity: 1,
|
||||
LimiterCyc: 1,
|
||||
PredefinedUsers: []*userstore.UserCfg{
|
||||
&userstore.UserCfg{
|
||||
Name: "1",
|
||||
Pwd: "1",
|
||||
Role: "1",
|
||||
},
|
||||
},
|
||||
},
|
||||
Secrets: &Secrets{
|
||||
TokenSecret: "1",
|
||||
},
|
||||
Server: &ServerCfg{
|
||||
Debug: true,
|
||||
Host: "1",
|
||||
Port: 1,
|
||||
ReadTimeout: 1,
|
||||
WriteTimeout: 1,
|
||||
MaxHeaderBytes: 1,
|
||||
PublicPath: "1",
|
||||
},
|
||||
Workers: &WorkerPoolCfg{
|
||||
QueueSize: 1,
|
||||
SleepCyc: 1,
|
||||
WorkerCount: 1,
|
||||
},
|
||||
Site: &sitestore.SiteConfig{
|
||||
ClientCfg: &sitestore.ClientConfig{
|
||||
SiteName: "1",
|
||||
SiteDesc: "1",
|
||||
Bg: &sitestore.BgConfig{
|
||||
Url: "1",
|
||||
Repeat: "1",
|
||||
Position: "1",
|
||||
Align: "1",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
cfg4 := &Config{
|
||||
Fs: &FSConfig{
|
||||
Root: "4",
|
||||
OpensLimit: 4,
|
||||
OpenTTL: 4,
|
||||
},
|
||||
Users: &UsersCfg{
|
||||
EnableAuth: false,
|
||||
DefaultAdmin: "4",
|
||||
DefaultAdminPwd: "4",
|
||||
CookieTTL: 4,
|
||||
CookieSecure: false,
|
||||
CookieHttpOnly: false,
|
||||
MinUserNameLen: 4,
|
||||
MinPwdLen: 4,
|
||||
CaptchaWidth: 4,
|
||||
CaptchaHeight: 4,
|
||||
CaptchaEnabled: false,
|
||||
UploadSpeedLimit: 4,
|
||||
DownloadSpeedLimit: 4,
|
||||
SpaceLimit: 4,
|
||||
LimiterCapacity: 4,
|
||||
LimiterCyc: 4,
|
||||
PredefinedUsers: []*userstore.UserCfg{
|
||||
&userstore.UserCfg{
|
||||
Name: "4",
|
||||
Pwd: "4",
|
||||
Role: "4",
|
||||
},
|
||||
},
|
||||
},
|
||||
Secrets: &Secrets{
|
||||
TokenSecret: "4",
|
||||
},
|
||||
Server: &ServerCfg{
|
||||
Debug: false,
|
||||
Host: "4",
|
||||
Port: 4,
|
||||
ReadTimeout: 4,
|
||||
WriteTimeout: 4,
|
||||
MaxHeaderBytes: 4,
|
||||
PublicPath: "4",
|
||||
},
|
||||
Workers: &WorkerPoolCfg{
|
||||
QueueSize: 4,
|
||||
SleepCyc: 4,
|
||||
WorkerCount: 4,
|
||||
},
|
||||
Site: &sitestore.SiteConfig{
|
||||
ClientCfg: &sitestore.ClientConfig{
|
||||
SiteName: "4",
|
||||
SiteDesc: "4",
|
||||
Bg: &sitestore.BgConfig{
|
||||
Url: "4",
|
||||
Repeat: "4",
|
||||
Position: "4",
|
||||
Align: "4",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
cfg5 := &Config{
|
||||
Fs: &FSConfig{
|
||||
Root: "4",
|
||||
OpensLimit: 4,
|
||||
OpenTTL: 4,
|
||||
},
|
||||
Users: &UsersCfg{
|
||||
EnableAuth: true,
|
||||
DefaultAdmin: "5",
|
||||
DefaultAdminPwd: "5",
|
||||
CookieTTL: 5,
|
||||
CookieSecure: true,
|
||||
CookieHttpOnly: true,
|
||||
MinUserNameLen: 5,
|
||||
MinPwdLen: 5,
|
||||
CaptchaWidth: 5,
|
||||
CaptchaHeight: 5,
|
||||
CaptchaEnabled: true,
|
||||
UploadSpeedLimit: 5,
|
||||
DownloadSpeedLimit: 5,
|
||||
SpaceLimit: 5,
|
||||
LimiterCapacity: 5,
|
||||
LimiterCyc: 5,
|
||||
PredefinedUsers: []*userstore.UserCfg{
|
||||
&userstore.UserCfg{
|
||||
Name: "5",
|
||||
Pwd: "5",
|
||||
Role: "5",
|
||||
},
|
||||
},
|
||||
},
|
||||
Secrets: &Secrets{
|
||||
TokenSecret: "4",
|
||||
},
|
||||
Server: &ServerCfg{
|
||||
Debug: false,
|
||||
Host: "4",
|
||||
Port: 4,
|
||||
ReadTimeout: 4,
|
||||
WriteTimeout: 4,
|
||||
MaxHeaderBytes: 4,
|
||||
PublicPath: "4",
|
||||
},
|
||||
Workers: &WorkerPoolCfg{
|
||||
QueueSize: 4,
|
||||
SleepCyc: 4,
|
||||
WorkerCount: 4,
|
||||
},
|
||||
Site: &sitestore.SiteConfig{
|
||||
ClientCfg: &sitestore.ClientConfig{
|
||||
SiteName: "4",
|
||||
SiteDesc: "4",
|
||||
Bg: &sitestore.BgConfig{
|
||||
Url: "4",
|
||||
Repeat: "4",
|
||||
Position: "4",
|
||||
Align: "4",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
expects := []*Config{
|
||||
cfg1,
|
||||
cfg2,
|
||||
cfg3,
|
||||
cfg4,
|
||||
cfg5,
|
||||
}
|
||||
|
||||
testLoadCfg := func(t *testing.T) {
|
||||
for i, opts := range argsList {
|
||||
gotCfg, err := LoadCfg(opts)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
expectCfgStruct := expects[i]
|
||||
expectCfgBytes, err := json.Marshal(expectCfgStruct)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
expectCfg, err := gocfg.New(NewConfig()).Load(gocfg.JSONStr(string(expectCfgBytes)))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if !Equal(gotCfg, expectCfg) {
|
||||
t.Fatal("cfgs are not identical")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
t.Run("test LoadCfg", testLoadCfg)
|
||||
}
|
||||
|
||||
// TODO: use better comparing method
|
||||
func Equal(cfg1, cfg2 *gocfg.Cfg) bool {
|
||||
// check cfg1
|
||||
for k1, v1 := range cfg1.Bools() {
|
||||
v2, ok := cfg2.Bool(k1)
|
||||
if !ok || v2 != v1 {
|
||||
fmt.Println(k1, v1, v2)
|
||||
return false
|
||||
}
|
||||
}
|
||||
for k1, v1 := range cfg1.Ints() {
|
||||
v2, ok := cfg2.Int(k1)
|
||||
if !ok || v2 != v1 {
|
||||
fmt.Println(k1, v1, v2)
|
||||
return false
|
||||
}
|
||||
}
|
||||
for k1, v1 := range cfg1.Floats() {
|
||||
v2, ok := cfg2.Float(k1)
|
||||
if !ok || v2 != v1 {
|
||||
fmt.Println(k1, v1, v2)
|
||||
return false
|
||||
}
|
||||
}
|
||||
for k1, v1 := range cfg1.Strings() {
|
||||
v2, ok := cfg2.String(k1)
|
||||
if !ok || v2 != v1 {
|
||||
fmt.Println(k1, v1, v2)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// check cfg2
|
||||
for k2, v2 := range cfg2.Bools() {
|
||||
v1, ok := cfg1.Bool(k2)
|
||||
if !ok || v1 != v2 {
|
||||
fmt.Println(k2, v1, v2)
|
||||
return false
|
||||
}
|
||||
}
|
||||
for k2, v2 := range cfg2.Ints() {
|
||||
v1, ok := cfg1.Int(k2)
|
||||
if !ok || v1 != v2 {
|
||||
fmt.Println(k2, v1, v2)
|
||||
return false
|
||||
}
|
||||
}
|
||||
for k2, v2 := range cfg2.Floats() {
|
||||
v1, ok := cfg1.Float(k2)
|
||||
if !ok || v1 != v2 {
|
||||
fmt.Println(k2, v1, v2)
|
||||
return false
|
||||
}
|
||||
}
|
||||
for k2, v2 := range cfg2.Strings() {
|
||||
v1, ok := cfg1.String(k2)
|
||||
if !ok || v1 != v2 {
|
||||
fmt.Println(k2, v1, v2)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
48
src/server/testdata/config_1.yml
vendored
Normal file
48
src/server/testdata/config_1.yml
vendored
Normal file
|
@ -0,0 +1,48 @@
|
|||
fs:
|
||||
root: "1"
|
||||
opensLimit: 1
|
||||
openTTL: 1
|
||||
secrets:
|
||||
tokenSecret: "1"
|
||||
server:
|
||||
debug: true
|
||||
host: "1"
|
||||
port: 1
|
||||
readTimeout: 1
|
||||
writeTimeout: 1
|
||||
maxHeaderBytes: 1
|
||||
publicPath: "1"
|
||||
users:
|
||||
enableAuth: true
|
||||
defaultAdmin: "1"
|
||||
defaultAdminPwd: "1"
|
||||
cookieTTL: 1
|
||||
cookieSecure: true
|
||||
cookieHttpOnly: true
|
||||
minUserNameLen: 1
|
||||
minPwdLen: 1
|
||||
captchaWidth: 1
|
||||
captchaHeight: 1
|
||||
captchaEnabled: true
|
||||
uploadSpeedLimit: 1
|
||||
downloadSpeedLimit: 1
|
||||
spaceLimit: 1
|
||||
limiterCapacity: 1
|
||||
limiterCyc: 1
|
||||
predefinedUsers:
|
||||
- name: "1"
|
||||
pwd: "1"
|
||||
role: "1"
|
||||
workers:
|
||||
queueSize: 1
|
||||
sleepCyc: 1
|
||||
workerCount: 1
|
||||
site:
|
||||
clientCfg:
|
||||
siteName: "1"
|
||||
siteDesc: "1"
|
||||
bg:
|
||||
url: "1"
|
||||
repeat: "1"
|
||||
position: "1"
|
||||
align: "1"
|
48
src/server/testdata/config_4.yml
vendored
Normal file
48
src/server/testdata/config_4.yml
vendored
Normal file
|
@ -0,0 +1,48 @@
|
|||
fs:
|
||||
root: "4"
|
||||
opensLimit: 4
|
||||
openTTL: 4
|
||||
secrets:
|
||||
tokenSecret: "4"
|
||||
server:
|
||||
debug: false
|
||||
host: "4"
|
||||
port: 4
|
||||
readTimeout: 4
|
||||
writeTimeout: 4
|
||||
maxHeaderBytes: 4
|
||||
publicPath: "4"
|
||||
users:
|
||||
enableAuth: false
|
||||
defaultAdmin: "4"
|
||||
defaultAdminPwd: "4"
|
||||
cookieTTL: 4
|
||||
cookieSecure: false
|
||||
cookieHttpOnly: false
|
||||
minUserNameLen: 4
|
||||
minPwdLen: 4
|
||||
captchaWidth: 4
|
||||
captchaHeight: 4
|
||||
captchaEnabled: false
|
||||
uploadSpeedLimit: 4
|
||||
downloadSpeedLimit: 4
|
||||
spaceLimit: 4
|
||||
limiterCapacity: 4
|
||||
limiterCyc: 4
|
||||
predefinedUsers:
|
||||
- name: "4"
|
||||
pwd: "4"
|
||||
role: "4"
|
||||
workers:
|
||||
queueSize: 4
|
||||
sleepCyc: 4
|
||||
workerCount: 4
|
||||
site:
|
||||
clientCfg:
|
||||
siteName: "4"
|
||||
siteDesc: "4"
|
||||
bg:
|
||||
url: "4"
|
||||
repeat: "4"
|
||||
position: "4"
|
||||
align: "4"
|
21
src/server/testdata/config_partial_users.yml
vendored
Normal file
21
src/server/testdata/config_partial_users.yml
vendored
Normal file
|
@ -0,0 +1,21 @@
|
|||
users:
|
||||
enableAuth: true
|
||||
defaultAdmin: "5"
|
||||
defaultAdminPwd: "5"
|
||||
cookieTTL: 5
|
||||
cookieSecure: true
|
||||
cookieHttpOnly: true
|
||||
minUserNameLen: 5
|
||||
minPwdLen: 5
|
||||
captchaWidth: 5
|
||||
captchaHeight: 5
|
||||
captchaEnabled: true
|
||||
uploadSpeedLimit: 5
|
||||
downloadSpeedLimit: 5
|
||||
spaceLimit: 5
|
||||
limiterCapacity: 5
|
||||
limiterCyc: 5
|
||||
predefinedUsers:
|
||||
- name: "5"
|
||||
pwd: "5"
|
||||
role: "5"
|
Loading…
Add table
Add a link
Reference in a new issue