diff --git a/.github/workflows/apiserver-test.yaml b/.github/workflows/apiserver-test.yaml index 2200be360..75dc769b6 100644 --- a/.github/workflows/apiserver-test.yaml +++ b/.github/workflows/apiserver-test.yaml @@ -5,11 +5,13 @@ on: branches: - master - release-* + - apiserver workflow_dispatch: {} pull_request: branches: - master - release-* + - apiserver env: # Common versions diff --git a/pkg/apiserver/rest/usecase/application_test.go b/pkg/apiserver/rest/usecase/application_test.go index 72cdee439..94f9285f3 100644 --- a/pkg/apiserver/rest/usecase/application_test.go +++ b/pkg/apiserver/rest/usecase/application_test.go @@ -48,7 +48,7 @@ var _ = Describe("Test application usecase function", func() { kubeClient: k8sClient, } }) - It("Test CreateApplication funtion", func() { + It("Test CreateApplication function", func() { By("test sample create") req := v1.CreateApplicationRequest{ Name: "test-app", @@ -106,13 +106,13 @@ var _ = Describe("Test application usecase function", func() { Expect(equal).Should(BeTrue()) }) - It("Test ListApplications funtion", func() { + It("Test ListApplications function", func() { apps, err := appUsecase.ListApplications(context.TODO()) Expect(err).Should(BeNil()) Expect(cmp.Diff(len(apps), 2)).Should(BeEmpty()) }) - It("Test DetailApplication funtion", func() { + It("Test DetailApplication function", func() { appModel, err := appUsecase.GetApplication(context.TODO(), "test-app-sadasd") Expect(err).Should(BeNil()) Expect(cmp.Diff(appModel.Namespace, "test-app-namespace")).Should(BeEmpty()) @@ -123,7 +123,7 @@ var _ = Describe("Test application usecase function", func() { Expect(cmp.Diff(len(detail.Policies), 1)).Should(BeEmpty()) }) - It("Test GetWorkflow funtion", func() { + It("Test GetWorkflow function", func() { appModel, err := appUsecase.GetApplication(context.TODO(), "test-app-sadasd") Expect(err).Should(BeNil()) Expect(cmp.Diff(appModel.Namespace, "test-app-namespace")).Should(BeEmpty()) @@ -134,7 +134,7 @@ var _ = Describe("Test application usecase function", func() { Expect(cmp.Diff(detail.Enable, true)).Should(BeEmpty()) }) - It("Test ListPolicies funtion", func() { + It("Test ListPolicies function", func() { appModel, err := appUsecase.GetApplication(context.TODO(), "test-app-sadasd") Expect(err).Should(BeNil()) Expect(cmp.Diff(appModel.Namespace, "test-app-namespace")).Should(BeEmpty()) @@ -146,7 +146,7 @@ var _ = Describe("Test application usecase function", func() { Expect((*policies[0].Properties)["envs"]).ShouldNot(BeEmpty()) }) - It("Test ListComponents funtion", func() { + It("Test ListComponents function", func() { appModel, err := appUsecase.GetApplication(context.TODO(), "test-app-sadasd") Expect(err).Should(BeNil()) Expect(cmp.Diff(appModel.Namespace, "test-app-namespace")).Should(BeEmpty()) @@ -158,7 +158,7 @@ var _ = Describe("Test application usecase function", func() { Expect(components[1].UpdateTime).ShouldNot(BeNil()) }) - It("Test DetailComponent funtion", func() { + It("Test DetailComponent function", func() { appModel, err := appUsecase.GetApplication(context.TODO(), "test-app-sadasd") Expect(err).Should(BeNil()) Expect(cmp.Diff(appModel.Namespace, "test-app-namespace")).Should(BeEmpty()) @@ -170,7 +170,7 @@ var _ = Describe("Test application usecase function", func() { Expect(cmp.Diff(strings.Contains((*detail.Properties)["image"].(string), "crccheck/hello-world"), true)).Should(BeEmpty()) }) - It("Test DetailPolicy funtion", func() { + It("Test DetailPolicy function", func() { appModel, err := appUsecase.GetApplication(context.TODO(), "test-app-sadasd") Expect(err).Should(BeNil()) Expect(cmp.Diff(appModel.Namespace, "test-app-namespace")).Should(BeEmpty()) @@ -180,7 +180,7 @@ var _ = Describe("Test application usecase function", func() { Expect(cmp.Diff(detail.Type, "env-binding")).Should(BeEmpty()) Expect((*detail.Properties)["envs"]).ShouldNot(BeEmpty()) }) - It("Test AddComponent funtion", func() { + It("Test AddComponent function", func() { appModel, err := appUsecase.GetApplication(context.TODO(), "test-app-sadasd") Expect(err).Should(BeNil()) Expect(cmp.Diff(appModel.Namespace, "test-app-namespace")).Should(BeEmpty()) @@ -195,7 +195,7 @@ var _ = Describe("Test application usecase function", func() { Expect(err).Should(BeNil()) Expect(cmp.Diff(base.ComponentType, "worker")).Should(BeEmpty()) }) - It("Test DetailComponent funtion", func() { + It("Test DetailComponent function", func() { appModel, err := appUsecase.GetApplication(context.TODO(), "test-app-sadasd") Expect(err).Should(BeNil()) Expect(cmp.Diff(appModel.Namespace, "test-app-namespace")).Should(BeEmpty()) @@ -206,7 +206,7 @@ var _ = Describe("Test application usecase function", func() { Expect(cmp.Diff((*detailResponse.Properties)["image"], "busybox")).Should(BeEmpty()) }) - It("Test AddPolicy funtion", func() { + It("Test AddPolicy function", func() { appModel, err := appUsecase.GetApplication(context.TODO(), "test-app-sadasd") Expect(err).Should(BeNil()) Expect(cmp.Diff(appModel.Namespace, "test-app-namespace")).Should(BeEmpty()) @@ -225,7 +225,7 @@ var _ = Describe("Test application usecase function", func() { }) Expect(err).Should(BeNil()) }) - It("Test DetailPolicy funtion", func() { + It("Test DetailPolicy function", func() { appModel, err := appUsecase.GetApplication(context.TODO(), "test-app-sadasd") Expect(err).Should(BeNil()) Expect(cmp.Diff(appModel.Namespace, "test-app-namespace")).Should(BeEmpty()) @@ -234,7 +234,7 @@ var _ = Describe("Test application usecase function", func() { Expect(detail.Properties).ShouldNot(BeNil()) Expect((*detail.Properties)["envs"]).ShouldNot(BeEmpty()) }) - It("Test UpdatePolicy funtion", func() { + It("Test UpdatePolicy function", func() { appModel, err := appUsecase.GetApplication(context.TODO(), "test-app-sadasd") Expect(err).Should(BeNil()) Expect(cmp.Diff(appModel.Namespace, "test-app-namespace")).Should(BeEmpty()) @@ -246,21 +246,21 @@ var _ = Describe("Test application usecase function", func() { Expect(base.Properties).ShouldNot(BeNil()) Expect((*base.Properties)["envs"]).Should(BeEmpty()) }) - It("Test DeletePolicy funtion", func() { + It("Test DeletePolicy function", func() { appModel, err := appUsecase.GetApplication(context.TODO(), "test-app-sadasd") Expect(err).Should(BeNil()) Expect(cmp.Diff(appModel.Namespace, "test-app-namespace")).Should(BeEmpty()) err = appUsecase.DeletePolicy(context.TODO(), appModel, "env-binding-2") Expect(err).Should(BeNil()) }) - It("Test DeleteComponent funtion", func() { + It("Test DeleteComponent function", func() { appModel, err := appUsecase.GetApplication(context.TODO(), "test-app-sadasd") Expect(err).Should(BeNil()) Expect(cmp.Diff(appModel.Namespace, "test-app-namespace")).Should(BeEmpty()) err = appUsecase.DeleteComponent(context.TODO(), appModel, "test2") Expect(err).Should(BeNil()) }) - It("Test Deploy Application funtion", func() { + It("Test Deploy Application function", func() { appModel, err := appUsecase.GetApplication(context.TODO(), "test-app-sadasd") Expect(err).Should(BeNil()) Expect(cmp.Diff(appModel.Namespace, "test-app-namespace")).Should(BeEmpty()) @@ -277,7 +277,7 @@ var _ = Describe("Test application usecase function", func() { Expect(cmp.Diff(len(oam.Spec.Components), 2)).Should(BeEmpty()) Expect(cmp.Diff(len(oam.Spec.Policies), 1)).Should(BeEmpty()) }) - It("Test DeleteApplication funtion", func() { + It("Test DeleteApplication function", func() { appModel, err := appUsecase.GetApplication(context.TODO(), "test-app-sadasd") Expect(err).Should(BeNil()) err = appUsecase.DeleteApplication(context.TODO(), appModel) diff --git a/pkg/apiserver/rest/usecase/oam_application.go b/pkg/apiserver/rest/usecase/oam_application.go new file mode 100644 index 000000000..6005c9f3d --- /dev/null +++ b/pkg/apiserver/rest/usecase/oam_application.go @@ -0,0 +1,109 @@ +/* + 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/oam-dev/kubevela/pkg/apiserver/clients" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + v1 "k8s.io/api/core/v1" + kerrors "k8s.io/apimachinery/pkg/api/errors" + "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/rest/apis/v1" +) + +// OAMApplicationUsecase oam_application usecase +type OAMApplicationUsecase interface { + CreateOrUpdateOAMApplication(context.Context, apisv1.ApplicationRequest, string, string) error + GetOAMApplication(context.Context, string, string) (*apisv1.ApplicationResponse, error) + DeleteOAMApplication(context.Context, string, string) error +} + +// NewOAMApplicationUsecase new oam_application usecase +func NewOAMApplicationUsecase() OAMApplicationUsecase { + kubeClient, _ := clients.GetKubeClient() + return &oamApplicationUsecaseImpl{kubeClient: kubeClient} +} + +type oamApplicationUsecaseImpl struct { + kubeClient client.Client +} + +// CreateOrUpdateOAMApplication create or update application +func (o oamApplicationUsecaseImpl) CreateOrUpdateOAMApplication(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{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: namespace, + }, + Spec: v1beta1.ApplicationSpec{ + Components: request.Components, + Policies: request.Policies, + Workflow: request.Workflow, + }, + } + + 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) + } + return err + } + + existApp.Spec = app.Spec + return o.kubeClient.Update(ctx, existApp) +} + +// GetOAMApplication get application +func (o oamApplicationUsecaseImpl) GetOAMApplication(ctx context.Context, name, namespace string) (*apisv1.ApplicationResponse, error) { + app := new(v1beta1.Application) + if err := o.kubeClient.Get(ctx, client.ObjectKey{Name: name, Namespace: namespace}, app); err != nil { + return nil, err + } + return &apisv1.ApplicationResponse{ + APIVersion: app.APIVersion, + Kind: app.Kind, + Spec: app.Spec, + Status: app.Status, + }, nil +} + +// DeleteOAMApplication delete application +func (o oamApplicationUsecaseImpl) DeleteOAMApplication(ctx context.Context, name, namespace string) error { + return client.IgnoreNotFound(o.kubeClient.Delete(ctx, &v1beta1.Application{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: namespace, + }, + })) +} diff --git a/pkg/apiserver/rest/usecase/oam_application_test.go b/pkg/apiserver/rest/usecase/oam_application_test.go new file mode 100644 index 000000000..a8fff0087 --- /dev/null +++ b/pkg/apiserver/rest/usecase/oam_application_test.go @@ -0,0 +1,120 @@ +/* + 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" + "fmt" + "time" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + corev1 "k8s.io/api/core/v1" + kerrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "sigs.k8s.io/controller-runtime/pkg/client" + + "github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1" + apiv1 "github.com/oam-dev/kubevela/pkg/apiserver/rest/apis/v1" + "github.com/oam-dev/kubevela/pkg/oam/util" + "github.com/oam-dev/kubevela/pkg/utils/common" +) + +var _ = Describe("Test oam application usecase function", func() { + var oamAppUsecase *oamApplicationUsecaseImpl + var ctx context.Context + var baseApp v1beta1.Application + var ns corev1.Namespace + var namespace string + + BeforeEach(func() { + ctx = context.Background() + namespace = randomNamespaceName("test-oam-app") + ns = corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: namespace}} + oamAppUsecase = &oamApplicationUsecaseImpl{ + kubeClient: k8sClient, + } + Expect(common.ReadYamlToObject("./testdata/example-app.yaml", &baseApp)).Should(BeNil()) + + Eventually(func() error { + return k8sClient.Create(ctx, &ns) + }, time.Second*3, time.Microsecond*300).Should(SatisfyAny(BeNil(), &util.AlreadyExistMatcher{})) + + baseApp.SetNamespace(namespace) + Eventually(func() error { + return k8sClient.Create(ctx, &baseApp) + }, time.Second*3, time.Microsecond*300).Should(SatisfyAny(BeNil(), &util.AlreadyExistMatcher{})) + }) + + AfterEach(func() { + By("Clean up resources after a test") + k8sClient.DeleteAllOf(ctx, &v1beta1.Application{}, client.InNamespace(namespace)) + baseApp = v1beta1.Application{} + By(fmt.Sprintf("Delete the entire namespaceName %s", ns.Name)) + Expect(k8sClient.Delete(ctx, &ns, client.PropagationPolicy(metav1.DeletePropagationForeground))).Should(Succeed()) + }) + + It("Test CreateOrUpdateOAMApplication function", func() { + By("test create application") + appName := "test-new-app" + appNs := randomNamespaceName("test-new-app") + req := apiv1.ApplicationRequest{ + Components: baseApp.Spec.Components, + Policies: baseApp.Spec.Policies, + Workflow: baseApp.Spec.Workflow, + } + Expect(oamAppUsecase.CreateOrUpdateOAMApplication(ctx, req, appName, appNs)).Should(BeNil()) + + app := new(v1beta1.Application) + Expect(k8sClient.Get(ctx, client.ObjectKey{Namespace: appNs, Name: appName}, app)).Should(BeNil()) + Expect(app.Spec.Components).Should(Equal(req.Components)) + Expect(app.Spec.Policies).Should(Equal(req.Policies)) + Expect(app.Spec.Workflow).Should(Equal(req.Workflow)) + + By("test update application") + updateReq := apiv1.ApplicationRequest{ + Components: baseApp.Spec.Components[1:], + } + Expect(oamAppUsecase.CreateOrUpdateOAMApplication(ctx, updateReq, appName, appNs)).Should(BeNil()) + + updatedApp := new(v1beta1.Application) + Expect(k8sClient.Get(ctx, client.ObjectKey{Namespace: appNs, Name: appName}, updatedApp)).Should(BeNil()) + Expect(updatedApp.Spec.Components).Should(Equal(updateReq.Components)) + Expect(updatedApp.Spec.Policies).Should(BeNil()) + Expect(updatedApp.Spec.Workflow).Should(BeNil()) + }) + + It("Test GetOAMApplication function", func() { + By("test get an existed application") + resp, err := oamAppUsecase.GetOAMApplication(ctx, baseApp.Name, namespace) + Expect(err).Should(BeNil()) + + Expect(resp.Spec.Components).Should(Equal(baseApp.Spec.Components)) + Expect(resp.Spec.Policies).Should(Equal(baseApp.Spec.Policies)) + Expect(resp.Spec.Workflow).Should(Equal(baseApp.Spec.Workflow)) + }) + + It("Test DeleteOAMApplication function", func() { + By("test delete application") + app := new(v1beta1.Application) + Expect(k8sClient.Get(ctx, client.ObjectKey{Namespace: namespace, Name: baseApp.Name}, app)).Should(BeNil()) + + Expect(oamAppUsecase.DeleteOAMApplication(ctx, baseApp.Name, namespace)).Should(BeNil()) + err := k8sClient.Get(ctx, client.ObjectKey{Namespace: namespace, Name: baseApp.Name}, app) + Expect(kerrors.IsNotFound(err)).Should(BeTrue()) + }) +}) diff --git a/pkg/apiserver/rest/usecase/usecase_suite_test.go b/pkg/apiserver/rest/usecase/usecase_suite_test.go index a58e48a77..e77f33b77 100644 --- a/pkg/apiserver/rest/usecase/usecase_suite_test.go +++ b/pkg/apiserver/rest/usecase/usecase_suite_test.go @@ -20,6 +20,7 @@ import ( "context" "fmt" "math/rand" + "strconv" "testing" "time" @@ -100,3 +101,7 @@ func TestUsecase(t *testing.T) { RegisterFailHandler(Fail) RunSpecs(t, "Usecase Suite") } + +func randomNamespaceName(basic string) string { + return fmt.Sprintf("%s-%s", basic, strconv.FormatInt(rand.Int63(), 16)) +} diff --git a/pkg/apiserver/rest/webservice/oam_application.go b/pkg/apiserver/rest/webservice/oam_application.go index 7e19b3648..55725ae3a 100644 --- a/pkg/apiserver/rest/webservice/oam_application.go +++ b/pkg/apiserver/rest/webservice/oam_application.go @@ -18,12 +18,23 @@ package webservice import ( restfulspec "github.com/emicklei/go-restful-openapi/v2" - restful "github.com/emicklei/go-restful/v3" + "github.com/emicklei/go-restful/v3" + "github.com/oam-dev/kubevela/pkg/apiserver/log" 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/bcode" ) type oamApplicationWebService struct { + oamApplicationUsecase usecase.OAMApplicationUsecase +} + +// NewOAMApplication new oam application +func NewOAMApplication(oamApplicationUsecase usecase.OAMApplicationUsecase) WebService { + return &oamApplicationWebService{ + oamApplicationUsecase: oamApplicationUsecase, + } } func (c *oamApplicationWebService) GetWebService() *restful.WebService { @@ -35,24 +46,82 @@ func (c *oamApplicationWebService) GetWebService() *restful.WebService { tags := []string{"oam"} - ws.Route(ws.GET("/{namespace}/applications/:appname").To(noop). + ws.Route(ws.GET("/namespaces/{namespace}/applications/{appname}").To(c.getApplication). Doc("get the specified oam application in the specified namespace"). Metadata(restfulspec.KeyOpenAPITags, tags). Param(ws.PathParameter("namespace", "identifier of the namespace").DataType("string")). Param(ws.PathParameter("appname", "identifier of the oam application").DataType("string")). Writes(apis.ApplicationResponse{})) - ws.Route(ws.POST("/{namespace}/applications/{appname}").To(noop). + ws.Route(ws.POST("/namespaces/{namespace}/applications/{appname}").To(c.createOrUpdateApplication). Doc("create or update oam application in the specified namespace"). Metadata(restfulspec.KeyOpenAPITags, tags). Param(ws.PathParameter("namespace", "identifier of the namespace").DataType("string")). Param(ws.PathParameter("appname", "identifier of the oam application").DataType("string")). Reads(apis.ApplicationRequest{})) - ws.Route(ws.DELETE("/{namespace}/applications/:appname").To(noop). + ws.Route(ws.DELETE("/namespaces/{namespace}/applications/{appname}").To(c.deleteApplication). Doc("create or update oam application in the specified namespace"). Metadata(restfulspec.KeyOpenAPITags, tags). Param(ws.PathParameter("namespace", "identifier of the namespace").DataType("string")). Param(ws.PathParameter("appname", "identifier of the oam application").DataType("string"))) + return ws } + +func (c *oamApplicationWebService) getApplication(req *restful.Request, res *restful.Response) { + namespace := req.PathParameter("namespace") + appName := req.PathParameter("appname") + appRes, err := c.oamApplicationUsecase.GetOAMApplication(req.Request.Context(), appName, namespace) + if err != nil { + log.Logger.Errorf("get application failure %s", err.Error()) + bcode.ReturnError(req, res, err) + return + } + + // Write back response data + if err := res.WriteEntity(appRes); err != nil { + bcode.ReturnError(req, res, err) + return + } +} + +func (c *oamApplicationWebService) createOrUpdateApplication(req *restful.Request, res *restful.Response) { + namespace := req.PathParameter("namespace") + appName := req.PathParameter("appname") + + var createReq apis.ApplicationRequest + if err := req.ReadEntity(&createReq); err != nil { + bcode.ReturnError(req, res, err) + return + } + + err := c.oamApplicationUsecase.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 + } + + if err := res.WriteEntity(apis.EmptyResponse{}); err != nil { + bcode.ReturnError(req, res, err) + return + } +} + +func (c *oamApplicationWebService) deleteApplication(req *restful.Request, res *restful.Response) { + namespace := req.PathParameter("namespace") + appName := req.PathParameter("appname") + + err := c.oamApplicationUsecase.DeleteOAMApplication(req.Request.Context(), appName, namespace) + if err != nil { + log.Logger.Errorf("delete application failure %s", err.Error()) + bcode.ReturnError(req, res, err) + return + } + + if err := res.WriteEntity(apis.EmptyResponse{}); err != nil { + bcode.ReturnError(req, res, err) + return + } +} diff --git a/pkg/apiserver/rest/webservice/webservice.go b/pkg/apiserver/rest/webservice/webservice.go index 65bec77f1..8681550bf 100644 --- a/pkg/apiserver/rest/webservice/webservice.go +++ b/pkg/apiserver/rest/webservice/webservice.go @@ -62,12 +62,13 @@ func Init(ctx context.Context, ds datastore.DataStore) { clusterUsecase := usecase.NewClusterUsecase(ds) workflowUsecase := usecase.NewWorkflowUsecase(ds) applicationUsecase := usecase.NewApplicationUsecase(ds, workflowUsecase) + oamApplicationUsecase := usecase.NewOAMApplicationUsecase() RegistWebService(NewClusterWebService(clusterUsecase)) RegistWebService(NewApplicationWebService(applicationUsecase)) RegistWebService(&namespaceWebService{}) RegistWebService(&componentDefinitionWebservice{}) RegistWebService(&addonWebService{}) - RegistWebService(&oamApplicationWebService{}) + RegistWebService(NewOAMApplication(oamApplicationUsecase)) RegistWebService(&policyDefinitionWebservice{}) RegistWebService(NewWorkflowWebService(workflowUsecase, applicationUsecase)) } diff --git a/test/e2e-apiserver-test/oam_application_test.go b/test/e2e-apiserver-test/oam_application_test.go new file mode 100644 index 000000000..4048541df --- /dev/null +++ b/test/e2e-apiserver-test/oam_application_test.go @@ -0,0 +1,120 @@ +/* + 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 e2e_apiserver_test + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "net/http" + + "github.com/google/go-cmp/cmp" + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + "sigs.k8s.io/controller-runtime/pkg/client" + + "github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1" + apiv1 "github.com/oam-dev/kubevela/pkg/apiserver/rest/apis/v1" + "github.com/oam-dev/kubevela/pkg/utils/common" +) + +var _ = Describe("Test oam application rest api", func() { + namespace := "test-oam-app" + appName := "example-app" + var app v1beta1.Application + + It("Test create and update oam app", func() { + defer GinkgoRecover() + By("test create app") + + Expect(common.ReadYamlToObject("./testdata/example-app.yaml", &app)).Should(BeNil()) + req := apiv1.ApplicationRequest{ + Components: app.Spec.Components, + Policies: app.Spec.Policies, + Workflow: app.Spec.Workflow, + } + bodyByte, err := json.Marshal(req) + Expect(err).Should(BeNil()) + res, err := http.Post( + fmt.Sprintf("http://127.0.0.1:8000/v1/namespaces/%s/applications/%s", namespace, appName), + "application/json", + bytes.NewBuffer(bodyByte), + ) + Expect(err).ShouldNot(HaveOccurred()) + Expect(res).ShouldNot(BeNil()) + Expect(cmp.Diff(res.StatusCode, 200)).Should(BeEmpty()) + Expect(res.Body).ShouldNot(BeNil()) + defer res.Body.Close() + + ctx := context.Background() + oldApp := new(v1beta1.Application) + Expect(k8sClient.Get(ctx, client.ObjectKey{Name: appName, Namespace: namespace}, oldApp)).Should(BeNil()) + Expect(oldApp.Spec.Components).Should(Equal(req.Components)) + Expect(oldApp.Spec.Policies).Should(Equal(req.Policies)) + Expect(oldApp.Spec.Workflow).Should(Equal(req.Workflow)) + + By("test update app") + updateReq := apiv1.ApplicationRequest{ + Components: app.Spec.Components[1:], + } + bodyByte, err = json.Marshal(updateReq) + Expect(err).Should(BeNil()) + res, err = http.Post( + fmt.Sprintf("http://127.0.0.1:8000/v1/namespaces/%s/applications/%s", namespace, appName), + "application/json", + bytes.NewBuffer(bodyByte), + ) + Expect(err).ShouldNot(HaveOccurred()) + Expect(res).ShouldNot(BeNil()) + Expect(cmp.Diff(res.StatusCode, 200)).Should(BeEmpty()) + Expect(res.Body).ShouldNot(BeNil()) + defer res.Body.Close() + + newApp := new(v1beta1.Application) + Expect(k8sClient.Get(ctx, client.ObjectKey{Name: appName, Namespace: namespace}, newApp)).Should(BeNil()) + Expect(newApp.Spec.Components).Should(Equal(updateReq.Components)) + Expect(newApp.Spec.Policies).Should(BeNil()) + Expect(newApp.Spec.Workflow).Should(BeNil()) + }) + + It("Test get oam app", func() { + defer GinkgoRecover() + res, err := http.Get( + fmt.Sprintf("http://127.0.0.1:8000/v1/namespaces/%s/applications/%s", namespace, appName), + ) + Expect(err).ShouldNot(HaveOccurred()) + Expect(res).ShouldNot(BeNil()) + + defer res.Body.Close() + var appResp apiv1.ApplicationResponse + err = json.NewDecoder(res.Body).Decode(&appResp) + Expect(err).ShouldNot(HaveOccurred()) + + Expect(len(appResp.Spec.Components)).Should(Equal(1)) + }) + + It("Test delete oam app", func() { + defer GinkgoRecover() + req, err := http.NewRequest(http.MethodDelete, fmt.Sprintf("http://127.0.0.1:8000/v1/namespaces/%s/applications/%s", namespace, appName), nil) + Expect(err).ShouldNot(HaveOccurred()) + res, err := http.DefaultClient.Do(req) + Expect(err).ShouldNot(HaveOccurred()) + Expect(res).ShouldNot(BeNil()) + Expect(cmp.Diff(res.StatusCode, 200)).Should(BeEmpty()) + }) +})