push config to agent through saving file

This commit is contained in:
liugq 2023-06-01 10:20:56 +08:00
parent e9f9d925f2
commit 0cd8aac956
11 changed files with 569 additions and 310 deletions

View File

@ -156,6 +156,8 @@ if [ $? -ne 0 ]; then
exit 1 exit 1
fi fi
rm -f ${agent}
################## ##################
# save cert # save cert
################## ##################
@ -167,14 +169,13 @@ if [ $? -ne 0 ]; then
exit 1 exit 1
fi fi
port="{{port}}"
## generate agent.yml ## generate agent.yml
agent_config="path.configs: "config" agent_config="path.configs: "config"
configs.auto_reload: true configs.auto_reload: true
env: env:
LOGGING_ES_ENDPOINT: {{logging_es_endpoint}} API_BINDING: "0.0.0.0:${port}"
LOGGING_ES_USER: {{logging_es_user}}
LOGGING_ES_PASS: {{logging_es_password}}
API_BINDING: "0.0.0.0:8080"
path.data: data path.data: data
path.logs: log path.logs: log
@ -194,104 +195,6 @@ badger:
value_log_max_entries: 1000000 value_log_max_entries: 1000000
value_log_file_size: 104857600 value_log_file_size: 104857600
value_threshold: 1024 value_threshold: 1024
metrics:
enabled: true
queue: metrics
network:
enabled: true
summary: true
details: true
memory:
metrics:
- swap
- memory
disk:
metrics:
- iops
- usage
cpu:
metrics:
- idle
- system
- user
- iowait
- load
instance:
enabled: true
elasticsearch:
- name: default
enabled: true
endpoint: \$[[env.LOGGING_ES_ENDPOINT]]
discovery:
enabled: true
basic_auth:
username: \$[[env.LOGGING_ES_USER]]
password: \$[[env.LOGGING_ES_PASS]]
pipeline:
- name: logs_indexing_merge
auto_start: true
keep_running: true
processor:
- indexing_merge:
index_name: ".infini_logs"
elasticsearch: "default"
input_queue: "logs"
idle_timeout_in_seconds: 10
output_queue:
name: "logs_requests"
label:
tag: "logs"
worker_size: 1
bulk_size_in_mb: 10
- name: ingest_logs
auto_start: true
keep_running: true
processor:
- bulk_indexing:
bulk:
compress: true
batch_size_in_mb: 5
batch_size_in_docs: 5000
consumer:
fetch_max_messages: 100
queues:
type: indexing_merge
tag: "logs"
when:
cluster_available: ["default"]
- name: metrics_indexing_merge
auto_start: true
keep_running: true
processor:
- indexing_merge:
elasticsearch: "default"
index_name: ".infini_metrics"
input_queue: "metrics"
output_queue:
name: "metrics_requests"
label:
tag: "metrics"
worker_size: 1
bulk_size_in_mb: 5
- name: ingest_metrics
auto_start: true
keep_running: true
processor:
- bulk_indexing:
bulk:
compress: true
batch_size_in_mb: 5
batch_size_in_docs: 5000
consumer:
fetch_max_messages: 100
queues:
type: indexing_merge
tag: "metrics"
when:
cluster_available: ["default"]
agent: agent:
major_ip_pattern: "192.*" major_ip_pattern: "192.*"
" "
@ -331,6 +234,16 @@ if [ $? -ne 0 ]; then
fi fi
printf "\n* agent service started" printf "\n* agent service started"
console_endpoint="{{console_endpoint}}"
sleep 3
printf "\n* start register\n"
token={{token}}
curl -X POST ${console_endpoint}/agent/instance?token=${token}
if [ $? -ne 0 ]; then
exit 1
fi
printf "\n* agent registered\n"
printf "\n* ${GREEN}Congratulations, install success!${CLR}\n\n" printf "\n* ${GREEN}Congratulations, install success!${CLR}\n\n"

View File

@ -41,7 +41,22 @@ func (module *AgentModule) Start() error {
orm.RegisterSchemaWithIndexName(agent.ESNodeInfo{}, "agent-node") orm.RegisterSchemaWithIndexName(agent.ESNodeInfo{}, "agent-node")
orm.RegisterSchemaWithIndexName(host.HostInfo{}, "host") orm.RegisterSchemaWithIndexName(host.HostInfo{}, "host")
orm.RegisterSchemaWithIndexName(agent.Setting{}, "agent-setting") orm.RegisterSchemaWithIndexName(agent.Setting{}, "agent-setting")
client.RegisterClient(&client.Client{}) var (
executor client.Executor
err error
)
if module.AgentConfig.Setup == nil {
executor = &client.HttpExecutor{}
}else{
executor, err = client.NewMTLSExecutor(module.AgentConfig.Setup.CACertFile, module.AgentConfig.Setup.CAKeyFile)
if err != nil {
panic(err)
}
}
agClient := &client.Client{
Executor: executor,
}
client.RegisterClient(agClient)
if module.AgentConfig.StateManager.Enabled { if module.AgentConfig.StateManager.Enabled {
onlineAgentIDs, err := common.GetLatestOnlineAgentIDs(nil, 60) onlineAgentIDs, err := common.GetLatestOnlineAgentIDs(nil, 60)
@ -59,7 +74,7 @@ func (module *AgentModule) Start() error {
} }
} }
sm := state.NewStateManager(time.Second*30, "agent_state", agentIds) sm := state.NewStateManager(time.Second*30, "agent_state", agentIds, agClient)
state.RegisterStateManager(sm) state.RegisterStateManager(sm)
go sm.LoopState() go sm.LoopState()
} }

