侧边栏壁纸
博主头像
问道

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

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

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

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

主要编写资源的通用信息模块,protobuf的数据结构代码,和逻辑实现的代码

resource模块

保存资源的通用信息,

resource的proto文件

apps/resource/pb/resource.proto—新建

syntax = "proto3";

package go8.devcloud.cmdb.resource;
option go_package = "gitee.com/go-course/go8/projects/devcloud/cmdb/apps/resource";

message ResourceSet {
    // @gotags: json:"total"
    int64 total = 1;
    // @gotags: json:"items"
    repeated Resource items = 2;
}

message Resource {
    // 资源元数据信息
    // @gotags: json:"meta"
    Meta meta = 1;
    // 资源规格信息
    // @gotags: json:"spec"
    Spec spec = 2;
    // 资源费用
    // @gotags: json:"cost"
    Cost cost = 3;
    // 资源状态
    // @gotags: json:"status"
    Status status = 4;
    // 资源标签
    // @gotags: json:"tags"
    repeated Tag tags = 5;
    // 关联资源
    // @gotags: json:"related_resources"
    repeated Resource related_resources = 6;
}

// 资源元数据信息, 静态数据
message Meta {
    // 全局唯一Id, 直接使用个云商自己的Id
    // @gotags: json:"resource_id" validate:"required"
    string resource_id = 1; 
    // 资源所属域
    // @gotags: json:"domain" validate:"required"
    string domain = 2;
    // 资源所属空间
    // @gotags: json:"namespace" validate:"required"
    string namespace = 3;
    // 资源所属环境
    // @gotags: json:"env"
    string env = 4;
    // 同步时间
    // @gotags: json:"sync_at" validate:"required"
    int64 sync_at = 5;  
    // 用于同步的凭证ID, 该资源绑定的API Key, 通过该Key可以操作资源
    // @gotags: json:"credential_id"
    string credential_id = 6; 
    // 创建时间
    // @gotags: json:"create_at"
    int64  create_at = 7;
    // 序列号
    // @gotags: json:"serial_number"
    string serial_number = 9;
}

enum VENDOR {
    ALIYUN = 0;
    TENCENT = 1;
    HUAWEI = 2;
    VSPHERE = 3;
    AMAZON = 4;
}

enum TYPE {
    // 业务资源
    HOST = 0;
    RDS = 1;
    REDIS = 2;
    BUCKET = 3;
    DISK = 4;
    LB = 5;
    DOMAIN = 6;
    EIP = 7;
    MONGODB = 8;

    // 子资源
    DATABASE = 100;
    ACCOUNT = 101;

    // 未知资源
    OTHER = 199;

    // 辅助资源
    BILL = 200;
    ORDER = 201;
}

// 资源规格信息, 动态数据, 申请资源的规格信息
message Spec {
    // 厂商
    // @gotags: json:"vendor"
    VENDOR vendor = 1;
    // 资源类型
    // @gotags: json:"resource_type"
    TYPE resource_type = 2;
    // 地域
    // @gotags: json:"region"
    string region = 3; 
    // 区域
    // @gotags: json:"zone"
    string zone = 4; 
    // 资源所属账号
    // @gotags: json:"owner"
    string owner = 5;
    // 名称
    // @gotags: json:"name"
    string name = 6;
    // 种类
    // @gotags: json:"category"
    string category = 7;
    // 规格
    // @gotags: json:"type"
    string type = 8;
    // 描述
    // @gotags: json:"description"
    string description = 9;
    // 过期时间
    // @gotags: json:"expire_at"
    int64 expire_at = 10;
    // 更新时间
    // @gotags: json:"update_at"
    int64 update_at = 11;
    // 是否开启实例释放保护
    // @gotags: json:"release_protection"
    optional bool release_protection = 12;

    // 资源占用Cpu数量
    // @gotags: json:"cpu"
    int32 cpu = 15;
    // GPU数量
    // @gotags: json:"gpu"
    int32 gpu = 16;
    // 资源使用的内存
    // @gotags: json:"memory"
    int32 memory = 17;
    // 资源使用的存储
    // @gotags: json:"storage"
    int32 storage = 18;
    // 公网IP带宽, 单位M
    // @gotags: json:"band_width"
    int32 band_width =19;
}

