310 lines
8.1 KiB
Go
310 lines
8.1 KiB
Go
// Copyright (C) INFINI Labs & INFINI LIMITED.
|
|
//
|
|
// The INFINI Console is offered under the GNU Affero General Public License v3.0
|
|
// and as commercial software.
|
|
//
|
|
// For commercial licensing, contact us at:
|
|
// - Website: infinilabs.com
|
|
// - Email: hello@infini.ltd
|
|
//
|
|
// Open Source licensed under AGPL V3:
|
|
// This program is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU Affero General Public License as published by
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
// (at your option) any later version.
|
|
//
|
|
// This program is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU Affero General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU Affero General Public License
|
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
/* Copyright © INFINI LTD. All rights reserved.
|
|
* Web: https://infinilabs.com
|
|
* Email: hello#infini.ltd */
|
|
|
|
package server
|
|
|
|
import (
|
|
log "github.com/cihub/seelog"
|
|
httprouter "infini.sh/framework/core/api/router"
|
|
config3 "infini.sh/framework/core/config"
|
|
"infini.sh/framework/core/global"
|
|
"infini.sh/framework/core/model"
|
|
"infini.sh/framework/core/util"
|
|
"infini.sh/framework/modules/configs/common"
|
|
"infini.sh/framework/modules/configs/config"
|
|
"net/http"
|
|
"path"
|
|
"sync"
|
|
)
|
|
|
|
var configProvidersLock = sync.RWMutex{}
|
|
var configProviders = []func(instance model.Instance) []*common.ConfigFile{}
|
|
|
|
func RegisterConfigProvider(provider func(instance model.Instance) []*common.ConfigFile) {
|
|
configProvidersLock.Lock()
|
|
defer configProvidersLock.Unlock()
|
|
configProviders = append(configProviders, provider)
|
|
}
|
|
|
|
func refreshConfigsRepo() {
|
|
|
|
//load config settings from file
|
|
if global.Env().SystemConfig.Configs.ManagerConfig.LocalConfigsRepoPath != "" {
|
|
configRepo = common.ConfigRepo{}
|
|
cfgPath := path.Join(global.Env().SystemConfig.Configs.ManagerConfig.LocalConfigsRepoPath, "/settings.yml")
|
|
|
|
if !util.FileExists(cfgPath) {
|
|
log.Debugf("config not exists, skip loading: %v", cfgPath)
|
|
return
|
|
}
|
|
|
|
setCfg, err := config3.LoadFiles(cfgPath)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
err = setCfg.Unpack(&configRepo)
|
|
log.Debug("loading config_repo: ", configRepo)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
if configRepo.InstanceGroups != nil {
|
|
for _, v := range configRepo.InstanceGroups {
|
|
cfgs := []string{}
|
|
for _, f := range v.ConfigGroups {
|
|
cfg, ok := configRepo.ConfigGroups[f]
|
|
if ok {
|
|
cfgs = append(cfgs, cfg.Files...)
|
|
}
|
|
}
|
|
|
|
secrets := []common.Secrets{}
|
|
for _, f := range v.Secrets {
|
|
secret, ok := configRepo.SecretGroups[f]
|
|
if ok {
|
|
secrets = append(secrets, secret)
|
|
}
|
|
}
|
|
for _, x := range v.Instances {
|
|
instanceConfigFiles[x] = cfgs
|
|
instanceSecrets[x] = secrets
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func getSecretsForInstance(instance model.Instance) *common.Secrets {
|
|
secrets := common.Secrets{}
|
|
secrets.Keystore = map[string]common.KeystoreValue{}
|
|
|
|
//get config files for static settings
|
|
serverInit.Do(func() {
|
|
refreshConfigsRepo()
|
|
})
|
|
|
|
if instanceSecrets != nil {
|
|
v, ok := instanceSecrets[instance.ID]
|
|
if ok {
|
|
for _, f := range v {
|
|
if ok {
|
|
for m, n := range f.Keystore {
|
|
secrets.Keystore[m] = n
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return &secrets
|
|
}
|
|
|
|
func getConfigsForInstance(instance model.Instance) []*common.ConfigFile {
|
|
result := []*common.ConfigFile{}
|
|
|
|
//get config files for static settings
|
|
serverInit.Do(func() {
|
|
refreshConfigsRepo()
|
|
})
|
|
|
|
if instanceConfigFiles != nil {
|
|
v, ok := instanceConfigFiles[instance.ID]
|
|
if ok {
|
|
for _, x := range v {
|
|
file := path.Join(global.Env().SystemConfig.Configs.ManagerConfig.LocalConfigsRepoPath, x)
|
|
log.Debug("prepare config:", file)
|
|
cfg, err := config.GetConfigFromFile(global.Env().SystemConfig.Configs.ManagerConfig.LocalConfigsRepoPath, file)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
if cfg != nil {
|
|
cfg.Managed = true
|
|
result = append(result, cfg)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return result
|
|
}
|
|
|
|
func (h APIHandler) refreshConfigsRepo(w http.ResponseWriter, req *http.Request, ps httprouter.Params) {
|
|
refreshConfigsRepo()
|
|
h.WriteAckOKJSON(w)
|
|
}
|
|
|
|
func (h APIHandler) syncConfigs(w http.ResponseWriter, req *http.Request, ps httprouter.Params) {
|
|
|
|
var obj = &common.ConfigSyncRequest{}
|
|
err := h.DecodeJSON(req, obj)
|
|
if err != nil {
|
|
h.WriteError(w, err.Error(), http.StatusInternalServerError)
|
|
}
|
|
|
|
if global.Env().IsDebug {
|
|
log.Trace("request:", util.MustToJSON(obj))
|
|
}
|
|
|
|
//TODO, check the client's and the server's hash, if same, skip the sync
|
|
|
|
var res = common.ConfigSyncResponse{}
|
|
res.Configs.CreatedConfigs = map[string]common.ConfigFile{}
|
|
res.Configs.UpdatedConfigs = map[string]common.ConfigFile{}
|
|
res.Configs.DeletedConfigs = map[string]common.ConfigFile{}
|
|
|
|
//check if client is enrolled
|
|
|
|
//check if client was marked as deleted
|
|
|
|
//find out the client belongs to which config group
|
|
|
|
//if server's hash didn't change, skip
|
|
|
|
//if client's hash don't change, skip
|
|
|
|
//find out different configs, add or delete configs
|
|
cfgs := getConfigsForInstance(obj.Client)
|
|
|
|
newCfgs := getConfigsFromExternalProviders(obj.Client)
|
|
|
|
if newCfgs != nil && len(newCfgs) > 0 {
|
|
cfgs = append(cfgs, newCfgs...)
|
|
}
|
|
|
|
if global.Env().IsDebug {
|
|
log.Debugf("get configs for agent(%v): %v", obj.Client.ID, util.MustToJSON(cfgs))
|
|
}
|
|
|
|
if cfgs == nil || len(cfgs) == 0 {
|
|
|
|
if len(obj.Configs.Configs) > 0 {
|
|
//set everything is deleted
|
|
log.Debugf("no config get from manager, exists config should be all deleted for instance: %v", obj.Client.ID)
|
|
res.Configs.DeletedConfigs = obj.Configs.Configs
|
|
res.Changed = true
|
|
} else {
|
|
log.Debugf("no config found from manager for instance: %v", obj.Client.ID)
|
|
res.Changed = false
|
|
}
|
|
h.WriteJSON(w, res, 200)
|
|
return
|
|
}
|
|
|
|
//get configs from repo, let's diff and send to client
|
|
if len(cfgs) > 0 {
|
|
|
|
cfgMap := map[string]common.ConfigFile{}
|
|
for _, c := range cfgs {
|
|
cfgMap[c.Name] = *c
|
|
}
|
|
|
|
//find out which config content was changed, replace to new content
|
|
if len(obj.Configs.Configs) > 0 {
|
|
//check diff
|
|
for k, v := range cfgMap {
|
|
x, ok := obj.Configs.Configs[k]
|
|
//both exists
|
|
if ok {
|
|
if global.Env().IsDebug {
|
|
log.Trace("both exists: ", k, ", checking version: ", v.Version, " vs ", x.Version)
|
|
}
|
|
|
|
if !x.Managed {
|
|
log.Debugf("config %v was marked as not to be managed, skip", k)
|
|
continue
|
|
}
|
|
|
|
if global.Env().IsDebug {
|
|
log.Debugf("check version for config %v, %v vs %v, %v", k, v.Version, x.Version, x.Managed)
|
|
}
|
|
|
|
//let's diff the version
|
|
if v.Version > x.Version {
|
|
if global.Env().IsDebug {
|
|
log.Trace("get newly version from server, let's sync to client: ", k)
|
|
}
|
|
|
|
res.Configs.UpdatedConfigs[k] = v
|
|
res.Changed = true
|
|
} else {
|
|
//this config no need to update
|
|
if global.Env().IsDebug {
|
|
log.Trace("config not changed: ", k)
|
|
}
|
|
}
|
|
} else {
|
|
if global.Env().IsDebug {
|
|
log.Trace("found new configs: ", k, ", version: ", v.Version)
|
|
}
|
|
res.Configs.CreatedConfigs[k] = v
|
|
res.Changed = true
|
|
}
|
|
}
|
|
|
|
//check removed files
|
|
for k, v := range obj.Configs.Configs {
|
|
_, ok := cfgMap[k]
|
|
if !ok {
|
|
//missing in server's config
|
|
res.Configs.DeletedConfigs[k] = v
|
|
res.Changed = true
|
|
if global.Env().IsDebug {
|
|
log.Trace("config was removed from server, let's mark it as deleted: ", k)
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
if global.Env().IsDebug {
|
|
log.Tracef("found %v new configs", len(cfgs))
|
|
}
|
|
res.Changed = true
|
|
res.Configs.CreatedConfigs = cfgMap
|
|
}
|
|
}
|
|
|
|
//only if config changed, we change try to update the client's secrets, //TODO maybe there are coupled
|
|
if res.Changed {
|
|
secrets := getSecretsForInstance(obj.Client)
|
|
res.Secrets = secrets
|
|
}
|
|
|
|
h.WriteJSON(w, res, 200)
|
|
|
|
}
|
|
|
|
func getConfigsFromExternalProviders(client model.Instance) []*common.ConfigFile {
|
|
configProvidersLock.Lock()
|
|
defer configProvidersLock.Unlock()
|
|
var cfgs []*common.ConfigFile
|
|
for _, p := range configProviders {
|
|
c := p(client)
|
|
if c != nil && len(c) > 0 {
|
|
cfgs = append(cfgs, c...)
|
|
}
|
|
}
|
|
return cfgs
|
|
}
|