View File

@ -23,6 +23,7 @@ import (
"infini.sh/framework/modules/elastic/common" "infini.sh/framework/modules/elastic/common"
"net/http" "net/http"
"strconv" "strconv"
"time"
) )
type APIHandler struct { type APIHandler struct {
@ -37,22 +38,30 @@ func (h *APIHandler) createInstance(w http.ResponseWriter, req *http.Request, ps
log.Error(err) log.Error(err)
return return
} }
//validate token for auto register
oldInst := &agent.Instance{} token := h.GetParameter(req, "token")
oldInst.ID = obj.ID if token != "" {
exists, err := orm.Get(oldInst) if v, ok := tokens.Load(token); !ok {
h.WriteError(w, "token is invalid", http.StatusUnauthorized)
if err != nil && err != elastic2.ErrNotFound { return
h.WriteError(w, err.Error(), http.StatusInternalServerError) }else{
log.Error(err) if t, ok := v.(*Token); !ok || t.CreatedAt.Add(ExpiredIn).Before(time.Now()) {
return tokens.Delete(token)
} h.WriteError(w, "token was expired", http.StatusUnauthorized)
if exists { return
errMsg := fmt.Sprintf("agent [%s] already exists", obj.ID) }
h.WriteError(w, errMsg, http.StatusInternalServerError) }
log.Error(errMsg) remoteIP := util.ClientIP(req)
return agCfg := common2.GetAgentConfig()
port := agCfg.Setup.Port
if port == "" {
port = "8080"
}
obj.Endpoint = fmt.Sprintf("https://%s:%s", remoteIP, port)
obj.Tags = append(obj.Tags, "mtls")
obj.Tags = append(obj.Tags, "auto")
} }
//fetch more information of agent instance //fetch more information of agent instance
res, err := client.GetClient().GetInstanceBasicInfo(context.Background(), obj.GetEndpoint()) res, err := client.GetClient().GetInstanceBasicInfo(context.Background(), obj.GetEndpoint())
if err != nil { if err != nil {
@ -72,6 +81,24 @@ func (h *APIHandler) createInstance(w http.ResponseWriter, req *http.Request, ps
obj.MajorIP = res.MajorIP obj.MajorIP = res.MajorIP
obj.Host = res.Host obj.Host = res.Host
obj.IPS = res.IPS obj.IPS = res.IPS
if obj.Name == "" {
obj.Name = res.Name
}
}
oldInst := &agent.Instance{}
oldInst.ID = obj.ID
exists, err := orm.Get(oldInst)
if err != nil && err != elastic2.ErrNotFound {
h.WriteError(w, err.Error(), http.StatusInternalServerError)
log.Error(err)
return
}
if exists {
errMsg := fmt.Sprintf("agent [%s] already exists", obj.ID)
h.WriteError(w, errMsg, http.StatusInternalServerError)
log.Error(errMsg)
return
} }
obj.Status = model.StatusOnline obj.Status = model.StatusOnline
@ -85,11 +112,33 @@ func (h *APIHandler) createInstance(w http.ResponseWriter, req *http.Request, ps
if err != nil { if err != nil {
log.Error(err) log.Error(err)
} }
err = pushIngestConfigToAgent(obj)
if err != nil {
log.Error(err)
}
h.WriteCreatedOKJSON(w, obj.ID) h.WriteCreatedOKJSON(w, obj.ID)
} }
func pushIngestConfigToAgent(inst *agent.Instance) error{
ingestCfg, basicAuth, err := common2.GetAgentIngestConfig()
if err != nil {
return err
}
if basicAuth != nil && basicAuth.Password != "" {
err = client.GetClient().SetKeystoreValue(context.Background(), inst.GetEndpoint(), "ingest_cluster_password", basicAuth.Password)
if err != nil {
return fmt.Errorf("set keystore value to agent error: %w", err)
}
}
err = client.GetClient().SaveDynamicConfig(context.Background(), inst.GetEndpoint(), "ingest", ingestCfg )
if err != nil {
fmt.Errorf("save dynamic config to agent error: %w", err)
}
return nil
}
func (h *APIHandler) getInstance(w http.ResponseWriter, req *http.Request, ps httprouter.Params) { func (h *APIHandler) getInstance(w http.ResponseWriter, req *http.Request, ps httprouter.Params) {
id := ps.MustGetParameter("instance_id") id := ps.MustGetParameter("instance_id")
@ -157,6 +206,22 @@ func (h *APIHandler) deleteInstance(w http.ResponseWriter, req *http.Request, ps
return return
} }
queryDsl = util.MapStr{
"query": util.MapStr{
"term": util.MapStr{
"metadata.labels.agent_id": util.MapStr{
"value": id,
},
},
},
}
err = orm.DeleteBy(agent.Setting{}, util.MustToJSONBytes(queryDsl))
if err != nil {
h.WriteError(w, err.Error(), http.StatusInternalServerError)
log.Error("delete agent settings error: ", err)
return
}
h.WriteDeletedOKJSON(w, id) h.WriteDeletedOKJSON(w, id)
} }
@ -500,6 +565,26 @@ func (h *APIHandler) deleteESNode(w http.ResponseWriter, req *http.Request, ps h
h.WriteError(w, err.Error(), http.StatusInternalServerError) h.WriteError(w, err.Error(), http.StatusInternalServerError)
return return
} }
q = util.MapStr{
"query": util.MapStr{
"bool": util.MapStr{
"must": []util.MapStr{
{
"terms": util.MapStr{
"metadata.labels.node_uuid": nodeIDs,
},
},
{
"term": util.MapStr{
"metadata.labels.agent_id": util.MapStr{
"value": id,
},
},
},
},
},
},
}
} }
h.WriteAckOKJSON(w) h.WriteAckOKJSON(w)
} }

