Merge branch 'apiserver' into merge

This commit is contained in:
Hongchao Deng
2021-11-22 17:25:50 +08:00
177 changed files with 25065 additions and 3350 deletions
+137
View File
@@ -0,0 +1,137 @@
package e2e_apiserver_test
import (
"bytes"
"context"
"encoding/json"
"fmt"
"net/http"
"os"
"time"
"github.com/pkg/errors"
"sigs.k8s.io/controller-runtime/pkg/client"
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
"github.com/oam-dev/kubevela/pkg/addon"
apis "github.com/oam-dev/kubevela/pkg/apiserver/rest/apis/v1"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)
const baseURL = "http://127.0.0.1:8000"
func post(path string, body interface{}) *http.Response {
b, err := json.Marshal(body)
Expect(err).Should(BeNil())
res, err := http.Post(baseURL+path, "application/json", bytes.NewBuffer(b))
Expect(err).Should(BeNil())
return res
}
func get(path string) *http.Response {
res, err := http.Get(baseURL + path)
Expect(err).Should(BeNil())
return res
}
var _ = Describe("Test addon rest api", func() {
createReq := apis.CreateAddonRegistryRequest{
Name: "test-addon-registry-1",
Git: &addon.GitAddonSource{
URL: "https://github.com/oam-dev/catalog",
Path: "addons/",
Token: os.Getenv("GITHUB_TOKEN"),
},
}
It("should add a registry and list addons from it", func() {
defer GinkgoRecover()
By("add registry")
createRes := post("/api/v1/addon_registries", createReq)
Expect(createRes).ShouldNot(BeNil())
Expect(createRes.Body).ShouldNot(BeNil())
Expect(createRes.StatusCode).Should(Equal(200))
defer createRes.Body.Close()
var rmeta apis.AddonRegistryMeta
err := json.NewDecoder(createRes.Body).Decode(&rmeta)
Expect(err).Should(BeNil())
Expect(rmeta.Name).Should(Equal(createReq.Name))
Expect(rmeta.Git).Should(Equal(createReq.Git))
By("list addons")
listRes := get("/api/v1/addons/")
defer listRes.Body.Close()
var lres apis.ListAddonResponse
err = json.NewDecoder(listRes.Body).Decode(&lres)
Expect(err).Should(BeNil())
Expect(lres.Addons).ShouldNot(BeZero())
firstAddon := lres.Addons[0]
Expect(firstAddon.Name).Should(Equal("example"))
})
It("should enable and disable an addon", func() {
defer GinkgoRecover()
req := apis.EnableAddonRequest{
Args: map[string]string{
"example": "test-args",
},
}
testAddon := "example"
res := post("/api/v1/addons/"+testAddon+"/enable", req)
Expect(res).ShouldNot(BeNil())
Expect(res.StatusCode).Should(Equal(200))
Expect(res.Body).ShouldNot(BeNil())
defer res.Body.Close()
var statusRes apis.AddonStatusResponse
err := json.NewDecoder(res.Body).Decode(&statusRes)
Expect(err).Should(BeNil())
Expect(statusRes.Phase).Should(Equal(apis.AddonPhaseEnabling))
// Wait for addon enabled
period := 10 * time.Second
timeout := 2 * time.Minute
Eventually(func() error {
res = get("/api/v1/addons/" + testAddon + "/status")
err = json.NewDecoder(res.Body).Decode(&statusRes)
Expect(err).Should(BeNil())
if statusRes.Phase == apis.AddonPhaseEnabled {
return nil
}
var app v1beta1.Application
err = k8sClient.Get(context.Background(), client.ObjectKey{Name: "addon-example", Namespace: "vela-system"}, &app)
Expect(err).Should(BeNil())
data, err := json.Marshal(app)
Expect(err).Should(BeNil())
fmt.Println(data)
return errors.New("not ready")
}, timeout, period).Should(BeNil())
res = post("/api/v1/addons/"+testAddon+"/disable", req)
Expect(res).ShouldNot(BeNil())
Expect(res.StatusCode).Should(Equal(200))
Expect(res.Body).ShouldNot(BeNil())
err = json.NewDecoder(res.Body).Decode(&statusRes)
Expect(err).Should(BeNil())
})
It("should delete test registry", func() {
defer GinkgoRecover()
deleteReq, err := http.NewRequest(http.MethodDelete, baseURL+"/api/v1/addon_registries/"+createReq.Name, nil)
Expect(err).Should(BeNil())
deleteRes, err := http.DefaultClient.Do(deleteReq)
Expect(err).Should(BeNil())
Expect(deleteRes).ShouldNot(BeNil())
Expect(deleteRes.StatusCode).Should(Equal(200))
})
})
+314 -3
View File
@@ -18,26 +18,34 @@ package e2e_apiserver_test
import (
"bytes"
"context"
"encoding/json"
"io/ioutil"
"net/http"
"github.com/google/go-cmp/cmp"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"k8s.io/apimachinery/pkg/types"
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
"github.com/oam-dev/kubevela/pkg/apiserver/model"
apisv1 "github.com/oam-dev/kubevela/pkg/apiserver/rest/apis/v1"
)
var appName = "app-e2e"
var appProject = "test-app-project"
var _ = Describe("Test application rest api", func() {
It("Test create app", func() {
defer GinkgoRecover()
var req = apisv1.CreateApplicationRequest{
Name: "test-app-sadasd",
Namespace: "test-app-namesapce",
Name: appName,
Namespace: appProject,
Description: "this is a test app",
Icon: "",
Labels: map[string]string{"test": "true"},
ClusterList: []string{},
EnvBinding: []*apisv1.EnvBinding{{Name: "dev-env", TargetNames: []string{"test-target"}}},
}
bodyByte, err := json.Marshal(req)
Expect(err).ShouldNot(HaveOccurred())
@@ -52,6 +60,309 @@ var _ = Describe("Test application rest api", func() {
Expect(err).ShouldNot(HaveOccurred())
Expect(cmp.Diff(appBase.Name, req.Name)).Should(BeEmpty())
Expect(cmp.Diff(appBase.Description, req.Description)).Should(BeEmpty())
Expect(cmp.Diff(appBase.Namespace, req.Namespace)).Should(BeEmpty())
Expect(cmp.Diff(appBase.Labels["test"], req.Labels["test"])).Should(BeEmpty())
})
It("Test delete app", func() {
defer GinkgoRecover()
req, err := http.NewRequest(http.MethodDelete, "http://127.0.0.1:8000/api/v1/applications/"+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())
})
It("Test create app with oamspec", func() {
defer GinkgoRecover()
bs, err := ioutil.ReadFile("./testdata/example-app.yaml")
Expect(err).Should(Succeed())
var req = apisv1.CreateApplicationRequest{
Name: appName,
Namespace: appProject,
Description: "this is a test app",
Icon: "",
Labels: map[string]string{"test": "true"},
YamlConfig: string(bs),
}
bodyByte, err := json.Marshal(req)
Expect(err).ShouldNot(HaveOccurred())
res, err := http.Post("http://127.0.0.1:8000/api/v1/applications", "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()
var appBase apisv1.ApplicationBase
err = json.NewDecoder(res.Body).Decode(&appBase)
Expect(err).ShouldNot(HaveOccurred())
Expect(cmp.Diff(appBase.Name, req.Name)).Should(BeEmpty())
Expect(cmp.Diff(appBase.Description, req.Description)).Should(BeEmpty())
Expect(cmp.Diff(appBase.Namespace, req.Namespace)).Should(BeEmpty())
Expect(cmp.Diff(appBase.Labels["test"], req.Labels["test"])).Should(BeEmpty())
})
It("Test list components", func() {
defer GinkgoRecover()
res, err := http.Get("http://127.0.0.1:8000/api/v1/applications/" + appName + "/components")
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()
var components apisv1.ComponentListResponse
err = json.NewDecoder(res.Body).Decode(&components)
Expect(err).ShouldNot(HaveOccurred())
Expect(cmp.Diff(len(components.Components), 2)).Should(BeEmpty())
})
It("Test detail application", func() {
defer GinkgoRecover()
res, err := http.Get("http://127.0.0.1:8000/api/v1/applications/" + appName)
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()
var detail apisv1.DetailApplicationResponse
err = json.NewDecoder(res.Body).Decode(&detail)
Expect(err).ShouldNot(HaveOccurred())
Expect(cmp.Diff(len(detail.Policies), 0)).Should(BeEmpty())
})
It("Test deploy application", func() {
defer GinkgoRecover()
var targetName = "dev-default"
var envName = "dev"
var namespace = "default"
// create target
var createTarget = apisv1.CreateDeliveryTargetRequest{
Name: targetName,
Namespace: appProject,
Cluster: &apisv1.ClusterTarget{
ClusterName: "local",
Namespace: namespace,
},
}
bodyByte, err := json.Marshal(createTarget)
Expect(err).ShouldNot(HaveOccurred())
res, err := http.Post("http://127.0.0.1:8000/api/v1/deliveryTargets", "application/json", bytes.NewBuffer(bodyByte))
Expect(err).ShouldNot(HaveOccurred())
Expect(res).ShouldNot(BeNil())
Expect(cmp.Diff(res.StatusCode, 200)).Should(BeEmpty())
// create env
var createEnvReq = apisv1.CreateApplicationEnvRequest{
EnvBinding: apisv1.EnvBinding{
Name: envName,
TargetNames: []string{targetName},
},
}
bodyByte, err = json.Marshal(createEnvReq)
Expect(err).ShouldNot(HaveOccurred())
res, err = http.Post("http://127.0.0.1:8000/api/v1/applications/"+appName+"/envs", "application/json", bytes.NewBuffer(bodyByte))
Expect(err).ShouldNot(HaveOccurred())
Expect(res).ShouldNot(BeNil())
// deploy app
var req = apisv1.ApplicationDeployRequest{
Note: "test apply",
TriggerType: "web",
WorkflowName: "dev",
Force: false,
}
bodyByte, err = json.Marshal(req)
Expect(err).ShouldNot(HaveOccurred())
res, err = http.Post("http://127.0.0.1:8000/api/v1/applications/"+appName+"/deploy", "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()
var response apisv1.ApplicationDeployResponse
err = json.NewDecoder(res.Body).Decode(&response)
Expect(err).ShouldNot(HaveOccurred())
Expect(cmp.Diff(response.Status, model.RevisionStatusRunning)).Should(BeEmpty())
var oam v1beta1.Application
err = k8sClient.Get(context.TODO(), types.NamespacedName{Name: appName + "-" + envName, Namespace: appProject}, &oam)
Expect(err).Should(BeNil())
Expect(cmp.Diff(len(oam.Spec.Components), 2)).Should(BeEmpty())
Expect(cmp.Diff(len(oam.Spec.Policies), 1)).Should(BeEmpty())
})
It("Test create component", func() {
defer GinkgoRecover()
var req = apisv1.CreateComponentRequest{
Name: "test2",
Description: "this is a test2 component",
Labels: map[string]string{},
ComponentType: "worker",
Properties: `{"image": "busybox","cmd":["sleep", "1000"],"lives": "3","enemies": "alien"}`,
DependsOn: []string{"data-worker"},
}
bodyByte, err := json.Marshal(req)
Expect(err).ShouldNot(HaveOccurred())
res, err := http.Post("http://127.0.0.1:8000/api/v1/applications/"+appName+"/components", "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()
var response apisv1.ComponentBase
err = json.NewDecoder(res.Body).Decode(&response)
Expect(err).ShouldNot(HaveOccurred())
Expect(cmp.Diff(response.ComponentType, "worker")).Should(BeEmpty())
})
It("Test detail component", func() {
defer GinkgoRecover()
res, err := http.Get("http://127.0.0.1:8000/api/v1/applications/" + appName + "/components/test2")
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()
var response apisv1.DetailComponentResponse
err = json.NewDecoder(res.Body).Decode(&response)
Expect(err).ShouldNot(HaveOccurred())
Expect(cmp.Diff(len(response.DependsOn), 1)).Should(BeEmpty())
})
It("Test add trait", func() {
defer GinkgoRecover()
var req = apisv1.CreateApplicationTraitRequest{
Type: "ingress",
Properties: `{"domain": "www.test.com"}`,
}
bodyByte, err := json.Marshal(req)
Expect(err).ShouldNot(HaveOccurred())
res, err := http.Post("http://127.0.0.1:8000/api/v1/applications/"+appName+"/components/test2/traits", "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()
var response apisv1.ApplicationTrait
err = json.NewDecoder(res.Body).Decode(&response)
Expect(err).ShouldNot(HaveOccurred())
Expect(cmp.Diff(response.Properties.JSON(), `{"domain":"www.test.com"}`)).Should(BeEmpty())
})
It("Test update trait", func() {
defer GinkgoRecover()
var req2 = apisv1.CreateApplicationTraitRequest{
Type: "ingress",
Properties: `{"domain": "www.test1.com"}`,
}
bodyByte, err := json.Marshal(req2)
Expect(err).ShouldNot(HaveOccurred())
req, err := http.NewRequest(http.MethodPut, "http://127.0.0.1:8000/api/v1/applications/"+appName+"/components/test2/traits/ingress", bytes.NewBuffer(bodyByte))
Expect(err).ShouldNot(HaveOccurred())
req.Header.Set("Content-Type", "application/json")
res, err := http.DefaultClient.Do(req)
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()
var response apisv1.ApplicationTrait
err = json.NewDecoder(res.Body).Decode(&response)
Expect(err).ShouldNot(HaveOccurred())
Expect(cmp.Diff(response.Properties.JSON(), `{"domain":"www.test1.com"}`)).Should(BeEmpty())
})
It("Test delete trait", func() {
defer GinkgoRecover()
req, err := http.NewRequest(http.MethodDelete, "http://127.0.0.1:8000/api/v1/applications/"+appName+"/components/test2/traits/ingress", 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())
})
It("Test create application policy", func() {
defer GinkgoRecover()
var req = apisv1.CreatePolicyRequest{
Name: "test2",
Description: "this is a test2 component",
Properties: `{"image": "busybox","cmd":["sleep", "1000"],"lives": "3","enemies": "alien"}`,
}
bodyByte, err := json.Marshal(req)
Expect(err).ShouldNot(HaveOccurred())
res, err := http.Post("http://127.0.0.1:8000/api/v1/applications/"+appName+"/policies", "application/json", bytes.NewBuffer(bodyByte))
Expect(err).ShouldNot(HaveOccurred())
Expect(res).ShouldNot(BeNil())
Expect(cmp.Diff(res.StatusCode, 400)).Should(BeEmpty())
var req2 = apisv1.CreatePolicyRequest{
Name: "test2",
Description: "this is a test2 policy",
Type: "wqsdasd",
Properties: `{"image": "busybox","cmd":["sleep", "1000"],"lives": "3","enemies": "alien"}`,
}
bodyByte2, err := json.Marshal(req2)
Expect(err).ShouldNot(HaveOccurred())
res, err = http.Post("http://127.0.0.1:8000/api/v1/applications/"+appName+"/policies", "application/json", bytes.NewBuffer(bodyByte2))
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()
var response apisv1.PolicyBase
err = json.NewDecoder(res.Body).Decode(&response)
Expect(err).ShouldNot(HaveOccurred())
Expect(cmp.Diff(response.Type, "wqsdasd")).Should(BeEmpty())
})
It("Test detail application policy", func() {
defer GinkgoRecover()
res, err := http.Get("http://127.0.0.1:8000/api/v1/applications/" + appName + "/policies/test2")
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()
var response apisv1.DetailPolicyResponse
err = json.NewDecoder(res.Body).Decode(&response)
Expect(err).ShouldNot(HaveOccurred())
Expect(cmp.Diff(response.Description, "this is a test2 policy")).Should(BeEmpty())
})
It("Test update application policy", func() {
var req2 = apisv1.UpdatePolicyRequest{
Description: "this is a test2 policy update",
Type: "wqsdasd",
Properties: `{"image": "busybox","cmd":["sleep", "1000"],"lives": "3","enemies": "alien"}`,
}
bodyByte2, err := json.Marshal(req2)
Expect(err).ShouldNot(HaveOccurred())
req, err := http.NewRequest(http.MethodPut, "http://127.0.0.1:8000/api/v1/applications/"+appName+"/policies/test2", bytes.NewBuffer(bodyByte2))
Expect(err).ShouldNot(HaveOccurred())
req.Header.Set("Content-Type", "application/json")
res, err := http.DefaultClient.Do(req)
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()
var response apisv1.PolicyBase
err = json.NewDecoder(res.Body).Decode(&response)
Expect(err).ShouldNot(HaveOccurred())
Expect(cmp.Diff(response.Description, "this is a test2 policy update")).Should(BeEmpty())
})
It("Test delete application policy", func() {
defer GinkgoRecover()
req, err := http.NewRequest(http.MethodDelete, "http://127.0.0.1:8000/api/v1/applications/"+appName+"/policies/test2", 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())
})
})
+157
View File
@@ -0,0 +1,157 @@
/*
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
import (
"fmt"
"io/ioutil"
"net/http"
"os"
"time"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
v1 "github.com/oam-dev/kubevela/pkg/apiserver/rest/apis/v1"
"github.com/oam-dev/kubevela/pkg/multicluster"
util "github.com/oam-dev/kubevela/pkg/utils"
)
const (
WorkerClusterName = "cluster-worker"
WorkerClusterKubeConfigPath = "/tmp/worker.kubeconfig"
)
var _ = Describe("Test cluster rest api", func() {
Context("Test basic cluster CURD", func() {
var clusterName string
BeforeEach(func() {
clusterName = WorkerClusterName + "-" + util.RandomString(8)
kubeconfigBytes, err := ioutil.ReadFile(WorkerClusterKubeConfigPath)
Expect(err).Should(Succeed())
resp, err := CreateRequest(http.MethodPost, "/clusters", v1.CreateClusterRequest{
Name: clusterName,
KubeConfig: string(kubeconfigBytes),
})
Expect(err).Should(Succeed())
Expect(resp.StatusCode).Should(Equal(200))
Expect(resp.Body).ShouldNot(BeNil())
Expect(resp.Body.Close()).Should(Succeed())
})
AfterEach(func() {
resp, err := CreateRequest(http.MethodDelete, "/clusters/"+clusterName, nil)
Expect(err).Should(Succeed())
Expect(resp.StatusCode).Should(Equal(200))
Expect(resp.Body).ShouldNot(BeNil())
Expect(resp.Body.Close()).Should(Succeed())
})
It("Test get cluster", func() {
resp, err := CreateRequest(http.MethodGet, "/clusters/"+clusterName, nil)
clusterResp := &v1.DetailClusterResponse{}
Expect(DecodeResponseBody(resp, err, clusterResp)).Should(Succeed())
Expect(clusterResp.Status).Should(Equal("Healthy"))
})
It("Test list clusters", func() {
resp, err := CreateRequest(http.MethodGet, "/clusters/?page=1&pageSize=5", nil)
clusterResp := &v1.ListClusterResponse{}
Expect(DecodeResponseBody(resp, err, clusterResp)).Should(Succeed())
Expect(len(clusterResp.Clusters) >= 2).Should(BeTrue())
Expect(clusterResp.Clusters[0].Name).Should(Equal(multicluster.ClusterLocalName))
Expect(clusterResp.Clusters[1].Name).Should(Equal(clusterName))
resp, err = CreateRequest(http.MethodGet, "/clusters/?page=1&pageSize=5&query="+WorkerClusterName, nil)
clusterResp = &v1.ListClusterResponse{}
Expect(DecodeResponseBody(resp, err, clusterResp)).Should(Succeed())
Expect(len(clusterResp.Clusters) >= 1).Should(BeTrue())
Expect(clusterResp.Clusters[0].Name).Should(Equal(clusterName))
})
It("Test modify cluster", func() {
kubeconfigBytes, err := ioutil.ReadFile(WorkerClusterKubeConfigPath)
Expect(err).Should(Succeed())
resp, err := CreateRequest(http.MethodPut, "/clusters/"+clusterName, v1.CreateClusterRequest{
Name: clusterName,
KubeConfig: string(kubeconfigBytes),
Description: "Example description",
})
clusterResp := &v1.ClusterBase{}
Expect(DecodeResponseBody(resp, err, clusterResp)).Should(Succeed())
Expect(clusterResp.Description).ShouldNot(Equal(""))
})
It("Test create ns in cluster", func() {
testNamespace := fmt.Sprintf("test-%d", time.Now().Unix())
resp, err := CreateRequest(http.MethodPost, "/clusters/"+clusterName+"/namespaces", v1.CreateClusterNamespaceRequest{Namespace: testNamespace})
Expect(err).Should(Succeed())
nsResp := &v1.CreateClusterNamespaceResponse{}
Expect(DecodeResponseBody(resp, err, nsResp)).Should(Succeed())
Expect(nsResp.Exists).Should(Equal(false))
resp, err = CreateRequest(http.MethodPost, "/clusters/"+clusterName+"/namespaces", v1.CreateClusterNamespaceRequest{Namespace: testNamespace})
Expect(err).Should(Succeed())
nsResp = &v1.CreateClusterNamespaceResponse{}
Expect(DecodeResponseBody(resp, err, nsResp)).Should(Succeed())
Expect(nsResp.Exists).Should(Equal(true))
})
})
PContext("Test cloud cluster rest api", func() {
var clusterName string
BeforeEach(func() {
clusterName = WorkerClusterName + "-" + util.RandomString(8)
})
AfterEach(func() {
resp, err := CreateRequest(http.MethodDelete, "/clusters/"+clusterName, nil)
Expect(err).Should(Succeed())
Expect(resp.StatusCode).Should(Equal(200))
Expect(resp.Body).ShouldNot(BeNil())
Expect(resp.Body.Close()).Should(Succeed())
})
It("Test list aliyun cloud cluster and connect", func() {
AccessKeyID := os.Getenv("ALIYUN_ACCESS_KEY_ID")
AccessKeySecret := os.Getenv("ALIYUN_ACCESS_KEY_SECRET")
resp, err := CreateRequest(http.MethodPost, "/clusters/cloud-clusters/aliyun/?page=1&pageSize=5", v1.AccessKeyRequest{
AccessKeyID: AccessKeyID,
AccessKeySecret: AccessKeySecret,
})
clusterResp := &v1.ListCloudClusterResponse{}
Expect(DecodeResponseBody(resp, err, clusterResp)).Should(Succeed())
Expect(len(clusterResp.Clusters)).ShouldNot(Equal(0))
ClusterID := clusterResp.Clusters[0].ID
resp, err = CreateRequest(http.MethodPost, "/clusters/cloud-clusters/aliyun/connect", v1.ConnectCloudClusterRequest{
AccessKeyID: AccessKeyID,
AccessKeySecret: AccessKeySecret,
ClusterID: ClusterID,
Name: clusterName,
})
clusterBase := &v1.ClusterBase{}
Expect(DecodeResponseBody(resp, err, clusterBase)).Should(Succeed())
Expect(clusterBase.Status).Should(Equal("Healthy"))
})
})
})
@@ -0,0 +1,45 @@
/*
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 (
"encoding/json"
"net/http"
"github.com/google/go-cmp/cmp"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
apisv1 "github.com/oam-dev/kubevela/pkg/apiserver/rest/apis/v1"
)
var _ = Describe("Test definitions rest api", func() {
It("Test list definitions", func() {
defer GinkgoRecover()
res, err := http.Get("http://127.0.0.1:8000/api/v1/definitions?type=component")
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()
var definitions apisv1.ListDefinitionResponse
err = json.NewDecoder(res.Body).Decode(&definitions)
Expect(err).ShouldNot(HaveOccurred())
})
})
+65
View File
@@ -0,0 +1,65 @@
/*
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"
"encoding/json"
"net/http"
"github.com/google/go-cmp/cmp"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
apisv1 "github.com/oam-dev/kubevela/pkg/apiserver/rest/apis/v1"
)
var _ = Describe("Test namespace rest api", func() {
It("Test create namespace", func() {
defer GinkgoRecover()
var req = apisv1.CreateNamespaceRequest{
Name: "dev-team",
Description: "开发环境租户",
}
bodyByte, err := json.Marshal(req)
Expect(err).ShouldNot(HaveOccurred())
res, err := http.Post("http://127.0.0.1:8000/api/v1/namespaces", "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()
var namespaceBase apisv1.NamespaceBase
err = json.NewDecoder(res.Body).Decode(&namespaceBase)
Expect(err).ShouldNot(HaveOccurred())
Expect(cmp.Diff(namespaceBase.Name, req.Name)).Should(BeEmpty())
Expect(cmp.Diff(namespaceBase.Description, req.Description)).Should(BeEmpty())
})
It("Test list namespace", func() {
defer GinkgoRecover()
res, err := http.Get("http://127.0.0.1:8000/api/v1/namespaces")
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()
var namespaces apisv1.ListNamespaceResponse
err = json.NewDecoder(res.Body).Decode(&namespaces)
Expect(err).ShouldNot(HaveOccurred())
})
})
@@ -0,0 +1,122 @@
/*
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"
"time"
"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())
Eventually(func(g Gomega) {
res, err = http.Post(
fmt.Sprintf("http://127.0.0.1:8000/v1/namespaces/%s/applications/%s", namespace, appName),
"application/json",
bytes.NewBuffer(bodyByte),
)
g.Expect(err).ShouldNot(HaveOccurred())
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())
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())
})
})
+25 -38
View File
@@ -18,81 +18,68 @@ package e2e_apiserver_test
import (
"context"
"errors"
"net/http"
"testing"
"time"
"github.com/google/uuid"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/client-go/kubernetes/scheme"
"k8s.io/client-go/rest"
"k8s.io/utils/pointer"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/envtest"
"github.com/oam-dev/kubevela/pkg/apiserver/clients"
"github.com/oam-dev/kubevela/pkg/apiserver/datastore"
arest "github.com/oam-dev/kubevela/pkg/apiserver/rest"
)
var cfg *rest.Config
var k8sClient client.Client
var testEnv *envtest.Environment
var testScheme = runtime.NewScheme()
func TestE2eApiserverTest(t *testing.T) {
RegisterFailHandler(Fail)
RunSpecs(t, "E2eApiserverTest Suite")
}
// Suite test in e2e-apiserver-test relies on the pre-setup kubernetes environment
var _ = BeforeSuite(func() {
By("bootstrapping test environment")
testEnv = &envtest.Environment{
ControlPlaneStartTimeout: time.Minute * 3,
ControlPlaneStopTimeout: time.Minute,
UseExistingCluster: pointer.BoolPtr(false),
}
By("start kube test env")
var err error
cfg, err = testEnv.Start()
Expect(err).ShouldNot(HaveOccurred())
Expect(cfg).ToNot(BeNil())
err = scheme.AddToScheme(testScheme)
Expect(err).NotTo(HaveOccurred())
By("new kube client")
cfg.Timeout = time.Minute * 2
k8sClient, err = client.New(cfg, client.Options{Scheme: testScheme})
var err error
k8sClient, err = clients.GetKubeClient()
Expect(err).Should(BeNil())
Expect(k8sClient).ToNot(BeNil())
By("new kube client success")
clients.SetKubeClient(k8sClient)
ctx := context.Background()
server, err := arest.New(arest.Config{
cfg := arest.Config{
BindAddr: "127.0.0.1:8000",
Datastore: datastore.Config{
Type: "kubeapi",
Database: "kubevela",
},
})
}
cfg.LeaderConfig.ID = uuid.New().String()
cfg.LeaderConfig.LockName = "apiserver-lock"
cfg.LeaderConfig.Duration = time.Second * 10
server, err := arest.New(cfg)
Expect(err).ShouldNot(HaveOccurred())
Expect(server).ShouldNot(BeNil())
go func() {
err = server.Run(ctx)
Expect(err).ShouldNot(HaveOccurred())
}()
By("wait for api server to start")
Eventually(
func() error {
res, err := http.Get("http://127.0.0.1:8000/api/v1/namespaces")
if err != nil {
return err
}
if res.StatusCode == http.StatusOK {
return nil
}
return errors.New("rest service not ready")
}, time.Second*5, time.Millisecond*200).Should(BeNil())
By("api server started")
time.Sleep(time.Second * 2)
})
var _ = AfterSuite(func() {
By("tearing down the test environment")
err := testEnv.Stop()
Expect(err).ToNot(HaveOccurred())
})
@@ -0,0 +1,91 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: test-component-pod-view
namespace: vela-system
data:
template: |
import (
"vela/ql"
"vela/op"
)
parameter: {
appName: string
appNs: string
name: string
cluster?: string
clusterNs?: string
}
application: ql.#ListResourcesInApp & {
app: {
name: parameter.appName
namespace: parameter.appNs
components: [parameter.name]
filter: {
if parameter.cluster != _|_ {
cluster: parameter.cluster
}
if parameter.clusterNs != _|_ {
clusterNamespace: parameter.clusterNs
}
}
}
}
app: application.list[0]
resources: app.components[0].resources
podsMap: op.#Steps & {
for i, resource in resources {
"\(i)": ql.#CollectPods & {
value: resource.object
cluster: resource.cluster
}
}
}
podsWithCluster: [ for i, pods in podsMap for podObj in pods.list {
cluster: pods.cluster
obj: podObj
}]
podStatus: op.#Steps & {
for i, pod in podsWithCluster {
"\(i)": op.#Steps & {
name: pod.obj.metadata.name
containers: {for container in pod.obj.status.containerStatuses {
"\(container.name)": {
image: container.image
state: container.state
}
}}
events: ql.#SearchEvents & {
value: pod.obj
cluster: pod.cluster
}
metrics: ql.#Read & {
cluster: pod.cluster
value: {
apiVersion: "metrics.k8s.io/v1beta1"
kind: "PodMetrics"
metadata: {
name: pod.obj.metadata.name
namespace: pod.obj.metadata.namespace
}
}
}
}
}
}
status: {
podList: [ for podInfo in podStatus {
name: podInfo.name
containers: [ for containerName, container in podInfo.containers {
containerName
}]
events: podInfo.events.list
}]
}
+78
View File
@@ -0,0 +1,78 @@
apiVersion: core.oam.dev/v1beta1
kind: Application
metadata:
name: example-app
namespace: default
spec:
components:
- name: hello-world-server
type: webservice
properties:
image: crccheck/hello-world
port: 8000
traits:
- type: scaler
properties:
replicas: 1
- name: data-worker
type: worker
properties:
image: busybox
cmd:
- sleep
- '1000000'
policies:
- name: example-multi-env-policy
type: env-binding
properties:
envs:
- name: test
placement: # selecting the namespace (in local cluster) to deploy to
namespaceSelector:
name: TEST_NAMESPACE
selector:
components:
- data-worker
- name: staging
placement: # selecting the cluster to deploy to
clusterSelector:
name: cluster-worker
- name: prod
placement: # selecting both namespace and cluster to deploy to
clusterSelector:
name: cluster-worker
namespaceSelector:
name: PROD_NAMESPACE
patch: # overlay patch on above components
components:
- name: hello-world-server
type: webservice
traits:
- type: scaler
properties:
replicas: 3
workflow:
steps:
# deploy to test env
- name: deploy-test
type: deploy2env
properties:
policy: example-multi-env-policy
env: test
# deploy to staging env
- name: deploy-staging
type: deploy2env
properties:
policy: example-multi-env-policy
env: staging
# deploy to prod env
- name: deploy-prod
type: deploy2env
properties:
policy: example-multi-env-policy
env: prod
+53
View File
@@ -0,0 +1,53 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: read-view
namespace: vela-system
data:
template: |
import (
"vela/op"
)
output: {
if parameter.apiVersion == _|_ && parameter.kind == _|_ {
op.#Read & {
value: {
apiVersion: "core.oam.dev/v1beta1"
kind: "Application"
metadata: {
name: parameter.name
if parameter.namespace != _|_ {
namespace: parameter.namespace
}
}
}
}
}
if parameter.apiVersion != _|_ || parameter.kind != _|_ {
op.#Read & {
value: {
apiVersion: parameter.apiVersion
kind: parameter.kind
metadata: {
name: parameter.name
if parameter.namespace != _|_ {
namespace: parameter.namespace
}
}
}
}
}
}
parameter: {
// +usage=Specify the apiVersion of the object, defaults to core.oam.dev/v1beta1
apiVersion?: string
// +usage=Specify the kind of the object, defaults to Application
kind?: string
// +usage=Specify the name of the object
name: string
// +usage=Specify the namespace of the object
namespace?: string
}
+15
View File
@@ -0,0 +1,15 @@
{
"appName": "appName",
"name": "workflowName",
"alias": "workflowAlias",
"description": "workflow description",
"enable": true,
"default": true,
"steps": [
{
"name": "deploy-test",
"type": "deploy2env",
"properties": ""
}
]
}
+59
View File
@@ -0,0 +1,59 @@
/*
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
import (
"bytes"
"encoding/json"
"fmt"
"net/http"
)
// CreateRequest wraps request
func CreateRequest(method string, path string, body interface{}) (*http.Response, error) {
if body == nil {
body = map[string]string{}
}
bs, err := json.Marshal(body)
if err != nil {
return nil, err
}
req, err := http.NewRequest(method, "http://127.0.0.1:8000/api/v1"+path, bytes.NewBuffer(bs))
if err != nil {
return nil, err
}
req.Header.Set("Content-Type", "application/json")
return http.DefaultClient.Do(req)
}
// DecodeResponseBody decode response and close response
func DecodeResponseBody(resp *http.Response, err error, dst interface{}) error {
if err != nil {
return err
}
if resp.StatusCode != 200 {
return fmt.Errorf("response code is not 200: %d", resp.StatusCode)
}
if resp.Body == nil {
return fmt.Errorf("response body is nil")
}
err = json.NewDecoder(resp.Body).Decode(dst)
if err != nil {
return err
}
return resp.Body.Close()
}
+349
View File
@@ -0,0 +1,349 @@
/*
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"
"time"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"github.com/pkg/errors"
batchv1beta1 "k8s.io/api/batch/v1beta1"
corev1 "k8s.io/api/core/v1"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/yaml"
common2 "github.com/oam-dev/kubevela/apis/core.oam.dev/common"
"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"
)
type PodStatus struct {
Name string `json:"name"`
Containers []string `json:"containers"`
Events interface{} `json:"events"`
}
type Status struct {
PodList []PodStatus `json:"podList"`
}
var _ = Describe("Test velaQL rest api", func() {
namespace := "test-velaql"
appName := "example-app"
component1Name := "ql-webservice"
component2Name := "ql-worker"
var app v1beta1.Application
var readView corev1.ConfigMap
It("Test query application status via view", func() {
Expect(common.ReadYamlToObject("./testdata/read-view.yaml", &readView)).Should(BeNil())
Expect(k8sClient.Create(context.Background(), &readView)).Should(SatisfyAny(BeNil(), &util.AlreadyExistMatcher{}))
Expect(common.ReadYamlToObject("./testdata/example-app.yaml", &app)).Should(BeNil())
app.Spec.Components[0].Name = component1Name
app.Spec.Components[1].Name = component2Name
req := apiv1.ApplicationRequest{
Components: app.Spec.Components,
}
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(res.StatusCode).Should(Equal(200))
oldApp := new(v1beta1.Application)
Eventually(func() error {
if err := k8sClient.Get(context.Background(), client.ObjectKey{Name: appName, Namespace: namespace}, oldApp); err != nil {
return err
}
if len(oldApp.Status.AppliedResources) != 2 {
return errors.Errorf("expect the applied resources number is %d, but get %d", 2, len(oldApp.Status.AppliedResources))
}
return nil
}, 3*time.Second, 300*time.Microsecond).Should(BeNil())
queryRes, err := http.Get(
fmt.Sprintf("http://127.0.0.1:8000/api/v1/query?velaql=%s{name=%s,namespace=%s}.%s", "read-view", appName, namespace, "output.value.spec"),
)
Expect(err).Should(BeNil())
Expect(queryRes.StatusCode).Should(Equal(200))
defer queryRes.Body.Close()
var appSpec v1beta1.ApplicationSpec
err = json.NewDecoder(queryRes.Body).Decode(&appSpec)
Expect(err).ShouldNot(HaveOccurred())
var existApp v1beta1.Application
Expect(k8sClient.Get(context.Background(), client.ObjectKey{Name: appName, Namespace: namespace}, &existApp)).Should(BeNil())
Expect(len(appSpec.Components)).Should(Equal(len(existApp.Spec.Components)))
})
It("Test query application status with wrong velaQL", func() {
queryRes, err := http.Get(
fmt.Sprintf("http://127.0.0.1:8000/api/v1/query?velaql=%s{err=,name=%s,namespace=%s}.%s", "read-object", appName, namespace, "output.value.spec"),
)
Expect(err).Should(BeNil())
Expect(queryRes.StatusCode).Should(Equal(400))
})
It("Test query application component view", func() {
componentView := new(corev1.ConfigMap)
Expect(common.ReadYamlToObject("./testdata/component-pod-view.yaml", componentView)).Should(BeNil())
Expect(k8sClient.Create(context.Background(), componentView)).Should(SatisfyAny(BeNil(), &util.AlreadyExistMatcher{}))
oldApp := new(v1beta1.Application)
Eventually(func() error {
if err := k8sClient.Get(context.Background(), client.ObjectKey{Name: appName, Namespace: namespace}, oldApp); err != nil {
return err
}
if len(oldApp.Status.AppliedResources) != 2 {
return errors.Errorf("expect the applied resources number is %d, but get %d", 2, len(oldApp.Status.AppliedResources))
}
return nil
}, 3*time.Second, 300*time.Microsecond).Should(BeNil())
queryRes, err := http.Get(
fmt.Sprintf("http://127.0.0.1:8000/api/v1/query?velaql=%s{appName=%s,appNs=%s,name=%s}.%s", "test-component-pod-view", appName, namespace, component1Name, "status"),
)
Expect(err).Should(BeNil())
Expect(queryRes.StatusCode).Should(Equal(200))
defer queryRes.Body.Close()
status := new(Status)
err = json.NewDecoder(queryRes.Body).Decode(status)
Expect(err).ShouldNot(HaveOccurred())
Expect(len(status.PodList)).Should(Equal(1))
Expect(status.PodList[0].Containers[0]).Should(Equal(component1Name))
Eventually(func() error {
queryRes1, err := http.Get(
fmt.Sprintf("http://127.0.0.1:8000/api/v1/query?velaql=%s{appName=%s,appNs=%s,name=%s}.%s", "test-component-pod-view", appName, namespace, component2Name, "status"),
)
if err != nil {
return err
}
if queryRes1.StatusCode != 200 {
return errors.Errorf("status code is %d", queryRes1.StatusCode)
}
defer queryRes1.Body.Close()
status1 := new(Status)
err = json.NewDecoder(queryRes1.Body).Decode(status1)
if err != nil {
return err
}
if len(status1.PodList) != 1 {
return errors.New("pod number is zero")
}
if status1.PodList[0].Containers[0] != component2Name {
return errors.New("container name is not correct")
}
return nil
}, 10*time.Second, 300*time.Microsecond).Should(BeNil())
})
It("Test collect pod from cronJob", func() {
cronJob := new(v1beta1.ComponentDefinition)
Expect(yaml.Unmarshal([]byte(cronJobComponentDefinition), cronJob)).Should(BeNil())
Expect(k8sClient.Create(context.Background(), cronJob)).Should(SatisfyAny(BeNil(), &util.AlreadyExistMatcher{}))
oldApp := new(v1beta1.Application)
Eventually(func() error {
if err := k8sClient.Get(context.Background(), client.ObjectKey{Name: appName, Namespace: namespace}, oldApp); err != nil {
return err
}
oldApp.Spec.Components[1].Type = "cronjob"
oldApp.Spec.Components[1].Properties = util.Object2RawExtension(map[string]interface{}{
"image": "busybox",
"cmd": []string{"sleep", "1"},
})
if err := k8sClient.Update(context.Background(), oldApp); err != nil {
return err
}
return nil
}, 3*time.Second, 300*time.Microsecond).Should(BeNil())
newApp := new(v1beta1.Application)
Eventually(func() error {
if err := k8sClient.Get(context.Background(), client.ObjectKeyFromObject(oldApp), newApp); err != nil {
return err
}
appliedCronJob := false
for _, resource := range newApp.Status.AppliedResources {
if resource.ObjectReference.Kind == "CronJob" {
appliedCronJob = true
break
}
}
if !appliedCronJob {
return errors.New("fail to apply cronjob")
}
return nil
}, 3*time.Second, 300*time.Microsecond).Should(BeNil())
newWorkload := new(batchv1beta1.CronJob)
Eventually(func() error {
return k8sClient.Get(context.Background(), client.ObjectKey{Name: component2Name, Namespace: namespace}, newWorkload)
}, 10*time.Second, 300*time.Microsecond).Should(BeNil())
Eventually(func() error {
queryRes, err := http.Get(
fmt.Sprintf("http://127.0.0.1:8000/api/v1/query?velaql=%s{appName=%s,appNs=%s,name=%s}.%s", "test-component-pod-view", appName, namespace, component2Name, "status"),
)
if err != nil {
return err
}
if queryRes.StatusCode != 200 {
return errors.Errorf("status code is %d", queryRes.StatusCode)
}
defer queryRes.Body.Close()
status := new(Status)
err = json.NewDecoder(queryRes.Body).Decode(status)
if err != nil {
return err
}
if len(status.PodList) == 0 {
return errors.New("pod list is 0")
}
return nil
}, 2*time.Minute, 3*time.Microsecond).Should(BeNil())
})
It("Test collect pod from helmRelease", func() {
appWithHelm := new(v1beta1.Application)
Expect(yaml.Unmarshal([]byte(podInfoApp), appWithHelm)).Should(BeNil())
req := apiv1.ApplicationRequest{
Components: appWithHelm.Spec.Components,
}
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, appWithHelm.Name),
"application/json",
bytes.NewBuffer(bodyByte),
)
Expect(err).ShouldNot(HaveOccurred())
Expect(res).ShouldNot(BeNil())
Expect(res.StatusCode).Should(Equal(200))
newApp := new(v1beta1.Application)
Eventually(func() error {
if err := k8sClient.Get(context.Background(), client.ObjectKey{Name: appWithHelm.Name, Namespace: namespace}, newApp); err != nil {
return err
}
if newApp.Status.Phase != common2.ApplicationRunning {
return errors.New("application is not ready")
}
return nil
}, 2*time.Minute, 1*time.Second).Should(BeNil())
Eventually(func() error {
queryRes, err := http.Get(
fmt.Sprintf("http://127.0.0.1:8000/api/v1/query?velaql=%s{appName=%s,appNs=%s,name=%s}.%s", "component-pod-view", appWithHelm.Name, namespace, "podinfo", "status"),
)
if err != nil {
return err
}
if queryRes.StatusCode != 200 {
return errors.Errorf("status code is %d", queryRes.StatusCode)
}
defer queryRes.Body.Close()
type queryResult struct {
PodList []interface{} `json:"podList,omitempty"`
Error interface{} `json:"error,omitempty"`
}
status := new(queryResult)
err = json.NewDecoder(queryRes.Body).Decode(status)
if err != nil {
return err
}
if status.Error != nil {
return errors.Errorf("error %v", status.Error)
}
if len(status.PodList) == 0 {
return errors.New("pod list is 0")
}
return nil
}, 2*time.Minute, 300*time.Microsecond).Should(BeNil())
})
})
var cronJobComponentDefinition = `
apiVersion: core.oam.dev/v1beta1
kind: ComponentDefinition
metadata:
annotations: {}
name: cronjob
namespace: vela-system
spec:
schematic:
cue:
template: |
output: {
apiVersion: "batch/v1beta1"
kind: "CronJob"
metadata: name: context.name
spec: {
schedule: "*/1 * * * *"
jobTemplate: spec: template: spec: {
containers: [{
name: context.name
image: parameter.image
imagePullPolicy: "IfNotPresent"
command: parameter.cmd
}]
restartPolicy: "OnFailure"
}
}
}
parameter: {
image: string
cmd: [...string]
}
workload:
type: autodetects.core.oam.dev
`
var podInfoApp = `
apiVersion: core.oam.dev/v1beta1
kind: Application
metadata:
name: podinfo
spec:
components:
- name: podinfo
type: helm
properties:
chart: podinfo
url: https://stefanprodan.github.io/podinfo
repoType: helm
version: 5.1.2
`
+1 -1
View File
@@ -378,7 +378,7 @@ var _ = Describe("Test application containing helm module", func() {
if err := k8sClient.Get(ctx, client.ObjectKey{Name: cmName, Namespace: namespace}, cm); err != nil {
return err
}
if cm.Data["openapi-v3-json-schema"] == "" {
if cm.Data[types.OpenapiV3JSONSchema] == "" {
return errors.New("json schema is not found in the ConfigMap")
}
return nil
+2 -1
View File
@@ -32,6 +32,7 @@ import (
"github.com/oam-dev/kubevela/apis/core.oam.dev/common"
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
"github.com/oam-dev/kubevela/apis/types"
"github.com/oam-dev/kubevela/pkg/oam/util"
. "github.com/onsi/ginkgo"
@@ -347,7 +348,7 @@ spec:
if err := k8sClient.Get(ctx, client.ObjectKey{Name: cmName, Namespace: namespace}, cm); err != nil {
return err
}
if cm.Data["openapi-v3-json-schema"] == "" {
if cm.Data[types.OpenapiV3JSONSchema] == "" {
return errors.New("json schema is not found in the ConfigMap")
}
return nil
+1 -1
View File
@@ -302,7 +302,7 @@ func RequestReconcileNow(ctx context.Context, o client.Object) {
// randomNamespaceName generates a random name based on the basic name.
// Running each ginkgo case in a new namespace with a random name can avoid
// waiting a long time to GC namesapce.
// waiting a long time to GC namespace.
func randomNamespaceName(basic string) string {
return fmt.Sprintf("%s-%s", basic, strconv.FormatInt(rand.Int63(), 16))
}