289 lines
7.1 KiB
Go
289 lines
7.1 KiB
Go
package create_ecs
|
||
|
||
import (
|
||
"fmt"
|
||
"io"
|
||
"strings"
|
||
"sync"
|
||
"time"
|
||
|
||
"gitlink.org.cn/cloudream/common/pkgs/logger"
|
||
schsdk "gitlink.org.cn/cloudream/common/sdks/scheduler"
|
||
"gitlink.org.cn/cloudream/common/utils/http2"
|
||
"gitlink.org.cn/cloudream/common/utils/serder"
|
||
exemq "gitlink.org.cn/cloudream/scheduler/common/pkgs/mq/executor"
|
||
)
|
||
|
||
type response[T any] struct {
|
||
Code string `json:"code"`
|
||
Msg string `json:"msg"`
|
||
Data T `json:"data"`
|
||
}
|
||
|
||
type sugonToken struct {
|
||
ClusterId string `json:"clusterId"`
|
||
ClusterName string `json:"clusterName"`
|
||
Token string `json:"token"`
|
||
}
|
||
|
||
type sugonTokenResp struct {
|
||
Code string `json:"code"`
|
||
Msg string `json:"msg"`
|
||
Data []sugonToken `json:"data"`
|
||
}
|
||
|
||
func getToken(authConfigs map[string]interface{}) (string, error) {
|
||
|
||
header := make(map[string]string)
|
||
header["User"] = authConfigs["user"].(string)
|
||
header["Password"] = authConfigs["password"].(string)
|
||
header["Orgid"] = authConfigs["orgid"].(string)
|
||
|
||
resp, err := http2.PostJSON(authConfigs["get_token_url"].(string), http2.RequestParam{
|
||
Header: header,
|
||
})
|
||
if err != nil {
|
||
return "", err
|
||
}
|
||
|
||
contType := resp.Header.Get("Content-Type")
|
||
if strings.Contains(contType, http2.ContentTypeJSON) {
|
||
var codeResp sugonTokenResp
|
||
if err := serder.JSONToObjectStream(resp.Body, &codeResp); err != nil {
|
||
return "", fmt.Errorf("parsing response: %w", err)
|
||
}
|
||
|
||
for i := 0; i < len(codeResp.Data); i++ {
|
||
data := codeResp.Data[i]
|
||
if data.ClusterName == authConfigs["clusterName"] {
|
||
return data.Token, nil
|
||
}
|
||
}
|
||
}
|
||
|
||
return "", fmt.Errorf("there is no token")
|
||
}
|
||
|
||
type sugonUrlResp struct {
|
||
Name string `json:"name"`
|
||
AiUrls []sugonUrl `json:"aiUrls"`
|
||
EfileUrls []sugonUrl `json:"efileUrls"`
|
||
}
|
||
|
||
type sugonUrl struct {
|
||
Url string `json:"url"`
|
||
}
|
||
|
||
func getUrl(token string, url string) (string, string, error) {
|
||
header := make(map[string]string)
|
||
header["Token"] = token
|
||
resp, err := http2.GetForm(url, http2.RequestParam{
|
||
Header: header,
|
||
})
|
||
if err != nil {
|
||
return "", "", err
|
||
}
|
||
|
||
// 读取并打印原始响应
|
||
bodyBytes, err := io.ReadAll(resp.Body)
|
||
if err != nil {
|
||
return "", "", fmt.Errorf("reading response body: %w", err)
|
||
}
|
||
|
||
contType := resp.Header.Get("Content-Type")
|
||
if strings.Contains(contType, http2.ContentTypeJSON) {
|
||
var codeResp response[sugonUrlResp]
|
||
if err := serder.JSONToObject(bodyBytes, &codeResp); err != nil {
|
||
return "", "", fmt.Errorf("parsing response: %w", err)
|
||
}
|
||
|
||
if len(codeResp.Data.AiUrls) == 0 {
|
||
return "", "", fmt.Errorf("there is no url")
|
||
}
|
||
|
||
return codeResp.Data.AiUrls[0].Url, codeResp.Data.EfileUrls[0].Url, nil
|
||
}
|
||
|
||
return "", "", fmt.Errorf("there is no token")
|
||
}
|
||
|
||
type SugonCloud struct {
|
||
Lock sync.Mutex
|
||
}
|
||
|
||
var ecsConfig map[string]interface{}
|
||
var authConfig map[string]interface{}
|
||
var instanceKV map[string]string
|
||
var sugonClient exemq.HttpClient
|
||
var efileClient exemq.HttpClient
|
||
|
||
func SugonCloudConfig(authConfigs map[string]interface{}, ecsConfigs map[string]interface{}) {
|
||
authConfigs["get_token_url"] = "https://ac.sugon.com/ac/openapi/v2/tokens"
|
||
authConfig = authConfigs
|
||
ecsConfig = ecsConfigs
|
||
instanceKV = make(map[string]string)
|
||
|
||
// 获取token
|
||
token, err := getToken(authConfigs)
|
||
if err != nil {
|
||
logger.Error(err.Error())
|
||
return
|
||
}
|
||
// 获取请求链接
|
||
url, efileUrl, err := getUrl(token, "https://ac.sugon.com/ac/openapi/v2/center")
|
||
httpPool := exemq.NewHttpPool(&exemq.Config{})
|
||
c, err := httpPool.AcquireByUrl(url)
|
||
if err != nil {
|
||
logger.Error(err.Error())
|
||
return
|
||
}
|
||
sugonClient = *c
|
||
|
||
ec, err := httpPool.AcquireByUrl(efileUrl)
|
||
if err != nil {
|
||
logger.Error(err.Error())
|
||
return
|
||
}
|
||
efileClient = *ec
|
||
}
|
||
|
||
func (s *SugonCloud) CreateServer() (string, string, error) {
|
||
|
||
instanceServiceName := "auto_instance_" + time.Now().Format("20060102150405")
|
||
ecsConfig["instanceServiceName"] = instanceServiceName
|
||
// 获取token
|
||
token, err := getToken(authConfig)
|
||
if err != nil {
|
||
logger.Error(err.Error())
|
||
return "", "", err
|
||
}
|
||
// 创建实例
|
||
_, err = sugonClient.CreateSugonInstance(token, ecsConfig)
|
||
if err != nil {
|
||
return "", "", err
|
||
}
|
||
|
||
// 获取实例ID,4*1000s后还未获取ID,则认为实例创建失败
|
||
var instanceID string
|
||
for i := 0; i <= 1000; i++ {
|
||
id, status, err := sugonClient.GetInstanceID(token, instanceServiceName)
|
||
|
||
if status == schsdk.Failed {
|
||
return "", "", fmt.Errorf("create instance failed")
|
||
}
|
||
|
||
if err != nil || status == schsdk.Waiting || status == schsdk.Deploying {
|
||
time.Sleep(4 * time.Second)
|
||
continue
|
||
}
|
||
|
||
if i == 1000 {
|
||
return "", "", fmt.Errorf("get instance id timeout")
|
||
}
|
||
|
||
instanceID = id
|
||
break
|
||
}
|
||
if instanceID == "" {
|
||
return "", "", fmt.Errorf("get instance id failed")
|
||
}
|
||
|
||
// 获取实例url
|
||
url, err := sugonClient.GetInstanceUrl(token, instanceID)
|
||
|
||
logger.Info("create ecs success, instanceID: " + instanceID + " url: " + url)
|
||
instanceKV[instanceID] = instanceServiceName
|
||
|
||
return instanceID, url, nil
|
||
}
|
||
|
||
func (s *SugonCloud) RunCommand(commands []string, instanceID string, timeout int) (string, error) {
|
||
s.Lock.Lock()
|
||
defer s.Lock.Unlock()
|
||
|
||
// 获取token
|
||
token, err := getToken(authConfig)
|
||
if err != nil {
|
||
logger.Error(err.Error())
|
||
return "", err
|
||
}
|
||
for i := 0; i < len(commands); i++ {
|
||
// 曙光集群不支持查看日志,通过预览日志文件返回
|
||
if timeout == -1 && i == len(commands)-1 {
|
||
// 命令执行完成需要时间,但是接口不会等待完成后才返回,所以这里需要轮询看是否有结果
|
||
content := ""
|
||
for j := 0; j < 10; j++ {
|
||
content, err = efileClient.PreviewFile(token, commands[i])
|
||
if err != nil {
|
||
return "", err
|
||
}
|
||
if content == "" {
|
||
time.Sleep(3 * time.Second)
|
||
continue
|
||
}
|
||
break
|
||
}
|
||
return content, err
|
||
}
|
||
|
||
_, err := sugonClient.RunCommand(token, instanceID, commands[i])
|
||
if err != nil {
|
||
return "", err
|
||
}
|
||
}
|
||
|
||
return "", err
|
||
}
|
||
|
||
func (s *SugonCloud) DeleteInstance(instanceID string) (string, error) {
|
||
// 获取token
|
||
token, err := getToken(authConfig)
|
||
if err != nil {
|
||
logger.Error(err.Error())
|
||
return "", err
|
||
}
|
||
instance, err := sugonClient.OperateSugonInstance(token, instanceID, schsdk.DestroyECS)
|
||
return instance, err
|
||
}
|
||
|
||
func (s *SugonCloud) StopInstance(instanceID string) (string, error) {
|
||
// 获取token
|
||
token, err := getToken(authConfig)
|
||
if err != nil {
|
||
logger.Error(err.Error())
|
||
return "", err
|
||
}
|
||
instance, err := sugonClient.OperateSugonInstance(token, instanceID, schsdk.PauseECS)
|
||
return instance, err
|
||
}
|
||
|
||
func (s *SugonCloud) RebootInstances(instanceID string) (string, error) {
|
||
//TODO implement me
|
||
panic("implement me")
|
||
}
|
||
|
||
func (s *SugonCloud) StartInstances(instanceID string) (string, error) {
|
||
// 获取token
|
||
token, err := getToken(authConfig)
|
||
if err != nil {
|
||
logger.Error(err.Error())
|
||
return "", err
|
||
}
|
||
instance, err := sugonClient.OperateSugonInstance(token, instanceID, schsdk.RunECS)
|
||
return instance, err
|
||
}
|
||
|
||
func (s *SugonCloud) AvailableCheck(instanceID string) bool {
|
||
instanceName, ok := instanceKV[instanceID]
|
||
if !ok {
|
||
return false
|
||
}
|
||
token, _ := getToken(authConfig)
|
||
instanceID, _, err := sugonClient.GetInstanceID(token, instanceName)
|
||
if err != nil || instanceID == "" {
|
||
logger.Error(err.Error())
|
||
return false
|
||
}
|
||
return true
|
||
}
|