View File

@ -11,7 +11,6 @@ import (
"infini.sh/console/modules/agent/common" "infini.sh/console/modules/agent/common"
"infini.sh/framework/core/api/rbac" "infini.sh/framework/core/api/rbac"
httprouter "infini.sh/framework/core/api/router" httprouter "infini.sh/framework/core/api/router"
"infini.sh/framework/core/elastic"
"infini.sh/framework/core/global" "infini.sh/framework/core/global"
"infini.sh/framework/core/util" "infini.sh/framework/core/util"
"os" "os"
@ -69,16 +68,16 @@ func (h *APIHandler) generateInstallCommand(w http.ResponseWriter, req *http.Req
} }
tokens.Store(tokenStr, t) tokens.Store(tokenStr, t)
agCfg := common.GetAgentConfig() agCfg := common.GetAgentConfig()
scriptEndpoint := agCfg.Setup.ScriptEndpoint consoleEndpoint := agCfg.Setup.ConsoleEndpoint
if scriptEndpoint == "" { if consoleEndpoint == "" {
scheme := "http" scheme := "http"
if req.TLS != nil { if req.TLS != nil {
scheme = "https" scheme = "https"
} }
scriptEndpoint = fmt.Sprintf("%s://%s", scheme, req.Host) consoleEndpoint = fmt.Sprintf("%s://%s", scheme, req.Host)
} }
h.WriteJSON(w, util.MapStr{ h.WriteJSON(w, util.MapStr{
"script": fmt.Sprintf(`sudo bash -c "$(curl -L '%s/agent/install.sh?token=%s')"`, scriptEndpoint, tokenStr), "script": fmt.Sprintf(`sudo bash -c "$(curl -L '%s/agent/install.sh?token=%s')"`, consoleEndpoint, tokenStr),
"token": tokenStr, "token": tokenStr,
"expired_at": t.CreatedAt.Add(ExpiredIn), "expired_at": t.CreatedAt.Add(ExpiredIn),
}, http.StatusOK) }, http.StatusOK)
@ -120,25 +119,31 @@ func (h *APIHandler) getInstallScript(w http.ResponseWriter, req *http.Request,
if downloadURL == "" { if downloadURL == "" {
downloadURL = "https://release.infinilabs.com/agent/stable/" downloadURL = "https://release.infinilabs.com/agent/stable/"
} }
esCfg := elastic.GetConfig(global.MustLookupString(elastic.GlobalSystemElasticsearchID)) //esCfg := elastic.GetConfig(global.MustLookupString(elastic.GlobalSystemElasticsearchID))
var ( //var (
loggingESUser string // loggingESUser string
loggingESPassword string // loggingESPassword string
) //)
if esCfg.BasicAuth != nil { //if esCfg.BasicAuth != nil {
loggingESUser = esCfg.BasicAuth.Username // loggingESUser = esCfg.BasicAuth.Username
loggingESPassword = esCfg.BasicAuth.Password // loggingESPassword = esCfg.BasicAuth.Password
//}
port := agCfg.Setup.Port
if port == "" {
port = "8080"
} }
tpl.Execute(w, map[string]interface{}{ tpl.Execute(w, map[string]interface{}{
"base_url": agCfg.Setup.DownloadURL, "base_url": agCfg.Setup.DownloadURL,
"agent_version": agCfg.Setup.Version, "agent_version": agCfg.Setup.Version,
//"console_endpoint": util.MustToJSON(util.MustToJSON(gatewayEndpoints)), "console_endpoint": agCfg.Setup.ConsoleEndpoint,
"client_crt": clientCertPEM, "client_crt": clientCertPEM,
"client_key": clientKeyPEM, "client_key": clientKeyPEM,
"ca_crt": caCert, "ca_crt": caCert,
"logging_es_endpoint": esCfg.Endpoint, "port": port,
"logging_es_user": loggingESUser, "token": tokenStr,
"logging_es_password": loggingESPassword, //"logging_es_endpoint": esCfg.Endpoint,
//"logging_es_user": loggingESUser,
//"logging_es_password": loggingESPassword,
}) })
} }

