侧边栏壁纸
博主头像
问道

问道的小花园,总能给你带来惊喜

  • 累计撰写 68 篇文章
  • 累计创建 35 个标签
  • 累计收到 3 条评论

混合云资产管理项目(四)

问道
2023-01-26 / 0 评论 / 0 点赞 / 362 阅读 / 9,957 字 / 正在检测是否收录...
温馨提示:
本文最后更新于 2023-01-26,若内容或图片失效,请留言反馈。部分素材来自网络,若不小心影响到您的利益,请联系我们删除。

主机信息模块,对接云商的云主机,用于保存主机的专有信息

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
0

评论区