mirror of
https://github.com/kubevela/kubevela.git
synced 2026-02-14 18:10:21 +00:00
1144 lines
31 KiB
Go
1144 lines
31 KiB
Go
/*
|
|
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 controllers_test
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"time"
|
|
|
|
"github.com/ghodss/yaml"
|
|
. "github.com/onsi/ginkgo"
|
|
. "github.com/onsi/gomega"
|
|
"github.com/pkg/errors"
|
|
appsv1 "k8s.io/api/apps/v1"
|
|
batchv1 "k8s.io/api/batch/v1"
|
|
corev1 "k8s.io/api/core/v1"
|
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
"k8s.io/apimachinery/pkg/runtime"
|
|
"k8s.io/utils/pointer"
|
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
|
|
|
"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/pkg/oam"
|
|
"github.com/oam-dev/kubevela/pkg/oam/util"
|
|
)
|
|
|
|
var _ = Describe("Test application of the specified definition version", func() {
|
|
ctx := context.Background()
|
|
|
|
var namespace string
|
|
var ns corev1.Namespace
|
|
|
|
BeforeEach(func() {
|
|
namespace = randomNamespaceName("defrev-e2e-test")
|
|
ns = corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: namespace}}
|
|
|
|
Eventually(func() error {
|
|
return k8sClient.Create(ctx, &ns)
|
|
}, time.Second*3, time.Microsecond*300).Should(SatisfyAny(BeNil(), &util.AlreadyExistMatcher{}))
|
|
|
|
labelV1 := labelWithNoTemplate.DeepCopy()
|
|
labelV1.Spec.Schematic.CUE.Template = labelV1Template
|
|
labelV1.SetNamespace(namespace)
|
|
Expect(k8sClient.Create(ctx, labelV1)).Should(Succeed())
|
|
|
|
labelV1DefRev := new(v1beta1.DefinitionRevision)
|
|
Eventually(func() error {
|
|
return k8sClient.Get(ctx, client.ObjectKey{Name: "label-v1", Namespace: namespace}, labelV1DefRev)
|
|
}, 15*time.Second, time.Second).Should(BeNil())
|
|
|
|
labelV2 := new(v1beta1.TraitDefinition)
|
|
Eventually(func() error {
|
|
err := k8sClient.Get(ctx, client.ObjectKey{Name: "label", Namespace: namespace}, labelV2)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
labelV2.Spec.Schematic.CUE.Template = labelV2Template
|
|
return k8sClient.Update(ctx, labelV2)
|
|
}, 15*time.Second, time.Second).Should(BeNil())
|
|
|
|
labelV2DefRev := new(v1beta1.DefinitionRevision)
|
|
Eventually(func() error {
|
|
return k8sClient.Get(ctx, client.ObjectKey{Name: "label-v2", Namespace: namespace}, labelV2DefRev)
|
|
}, 15*time.Second, time.Second).Should(BeNil())
|
|
|
|
webserviceV1 := webServiceWithNoTemplate.DeepCopy()
|
|
webserviceV1.Spec.Schematic.CUE.Template = webServiceV1Template
|
|
webserviceV1.SetNamespace(namespace)
|
|
Expect(k8sClient.Create(ctx, webserviceV1)).Should(Succeed())
|
|
|
|
webserviceV1DefRev := new(v1beta1.DefinitionRevision)
|
|
Eventually(func() error {
|
|
return k8sClient.Get(ctx, client.ObjectKey{Name: "webservice-v1", Namespace: namespace}, webserviceV1DefRev)
|
|
}, 15*time.Second, time.Second).Should(BeNil())
|
|
|
|
webserviceV2 := new(v1beta1.ComponentDefinition)
|
|
Eventually(func() error {
|
|
err := k8sClient.Get(ctx, client.ObjectKey{Name: "webservice", Namespace: namespace}, webserviceV2)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
webserviceV2.Spec.Schematic.CUE.Template = webServiceV2Template
|
|
return k8sClient.Update(ctx, webserviceV2)
|
|
}, 15*time.Second, time.Second).Should(BeNil())
|
|
|
|
webserviceV2DefRev := new(v1beta1.DefinitionRevision)
|
|
Eventually(func() error {
|
|
return k8sClient.Get(ctx, client.ObjectKey{Name: "webservice-v2", Namespace: namespace}, webserviceV2DefRev)
|
|
}, 15*time.Second, time.Second).Should(BeNil())
|
|
})
|
|
|
|
AfterEach(func() {
|
|
By("Clean up resources after a test")
|
|
k8sClient.DeleteAllOf(ctx, &v1beta1.Application{}, client.InNamespace(namespace))
|
|
k8sClient.DeleteAllOf(ctx, &v1beta1.ComponentDefinition{}, client.InNamespace(namespace))
|
|
k8sClient.DeleteAllOf(ctx, &v1beta1.WorkloadDefinition{}, client.InNamespace(namespace))
|
|
k8sClient.DeleteAllOf(ctx, &v1beta1.TraitDefinition{}, client.InNamespace(namespace))
|
|
k8sClient.DeleteAllOf(ctx, &v1beta1.DefinitionRevision{}, client.InNamespace(namespace))
|
|
|
|
By(fmt.Sprintf("Delete the entire namespaceName %s", ns.Name))
|
|
Expect(k8sClient.Delete(ctx, &ns, client.PropagationPolicy(metav1.DeletePropagationForeground))).Should(Succeed())
|
|
})
|
|
|
|
It("Test deploy application which containing cue rendering module", func() {
|
|
var (
|
|
appName = "test-website-app"
|
|
comp1Name = "front"
|
|
comp2Name = "backend"
|
|
)
|
|
|
|
workerV1 := workerWithNoTemplate.DeepCopy()
|
|
workerV1.Spec.Workload = common.WorkloadTypeDescriptor{
|
|
Definition: common.WorkloadGVK{
|
|
APIVersion: "batch/v1",
|
|
Kind: "Job",
|
|
},
|
|
}
|
|
workerV1.Spec.Schematic.CUE.Template = workerV1Template
|
|
workerV1.SetNamespace(namespace)
|
|
Expect(k8sClient.Create(ctx, workerV1)).Should(Succeed())
|
|
|
|
workerV1DefRev := new(v1beta1.DefinitionRevision)
|
|
Eventually(func() error {
|
|
return k8sClient.Get(ctx, client.ObjectKey{Name: "worker-v1", Namespace: namespace}, workerV1DefRev)
|
|
}, 15*time.Second, time.Second).Should(BeNil())
|
|
|
|
workerV2 := new(v1beta1.ComponentDefinition)
|
|
Eventually(func() error {
|
|
err := k8sClient.Get(ctx, client.ObjectKey{Name: "worker", Namespace: namespace}, workerV2)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
workerV1.Spec.Workload = common.WorkloadTypeDescriptor{
|
|
Definition: common.WorkloadGVK{
|
|
APIVersion: "apps/v1",
|
|
Kind: "Deployment",
|
|
},
|
|
}
|
|
workerV2.Spec.Schematic.CUE.Template = workerV2Template
|
|
return k8sClient.Update(ctx, workerV2)
|
|
}, 15*time.Second, time.Second).Should(BeNil())
|
|
|
|
workerV2DefRev := new(v1beta1.DefinitionRevision)
|
|
Eventually(func() error {
|
|
return k8sClient.Get(ctx, client.ObjectKey{Name: "worker-v2", Namespace: namespace}, workerV2DefRev)
|
|
}, 15*time.Second, time.Second).Should(BeNil())
|
|
|
|
app := v1beta1.Application{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: appName,
|
|
Namespace: namespace,
|
|
},
|
|
Spec: v1beta1.ApplicationSpec{
|
|
Components: []v1beta1.ApplicationComponent{
|
|
{
|
|
Name: comp1Name,
|
|
Type: "webservice",
|
|
Properties: util.Object2RawExtension(map[string]interface{}{
|
|
"image": "nginx",
|
|
}),
|
|
Traits: []v1beta1.ApplicationTrait{
|
|
{
|
|
Type: "label",
|
|
Properties: util.Object2RawExtension(map[string]interface{}{
|
|
"labels": map[string]string{
|
|
"hello": "world",
|
|
},
|
|
}),
|
|
},
|
|
},
|
|
},
|
|
{
|
|
Name: comp2Name,
|
|
Type: "worker",
|
|
Properties: util.Object2RawExtension(map[string]interface{}{
|
|
"image": "busybox",
|
|
"cmd": []string{"sleep", "1000"},
|
|
}),
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
By("Create application")
|
|
Eventually(func() error {
|
|
return k8sClient.Create(ctx, app.DeepCopy())
|
|
}, 10*time.Second, 500*time.Millisecond).Should(Succeed())
|
|
|
|
By("Verify the workload(deployment) is created successfully")
|
|
webServiceDeploy := &appsv1.Deployment{}
|
|
deployName := comp1Name
|
|
Eventually(func() error {
|
|
return k8sClient.Get(ctx, client.ObjectKey{Name: deployName, Namespace: namespace}, webServiceDeploy)
|
|
}, 30*time.Second, 3*time.Second).Should(Succeed())
|
|
|
|
workerDeploy := &appsv1.Deployment{}
|
|
deployName = comp2Name
|
|
Eventually(func() error {
|
|
return k8sClient.Get(ctx, client.ObjectKey{Name: deployName, Namespace: namespace}, workerDeploy)
|
|
}, 30*time.Second, 3*time.Second).Should(Succeed())
|
|
|
|
By("Verify trait is applied to the workload")
|
|
webserviceLabels := webServiceDeploy.GetLabels()
|
|
Expect(webserviceLabels["hello"]).Should(Equal("world"))
|
|
|
|
By("Update Application and Specify the Definition version in Application")
|
|
app = v1beta1.Application{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: appName,
|
|
Namespace: namespace,
|
|
},
|
|
Spec: v1beta1.ApplicationSpec{
|
|
Components: []v1beta1.ApplicationComponent{
|
|
{
|
|
Name: comp1Name,
|
|
Type: "webservice@v1",
|
|
Properties: util.Object2RawExtension(map[string]interface{}{
|
|
"image": "nginx",
|
|
}),
|
|
Traits: []v1beta1.ApplicationTrait{
|
|
{
|
|
Type: "label@v1",
|
|
Properties: util.Object2RawExtension(map[string]interface{}{
|
|
"labels": map[string]string{
|
|
"hello": "kubevela",
|
|
},
|
|
}),
|
|
},
|
|
},
|
|
},
|
|
{
|
|
Name: comp2Name,
|
|
Type: "worker@v1",
|
|
Properties: util.Object2RawExtension(map[string]interface{}{
|
|
"image": "busybox",
|
|
"cmd": []string{"sleep", "1000"},
|
|
}),
|
|
},
|
|
},
|
|
},
|
|
}
|
|
Expect(k8sClient.Patch(ctx, &app, client.Merge)).Should(Succeed())
|
|
|
|
By("Wait for dispatching v2 resources successfully")
|
|
Eventually(func() error {
|
|
requestReconcileNow(ctx, &app)
|
|
rt := &v1beta1.ResourceTracker{}
|
|
if err := k8sClient.Get(ctx, client.ObjectKey{Name: fmt.Sprintf("%s-v2-%s", appName, namespace)}, rt); err != nil {
|
|
return err
|
|
}
|
|
if len(rt.Status.TrackedResources) != 0 {
|
|
return nil
|
|
}
|
|
return errors.New("v2 resources have not been dispatched")
|
|
}, 10*time.Second, 500*time.Millisecond).Should(Succeed())
|
|
|
|
By("Verify the workload(deployment) is created successfully")
|
|
webServiceV1Deploy := &appsv1.Deployment{}
|
|
deployName = comp1Name
|
|
Eventually(func() error {
|
|
return k8sClient.Get(ctx, client.ObjectKey{Name: deployName, Namespace: namespace}, webServiceV1Deploy)
|
|
}, 30*time.Second, 3*time.Second).Should(Succeed())
|
|
|
|
By("Verify the workload(job) is created successfully")
|
|
workerJob := &batchv1.Job{}
|
|
jobName := comp2Name
|
|
Eventually(func() error {
|
|
return k8sClient.Get(ctx, client.ObjectKey{Name: jobName, Namespace: namespace}, workerJob)
|
|
}, 30*time.Second, 3*time.Second).Should(Succeed())
|
|
|
|
By("Verify trait is applied to the workload")
|
|
webserviceV1Labels := webServiceV1Deploy.GetLabels()
|
|
Expect(webserviceV1Labels["hello"]).Should(Equal("kubevela"))
|
|
|
|
By("Check Application is rendered by the specified version of the Definition")
|
|
Expect(webServiceV1Deploy.Labels["componentdefinition.oam.dev/version"]).Should(Equal("v1"))
|
|
Expect(webServiceV1Deploy.Labels["traitdefinition.oam.dev/version"]).Should(Equal("v1"))
|
|
|
|
By("Application specifies the wrong version of the Definition, it will raise an error")
|
|
app = v1beta1.Application{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: appName,
|
|
Namespace: namespace,
|
|
},
|
|
Spec: v1beta1.ApplicationSpec{
|
|
Components: []v1beta1.ApplicationComponent{
|
|
{
|
|
Name: comp1Name,
|
|
Type: "webservice@v10",
|
|
Properties: util.Object2RawExtension(map[string]interface{}{
|
|
"image": "nginx",
|
|
}),
|
|
},
|
|
},
|
|
},
|
|
}
|
|
Expect(k8sClient.Patch(ctx, &app, client.Merge)).Should(HaveOccurred())
|
|
})
|
|
|
|
It("Test deploy application which containing helm module", func() {
|
|
var (
|
|
appName = "test-helm"
|
|
compName = "worker"
|
|
)
|
|
|
|
helmworkerV1 := HELMWorker.DeepCopy()
|
|
helmworkerV1.SetNamespace(namespace)
|
|
helmworkerV1.Spec.Workload.Definition = common.WorkloadGVK{
|
|
APIVersion: "batch/v1beta1",
|
|
Kind: "CronJob",
|
|
}
|
|
helmworkerV1.Spec.Schematic = &common.Schematic{
|
|
HELM: &common.Helm{
|
|
Release: util.Object2RawExtension(map[string]interface{}{
|
|
"chart": map[string]interface{}{
|
|
"spec": map[string]interface{}{
|
|
"chart": "podinfo",
|
|
"version": "5.1.4",
|
|
},
|
|
},
|
|
}),
|
|
Repository: util.Object2RawExtension(map[string]interface{}{
|
|
"url": "https://stefanprodan.github.io/podinfo",
|
|
}),
|
|
},
|
|
}
|
|
Expect(k8sClient.Create(ctx, helmworkerV1)).Should(Succeed())
|
|
|
|
helmworkerV1DefRev := new(v1beta1.DefinitionRevision)
|
|
Eventually(func() error {
|
|
return k8sClient.Get(ctx, client.ObjectKey{Name: "helm-worker-v1", Namespace: namespace}, helmworkerV1DefRev)
|
|
}, 15*time.Second, time.Second).Should(BeNil())
|
|
|
|
helmworkerV2 := new(v1beta1.ComponentDefinition)
|
|
Eventually(func() error {
|
|
err := k8sClient.Get(ctx, client.ObjectKey{Name: "helm-worker", Namespace: namespace}, helmworkerV2)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
helmworkerV2.Spec.Workload.Definition = common.WorkloadGVK{
|
|
APIVersion: "apps/v1",
|
|
Kind: "Deployment",
|
|
}
|
|
helmworkerV2.Spec.Workload.Type = "deployments.apps"
|
|
helmworkerV2.Spec.Schematic = &common.Schematic{
|
|
HELM: &common.Helm{
|
|
Release: util.Object2RawExtension(map[string]interface{}{
|
|
"chart": map[string]interface{}{
|
|
"spec": map[string]interface{}{
|
|
"chart": "podinfo",
|
|
"version": "5.2.0",
|
|
},
|
|
},
|
|
}),
|
|
Repository: util.Object2RawExtension(map[string]interface{}{
|
|
"url": "https://stefanprodan.github.io/podinfo",
|
|
}),
|
|
},
|
|
}
|
|
return k8sClient.Update(ctx, helmworkerV2)
|
|
}, 15*time.Second, time.Second).Should(BeNil())
|
|
|
|
helmworkerV2DefRev := new(v1beta1.DefinitionRevision)
|
|
Eventually(func() error {
|
|
return k8sClient.Get(ctx, client.ObjectKey{Name: "helm-worker-v2", Namespace: namespace}, helmworkerV2DefRev)
|
|
}, 15*time.Second, time.Second).Should(BeNil())
|
|
|
|
app := v1beta1.Application{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: appName,
|
|
Namespace: namespace,
|
|
},
|
|
Spec: v1beta1.ApplicationSpec{
|
|
Components: []v1beta1.ApplicationComponent{
|
|
{
|
|
Name: compName,
|
|
Type: "helm-worker",
|
|
Properties: util.Object2RawExtension(map[string]interface{}{
|
|
"image": map[string]interface{}{
|
|
"tag": "5.1.2",
|
|
},
|
|
}),
|
|
Traits: []v1beta1.ApplicationTrait{
|
|
{
|
|
Type: "label",
|
|
Properties: util.Object2RawExtension(map[string]interface{}{
|
|
"labels": map[string]string{
|
|
"hello": "world",
|
|
},
|
|
}),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
By("Create application")
|
|
Eventually(func() error {
|
|
return k8sClient.Create(ctx, app.DeepCopy())
|
|
}, 10*time.Second, 500*time.Millisecond).Should(Succeed())
|
|
|
|
By("Verify the workload(deployment) is created successfully by Helm")
|
|
deploy := &appsv1.Deployment{}
|
|
deployName := fmt.Sprintf("%s-%s-podinfo", appName, compName)
|
|
Eventually(func() bool {
|
|
err := k8sClient.Get(ctx, client.ObjectKey{Name: deployName, Namespace: namespace}, deploy)
|
|
if err != nil {
|
|
return false
|
|
}
|
|
DeployLabels := deploy.GetLabels()
|
|
return DeployLabels["helm.sh/chart"] == "podinfo-5.2.0"
|
|
}, 120*time.Second, 5*time.Second).Should(BeTrue())
|
|
|
|
By("Verify trait is applied to the workload")
|
|
Eventually(func() bool {
|
|
deploy := &appsv1.Deployment{}
|
|
if err := k8sClient.Get(ctx, client.ObjectKey{Name: deployName, Namespace: namespace}, deploy); err != nil {
|
|
return false
|
|
}
|
|
By("Verify patch trait is applied")
|
|
templateLabels := deploy.GetLabels()
|
|
return templateLabels["hello"] != "world"
|
|
}, 120*time.Second, 10*time.Second).Should(BeTrue())
|
|
|
|
app = v1beta1.Application{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: appName,
|
|
Namespace: namespace,
|
|
},
|
|
Spec: v1beta1.ApplicationSpec{
|
|
Components: []v1beta1.ApplicationComponent{
|
|
{
|
|
Name: compName,
|
|
Type: "helm-worker@v1",
|
|
Properties: util.Object2RawExtension(map[string]interface{}{
|
|
"image": map[string]interface{}{
|
|
"tag": "5.1.2",
|
|
},
|
|
}),
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
By("Create application")
|
|
Expect(k8sClient.Patch(ctx, &app, client.Merge)).Should(Succeed())
|
|
|
|
By("Verify the workload(deployment) is update successfully by Helm")
|
|
deploy = &appsv1.Deployment{}
|
|
Eventually(func() bool {
|
|
err := k8sClient.Get(ctx, client.ObjectKey{Name: deployName, Namespace: namespace}, deploy)
|
|
if err != nil {
|
|
return false
|
|
}
|
|
DeployLabels := deploy.GetLabels()
|
|
return DeployLabels["helm.sh/chart"] != "podinfo-5.1.4"
|
|
}, 120*time.Second, 5*time.Second).Should(BeTrue())
|
|
})
|
|
|
|
It("Test deploy application which containing kube module", func() {
|
|
var (
|
|
appName = "test-kube-app"
|
|
compName = "worker"
|
|
)
|
|
|
|
kubeworkerV1 := KUBEWorker.DeepCopy()
|
|
kubeworkerV1.Spec.Workload.Definition = common.WorkloadGVK{
|
|
APIVersion: "apps/v1",
|
|
Kind: "Deployment",
|
|
}
|
|
kubeworkerV1.Spec.Schematic = &common.Schematic{
|
|
KUBE: &common.Kube{
|
|
Template: generateTemplate(KUBEWorkerV1Template),
|
|
Parameters: []common.KubeParameter{
|
|
{
|
|
Name: "image",
|
|
ValueType: common.StringType,
|
|
FieldPaths: []string{"spec.template.spec.containers[0].image"},
|
|
Required: pointer.BoolPtr(true),
|
|
Description: pointer.StringPtr("test description"),
|
|
},
|
|
},
|
|
},
|
|
}
|
|
kubeworkerV1.SetNamespace(namespace)
|
|
Expect(k8sClient.Create(ctx, kubeworkerV1)).Should(Succeed())
|
|
|
|
kubeworkerV1DefRev := new(v1beta1.DefinitionRevision)
|
|
Eventually(func() error {
|
|
return k8sClient.Get(ctx, client.ObjectKey{Name: "kube-worker-v1", Namespace: namespace}, kubeworkerV1DefRev)
|
|
}, 15*time.Second, time.Second).Should(BeNil())
|
|
|
|
kubeworkerV2 := new(v1beta1.ComponentDefinition)
|
|
Eventually(func() error {
|
|
err := k8sClient.Get(ctx, client.ObjectKey{Name: "kube-worker", Namespace: namespace}, kubeworkerV2)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
kubeworkerV2.Spec.Workload.Definition = common.WorkloadGVK{
|
|
APIVersion: "batch/v1",
|
|
Kind: "Job",
|
|
}
|
|
kubeworkerV2.Spec.Workload.Type = "jobs.batch"
|
|
kubeworkerV2.Spec.Schematic = &common.Schematic{
|
|
KUBE: &common.Kube{
|
|
Template: generateTemplate(KUBEWorkerV2Template),
|
|
Parameters: []common.KubeParameter{
|
|
{
|
|
Name: "image",
|
|
ValueType: common.StringType,
|
|
FieldPaths: []string{"spec.template.spec.containers[0].image"},
|
|
Required: pointer.BoolPtr(true),
|
|
Description: pointer.StringPtr("test description"),
|
|
},
|
|
},
|
|
},
|
|
}
|
|
return k8sClient.Update(ctx, kubeworkerV2)
|
|
}, 15*time.Second, time.Second).Should(BeNil())
|
|
|
|
kubeworkerV2DefRev := new(v1beta1.DefinitionRevision)
|
|
Eventually(func() error {
|
|
return k8sClient.Get(ctx, client.ObjectKey{Name: "kube-worker-v2", Namespace: namespace}, kubeworkerV2DefRev)
|
|
}, 15*time.Second, time.Second).Should(BeNil())
|
|
|
|
app := v1beta1.Application{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: appName,
|
|
Namespace: namespace,
|
|
},
|
|
Spec: v1beta1.ApplicationSpec{
|
|
Components: []v1beta1.ApplicationComponent{
|
|
{
|
|
Name: compName,
|
|
Type: "kube-worker",
|
|
Properties: util.Object2RawExtension(map[string]interface{}{
|
|
"image": "busybox",
|
|
}),
|
|
Traits: []v1beta1.ApplicationTrait{
|
|
{
|
|
Type: "label",
|
|
Properties: util.Object2RawExtension(map[string]interface{}{
|
|
"labels": map[string]string{
|
|
"hello": "world",
|
|
},
|
|
}),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
By("Create application")
|
|
Eventually(func() error {
|
|
return k8sClient.Create(ctx, app.DeepCopy())
|
|
}, 10*time.Second, 500*time.Millisecond).Should(Succeed())
|
|
|
|
By("Verify the workload(job) is created successfully")
|
|
job := &batchv1.Job{}
|
|
jobName := compName
|
|
Eventually(func() error {
|
|
return k8sClient.Get(ctx, client.ObjectKey{Name: jobName, Namespace: namespace}, job)
|
|
}, 30*time.Second, 3*time.Second).Should(Succeed())
|
|
|
|
By("Verify trait is applied to the workload")
|
|
Labels := job.GetLabels()
|
|
Expect(Labels["hello"]).Should(Equal("world"))
|
|
|
|
By("Update Application and Specify the Definition version in Application")
|
|
app = v1beta1.Application{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: appName,
|
|
Namespace: namespace,
|
|
},
|
|
Spec: v1beta1.ApplicationSpec{
|
|
Components: []v1beta1.ApplicationComponent{
|
|
{
|
|
Name: compName,
|
|
Type: "kube-worker@v1",
|
|
Properties: util.Object2RawExtension(map[string]interface{}{
|
|
"image": "nginx",
|
|
}),
|
|
Traits: []v1beta1.ApplicationTrait{
|
|
{
|
|
Type: "label",
|
|
Properties: util.Object2RawExtension(map[string]interface{}{
|
|
"labels": map[string]string{
|
|
"hello": "kubevela",
|
|
},
|
|
}),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
Expect(k8sClient.Patch(ctx, &app, client.Merge)).Should(Succeed())
|
|
|
|
By("Verify the workload(deployment) is created successfully")
|
|
deploy := &appsv1.Deployment{}
|
|
deployName := compName
|
|
Eventually(func() error {
|
|
return k8sClient.Get(ctx, client.ObjectKey{Name: deployName, Namespace: namespace}, deploy)
|
|
}, 30*time.Second, 3*time.Second).Should(Succeed())
|
|
|
|
By("Verify trait is applied to the workload")
|
|
webserviceV1Labels := deploy.GetLabels()
|
|
Expect(webserviceV1Labels["hello"]).Should(Equal("kubevela"))
|
|
|
|
By("Application specifies the wrong version of the Definition, it will raise an error")
|
|
app = v1beta1.Application{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: appName,
|
|
Namespace: namespace,
|
|
},
|
|
Spec: v1beta1.ApplicationSpec{
|
|
Components: []v1beta1.ApplicationComponent{
|
|
{
|
|
Name: compName,
|
|
Type: "kube-worker@a1",
|
|
Properties: util.Object2RawExtension(map[string]interface{}{
|
|
"image": "nginx",
|
|
}),
|
|
},
|
|
},
|
|
},
|
|
}
|
|
Expect(k8sClient.Patch(ctx, &app, client.Merge)).Should(HaveOccurred())
|
|
})
|
|
|
|
// refer to https://github.com/oam-dev/kubevela/discussions/1810#discussioncomment-914295
|
|
It("Test k8s resources created by application whether with correct label", func() {
|
|
var (
|
|
appName = "test-resources-labels"
|
|
compName = "web"
|
|
)
|
|
|
|
exposeV1 := exposeWithNoTemplate.DeepCopy()
|
|
exposeV1.Spec.Schematic.CUE.Template = exposeV1Template
|
|
exposeV1.SetNamespace(namespace)
|
|
Expect(k8sClient.Create(ctx, exposeV1)).Should(Succeed())
|
|
|
|
exposeV1DefRev := new(v1beta1.DefinitionRevision)
|
|
Eventually(func() error {
|
|
return k8sClient.Get(ctx, client.ObjectKey{Name: "expose-v1", Namespace: namespace}, exposeV1DefRev)
|
|
}, 15*time.Second, time.Second).Should(BeNil())
|
|
|
|
exposeV2 := new(v1beta1.TraitDefinition)
|
|
Eventually(func() error {
|
|
err := k8sClient.Get(ctx, client.ObjectKey{Name: "expose", Namespace: namespace}, exposeV2)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
exposeV2.Spec.Schematic.CUE.Template = exposeV2Templae
|
|
return k8sClient.Update(ctx, exposeV2)
|
|
}, 15*time.Second, time.Second).Should(BeNil())
|
|
|
|
exposeV2DefRev := new(v1beta1.DefinitionRevision)
|
|
Eventually(func() error {
|
|
return k8sClient.Get(ctx, client.ObjectKey{Name: "expose-v2", Namespace: namespace}, exposeV2DefRev)
|
|
}, 15*time.Second, time.Second).Should(BeNil())
|
|
|
|
app := v1beta1.Application{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: appName,
|
|
Namespace: namespace,
|
|
},
|
|
Spec: v1beta1.ApplicationSpec{
|
|
Components: []v1beta1.ApplicationComponent{
|
|
{
|
|
Name: compName,
|
|
Type: "webservice@v1",
|
|
Properties: util.Object2RawExtension(map[string]interface{}{
|
|
"image": "crccheck/hello-world",
|
|
"port": 8000,
|
|
}),
|
|
Traits: []v1beta1.ApplicationTrait{
|
|
{
|
|
Type: "expose@v1",
|
|
Properties: util.Object2RawExtension(map[string]interface{}{
|
|
"port": []int{8000},
|
|
}),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
By("Create application")
|
|
Eventually(func() error {
|
|
return k8sClient.Create(ctx, app.DeepCopy())
|
|
}, 10*time.Second, 500*time.Millisecond).Should(Succeed())
|
|
|
|
By("Verify the workload(deployment) is created successfully")
|
|
webServiceDeploy := &appsv1.Deployment{}
|
|
deployName := compName
|
|
Eventually(func() error {
|
|
return k8sClient.Get(ctx, client.ObjectKey{Name: deployName, Namespace: namespace}, webServiceDeploy)
|
|
}, 30*time.Second, 3*time.Second).Should(Succeed())
|
|
|
|
By("Verify the workload label generated by KubeVela")
|
|
workloadLabel := webServiceDeploy.GetLabels()[oam.WorkloadTypeLabel]
|
|
Expect(workloadLabel).Should(Equal("webservice-v1"))
|
|
|
|
By("Verify the trait(service) is created successfully")
|
|
exposeSVC := &corev1.Service{}
|
|
Eventually(func() error {
|
|
return k8sClient.Get(ctx, client.ObjectKey{Name: compName, Namespace: namespace}, exposeSVC)
|
|
}, 30*time.Second, 3*time.Second).Should(Succeed())
|
|
|
|
By("Verify the trait label generated by KubeVela")
|
|
traitLabel := exposeSVC.GetLabels()[oam.TraitTypeLabel]
|
|
Expect(traitLabel).Should(Equal("expose-v1"))
|
|
})
|
|
})
|
|
|
|
var webServiceWithNoTemplate = &v1beta1.ComponentDefinition{
|
|
TypeMeta: metav1.TypeMeta{
|
|
Kind: "ComponentDefinition",
|
|
APIVersion: "core.oam.dev/v1beta1",
|
|
},
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "webservice",
|
|
},
|
|
Spec: v1beta1.ComponentDefinitionSpec{
|
|
Workload: common.WorkloadTypeDescriptor{
|
|
Definition: common.WorkloadGVK{
|
|
APIVersion: "apps/v1",
|
|
Kind: "Deployment",
|
|
},
|
|
},
|
|
Schematic: &common.Schematic{
|
|
CUE: &common.CUE{
|
|
Template: "",
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
var workerWithNoTemplate = &v1beta1.ComponentDefinition{
|
|
TypeMeta: metav1.TypeMeta{
|
|
Kind: "ComponentDefinition",
|
|
APIVersion: "core.oam.dev/v1beta1",
|
|
},
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "worker",
|
|
},
|
|
Spec: v1beta1.ComponentDefinitionSpec{
|
|
Schematic: &common.Schematic{
|
|
CUE: &common.CUE{
|
|
Template: "",
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
var KUBEWorker = &v1beta1.ComponentDefinition{
|
|
TypeMeta: metav1.TypeMeta{
|
|
Kind: "ComponentDefinition",
|
|
APIVersion: "core.oam.dev/v1beta1",
|
|
},
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "kube-worker",
|
|
},
|
|
}
|
|
|
|
var HELMWorker = &v1beta1.ComponentDefinition{
|
|
TypeMeta: metav1.TypeMeta{
|
|
Kind: "ComponentDefinition",
|
|
APIVersion: "core.oam.dev/v1beta1",
|
|
},
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "helm-worker",
|
|
},
|
|
}
|
|
|
|
var labelWithNoTemplate = &v1beta1.TraitDefinition{
|
|
TypeMeta: metav1.TypeMeta{
|
|
Kind: "TraitDefinition",
|
|
APIVersion: "core.oam.dev/v1beta1",
|
|
},
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "label",
|
|
},
|
|
Spec: v1beta1.TraitDefinitionSpec{
|
|
Schematic: &common.Schematic{
|
|
CUE: &common.CUE{
|
|
Template: "",
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
var exposeWithNoTemplate = &v1beta1.TraitDefinition{
|
|
TypeMeta: metav1.TypeMeta{
|
|
Kind: "TraitDefinition",
|
|
APIVersion: "core.oam.dev/v1beta1",
|
|
},
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "expose",
|
|
},
|
|
Spec: v1beta1.TraitDefinitionSpec{
|
|
Schematic: &common.Schematic{
|
|
CUE: &common.CUE{
|
|
Template: "",
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
func generateTemplate(template string) runtime.RawExtension {
|
|
b, _ := yaml.YAMLToJSON([]byte(template))
|
|
return runtime.RawExtension{Raw: b}
|
|
}
|
|
|
|
var webServiceV1Template = `output: {
|
|
apiVersion: "apps/v1"
|
|
kind: "Deployment"
|
|
metadata: labels: {
|
|
"componentdefinition.oam.dev/version": "v1"
|
|
}
|
|
spec: {
|
|
selector: matchLabels: {
|
|
"app.oam.dev/component": context.name
|
|
}
|
|
template: {
|
|
metadata: labels: {
|
|
"app.oam.dev/component": context.name
|
|
}
|
|
spec: {
|
|
containers: [{
|
|
name: context.name
|
|
image: parameter.image
|
|
if parameter["cmd"] != _|_ {
|
|
command: parameter.cmd
|
|
}
|
|
if parameter["env"] != _|_ {
|
|
env: parameter.env
|
|
}
|
|
if context["config"] != _|_ {
|
|
env: context.config
|
|
}
|
|
ports: [{
|
|
containerPort: parameter.port
|
|
}]
|
|
if parameter["cpu"] != _|_ {
|
|
resources: {
|
|
limits:
|
|
cpu: parameter.cpu
|
|
requests:
|
|
cpu: parameter.cpu
|
|
}
|
|
}
|
|
}]
|
|
}
|
|
}
|
|
}
|
|
}
|
|
parameter: {
|
|
image: string
|
|
cmd?: [...string]
|
|
port: *80 | int
|
|
env?: [...{
|
|
name: string
|
|
value?: string
|
|
valueFrom?: {
|
|
secretKeyRef: {
|
|
name: string
|
|
key: string
|
|
}
|
|
}
|
|
}]
|
|
cpu?: string
|
|
}
|
|
`
|
|
|
|
var webServiceV2Template = `output: {
|
|
apiVersion: "apps/v1"
|
|
kind: "Deployment"
|
|
spec: {
|
|
selector: matchLabels: {
|
|
"app.oam.dev/component": context.name
|
|
if parameter.addRevisionLabel {
|
|
"app.oam.dev/appRevision": context.appRevision
|
|
}
|
|
}
|
|
template: {
|
|
metadata: labels: {
|
|
"app.oam.dev/component": context.name
|
|
if parameter.addRevisionLabel {
|
|
"app.oam.dev/appRevision": context.appRevision
|
|
}
|
|
}
|
|
spec: {
|
|
containers: [{
|
|
name: context.name
|
|
image: parameter.image
|
|
if parameter["cmd"] != _|_ {
|
|
command: parameter.cmd
|
|
}
|
|
if parameter["env"] != _|_ {
|
|
env: parameter.env
|
|
}
|
|
if context["config"] != _|_ {
|
|
env: context.config
|
|
}
|
|
ports: [{
|
|
containerPort: parameter.port
|
|
}]
|
|
if parameter["cpu"] != _|_ {
|
|
resources: {
|
|
limits:
|
|
cpu: parameter.cpu
|
|
requests:
|
|
cpu: parameter.cpu
|
|
}
|
|
}
|
|
}]
|
|
}
|
|
}
|
|
}
|
|
}
|
|
parameter: {
|
|
image: string
|
|
cmd?: [...string]
|
|
port: *80 | int
|
|
env?: [...{
|
|
name: string
|
|
value?: string
|
|
valueFrom?: {
|
|
secretKeyRef: {
|
|
name: string
|
|
key: string
|
|
}
|
|
}
|
|
}]
|
|
cpu?: string
|
|
addRevisionLabel: *false | bool
|
|
}
|
|
`
|
|
|
|
var workerV1Template = `output: {
|
|
apiVersion: "batch/v1"
|
|
kind: "Job"
|
|
spec: {
|
|
parallelism: parameter.count
|
|
completions: parameter.count
|
|
template: spec: {
|
|
restartPolicy : parameter.restart
|
|
containers: [{
|
|
name: context.name
|
|
image: parameter.image
|
|
if parameter["cmd"] != _|_ {
|
|
command: parameter.cmd
|
|
}
|
|
}]
|
|
}
|
|
}
|
|
}
|
|
parameter: {
|
|
count: *1 | int
|
|
image: string
|
|
restart: *"Never" | string
|
|
cmd?: [...string]
|
|
}
|
|
`
|
|
|
|
var workerV2Template = `output: {
|
|
apiVersion: "apps/v1"
|
|
kind: "Deployment"
|
|
spec: {
|
|
selector: matchLabels: {
|
|
"app.oam.dev/component": context.name
|
|
}
|
|
template: {
|
|
metadata: labels: {
|
|
"app.oam.dev/component": context.name
|
|
}
|
|
spec: {
|
|
containers: [{
|
|
name: context.name
|
|
image: parameter.image
|
|
|
|
if parameter["cmd"] != _|_ {
|
|
command: parameter.cmd
|
|
}
|
|
}]
|
|
}
|
|
}
|
|
}
|
|
}
|
|
parameter: {
|
|
image: string
|
|
cmd?: [...string]
|
|
}
|
|
`
|
|
|
|
var labelV1Template = `patch: {
|
|
metadata: labels: {
|
|
for k, v in parameter.labels {
|
|
"\(k)": v
|
|
}
|
|
"traitdefinition.oam.dev/version": "v1"
|
|
}
|
|
}
|
|
parameter: {
|
|
labels: [string]: string
|
|
}
|
|
`
|
|
|
|
var labelV2Template = `patch: {
|
|
metadata: labels: {
|
|
for k, v in parameter.labels {
|
|
"\(k)": v
|
|
}
|
|
}
|
|
}
|
|
parameter: {
|
|
labels: [string]: string
|
|
}
|
|
`
|
|
|
|
var KUBEWorkerV1Template = `apiVersion: apps/v1
|
|
kind: Deployment
|
|
spec:
|
|
selector:
|
|
matchLabels:
|
|
app: nginx
|
|
template:
|
|
metadata:
|
|
labels:
|
|
app: nginx
|
|
spec:
|
|
containers:
|
|
- name: nginx
|
|
ports:
|
|
- containerPort: 80
|
|
`
|
|
|
|
var KUBEWorkerV2Template = `apiVersion: "batch/v1"
|
|
kind: "Job"
|
|
spec:
|
|
parallelism: 1
|
|
completions: 1
|
|
template:
|
|
spec:
|
|
restartPolicy: "Never"
|
|
containers:
|
|
- name: "job"
|
|
image: "busybox"
|
|
command:
|
|
- "sleep"
|
|
- "1000"
|
|
`
|
|
|
|
var exposeV1Template = `
|
|
outputs: service: {
|
|
apiVersion: "v1"
|
|
kind: "Service"
|
|
metadata:
|
|
name: context.name
|
|
spec: {
|
|
selector:
|
|
"app.oam.dev/component": context.name
|
|
ports: [
|
|
for p in parameter.port {
|
|
port: p
|
|
targetPort: p
|
|
},
|
|
]
|
|
}
|
|
}
|
|
parameter: {
|
|
// +usage=Specify the exposion ports
|
|
port: [...int]
|
|
}
|
|
`
|
|
|
|
var exposeV2Templae = `
|
|
outputs: service: {
|
|
apiVersion: "v1"
|
|
kind: "Service"
|
|
metadata:
|
|
name: context.name
|
|
spec: {
|
|
selector: {
|
|
"app.oam.dev/component": context.name
|
|
}
|
|
ports: [
|
|
for k, v in parameter.http {
|
|
port: v
|
|
targetPort: v
|
|
},
|
|
]
|
|
}
|
|
}
|
|
|
|
outputs: ingress: {
|
|
apiVersion: "networking.k8s.io/v1beta1"
|
|
kind: "Ingress"
|
|
metadata:
|
|
name: context.name
|
|
spec: {
|
|
rules: [{
|
|
host: parameter.domain
|
|
http: {
|
|
paths: [
|
|
for k, v in parameter.http {
|
|
path: k
|
|
backend: {
|
|
serviceName: context.name
|
|
servicePort: v
|
|
}
|
|
},
|
|
]
|
|
}
|
|
}]
|
|
}
|
|
}
|
|
|
|
parameter: {
|
|
domain: string
|
|
http: [string]: int
|
|
}
|
|
`
|