View File

@ -5,20 +5,13 @@
package client package client
import ( import (
"bytes"
"context" "context"
"fmt" "fmt"
"infini.sh/console/modules/agent/common"
"infini.sh/framework/core/agent" "infini.sh/framework/core/agent"
"infini.sh/framework/core/elastic" "infini.sh/framework/core/elastic"
"infini.sh/framework/core/global"
"infini.sh/framework/core/host" "infini.sh/framework/core/host"
"infini.sh/framework/core/util" "infini.sh/framework/core/util"
"io"
"net/http" "net/http"
"os"
"path"
"sync"
) )
var defaultClient ClientAPI var defaultClient ClientAPI
@ -44,11 +37,15 @@ type ClientAPI interface {
AuthESNode(ctx context.Context, agentBaseURL string, cfg elastic.ElasticsearchConfig) (*agent.ESNodeInfo, error) AuthESNode(ctx context.Context, agentBaseURL string, cfg elastic.ElasticsearchConfig) (*agent.ESNodeInfo, error)
CreatePipeline(ctx context.Context, agentBaseURL string, body []byte) error CreatePipeline(ctx context.Context, agentBaseURL string, body []byte) error
DeletePipeline(ctx context.Context, agentBaseURL, pipelineID string) error DeletePipeline(ctx context.Context, agentBaseURL, pipelineID string) error
SetKeystoreValue(ctx context.Context, agentBaseURL string, key, value string) error
SaveDynamicConfig(ctx context.Context, agentBaseURL string, name, content string) error
} }
type Client struct { type Client struct {
Executor Executor
} }
func (client *Client) GetHostInfo(ctx context.Context, agentBaseURL string) (*host.HostInfo, error) { func (client *Client) GetHostInfo(ctx context.Context, agentBaseURL string) (*host.HostInfo, error) {
req := &util.Request{ req := &util.Request{
Method: http.MethodGet, Method: http.MethodGet,
@ -220,100 +217,37 @@ func (client *Client) DeletePipeline(ctx context.Context, agentBaseURL, pipeline
return client.doRequest(req, nil) return client.doRequest(req, nil)
} }
//func (client *Client) doRequest(req *util.Request, respObj interface{}) error { func (client *Client) SetKeystoreValue(ctx context.Context, agentBaseURL string, key, value string) error{
// result, err := util.ExecuteRequest(req) body := util.MapStr{
// if err != nil { "key": key,
// return err "value": value,
// } }
// if result.StatusCode != 200 { req := &util.Request{
// return fmt.Errorf(string(result.Body)) Method: http.MethodPost,
// } Url: fmt.Sprintf("%s/_framework/keystore", agentBaseURL),
// if respObj == nil { Context: ctx,
// return nil Body: util.MustToJSONBytes(body),
// } }
// return util.FromJSONBytes(result.Body, respObj) return client.doRequest(req, nil)
//} }
func (client *Client) SaveDynamicConfig(ctx context.Context, agentBaseURL string, name, content string) error{
body := util.MapStr{
"configs": util.MapStr{
name: content,
},
}
req := &util.Request{
Method: http.MethodPost,
Url: fmt.Sprintf("%s/agent/config", agentBaseURL),
Context: ctx,
Body: util.MustToJSONBytes(body),
}
return client.doRequest(req, nil)
}
var(
hClient *http.Client
hClientOnce = sync.Once{}
)
func (client *Client) doRequest(req *util.Request, respObj interface{}) error { func (client *Client) doRequest(req *util.Request, respObj interface{}) error {
agCfg := common.GetAgentConfig() return client.Executor.DoRequest(req, respObj)
var err error
hClientOnce.Do(func() {
var (
instanceCrt string
instanceKey string
)
instanceCrt, instanceKey, err = getAgentInstanceCerts(agCfg.Setup.CACertFile, agCfg.Setup.CAKeyFile)
hClient, err = util.NewMTLSClient(agCfg.Setup.CACertFile, instanceCrt, instanceKey)
})
if err != nil {
return err
}
var reader io.Reader
if len(req.Body) > 0 {
reader = bytes.NewReader(req.Body)
}
var hr *http.Request
if req.Context == nil {
hr, err = http.NewRequest(req.Method, req.Url, reader)
}else{
hr, err = http.NewRequestWithContext(req.Context, req.Method, req.Url, reader)
}
if err != nil {
return err
}
res, err := hClient.Do(hr)
if err != nil {
return err
}
if respObj != nil {
defer res.Body.Close()
buf, err := io.ReadAll(res.Body)
if err != nil {
return err
}
err = util.FromJSONBytes(buf, respObj)
if err != nil {
return err
}
}
return nil
} }
func getAgentInstanceCerts(caFile, caKey string) (string, string, error) {
dataDir := global.Env().GetDataDir()
instanceCrt := path.Join(dataDir, "certs/agent/instance.crt")
instanceKey := path.Join(dataDir, "certs/agent/instance.key")
var (
err error
clientCertPEM []byte
clientKeyPEM []byte
)
if util.FileExists(instanceCrt) && util.FileExists(instanceKey) {
return instanceCrt, instanceKey, nil
}
_, clientCertPEM, clientKeyPEM, err = common.GenerateClientCert(caFile, caKey)
if err != nil {
return "", "", err
}
baseDir := path.Join(dataDir, "certs/agent")
if !util.IsExist(baseDir){
err = os.MkdirAll(baseDir, 0775)
if err != nil {
return "", "", err
}
}
_, err = util.FilePutContentWithByte(instanceCrt, clientCertPEM)
if err != nil {
return "", "", err
}
_, err = util.FilePutContentWithByte(instanceKey, clientKeyPEM)
if err != nil {
return "", "", err
}
return instanceCrt, instanceKey, nil
}

View File

@ -0,0 +1,100 @@
/* Copyright © INFINI Ltd. All rights reserved.
* Web: https://infinilabs.com
* Email: hello#infini.ltd */
package client
import (
"bytes"
"fmt"
"infini.sh/console/modules/agent/common"
"infini.sh/framework/core/util"
"io"
"net/http"
)
type Executor interface {
DoRequest(req *util.Request, respObj interface{}) error
}
type HttpExecutor struct {
}
func (executor *HttpExecutor) DoRequest(req *util.Request, respObj interface{}) error {
result, err := util.ExecuteRequest(req)
if err != nil {
return err
}
if result.StatusCode != 200 {
return fmt.Errorf(string(result.Body))
}
if respObj == nil {
return nil
}
return util.FromJSONBytes(result.Body, respObj)
}
func NewMTLSExecutor(caCertFile, caKeyFile string) (*MTLSExecutor, error){
var (
instanceCrt string
instanceKey string
)
instanceCrt, instanceKey, err := common.GetAgentInstanceCerts(caCertFile, caKeyFile)
if err != nil {
return nil, fmt.Errorf("generate tls cert error: %w", err)
}
hClient, err := util.NewMTLSClient(caCertFile, instanceCrt, instanceKey)
if err != nil {
return nil, err
}
return &MTLSExecutor{
CaCertFile: caCertFile,
CAKeyFile: caKeyFile,
client: hClient,
}, nil
}
type MTLSExecutor struct {
CaCertFile string
CAKeyFile string
client *http.Client
}
func (executor *MTLSExecutor) DoRequest(req *util.Request, respObj interface{}) error {
var reader io.Reader
if len(req.Body) > 0 {
reader = bytes.NewReader(req.Body)
}
var (
hr *http.Request
err error
)
if req.Context == nil {
hr, err = http.NewRequest(req.Method, req.Url, reader)
}else{
hr, err = http.NewRequestWithContext(req.Context, req.Method, req.Url, reader)
}
if err != nil {
return err
}
res, err := executor.client.Do(hr)
if err != nil {
return err
}
defer res.Body.Close()
buf, err := io.ReadAll(res.Body)
if err != nil {
return err
}
if res.StatusCode != 200 {
return fmt.Errorf(string(buf))
}
if respObj != nil {
err = util.FromJSONBytes(buf, respObj)
if err != nil {
return err
}
}
return nil
}

View File

@ -61,3 +61,37 @@ func generateCert(caFile, caKey string, isServer bool)(caCert, instanceCertPEM,
} }
return caCert, instanceCertPEM, instanceKeyPEM, nil return caCert, instanceCertPEM, instanceKeyPEM, nil
} }
func GetAgentInstanceCerts(caFile, caKey string) (string, string, error) {
dataDir := global.Env().GetDataDir()
instanceCrt := path.Join(dataDir, "certs/agent/instance.crt")
instanceKey := path.Join(dataDir, "certs/agent/instance.key")
var (
err error
clientCertPEM []byte
clientKeyPEM []byte
)
if util.FileExists(instanceCrt) && util.FileExists(instanceKey) {
return instanceCrt, instanceKey, nil
}
_, clientCertPEM, clientKeyPEM, err = GenerateClientCert(caFile, caKey)
if err != nil {
return "", "", err
}
baseDir := path.Join(dataDir, "certs/agent")
if !util.IsExist(baseDir){
err = os.MkdirAll(baseDir, 0775)
if err != nil {
return "", "", err
}
}
_, err = util.FilePutContentWithByte(instanceCrt, clientCertPEM)
if err != nil {
return "", "", err
}
_, err = util.FilePutContentWithByte(instanceKey, clientKeyPEM)
if err != nil {
return "", "", err
}
return instanceCrt, instanceKey, nil
}

