Feat: add delivery-target API (#2703)

* Feat: add delivery-target API

* Fix: for unit test

Co-authored-by: zhuxiaobing <zhuxiaobing@lixiang.com>
This commit is contained in:
朱晓兵
2021-11-15 10:28:41 +08:00
committed by GitHub
parent e1c64540f4
commit 4eb9cc114e
6 changed files with 485 additions and 3 deletions

View File

@@ -34,6 +34,8 @@ var (
CtxKeyApplication = "application"
// CtxKeyWorkflow request context key of workflow
CtxKeyWorkflow = "workflow"
// CtxKeyDeliveryTarget request context key of workflow
CtxKeyDeliveryTarget = "delivery-target"
// CtxKeyApplicationEnvBinding request context key of env binding
CtxKeyApplicationEnvBinding = "envbinding-policy"
)
@@ -672,8 +674,8 @@ type ApplicationTrait struct {
Properties *model.JSONStruct `json:"properties"`
}
// CreateDeliveryTarget create delivery target request body
type CreateDeliveryTarget struct {
// CreateDeliveryTargetRequest create delivery target request body
type CreateDeliveryTargetRequest struct {
Name string `json:"name" validate:"checkname"`
Namespace string `json:"namespace" validate:"checkname"`
Alias string `json:"alias,omitempty" validate:"checkalias" optional:"true"`
@@ -683,7 +685,7 @@ type CreateDeliveryTarget struct {
}
// UpdateDeliveryTarget only support full quantity update
type UpdateDeliveryTarget struct {
type UpdateDeliveryTargetRequest struct {
Alias string `json:"alias,omitempty" validate:"checkalias" optional:"true"`
Description string `json:"description,omitempty" optional:"true"`
Kubernetes *KubernetesTarget `json:"kubernetes,omitempty"`
@@ -704,6 +706,29 @@ type CloudTarget struct {
VpcID string `json:"vpcID" optional:"true"`
}
// DetailDeliveryTargetResponse detail deliveryTarget response
type DetailDeliveryTargetResponse struct {
DeliveryTargetBase
}
// ListDeliveryTargetBaseResponse list application workflows
type ListDeliveryTargetResponse struct {
DeliveryTargets []DeliveryTargetBase `json:"deliveryTargets"`
Total int64 `json:"total"`
}
// DeliveryTargetBase deliveryTarget base model
type DeliveryTargetBase struct {
Name string `json:"name" validate:"checkname"`
Namespace string `json:"namespace" validate:"checkname"`
Alias string `json:"alias,omitempty" validate:"checkalias" optional:"true"`
Description string `json:"description,omitempty" optional:"true"`
Kubernetes *KubernetesTarget `json:"kubernetes,omitempty"`
Cloud *CloudTarget `json:"cloud,omitempty"`
CreateTime time.Time `json:"createTime"`
UpdateTime time.Time `json:"updateTime"`
}
// ApplicationRevisionBase application revision base spec
type ApplicationRevisionBase struct {
Version string `json:"version"`

View File

@@ -0,0 +1,162 @@
/*
Copyright 2021 The KubeVela 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 usecase
import (
"context"
"errors"
"github.com/oam-dev/kubevela/pkg/apiserver/datastore"
"github.com/oam-dev/kubevela/pkg/apiserver/log"
"github.com/oam-dev/kubevela/pkg/apiserver/model"
apisv1 "github.com/oam-dev/kubevela/pkg/apiserver/rest/apis/v1"
"github.com/oam-dev/kubevela/pkg/apiserver/rest/utils/bcode"
)
// DeliveryTargetUsecase deliveryTarget manage api
type DeliveryTargetUsecase interface {
GetDeliveryTarget(ctx context.Context, deliveryTargetName string) (*model.DeliveryTarget, error)
DetailDeliveryTarget(ctx context.Context, deliveryTarget *model.DeliveryTarget) (*apisv1.DetailDeliveryTargetResponse, error)
DeleteDeliveryTarget(ctx context.Context, deliveryTargetName string) error
CreateDeliveryTarget(ctx context.Context, req apisv1.CreateDeliveryTargetRequest) (*apisv1.DetailDeliveryTargetResponse, error)
UpdateDeliveryTarget(ctx context.Context, deliveryTarget *model.DeliveryTarget, req apisv1.UpdateDeliveryTargetRequest) (*apisv1.DetailDeliveryTargetResponse, error)
ListDeliveryTargets(ctx context.Context, page, pageSize int) (*apisv1.ListDeliveryTargetResponse, error)
}
// NewDeliveryTargetUsecase new DeliveryTarget usecase
func NewDeliveryTargetUsecase(ds datastore.DataStore) DeliveryTargetUsecase {
return &deliveryTargetUsecaseImpl{ds: ds}
}
type deliveryTargetUsecaseImpl struct {
ds datastore.DataStore
}
func (dt *deliveryTargetUsecaseImpl) ListDeliveryTargets(ctx context.Context, page, pageSize int) (*apisv1.ListDeliveryTargetResponse, error) {
deliveryTarget := model.DeliveryTarget{}
deliveryTargets, err := dt.ds.List(ctx, &deliveryTarget, &datastore.ListOptions{Page: page, PageSize: pageSize})
if err != nil {
return nil, err
}
resp := &apisv1.ListDeliveryTargetResponse{
DeliveryTargets: []apisv1.DeliveryTargetBase{},
}
for _, raw := range deliveryTargets {
dt, ok := raw.(*model.DeliveryTarget)
if ok {
resp.DeliveryTargets = append(resp.DeliveryTargets, *convertFromDeliveryTargetModel(dt))
}
}
count, err := dt.ds.Count(ctx, &deliveryTarget)
if err != nil {
return nil, err
}
resp.Total = count
return resp, nil
}
// DeleteDeliveryTarget delete application DeliveryTarget
func (dt *deliveryTargetUsecaseImpl) DeleteDeliveryTarget(ctx context.Context, DeliveryTargetName string) error {
deliveryTarget := &model.DeliveryTarget{
Name: DeliveryTargetName,
}
if err := dt.ds.Delete(ctx, deliveryTarget); err != nil {
if errors.Is(err, datastore.ErrRecordNotExist) {
return bcode.ErrDeliveryTargetNotExist
}
return err
}
return nil
}
func (dt *deliveryTargetUsecaseImpl) CreateDeliveryTarget(ctx context.Context, req apisv1.CreateDeliveryTargetRequest) (*apisv1.DetailDeliveryTargetResponse, error) {
deliveryTarget := convertCreateReqToDeliveryTargetModel(req)
// check deliveryTarget name.
exit, err := dt.ds.IsExist(ctx, &deliveryTarget)
if err != nil {
log.Logger.Errorf("check application name is exist failure %s", err.Error())
return nil, bcode.ErrDeliveryTargetExist
}
if exit {
return nil, bcode.ErrDeliveryTargetExist
}
if err := dt.ds.Add(ctx, &deliveryTarget); err != nil {
return nil, err
}
return dt.DetailDeliveryTarget(ctx, &deliveryTarget)
}
func (dt *deliveryTargetUsecaseImpl) UpdateDeliveryTarget(ctx context.Context, deliveryTarget *model.DeliveryTarget, req apisv1.UpdateDeliveryTargetRequest) (*apisv1.DetailDeliveryTargetResponse, error) {
deliveryTargetModel := convertUpdateReqToDeliveryTargetModel(deliveryTarget, req)
if err := dt.ds.Put(ctx, deliveryTargetModel); err != nil {
return nil, err
}
return dt.DetailDeliveryTarget(ctx, deliveryTargetModel)
}
// DetailDeliveryTarget detail DeliveryTarget
func (dt *deliveryTargetUsecaseImpl) DetailDeliveryTarget(ctx context.Context, deliveryTarget *model.DeliveryTarget) (*apisv1.DetailDeliveryTargetResponse, error) {
return &apisv1.DetailDeliveryTargetResponse{
DeliveryTargetBase: *convertFromDeliveryTargetModel(deliveryTarget),
}, nil
}
// GetDeliveryTarget get DeliveryTarget model
func (dt *deliveryTargetUsecaseImpl) GetDeliveryTarget(ctx context.Context, deliveryTargetName string) (*model.DeliveryTarget, error) {
deliveryTarget := &model.DeliveryTarget{
Name: deliveryTargetName,
}
if err := dt.ds.Get(ctx, deliveryTarget); err != nil {
return nil, err
}
return deliveryTarget, nil
}
func convertUpdateReqToDeliveryTargetModel(deliveryTarget *model.DeliveryTarget, req apisv1.UpdateDeliveryTargetRequest) *model.DeliveryTarget {
deliveryTarget.Alias = req.Alias
deliveryTarget.Description = req.Description
deliveryTarget.Kubernetes = (*model.KubernetesTarget)(req.Kubernetes)
deliveryTarget.Cloud = (*model.CloudTarget)(req.Cloud)
return deliveryTarget
}
func convertCreateReqToDeliveryTargetModel(req apisv1.CreateDeliveryTargetRequest) model.DeliveryTarget {
deliveryTarget := model.DeliveryTarget{
Name: req.Name,
Namespace: req.Namespace,
Alias: req.Alias,
Description: req.Description,
Kubernetes: (*model.KubernetesTarget)(req.Kubernetes),
Cloud: (*model.CloudTarget)(req.Cloud),
}
return deliveryTarget
}
func convertFromDeliveryTargetModel(deliveryTarget *model.DeliveryTarget) *apisv1.DeliveryTargetBase {
return &apisv1.DeliveryTargetBase{
Name: deliveryTarget.Name,
Namespace: deliveryTarget.Namespace,
Alias: deliveryTarget.Alias,
Description: deliveryTarget.Description,
Kubernetes: (*apisv1.KubernetesTarget)(deliveryTarget.Kubernetes),
Cloud: (*apisv1.CloudTarget)(deliveryTarget.Cloud),
CreateTime: deliveryTarget.CreateTime,
UpdateTime: deliveryTarget.UpdateTime,
}
}

View File

@@ -0,0 +1,76 @@
/*
Copyright 2021 The KubeVela 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 usecase
import (
"context"
"github.com/google/go-cmp/cmp"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"github.com/oam-dev/kubevela/pkg/apiserver/model"
apisv1 "github.com/oam-dev/kubevela/pkg/apiserver/rest/apis/v1"
)
var _ = Describe("Test delivery target usecase functions", func() {
var (
deliveryTargetUsecase *deliveryTargetUsecaseImpl
)
BeforeEach(func() {
deliveryTargetUsecase = &deliveryTargetUsecaseImpl{ds: ds}
})
It("Test CreateDeliveryTarget function", func() {
req := apisv1.CreateDeliveryTargetRequest{
Name: "test-delivery-target",
Namespace: "test-namespace",
Alias: "test-alias",
Description: "this is a deliveryTarget",
Kubernetes: &apisv1.KubernetesTarget{ClusterName: "cluster-dev", Namespace: "dev"},
Cloud: &apisv1.CloudTarget{TerraformProviderName: "provider", Region: "us-1"},
}
base, err := deliveryTargetUsecase.CreateDeliveryTarget(context.TODO(), req)
Expect(err).Should(BeNil())
Expect(cmp.Diff(base.Name, req.Name)).Should(BeEmpty())
})
It("Test GetDeliveryTarget function", func() {
deliveryTarget, err := deliveryTargetUsecase.GetDeliveryTarget(context.TODO(), "test-delivery-target")
Expect(err).Should(BeNil())
Expect(deliveryTarget).ShouldNot(BeNil())
Expect(cmp.Diff(deliveryTarget.Name, "test-delivery-target")).Should(BeEmpty())
})
It("Test ListDeliveryTargets function", func() {
_, err := deliveryTargetUsecase.ListDeliveryTargets(context.TODO(), 1, 1)
Expect(err).Should(BeNil())
})
It("Test DetailDeliveryTarget function", func() {
detail, err := deliveryTargetUsecase.DetailDeliveryTarget(context.TODO(),
&model.DeliveryTarget{
Name: "test-delivery-target",
Namespace: "test-namespace",
Alias: "test-alias",
Description: "this is a deliveryTarget",
Kubernetes: &model.KubernetesTarget{ClusterName: "cluster-dev", Namespace: "dev"},
Cloud: &model.CloudTarget{TerraformProviderName: "provider", Region: "us-1"}})
Expect(err).Should(BeNil())
Expect(detail.Name).Should(Equal("test-delivery-target"))
})
})

View File

@@ -0,0 +1,23 @@
/*
Copyright 2021 The KubeVela 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 bcode
// ErrDeliveryTargetExist deliveryTarget is exist
var ErrDeliveryTargetExist = NewBcode(400, 20006, "deliveryTarget is exist")
// ErrDeliveryTargetNotExist deliveryTarget is not exist
var ErrDeliveryTargetNotExist = NewBcode(404, 20007, "deliveryTarget is not exist")

View File

@@ -0,0 +1,194 @@
/*
Copyright 2021 The KubeVela 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 webservice
import (
"context"
restfulspec "github.com/emicklei/go-restful-openapi/v2"
restful "github.com/emicklei/go-restful/v3"
"github.com/oam-dev/kubevela/pkg/apiserver/log"
"github.com/oam-dev/kubevela/pkg/apiserver/model"
apis "github.com/oam-dev/kubevela/pkg/apiserver/rest/apis/v1"
"github.com/oam-dev/kubevela/pkg/apiserver/rest/usecase"
"github.com/oam-dev/kubevela/pkg/apiserver/rest/utils"
"github.com/oam-dev/kubevela/pkg/apiserver/rest/utils/bcode"
)
// NewDeliveryTargetWebService new deliveryTarget webservice
func NewDeliveryTargetWebService(deliveryTargetUsecase usecase.DeliveryTargetUsecase) WebService {
return &DeliveryTargetWebService{
deliveryTargetUsecase: deliveryTargetUsecase,
}
}
type DeliveryTargetWebService struct {
deliveryTargetUsecase usecase.DeliveryTargetUsecase
}
func (dt *DeliveryTargetWebService) GetWebService() *restful.WebService {
ws := new(restful.WebService)
ws.Path(versionPrefix+"/deliveryTargets").
Consumes(restful.MIME_XML, restful.MIME_JSON).
Produces(restful.MIME_JSON, restful.MIME_XML).
Doc("api for deliveryTarget manage")
tags := []string{"deliveryTarget"}
ws.Route(ws.GET("/").To(dt.listDeliveryTargets).
Doc("list deliveryTarget").
Metadata(restfulspec.KeyOpenAPITags, tags).
Returns(200, "", apis.ListDeliveryTargetResponse{}).
Writes(apis.ListDeliveryTargetResponse{}).Do(returns200, returns500))
ws.Route(ws.POST("/").To(dt.createDeliveryTarget).
Doc("create deliveryTarget").
Metadata(restfulspec.KeyOpenAPITags, tags).
Reads(apis.CreateDeliveryTargetRequest{}).
Returns(200, "create success", apis.DetailDeliveryTargetResponse{}).
Returns(400, "create failure", bcode.Bcode{}).
Writes(apis.DetailDeliveryTargetResponse{}).Do(returns200, returns500))
ws.Route(ws.GET("/{name}").To(dt.detailDeliveryTarget).
Doc("detail deliveryTarget").
Param(ws.PathParameter("name", "identifier of the deliveryTarget.").DataType("string")).
Metadata(restfulspec.KeyOpenAPITags, tags).
Filter(dt.deliveryTargetCheckFilter).
Returns(200, "create success", apis.DetailDeliveryTargetResponse{}).
Writes(apis.DetailDeliveryTargetResponse{}).Do(returns200, returns500))
ws.Route(ws.PUT("/{name}").To(dt.updateDeliveryTarget).
Doc("update application DeliveryTarget config").
Metadata(restfulspec.KeyOpenAPITags, tags).
Filter(dt.deliveryTargetCheckFilter).
Param(ws.PathParameter("name", "identifier of the deliveryTarget").DataType("string")).
Reads(apis.UpdateDeliveryTargetRequest{}).
Returns(200, "", apis.DetailDeliveryTargetResponse{}).
Writes(apis.DetailDeliveryTargetResponse{}).Do(returns200, returns500))
ws.Route(ws.DELETE("/{name}").To(dt.deleteDeliveryTarget).
Doc("deletet DeliveryTarget").
Metadata(restfulspec.KeyOpenAPITags, tags).
Filter(dt.deliveryTargetCheckFilter).
Param(ws.PathParameter("name", "identifier of the deliveryTarget").DataType("string")).
Returns(200, "", apis.EmptyResponse{}).
Writes(apis.EmptyResponse{}).Do(returns200, returns500))
return ws
}
func (dt *DeliveryTargetWebService) createDeliveryTarget(req *restful.Request, res *restful.Response) {
// Verify the validity of parameters
var createReq apis.CreateDeliveryTargetRequest
if err := req.ReadEntity(&createReq); err != nil {
bcode.ReturnError(req, res, err)
return
}
if err := validate.Struct(&createReq); err != nil {
bcode.ReturnError(req, res, err)
return
}
// Call the usecase layer code
deliveryTargetDetail, err := dt.deliveryTargetUsecase.CreateDeliveryTarget(req.Request.Context(), createReq)
if err != nil {
log.Logger.Errorf("create delivery-target failure %s", err.Error())
bcode.ReturnError(req, res, err)
return
}
// Write back response data
if err := res.WriteEntity(deliveryTargetDetail); err != nil {
bcode.ReturnError(req, res, err)
return
}
}
func (dt *DeliveryTargetWebService) deliveryTargetCheckFilter(req *restful.Request, res *restful.Response, chain *restful.FilterChain) {
deliveryTarget, err := dt.deliveryTargetUsecase.GetDeliveryTarget(req.Request.Context(), req.PathParameter("name"))
if err != nil {
bcode.ReturnError(req, res, err)
return
}
req.Request = req.Request.WithContext(context.WithValue(req.Request.Context(), &apis.CtxKeyDeliveryTarget, deliveryTarget))
chain.ProcessFilter(req, res)
}
func (dt *DeliveryTargetWebService) detailDeliveryTarget(req *restful.Request, res *restful.Response) {
deliveryTarget := req.Request.Context().Value(&apis.CtxKeyDeliveryTarget).(*model.DeliveryTarget)
detail, err := dt.deliveryTargetUsecase.DetailDeliveryTarget(req.Request.Context(), deliveryTarget)
if err != nil {
bcode.ReturnError(req, res, err)
return
}
if err := res.WriteEntity(detail); err != nil {
bcode.ReturnError(req, res, err)
return
}
}
func (dt *DeliveryTargetWebService) updateDeliveryTarget(req *restful.Request, res *restful.Response) {
deliveryTarget := req.Request.Context().Value(&apis.CtxKeyDeliveryTarget).(*model.DeliveryTarget)
// Verify the validity of parameters
var updateReq apis.UpdateDeliveryTargetRequest
if err := req.ReadEntity(&updateReq); err != nil {
bcode.ReturnError(req, res, err)
return
}
if err := validate.Struct(&updateReq); err != nil {
bcode.ReturnError(req, res, err)
return
}
detail, err := dt.deliveryTargetUsecase.UpdateDeliveryTarget(req.Request.Context(), deliveryTarget, updateReq)
if err != nil {
bcode.ReturnError(req, res, err)
return
}
if err := res.WriteEntity(detail); err != nil {
bcode.ReturnError(req, res, err)
return
}
}
func (dt *DeliveryTargetWebService) deleteDeliveryTarget(req *restful.Request, res *restful.Response) {
if err := dt.deliveryTargetUsecase.DeleteDeliveryTarget(req.Request.Context(), req.PathParameter("name")); err != nil {
bcode.ReturnError(req, res, err)
return
}
if err := res.WriteEntity(apis.EmptyResponse{}); err != nil {
bcode.ReturnError(req, res, err)
return
}
}
func (dt *DeliveryTargetWebService) listDeliveryTargets(req *restful.Request, res *restful.Response) {
page, pageSize, err := utils.ExtractPagingParams(req, minPageSize, maxPageSize)
if err != nil {
bcode.ReturnError(req, res, err)
return
}
deliveryTargets, err := dt.deliveryTargetUsecase.ListDeliveryTargets(req.Request.Context(), page, pageSize)
if err != nil {
bcode.ReturnError(req, res, err)
return
}
if err := res.WriteEntity(deliveryTargets); err != nil {
bcode.ReturnError(req, res, err)
return
}
}

View File

@@ -66,6 +66,7 @@ func Init(ds datastore.DataStore) {
velaQLUsecase := usecase.NewVelaQLUsecase()
definitionUsecase := usecase.NewDefinitionUsecase()
addonUsecase := usecase.NewAddonUsecase(ds)
deliveryTargetUsecase := usecase.NewDeliveryTargetUsecase(ds)
RegistWebService(NewClusterWebService(clusterUsecase))
RegistWebService(NewApplicationWebService(applicationUsecase))
RegistWebService(NewNamespaceWebService(namespaceUsecase))
@@ -75,5 +76,6 @@ func Init(ds datastore.DataStore) {
RegistWebService(NewOAMApplication(oamApplicationUsecase))
RegistWebService(&policyDefinitionWebservice{})
RegistWebService(NewWorkflowWebService(workflowUsecase, applicationUsecase))
RegistWebService(NewDeliveryTargetWebService(deliveryTargetUsecase))
RegistWebService(NewVelaQLWebService(velaQLUsecase))
}