主机信息模块,对接云商的云主机,用于保存主机的专有信息
apps/host/pb/host.proto
Host 保存通用信息,Spec保存专有信息
message Host {
// 通用定义
// @gotags: json:"resource"
resource.Resource resource = 1;
// 通用定义
// @gotags: json:"spec"
Spec spec = 2;
}
message Spec {
// GPU类型
// @gotags: json:"gpu_spec"
string gpu_spec = 2;
// 操作系统类型,分为Windows和Linux
// @gotags: json:"os_type"
string os_type = 3;
// 操作系统名称
// @gotags: json:"os_name"
string os_name = 4;
// 镜像ID
// @gotags: json:"image_id"
string image_id = 5;
// 公网出带宽最大值,单位为 Mbps
// @gotags: json:"internet_max_bandwidth_out"
int64 internet_max_bandwidth_out = 6;
// 公网入带宽最大值,单位为 Mbps
// @gotags: json:"internet_max_bandwidth_in"
int64 internet_max_bandwidth_in = 7;
}
message HostSet {
// 数据
// @gotags: json:"total"
int64 total = 1;
// 数据
// @gotags: json:"items"
repeated Host items = 2;
}
apps/host/pb/rpc.proto
syntax = "proto3";
package wendao365.cmdb.host;
option go_package = "gitee.com/wendao365/mycmdb/apps/host";
import "apps/host/pd/host.proto";
import "github.com/infraboard/mcube/pb/page/page.proto";
service RPC {
rpc CreateHost(Host) returns(Host);
rpc QueryHost(QueryHostRequest) returns(HostSet);
}
message QueryHostRequest {
// 分页参数
// @gotags: json:"page"
infraboard.mcube.page.PageRequest page = 1;
// 主机资源Id列表
// @gotags: json:"resource_ids"
repeated string resource_ids = 2;
}
apps/host/impl/impl.go
var (
// Service 服务实例
svr = &service{}
)
type service struct {
db *gorm.DB
log logger.Logger
host.UnimplementedRPCServer
resource resource.Service
}
func (s *service) Config() error {
db, err := conf.C().MySQL.ORM()
if err != nil {
return err
}
if conf.C().Log.Level == "debug" {
db = db.Debug()
}
db = db.Clauses(clause.OnConflict{
UpdateAll: true,
})
if err != nil {
return err
}
s.db = db
s.log = zap.L().Named(s.Name())
s.resource = app.GetInternalApp(resource.AppName).(resource.Service)
return nil
}
func (s *service) Name() string {
return host.AppName
}
func (s *service) Registry(server *grpc.Server) {
host.RegisterRPCServer(server, svr)
}
func init() {
app.RegistryGrpcApp(svr)
app.RegistryInternalApp(svr)
}
apps/host/impl/host.go
func (s *service) CreateHost(ctx context.Context, req *host.Host) (
*host.Host, error) {
ins := NewHost(req)
// 保存通用信息
rs, err := s.resource.SaveResource(ctx, req.Resource)
if err != nil {
return nil, err
}
req.Resource = rs
// 保存主机信息
err = s.db.WithContext(ctx).Create(ins).Error
if err != nil {
return nil, err
}
return req, nil
}
func (s *service) QueryHost(ctx context.Context, req *host.QueryHostRequest) (
*host.HostSet, error) {
// 1. 先查询Host信息
temp := NewHostSet()
query := s.db.WithContext(ctx).Model(&Host{})
if len(req.ResourceIds) > 0 {
query = query.Where("resource_id IN (?)", req.ResourceIds)
}
err := query.
Offset(int(req.Page.ComputeOffset())).
Limit(int(req.Page.PageSize)).
Scan(&temp.Items).
Error
if err != nil {
return nil, err
}
err = query.Count(&temp.Total).Error
if err != nil {
return nil, err
}
set := temp.HostSet()
// 2. 为这些Host补充 Resource信息
rReq := resource.NewQueryResourceRequest()
rReq.ResourceIds = req.ResourceIds
rs, err := s.resource.QueryResource(ctx, rReq)
if err != nil {
return nil, err
}
set.UpdateResource(rs.Items...)
return set, nil
}
apps/host/app.go
const (
AppName = "host"
)
type Service interface {
RPCServer
}
func NewHostSet() *HostSet {
return &HostSet{
Items: []*Host{},
}
}
func (s *HostSet) Add(item *Host) {
s.Items = append(s.Items, item)
}
func (s *HostSet) Length() int {
return len(s.Items)
}
func NeWQueryHostRequest() *QueryHostRequest {
return &QueryHostRequest{
Page: request.NewDefaultPageRequest(),
}
}
func NewDefaultHost() *Host {
return &Host{
Resource: resource.NewDefaultResource(resource.TYPE_HOST),
Spec: &Spec{},
}
}
func (s *HostSet) UpdateResource(rss ...*resource.Resource) {
for m := range rss {
rid := rss[m].Meta.ResourceId
for i := range s.Items {
if s.Items[i].Resource.Meta.ResourceId == rid {
s.Items[i].Resource = rss[m]
}
}
}
}
apps/host/impl/host_dao.go
func NewHostSet() *HostSet {
return &HostSet{
Items: []*Host{},
}
}
type HostSet struct {
Total int64
Items []*Host
}
func (h *HostSet) HostSet() *host.HostSet {
set := host.NewHostSet()
for i := range h.Items {
item := h.Items[i]
ins := host.NewDefaultHost()
ins.Resource.Meta.ResourceId = item.ResourceId
ins.Spec = item.Spec
set.Add(ins)
}
return set
}
func NewHost(h *host.Host) *Host {
return &Host{
ResourceId: h.Resource.Meta.ResourceId,
Spec: h.Spec,
}
}
// 构造符合数据库映射逻辑的对象
type Host struct {
ResourceId string `json:"resource_id"`
*host.Spec
}
func (i *Host) TableName() string {
return "resource_host"
}
IOC注册—apps/internal.go
import (
// 注册所有GRPC服务模块, 暴露给框架GRPC服务器加载, 注意 导入有先后顺序
_ "gitee.com/wendao365/mycmdb/apps/host/impl"
_ "gitee.com/wendao365/mycmdb/apps/resource/impl"
)
单元测试
apps/host/impl/impl_test.go
创建主机的测试
func init() {
tools.DevelopmentSetup()
impl = app.GetInternalApp(host.AppName).(host.Service)
}
var (
impl host.Service
ctx = context.Background()
)
func TestCreateHost(t *testing.T) {
res := &resource.Resource{
Meta: &resource.Meta{
ResourceId: "host_01",
Domain: "default",
Namespace: "default",
Env: "测试环境",
SyncAt: time.Now().Unix(),
},
Spec: &resource.Spec{
Vendor: resource.VENDOR_ALIYUN,
ResourceType: resource.TYPE_HOST,
Name: "test01",
},
Cost: &resource.Cost{
RealCost: 80000,
},
Status: &resource.Status{
Phase: "Running",
},
Tags: []*resource.Tag{
{Key: "app", Value: "app_01", Describe: "应用01"},
{Key: "app", Value: "app_02", Describe: "应用02"},
},
}
ins, err := impl.CreateHost(ctx, &host.Host{
Resource: res,
Spec: &host.Spec{
GpuSpec: "amd",
OsType: "Linux",
OsName: "CentOs 8",
},
})
if err != nil {
t.Fatal(err)
}
t.Log(ins)
}
测试结果
=== RUN TestCreateHost
2023/01/26 13:36:39 F:/go/project/0-shizhanxiangmu/myCMDB/mycmdb/apps/resource/impl/resource_dao.go:23
[29.504ms] [rows:1] INSERT INTO `resource_meta` (`resource_id`,`domain`,`namespace`,`env`,`sync_at`,`credential_id`,`create_at`,`serial_number`) VALUES ('instance_06','default','default','测试环境',1674711399,'',0,'')
2023/01/26 13:36:39 F:/go/project/0-shizhanxiangmu/myCMDB/mycmdb/apps/resource/impl/resource_dao.go:27
[28.631ms] [rows:1] INSERT INTO `resource_spec` (`resource_id`,`vendor`,`resource_type`,`region`,`zone`,`owner`,`name`,`category`,`type`,`description`,`expire_at`,`update_at`,`release_protection`,`cpu`,`gpu`,`memory`,`storage`,`band_width`) VALUES ('instance_06',0,0,'','','','test01','','','',0,0,NULL,0,0,0,0,0)
2023/01/26 13:36:39 F:/go/project/0-shizhanxiangmu/myCMDB/mycmdb/apps/resource/impl/resource_dao.go:31
[28.761ms] [rows:1] INSERT INTO `resource_status` (`resource_id`,`phase`,`lock_mode`,`lock_reason`,`public_ip`,`private_ip`) VALUES ('instance_06','Running','','','','')
2023/01/26 13:36:39 F:/go/project/0-shizhanxiangmu/myCMDB/mycmdb/apps/resource/impl/resource_dao.go:35
[31.730ms] [rows:1] INSERT INTO `resource_cost` (`resource_id`,`pay_mode`,`pay_mode_detail`,`sale_price`,`real_cost`,`policy`,`unit_price`) VALUES ('instance_06',NULL,'',0.000000,80000.000000,0.000000,0.000000)
2023/01/26 13:36:39 F:/go/project/0-shizhanxiangmu/myCMDB/mycmdb/apps/resource/impl/resource_dao.go:40
[27.046ms] [rows:0] DELETE FROM `resource_tag` WHERE resource_id = 'instance_06'
2023/01/26 13:36:39 F:/go/project/0-shizhanxiangmu/myCMDB/mycmdb/apps/resource/impl/resource_dao.go:45
[27.724ms] [rows:1] INSERT INTO `resource_tag` (`resource_id`,`purpose`,`key`,`value`,`describe`,`weight`,`read_only`,`hidden`) VALUES ('instance_06',0,'app','app_01','应用01',0,false, false)
2023/01/26 13:36:39 F:/go/project/0-shizhanxiangmu/myCMDB/mycmdb/apps/resource/impl/resource_dao.go:45
[18.881ms] [rows:1] INSERT INTO `resource_tag` (`resource_id`,`purpose`,`key`,`value`,`describe`,`weight`,`read_only`,`hidden`) VALUES ('instance_06',0,'app','app_02','应用02',0,false,fal se)
2023/01/26 13:36:39 F:/go/project/0-shizhanxiangmu/myCMDB/mycmdb/apps/host/impl/host.go:21
[27.889ms] [rows:1] INSERT INTO `resource_host` (`resource_id`,`gpu_spec`,`os_type`,`os_name`,`image_id`,`internet_max_bandwidth_out`,`internet_max_bandwidth_in`) VALUES ('instance_06','amd','Linux','CentOs 8','',0,0) ON DUPLICATE KEY UPDATE `resource_id`=VALUES(`resource_id`),`gpu_spec`=VALUES(`gpu_spec`),`os_type`=VALUES(`os_type`),`os_name`=VALUES(`os_name`),`image_id`=VALUES(`image_id`),`internet_max_bandwidth_out`=VALUES(`internet_max_bandwidth_out`),`internet_max_bandwidth_in`=VALUES(`internet_max_bandwidth_in`)
impl_test.go:80: resource:{meta:{resource_id:"instance_06" domain:"default" namespace:"default" env:"测试环境" sync_at:1674711399} s pec:{name:"test01"} cost:{real_cost:80000} status:{phase:"Running"} tags:{key:"app" value:"app_01" describe:"应用01"} tags:{key:"app" value:"app_02 " describe:"应用02"}} spec:{gpu_spec:"amd" os_type:"Linux" os_name:"Cent Os 8"}
--- PASS: TestCreateHost (0.27s)
PASS
apps/host/impl/impl_test.go
查询主机的测试
func TestQueryHost(t *testing.T) {
set, err := impl.QueryHost(ctx, host.NeWQueryHostRequest())
if err != nil {
t.Fatal(err)
}
t.Log(set)
}
测试结果
=== RUN TestQueryHost
2023/01/26 13:41:06 F:/go/project/0-shizhanxiangmu/myCMDB/mycmdb/apps/host/impl/host.go:41
[15.802ms] [rows:1] SELECT * FROM `resource_host` LIMIT 20
2023/01/26 13:41:06 F:/go/project/0-shizhanxiangmu/myCMDB/mycmdb/apps/host/impl/host.go:47
[17.023ms] [rows:1] SELECT count(*) FROM `resource_host` LIMIT 20
2023/01/26 13:41:06 F:/go/project/0-shizhanxiangmu/myCMDB/mycmdb/apps/resource/impl/resource.go:68
[17.411ms] [rows:2] SELECT * FROM resource_meta m LEFT JOIN resource_spec s ON s.resource_id = m.resource_id LEFT JOIN resource_cost c ON c.resource_id = m.resource_id LEFT JOIN resource_status t ON t.resource_id = m.resource_id ORDER BY m.create_at DESC LIMIT 20
2023/01/26 13:41:06 F:/go/project/0-shizhanxiangmu/myCMDB/mycmdb/apps/resource/impl/resource.go:80
[14.825ms] [rows:1] SELECT count(*) FROM resource_meta m LEFT JOIN resource_spec s ON s.resource_id = m.resource_id LEFT JOIN resource_cost c ON c.resource_id = m.resource_id LEFT JOIN resource_status t ON t.resource_id = m.resource_id LIMIT 20
impl_test.go:88: items:{resource:{meta:{resource_id:"instance_06" domain:"default" namespace:"default" env:"测试环境" sync_at:1674711 399} spec:{name:"test01"} cost:{real_cost:80000} status:{phase:"Running"}} spec:{gpu_spec:"amd" os_type:"Linux" os_name:"CentOs 8"}}
--- PASS: TestQueryHost (0.07s)
PASS
评论区