diff --git a/devcloud/cmdb/apps/registry.go b/devcloud/cmdb/apps/registry.go index cff2ab9..57e3fff 100644 --- a/devcloud/cmdb/apps/registry.go +++ b/devcloud/cmdb/apps/registry.go @@ -1 +1,7 @@ package apps + +import ( + _ "122.51.31.227/go-course/go18/devcloud/cmdb/apps/resource/impl" + _ "122.51.31.227/go-course/go18/devcloud/cmdb/apps/secret/impl" + _ "122.51.31.227/go-course/go18/devcloud/cmdb/apps/secret/sync" +) diff --git a/devcloud/cmdb/apps/resource/enum.go b/devcloud/cmdb/apps/resource/enum.go index 1dda30c..40b32c1 100644 --- a/devcloud/cmdb/apps/resource/enum.go +++ b/devcloud/cmdb/apps/resource/enum.go @@ -32,7 +32,3 @@ const ( ) type TYPE string - -const () - -type STATUS string diff --git a/devcloud/cmdb/apps/secret/const.go b/devcloud/cmdb/apps/secret/const.go new file mode 100644 index 0000000..70de0f1 --- /dev/null +++ b/devcloud/cmdb/apps/secret/const.go @@ -0,0 +1,5 @@ +package secret + +const ( + TASK_LABLE_SECRET_ID = "secret_id" +) diff --git a/devcloud/cmdb/apps/secret/impl/impl.go b/devcloud/cmdb/apps/secret/impl/impl.go index 4f9d22e..9dc6bbf 100644 --- a/devcloud/cmdb/apps/secret/impl/impl.go +++ b/devcloud/cmdb/apps/secret/impl/impl.go @@ -1 +1,31 @@ package impl + +import ( + "122.51.31.227/go-course/go18/devcloud/cmdb/apps/secret" + "github.com/infraboard/mcube/v2/ioc" + "github.com/infraboard/mcube/v2/ioc/config/datasource" +) + +func init() { + ioc.Controller().Registry(&SecretServiceImpl{}) +} + +var _ secret.Service = (*SecretServiceImpl)(nil) + +type SecretServiceImpl struct { + ioc.ObjectImpl +} + +func (s *SecretServiceImpl) Name() string { + return secret.APP_NAME +} + +func (s *SecretServiceImpl) Init() error { + if datasource.Get().AutoMigrate { + err := datasource.DB().AutoMigrate(&secret.Secret{}) + if err != nil { + return err + } + } + return nil +} diff --git a/devcloud/cmdb/apps/secret/impl/impl_test.go b/devcloud/cmdb/apps/secret/impl/impl_test.go new file mode 100644 index 0000000..1d63b8b --- /dev/null +++ b/devcloud/cmdb/apps/secret/impl/impl_test.go @@ -0,0 +1,15 @@ +package impl_test + +import ( + "122.51.31.227/go-course/go18/devcloud/cmdb/apps/secret" + "122.51.31.227/go-course/go18/devcloud/cmdb/test" +) + +var ( + svc secret.Service +) + +func init() { + test.DevelopmentSetUp() + svc = secret.GetService() +} diff --git a/devcloud/cmdb/apps/secret/impl/secret.go b/devcloud/cmdb/apps/secret/impl/secret.go index 4f9d22e..d76d9e7 100644 --- a/devcloud/cmdb/apps/secret/impl/secret.go +++ b/devcloud/cmdb/apps/secret/impl/secret.go @@ -1 +1,98 @@ package impl + +import ( + "context" + "fmt" + + "122.51.31.227/go-course/go18/devcloud/cmdb/apps/secret" + "github.com/infraboard/mcube/v2/exception" + "github.com/infraboard/mcube/v2/ioc/config/datasource" + "github.com/infraboard/mcube/v2/types" + "github.com/infraboard/modules/task/apps/task" + "gorm.io/gorm" +) + +// CreateSecret implements secret.Service. +func (s *SecretServiceImpl) CreateSecret(ctx context.Context, in *secret.CreateSecretRequest) (*secret.Secret, error) { + ins := secret.NewSecret(in) + + // 需要加密 + if err := ins.EncryptedApiSecret(); err != nil { + return nil, err + } + + // upsert, gorm save + if err := datasource.DBFromCtx(ctx).Save(ins).Error; err != nil { + return nil, err + } + + return ins, nil +} + +// DescribeSecret implements secret.Service. +func (s *SecretServiceImpl) DescribeSecret(ctx context.Context, in *secret.DescribeSecretRequeset) (*secret.Secret, error) { + // 取出后,需要解密 + ins := &secret.Secret{} + query := in.GormResourceFilter(datasource.DBFromCtx(ctx).Model(&secret.Secret{})) + if err := query.Where("id = ?", in.Id).Take(ins).Error; err != nil { + if err == gorm.ErrRecordNotFound { + return nil, exception.NewNotFound("secret %s not found", in.Id) + } + return nil, err + } + ins.SetIsEncrypted(true) + if err := ins.DecryptedApiSecret(); err != nil { + return nil, err + } + + // 解密过后的数据 + return ins, nil +} + +// QuerySecret implements secret.Service. +func (s *SecretServiceImpl) QuerySecret(ctx context.Context, in *secret.QuerySecretRequest) (*types.Set[*secret.Secret], error) { + set := types.New[*secret.Secret]() + + query := in.GormResourceFilter(datasource.DBFromCtx(ctx).Model(&secret.Secret{})) + err := query.Count(&set.Total).Error + if err != nil { + return nil, err + } + + err = query. + Order("create_at desc"). + Offset(int(in.ComputeOffset())). + Limit(int(in.PageSize)). + Find(&set.Items). + Error + if err != nil { + return nil, err + } + return set, nil + +} + +// SyncResource implements secret.Service. +// 资源同步接口 +func (s *SecretServiceImpl) SyncResource(ctx context.Context, in *secret.SyncResourceRequest) (*types.Set[*task.Task], error) { + // 直接初始化并返回,避免警告 + taskSet := types.NewSet[*task.Task]() + + // 查询Secret信息 + ins, err := s.DescribeSecret(ctx, secret.NewDescribeSecretRequeset(in.Id)) + if err != nil { + return taskSet, err + } + + if !ins.GetEnabled() { + return taskSet, fmt.Errorf("secret %s not enabled", ins.Name) + } + + // 获取syncer, 执行同步 + for _, rs := range ins.ResourceType { + syncer := secret.GetSyncer(rs) + taskInfo := syncer.Sync(ctx, ins, rs) + taskSet.Add(taskInfo) + } + return taskSet, nil +} diff --git a/devcloud/cmdb/apps/secret/impl/secret_test.go b/devcloud/cmdb/apps/secret/impl/secret_test.go new file mode 100644 index 0000000..e231190 --- /dev/null +++ b/devcloud/cmdb/apps/secret/impl/secret_test.go @@ -0,0 +1,58 @@ +package impl_test + +import ( + "testing" + "time" + + "122.51.31.227/go-course/go18/devcloud/cmdb/apps/resource" + "122.51.31.227/go-course/go18/devcloud/cmdb/apps/secret" +) + +func TestCreateSecret(t *testing.T) { + req := secret.NewCreateSecretRequest() + req.Name = "腾讯云只读账号" + req.Vendor = resource.VENDOR_TENCENT + req.ApiKey = "xxx" + req.ApiSecret = "xx" + req.SetEnabled(true) + req.ResourceType = append(req.ResourceType, resource.TYPE_VM) + req.Regions = []string{"ap-shanghai", "ap-guangzhou"} + ins, err := svc.CreateSecret(t.Context(), req) + if err != nil { + t.Fatal(err) + } + t.Log(ins) +} + +func TestQuerySecret(t *testing.T) { + req := secret.NewQuerySecretRequest() + set, err := svc.QuerySecret(t.Context(), req) + if err != nil { + t.Fatal(err) + } + t.Log(set) +} + +const ( + SECRET_ID = "ed02f7cd-ee5b-33f0-bdf5-e305ecb3efb4" +) + +func TestDescribeSecret(t *testing.T) { + req := secret.NewDescribeSecretRequeset(SECRET_ID) + ins, err := svc.DescribeSecret(t.Context(), req) + if err != nil { + t.Fatal(err) + } + t.Log(ins) +} + +func TestSyncResource(t *testing.T) { + req := secret.NewSyncResourceRequest(SECRET_ID) + ins, err := svc.SyncResource(t.Context(), req) + if err != nil { + t.Fatal(err) + } + t.Log(ins) + + time.Sleep(3 * time.Second) +} diff --git a/devcloud/cmdb/apps/secret/interface.go b/devcloud/cmdb/apps/secret/interface.go index 44961a4..a972961 100644 --- a/devcloud/cmdb/apps/secret/interface.go +++ b/devcloud/cmdb/apps/secret/interface.go @@ -4,10 +4,12 @@ import ( "context" "122.51.31.227/go-course/go18/devcloud/cmdb/apps/resource" + "122.51.31.227/go-course/go18/devcloud/mcenter/apps/policy" "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" + "github.com/infraboard/modules/task/apps/task" ) const ( @@ -34,10 +36,8 @@ type Service interface { // 这个接口调用持续30分钟... // 需要拆解为异步任务: 用户调用了同步后, 里面返回, 这个同步任务在后台执行(Gorouties), 需要查询同步日志(Ws) - // 执行同步 - SyncResource(context.Context, *SyncResourceRequest) (*SyncResourceTask, error) - // 查询同步日志 - QuerySyncLog(context.Context, *QuerySyncLogRequest) (*types.Set[*SyncRecord], error) + // 使用task模块来执行异步任务, 通过TaskId查询异步任务状态 + SyncResource(context.Context, *SyncResourceRequest) (*types.Set[*task.Task], error) } // 同步记录(task) 有状态 @@ -70,6 +70,7 @@ func NewQuerySecretRequest() *QuerySecretRequest { } type QuerySecretRequest struct { + policy.ResourceScope // 分页请求 *request.PageRequest } @@ -81,6 +82,7 @@ func NewDescribeSecretRequeset(id string) *DescribeSecretRequeset { } type DescribeSecretRequeset struct { + policy.ResourceScope Id string `json:"id"` } diff --git a/devcloud/cmdb/apps/secret/model.go b/devcloud/cmdb/apps/secret/model.go index 87e32a8..d6f80e1 100644 --- a/devcloud/cmdb/apps/secret/model.go +++ b/devcloud/cmdb/apps/secret/model.go @@ -5,6 +5,7 @@ import ( "fmt" "122.51.31.227/go-course/go18/devcloud/cmdb/apps/resource" + "122.51.31.227/go-course/go18/devcloud/mcenter/apps/policy" "github.com/google/uuid" "github.com/infraboard/devops/pkg/model" "github.com/infraboard/mcube/v2/crypto/cbc" @@ -28,7 +29,10 @@ func NewSecret(in *CreateSecretRequest) *Secret { type Secret struct { model.Meta - CreateSecretRequest `bson:"inline"` + // 资源范围, Namespace是继承的, Scope是API添加的 + policy.ResourceLabel + // 资源定义 + CreateSecretRequest } func (s *Secret) TableName() string { @@ -54,6 +58,8 @@ func NewCreateSecretRequest() *CreateSecretRequest { } type CreateSecretRequest struct { + // 是否启用 + Enabled *bool `json:"enabled" gorm:"column:enabled"` // 名称 Name string `json:"name" gorm:"column:name"` // 尝试 @@ -76,8 +82,21 @@ type CreateSecretRequest struct { isEncrypted bool `gorm:"-"` } -func (r *CreateSecretRequest) SetIsEncrypted(v bool) { +func (r *CreateSecretRequest) SetIsEncrypted(v bool) *CreateSecretRequest { r.isEncrypted = v + return r +} + +func (r *CreateSecretRequest) SetEnabled(v bool) *CreateSecretRequest { + r.Enabled = &v + return r +} + +func (r *CreateSecretRequest) GetEnabled() bool { + if r.Enabled == nil { + return false + } + return *r.Enabled } func (r *CreateSecretRequest) GetSyncLimit() int64 { diff --git a/devcloud/cmdb/apps/secret/sync.go b/devcloud/cmdb/apps/secret/sync.go new file mode 100644 index 0000000..08c23ed --- /dev/null +++ b/devcloud/cmdb/apps/secret/sync.go @@ -0,0 +1,34 @@ +package secret + +import ( + "context" + "fmt" + + "122.51.31.227/go-course/go18/devcloud/cmdb/apps/resource" + "github.com/infraboard/modules/task/apps/task" +) + +const ( + SYNCER_PREFIX = "syncer" +) + +var ( + syncers = map[string]Syncer{} +) + +func GetSyncerName(t resource.TYPE) string { + return fmt.Sprintf("%s_%s", SYNCER_PREFIX, t) +} + +func GetSyncer(t resource.TYPE) Syncer { + return syncers[fmt.Sprintf("%s_%s", SYNCER_PREFIX, t)] +} + +func RegistrySyncer(t resource.TYPE, s Syncer) { + syncers[fmt.Sprintf("%s_%s", SYNCER_PREFIX, t)] = s +} + +type Syncer interface { + // 资源同步 + Sync(context.Context, *Secret, resource.TYPE) *task.Task +} diff --git a/devcloud/cmdb/apps/secret/sync/vm.go b/devcloud/cmdb/apps/secret/sync/vm.go new file mode 100644 index 0000000..180e155 --- /dev/null +++ b/devcloud/cmdb/apps/secret/sync/vm.go @@ -0,0 +1,47 @@ +package sync + +import ( + "context" + "time" + + "122.51.31.227/go-course/go18/devcloud/cmdb/apps/resource" + "122.51.31.227/go-course/go18/devcloud/cmdb/apps/secret" + "github.com/infraboard/mcube/v2/ioc" + "github.com/infraboard/mcube/v2/ioc/config/log" + "github.com/infraboard/modules/task/apps/task" + "github.com/rs/zerolog" +) + +func init() { + ioc.Controller().Registry(&VmSyncerServiceImpl{}) +} + +var _ secret.Syncer = (*VmSyncerServiceImpl)(nil) + +type VmSyncerServiceImpl struct { + ioc.ObjectImpl + + log *zerolog.Logger +} + +func (s *VmSyncerServiceImpl) Name() string { + return secret.GetSyncerName(resource.TYPE_VM) +} + +func (s *VmSyncerServiceImpl) Init() error { + s.log = log.Sub(s.Name()) + secret.RegistrySyncer(resource.TYPE_VM, s) + return nil +} + +// Sync implements secret.Syncer. +func (s *VmSyncerServiceImpl) Sync(ctx context.Context, secretIns *secret.Secret, rs resource.TYPE) *task.Task { + return task.GetService().Run(ctx, task.NewFnTask(func(ctx context.Context, req any) error { + + // taskInfo := task.GetTaskFromCtx(ctx) + s.log.Debug().Msg("test for vm sync wait") + time.Sleep(1 * time.Second) + // secrt同步 + return nil + }, secretIns).SetAsync(true).SetLabel(secret.TASK_LABLE_SECRET_ID, secretIns.Id)) +} diff --git a/devcloud/cmdb/test/setup.go b/devcloud/cmdb/test/setup.go new file mode 100644 index 0000000..7f7ee83 --- /dev/null +++ b/devcloud/cmdb/test/setup.go @@ -0,0 +1,21 @@ +package test + +import ( + "os" + + "github.com/infraboard/mcube/v2/ioc" + // 要注册哪些对象, Book, Comment + + // 加载的业务对象 + _ "122.51.31.227/go-course/go18/devcloud/cmdb/apps" + // task模块 + _ "github.com/infraboard/modules/task/apps" + // _ "122.51.31.227/go-course/go18/devcloud/mcenter/apps" + // _ "122.51.31.227/go-course/go18/devcloud/mpaas/apps" +) + +func DevelopmentSetUp() { + // import 后自动执行的逻辑 + // 工具对象的初始化, 需要的是绝对路径 + ioc.DevelopmentSetupWithPath(os.Getenv("CONFIG_PATH")) +} diff --git a/go.mod b/go.mod index 6ee8c4f..4e88d26 100644 --- a/go.mod +++ b/go.mod @@ -10,7 +10,7 @@ require ( 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/infraboard/modules v0.0.13 github.com/rs/xid v1.6.0 github.com/rs/zerolog v1.34.0 github.com/segmentio/kafka-go v0.4.47 @@ -117,5 +117,6 @@ require ( modernc.org/mathutil v1.5.0 // indirect modernc.org/memory v1.5.0 // indirect modernc.org/sqlite v1.23.1 // indirect + resty.dev/v3 v3.0.0-beta.2 // indirect sigs.k8s.io/yaml v1.4.0 // indirect ) diff --git a/go.sum b/go.sum index cc2170f..0ff8637 100644 --- a/go.sum +++ b/go.sum @@ -99,8 +99,8 @@ github.com/infraboard/devops v0.0.6 h1:oo7RfRBxu9hbI/+bYzXuX40BfG1hRyVtxC0fBhoFG 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= -github.com/infraboard/modules v0.0.12/go.mod h1:NdgdH/NoeqibJmFPn9th+tisMuR862/crbXeH4FPMaU= +github.com/infraboard/modules v0.0.13 h1:wykjQswgVnqv3mL2qtjqAui/AWuZWPaG/dkzITmDkrc= +github.com/infraboard/modules v0.0.13/go.mod h1:B+wkbV8oGOmSwribb3y/+F9PAp8H8UzhkrK7YySuYIk= github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= github.com/jackc/pgservicefile v0.0.0-20231201235250-de7065d80cb9 h1:L0QtFUgDarD7Fpv9jeVMgy/+Ec0mtnmYuImjTz6dtDA= @@ -353,5 +353,7 @@ modernc.org/memory v1.5.0/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU= modernc.org/sqlite v1.23.1 h1:nrSBg4aRQQwq59JpvGEQ15tNxoO5pX/kUjcRNwSAGQM= modernc.org/sqlite v1.23.1/go.mod h1:OrDj17Mggn6MhE+iPbBNf7RGKODDE9NFT0f3EwDzJqk= nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50= +resty.dev/v3 v3.0.0-beta.2 h1:xu4mGAdbCLuc3kbk7eddWfWm4JfhwDtdapwss5nCjnQ= +resty.dev/v3 v3.0.0-beta.2/go.mod h1:OgkqiPvTDtOuV4MGZuUDhwOpkY8enjOsjjMzeOHefy4= sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E= sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY=