mirror of
https://github.com/vxcontrol/vxcommon.git
synced 2026-07-20 09:34:35 -04:00
338 lines
9.1 KiB
Go
338 lines
9.1 KiB
Go
package controller
|
|
|
|
import (
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"path/filepath"
|
|
"strings"
|
|
|
|
"github.com/vxcontrol/vxcommon/db"
|
|
"github.com/vxcontrol/vxcommon/loader"
|
|
"github.com/vxcontrol/vxcommon/storage"
|
|
)
|
|
|
|
// tConfigLoaderType is type for loading config
|
|
type tConfigLoaderType int32
|
|
|
|
// Enum config loading types
|
|
const (
|
|
eDBConfigLoader tConfigLoaderType = 0
|
|
eS3ConfigLoader tConfigLoaderType = 1
|
|
eFSConfigLoader tConfigLoaderType = 2
|
|
)
|
|
|
|
// List of SQL queries string
|
|
const (
|
|
sLoadModulesSQL string = "SELECT `id`, IF(`agent_id` = 0, '', (SELECT `hash` FROM `agents` WHERE `id` = `agent_id`)) AS `agent_id`, `info`, `last_update` FROM `modules` WHERE `status` = 'joined'"
|
|
sGetModuleFieldSQL string = "SELECT `%s` FROM `modules` WHERE `id` = ? LIMIT 1"
|
|
sSetModuleFieldSQL string = "UPDATE `modules` SET `%s` = ? WHERE `id` = ?"
|
|
)
|
|
|
|
// tFilesLoaderType is type for loading module
|
|
type tFilesLoaderType int32
|
|
|
|
// Enum files loading types
|
|
const (
|
|
eS3FilesLoader tFilesLoaderType = 0
|
|
eFSFilesLoader tFilesLoaderType = 1
|
|
)
|
|
|
|
// IConfigLoader is internal interface for loading config from external storage
|
|
type IConfigLoader interface {
|
|
load() ([]*loader.ModuleConfig, error)
|
|
}
|
|
|
|
// IFilesLoader is internal interface for loading files from external storage
|
|
type IFilesLoader interface {
|
|
load(mcl []*loader.ModuleConfig) ([]*loader.ModuleFiles, error)
|
|
}
|
|
|
|
// configLoaderDB is container for config which loaded from DB
|
|
type configLoaderDB struct {
|
|
}
|
|
|
|
func (cl *configLoaderDB) getCb(id, col string) getCallback {
|
|
sql := fmt.Sprintf(sGetModuleFieldSQL, col)
|
|
return func() string {
|
|
db, err := db.New()
|
|
if err != nil {
|
|
return ""
|
|
}
|
|
|
|
rows, err := db.Query(sql, id)
|
|
if err != nil {
|
|
return ""
|
|
}
|
|
if len(rows) != 1 {
|
|
return ""
|
|
}
|
|
if data, ok := rows[0][col]; ok {
|
|
return data
|
|
}
|
|
|
|
return ""
|
|
}
|
|
}
|
|
|
|
func (cl *configLoaderDB) setCb(id, col string) setCallback {
|
|
sql := fmt.Sprintf(sSetModuleFieldSQL, col)
|
|
return func(val string) bool {
|
|
db, err := db.New()
|
|
if err != nil {
|
|
return false
|
|
}
|
|
|
|
_, err = db.Exec(sql, val, id)
|
|
if err != nil {
|
|
return false
|
|
}
|
|
|
|
return true
|
|
}
|
|
}
|
|
|
|
func joinPath(args ...string) string {
|
|
tpath := filepath.Join(args...)
|
|
return strings.Replace(tpath, "\\", "/", -1)
|
|
}
|
|
|
|
// load is function what retrieve modules config list from DB
|
|
func (cl *configLoaderDB) load() ([]*loader.ModuleConfig, error) {
|
|
db, err := db.New()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
rows, err := db.Query(sLoadModulesSQL)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var ml []*loader.ModuleConfig
|
|
for _, m := range rows {
|
|
var mc loader.ModuleConfig
|
|
if info, ok := m["info"]; ok {
|
|
if err = json.Unmarshal([]byte(info), &mc); err != nil {
|
|
return nil, errors.New("error parsing module config: " + err.Error())
|
|
}
|
|
if agentID, ok := m["agent_id"]; ok {
|
|
mc.AgentID = agentID
|
|
}
|
|
if lastUpdate, ok := m["last_update"]; ok {
|
|
mc.LastUpdate = lastUpdate
|
|
}
|
|
} else {
|
|
return nil, errors.New("failed loading module config")
|
|
}
|
|
ci := &sConfigItem{}
|
|
if id, ok := m["id"]; ok {
|
|
ci.getConfigSchema = cl.getCb(id, "config_schema")
|
|
ci.getDefaultConfig = cl.getCb(id, "default_config")
|
|
ci.getCurrentConfig = cl.getCb(id, "current_config")
|
|
ci.setCurrentConfig = cl.setCb(id, "current_config")
|
|
ci.getEventDataSchema = cl.getCb(id, "event_data_schema")
|
|
ci.getEventConfigSchema = cl.getCb(id, "event_config_schema")
|
|
ci.getDefaultEventConfig = cl.getCb(id, "default_event_config")
|
|
ci.getCurrentEventConfig = cl.getCb(id, "current_event_config")
|
|
}
|
|
mc.IConfigItem = ci
|
|
ml = append(ml, &mc)
|
|
}
|
|
|
|
return ml, nil
|
|
}
|
|
|
|
func getStorageCb(s storage.IStorage, mpath, file string) getCallback {
|
|
return func() string {
|
|
data, err := s.ReadFile(joinPath(mpath, file))
|
|
if err == nil {
|
|
return string(data)
|
|
}
|
|
|
|
return ""
|
|
}
|
|
}
|
|
|
|
func setStorageCb(s storage.IStorage, mpath, file string) setCallback {
|
|
return func(val string) bool {
|
|
if s.WriteFile(joinPath(mpath, file), []byte(val)) != nil {
|
|
return false
|
|
}
|
|
|
|
return true
|
|
}
|
|
}
|
|
|
|
func loadConfig(s storage.IStorage, path string) ([]*loader.ModuleConfig, error) {
|
|
var mcl []*loader.ModuleConfig
|
|
cpath := strings.Replace(filepath.Join(path, "config.json"), "\\", "/", -1)
|
|
if s.IsNotExist(path) {
|
|
return nil, errors.New("directory with config not found")
|
|
}
|
|
if s.IsNotExist(cpath) {
|
|
return nil, errors.New("config file not found")
|
|
}
|
|
cdata, err := s.ReadFile(cpath)
|
|
if err != nil {
|
|
return nil, errors.New("failed read config file: " + err.Error())
|
|
}
|
|
if err = json.Unmarshal(cdata, &mcl); err != nil {
|
|
return nil, errors.New("error while reading modules config: " + err.Error())
|
|
}
|
|
for _, mc := range mcl {
|
|
mpath := joinPath(path, mc.Name, mc.Version, "config")
|
|
ci := &sConfigItem{
|
|
getConfigSchema: getStorageCb(s, mpath, "config_schema.json"),
|
|
getDefaultConfig: getStorageCb(s, mpath, "default_config.json"),
|
|
getCurrentConfig: getStorageCb(s, mpath, "current_config.json"),
|
|
setCurrentConfig: setStorageCb(s, mpath, "current_config.json"),
|
|
getEventDataSchema: getStorageCb(s, mpath, "event_data_schema.json"),
|
|
getEventConfigSchema: getStorageCb(s, mpath, "event_config_schema.json"),
|
|
getDefaultEventConfig: getStorageCb(s, mpath, "default_event_config.json"),
|
|
getCurrentEventConfig: getStorageCb(s, mpath, "current_event_config.json"),
|
|
}
|
|
mc.IConfigItem = ci
|
|
}
|
|
|
|
return mcl, nil
|
|
}
|
|
|
|
// configLoaderS3 is container for config which loaded from D3
|
|
type configLoaderS3 struct {
|
|
}
|
|
|
|
// load is function what retrieve modules config list from S3
|
|
func (cl *configLoaderS3) load() ([]*loader.ModuleConfig, error) {
|
|
s, err := storage.NewS3()
|
|
if err != nil {
|
|
return nil, errors.New("failed initialize S3 driver: " + err.Error())
|
|
}
|
|
|
|
return loadConfig(s, "/")
|
|
}
|
|
|
|
// configLoaderFS is container for config which loaded from FS
|
|
type configLoaderFS struct {
|
|
path string
|
|
}
|
|
|
|
// load is function what retrieve modules config list from FS
|
|
func (cl *configLoaderFS) load() ([]*loader.ModuleConfig, error) {
|
|
fs, err := storage.NewFS()
|
|
if err != nil {
|
|
return nil, errors.New("failed initialize FS driver: " + err.Error())
|
|
}
|
|
|
|
return loadConfig(fs, cl.path)
|
|
}
|
|
|
|
func removeLeadSlash(files map[string][]byte) map[string][]byte {
|
|
rfiles := make(map[string][]byte)
|
|
for name, data := range files {
|
|
rfiles[name[1:]] = data
|
|
}
|
|
return rfiles
|
|
}
|
|
|
|
func loadUtils(s storage.IStorage, path string) (map[string][]byte, error) {
|
|
var err error
|
|
upath := joinPath(path, "utils")
|
|
if s.IsNotExist(upath) {
|
|
return nil, errors.New("utils directory not found")
|
|
}
|
|
|
|
files, err := s.ReadDirRec(upath)
|
|
if err != nil {
|
|
return nil, errors.New("failed read utils files: " + err.Error())
|
|
}
|
|
files = removeLeadSlash(files)
|
|
|
|
return files, nil
|
|
}
|
|
|
|
func loadFiles(s storage.IStorage, path string, mcl []*loader.ModuleConfig) ([]*loader.ModuleFiles, error) {
|
|
var mfl []*loader.ModuleFiles
|
|
if s.IsNotExist(path) {
|
|
return nil, errors.New("directory with modules not found")
|
|
}
|
|
|
|
utils, err := loadUtils(s, path)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
for _, mc := range mcl {
|
|
var mf loader.ModuleFiles
|
|
loadModuleDir := func(dir string) (*loader.ModuleItem, error) {
|
|
mpath := joinPath(path, mc.Name, mc.Version, dir)
|
|
if s.IsNotExist(mpath) {
|
|
return nil, errors.New("module directory not found")
|
|
}
|
|
var mi loader.ModuleItem
|
|
files, err := s.ReadDirRec(mpath)
|
|
if err != nil {
|
|
return nil, errors.New("failed read module files: " + err.Error())
|
|
}
|
|
files = removeLeadSlash(files)
|
|
for p, d := range utils {
|
|
if _, ok := files[p]; !ok {
|
|
files[p] = d
|
|
}
|
|
}
|
|
args := make(map[string][]string)
|
|
if data, ok := files["args.json"]; ok {
|
|
if err = json.Unmarshal(data, &args); err != nil {
|
|
return nil, errors.New("failed read module args: " + err.Error())
|
|
}
|
|
}
|
|
mi.SetArgs(args)
|
|
mi.SetFiles(files)
|
|
|
|
return &mi, nil
|
|
}
|
|
|
|
var smi, cmi *loader.ModuleItem
|
|
if cmi, err = loadModuleDir("cmodule"); err != nil {
|
|
return nil, err
|
|
}
|
|
if smi, err = loadModuleDir("smodule"); err != nil {
|
|
return nil, err
|
|
}
|
|
mf.SetCModule(cmi)
|
|
mf.SetSModule(smi)
|
|
mfl = append(mfl, &mf)
|
|
}
|
|
|
|
return mfl, nil
|
|
}
|
|
|
|
// filesLoaderS3 is container for files structure which loaded from S3
|
|
type filesLoaderS3 struct {
|
|
}
|
|
|
|
// load is function what retrieve modules files data from S3
|
|
func (fl *filesLoaderS3) load(mcl []*loader.ModuleConfig) ([]*loader.ModuleFiles, error) {
|
|
s, err := storage.NewS3()
|
|
if err != nil {
|
|
return nil, errors.New("failed initialize S3 driver: " + err.Error())
|
|
}
|
|
|
|
return loadFiles(s, "/", mcl)
|
|
}
|
|
|
|
// filesLoaderFS is container for files structure which loaded from FS
|
|
type filesLoaderFS struct {
|
|
path string
|
|
}
|
|
|
|
// load is function what retrieve modules files data from FS
|
|
func (fl *filesLoaderFS) load(mcl []*loader.ModuleConfig) ([]*loader.ModuleFiles, error) {
|
|
fs, err := storage.NewFS()
|
|
if err != nil {
|
|
return nil, errors.New("failed initialize FS driver: " + err.Error())
|
|
}
|
|
|
|
return loadFiles(fs, fl.path, mcl)
|
|
}
|