enum PayMode {
    // 预付费, 包年包月
    PRE_PAY = 0;
    // 按需
    POST_PAY = 1;
    // 预定
    RESERVED_PAY = 2;
}

// 资源价格,动态数据
message Cost {
    // 实例付费方式
    // @gotags: json:"pay_mode"
    optional PayMode pay_mode = 1;
    // 付费方式详情
    // @gotags: json:"pay_mode_detail"
    string pay_mode_detail = 2;
    // 官网价,原价(分)
    // @gotags: json:"sale_price"
    double sale_price = 3;
    // 实际支付金额(分)
    // @gotags: json:"real_cost"
    double real_cost = 4;
    // 折扣率
    // @gotags: json:"policy"
    double policy = 5;
    // 单价(分)
    // @gotags: json:"unit_price"
    double unit_price = 6;
}

// 资源价格,动态数据
message Status {
    // 资源当前状态
    // @gotags: json:"phase"
    string phase = 1;
    // 实例锁定模式; Unlock:正常;ManualLock:手动触发锁定;LockByExpiration:实例过期自动锁定;LockByRestoration:实例回滚前的自动锁定;LockByDiskQuota:实例空间满自动锁定
    // @gotags: json:"lock_mode"
    string lock_mode = 2;
    // 锁定原因
    // @gotags: json:"lock_reason"
    string lock_reason = 3;
    // 资源访问地址
    // 公网IP, 或者域名
    // @gotags: json:"public_ip"
    string public_ip = 4;
    // 内网IP, 或者域名
    // @gotags: json:"private_ip"
    string private_ip = 5;
}


// 标签用途
enum TAG_PURPOSE {
    // 用于资源分组
    GROUP = 0;
    // 系统使用标签, 表示被其他系统引用, 比如应用关联标签
    SYSTEM = 1;
    // 用于资源监控
    MONITOR = 2;
}

// 资源价格,动态数据
message Tag {
    // 标签的类型
    // @gotags: json:"purpose"
    TAG_PURPOSE purpose = 1;
    // 标签的Key
    // @gotags: json:"key" validate:"lte=255,required"
    string key = 2;
    // 标签的值
    // @gotags: json:"value" validate:"lte=255,required"
    string value = 3;
    // 标签的值的描述, 通常用于展示
    // @gotags: json:"describe"
    string describe = 4;
    // 标签权重, 针对同一个key, 多个value场景, 默认值1
    // @gotags: json:"weight"
    int64 weight = 5;
    // 是否是只读标签
    // @gotags: json:"read_only"
    bool read_only = 6;
    // 标签是否隐藏, 用于控制是否在前端展示
    // @gotags: json:"hidden"
    bool hidden = 7;
}

apps/resource/pb/rpc.proto—新建

syntax = "proto3";

package go8.devcloud.cmdb.resource;
option go_package = "gitee.com/go-course/go8/projects/devcloud/cmdb/apps/resource";

import "apps/resource/pb/resource.proto";
import "github.com/infraboard/mcube/pb/page/page.proto";

service RPC {
    // 保存资源
    rpc SaveResource(Resource) returns(Resource);
    // 查询资源
    rpc QueryResource(QueryResourceRequest) returns(ResourceSet);
}

message QueryResourceRequest {
    // 分页参数
    // @gotags: json:"page"
    infraboard.mcube.page.PageRequest page = 1;
    // 资源所属域
    // @gotags: json:"domain"
    string domain = 2;
    // 资源所属空间
    // @gotags: json:"namespace"
    string namespace = 3;
    // 资源所属环境
    // @gotags: json:"env"
    string env = 4;
    // 资源类型
    // @gotags: json:"type"
    optional TYPE type = 8;
    // 服务商中的状态
    // @gotags: json:"status"
    string status = 9;
}

make gen生成代码

有时候修改了proto文件,正常make gen可以重新生成,如果不行,重启一下IDE

resource模块的全局app

apps/resource/app.go

