diff --git a/go.mod b/go.mod index cf2d832..4b8f177 100644 --- a/go.mod +++ b/go.mod @@ -10,13 +10,21 @@ require ( gorm.io/gorm v1.25.1 ) -require github.com/mattn/go-sqlite3 v1.14.16 // indirect +require ( + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/mattn/go-sqlite3 v1.14.16 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/stretchr/objx v0.5.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) require ( github.com/jinzhu/inflection v1.0.0 // indirect github.com/jinzhu/now v1.1.5 // indirect + github.com/stretchr/testify v1.8.4 github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.669 github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cvm v1.0.669 + google.golang.org/protobuf v1.30.0 gopkg.in/yaml.v2 v2.4.0 gorm.io/driver/sqlite v1.5.0 ) diff --git a/go.sum b/go.sum index 3ffa613..e73bd76 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,8 @@ +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= @@ -11,15 +16,32 @@ github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/ github.com/mattn/go-sqlite3 v1.14.15/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= github.com/mattn/go-sqlite3 v1.14.16 h1:yOQRA0RpS5PFz/oikGwBEqvAWhWg5ufRz4ETLjwpU1Y= github.com/mattn/go-sqlite3 v1.14.16/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/russross/blackfriday v1.6.0 h1:KqfZb0pUVN2lYqZUYRddxF4OR8ZMURnJIG5Y3VRLtww= github.com/russross/blackfriday v1.6.0/go.mod h1:ti0ldHuxg49ri4ksnFxlkCfN+hvslNlmVHqNRXXJNAY= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.669 h1:5KKJBcemqKONBFxMdMyLMvk+TrqXaEPhqe9TrZqB3r0= github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.669/go.mod h1:7sCQWVkxcsR38nffDW057DRGk8mUjK1Ing/EFOK8s8Y= github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cvm v1.0.669 h1:gc1bPO/YVfuXEIs+HbQ/gFlFjdkJjOsjm8xWqF7hPww= github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cvm v1.0.669/go.mod h1:hhy13j6NKKxt/g62JZEDekJNQx3EAevnHopmwlt2tRc= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= +google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gorm.io/driver/sqlite v1.5.0 h1:zKYbzRCpBrT1bNijRnxLDJWPjVfImGEn0lSnUY5gZ+c= gorm.io/driver/sqlite v1.5.0/go.mod h1:kDMDfntV9u/vuMmz8APHtHF0b4nyBB7sfCieC6G8k8I= gorm.io/gorm v1.24.7-0.20230306060331-85eaf9eeda11/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k= diff --git a/models/Server.go b/models/server.go similarity index 51% rename from models/Server.go rename to models/server.go index c64e407..a8dd9eb 100644 --- a/models/Server.go +++ b/models/server.go @@ -3,8 +3,6 @@ package models import ( "encoding/json" "fmt" - "io/ioutil" - "log" "main/configs" "net/http" "time" @@ -13,8 +11,6 @@ import ( "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/errors" "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/profile" cvm "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cvm/v20170312" - - "gopkg.in/yaml.v2" ) type Server struct { @@ -38,46 +34,100 @@ type Config struct { } `yaml:"TencentCloud"` } -func CreateServerByTencentCloud() { - // 從 data/config.yaml 中獲取配置 - configFile, err := ioutil.ReadFile("data/config.yaml") - if err != nil { - log.Fatalf("Failed to read config file: %v", err) - } - - var config Config - err = yaml.Unmarshal(configFile, &config) - if err != nil { - log.Fatalf("Failed to unmarshal config file: %v", err) - } - - fmt.Println(config.TencentCloud.SecretId) - fmt.Println(config.TencentCloud.SecretKey) - - // 实例化一个认证对象,入参需要传入腾讯云账户 SecretId 和 SecretKey,此处还需注意密钥对的保密 - // 代码泄露可能会导致 SecretId 和 SecretKey 泄露,并威胁账号下所有资源的安全性。以下代码示例仅供参考,建议采用更安全的方式来使用密钥,请参见:https://cloud.tencent.com/document/product/1278/85305 - // 密钥可前往官网控制台 https://console.cloud.tencent.com/cam/capi 进行获取 - // 实例化一个client选项,可选的,没有特殊需求可以跳过 - // 实例化要请求产品的client对象,clientProfile是可选的 - credential := common.NewCredential(config.TencentCloud.SecretId, config.TencentCloud.SecretKey) +// 通過腾讯云API創建服務器 +func CreateServerByTencentCloud(SecretId, SecretKey string) error { + credential := common.NewCredential(SecretId, SecretKey) cpf := profile.NewClientProfile() cpf.HttpProfile.Endpoint = "cvm.tencentcloudapi.com" - client, _ := cvm.NewClient(credential, "", cpf) + client, err := cvm.NewClient(credential, "ap-shanghai", cpf) + if err != nil { + return fmt.Errorf("初始化騰訊雲SDK客戶端失敗: %v", err) + } // 实例化一个请求对象,每个接口都会对应一个request对象 request := cvm.NewRunInstancesRequest() + // 指定啓動模板, 以創建指定規格的服務器 + request.LaunchTemplate = &cvm.LaunchTemplate{ + LaunchTemplateId: common.StringPtr("lt-ks6y5evh"), + } + // 返回的resp是一个RunInstancesResponse的实例,与请求对象对应 response, err := client.RunInstances(request) if _, ok := err.(*errors.TencentCloudSDKError); ok { - fmt.Printf("An API error has returned: %s", err) - return + return fmt.Errorf("已返回 API 错误: %v", err) } if err != nil { - panic(err) + return fmt.Errorf("运行实例失败: %v", err) } // 输出json格式的字符串回包 fmt.Printf("%s", response.ToJsonString()) + return nil +} + +// 通過腾讯云API註銷指定的服務器 +func TerminateServerByTencentCloud(SecretId, SecretKey string) error { + credential := common.NewCredential(SecretId, SecretKey) + cpf := profile.NewClientProfile() + cpf.HttpProfile.Endpoint = "cvm.tencentcloudapi.com" + client, err := cvm.NewClient(credential, "ap-shanghai", cpf) + if err != nil { + return fmt.Errorf("初始化騰訊雲SDK客戶端失敗: %v", err) + } + + // 实例化一个请求对象,每个接口都会对应一个request对象 + request := cvm.NewTerminateInstancesRequest() + + // 指定要註銷的服務器ID + request.InstanceIds = []*string{common.StringPtr("ins-q0z8ev39")} + + // 返回的resp是一个TerminateInstancesResponse的实例,与请求对象对应 + response, err := client.TerminateInstances(request) + if _, ok := err.(*errors.TencentCloudSDKError); ok { + return fmt.Errorf("已返回 API 错误: %v", err) + } + if err != nil { + return fmt.Errorf("註銷實例失敗: %v", err) + } + // 输出json格式的字符串回包 + fmt.Printf("%s", response.ToJsonString()) + return nil +} + +// 檢查騰訊雲中的服務器狀態 +func CheckServerStatusByTencentCloud(SecretId, SecretKey string) error { + credential := common.NewCredential(SecretId, SecretKey) + cpf := profile.NewClientProfile() + cpf.HttpProfile.Endpoint = "cvm.tencentcloudapi.com" + client, err := cvm.NewClient(credential, "ap-shanghai", cpf) + if err != nil { + return fmt.Errorf("初始化騰訊雲SDK客戶端失敗: %v", err) + } + + // 实例化一个请求对象,每个接口都会对应一个request对象 + request := cvm.NewDescribeInstancesRequest() + + // 返回的resp是一个DescribeInstancesResponse的实例,与请求对象对应 + response, err := client.DescribeInstances(request) + if _, ok := err.(*errors.TencentCloudSDKError); ok { + return fmt.Errorf("已返回 API 错误: %v", err) + } + if err != nil { + return fmt.Errorf("运行实例失败: %v", err) + } + // 输出json格式的字符串回包 + fmt.Printf("%s", response.ToJsonString()) + + // 解析JSON + var data map[string]interface{} + if err := json.Unmarshal([]byte(response.ToJsonString()), &data); err != nil { + return err + } + fmt.Println(data["TotalCount"]) + fmt.Println(data["InstanceSet"]) + // TODO: 判斷每臺服務器狀態 + + return nil } func (server *Server) CheckStatus() error { diff --git a/models/server_test.go b/models/server_test.go new file mode 100644 index 0000000..e0f556c --- /dev/null +++ b/models/server_test.go @@ -0,0 +1,57 @@ +package models + +import ( + "io/ioutil" + "path/filepath" + "testing" + + "gopkg.in/yaml.v2" +) + +// 單元測試:創建服務器 +func TestCreateServerByTencentCloud(t *testing.T) { + absPath, _ := filepath.Abs("../data/config.yaml") + configFile, err := ioutil.ReadFile(absPath) + if err != nil { + t.Errorf("讀取配置文件失敗: %v", err) + } + var config Config + if err := yaml.Unmarshal(configFile, &config); err != nil { + t.Errorf("格式化配置文件失敗: %v", err) + } + if err := CreateServerByTencentCloud(config.TencentCloud.SecretId, config.TencentCloud.SecretKey); err != nil { + t.Errorf("創建服務器失敗: %v", err) + } +} + +// 單元測試:獲取服務器列表 +func TestGetServerListByTencentCloud(t *testing.T) { + absPath, _ := filepath.Abs("../data/config.yaml") + configFile, err := ioutil.ReadFile(absPath) + if err != nil { + t.Errorf("讀取配置文件失敗: %v", err) + } + var config Config + if err := yaml.Unmarshal(configFile, &config); err != nil { + t.Errorf("格式化配置文件失敗: %v", err) + } + if err := CheckServerStatusByTencentCloud(config.TencentCloud.SecretId, config.TencentCloud.SecretKey); err != nil { + t.Errorf("獲取服務器列表失敗: %v", err) + } +} + +// 單元測試:註銷服務器 +func TestTerminateServerByTencentCloud(t *testing.T) { + absPath, _ := filepath.Abs("../data/config.yaml") + configFile, err := ioutil.ReadFile(absPath) + if err != nil { + t.Errorf("讀取配置文件失敗: %v", err) + } + var config Config + if err := yaml.Unmarshal(configFile, &config); err != nil { + t.Errorf("格式化配置文件失敗: %v", err) + } + if err := TerminateServerByTencentCloud(config.TencentCloud.SecretId, config.TencentCloud.SecretKey); err != nil { + t.Errorf("註銷服務器失敗: %v", err) + } +}