update cmdb
This commit is contained in:
parent
c76c8b1100
commit
0abb789a44
@ -1,2 +1,14 @@
|
||||
# 资源管理
|
||||
|
||||
## CMDB需求介绍
|
||||
|
||||
公司所有的资产(ITMS), 运维管理的资产(虚拟机), 为这些资产提供维护(日志清理, 安全检查, 监控告警, ...), Excel
|
||||
|
||||
1. 维护麻烦, 静态的, 每次变更都需要人为把信息同步给其他系统。
|
||||
2. 没法通过API提供数据给其他系统, 没法做系统集成, 系统化程度低。
|
||||
|
||||
|
||||
大的数据库,通过为这些数据提供API, 实现数据的 高速集成, 数据一定准确
|
||||
|
||||
## 常见的CMDB设计模式
|
||||
|
||||
|
1
devcloud/cmdb/apps/registry.go
Normal file
1
devcloud/cmdb/apps/registry.go
Normal file
@ -0,0 +1 @@
|
||||
package apps
|
2
devcloud/cmdb/apps/resource/README.md
Normal file
2
devcloud/cmdb/apps/resource/README.md
Normal file
@ -0,0 +1,2 @@
|
||||
# 资源管理模块
|
||||
|
1
devcloud/cmdb/apps/resource/api/api.go
Normal file
1
devcloud/cmdb/apps/resource/api/api.go
Normal file
@ -0,0 +1 @@
|
||||
package api
|
38
devcloud/cmdb/apps/resource/enum.go
Normal file
38
devcloud/cmdb/apps/resource/enum.go
Normal file
@ -0,0 +1,38 @@
|
||||
package resource
|
||||
|
||||
const (
|
||||
VENDOR_ALIYUN VENDOR = "ali"
|
||||
VENDOR_TENCENT VENDOR = "tx"
|
||||
VENDOR_HUAWEI VENDOR = "hw"
|
||||
VENDOR_VMWARE VENDOR = "vmware"
|
||||
VENDOR_AWS VENDOR = "aws"
|
||||
)
|
||||
|
||||
type VENDOR string
|
||||
|
||||
const (
|
||||
// 业务资源
|
||||
TYPE_VM TYPE = "vm"
|
||||
TYPE_RDS TYPE = "rds"
|
||||
TYPE_REDIS TYPE = "redis"
|
||||
TYPE_BUCKET TYPE = "bucket"
|
||||
TYPE_DISK TYPE = "disk"
|
||||
TYPE_LB TYPE = "lb"
|
||||
TYPE_DOMAIN TYPE = "domain"
|
||||
TYPE_EIP TYPE = "eip"
|
||||
TYPE_MONGODB TYPE = "mongodb"
|
||||
// 子资源
|
||||
TYPE_DATABASE TYPE = "database"
|
||||
TYPE_ACCOUNT TYPE = "account"
|
||||
// 未知资源
|
||||
TYPE_OTHER TYPE = "other"
|
||||
// 辅助资源
|
||||
TYPE_BILL TYPE = "bill"
|
||||
TYPE_ORDER TYPE = "order"
|
||||
)
|
||||
|
||||
type TYPE string
|
||||
|
||||
const ()
|
||||
|
||||
type STATUS string
|
1
devcloud/cmdb/apps/resource/impl/impl.go
Normal file
1
devcloud/cmdb/apps/resource/impl/impl.go
Normal file
@ -0,0 +1 @@
|
||||
package impl
|
1
devcloud/cmdb/apps/resource/impl/resource.go
Normal file
1
devcloud/cmdb/apps/resource/impl/resource.go
Normal file
@ -0,0 +1 @@
|
||||
package impl
|
60
devcloud/cmdb/apps/resource/interface.go
Normal file
60
devcloud/cmdb/apps/resource/interface.go
Normal file
@ -0,0 +1,60 @@
|
||||
package resource
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/infraboard/mcube/v2/http/request"
|
||||
"github.com/infraboard/mcube/v2/ioc"
|
||||
"github.com/infraboard/mcube/v2/types"
|
||||
)
|
||||
|
||||
const (
|
||||
APP_NAME = "resource"
|
||||
)
|
||||
|
||||
func GetService() Service {
|
||||
return ioc.Controller().Get(APP_NAME).(Service)
|
||||
}
|
||||
|
||||
type Service interface {
|
||||
// 资源搜索
|
||||
Search(context.Context, *SearchRequest) (*types.Set[*Resource], error)
|
||||
// Save 更新与创建同时
|
||||
Add(context.Context, *Resource) (*Resource, error)
|
||||
// 删除
|
||||
DeleteResource(context.Context, *DeleteResourceRequest) error
|
||||
}
|
||||
|
||||
// NewSearchRequest().SetType(”).SetXXX(v)
|
||||
// (WithOptz(), WithOptx(), WithOpty()...)
|
||||
func NewSearchRequest() *SearchRequest {
|
||||
return &SearchRequest{
|
||||
PageRequest: request.NewDefaultPageRequest(),
|
||||
Tags: map[string]string{},
|
||||
}
|
||||
}
|
||||
|
||||
type SearchRequest struct {
|
||||
// 分页请求
|
||||
*request.PageRequest
|
||||
// 名称做模糊搜索
|
||||
Keywords string `json:"keywords"`
|
||||
// 类型
|
||||
Type *TYPE `json:"type"`
|
||||
// 标签
|
||||
Tags map[string]string `json:"lable"`
|
||||
}
|
||||
|
||||
func (r *SearchRequest) SetType(t TYPE) *SearchRequest {
|
||||
r.Type = &t
|
||||
return r
|
||||
}
|
||||
|
||||
func NewDeleteResourceRequest() *DeleteResourceRequest {
|
||||
return &DeleteResourceRequest{}
|
||||
}
|
||||
|
||||
// 支持多个
|
||||
type DeleteResourceRequest struct {
|
||||
ResourceIds []string `json:"resource_ids"`
|
||||
}
|
130
devcloud/cmdb/apps/resource/model.go
Normal file
130
devcloud/cmdb/apps/resource/model.go
Normal file
@ -0,0 +1,130 @@
|
||||
package resource
|
||||
|
||||
import (
|
||||
"github.com/infraboard/mcube/v2/tools/pretty"
|
||||
"github.com/infraboard/mcube/v2/types"
|
||||
)
|
||||
|
||||
func NewResourceSet() *types.Set[*Resource] {
|
||||
return types.New[*Resource]()
|
||||
}
|
||||
|
||||
func NewResource() *Resource {
|
||||
return &Resource{
|
||||
Meta: Meta{},
|
||||
Spec: Spec{
|
||||
Tags: map[string]string{},
|
||||
Extra: map[string]string{},
|
||||
},
|
||||
Status: Status{},
|
||||
}
|
||||
}
|
||||
|
||||
// 资源
|
||||
// https://www.mongodb.com/docs/drivers/go/current/fundamentals/bson/#struct-tags
|
||||
type Resource struct {
|
||||
Meta `bson:"inline"`
|
||||
Spec `bson:"inline"`
|
||||
Status `bson:"inline"`
|
||||
}
|
||||
|
||||
func (r *Resource) String() string {
|
||||
return pretty.ToJSON(r)
|
||||
}
|
||||
|
||||
func (r *Resource) TableName() string {
|
||||
return "resources"
|
||||
}
|
||||
|
||||
// 元数据,不会变的
|
||||
type Meta struct {
|
||||
// 全局唯一Id, 直接使用个云商自己的Id
|
||||
Id string `bson:"_id" json:"id" validate:"required" gorm:"column:id;primaryKey"`
|
||||
// 资源所属域
|
||||
Domain string `json:"domain" validate:"required" gorm:"column:domain"`
|
||||
// 资源所属空间
|
||||
Namespace string `json:"namespace" validate:"required" gorm:"column:namespace"`
|
||||
// 资源所属环境
|
||||
Env string `json:"env" gorm:"column:env"`
|
||||
// 创建时间
|
||||
CreateAt int64 `json:"create_at" gorm:"column:create_at"`
|
||||
// 删除时间
|
||||
DeteteAt int64 `json:"detete_at" gorm:"column:detete_at"`
|
||||
// 删除人
|
||||
DeteteBy string `json:"detete_by" gorm:"column:detete_by"`
|
||||
// 同步时间
|
||||
SyncAt int64 `json:"sync_at" validate:"required" gorm:"column:sync_at"`
|
||||
// 同步人
|
||||
SyncBy string `json:"sync_by" gorm:"column:sync_by"`
|
||||
// 用于同步的凭证ID
|
||||
CredentialId string `json:"credential_id" gorm:"column:credential_id"`
|
||||
// 序列号
|
||||
SerialNumber string `json:"serial_number" gorm:"column:serial_number"`
|
||||
}
|
||||
|
||||
// 表单数据, 用户申请资源时 需要提供的参数
|
||||
type Spec struct {
|
||||
// 厂商
|
||||
Vendor VENDOR `json:"vendor" gorm:"column:vendor"`
|
||||
// 资源类型
|
||||
ResourceType TYPE `json:"resource_type" gorm:"column:resource_type"`
|
||||
// 地域
|
||||
Region string `json:"region" gorm:"column:region"`
|
||||
// 区域
|
||||
Zone string `json:"zone" gorm:"column:zone"`
|
||||
// 资源所属账号
|
||||
Owner string `json:"owner" gorm:"column:owner"`
|
||||
// 名称
|
||||
Name string `json:"name" gorm:"column:name"`
|
||||
// 种类
|
||||
Category string `json:"category" gorm:"column:category"`
|
||||
// 规格
|
||||
Type string `json:"type" gorm:"column:type"`
|
||||
// 描述
|
||||
Description string `json:"description" gorm:"column:description"`
|
||||
// 过期时间
|
||||
ExpireAt int64 `json:"expire_at" gorm:"column:expire_at"`
|
||||
// 更新时间
|
||||
UpdateAt int64 `json:"update_at" gorm:"column:update_at"`
|
||||
// 资源占用Cpu数量
|
||||
Cpu int64 `json:"cpu" gorm:"column:cpu"`
|
||||
// GPU数量
|
||||
Gpu int64 `json:"gpu" gorm:"column:gpu"`
|
||||
// 资源使用的内存
|
||||
Memory int64 `json:"memory" gorm:"column:memory"`
|
||||
// 系统盘
|
||||
SystemStorage int64 `json:"system_storage" gorm:"column:system_storage"`
|
||||
// 数据盘
|
||||
DataStorage int64 `json:"data_storage" gorm:"column:data_storage"`
|
||||
// 公网IP带宽, 单位M
|
||||
BandWidth int32 `json:"band_width" gorm:"column:band_width"`
|
||||
// 资源标签
|
||||
Tags map[string]string `json:"tags" gorm:"column:tags;serializer:json;type:json"`
|
||||
// 额外的通用属性
|
||||
Extra map[string]string `json:"extra" gorm:"column:extra;serializer:json;type:json"`
|
||||
}
|
||||
|
||||
// 资源当前状态
|
||||
type Status struct {
|
||||
// 资源当前状态
|
||||
Phase string `json:"phase" gorm:"column:phase"`
|
||||
// 资源当前状态描述
|
||||
Describe string `json:"describe" gorm:"column:describe"`
|
||||
// 实例锁定模式; Unlock:正常;ManualLock:手动触发锁定;LockByExpiration:实例过期自动锁定;LockByRestoration:实例回滚前的自动锁定;LockByDiskQuota:实例空间满自动锁定
|
||||
LockMode string `json:"lock_mode" gorm:"column:lock_mode"`
|
||||
// 锁定原因
|
||||
LockReason string `json:"lock_reason" gorm:"column:lock_reason"`
|
||||
// 资源访问地址
|
||||
// 公网地址, 或者域名
|
||||
PublicAddress []string `json:"public_address" gorm:"column:public_address;serializer:json;type:json"`
|
||||
// 内网地址, 或者域名
|
||||
PrivateAddress []string `json:"private_address" gorm:"column:private_address;serializer:json;type:json"`
|
||||
}
|
||||
|
||||
func (s *Status) GetFirstPrivateAddress() string {
|
||||
if len(s.PrivateAddress) > 0 {
|
||||
return s.PrivateAddress[0]
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
2
devcloud/cmdb/apps/secret/README.md
Normal file
2
devcloud/cmdb/apps/secret/README.md
Normal file
@ -0,0 +1,2 @@
|
||||
# 资源凭证管理
|
||||
|
1
devcloud/cmdb/apps/secret/api/api.go
Normal file
1
devcloud/cmdb/apps/secret/api/api.go
Normal file
@ -0,0 +1 @@
|
||||
package api
|
1
devcloud/cmdb/apps/secret/impl/impl.go
Normal file
1
devcloud/cmdb/apps/secret/impl/impl.go
Normal file
@ -0,0 +1 @@
|
||||
package impl
|
1
devcloud/cmdb/apps/secret/impl/secret.go
Normal file
1
devcloud/cmdb/apps/secret/impl/secret.go
Normal file
@ -0,0 +1 @@
|
||||
package impl
|
95
devcloud/cmdb/apps/secret/interface.go
Normal file
95
devcloud/cmdb/apps/secret/interface.go
Normal file
@ -0,0 +1,95 @@
|
||||
package secret
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"122.51.31.227/go-course/go18/devcloud/cmdb/apps/resource"
|
||||
"github.com/infraboard/mcube/v2/http/request"
|
||||
"github.com/infraboard/mcube/v2/ioc"
|
||||
"github.com/infraboard/mcube/v2/tools/pretty"
|
||||
"github.com/infraboard/mcube/v2/types"
|
||||
)
|
||||
|
||||
const (
|
||||
APP_NAME = "secret"
|
||||
SECRET_KEY = "23gs6gxHrz1kNEvshRmunkXbwIiaEcYfh+EMu+e9ewA="
|
||||
)
|
||||
|
||||
func GetService() Service {
|
||||
return ioc.Controller().Get(APP_NAME).(Service)
|
||||
}
|
||||
|
||||
type Service interface {
|
||||
// 用于Secret的管理(后台管理员配置)
|
||||
// 创建secret
|
||||
CreateSecret(context.Context, *CreateSecretRequest) (*Secret, error)
|
||||
// 查询secret
|
||||
QuerySecret(context.Context, *QuerySecretRequest) (*types.Set[*Secret], error)
|
||||
// 查询详情, 已解密, API层需要脱敏
|
||||
DescribeSecret(context.Context, *DescribeSecretRequeset) (*Secret, error)
|
||||
|
||||
// 基于云商凭证来同步资源
|
||||
// 怎么API怎么设计
|
||||
// 同步阿里云所有资源, 10分钟,30分钟 ...
|
||||
// 这个接口调用持续30分钟...
|
||||
// 需要拆解为异步任务: 用户调用了同步后, 里面返回, 这个同步任务在后台执行(Gorouties), 需要查询同步日志(Ws)
|
||||
|
||||
// 执行同步
|
||||
SyncResource(context.Context, *SyncResourceRequest) (*SyncResourceTask, error)
|
||||
// 查询同步日志
|
||||
QuerySyncLog(context.Context, *QuerySyncLogRequest) (*types.Set[*SyncRecord], error)
|
||||
}
|
||||
|
||||
// 同步记录(task) 有状态
|
||||
type SyncResourceTask struct {
|
||||
}
|
||||
|
||||
type QuerySyncLogRequest struct {
|
||||
TaskId int `json:"task_id"`
|
||||
}
|
||||
|
||||
// 每个资源的同步日志
|
||||
type SyncRecord struct {
|
||||
}
|
||||
|
||||
type ResourceResponse struct {
|
||||
Success bool
|
||||
InstanceId string `json:"instance_id"`
|
||||
Resource *resource.Resource `json:"resource"`
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
func (t ResourceResponse) String() string {
|
||||
return pretty.ToJSON(t)
|
||||
}
|
||||
|
||||
func NewQuerySecretRequest() *QuerySecretRequest {
|
||||
return &QuerySecretRequest{
|
||||
PageRequest: request.NewDefaultPageRequest(),
|
||||
}
|
||||
}
|
||||
|
||||
type QuerySecretRequest struct {
|
||||
// 分页请求
|
||||
*request.PageRequest
|
||||
}
|
||||
|
||||
func NewDescribeSecretRequeset(id string) *DescribeSecretRequeset {
|
||||
return &DescribeSecretRequeset{
|
||||
Id: id,
|
||||
}
|
||||
}
|
||||
|
||||
type DescribeSecretRequeset struct {
|
||||
Id string `json:"id"`
|
||||
}
|
||||
|
||||
func NewSyncResourceRequest(id string) *SyncResourceRequest {
|
||||
return &SyncResourceRequest{
|
||||
Id: id,
|
||||
}
|
||||
}
|
||||
|
||||
type SyncResourceRequest struct {
|
||||
Id string `json:"id"`
|
||||
}
|
133
devcloud/cmdb/apps/secret/model.go
Normal file
133
devcloud/cmdb/apps/secret/model.go
Normal file
@ -0,0 +1,133 @@
|
||||
package secret
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
|
||||
"122.51.31.227/go-course/go18/devcloud/cmdb/apps/resource"
|
||||
"github.com/google/uuid"
|
||||
"github.com/infraboard/devops/pkg/model"
|
||||
"github.com/infraboard/mcube/v2/crypto/cbc"
|
||||
"github.com/infraboard/mcube/v2/tools/pretty"
|
||||
"github.com/infraboard/mcube/v2/types"
|
||||
)
|
||||
|
||||
func NewSecretSet() *types.Set[*Secret] {
|
||||
return types.New[*Secret]()
|
||||
}
|
||||
|
||||
func NewSecret(in *CreateSecretRequest) *Secret {
|
||||
// hash版本的UUID
|
||||
// Vendor Address ApiKey
|
||||
uid := uuid.NewMD5(uuid.Nil, fmt.Appendf(nil, "%s.%s.%s", in.Vendor, in.Address, in.ApiKey)).String()
|
||||
return &Secret{
|
||||
Meta: *model.NewMeta().SetId(uid),
|
||||
CreateSecretRequest: *in,
|
||||
}
|
||||
}
|
||||
|
||||
type Secret struct {
|
||||
model.Meta
|
||||
CreateSecretRequest `bson:"inline"`
|
||||
}
|
||||
|
||||
func (s *Secret) TableName() string {
|
||||
return "secrets"
|
||||
}
|
||||
|
||||
func (s *Secret) SetDefault() *Secret {
|
||||
if s.SyncLimit == 0 {
|
||||
s.SyncLimit = 10
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *Secret) String() string {
|
||||
return pretty.ToJSON(s)
|
||||
}
|
||||
|
||||
func NewCreateSecretRequest() *CreateSecretRequest {
|
||||
return &CreateSecretRequest{
|
||||
Regions: []string{},
|
||||
SyncLimit: 10,
|
||||
}
|
||||
}
|
||||
|
||||
type CreateSecretRequest struct {
|
||||
// 名称
|
||||
Name string `json:"name" gorm:"column:name"`
|
||||
// 尝试
|
||||
Vendor resource.VENDOR `json:"vendor" gorm:"column:vendor"`
|
||||
// Vmware
|
||||
Address string `json:"address" gorm:"column:address"`
|
||||
// 需要被脱敏
|
||||
// Musk
|
||||
ApiKey string `json:"api_key" gorm:"column:api_key"`
|
||||
// ApiKey
|
||||
ApiSecret string `json:"api_secret" mask:",5,4" gorm:"column:api_secret"`
|
||||
// 资源所在区域
|
||||
Regions []string `json:"regions" gorm:"column:regions;serializer:json;type:json"`
|
||||
// 资源列表
|
||||
ResourceType []resource.TYPE `json:"resource_type" gorm:"column:resource_type;serializer:json;type:json"`
|
||||
// 同步分页大小
|
||||
SyncLimit int64 `json:"sync_limit" gorm:"column:sync_limit"`
|
||||
|
||||
//
|
||||
isEncrypted bool `gorm:"-"`
|
||||
}
|
||||
|
||||
func (r *CreateSecretRequest) SetIsEncrypted(v bool) {
|
||||
r.isEncrypted = v
|
||||
}
|
||||
|
||||
func (r *CreateSecretRequest) GetSyncLimit() int64 {
|
||||
if r.SyncLimit == 0 {
|
||||
return 10
|
||||
}
|
||||
return r.SyncLimit
|
||||
}
|
||||
|
||||
func (r *CreateSecretRequest) EncryptedApiSecret() error {
|
||||
if r.isEncrypted {
|
||||
return nil
|
||||
}
|
||||
// Hash, 对称,非对称
|
||||
// 对称加密 AES(cbc)
|
||||
// @v1,xxxx@xxxxx
|
||||
|
||||
key, err := base64.StdEncoding.DecodeString(SECRET_KEY)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cipherText, err := cbc.MustNewAESCBCCihper(key).Encrypt([]byte(r.ApiSecret))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
r.ApiSecret = base64.StdEncoding.EncodeToString(cipherText)
|
||||
r.SetIsEncrypted(true)
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
func (r *CreateSecretRequest) DecryptedApiSecret() error {
|
||||
if r.isEncrypted {
|
||||
cipherdText, err := base64.StdEncoding.DecodeString(r.ApiSecret)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
key, err := base64.StdEncoding.DecodeString(SECRET_KEY)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
plainText, err := cbc.MustNewAESCBCCihper(key).Decrypt([]byte(cipherdText))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
r.ApiSecret = string(plainText)
|
||||
r.SetIsEncrypted(false)
|
||||
}
|
||||
return nil
|
||||
}
|
68
devcloud/cmdb/design.drawio
Normal file
68
devcloud/cmdb/design.drawio
Normal file
@ -0,0 +1,68 @@
|
||||
<mxfile host="65bd71144e">
|
||||
<diagram id="_dFAOaWQpwMIgqf1TTqB" name="第 1 页">
|
||||
<mxGraphModel dx="888" dy="495" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="827" pageHeight="1169" math="0" shadow="0">
|
||||
<root>
|
||||
<mxCell id="0"/>
|
||||
<mxCell id="1" parent="0"/>
|
||||
<mxCell id="3" value="<h1 style="margin-top: 0px;">基于模型</h1><p>这里的模型,数据库里面的一种表</p><p>基于关系性数据库,设计 表概念</p><p>1. 传统: 数据库里面添加表</p><p>2.&nbsp; 通过cmdb提供的API来添加模型</p><p><br></p><p><br></p>" style="text;html=1;whiteSpace=wrap;overflow=hidden;rounded=0;" vertex="1" parent="1">
|
||||
<mxGeometry x="30" y="70" width="200" height="150" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="6" style="edgeStyle=none;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;" edge="1" parent="1" source="4" target="5">
|
||||
<mxGeometry relative="1" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="7" value="1: N" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" vertex="1" connectable="0" parent="6">
|
||||
<mxGeometry x="0.0067" y="-2" relative="1" as="geometry">
|
||||
<mxPoint as="offset"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="4" value="model表<div><br></div><div>模型的名称(ec2)</div>" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
|
||||
<mxGeometry x="50" y="250" width="310" height="150" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="10" style="edgeStyle=none;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;" edge="1" parent="1" source="5" target="8">
|
||||
<mxGeometry relative="1" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="11" value="1: N" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" vertex="1" connectable="0" parent="10">
|
||||
<mxGeometry x="-0.2893" y="-1" relative="1" as="geometry">
|
||||
<mxPoint as="offset"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="5" value="model 字段表<div><br></div><div>模型的字段</div><div>(name: 名称, ip: 主机的IP)</div>" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
|
||||
<mxGeometry x="460" y="250" width="310" height="150" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="8" value="实例表:<div>核心 记录 模型字段对应的数据</div><div>name字段编号(1): ec2_01</div><div>ip的字段编号(2): 10.10.10.1&nbsp;</div>" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
|
||||
<mxGeometry x="460" y="510" width="310" height="150" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="9" value="<div><br></div>ec2实例[] {<div><font color="#000000">name: ec2_01</font></div><div><font color="#000000">ip: 10.10.10.1</font></div><div><font color="#000000"><br></font><div>}</div></div>" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" vertex="1" parent="1">
|
||||
<mxGeometry x="60" y="510" width="170" height="90" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="12" value="<h1 style="margin-top: 0px;">基于表的CMDB</h1><p><br></p><p>沿用关系性数据库的设计逻辑, 已有的数据库工具</p><p>1. 提供一组API来让用户录入数控,并且通过API查询数据库</p><p><br></p><p><br></p>" style="text;html=1;whiteSpace=wrap;overflow=hidden;rounded=0;" vertex="1" parent="1">
|
||||
<mxGeometry x="70" y="690" width="410" height="130" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="13" value="资源表<div><br></div><div>通用字段,&nbsp; 通过直接添加, 非通用字段, 直接存储为json</div>" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
|
||||
<mxGeometry x="60" y="870" width="310" height="150" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="14" value="Resource: CRUD的API" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" vertex="1" parent="1">
|
||||
<mxGeometry x="60" y="840" width="110" height="30" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="15" value="<div>凭证管理</div><div><br></div><div>存储敏感数据的表(对称加解密)</div>" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
|
||||
<mxGeometry x="60" y="1110" width="310" height="150" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="16" value="Secret: CRUD API" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" vertex="1" parent="1">
|
||||
<mxGeometry x="60" y="1080" width="110" height="30" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="17" value="<h1 style="margin-top: 0px;">敏感数据</h1><p><br></p><p><span style="background-color: transparent;">1.&nbsp; 存储加密, 访问运维看到数据库</span></p><p><span style="background-color: transparent;">1.&nbsp; API 提供的数据 需要脱名,这种数据是不允许通过API对外开放的, 只允许进程内使用</span></p><p><br></p><p><br></p>" style="text;html=1;whiteSpace=wrap;overflow=hidden;rounded=0;" vertex="1" parent="1">
|
||||
<mxGeometry x="400" y="1100" width="410" height="160" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="18" value="sync" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
|
||||
<mxGeometry x="240" y="1220" width="120" height="30" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="19" value="secret" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
|
||||
<mxGeometry x="80" y="1220" width="120" height="30" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="20" style="edgeStyle=none;html=1;exitX=0.75;exitY=0;exitDx=0;exitDy=0;entryX=0.869;entryY=0.984;entryDx=0;entryDy=0;entryPerimeter=0;" edge="1" parent="1" source="18" target="13">
|
||||
<mxGeometry relative="1" as="geometry"/>
|
||||
</mxCell>
|
||||
</root>
|
||||
</mxGraphModel>
|
||||
</diagram>
|
||||
</mxfile>
|
@ -49,6 +49,11 @@ func (i *PolicyServiceImpl) QueryEndpoint(ctx context.Context, in *policy.QueryE
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 没有权限
|
||||
if policies.Len() == 0 {
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
if policies.Len() > 1 {
|
||||
return nil, fmt.Errorf("同一个空间下, 一个用户有多条[%d]授权", policies.Len())
|
||||
}
|
||||
|
@ -11,16 +11,18 @@ import (
|
||||
|
||||
func (h *UserRestfulApiHandler) QueryApplication(r *restful.Request, w *restful.Response) {
|
||||
req := application.NewQueryApplicationRequest()
|
||||
if err := binding.Query.Bind(r.Request, &req.QueryApplicationRequestSpec); err != nil {
|
||||
response.Failed(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
// 过滤条件在认证完成后的上下文中
|
||||
tk := token.GetTokenFromCtx(r.Request.Context())
|
||||
req.ResourceScope = tk.ResourceScope
|
||||
log.L().Debug().Msgf("resource scope: %s", tk.ResourceScope)
|
||||
|
||||
// 用户的参数
|
||||
if err := binding.Query.Bind(r.Request, &req.QueryApplicationRequestSpec); err != nil {
|
||||
response.Failed(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
set, err := h.svc.QueryApplication(r.Request.Context(), req)
|
||||
if err != nil {
|
||||
response.Failed(w, err)
|
||||
|
@ -12,10 +12,10 @@ func TestCreateApplication(t *testing.T) {
|
||||
req.Description = "应用研发云"
|
||||
req.Type = application.TYPE_SOURCE_CODE
|
||||
req.CodeRepository = application.CodeRepository{
|
||||
SshUrl: "git@122.51.31.227:go-course/go18.git",
|
||||
SshUrl: "git@122.51.31.227:go-course/go17.git",
|
||||
}
|
||||
req.SetNamespaceId(1)
|
||||
req.SetLabel("team", "dev01.web_developer")
|
||||
req.SetLabel("team", "dev01.backend_developer")
|
||||
ins, err := svc.CreateApplication(ctx, req)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@ -27,7 +27,7 @@ func TestCreateApplication(t *testing.T) {
|
||||
func TestQueryApplication(t *testing.T) {
|
||||
req := application.NewQueryApplicationRequest()
|
||||
// dev01.%
|
||||
// req.SetNamespaceId(1)
|
||||
req.SetNamespaceId(1)
|
||||
req.SetScope("team", []string{"dev01%"})
|
||||
// req.SetScope("env", []string{"prod"})
|
||||
ins, err := svc.QueryApplication(ctx, req)
|
||||
|
3
go.mod
3
go.mod
@ -8,6 +8,7 @@ require (
|
||||
github.com/emicklei/go-restful/v3 v3.12.2
|
||||
github.com/gin-gonic/gin v1.10.0
|
||||
github.com/google/uuid v1.6.0
|
||||
github.com/infraboard/devops v0.0.6
|
||||
github.com/infraboard/mcube/v2 v2.0.61
|
||||
github.com/infraboard/modules v0.0.12
|
||||
github.com/rs/xid v1.6.0
|
||||
@ -31,7 +32,7 @@ require (
|
||||
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||
github.com/cloudwego/base64x v0.1.5 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||
github.com/gabriel-vasile/mimetype v1.4.8 // indirect
|
||||
github.com/gin-contrib/sse v1.0.0 // indirect
|
||||
|
5
go.sum
5
go.sum
@ -22,8 +22,9 @@ github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSV
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
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/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
|
||||
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
||||
github.com/emicklei/go-restful-openapi/v2 v2.11.0 h1:Ur+yGxoOH/7KRmcj/UoMFqC3VeNc9VOe+/XidumxTvk=
|
||||
@ -94,6 +95,8 @@ github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.1 h1:e9Rjr40Z98/clHv5Yg79Is0Ntos
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.1/go.mod h1:tIxuGz/9mpox++sgp9fJjHO0+q1X9/UOWd798aAm22M=
|
||||
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
||||
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||
github.com/infraboard/devops v0.0.6 h1:oo7RfRBxu9hbI/+bYzXuX40BfG1hRyVtxC0fBhoFGBU=
|
||||
github.com/infraboard/devops v0.0.6/go.mod h1:Ac+W3wFy5pG9EH7f1W8DxiAZRhnTDFHrp27WKkYnNoU=
|
||||
github.com/infraboard/mcube/v2 v2.0.61 h1:al8Z+poXXOjfTIAY48ujFzV1uYzH/N7/xmve/ZXArbo=
|
||||
github.com/infraboard/mcube/v2 v2.0.61/go.mod h1:TbYs8cnD8Cg19sTdU0D+vqWAN+LzoxhMYWmAC2pfJkQ=
|
||||
github.com/infraboard/modules v0.0.12 h1:vQqm+JwzmhL+hcD9SV+WVlp9ecInc7NsbGahcTmJ0Wk=
|
||||
|
Loading…
x
Reference in New Issue
Block a user