Refactor: E2E Apply tests refactored (#58)

* Refactored E2E apply_tests.
This commit is contained in:
Sean Hobbs 2022-07-14 11:10:47 -07:00 коммит произвёл GitHub
Родитель a88c950d71
Коммит 5498a9bd2e
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
10 изменённых файлов: 437 добавлений и 408 удалений

Просмотреть файл

@ -1,29 +1,13 @@
/*
Copyright 2021 The Kubernetes 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
import (
"context"
"time"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
v1 "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"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/util/json"
@ -32,425 +16,464 @@ import (
workapi "sigs.k8s.io/work-api/pkg/apis/v1alpha1"
)
type manifestDetails struct {
Manifest workapi.Manifest
GVK *schema.GroupVersionKind
GVR *schema.GroupVersionResource
ObjMeta metav1.ObjectMeta
}
const (
eventuallyTimeout = 60 // seconds
eventuallyTimeout = 10 // seconds
eventuallyInterval = 1 // seconds
)
var (
appliedWork *workapi.AppliedWork
createdWork *workapi.Work
createError error
deleteError error
getError error
updateError error
manifests []string
manifestMetaName []string
manifestMetaNamespaces []string
workName string
workNamespace string
var _ = Describe("Work creation", func() {
_ = Describe("Work", func() {
manifestMetaName = []string{"test-nginx", "test-nginx", "test-configmap"} //Todo - Unmarshal raw file bytes into JSON, extract key / values programmatically.
manifestMetaNamespaces = []string{"default", "default", "default"} //Todo - Unmarshal raw file bytes into JSON, extract key / values programmatically.
WorkCreatedContext(
"with a Work resource that has two manifests: Deployment & Service",
[]string{
"manifests/test-deployment.yaml",
"manifests/test-service.yaml",
})
// The Manifests' ordinal must be respected; some tests reference by ordinal.
manifests = []string{
"testmanifests/test-deployment.yaml",
"testmanifests/test-service.yaml",
"testmanifests/test-configmap.yaml",
}
WorkCreatedWithCRDContext(
"with a CRD manifest",
[]string{
"manifests/test-crd.yaml",
})
})
var _ = Describe("Work modification", func() {
WorkUpdateWithDependencyContext(
"with two newly added manifests: configmap & namespace",
[]string{
"manifests/test-secret.yaml",
},
[]string{
"manifests/test-configmap.ns.yaml",
"manifests/test-namespace.yaml",
})
WorkUpdateWithModifiedManifestContext(
"with a modified manifest",
[]string{
"manifests/test-configmap.yaml",
})
WorkUpdateWithReplacedManifestsContext(
"with all manifests replaced",
[]string{
"manifests/test-deployment.yaml",
"manifests/test-service.yaml",
},
[]string{
"manifests/test-configmap.yaml",
})
})
var _ = Describe("Work deletion", func() {
WorkDeletedContext(
"with a deletion request",
[]string{
"manifests/test-secret.yaml",
})
})
var WorkCreatedContext = func(description string, manifestFiles []string) bool {
return Context(description, func() {
var createdWork *workapi.Work
var err error
var mDetails []manifestDetails
BeforeEach(func() {
workName = "work-" + utilrand.String(5)
workNamespace = "default"
mDetails = generateManifestDetails(manifestFiles)
createdWork, createError = createWork(workName, workNamespace, manifests)
Expect(createError).ToNot(HaveOccurred())
workObj := createWorkObj(
utilrand.String(5),
"default",
mDetails,
)
createdWork, err = createWorkResource(workObj)
Expect(err).ToNot(HaveOccurred())
})
AfterEach(func() {
deleteError = safeDeleteWork(createdWork)
err = deleteWorkResource(createdWork.Namespace, createdWork.Name)
Expect(err).ToNot(HaveOccurred())
})
Describe("created on the Hub", func() {
// Todo - It would be better to have context of N manifest, and let the type be programmatically determined.
Context("with a Service & Deployment & Configmap manifest", func() {
It("should have a Work resource in the hub", func() {
Eventually(func() error {
_, err := retrieveWork(createdWork.Namespace, createdWork.Name)
It("should have created: a respective AppliedWork, and the resources specified in the Work's manifests", func() {
By("verifying an AppliedWork was created")
Eventually(func() error {
_, err := spokeWorkClient.MulticlusterV1alpha1().AppliedWorks().Get(context.Background(), createdWork.Name, metav1.GetOptions{})
return err
}, eventuallyTimeout, eventuallyInterval).ShouldNot(HaveOccurred())
return err
}, eventuallyTimeout, eventuallyInterval).ShouldNot(HaveOccurred())
})
It("should have an AppliedWork resource in the spoke ", func() {
Eventually(func() error {
_, err := spokeWorkClient.MulticlusterV1alpha1().AppliedWorks().Get(context.Background(), createdWork.Name, metav1.GetOptions{})
By("verifying a deployment was created")
Eventually(func() error {
_, err := spokeKubeClient.AppsV1().Deployments(mDetails[0].ObjMeta.Namespace).
Get(context.Background(), mDetails[0].ObjMeta.Name, metav1.GetOptions{})
return err
}, eventuallyTimeout, eventuallyInterval).ShouldNot(HaveOccurred())
})
It("should have created a Kubernetes deployment", func() {
Eventually(func() error {
_, err := spokeKubeClient.AppsV1().Deployments(manifestMetaNamespaces[0]).Get(context.Background(), manifestMetaName[0], metav1.GetOptions{})
return err
}, eventuallyTimeout, eventuallyInterval).ShouldNot(HaveOccurred())
return err
}, eventuallyTimeout, eventuallyInterval).ShouldNot(HaveOccurred())
})
It("should have created a Kubernetes service", func() {
Eventually(func() error {
_, err := spokeKubeClient.CoreV1().Services(manifestMetaNamespaces[1]).Get(context.Background(), manifestMetaName[1], metav1.GetOptions{})
By("verifying a service was created")
Eventually(func() error {
_, err := spokeKubeClient.CoreV1().Services(mDetails[1].ObjMeta.Namespace).
Get(context.Background(), mDetails[1].ObjMeta.Name, metav1.GetOptions{})
return err
}, eventuallyTimeout, eventuallyInterval).ShouldNot(HaveOccurred())
})
It("should have created a ConfigMap", func() {
Eventually(func() error {
_, err := spokeKubeClient.CoreV1().ConfigMaps(manifestMetaNamespaces[2]).Get(context.Background(), manifestMetaName[2], metav1.GetOptions{})
return err
}, eventuallyTimeout, eventuallyInterval).ShouldNot(HaveOccurred())
})
})
})
Describe("updated on the Hub", func() {
Context("with two new configmap & namespace manifests, where the configmap is dependent upon the namespace", func() {
// The order of these appended manifests is intentional. The test ensures that the configmap
// will eventually get created. The first attempt will fail as the namespace is created after.
manifests = append(manifests, "testmanifests/test-testns.configmap.yaml")
manifestMetaName = append(manifestMetaName, "test-configmap")
manifestMetaNamespaces = append(manifestMetaNamespaces, "test-namespace")
manifests = append(manifests, "testmanifests/test-namespace.yaml")
manifestMetaName = append(manifestMetaName, "test-namespace")
manifestMetaNamespaces = append(manifestMetaNamespaces, "")
BeforeEach(func() {
var work *workapi.Work
var err error
By("getting the existing Work resource on the hub")
Eventually(func() error {
work, err = retrieveWork(createdWork.Namespace, createdWork.Name)
return err
}, eventuallyTimeout, eventuallyInterval).ShouldNot(HaveOccurred())
By("adding the new manifest to the Work resource and updating it on the hub")
Eventually(func() error {
addManifestsToWorkSpec([]string{manifests[3], manifests[4]}, &work.Spec)
_, err = updateWork(work)
return err
}, eventuallyTimeout, eventuallyInterval).ShouldNot(HaveOccurred())
})
It("should have created the namespace", func() {
Eventually(func() error {
_, err := spokeKubeClient.CoreV1().Namespaces().Get(context.Background(), manifestMetaName[4], metav1.GetOptions{})
return err
}, eventuallyTimeout, eventuallyInterval).ShouldNot(HaveOccurred())
})
It("should have created the ConfigMap in the new namespace", func() {
Eventually(func() error {
_, err := spokeKubeClient.CoreV1().ConfigMaps(manifestMetaNamespaces[3]).Get(context.Background(), manifestMetaName[3], metav1.GetOptions{})
return err
}, eventuallyTimeout, eventuallyInterval).ShouldNot(HaveOccurred())
})
})
Context("with a modified manifest", func() {
// Todo, refactor this context to not use "over complicated structure".
var cm v1.ConfigMap
var newDataKey string
var newDataValue string
var configMapName string
var configMapNamespace string
BeforeEach(func() {
configManifest := createdWork.Spec.Workload.Manifests[2]
// Unmarshal the data into a struct, modify and then update it.
err := json.Unmarshal(configManifest.Raw, &cm)
Expect(err).ToNot(HaveOccurred())
configMapName = cm.Name
configMapNamespace = cm.Namespace
// Add random new key value pair into map.
newDataKey = utilrand.String(5)
newDataValue = utilrand.String(5)
cm.Data[newDataKey] = newDataValue
rawManifest, err := json.Marshal(cm)
Expect(err).ToNot(HaveOccurred())
updatedManifest := workapi.Manifest{}
updatedManifest.Raw = rawManifest
createdWork.Spec.Workload.Manifests[2] = updatedManifest
By("updating a manifest specification within the existing Work on the hub")
createdWork, updateError = updateWork(createdWork)
Expect(updateError).ToNot(HaveOccurred())
})
It("should reapply the manifest.", func() {
Eventually(func() bool {
configMap, _ := spokeKubeClient.CoreV1().ConfigMaps(configMapNamespace).Get(context.Background(), configMapName, metav1.GetOptions{})
return configMap.Data[newDataKey] == newDataValue
}, eventuallyTimeout, eventuallyInterval).Should(BeTrue())
})
})
Context("with a added, modified, and removed manifest", func() {
// Todo, refactor this context to not use "over complicated structure".
var cm v1.ConfigMap
var err error
var newDataKey string
var newDataValue string
var configMapName string
var configMapNamespace string
var namespaceToDelete string
It("should create a secret, modify the existing configmap, and remove the second configmap, from within the spoke", func() {
By("getting the existing Work resource on the hub")
Eventually(func() error {
createdWork, err = retrieveWork(createdWork.Namespace, createdWork.Name)
return err
}, eventuallyTimeout, eventuallyInterval).ShouldNot(HaveOccurred())
By("removing the test-namespace manifest")
namespaceToDelete = manifestMetaName[4]
manifests = manifests[:4]
manifestMetaName = manifestMetaName[:4]
manifestMetaNamespaces = manifestMetaNamespaces[:4]
createdWork.Spec.Workload.Manifests = createdWork.Spec.Workload.Manifests[:4]
By("modifying the existing configmap's manifest values")
configManifest := createdWork.Spec.Workload.Manifests[2]
// Unmarshal the data into a struct, modify and then update it.
err := json.Unmarshal(configManifest.Raw, &cm)
Expect(err).ToNot(HaveOccurred())
configMapName = cm.Name
configMapNamespace = cm.Namespace
// Add random new key value pair into map.
newDataKey = utilrand.String(5)
newDataValue = utilrand.String(5)
cm.Data[newDataKey] = newDataValue
rawManifest, err := json.Marshal(cm)
Expect(err).ToNot(HaveOccurred())
updatedManifest := workapi.Manifest{}
updatedManifest.Raw = rawManifest
createdWork.Spec.Workload.Manifests[2] = updatedManifest
By("adding a secret manifest")
manifests = append(manifests, "testmanifests/test-secret.yaml")
manifestMetaName = append(manifestMetaName, "test-secret")
manifestMetaNamespaces = append(manifestMetaNamespaces, "default")
addManifestsToWorkSpec([]string{manifests[4]}, &createdWork.Spec)
By("updating all manifest changes to the Work resource in the hub")
createdWork, updateError = updateWork(createdWork)
Expect(updateError).ToNot(HaveOccurred())
By("verifying that modified configmap was updated in the spoke")
Eventually(func() bool {
configMap, _ := spokeKubeClient.CoreV1().ConfigMaps(configMapNamespace).Get(context.Background(), configMapName, metav1.GetOptions{})
return configMap.Data[newDataKey] == newDataValue
}, eventuallyTimeout, eventuallyInterval).Should(BeTrue())
By("verifying that the test-namespace was deleted")
Eventually(func() error {
_, err := spokeKubeClient.CoreV1().Namespaces().Get(context.Background(), namespaceToDelete, metav1.GetOptions{})
return err
}, eventuallyTimeout, eventuallyInterval).Should(HaveOccurred())
By("verifying that new secret was created in the spoke")
Eventually(func() error {
_, err := spokeKubeClient.CoreV1().Secrets(manifestMetaNamespaces[4]).Get(context.Background(), manifestMetaName[4], metav1.GetOptions{})
return err
}, eventuallyTimeout, eventuallyInterval).ShouldNot(HaveOccurred())
})
})
Context("with a CRD manifest", func() {
It("should create the CRD on the spoke", func() {
manifests = append(manifests, "testmanifests/test-crd.yaml")
manifestMetaName = append(manifestMetaName, "testcrds.multicluster.x-k8s.io")
manifestMetaNamespaces = append(manifestMetaNamespaces, "default")
By("getting the existing Work resource on the hub")
Eventually(func() error {
createdWork, getError = retrieveWork(createdWork.Namespace, createdWork.Name)
return getError
}, eventuallyTimeout, eventuallyInterval).ShouldNot(HaveOccurred())
By("adding the new manifest to the Work resource and updating it on the hub")
Eventually(func() error {
addManifestsToWorkSpec([]string{manifests[5]}, &createdWork.Spec)
_, updateError = updateWork(createdWork)
return updateError
}, eventuallyTimeout, eventuallyInterval).ShouldNot(HaveOccurred())
Eventually(func() error {
_, getError = spokeApiExtensionClient.ApiextensionsV1().CustomResourceDefinitions().Get(context.Background(), manifestMetaName[5], metav1.GetOptions{})
return getError
}, eventuallyTimeout, eventuallyInterval).ShouldNot(HaveOccurred())
})
})
Context("with all new manifests", func() {
It("should delete all previously applied resources", func() {
manifests = []string{"testmanifests/test-serviceaccount.yaml"}
manifestMetaName = []string{"test-serviceaccount"}
manifestMetaNamespaces = []string{"default"}
aResourceStillExists := true
// Get the current AppliedWork, so we can verify the previously applied resources have been deleted.
time.Sleep(3 * time.Second) // Time for the AppliedWork to be updated by the reconciler.
appliedWork, getError = spokeWorkClient.MulticlusterV1alpha1().AppliedWorks().Get(context.Background(), createdWork.Name, metav1.GetOptions{})
// Get the Work resource and update replace its manifests.
createdWork, getError = hubWorkClient.MulticlusterV1alpha1().Works(createdWork.Namespace).Get(context.Background(), createdWork.Name, metav1.GetOptions{})
Expect(getError).ShouldNot(HaveOccurred())
createdWork.Spec.Workload.Manifests = nil
addManifestsToWorkSpec([]string{manifests[0]}, &createdWork.Spec)
createdWork, updateError = hubWorkClient.MulticlusterV1alpha1().Works(createdWork.Namespace).Update(context.Background(), createdWork, metav1.UpdateOptions{})
Expect(updateError).ShouldNot(HaveOccurred())
By("checking to see if all previously applied works have been deleted", func() {
Eventually(func() bool {
for aResourceStillExists == true {
for _, ar := range appliedWork.Status.AppliedResources {
gvr := schema.GroupVersionResource{
Group: ar.Group,
Version: ar.Version,
Resource: ar.Resource,
}
_, getError = spokeDynamicClient.Resource(gvr).Namespace(ar.Namespace).Get(context.Background(), ar.Name, metav1.GetOptions{})
if getError != nil {
aResourceStillExists = false
} else {
aResourceStillExists = true
}
}
}
return aResourceStillExists
}, eventuallyTimeout, eventuallyInterval).ShouldNot(BeTrue())
})
By("verifying the new manifest was applied", func() {
Eventually(func() error {
_, getError = spokeKubeClient.CoreV1().ServiceAccounts(manifestMetaNamespaces[0]).Get(context.Background(), manifestMetaName[0], metav1.GetOptions{})
return getError
}, eventuallyTimeout, eventuallyInterval).ShouldNot(HaveOccurred())
})
})
})
})
Describe("deleted from the Hub", func() {
BeforeEach(func() {
// Todo - Replace with Eventually.
time.Sleep(2 * time.Second) // Give time for AppliedWork to be created.
// Grab the AppliedWork, so resource garbage collection can be verified.
appliedWork, getError = retrieveAppliedWork(createdWork.Name)
Expect(getError).ToNot(HaveOccurred())
deleteError = safeDeleteWork(createdWork)
Expect(deleteError).ToNot(HaveOccurred())
})
It("should have deleted the Work resource on the hub", func() {
Eventually(func() error {
_, err := retrieveWork(workNamespace, workName)
return err
}, eventuallyTimeout, eventuallyInterval).Should(HaveOccurred())
})
It("should have deleted the resources from the spoke", func() {
Eventually(func() bool {
garbageCollectionComplete := true
for _, resourceMeta := range appliedWork.Status.AppliedResources {
gvr := schema.GroupVersionResource{
Group: resourceMeta.Group,
Version: resourceMeta.Version,
Resource: resourceMeta.Resource,
}
_, err := spokeDynamicClient.Resource(gvr).Get(context.Background(), resourceMeta.Name, metav1.GetOptions{})
if err == nil {
garbageCollectionComplete = false
break
}
}
return garbageCollectionComplete
}, eventuallyTimeout, eventuallyInterval).Should(BeTrue())
})
It("should have deleted the AppliedWork resource from the spoke", func() {
Eventually(func() error {
_, err := retrieveAppliedWork(createdWork.Name)
return err
}, eventuallyTimeout, eventuallyInterval).Should(HaveOccurred())
})
return err
}, eventuallyTimeout, eventuallyInterval).ShouldNot(HaveOccurred())
})
})
)
func addManifestsToWorkSpec(manifestFileRelativePaths []string, workSpec *workapi.WorkSpec) {
for _, file := range manifestFileRelativePaths {
fileRaw, err := testManifestFiles.ReadFile(file)
Expect(err).ToNot(HaveOccurred())
obj, _, err := genericCodec.Decode(fileRaw, nil, nil)
Expect(err).ToNot(HaveOccurred())
workSpec.Workload.Manifests = append(
workSpec.Workload.Manifests, workapi.Manifest{
RawExtension: runtime.RawExtension{Object: obj},
})
}
}
func createWork(workName string, workNamespace string, manifestFiles []string) (*workapi.Work, error) {
var WorkCreatedWithCRDContext = func(description string, manifestFiles []string) bool {
return Context(description, func() {
var createdWork *workapi.Work
var err error
var manifestDetails []manifestDetails
BeforeEach(func() {
manifestDetails = generateManifestDetails(manifestFiles)
workObj := createWorkObj(
utilrand.String(5),
"default",
manifestDetails,
)
createdWork, err = createWorkResource(workObj)
Expect(err).ToNot(HaveOccurred())
})
AfterEach(func() {
err = deleteWorkResource(createdWork.Namespace, createdWork.Name)
Expect(err).ToNot(HaveOccurred())
})
It("should have created the CRD within the spoke", func() {
Eventually(func() error {
By("verifying the CRD exists within the spoke")
_, err = spokeApiExtensionClient.ApiextensionsV1().CustomResourceDefinitions().Get(context.Background(), manifestDetails[0].ObjMeta.Name, metav1.GetOptions{})
return err
}, eventuallyTimeout, eventuallyInterval).ShouldNot(HaveOccurred())
})
})
}
var WorkUpdateWithDependencyContext = func(description string, initialManifestFiles []string, addedManifestFiles []string) bool {
return Context(description, func() {
var createdWork *workapi.Work
var err error
var initialManifestDetails []manifestDetails
var addedManifestDetails []manifestDetails
BeforeEach(func() {
initialManifestDetails = generateManifestDetails(initialManifestFiles)
addedManifestDetails = generateManifestDetails(addedManifestFiles)
workObj := createWorkObj(
utilrand.String(5),
"default",
initialManifestDetails,
)
createdWork, err = createWorkResource(workObj)
Expect(err).ToNot(HaveOccurred())
})
AfterEach(func() {
err = deleteWorkResource(createdWork.Namespace, createdWork.Name)
Expect(err).ToNot(HaveOccurred())
})
It("should have created the ConfigMap in the new namespace", func() {
By("retrieving the existing work and updating it by adding new manifests")
Eventually(func() error {
createdWork, err = retrieveWork(createdWork.Namespace, createdWork.Name)
Expect(err).ToNot(HaveOccurred())
createdWork.Spec.Workload.Manifests = append(createdWork.Spec.Workload.Manifests, addedManifestDetails[0].Manifest, addedManifestDetails[1].Manifest)
createdWork, err = hubWorkClient.MulticlusterV1alpha1().Works(createdWork.Namespace).Update(context.Background(), createdWork, metav1.UpdateOptions{})
return err
}, eventuallyTimeout, eventuallyInterval).ShouldNot(HaveOccurred())
By("checking if the ConfigMap was created in the new namespace")
Eventually(func() error {
_, err := spokeKubeClient.CoreV1().ConfigMaps(addedManifestDetails[0].ObjMeta.Namespace).Get(context.Background(), addedManifestDetails[0].ObjMeta.Name, metav1.GetOptions{})
return err
}, eventuallyTimeout, eventuallyInterval).ShouldNot(HaveOccurred())
By("checking if the new Namespace was created ")
Eventually(func() error {
_, err := spokeKubeClient.CoreV1().Namespaces().Get(context.Background(), addedManifestDetails[1].ObjMeta.Name, metav1.GetOptions{})
return err
}, eventuallyTimeout, eventuallyInterval).ShouldNot(HaveOccurred())
})
})
}
var WorkUpdateWithModifiedManifestContext = func(description string, manifestFiles []string) bool {
return Context(description, func() {
var configMap v1.ConfigMap
var createdWork *workapi.Work
var err error
var manifestDetails []manifestDetails
var newDataKey string
var newDataValue string
BeforeEach(func() {
manifestDetails = generateManifestDetails(manifestFiles)
newDataKey = utilrand.String(5)
newDataValue = utilrand.String(5)
workObj := createWorkObj(
utilrand.String(5),
"default",
manifestDetails,
)
createdWork, err = createWorkResource(workObj)
Expect(err).ToNot(HaveOccurred())
})
AfterEach(func() {
err = deleteWorkResource(createdWork.Namespace, createdWork.Name)
Expect(err).ToNot(HaveOccurred())
})
It("should reapply the manifest's updated spec on the spoke cluster", func() {
By("retrieving the existing work and modifying the manifest")
Eventually(func() error {
createdWork, err = retrieveWork(createdWork.Namespace, createdWork.Name)
// Extract and modify the ConfigMap by adding a new key value pair.
err = json.Unmarshal(createdWork.Spec.Workload.Manifests[0].Raw, &configMap)
configMap.Data[newDataKey] = newDataValue
rawUpdatedManifest, _ := json.Marshal(configMap)
obj, _, _ := genericCodec.Decode(rawUpdatedManifest, nil, nil)
createdWork.Spec.Workload.Manifests[0].Object = obj
createdWork.Spec.Workload.Manifests[0].Raw = rawUpdatedManifest
createdWork, err = hubWorkClient.MulticlusterV1alpha1().Works(createdWork.Namespace).Update(context.Background(), createdWork, metav1.UpdateOptions{})
return err
}, eventuallyTimeout, eventuallyInterval).ShouldNot(HaveOccurred())
By("verifying if the manifest was reapplied")
Eventually(func() bool {
configMap, _ := spokeKubeClient.CoreV1().ConfigMaps(manifestDetails[0].ObjMeta.Namespace).Get(context.Background(), manifestDetails[0].ObjMeta.Name, metav1.GetOptions{})
return configMap.Data[newDataKey] == newDataValue
}, eventuallyTimeout, eventuallyInterval).Should(BeTrue())
})
})
}
var WorkUpdateWithReplacedManifestsContext = func(description string, originalManifestFiles []string, replacedManifestFiles []string) bool {
return Context(description, func() {
var appliedWork *workapi.AppliedWork
var createdWork *workapi.Work
var err error
var originalManifestDetails []manifestDetails
var replacedManifestDetails []manifestDetails
resourcesStillExist := true
BeforeEach(func() {
originalManifestDetails = generateManifestDetails(originalManifestFiles)
replacedManifestDetails = generateManifestDetails(replacedManifestFiles)
workObj := createWorkObj(
utilrand.String(5),
"default",
originalManifestDetails,
)
createdWork, err = createWorkResource(workObj)
Expect(err).ToNot(HaveOccurred())
})
AfterEach(func() {
err = deleteWorkResource(createdWork.Namespace, createdWork.Name)
Expect(err).ToNot(HaveOccurred())
})
It("should have deleted the original Work's resources, and created new resources with the replaced manifests", func() {
By("getting the respective AppliedWork")
Eventually(func() int {
appliedWork, _ = spokeWorkClient.MulticlusterV1alpha1().AppliedWorks().Get(context.Background(), createdWork.Name, metav1.GetOptions{})
return len(appliedWork.Status.AppliedResources)
}, eventuallyTimeout, eventuallyInterval).Should(Equal(len(createdWork.Spec.Workload.Manifests)))
By("updating the Work resource with replaced manifests")
Eventually(func() error {
createdWork, err = hubWorkClient.MulticlusterV1alpha1().Works(createdWork.Namespace).Get(context.Background(), createdWork.Name, metav1.GetOptions{})
createdWork.Spec.Workload.Manifests = nil
for _, mD := range replacedManifestDetails {
createdWork.Spec.Workload.Manifests = append(createdWork.Spec.Workload.Manifests, mD.Manifest)
}
createdWork, err = hubWorkClient.MulticlusterV1alpha1().Works(createdWork.Namespace).Update(context.Background(), createdWork, metav1.UpdateOptions{})
return err
}, eventuallyTimeout, eventuallyInterval).ShouldNot(HaveOccurred())
By("verifying all the initial Work owned resources were deleted")
Eventually(func() bool {
for resourcesStillExist == true {
for _, ar := range appliedWork.Status.AppliedResources {
gvr := schema.GroupVersionResource{
Group: ar.Group,
Version: ar.Version,
Resource: ar.Resource,
}
_, err = spokeDynamicClient.Resource(gvr).Namespace(ar.Namespace).Get(context.Background(), ar.Name, metav1.GetOptions{})
if err != nil {
resourcesStillExist = false
} else {
resourcesStillExist = true
}
}
}
return resourcesStillExist
}, eventuallyTimeout, eventuallyInterval).ShouldNot(BeTrue())
By("verifying the new manifest was applied")
Eventually(func() error {
_, err = spokeKubeClient.CoreV1().ConfigMaps(replacedManifestDetails[0].ObjMeta.Namespace).Get(context.Background(), replacedManifestDetails[0].ObjMeta.Name, metav1.GetOptions{})
return err
}, eventuallyTimeout, eventuallyInterval).ShouldNot(HaveOccurred())
})
})
}
var WorkDeletedContext = func(description string, manifestFiles []string) bool {
return Context(description, func() {
var createdWork *workapi.Work
var err error
var manifestDetails []manifestDetails
BeforeEach(func() {
manifestDetails = generateManifestDetails(manifestFiles)
workObj := createWorkObj(
utilrand.String(5),
"default",
manifestDetails,
)
createdWork, err = createWorkResource(workObj)
Expect(err).ToNot(HaveOccurred())
})
AfterEach(func() {
err = deleteWorkResource(createdWork.Namespace, createdWork.Name)
Expect(err).ToNot(HaveOccurred())
})
It("should delete the Work and verify the resource has been garbage collected", func() {
By("verifying the manifest was applied")
Eventually(func() error {
_, err = spokeKubeClient.CoreV1().Secrets(manifestDetails[0].ObjMeta.Namespace).Get(context.Background(), manifestDetails[0].ObjMeta.Name, metav1.GetOptions{})
return err
}, eventuallyTimeout, eventuallyInterval).ShouldNot(HaveOccurred())
By("deleting the Work resource")
err = deleteWorkResource(createdWork.Namespace, createdWork.Name)
Expect(err).ToNot(HaveOccurred())
By("verifying the resource was garbage collected")
Eventually(func() error {
err = spokeKubeClient.CoreV1().Secrets(manifestDetails[0].ObjMeta.Namespace).Delete(context.Background(), manifestDetails[0].ObjMeta.Name, metav1.DeleteOptions{})
return err
}, eventuallyTimeout, eventuallyInterval).ShouldNot(HaveOccurred())
})
})
}
func createWorkObj(workName string, workNamespace string, manifestDetails []manifestDetails) *workapi.Work {
work := &workapi.Work{
ObjectMeta: metav1.ObjectMeta{
Name: workName,
Namespace: workNamespace,
},
Spec: workapi.WorkSpec{
Workload: workapi.WorkloadTemplate{
Manifests: []workapi.Manifest{},
},
},
}
addManifestsToWorkSpec(manifestFiles, &work.Spec)
createdWork, createError = hubWorkClient.MulticlusterV1alpha1().Works(work.Namespace).Create(context.Background(), work, metav1.CreateOptions{})
return createdWork, createError
}
func retrieveAppliedWork(resourceName string) (*workapi.AppliedWork, error) {
return spokeWorkClient.MulticlusterV1alpha1().AppliedWorks().Get(context.Background(), resourceName, metav1.GetOptions{})
}
func retrieveWork(workNamespace string, workName string) (*workapi.Work, error) {
return hubWorkClient.MulticlusterV1alpha1().Works(workNamespace).Get(context.Background(), workName, metav1.GetOptions{})
}
func safeDeleteWork(work *workapi.Work) error {
// ToDo - Replace with proper Eventually.
time.Sleep(1 * time.Second)
_, getError = hubWorkClient.MulticlusterV1alpha1().Works(work.Namespace).Get(context.Background(), work.Name, metav1.GetOptions{})
if getError == nil {
deleteError = hubWorkClient.MulticlusterV1alpha1().Works(createdWork.Namespace).Delete(context.Background(), createdWork.Name, metav1.DeleteOptions{})
for _, detail := range manifestDetails {
work.Spec.Workload.Manifests = append(work.Spec.Workload.Manifests, detail.Manifest)
}
return getError
return work
}
func updateWork(work *workapi.Work) (*workapi.Work, error) {
return hubWorkClient.MulticlusterV1alpha1().Works(work.Namespace).Update(context.Background(), work, metav1.UpdateOptions{})
func createWorkResource(work *workapi.Work) (*workapi.Work, error) {
return hubWorkClient.MulticlusterV1alpha1().Works(work.Namespace).Create(context.Background(), work, metav1.CreateOptions{})
}
func decodeUnstructured(manifest workapi.Manifest) (*unstructured.Unstructured, error) {
unstructuredObj := &unstructured.Unstructured{}
err := unstructuredObj.UnmarshalJSON(manifest.Raw)
return unstructuredObj, err
}
func deleteWorkResource(namespace string, name string) error {
return hubWorkClient.MulticlusterV1alpha1().Works(namespace).Delete(context.Background(), name, metav1.DeleteOptions{})
}
func generateManifestDetails(manifestFiles []string) []manifestDetails {
var details []manifestDetails
for _, file := range manifestFiles {
detail := manifestDetails{}
// Read files, create manifest
fileRaw, err := testManifestFiles.ReadFile(file)
Expect(err).ToNot(HaveOccurred())
obj, gvk, err := genericCodec.Decode(fileRaw, nil, nil)
Expect(err).ToNot(HaveOccurred())
jsonObj, err := json.Marshal(obj)
Expect(err).ToNot(HaveOccurred())
detail.Manifest = workapi.Manifest{
RawExtension: runtime.RawExtension{
Object: obj,
Raw: jsonObj},
}
unstructuredObj, err := decodeUnstructured(detail.Manifest)
Expect(err).ShouldNot(HaveOccurred())
mapping, err := restMapper.RESTMapping(unstructuredObj.GroupVersionKind().GroupKind(), unstructuredObj.GroupVersionKind().Version)
Expect(err).ShouldNot(HaveOccurred())
detail.GVK = gvk
detail.GVR = &mapping.Resource
detail.ObjMeta = metav1.ObjectMeta{
Name: unstructuredObj.GetName(),
Namespace: unstructuredObj.GetNamespace(),
}
details = append(details, detail)
}
return details
}
func retrieveWork(namespace string, name string) (*workapi.Work, error) {
return hubWorkClient.MulticlusterV1alpha1().Works(namespace).Get(context.Background(), name, metav1.GetOptions{})
}

Просмотреть файл

@ -18,7 +18,9 @@ package e2e
import (
"embed"
"k8s.io/apimachinery/pkg/api/meta"
"os"
"sigs.k8s.io/controller-runtime/pkg/client/apiutil"
"testing"
"github.com/onsi/ginkgo"
@ -41,6 +43,7 @@ import (
var (
restConfig *rest.Config
restMapper meta.RESTMapper
hubWorkClient workclientset.Interface
@ -49,7 +52,7 @@ var (
spokeKubeClient kubernetes.Interface
spokeWorkClient workclientset.Interface
//go:embed testmanifests
//go:embed manifests
testManifestFiles embed.FS
genericScheme = runtime.NewScheme()
@ -95,4 +98,7 @@ var _ = ginkgo.BeforeSuite(func() {
spokeWorkClient, err = workclientset.NewForConfig(restConfig)
gomega.Expect(err).ToNot(gomega.HaveOccurred())
restMapper, err = apiutil.NewDynamicRESTMapper(restConfig, apiutil.WithLazyDiscovery)
gomega.Expect(err).ToNot(gomega.HaveOccurred())
})