mirror of
https://github.com/kubevela/kubevela.git
synced 2026-05-13 04:46:46 +00:00
643 lines
22 KiB
Go
643 lines
22 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"
|
|
"encoding/json"
|
|
"fmt"
|
|
"time"
|
|
|
|
. "github.com/onsi/ginkgo"
|
|
. "github.com/onsi/gomega"
|
|
|
|
appsv1 "k8s.io/api/apps/v1"
|
|
corev1 "k8s.io/api/core/v1"
|
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
|
"k8s.io/apimachinery/pkg/runtime"
|
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
|
logf "sigs.k8s.io/controller-runtime/pkg/log"
|
|
|
|
commontypes "github.com/oam-dev/kubevela/apis/core.oam.dev/common"
|
|
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1alpha2"
|
|
"github.com/oam-dev/kubevela/pkg/oam/util"
|
|
"github.com/oam-dev/kubevela/pkg/utils/common"
|
|
)
|
|
|
|
var _ = Describe("Versioning mechanism of components", func() {
|
|
ctx := context.Background()
|
|
namespace := "component-versioning-test"
|
|
ns := corev1.Namespace{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: namespace,
|
|
},
|
|
}
|
|
componentName := "example-component"
|
|
|
|
// to identify different revisions of components
|
|
imageV1 := "wordpress:4.6.1-apache"
|
|
imageV2 := "wordpress:4.6.2-apache"
|
|
|
|
var cwV1, cwV2 v1alpha2.ContainerizedWorkload
|
|
var componentV1 v1alpha2.Component
|
|
var appConfig v1alpha2.ApplicationConfiguration
|
|
|
|
BeforeEach(func() {
|
|
cwV1 = v1alpha2.ContainerizedWorkload{
|
|
TypeMeta: metav1.TypeMeta{
|
|
Kind: "ContainerizedWorkload",
|
|
APIVersion: "core.oam.dev/v1alpha2",
|
|
},
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Namespace: namespace,
|
|
},
|
|
Spec: v1alpha2.ContainerizedWorkloadSpec{
|
|
Containers: []v1alpha2.Container{
|
|
{
|
|
Name: "wordpress",
|
|
Image: imageV1,
|
|
Ports: []v1alpha2.ContainerPort{
|
|
{
|
|
Name: "wordpress",
|
|
Port: 80,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
cwV2 = v1alpha2.ContainerizedWorkload{
|
|
TypeMeta: metav1.TypeMeta{
|
|
Kind: "ContainerizedWorkload",
|
|
APIVersion: "core.oam.dev/v1alpha2",
|
|
},
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Namespace: namespace,
|
|
},
|
|
Spec: v1alpha2.ContainerizedWorkloadSpec{
|
|
Containers: []v1alpha2.Container{
|
|
{
|
|
Name: "wordpress",
|
|
Image: imageV2,
|
|
Ports: []v1alpha2.ContainerPort{
|
|
{
|
|
Name: "wordpress",
|
|
Port: 80,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
componentV1 = v1alpha2.Component{
|
|
TypeMeta: metav1.TypeMeta{
|
|
APIVersion: "core.oam.dev/v1alpha2",
|
|
Kind: "Component",
|
|
},
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: componentName,
|
|
Namespace: namespace,
|
|
},
|
|
Spec: v1alpha2.ComponentSpec{
|
|
Workload: runtime.RawExtension{
|
|
Object: &cwV1,
|
|
},
|
|
},
|
|
}
|
|
|
|
appConfig = v1alpha2.ApplicationConfiguration{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "example-appconfig",
|
|
Namespace: namespace,
|
|
},
|
|
}
|
|
|
|
logf.Log.Info("Start to run a test, clean up previous resources")
|
|
ns = corev1.Namespace{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: namespace,
|
|
},
|
|
}
|
|
// delete the namespace with all its resources
|
|
Expect(k8sClient.Delete(ctx, &ns, client.PropagationPolicy(metav1.DeletePropagationForeground))).
|
|
Should(SatisfyAny(BeNil(), &util.NotFoundMatcher{}))
|
|
logf.Log.Info("make sure all the resources are removed")
|
|
objectKey := client.ObjectKey{
|
|
Name: namespace,
|
|
}
|
|
res := &corev1.Namespace{}
|
|
Eventually(
|
|
// gomega has a bug that can't take nil as the actual input, so has to make it a func
|
|
func() error {
|
|
return k8sClient.Get(ctx, objectKey, res)
|
|
},
|
|
time.Second*120, time.Millisecond*500).Should(&util.NotFoundMatcher{})
|
|
Eventually(
|
|
func() error {
|
|
return k8sClient.Create(ctx, &ns)
|
|
},
|
|
time.Second*3, time.Millisecond*300).Should(SatisfyAny(BeNil(), &util.AlreadyExistMatcher{}))
|
|
})
|
|
|
|
AfterEach(func() {
|
|
logf.Log.Info("Clean up resources")
|
|
// delete the namespace with all its resources
|
|
Expect(k8sClient.Delete(ctx, &ns, client.PropagationPolicy(metav1.DeletePropagationForeground))).Should(BeNil())
|
|
})
|
|
|
|
When("create or update a component", func() {
|
|
It("should create corresponding ControllerRevision", func() {
|
|
By("Create Component v1")
|
|
Expect(k8sClient.Create(ctx, &componentV1)).Should(Succeed())
|
|
|
|
cmpV1 := &v1alpha2.Component{}
|
|
By("Get Component v1")
|
|
Expect(k8sClient.Get(ctx, client.ObjectKey{Namespace: namespace, Name: componentName}, cmpV1)).Should(Succeed())
|
|
|
|
By("Get Component latest status after ControllerRevision created")
|
|
Eventually(
|
|
func() *commontypes.Revision {
|
|
k8sClient.Get(ctx, client.ObjectKey{Namespace: namespace, Name: componentName}, cmpV1)
|
|
return cmpV1.Status.LatestRevision
|
|
},
|
|
time.Second*15, time.Millisecond*500).ShouldNot(BeNil())
|
|
|
|
revisionNameV1 := cmpV1.Status.LatestRevision.Name
|
|
By("Get corresponding ControllerRevision of Component v1")
|
|
cr := &appsv1.ControllerRevision{}
|
|
Expect(k8sClient.Get(ctx,
|
|
client.ObjectKey{Namespace: namespace, Name: revisionNameV1}, cr)).ShouldNot(HaveOccurred())
|
|
By("Check revision seq number")
|
|
Expect(cr.Revision).Should(Equal(int64(1)))
|
|
|
|
cwV2raw, _ := json.Marshal(cwV2)
|
|
cmpV1.Spec.Workload.Raw = cwV2raw
|
|
By("Update Component into revision v2")
|
|
Expect(k8sClient.Update(ctx, cmpV1)).Should(Succeed())
|
|
|
|
cmpV2 := &v1alpha2.Component{}
|
|
By("Get Component v2")
|
|
Expect(k8sClient.Get(ctx, client.ObjectKey{Namespace: namespace, Name: componentName}, cmpV2)).Should(Succeed())
|
|
|
|
By("Get Component latest status after ControllerRevision created")
|
|
Eventually(
|
|
func() string {
|
|
k8sClient.Get(ctx, client.ObjectKey{Namespace: namespace, Name: componentName}, cmpV2)
|
|
return cmpV2.Status.LatestRevision.Name
|
|
},
|
|
time.Second*15, time.Millisecond*500).ShouldNot(Equal(revisionNameV1))
|
|
|
|
revisionNameV2 := cmpV2.Status.LatestRevision.Name
|
|
crV2 := &appsv1.ControllerRevision{}
|
|
By("Get corresponding ControllerRevision of Component v2")
|
|
Expect(k8sClient.Get(ctx, client.ObjectKey{Namespace: namespace, Name: revisionNameV2}, crV2)).Should(Succeed())
|
|
By("Check revision seq number")
|
|
Expect(crV2.Revision).Should(Equal(int64(2)))
|
|
|
|
})
|
|
})
|
|
|
|
When("Components have revisionName in AppConfig", func() {
|
|
It("should NOT create NOR update workloads, when update components", func() {
|
|
By("Create Component v1")
|
|
Expect(k8sClient.Create(ctx, &componentV1)).Should(Succeed())
|
|
|
|
cmpV1 := &v1alpha2.Component{}
|
|
By("Get Component v1")
|
|
Expect(k8sClient.Get(ctx, client.ObjectKey{Namespace: namespace, Name: componentName}, cmpV1)).Should(Succeed())
|
|
|
|
By("Get Component latest status after ControllerRevision created")
|
|
Eventually(
|
|
func() *commontypes.Revision {
|
|
k8sClient.Get(ctx, client.ObjectKey{Namespace: namespace, Name: componentName}, cmpV1)
|
|
return cmpV1.Status.LatestRevision
|
|
},
|
|
time.Second*15, time.Millisecond*500).ShouldNot(BeNil())
|
|
|
|
revisionNameV1 := cmpV1.Status.LatestRevision.Name
|
|
|
|
appConfigWithRevisionName := appConfig
|
|
appConfigWithRevisionName.Spec.Components = append(appConfigWithRevisionName.Spec.Components,
|
|
v1alpha2.ApplicationConfigurationComponent{
|
|
RevisionName: revisionNameV1,
|
|
})
|
|
By("Apply appConfig")
|
|
Expect(k8sClient.Create(ctx, &appConfigWithRevisionName)).Should(Succeed())
|
|
|
|
cwWlV1 := v1alpha2.ContainerizedWorkload{}
|
|
By("Check ContainerizedWorkload workload's image field is v1")
|
|
Eventually(
|
|
func() error {
|
|
return k8sClient.Get(ctx, client.ObjectKey{Namespace: namespace, Name: componentName}, &cwWlV1)
|
|
},
|
|
time.Second*15, time.Millisecond*500).Should(BeNil())
|
|
Expect(cwWlV1.Spec.Containers[0].Image).Should(Equal(imageV1))
|
|
|
|
cwV2raw, _ := json.Marshal(cwV2)
|
|
cmpV1.Spec.Workload.Raw = cwV2raw
|
|
By("Update Component to revision v2")
|
|
Expect(k8sClient.Update(ctx, cmpV1)).Should(Succeed())
|
|
|
|
By("Check ContainerizedWorkload workload's image field is still v1")
|
|
Expect(k8sClient.Get(ctx, client.ObjectKey{Namespace: namespace, Name: componentName}, &cwWlV1)).Should(Succeed())
|
|
Expect(cwWlV1.Spec.Containers[0].Image).Should(Equal(imageV1))
|
|
})
|
|
})
|
|
|
|
When("Components have componentName", func() {
|
|
It("should update workloads with new revision of components, when update components", func() {
|
|
By("Create Component v1")
|
|
Expect(k8sClient.Create(ctx, &componentV1)).Should(Succeed())
|
|
|
|
cmpV1 := &v1alpha2.Component{}
|
|
By("Get Component latest status after ControllerRevision created")
|
|
Eventually(
|
|
func() *commontypes.Revision {
|
|
k8sClient.Get(ctx, client.ObjectKey{Namespace: namespace, Name: componentName}, cmpV1)
|
|
return cmpV1.Status.LatestRevision
|
|
},
|
|
time.Second*30, time.Millisecond*500).ShouldNot(BeNil())
|
|
|
|
revisionNameV1 := cmpV1.Status.LatestRevision.Name
|
|
|
|
appConfigWithRevisionName := appConfig
|
|
appConfigWithRevisionName.Spec.Components = append(appConfigWithRevisionName.Spec.Components,
|
|
v1alpha2.ApplicationConfigurationComponent{
|
|
ComponentName: componentName,
|
|
})
|
|
By("Apply appConfig")
|
|
Expect(k8sClient.Create(ctx, &appConfigWithRevisionName)).Should(Succeed())
|
|
|
|
cwWlV1 := &v1alpha2.ContainerizedWorkload{}
|
|
By("Check ContainerizedWorkload workload's image field is v1")
|
|
Eventually(
|
|
func() error {
|
|
return k8sClient.Get(ctx, client.ObjectKey{Namespace: namespace, Name: componentName}, cwWlV1)
|
|
},
|
|
time.Second*15, time.Millisecond*500).Should(BeNil())
|
|
Expect(cwWlV1.Spec.Containers[0].Image).Should(Equal(imageV1))
|
|
|
|
cwV2raw, _ := json.Marshal(cwV2)
|
|
cmpV1.Spec.Workload.Raw = cwV2raw
|
|
By("Update Component to revision v2")
|
|
Expect(k8sClient.Update(ctx, cmpV1)).Should(Succeed())
|
|
|
|
By("Check Component has been changed to revision v2")
|
|
By("Get latest Component revision: revision 2")
|
|
cmpV2 := &v1alpha2.Component{}
|
|
Eventually(
|
|
func() string {
|
|
k8sClient.Get(ctx, client.ObjectKey{Namespace: namespace, Name: componentName}, cmpV2)
|
|
return cmpV2.Status.LatestRevision.Name
|
|
},
|
|
time.Second*30, time.Millisecond*500).ShouldNot(Equal(revisionNameV1))
|
|
|
|
By("Check ContainerizedWorkload workload's image field has been changed to v2")
|
|
cwWlV2 := &v1alpha2.ContainerizedWorkload{}
|
|
Eventually(func() string {
|
|
requestReconcileNow(ctx, &appConfigWithRevisionName)
|
|
k8sClient.Get(ctx, client.ObjectKey{Namespace: namespace, Name: componentName}, cwWlV2)
|
|
return cwWlV2.Spec.Containers[0].Image
|
|
}, time.Second*60, time.Microsecond*500).Should(Equal(imageV2))
|
|
})
|
|
})
|
|
|
|
When("Components have componentName and have revision-enabled trait", func() {
|
|
It("should create workloads with name of revision and keep the old revision", func() {
|
|
|
|
By("Create trait definition")
|
|
var td v1alpha2.TraitDefinition
|
|
Expect(common.ReadYamlToObject("testdata/revision/trait-def.yaml", &td)).Should(BeNil())
|
|
|
|
var gtd v1alpha2.TraitDefinition
|
|
if err := k8sClient.Get(ctx, client.ObjectKey{Name: td.Name, Namespace: td.Namespace}, >d); err != nil {
|
|
Expect(k8sClient.Create(ctx, &td)).Should(Succeed())
|
|
} else {
|
|
td.ResourceVersion = gtd.ResourceVersion
|
|
Expect(k8sClient.Update(ctx, &td)).Should(Succeed())
|
|
}
|
|
|
|
By("Create Component v1")
|
|
var comp1 v1alpha2.Component
|
|
Expect(common.ReadYamlToObject("testdata/revision/comp-v1.yaml", &comp1)).Should(BeNil())
|
|
Expect(k8sClient.Create(ctx, &comp1)).Should(Succeed())
|
|
|
|
By("Check component should already existed")
|
|
Eventually(func() error {
|
|
return k8sClient.Get(ctx, client.ObjectKey{Namespace: comp1.Namespace, Name: comp1.Name}, &v1alpha2.Component{})
|
|
}, time.Second*10, time.Microsecond*500).Should(BeNil())
|
|
|
|
By("Create AppConfig with component")
|
|
var appconfig v1alpha2.ApplicationConfiguration
|
|
Expect(common.ReadYamlToObject("testdata/revision/app.yaml", &appconfig)).Should(BeNil())
|
|
Expect(k8sClient.Create(ctx, &appconfig)).Should(Succeed())
|
|
|
|
By("Get Component latest status after ControllerRevision created")
|
|
Eventually(
|
|
func() *commontypes.Revision {
|
|
k8sClient.Get(ctx, client.ObjectKey{Namespace: namespace, Name: componentName}, &comp1)
|
|
return comp1.Status.LatestRevision
|
|
},
|
|
time.Second*300, time.Millisecond*500).ShouldNot(BeNil())
|
|
|
|
revisionNameV1 := comp1.Status.LatestRevision.Name
|
|
|
|
By("Workload created with revisionName v1")
|
|
var w1 unstructured.Unstructured
|
|
Eventually(
|
|
func() error {
|
|
requestReconcileNow(ctx, &appconfig)
|
|
w1.SetAPIVersion("example.com/v1")
|
|
w1.SetKind("Bar")
|
|
return k8sClient.Get(ctx, client.ObjectKey{Namespace: namespace, Name: revisionNameV1}, &w1)
|
|
},
|
|
time.Second*60, time.Millisecond*500).Should(BeNil())
|
|
k1, _, _ := unstructured.NestedString(w1.Object, "spec", "key")
|
|
Expect(k1).Should(BeEquivalentTo("v1"), fmt.Sprintf("%v", w1.Object))
|
|
|
|
By("Create Component v2")
|
|
var comp2 v1alpha2.Component
|
|
Expect(common.ReadYamlToObject("testdata/revision/comp-v2.yaml", &comp2)).Should(BeNil())
|
|
comp2.ResourceVersion = comp1.ResourceVersion
|
|
Expect(k8sClient.Update(ctx, &comp2)).Should(Succeed())
|
|
|
|
By("Get Component latest status after ControllerRevision created")
|
|
Eventually(
|
|
func() *commontypes.Revision {
|
|
k8sClient.Get(ctx, client.ObjectKey{Namespace: namespace, Name: componentName}, &comp2)
|
|
if comp2.Status.LatestRevision != nil && comp2.Status.LatestRevision.Revision > 1 {
|
|
return comp2.Status.LatestRevision
|
|
}
|
|
return nil
|
|
},
|
|
time.Second*120, time.Millisecond*500).ShouldNot(BeNil())
|
|
|
|
revisionNameV2 := comp2.Status.LatestRevision.Name
|
|
|
|
By("Workload exist with revisionName v2")
|
|
var w2 unstructured.Unstructured
|
|
Eventually(
|
|
func() error {
|
|
requestReconcileNow(ctx, &appconfig)
|
|
w2.SetAPIVersion("example.com/v1")
|
|
w2.SetKind("Bar")
|
|
return k8sClient.Get(ctx, client.ObjectKey{Namespace: namespace, Name: revisionNameV2}, &w2)
|
|
},
|
|
time.Second*30, time.Millisecond*500).Should(BeNil())
|
|
k2, _, _ := unstructured.NestedString(w2.Object, "spec", "key")
|
|
Expect(k2).Should(BeEquivalentTo("v2"), fmt.Sprintf("%v", w2.Object))
|
|
|
|
By("Check AppConfig status")
|
|
Eventually(
|
|
func() string {
|
|
err := k8sClient.Get(ctx, client.ObjectKey{Namespace: namespace, Name: appconfig.Name}, &appconfig)
|
|
if err != nil {
|
|
return ""
|
|
}
|
|
if len(appconfig.Status.Workloads) == 0 {
|
|
return ""
|
|
}
|
|
return appconfig.Status.Workloads[0].ComponentRevisionName
|
|
},
|
|
time.Second*60, time.Millisecond*500).Should(BeEquivalentTo(revisionNameV2))
|
|
|
|
Expect(len(appconfig.Status.Workloads)).Should(BeEquivalentTo(1))
|
|
|
|
Expect(len(appconfig.Status.HistoryWorkloads)).Should(BeEquivalentTo(1))
|
|
Expect(appconfig.Status.HistoryWorkloads[0].Revision).Should(BeEquivalentTo(revisionNameV1))
|
|
|
|
// Clean
|
|
k8sClient.Delete(ctx, &appconfig)
|
|
k8sClient.Delete(ctx, &comp1)
|
|
k8sClient.Delete(ctx, &comp2)
|
|
})
|
|
})
|
|
|
|
When("Components have componentName and without revision-enabled trait", func() {
|
|
It("should create workloads with name of component and replace the old revision", func() {
|
|
|
|
By("Create trait definition")
|
|
var td v1alpha2.TraitDefinition
|
|
Expect(common.ReadYamlToObject("testdata/revision/trait-def-no-revision.yaml", &td)).Should(BeNil())
|
|
var gtd v1alpha2.TraitDefinition
|
|
if err := k8sClient.Get(ctx, client.ObjectKey{Name: td.Name, Namespace: td.Namespace}, >d); err != nil {
|
|
Expect(k8sClient.Create(ctx, &td)).Should(Succeed())
|
|
} else {
|
|
td.ResourceVersion = gtd.ResourceVersion
|
|
Expect(k8sClient.Update(ctx, &td)).Should(Succeed())
|
|
}
|
|
|
|
By("Create Component v1")
|
|
var comp1 v1alpha2.Component
|
|
Expect(common.ReadYamlToObject("testdata/revision/comp-v1.yaml", &comp1)).Should(BeNil())
|
|
Expect(k8sClient.Create(ctx, &comp1)).Should(Succeed())
|
|
|
|
By("Create AppConfig with component")
|
|
var appconfig v1alpha2.ApplicationConfiguration
|
|
Expect(common.ReadYamlToObject("testdata/revision/app.yaml", &appconfig)).Should(BeNil())
|
|
Expect(k8sClient.Create(ctx, &appconfig)).Should(Succeed())
|
|
|
|
By("Workload created with component name")
|
|
var w1 unstructured.Unstructured
|
|
Eventually(
|
|
func() error {
|
|
w1.SetAPIVersion("example.com/v1")
|
|
w1.SetKind("Bar")
|
|
return k8sClient.Get(ctx, client.ObjectKey{Namespace: namespace, Name: componentName}, &w1)
|
|
},
|
|
time.Second*60, time.Millisecond*500).Should(BeNil())
|
|
|
|
k1, _, _ := unstructured.NestedString(w1.Object, "spec", "key")
|
|
Expect(k1).Should(BeEquivalentTo("v1"), fmt.Sprintf("%v", w1.Object))
|
|
|
|
By("Create Component v2")
|
|
var comp2 v1alpha2.Component
|
|
Expect(common.ReadYamlToObject("testdata/revision/comp-v2.yaml", &comp2)).Should(BeNil())
|
|
Eventually(func() error {
|
|
tmp := &v1alpha2.Component{}
|
|
k8sClient.Get(ctx, client.ObjectKey{Namespace: namespace, Name: componentName}, tmp)
|
|
updatedComp := comp2.DeepCopy()
|
|
updatedComp.ResourceVersion = tmp.ResourceVersion
|
|
return k8sClient.Update(ctx, updatedComp)
|
|
}, 5*time.Second, time.Second).Should(Succeed())
|
|
|
|
By("Workload exist with revisionName v2")
|
|
var w2 unstructured.Unstructured
|
|
Eventually(
|
|
func() string {
|
|
requestReconcileNow(ctx, &appconfig)
|
|
w2.SetAPIVersion("example.com/v1")
|
|
w2.SetKind("Bar")
|
|
err := k8sClient.Get(ctx, client.ObjectKey{Namespace: namespace, Name: componentName}, &w2)
|
|
if err != nil {
|
|
return ""
|
|
}
|
|
k2, _, _ := unstructured.NestedString(w2.Object, "spec", "key")
|
|
return k2
|
|
},
|
|
time.Second*30, time.Millisecond*500).Should(BeEquivalentTo("v2"))
|
|
|
|
By("Check AppConfig status")
|
|
Eventually(
|
|
func() error {
|
|
return k8sClient.Get(ctx, client.ObjectKey{Namespace: namespace, Name: appconfig.Name}, &appconfig)
|
|
},
|
|
time.Second*15, time.Millisecond*500).Should(BeNil())
|
|
|
|
Expect(len(appconfig.Status.Workloads)).Should(BeEquivalentTo(1))
|
|
|
|
// Clean
|
|
k8sClient.Delete(ctx, &appconfig)
|
|
k8sClient.Delete(ctx, &comp1)
|
|
k8sClient.Delete(ctx, &comp2)
|
|
})
|
|
})
|
|
})
|
|
|
|
var _ = Describe("Component revision", func() {
|
|
ctx := context.Background()
|
|
apiVersion := "core.oam.dev/v1alpha2"
|
|
namespace := "default"
|
|
componentName := "revision-component"
|
|
appConfigName := "revision-app"
|
|
workload := appsv1.Deployment{
|
|
TypeMeta: metav1.TypeMeta{APIVersion: "apps/v1", Kind: "Deployment"},
|
|
ObjectMeta: metav1.ObjectMeta{Namespace: namespace},
|
|
Spec: appsv1.DeploymentSpec{
|
|
Selector: &metav1.LabelSelector{
|
|
MatchLabels: map[string]string{
|
|
"app": "nginx",
|
|
},
|
|
},
|
|
Template: corev1.PodTemplateSpec{
|
|
Spec: corev1.PodSpec{
|
|
Containers: []corev1.Container{
|
|
{Name: "nginx",
|
|
Image: "nginx:1.9.4"},
|
|
},
|
|
},
|
|
ObjectMeta: metav1.ObjectMeta{Labels: map[string]string{"app": "nginx"}},
|
|
},
|
|
},
|
|
}
|
|
component := v1alpha2.Component{
|
|
TypeMeta: metav1.TypeMeta{
|
|
APIVersion: apiVersion,
|
|
Kind: "Component",
|
|
},
|
|
ObjectMeta: metav1.ObjectMeta{Name: componentName, Namespace: namespace},
|
|
Spec: v1alpha2.ComponentSpec{
|
|
Workload: runtime.RawExtension{Object: workload.DeepCopyObject()},
|
|
},
|
|
}
|
|
|
|
TraitDefinition := v1alpha2.TraitDefinition{
|
|
TypeMeta: metav1.TypeMeta{
|
|
APIVersion: apiVersion,
|
|
Kind: "TraitDefinition",
|
|
},
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "manualscalertraits2.core.oam.dev",
|
|
Namespace: "vela-system",
|
|
},
|
|
Spec: v1alpha2.TraitDefinitionSpec{
|
|
RevisionEnabled: true,
|
|
Reference: commontypes.DefinitionReference{
|
|
Name: "manualscalertraits.core.oam.dev",
|
|
},
|
|
WorkloadRefPath: "spec.workloadRef",
|
|
},
|
|
}
|
|
|
|
appConfig := v1alpha2.ApplicationConfiguration{
|
|
TypeMeta: metav1.TypeMeta{
|
|
APIVersion: apiVersion,
|
|
Kind: "ApplicationConfiguration",
|
|
},
|
|
ObjectMeta: metav1.ObjectMeta{Name: appConfigName, Namespace: namespace},
|
|
Spec: v1alpha2.ApplicationConfigurationSpec{
|
|
Components: []v1alpha2.ApplicationConfigurationComponent{{
|
|
ComponentName: componentName},
|
|
},
|
|
},
|
|
}
|
|
|
|
workloadObjKey := client.ObjectKey{Name: componentName, Namespace: namespace}
|
|
appConfigObjKey := client.ObjectKey{Name: appConfigName, Namespace: namespace}
|
|
|
|
trait := v1alpha2.ManualScalerTrait{
|
|
TypeMeta: metav1.TypeMeta{
|
|
APIVersion: apiVersion,
|
|
Kind: "ManualScalerTrait",
|
|
},
|
|
ObjectMeta: metav1.ObjectMeta{Name: appConfigName, Namespace: namespace},
|
|
Spec: v1alpha2.ManualScalerTraitSpec{
|
|
ReplicaCount: 2,
|
|
},
|
|
}
|
|
|
|
Context("Attach a revision-enable trait the first time, workload should not be recreated", func() {
|
|
It("should create Component and ApplicationConfiguration", func() {
|
|
By("submit Component")
|
|
Expect(k8sClient.Create(ctx, &component)).Should(Succeed())
|
|
By("check Component exist")
|
|
Eventually(
|
|
func() error {
|
|
return k8sClient.Get(ctx, client.ObjectKey{Namespace: namespace, Name: componentName}, &v1alpha2.Component{})
|
|
},
|
|
time.Second*3, time.Millisecond*500).Should(BeNil())
|
|
By("submit ApplicationConfiguration")
|
|
Expect(k8sClient.Create(ctx, &appConfig)).Should(Succeed())
|
|
|
|
By("check workload")
|
|
var deploy appsv1.Deployment
|
|
Eventually(
|
|
func() error {
|
|
return k8sClient.Get(ctx, workloadObjKey, &deploy)
|
|
},
|
|
time.Second*15, time.Millisecond*500).Should(BeNil())
|
|
|
|
By("apply new ApplicationConfiguration with a revision enabled trait")
|
|
Expect(k8sClient.Create(ctx, &TraitDefinition)).Should(Succeed())
|
|
Expect(k8sClient.Get(ctx, appConfigObjKey, &appConfig)).Should(Succeed())
|
|
updatedAppConfig := appConfig.DeepCopy()
|
|
updatedAppConfig.Spec.Components[0].Traits = []v1alpha2.ComponentTrait{{Trait: runtime.RawExtension{Object: trait.DeepCopyObject()}}}
|
|
updatedAppConfig.SetResourceVersion("")
|
|
Expect(k8sClient.Patch(ctx, updatedAppConfig, client.Merge)).Should(Succeed())
|
|
|
|
By("check current workload exists")
|
|
time.Sleep(3 * time.Second)
|
|
var currentDeploy appsv1.Deployment
|
|
Expect(k8sClient.Get(ctx, workloadObjKey, ¤tDeploy)).Should(BeNil())
|
|
|
|
By("check version 1 workload doesn't exist")
|
|
var v1Deploy appsv1.Deployment
|
|
workloadObjKey := client.ObjectKey{Name: componentName + "-v1", Namespace: namespace}
|
|
Expect(k8sClient.Get(ctx, workloadObjKey, &v1Deploy)).Should(SatisfyAny(&util.NotFoundMatcher{}))
|
|
})
|
|
})
|
|
|
|
AfterEach(func() {
|
|
k8sClient.Delete(ctx, &appConfig)
|
|
k8sClient.Delete(ctx, &component)
|
|
k8sClient.Delete(ctx, &TraitDefinition)
|
|
})
|
|
})
|