diff --git a/api/server/router/plan/config_routes.go b/api/server/router/plan/config_routes.go new file mode 100644 index 00000000..9465bb73 --- /dev/null +++ b/api/server/router/plan/config_routes.go @@ -0,0 +1,108 @@ +/* +Copyright 2021 The Pixiu Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package plan + +import ( + "github.com/gin-gonic/gin" + + "github.com/caoyingjunz/pixiu/api/server/httputils" + "github.com/caoyingjunz/pixiu/pkg/types" +) + +type planConfigMeta struct { + planMeta + + ConfigId int64 `uri:"configId" binding:"required"` +} + +func (t *planRouter) createPlanConfig(c *gin.Context) { + r := httputils.NewResponse() + + var ( + opt planMeta + req types.CreatePlanConfigRequest + err error + ) + if err = httputils.ShouldBindAny(c, &req, &opt, nil); err != nil { + httputils.SetFailed(c, r, err) + return + } + if err = t.c.Plan().CreateConfig(c, opt.PlanId, &req); err != nil { + httputils.SetFailed(c, r, err) + return + } + + httputils.SetSuccess(c, r) +} + +func (t *planRouter) updatePlanConfig(c *gin.Context) { + r := httputils.NewResponse() + + var ( + opt planConfigMeta + req types.UpdatePlanConfigRequest + err error + ) + if err = httputils.ShouldBindAny(c, &req, &opt, nil); err != nil { + httputils.SetFailed(c, r, err) + return + } + if err = t.c.Plan().UpdateConfig(c, opt.PlanId, opt.ConfigId, &req); err != nil { + httputils.SetFailed(c, r, err) + return + } + + httputils.SetSuccess(c, r) +} + +func (t *planRouter) deletePlanConfig(c *gin.Context) { + r := httputils.NewResponse() + + var ( + opt planConfigMeta + err error + ) + if err = httputils.ShouldBindAny(c, nil, &opt, nil); err != nil { + httputils.SetFailed(c, r, err) + return + } + if err = t.c.Plan().DeleteConfig(c, opt.PlanId, opt.ConfigId); err != nil { + httputils.SetFailed(c, r, err) + return + } + + httputils.SetSuccess(c, r) +} + +func (t *planRouter) getPlanConfig(c *gin.Context) { + r := httputils.NewResponse() + + var ( + opt planConfigMeta + err error + ) + if err = httputils.ShouldBindAny(c, nil, &opt, nil); err != nil { + httputils.SetFailed(c, r, err) + return + } + if r.Result, err = t.c.Plan().GetConfig(c, opt.PlanId, opt.ConfigId); err != nil { + httputils.SetFailed(c, r, err) + return + } + + httputils.SetSuccess(c, r) +} diff --git a/api/server/router/plan/node_routes.go b/api/server/router/plan/node_routes.go new file mode 100644 index 00000000..127ca805 --- /dev/null +++ b/api/server/router/plan/node_routes.go @@ -0,0 +1,127 @@ +/* +Copyright 2021 The Pixiu Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package plan + +import ( + "github.com/gin-gonic/gin" + + "github.com/caoyingjunz/pixiu/api/server/httputils" + "github.com/caoyingjunz/pixiu/pkg/types" +) + +type planNodeMeta struct { + planMeta `json:",inline"` + + NodeId int64 `uri:"nodeId" binding:"required"` +} + +func (t *planRouter) createPlanNode(c *gin.Context) { + r := httputils.NewResponse() + + var ( + opt planMeta + req types.CreatePlanNodeRequest + err error + ) + if err = httputils.ShouldBindAny(c, &req, &opt, nil); err != nil { + httputils.SetFailed(c, r, err) + return + } + if err = t.c.Plan().CreateNode(c, opt.PlanId, &req); err != nil { + httputils.SetFailed(c, r, err) + return + } + + httputils.SetSuccess(c, r) +} + +func (t *planRouter) updatePlanNode(c *gin.Context) { + r := httputils.NewResponse() + + var ( + opt planNodeMeta + req types.UpdatePlanNodeRequest + err error + ) + if err = httputils.ShouldBindAny(c, &req, &opt, nil); err != nil { + httputils.SetFailed(c, r, err) + return + } + if err = t.c.Plan().UpdateNode(c, opt.PlanId, opt.NodeId, &req); err != nil { + httputils.SetFailed(c, r, err) + return + } + + httputils.SetSuccess(c, r) +} + +func (t *planRouter) deletePlanNode(c *gin.Context) { + r := httputils.NewResponse() + + var ( + opt planNodeMeta + err error + ) + if err = httputils.ShouldBindAny(c, nil, &opt, nil); err != nil { + httputils.SetFailed(c, r, err) + return + } + if err = t.c.Plan().DeleteNode(c, opt.PlanId, opt.NodeId); err != nil { + httputils.SetFailed(c, r, err) + return + } + + httputils.SetSuccess(c, r) +} + +func (t *planRouter) getPlanNode(c *gin.Context) { + r := httputils.NewResponse() + + var ( + opt planNodeMeta + err error + ) + if err = httputils.ShouldBindAny(c, nil, &opt, nil); err != nil { + httputils.SetFailed(c, r, err) + return + } + if r.Result, err = t.c.Plan().GetNode(c, opt.PlanId, opt.NodeId); err != nil { + httputils.SetFailed(c, r, err) + return + } + + httputils.SetSuccess(c, r) +} + +func (t *planRouter) listPlanNodes(c *gin.Context) { + r := httputils.NewResponse() + + var ( + opt planMeta + err error + ) + if err = httputils.ShouldBindAny(c, nil, &opt, nil); err != nil { + httputils.SetFailed(c, r, err) + return + } + if r.Result, err = t.c.Plan().ListNodes(c, opt.PlanId); err != nil { + httputils.SetFailed(c, r, err) + return + } + + httputils.SetSuccess(c, r) +} diff --git a/api/server/router/plan/plan.go b/api/server/router/plan/plan.go index eac5e5cf..d8b5a3b8 100644 --- a/api/server/router/plan/plan.go +++ b/api/server/router/plan/plan.go @@ -42,5 +42,18 @@ func (t *planRouter) initRoutes(ginEngine *gin.Engine) { planRoute.DELETE("/:planId", t.deletePlan) planRoute.GET("/:planId", t.getPlan) planRoute.GET("", t.listPlans) + + // 部署计划的节点API + planRoute.POST("/:planId/nodes", t.createPlanNode) + planRoute.PUT("/:planId/nodes/:nodeId", t.updatePlanNode) + planRoute.DELETE("/:planId/nodes/:nodeId", t.deletePlanNode) + planRoute.GET("/:planId/nodes/:nodeId", t.getPlanNode) + planRoute.GET("/:planId/nodes", t.listPlanNodes) + + // 部署计划的部署配置 + planRoute.POST("/:planId/configs", t.createPlanConfig) + planRoute.PUT("/:planId/configs/:configId", t.updatePlanConfig) + planRoute.DELETE("/:planId/configs/:configId", t.deletePlanConfig) + planRoute.GET("/:planId/configs/:configId", t.getPlanConfig) } } diff --git a/pkg/controller/plan/plan.go b/pkg/controller/plan/plan.go index 7d2863b5..76dacc82 100644 --- a/pkg/controller/plan/plan.go +++ b/pkg/controller/plan/plan.go @@ -38,6 +38,17 @@ type Interface interface { Delete(ctx context.Context, pid int64) error Get(ctx context.Context, pid int64) (*types.Plan, error) List(ctx context.Context) ([]types.Plan, error) + + CreateNode(ctx context.Context, pid int64, req *types.CreatePlanNodeRequest) error + UpdateNode(ctx context.Context, pid int64, nodeId int64, req *types.UpdatePlanNodeRequest) error + DeleteNode(ctx context.Context, pid int64, nodeId int64) error + GetNode(ctx context.Context, pid int64, nodeId int64) (*types.PlanNode, error) + ListNodes(ctx context.Context, pid int64) ([]types.PlanNode, error) + + CreateConfig(ctx context.Context, pid int64, req *types.CreatePlanConfigRequest) error + UpdateConfig(ctx context.Context, pid int64, cfgId int64, req *types.UpdatePlanConfigRequest) error + DeleteConfig(ctx context.Context, pid int64, cfgId int64) error + GetConfig(ctx context.Context, pid int64, cfgId int64) (*types.PlanConfig, error) } type plan struct { @@ -45,17 +56,28 @@ type plan struct { factory db.ShareDaoFactory } +// Create +// 1. 创建部署计划 +// 2. 创建部署任务 +// 3. 创建部署配置 func (p *plan) Create(ctx context.Context, req *types.CreatePlanRequest) error { - - object := &model.Plan{ - Name: req.Name, - } - - if _, err := p.factory.Plan().Create(ctx, object); err != nil { + object, err := p.factory.Plan().Create(ctx, &model.Plan{ + Name: req.Name, + Description: req.Description, + }) + if err != nil { klog.Errorf("failed to create plan %s: %v", req.Name, err) return errors.ErrServerInternal } + // 初始化部署计划关联的任务 + if err = p.createPlanTask(ctx, object.Id, model.UnStartedPlanStep); err != nil { + _ = p.Delete(ctx, object.Id) + klog.Errorf("failed to create plan task: %v", err) + return err + } + + // TODO: 创建部署配置 return nil } @@ -69,6 +91,8 @@ func (p *plan) Update(ctx context.Context, pid int64, req *types.UpdatePlanReque return nil } +// Delete +// TODO: 删除前校验 func (p *plan) Delete(ctx context.Context, pid int64) error { _, err := p.factory.Plan().Delete(ctx, pid) if err != nil { @@ -76,6 +100,11 @@ func (p *plan) Delete(ctx context.Context, pid int64) error { return errors.ErrServerInternal } + // 删除部署计划后,同步删除任务,删除任务失败时,可直接忽略 + err = p.deletePlanTask(ctx, pid) + if err != nil { + klog.Errorf("failed to delete plan(%d) task: %v", pid, err) + } return nil } diff --git a/pkg/controller/plan/plan_config.go b/pkg/controller/plan/plan_config.go new file mode 100644 index 00000000..09dc919a --- /dev/null +++ b/pkg/controller/plan/plan_config.go @@ -0,0 +1,115 @@ +/* +Copyright 2021 The Pixiu Authors. + +Licensed under the Apache License, Version 2.0 (phe "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package plan + +import ( + "context" + + "k8s.io/klog/v2" + + "github.com/caoyingjunz/pixiu/api/server/errors" + "github.com/caoyingjunz/pixiu/pkg/db/model" + "github.com/caoyingjunz/pixiu/pkg/types" +) + +func (p *plan) CreateConfig(ctx context.Context, pid int64, req *types.CreatePlanConfigRequest) error { + ks, err := req.Kubernetes.Marshal() + if err != nil { + return err + } + ns, err := req.Network.Marshal() + if err != nil { + return err + } + rs, err := req.Runtime.Marshal() + if err != nil { + return err + } + + if _, err = p.factory.Plan().CreatConfig(ctx, &model.Config{ + Name: req.Name, + PlanId: pid, + Region: req.Region, + Kubernetes: ks, + Network: ns, + Runtime: rs, + Description: req.Description, + }); err != nil { + klog.Errorf("failed to create plan(%s) config(%d): %v", req.Name, pid, err) + return err + } + + return nil +} + +// UpdateConfig +// TODO +func (p *plan) UpdateConfig(ctx context.Context, pid int64, cfgId int64, req *types.UpdatePlanConfigRequest) error { + return nil +} + +func (p *plan) DeleteConfig(ctx context.Context, pid int64, cfgId int64) error { + if _, err := p.factory.Plan().DeleteConfig(ctx, cfgId); err != nil { + klog.Errorf("failed to delete plan(%d) config(%d): %v", pid, cfgId, err) + return errors.ErrServerInternal + } + + return nil +} + +func (p *plan) GetConfig(ctx context.Context, pid int64, cfgId int64) (*types.PlanConfig, error) { + object, err := p.factory.Plan().GetConfig(ctx, cfgId) + if err != nil { + klog.Errorf("failed to get plan(%d) config(%d): %v", pid, cfgId, err) + return nil, errors.ErrServerInternal + } + + return p.modelConfig2Type(object) +} + +func (p *plan) modelConfig2Type(o *model.Config) (*types.PlanConfig, error) { + ks := &types.KubernetesSpec{} + if err := ks.Unmarshal(o.Kubernetes); err != nil { + return nil, err + } + ns := &types.NetworkSpec{} + if err := ns.Unmarshal(o.Network); err != nil { + return nil, err + } + rs := &types.RuntimeSpec{} + if err := rs.Unmarshal(o.Runtime); err != nil { + return nil, err + } + + return &types.PlanConfig{ + PixiuMeta: types.PixiuMeta{ + Id: o.Id, + ResourceVersion: o.ResourceVersion, + }, + TimeMeta: types.TimeMeta{ + GmtCreate: o.GmtCreate, + GmtModified: o.GmtModified, + }, + PlanId: o.PlanId, + Name: o.Name, + Region: o.Region, + Description: o.Description, + Kubernetes: *ks, + Network: *ns, + Runtime: *rs, + }, nil +} diff --git a/pkg/controller/plan/plan_node.go b/pkg/controller/plan/plan_node.go new file mode 100644 index 00000000..dcb0474e --- /dev/null +++ b/pkg/controller/plan/plan_node.go @@ -0,0 +1,120 @@ +/* +Copyright 2021 The Pixiu Authors. + +Licensed under the Apache License, Version 2.0 (phe "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package plan + +import ( + "context" + + "k8s.io/klog/v2" + + "github.com/caoyingjunz/pixiu/api/server/errors" + "github.com/caoyingjunz/pixiu/pkg/db/model" + "github.com/caoyingjunz/pixiu/pkg/types" +) + +// 创建前预检查 +// 1. 创建 node 时 plan 必须存在 +func (p *plan) preCreateNode(ctx context.Context, pid int64, req *types.CreatePlanNodeRequest) error { + _, err := p.Get(ctx, pid) + if err != nil { + return err + } + + return nil +} + +func (p *plan) CreateNode(ctx context.Context, pid int64, req *types.CreatePlanNodeRequest) error { + if err := p.preCreateNode(ctx, pid, req); err != nil { + return err + } + + // 获取节点认证信息 + auth, err := req.Auth.Marshal() + if err != nil { + klog.Errorf("failed to parse node(%s) auth: %v", req.Name, err) + return err + } + if _, err = p.factory.Plan().CreatNode(ctx, &model.Node{ + Name: req.Name, + PlanId: pid, + Role: req.Role, + Ip: req.Ip, + Auth: auth, + }); err != nil { + klog.Errorf("failed to create node(%s): %v", req.Name, err) + return err + } + + return nil +} + +func (p *plan) UpdateNode(ctx context.Context, pid int64, nodeId int64, req *types.UpdatePlanNodeRequest) error { + return nil +} + +func (p *plan) DeleteNode(ctx context.Context, pid int64, nodeId int64) error { + if _, err := p.factory.Plan().DeleteNode(ctx, nodeId); err != nil { + klog.Errorf("failed to delete plan(%d) node(%d): %v", pid, nodeId, err) + return errors.ErrServerInternal + } + + return nil +} + +func (p *plan) GetNode(ctx context.Context, pid int64, nodeId int64) (*types.PlanNode, error) { + object, err := p.factory.Plan().GetNode(ctx, nodeId) + if err != nil { + klog.Errorf("failed to get plan(%d) node(%d): %v", pid, nodeId, err) + return nil, errors.ErrServerInternal + } + + return p.modelNode2Type(object), nil +} + +func (p *plan) ListNodes(ctx context.Context, pid int64) ([]types.PlanNode, error) { + objects, err := p.factory.Plan().ListNodes(ctx, pid) + if err != nil { + klog.Errorf("failed to get plan(%d) nodes: %v", pid, err) + return nil, errors.ErrServerInternal + } + + var nodes []types.PlanNode + for _, object := range objects { + nodes = append(nodes, *p.modelNode2Type(&object)) + } + return nodes, nil +} + +func (p *plan) modelNode2Type(o *model.Node) *types.PlanNode { + return &types.PlanNode{ + PixiuMeta: types.PixiuMeta{ + Id: o.Id, + ResourceVersion: o.ResourceVersion, + }, + TimeMeta: types.TimeMeta{ + GmtCreate: o.GmtCreate, + GmtModified: o.GmtModified, + }, + PlanId: o.PlanId, + Name: o.Name, + Role: o.Role, + Ip: o.Ip, + Auth: types.PlanNodeAuth{ + Type: types.NoneAuth, + }, + } +} diff --git a/pkg/controller/plan/plan_task.go b/pkg/controller/plan/plan_task.go new file mode 100644 index 00000000..41d9ba81 --- /dev/null +++ b/pkg/controller/plan/plan_task.go @@ -0,0 +1,48 @@ +/* +Copyright 2021 The Pixiu Authors. + +Licensed under the Apache License, Version 2.0 (phe "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package plan + +import ( + "context" + + "k8s.io/klog/v2" + + "github.com/caoyingjunz/pixiu/pkg/db/model" +) + +func (p *plan) createPlanTask(ctx context.Context, planId int64, step model.PlanStep) error { + if _, err := p.factory.Plan().CreatTask(ctx, &model.Task{ + PlanId: planId, + Step: step, + }); err != nil { + klog.Errorf("failed to create plan(%d) task: %v", planId, err) + return err + } + + return nil +} + +func (p *plan) deletePlanTask(ctx context.Context, planId int64) error { + if _, err := p.factory.Plan().DeleteTask(ctx, planId); err != nil { + klog.Errorf("failed to delete plan(%s) task: %v", planId, err) + } + return nil +} + +func (p *plan) syncPlanTask(ctx context.Context, planId int64, step int, message string) error { + return nil +} diff --git a/pkg/db/migrator.go b/pkg/db/migrator.go index cbd589cf..1e900d04 100644 --- a/pkg/db/migrator.go +++ b/pkg/db/migrator.go @@ -32,7 +32,7 @@ func (m *migrator) AutoMigrate() error { } func (m *migrator) CreateTables(dst ...interface{}) error { - db := m.db.Set("gorm:table_options", "AUTO_INCREMENT=20220801") + db := m.db.Set("gorm:table_options", "AUTO_INCREMENT=20220801 DEFAULT CHARSET=utf8") for _, d := range dst { if db.Migrator().HasTable(d) { diff --git a/pkg/db/model/model.go b/pkg/db/model/model.go index fe5d872e..c50dba14 100644 --- a/pkg/db/model/model.go +++ b/pkg/db/model/model.go @@ -18,8 +18,8 @@ package model var models = make([]interface{}, 0) -func register(model interface{}) { - models = append(models, model) +func register(model ...interface{}) { + models = append(models, model...) } // GetMigrationModels is a helper function returns all models for table initalization. diff --git a/pkg/db/model/node.go b/pkg/db/model/node.go deleted file mode 100644 index 0b4bab0a..00000000 --- a/pkg/db/model/node.go +++ /dev/null @@ -1,38 +0,0 @@ -/* -Copyright 2024 The Pixiu Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package model - -import "github.com/caoyingjunz/pixiu/pkg/db/model/pixiu" - -func init() { - register(&Node{}) -} - -type Node struct { - pixiu.Model - - CloudId int64 `gorm:"index:idx_cloud" json:"cloud_id"` - Role string `json:"role"` // k8s 节点的角色,master 为 0 和 node 为 1 - HostName string `json:"host_name"` - Address string `json:"address"` - User string `json:"user"` - Password string `json:"password"` -} - -func (*Node) TableName() string { - return "nodes" -} diff --git a/pkg/db/model/plan.go b/pkg/db/model/plan.go index 7cd61b3f..0de0b9ee 100644 --- a/pkg/db/model/plan.go +++ b/pkg/db/model/plan.go @@ -16,10 +16,12 @@ limitations under the License. package model -import "github.com/caoyingjunz/pixiu/pkg/db/model/pixiu" +import ( + "github.com/caoyingjunz/pixiu/pkg/db/model/pixiu" +) func init() { - register(&Plan{}) + register(&Plan{}, &Node{}, &Config{}, &Task{}) } type Plan struct { @@ -32,3 +34,58 @@ type Plan struct { func (plan *Plan) TableName() string { return "plans" } + +type KubeRole int + +const ( + NodeRole KubeRole = iota // kubernetes node role + MasterRole // kubernetes master role +) + +type Node struct { + pixiu.Model + + Name string `gorm:"index:idx_name,unique" json:"name"` + PlanId int64 `json:"plan_id"` + Role KubeRole `json:"role"` // k8s 节点的角色,master 为 1 和 node 为 0 + Ip string `json:"ip"` + Auth string `json:"auth"` +} + +func (node *Node) TableName() string { + return "nodes" +} + +type Config struct { + pixiu.Model + + Name string `gorm:"index:idx_name,unique" json:"name"` + PlanId int64 `json:"plan_id"` + Region string `json:"region"` + Kubernetes string `json:"kubernetes"` + Network string `json:"network"` + Runtime string `json:"runtime"` + Description string `json:"description"` +} + +func (config *Config) TableName() string { + return "configs" +} + +type PlanStep int + +const ( + UnStartedPlanStep PlanStep = iota +) + +type Task struct { + pixiu.Model + + PlanId int64 `json:"plan_id"` + Step PlanStep `json:"step"` + Message string `json:"message"` +} + +func (task *Task) TableName() string { + return "tasks" +} diff --git a/pkg/db/plan.go b/pkg/db/plan.go index 287beb95..0f68e155 100644 --- a/pkg/db/plan.go +++ b/pkg/db/plan.go @@ -32,6 +32,23 @@ type PlanInterface interface { Delete(ctx context.Context, pid int64) (*model.Plan, error) Get(ctx context.Context, pid int64) (*model.Plan, error) List(ctx context.Context) ([]model.Plan, error) + + CreatNode(ctx context.Context, object *model.Node) (*model.Node, error) + UpdateNode(ctx context.Context, nodeId int64, resourceVersion int64, updates map[string]interface{}) error + DeleteNode(ctx context.Context, nodeId int64) (*model.Node, error) + GetNode(ctx context.Context, nodeId int64) (*model.Node, error) + ListNodes(ctx context.Context, pid int64) ([]model.Node, error) + + CreatConfig(ctx context.Context, object *model.Config) (*model.Config, error) + UpdateConfig(ctx context.Context, cfgId int64, resourceVersion int64, updates map[string]interface{}) error + DeleteConfig(ctx context.Context, cfgId int64) (*model.Config, error) + GetConfig(ctx context.Context, cfgId int64) (*model.Config, error) + ListConfigs(ctx context.Context) ([]model.Config, error) + + CreatTask(ctx context.Context, object *model.Task) (*model.Task, error) + UpdateTask(ctx context.Context, pid int64, resourceVersion int64, updates map[string]interface{}) error + DeleteTask(ctx context.Context, pid int64) (*model.Task, error) + GetTask(ctx context.Context, pid int64) (*model.Task, error) } type plan struct { @@ -96,6 +113,168 @@ func (p *plan) List(ctx context.Context) ([]model.Plan, error) { return objects, nil } +func (p *plan) CreatNode(ctx context.Context, object *model.Node) (*model.Node, error) { + now := time.Now() + object.GmtCreate = now + object.GmtModified = now + + if err := p.db.WithContext(ctx).Create(object).Error; err != nil { + return nil, err + } + return object, nil +} + +func (p *plan) UpdateNode(ctx context.Context, nodeId int64, resourceVersion int64, updates map[string]interface{}) error { + // 系统维护字段 + updates["gmt_modified"] = time.Now() + updates["resource_version"] = resourceVersion + 1 + + f := p.db.WithContext(ctx).Model(&model.Node{}).Where("id = ? and resource_version = ?", nodeId, resourceVersion).Updates(updates) + if f.Error != nil { + return f.Error + } + if f.RowsAffected == 0 { + return errors.ErrRecordNotFound + } + + return nil +} + +func (p *plan) DeleteNode(ctx context.Context, nodeId int64) (*model.Node, error) { + object, err := p.GetNode(ctx, nodeId) + if err != nil { + return nil, err + } + if err = p.db.WithContext(ctx).Where("id = ?", nodeId).Delete(&model.Node{}).Error; err != nil { + return nil, err + } + + return object, nil +} + +func (p *plan) GetNode(ctx context.Context, nodeId int64) (*model.Node, error) { + var object model.Node + if err := p.db.WithContext(ctx).Where("id = ?", nodeId).First(&object).Error; err != nil { + return nil, err + } + + return &object, nil +} + +func (p *plan) ListNodes(ctx context.Context, pid int64) ([]model.Node, error) { + var objects []model.Node + if err := p.db.WithContext(ctx).Where("plan_id = ?", pid).Find(&objects).Error; err != nil { + return nil, err + } + + return objects, nil +} + +func (p *plan) CreatConfig(ctx context.Context, object *model.Config) (*model.Config, error) { + now := time.Now() + object.GmtCreate = now + object.GmtModified = now + + if err := p.db.WithContext(ctx).Create(object).Error; err != nil { + return nil, err + } + return object, nil +} + +func (p *plan) UpdateConfig(ctx context.Context, cid int64, resourceVersion int64, updates map[string]interface{}) error { + // 系统维护字段 + updates["gmt_modified"] = time.Now() + updates["resource_version"] = resourceVersion + 1 + + f := p.db.WithContext(ctx).Model(&model.Config{}).Where("id = ? and resource_version = ?", cid, resourceVersion).Updates(updates) + if f.Error != nil { + return f.Error + } + if f.RowsAffected == 0 { + return errors.ErrRecordNotFound + } + + return nil +} + +func (p *plan) DeleteConfig(ctx context.Context, cid int64) (*model.Config, error) { + object, err := p.GetConfig(ctx, cid) + if err != nil { + return nil, err + } + if err = p.db.WithContext(ctx).Where("id = ?", cid).Delete(&model.Config{}).Error; err != nil { + return nil, err + } + + return object, nil +} + +func (p *plan) GetConfig(ctx context.Context, cid int64) (*model.Config, error) { + var object model.Config + if err := p.db.WithContext(ctx).Where("id = ?", cid).First(&object).Error; err != nil { + return nil, err + } + + return &object, nil +} + +func (p *plan) ListConfigs(ctx context.Context) ([]model.Config, error) { + var objects []model.Config + if err := p.db.WithContext(ctx).Find(&objects).Error; err != nil { + return nil, err + } + + return objects, nil +} + +func (p *plan) CreatTask(ctx context.Context, object *model.Task) (*model.Task, error) { + now := time.Now() + object.GmtCreate = now + object.GmtModified = now + + if err := p.db.WithContext(ctx).Create(object).Error; err != nil { + return nil, err + } + return object, nil +} + +func (p *plan) UpdateTask(ctx context.Context, pid int64, resourceVersion int64, updates map[string]interface{}) error { + // 系统维护字段 + updates["gmt_modified"] = time.Now() + updates["resource_version"] = resourceVersion + 1 + + f := p.db.WithContext(ctx).Model(&model.Task{}).Where("plan_id = ? and resource_version = ?", pid, resourceVersion).Updates(updates) + if f.Error != nil { + return f.Error + } + if f.RowsAffected == 0 { + return errors.ErrRecordNotFound + } + + return nil +} + +func (p *plan) DeleteTask(ctx context.Context, pid int64) (*model.Task, error) { + object, err := p.GetTask(ctx, pid) + if err != nil { + return nil, err + } + if err = p.db.WithContext(ctx).Where("plan_id = ?", pid).Delete(&model.Task{}).Error; err != nil { + return nil, err + } + + return object, nil +} + +func (p *plan) GetTask(ctx context.Context, pid int64) (*model.Task, error) { + var object model.Task + if err := p.db.WithContext(ctx).Where("plan_id = ?", pid).First(&object).Error; err != nil { + return nil, err + } + + return &object, nil +} + func newPlan(db *gorm.DB) *plan { return &plan{db} } diff --git a/pkg/types/meta.go b/pkg/types/meta.go index 40eb00b6..35fd7db0 100644 --- a/pkg/types/meta.go +++ b/pkg/types/meta.go @@ -157,3 +157,63 @@ func (t *TerminalSession) Next() *remotecommand.TerminalSize { return nil } } + +func (a *PlanNodeAuth) Marshal() (string, error) { + data, err := json.Marshal(a) + if err != nil { + return "", err + } + return string(data), nil +} + +func (a *PlanNodeAuth) Unmarshal(s string) error { + if err := json.Unmarshal([]byte(s), a); err != nil { + return err + } + return nil +} + +func (ks *KubernetesSpec) Marshal() (string, error) { + data, err := json.Marshal(ks) + if err != nil { + return "", err + } + return string(data), nil +} + +func (ks *KubernetesSpec) Unmarshal(s string) error { + if err := json.Unmarshal([]byte(s), ks); err != nil { + return err + } + return nil +} + +func (ns *NetworkSpec) Marshal() (string, error) { + data, err := json.Marshal(ns) + if err != nil { + return "", err + } + return string(data), nil +} + +func (ns *NetworkSpec) Unmarshal(s string) error { + if err := json.Unmarshal([]byte(s), ns); err != nil { + return err + } + return nil +} + +func (rs *RuntimeSpec) Marshal() (string, error) { + data, err := json.Marshal(rs) + if err != nil { + return "", err + } + return string(data), nil +} + +func (rs *RuntimeSpec) Unmarshal(s string) error { + if err := json.Unmarshal([]byte(s), rs); err != nil { + return err + } + return nil +} diff --git a/pkg/types/request.go b/pkg/types/request.go index 36262ed1..2fcb097b 100644 --- a/pkg/types/request.go +++ b/pkg/types/request.go @@ -83,13 +83,45 @@ type ( } CreatePlanRequest struct { - Name string `json:"name" binding:"omitempty"` // optional + Name string `json:"name" binding:"omitempty"` // required Description string `json:"description" binding:"omitempty"` // optional } UpdatePlanRequest struct { ResourceVersion int64 `json:"resource_version" binding:"required"` // required } + + CreatePlanNodeRequest struct { + Name string `json:"name" binding:"omitempty"` // required + PlanId int64 `json:"plan_id"` + Role model.KubeRole `json:"role"` // k8s 节点的角色,master 为 1 和 node 为 0 + Ip string `json:"ip"` + Auth PlanNodeAuth `json:"auth"` + } + + UpdatePlanNodeRequest struct { + ResourceVersion int64 `json:"resource_version" binding:"required"` // required + Name string `json:"name" binding:"omitempty"` // required + PlanId int64 `json:"plan_id"` + Role model.KubeRole `json:"role"` // k8s 节点的角色,master 为 1 和 node 为 0 + Ip string `json:"ip"` + Auth PlanNodeAuth `json:"auth"` + } + + CreatePlanConfigRequest struct { + PlanId int64 `json:"plan_id" binding:"required"` // required + Name string `json:"name" binding:"required"` // required + Region string `json:"region"` + Description string `json:"description" binding:"omitempty"` // optional + + Kubernetes KubernetesSpec `json:"kubernetes"` + Network NetworkSpec `json:"network"` + Runtime RuntimeSpec `json:"runtime"` + } + + UpdatePlanConfigRequest struct { + ResourceVersion int64 `json:"resource_version" binding:"required"` // required + } ) type ( diff --git a/pkg/types/types.go b/pkg/types/types.go index f1aa3f55..377e8b5f 100644 --- a/pkg/types/types.go +++ b/pkg/types/types.go @@ -111,6 +111,53 @@ type Plan struct { Description string `json:"description"` // 用户描述信息 } +type PlanNode struct { + PixiuMeta `json:",inline"` + TimeMeta `json:",inline"` + + Name string `json:"name"` // required + PlanId int64 `json:"plan_id"` + Role model.KubeRole `json:"role"` // k8s 节点的角色,master 为 1 和 node 为 0 + Ip string `json:"ip"` + Auth PlanNodeAuth `json:"auth,omitempty"` +} + +type AuthType string + +const ( + NoneAuth AuthType = "none" // 已开启密码 + KeyAuth AuthType = "key" // 密钥 + PasswordAuth AuthType = "password" // 密码 +) + +type PlanNodeAuth struct { + Type AuthType `json:"type"` // 节点认证模式,支持 key 和 password + Key *KeySpec `json:"key,omitempty"` + Password *PasswordSpec `json:"password,omitempty"` +} + +type KeySpec struct { + Data string `json:"data,omitempty"` +} + +type PasswordSpec struct { + User string `json:"user,omitempty"` + Password string `json:"password,omitempty"` +} + +type PlanConfig struct { + PixiuMeta `json:",inline"` + TimeMeta `json:",inline"` + + PlanId int64 `json:"plan_id" binding:"required"` // required + Name string `json:"name" binding:"required"` // required + Region string `json:"region"` + Description string `json:"description"` // optional + Kubernetes KubernetesSpec `json:"kubernetes"` + Network NetworkSpec `json:"network"` + Runtime RuntimeSpec `json:"runtime"` +} + // TimeSpec 通用时间规格 type TimeSpec struct { GmtCreate interface{} `json:"gmt_create,omitempty"` @@ -162,3 +209,19 @@ type EventOptions struct { Namespaced bool `form:"namespaced"` Limit int64 `form:"limit"` } + +type KubernetesSpec struct { + ApiServer string `json:"api_server"` + KubernetesVersion string `json:"kubernetes_version"` + EnableHA bool `json:"enable_ha"` +} + +type NetworkSpec struct { + PodNetwork string `json:"pod_network"` + ServiceNetwork string `json:"service_network"` + KubeProxy string `json:"kube_proxy"` +} + +type RuntimeSpec struct { + Runtime string `json:"runtime"` +}