mirror of
https://github.com/kubevela/kubevela.git
synced 2026-08-19 04:26:39 +00:00
Feat: createOrUpdateApplication support dryRun operation (#4860)
Signed-off-by: wuzhongjian <wuzhongjian_yewu@cmss.chinamobile.com> Signed-off-by: wuzhongjian <wuzhongjian_yewu@cmss.chinamobile.com>
This commit is contained in:
@@ -23,10 +23,12 @@ import (
|
||||
|
||||
v1 "k8s.io/api/core/v1"
|
||||
kerrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
"k8s.io/client-go/rest"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
|
||||
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
|
||||
apisv1 "github.com/oam-dev/kubevela/pkg/apiserver/interfaces/api/dto/v1"
|
||||
"github.com/oam-dev/kubevela/pkg/utils/common"
|
||||
)
|
||||
|
||||
// OAMApplicationService oam_application service
|
||||
@@ -34,6 +36,7 @@ type OAMApplicationService interface {
|
||||
CreateOrUpdateOAMApplication(context.Context, apisv1.ApplicationRequest, string, string) error
|
||||
GetOAMApplication(context.Context, string, string) (*apisv1.ApplicationResponse, error)
|
||||
DeleteOAMApplication(context.Context, string, string) error
|
||||
DryRunOAMApplication(context.Context, apisv1.ApplicationRequest, string, string) error
|
||||
}
|
||||
|
||||
// NewOAMApplicationService new oam_application service
|
||||
@@ -43,6 +46,7 @@ func NewOAMApplicationService() OAMApplicationService {
|
||||
|
||||
type oamApplicationServiceImpl struct {
|
||||
KubeClient client.Client `inject:"kubeClient"`
|
||||
KubeConfig *rest.Config `inject:"kubeConfig"`
|
||||
}
|
||||
|
||||
// CreateOrUpdateOAMApplication create or update application
|
||||
@@ -104,3 +108,53 @@ func (o oamApplicationServiceImpl) DeleteOAMApplication(ctx context.Context, nam
|
||||
},
|
||||
}))
|
||||
}
|
||||
|
||||
// DryRunOAMApplication dryRun create or update application
|
||||
func (o oamApplicationServiceImpl) DryRunOAMApplication(ctx context.Context, request apisv1.ApplicationRequest, name, namespace string) error {
|
||||
ns := new(v1.Namespace)
|
||||
err := o.KubeClient.Get(ctx, client.ObjectKey{Name: namespace}, ns)
|
||||
if kerrors.IsNotFound(err) {
|
||||
ns.Name = namespace
|
||||
if err = o.KubeClient.Create(ctx, ns); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
app := &v1beta1.Application{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: "Application",
|
||||
APIVersion: "core.oam.dev/v1beta1",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: name,
|
||||
Namespace: namespace,
|
||||
},
|
||||
Spec: v1beta1.ApplicationSpec{
|
||||
Components: request.Components,
|
||||
Policies: request.Policies,
|
||||
Workflow: request.Workflow,
|
||||
},
|
||||
}
|
||||
|
||||
args := common.Args{
|
||||
Schema: common.Scheme,
|
||||
}
|
||||
_ = args.SetConfig(o.KubeConfig)
|
||||
args.SetClient(o.KubeClient)
|
||||
_, err = dryRunApplication(ctx, args, app)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
existApp := new(v1beta1.Application)
|
||||
err = o.KubeClient.Get(ctx, client.ObjectKey{Name: name, Namespace: namespace}, existApp)
|
||||
if err != nil {
|
||||
if kerrors.IsNotFound(err) {
|
||||
return o.KubeClient.Create(ctx, app, client.DryRunAll)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
existApp.Spec = app.Spec
|
||||
return o.KubeClient.Update(ctx, existApp, client.DryRunAll)
|
||||
}
|
||||
|
||||
@@ -47,6 +47,7 @@ var _ = Describe("Test oam application service function", func() {
|
||||
ns = corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: namespace}}
|
||||
oamAppService = &oamApplicationServiceImpl{
|
||||
KubeClient: k8sClient,
|
||||
KubeConfig: cfg,
|
||||
}
|
||||
Expect(common.ReadYamlToObject("./testdata/example-app.yaml", &baseApp)).Should(BeNil())
|
||||
|
||||
@@ -117,4 +118,30 @@ var _ = Describe("Test oam application service function", func() {
|
||||
err := k8sClient.Get(ctx, client.ObjectKey{Namespace: namespace, Name: baseApp.Name}, app)
|
||||
Expect(kerrors.IsNotFound(err)).Should(BeTrue())
|
||||
})
|
||||
|
||||
It("Test DryRunOAMApplication function", func() {
|
||||
By("test dryRun create application")
|
||||
appName := "test-new-app"
|
||||
appNs := randomNamespaceName("test-new-app")
|
||||
var newApp v1beta1.Application
|
||||
Expect(common.ReadYamlToObject("./testdata/dryrun-app.yaml", &newApp)).Should(BeNil())
|
||||
newApp.SetNamespace(namespace)
|
||||
Eventually(func() error {
|
||||
return k8sClient.Create(ctx, &newApp)
|
||||
}, time.Second*3, time.Microsecond*300).Should(SatisfyAny(BeNil(), &util.AlreadyExistMatcher{}))
|
||||
|
||||
req := apiv1.ApplicationRequest{
|
||||
Components: newApp.Spec.Components,
|
||||
Policies: newApp.Spec.Policies,
|
||||
Workflow: newApp.Spec.Workflow,
|
||||
}
|
||||
Expect(oamAppService.DryRunOAMApplication(ctx, req, appName, appNs)).Should(BeNil())
|
||||
|
||||
By("test dryRun update application")
|
||||
updateReq := apiv1.ApplicationRequest{
|
||||
Components: newApp.Spec.Components[1:],
|
||||
}
|
||||
Expect(oamAppService.DryRunOAMApplication(ctx, updateReq, appName, appNs)).Should(BeNil())
|
||||
|
||||
})
|
||||
})
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
apiVersion: core.oam.dev/v1beta1
|
||||
kind: Application
|
||||
metadata:
|
||||
name: app-dryrun
|
||||
namespace: default
|
||||
spec:
|
||||
components:
|
||||
- name: hello-world
|
||||
type: webservice
|
||||
properties:
|
||||
image: crccheck/hello-world
|
||||
port: 8000
|
||||
traits:
|
||||
- type: scaler
|
||||
properties:
|
||||
replicas: 1
|
||||
@@ -60,6 +60,9 @@ func (c *oamApplicationAPIInterface) GetWebServiceRoute() *restful.WebService {
|
||||
Filter(c.RbacService.CheckPerm("application", "deploy")).
|
||||
Param(ws.PathParameter("namespace", "identifier of the namespace").DataType("string")).
|
||||
Param(ws.PathParameter("appname", "identifier of the oam application").DataType("string")).
|
||||
Param(ws.QueryParameter("dryRun", "When present, indicates that modifications should not be persisted. "+
|
||||
"An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. "+
|
||||
"Valid values are: - All: all dry run stages will be processed").DataType("string").Required(false)).
|
||||
Reads(apis.ApplicationRequest{}))
|
||||
|
||||
ws.Route(ws.DELETE("/namespaces/{namespace}/applications/{appname}").To(c.deleteApplication).
|
||||
@@ -101,11 +104,23 @@ func (c *oamApplicationAPIInterface) createOrUpdateApplication(req *restful.Requ
|
||||
return
|
||||
}
|
||||
|
||||
err := c.OamApplicationService.CreateOrUpdateOAMApplication(req.Request.Context(), createReq, appName, namespace)
|
||||
if err != nil {
|
||||
log.Logger.Errorf("create application failure %s", err.Error())
|
||||
bcode.ReturnError(req, res, err)
|
||||
return
|
||||
dryRun := req.QueryParameter("dryRun")
|
||||
if len(dryRun) != 0 {
|
||||
if dryRun != "All" {
|
||||
bcode.ReturnError(req, res, bcode.ErrApplicationDryRunFailed.SetMessage("Invalid dryRun parameter. Must be 'All'"))
|
||||
return
|
||||
}
|
||||
if err := c.OamApplicationService.DryRunOAMApplication(req.Request.Context(), createReq, appName, namespace); err != nil {
|
||||
log.Logger.Errorf("dryrun application failure %s", err.Error())
|
||||
bcode.ReturnError(req, res, err)
|
||||
return
|
||||
}
|
||||
} else {
|
||||
if err := c.OamApplicationService.CreateOrUpdateOAMApplication(req.Request.Context(), createReq, appName, namespace); err != nil {
|
||||
log.Logger.Errorf("create application failure %s", err.Error())
|
||||
bcode.ReturnError(req, res, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if err := res.WriteEntity(apis.EmptyResponse{}); err != nil {
|
||||
|
||||
@@ -97,4 +97,33 @@ var _ = Describe("Test oam application rest api", func() {
|
||||
Expect(res).ShouldNot(BeNil())
|
||||
Expect(cmp.Diff(res.StatusCode, 200)).Should(BeEmpty())
|
||||
})
|
||||
|
||||
It("Test dryRun create and update oam app", func() {
|
||||
defer GinkgoRecover()
|
||||
By("test dryRun create app")
|
||||
|
||||
Expect(common.ReadYamlToObject("./testdata/dryrun-app.yaml", &app)).Should(BeNil())
|
||||
req := apiv1.ApplicationRequest{
|
||||
Components: app.Spec.Components,
|
||||
Policies: app.Spec.Policies,
|
||||
Workflow: app.Spec.Workflow,
|
||||
}
|
||||
res := post(fmt.Sprintf("/v1/namespaces/%s/applications/%s?dryRun=All", namespace, appName), req)
|
||||
Expect(res).ShouldNot(BeNil())
|
||||
Expect(cmp.Diff(res.StatusCode, 200)).Should(BeEmpty())
|
||||
Expect(res.Body).ShouldNot(BeNil())
|
||||
defer res.Body.Close()
|
||||
|
||||
By("test dryRun update app")
|
||||
updateReq := apiv1.ApplicationRequest{
|
||||
Components: app.Spec.Components[1:],
|
||||
}
|
||||
Eventually(func(g Gomega) {
|
||||
res = post(fmt.Sprintf("/v1/namespaces/%s/applications/%s?dryRun=All", namespace, appName), updateReq)
|
||||
g.Expect(res).ShouldNot(BeNil())
|
||||
g.Expect(cmp.Diff(res.StatusCode, 200)).Should(BeEmpty())
|
||||
g.Expect(res.Body).ShouldNot(BeNil())
|
||||
defer res.Body.Close()
|
||||
}, time.Minute).Should(Succeed())
|
||||
})
|
||||
})
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
apiVersion: core.oam.dev/v1beta1
|
||||
kind: Application
|
||||
metadata:
|
||||
name: app-dryrun
|
||||
namespace: default
|
||||
spec:
|
||||
components:
|
||||
- name: hello-world
|
||||
type: webservice
|
||||
properties:
|
||||
image: crccheck/hello-world
|
||||
port: 8000
|
||||
traits:
|
||||
- type: scaler
|
||||
properties:
|
||||
replicas: 1
|
||||
Reference in New Issue
Block a user