设置resource模块对应的模块名,校验,接口以及orm的表名

const (
  AppName = "resource"
)

var (
  validate = validator.New()
)

type Service interface {
  RPCServer
}

// 默认用TableName来指定写入表的名称
func (m *Meta) TableName() string {
  return "resource_meta"
}

func (r *Resource) Validate() error {
  validateStructs := []any{r.Meta, r.Spec, r.Cost}
  for _, v := range validateStructs {
    err := validate.Struct(v)
    if err != nil {
      return err
    }
  }

  return nil
}

resource的逻辑实现部分

apps/resource/impl/impl.go

将模块实现IOC

var (
  // Service 服务实例
  svr = &service{}
)

type service struct {
  db *gorm.DB

  log logger.Logger
  resource.UnimplementedRPCServer
}

func (s *service) Config() error {
  db, err := conf.C().MySQL.ORM()

  db = db.Clauses(clause.OnConflict{
    UpdateAll: true,
  })
  if err != nil {
    return err
  }
  s.db = db

  s.log = zap.L().Named(s.Name())
  return nil
}

func (s *service) Name() string {
  return resource.AppName
}

func (s *service) Registry(server *grpc.Server) {
  resource.RegisterRPCServer(server, svr)
}

func init() {
  app.RegistryGrpcApp(svr)
  app.RegistryInternalApp(svr)
}

mysql的orm—conf/config.go

func (m *mysql) ORM() (*gorm.DB, error) {
  conn, err := m.GetDB()
  if err != nil {
    return nil, err
  }

  return gorm.Open(orm_mysql.New(orm_mysql.Config{
    Conn: conn,
  }), &gorm.Config{
    // 执行任何 SQL 时都创建并缓存预编译语句,可以提高后续的调用速度
    PrepareStmt: true,
    // 对于写操作(创建、更新、删除),为了确保数据的完整性,GORM 会将它们封装在事务内运行。
    // 但这会降低性能,如果没有这方面的要求,您可以在初始化时禁用它,这将获得大约 30%+ 性能提升
    SkipDefaultTransaction: true,
    // 要有效地插入大量记录,请将一个 slice 传递给 Create 方法
    CreateBatchSize: 200,
  })


apps/resource/impl/resource_dao.go—保存resource

func (s *service) save(ctx context.Context, res *resource.Resource) (err error) {
  // 开启事务
  tx := s.db.WithContext(ctx).Begin()
  defer func() {
    if err != nil {
      tx.Rollback()
      return
    }
    err := tx.Commit().Error
    if err != nil {
      s.log.Errorf("commit error, %s", err)
    }
  }()

  rid := &ResourceId{ResourceId: res.Meta.ResourceId}
  err = tx.Create(res.Meta).Error
  if err != nil {
    return
  }
  err = tx.Create(&Spec{Spec: res.Spec, ResourceId: rid}).Error
  if err != nil {
    return
  }
  err = tx.Create(&Status{Status: res.Status, ResourceId: rid}).Error
  if err != nil {
    return
  }
  err = tx.Create(&Cost{Cost: res.Cost, ResourceId: rid}).Error
  if err != nil {
    return
  }

  err = tx.Where("resource_id = ?", res.Meta.ResourceId).Delete(&Tag{}).Error
  if err != nil {
    return
  }
  for i := range res.Tags {
    err = tx.Create(&Tag{Tag: res.Tags[i], ResourceId: rid}).Error
    if err != nil {
      return
    }
  }

  return
}

apps/resource/impl/resource.go

func (s *service) SaveResource(ctx context.Context, res *resource.Resource) (
  *resource.Resource, error) {
  // 比如检查
  if err := res.Validate(); err != nil {
    return nil, err
  }

  // 入库
  err := s.save(ctx, res)
  if err != nil {
    return nil, err
  }
  return res, nil
}

apps/resource/impl/resource_dao.go

添加查询的方法

func (s *service) query(ctx context.Context, res *resource.QueryResourceRequest) (
  *resource.ResourceSet, error) {
  // 首先查询Meta表
  // LEFT JOIN

  return nil, nil
}
0

评论区