View File

@ -7,19 +7,14 @@ package common
import ( import (
"infini.sh/console/modules/agent/model" "infini.sh/console/modules/agent/model"
"infini.sh/framework/core/env" "infini.sh/framework/core/env"
"sync"
) )
var agentCfg *model.AgentConfig
var onceCfg = sync.Once{}
func GetAgentConfig() *model.AgentConfig { func GetAgentConfig() *model.AgentConfig {
onceCfg.Do(func() { agentCfg := &model.AgentConfig{}
agentCfg = &model.AgentConfig{} _, err := env.ParseConfig("agent", agentCfg )
_, err := env.ParseConfig("agent", agentCfg ) if err != nil {
if err != nil { panic(err)
panic(err) }
}
})
return agentCfg return agentCfg
} }

View File

@ -8,11 +8,13 @@ import (
"fmt" "fmt"
"infini.sh/console/modules/agent/model" "infini.sh/console/modules/agent/model"
"infini.sh/framework/core/agent" "infini.sh/framework/core/agent"
"infini.sh/framework/core/credential"
"infini.sh/framework/core/elastic" "infini.sh/framework/core/elastic"
"infini.sh/framework/core/event" "infini.sh/framework/core/event"
"infini.sh/framework/core/orm" "infini.sh/framework/core/orm"
"infini.sh/framework/core/util" "infini.sh/framework/core/util"
log "src/github.com/cihub/seelog" log "src/github.com/cihub/seelog"
"strings"
) )
func ParseAgentSettings(settings []agent.Setting)(*model.ParseAgentSettingsResult, error){ func ParseAgentSettings(settings []agent.Setting)(*model.ParseAgentSettingsResult, error){
@ -71,44 +73,48 @@ func ParseAgentSettings(settings []agent.Setting)(*model.ParseAgentSettingsResul
}, nil }, nil
} }
// GetAgentSettings query agent setting by agent id and updated timestamp,
// if there has any setting was updated, then return setting list includes settings not changed,
// otherwise return empty setting list
func GetAgentSettings(agentID string, timestamp int64) ([]agent.Setting, error) { func GetAgentSettings(agentID string, timestamp int64) ([]agent.Setting, error) {
queryDsl := util.MapStr{ query := util.MapStr{
"size": 500, "bool": util.MapStr{
"query": util.MapStr{ "must": []util.MapStr{
"bool": util.MapStr{ {
"must": []util.MapStr{ "term": util.MapStr{
{ "metadata.category": util.MapStr{
"term": util.MapStr{ "value": "agent",
"metadata.category": util.MapStr{
"value": "agent",
},
},
},
{
"term": util.MapStr{
"metadata.name": util.MapStr{
"value": "task",
},
},
},
{
"term": util.MapStr{
"metadata.labels.agent_id": util.MapStr{
"value": agentID,
},
},
},
{
"range": util.MapStr{
"updated": util.MapStr{
"gt": timestamp,
},
}, },
}, },
}, },
{
"term": util.MapStr{
"metadata.name": util.MapStr{
"value": "task",
},
},
},
{
"term": util.MapStr{
"metadata.labels.agent_id": util.MapStr{
"value": agentID,
},
},
},
//{
// "range": util.MapStr{
// "updated": util.MapStr{
// "gt": timestamp,
// },
// },
//},
}, },
}, },
} }
queryDsl := util.MapStr{
"size": 500,
"query": query,
}
q := orm.Query{ q := orm.Query{
RawQuery: util.MustToJSONBytes(queryDsl), RawQuery: util.MustToJSONBytes(queryDsl),
} }
@ -119,7 +125,10 @@ func GetAgentSettings(agentID string, timestamp int64) ([]agent.Setting, error)
if len(result.Result) == 0 { if len(result.Result) == 0 {
return nil, nil return nil, nil
} }
var settings []agent.Setting var (
settings []agent.Setting
hasUpdated bool
)
for _, row := range result.Result { for _, row := range result.Result {
setting := agent.Setting{} setting := agent.Setting{}
buf, err := util.ToJSONBytes(row) buf, err := util.ToJSONBytes(row)
@ -130,8 +139,14 @@ func GetAgentSettings(agentID string, timestamp int64) ([]agent.Setting, error)
if err != nil { if err != nil {
return nil, err return nil, err
} }
if setting.Updated != nil && setting.Updated.UnixMilli() > timestamp {
hasUpdated = true
}
settings = append(settings, setting) settings = append(settings, setting)
} }
if !hasUpdated {
return nil, nil
}
return settings, nil return settings, nil
} }
@ -336,3 +351,131 @@ func GetLatestOnlineAgentIDs(agentIds []string, lastSeconds int) (map[string]str
} }
return agentIDs, nil return agentIDs, nil
} }
func GetAgentIngestConfig() (string, *elastic.BasicAuth, error) {
agCfg := GetAgentConfig()
var (
endpoint string
ok bool
)
if endpoint, ok = agCfg.Setup.IngestClusterEndpoint.(string);ok {
if endpoint = strings.TrimSpace(endpoint); endpoint == "" {
return "", nil, fmt.Errorf("config ingest_cluster_endpoint must not be empty")
}
}
var (
basicAuth elastic.BasicAuth
)
if agCfg.Setup.IngestClusterCredentialID != "" {
cred := credential.Credential{}
cred.ID = agCfg.Setup.IngestClusterCredentialID
_, err := orm.Get(&cred)
if err != nil {
return "", nil, fmt.Errorf("query credential [%s] error: %w", cred.ID, err)
}
info, err := cred.Decode()
if err != nil {
return "", nil, fmt.Errorf("decode credential [%s] error: %w", cred.ID, err)
}
if basicAuth, ok = info.(elastic.BasicAuth); !ok {
log.Debug("invalid credential: ", cred)
}
}
tpl := `elasticsearch:
- name: default
enabled: true
endpoint: %s
discovery:
enabled: true
basic_auth:
username: %s
password: $[[keystore.ingest_cluster_password]]
metrics:
enabled: true
queue: metrics
network:
enabled: true
summary: true
details: true
memory:
metrics:
- swap
- memory
disk:
metrics:
- iops
- usage
cpu:
metrics:
- idle
- system
- user
- iowait
- load
instance:
enabled: true
pipeline:
- name: logs_indexing_merge
auto_start: true
keep_running: true
processor:
- indexing_merge:
index_name: ".infini_logs"
elasticsearch: "default"
input_queue: "logs"
idle_timeout_in_seconds: 10
output_queue:
name: "logs_requests"
label:
tag: "logs"
worker_size: 1
bulk_size_in_mb: 10
- name: ingest_logs
auto_start: true
keep_running: true
processor:
- bulk_indexing:
bulk:
compress: true
batch_size_in_mb: 5
batch_size_in_docs: 5000
consumer:
fetch_max_messages: 100
queues:
type: indexing_merge
tag: "logs"
when:
cluster_available: ["default"]
- name: metrics_indexing_merge
auto_start: true
keep_running: true
processor:
- indexing_merge:
elasticsearch: "default"
index_name: ".infini_metrics"
input_queue: "metrics"
output_queue:
name: "metrics_requests"
label:
tag: "metrics"
worker_size: 1
bulk_size_in_mb: 5
- name: ingest_metrics
auto_start: true
keep_running: true
processor:
- bulk_indexing:
bulk:
compress: true
batch_size_in_mb: 5
batch_size_in_docs: 5000
consumer:
fetch_max_messages: 100
queues:
type: indexing_merge
tag: "metrics"
when:
cluster_available: ["default"]`
tpl = fmt.Sprintf(tpl, endpoint, basicAuth.Username)
return tpl, &basicAuth, nil
}

