зеркало из https://github.com/Azure/k8s-work-api.git
Refactor: E2E Apply tests refactored (#58)
* Refactored E2E apply_tests.
This commit is contained in:
Родитель
a88c950d71
Коммит
5498a9bd2e
|
@ -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())
|
||||
})
|
||||
|
|
Загрузка…
Ссылка в новой задаче