View File

@ -9,7 +9,7 @@ type AgentConfig struct {
StateManager struct{ StateManager struct{
Enabled bool `config:"enabled"` Enabled bool `config:"enabled"`
} `config:"state_manager"` } `config:"state_manager"`
Setup SetupConfig `config:"setup"` Setup *SetupConfig `config:"setup"`
} }
type SetupConfig struct { type SetupConfig struct {
@ -17,5 +17,8 @@ type SetupConfig struct {
Version string `config:"version"` Version string `config:"version"`
CACertFile string `config:"ca_cert"` CACertFile string `config:"ca_cert"`
CAKeyFile string `config:"ca_key"` CAKeyFile string `config:"ca_key"`
ScriptEndpoint string `config:"script_endpoint"` ConsoleEndpoint string `config:"console_endpoint"`
IngestClusterEndpoint interface{} `config:"ingest_cluster_endpoint"`
IngestClusterCredentialID string `config:"ingest_cluster_credential_id"`
Port string `config:"port"`
} }

View File

@ -17,9 +17,10 @@ import (
"infini.sh/framework/core/kv" "infini.sh/framework/core/kv"
"infini.sh/framework/core/orm" "infini.sh/framework/core/orm"
"infini.sh/framework/core/util" "infini.sh/framework/core/util"
"infini.sh/framework/modules/elastic"
"runtime" "runtime"
"runtime/debug" "runtime/debug"
"strings" "src/gopkg.in/yaml.v2"
"sync" "sync"
"time" "time"
) )
@ -63,13 +64,13 @@ type StateManager struct {
timestamps map[string]int64 timestamps map[string]int64
} }
func NewStateManager(TTL time.Duration, kvKey string, agentIds map[string]string) *StateManager { func NewStateManager(TTL time.Duration, kvKey string, agentIds map[string]string, agentClient *client.Client) *StateManager {
return &StateManager{ return &StateManager{
TTL: TTL, TTL: TTL,
KVKey: kvKey, KVKey: kvKey,
stopC: make(chan struct{}), stopC: make(chan struct{}),
stopCompleteC: make(chan struct{}), stopCompleteC: make(chan struct{}),
agentClient: &client.Client{}, agentClient: agentClient,
agentIds: agentIds, agentIds: agentIds,
workerChan: make(chan struct{}, runtime.NumCPU()), workerChan: make(chan struct{}, runtime.NumCPU()),
timestamps: map[string]int64{}, timestamps: map[string]int64{},
@ -124,7 +125,9 @@ func (sm *StateManager) checkAgentStatus() {
}() }()
ag, err := sm.GetAgent(agentID) ag, err := sm.GetAgent(agentID)
if err != nil { if err != nil {
log.Error(err) if err != elastic.ErrNotFound {
log.Error(err)
}
return return
} }
ag.Status = model.StatusOffline ag.Status = model.StatusOffline
@ -142,6 +145,13 @@ func (sm *StateManager) checkAgentStatus() {
} }
func (sm *StateManager) syncSettings(agentID string) { func (sm *StateManager) syncSettings(agentID string) {
ag, err := sm.GetAgent(agentID)
if err != nil {
if err != elastic.ErrNotFound {
log.Errorf("get agent error: %v", err)
}
return
}
newTimestamp := time.Now().UnixMilli() newTimestamp := time.Now().UnixMilli()
settings, err := common.GetAgentSettings(agentID, sm.timestamps[agentID]) settings, err := common.GetAgentSettings(agentID, sm.timestamps[agentID])
if err != nil { if err != nil {
@ -157,36 +167,58 @@ func (sm *StateManager) syncSettings(agentID string) {
log.Errorf("parse agent settings error: %v", err) log.Errorf("parse agent settings error: %v", err)
return return
} }
ag, err := sm.GetAgent(agentID) agClient := sm.GetAgentClient()
var clusterCfgs []util.MapStr
if len(parseResult.ClusterConfigs) > 0 {
for _, cfg := range parseResult.ClusterConfigs {
clusterCfg := util.MapStr{
"name": cfg.ID,
"enabled": true,
"endpoint": cfg.Endpoint,
}
if cfg.BasicAuth != nil && cfg.BasicAuth.Password != ""{
err = agClient.SetKeystoreValue(context.Background(), ag.GetEndpoint(), fmt.Sprintf("%s_password", cfg.ID), cfg.BasicAuth.Password)
if err != nil {
log.Errorf("set keystore value error: %v", err)
continue
}
clusterCfg["basic_auth"] = util.MapStr{
"username": cfg.BasicAuth.Username,
"password": fmt.Sprintf("$[[keystore.%s_password]]", cfg.ID),
}
}
clusterCfgs = append(clusterCfgs, clusterCfg)
}
}
var dynamicCfg = util.MapStr{}
if len(clusterCfgs) > 0 {
dynamicCfg["elasticsearch"] = clusterCfgs
}
if len(parseResult.Pipelines) > 0 {
dynamicCfg["pipeline"] = parseResult.Pipelines
}
cfgBytes, err := yaml.Marshal(dynamicCfg)
if err != nil { if err != nil {
log.Errorf("get agent error: %v", err) log.Error("serialize config to yaml error: ", err)
return return
} }
agClient := sm.GetAgentClient() err = agClient.SaveDynamicConfig(context.Background(), ag.GetEndpoint(), "dynamic_task", string(cfgBytes))
if len(parseResult.ClusterConfigs) > 0 { //for _, pipelineID := range parseResult.ToDeletePipelineNames {
err = agClient.RegisterElasticsearch(nil, ag.GetEndpoint(), parseResult.ClusterConfigs) // err = agClient.DeletePipeline(context.Background(), ag.GetEndpoint(), pipelineID)
if err != nil { // if err != nil {
log.Errorf("register elasticsearch config error: %v", err) // if !strings.Contains(err.Error(), "not found") {
return // log.Errorf("delete pipeline error: %v", err)
} // continue
} // }
for _, pipelineID := range parseResult.ToDeletePipelineNames { // }
err = agClient.DeletePipeline(context.Background(), ag.GetEndpoint(), pipelineID) //}
if err != nil { //for _, pipeline := range parseResult.Pipelines {
if !strings.Contains(err.Error(), "not found") { // err = agClient.CreatePipeline(context.Background(), ag.GetEndpoint(), util.MustToJSONBytes(pipeline))
log.Errorf("delete pipeline error: %v", err) // if err != nil {
continue // log.Errorf("create pipeline error: %v", err)
} // return
} // }
//todo update delete pipeline state //}
}
for _, pipeline := range parseResult.Pipelines {
err = agClient.CreatePipeline(context.Background(), ag.GetEndpoint(), util.MustToJSONBytes(pipeline))
if err != nil {
log.Errorf("create pipeline error: %v", err)
return
}
}
sm.timestamps[agentID] = newTimestamp sm.timestamps[agentID] = newTimestamp
} }
@ -239,7 +271,7 @@ func (sm *StateManager) GetAgent(ID string) (*agent.Instance, error) {
if time.Since(timestamp) > sm.TTL { if time.Since(timestamp) > sm.TTL {
exists, err := orm.Get(inst) exists, err := orm.Get(inst)
if err != nil { if err != nil {
return nil, fmt.Errorf("get agent [%s] error: %w", ID, err) return nil, err
} }
if !exists { if !exists {
return nil, fmt.Errorf("can not found agent [%s]", ID) return nil, fmt.Errorf("can not found agent [%s]", ID)