From beea8d889ee723e01e1b59a6251cb09ca8238b21 Mon Sep 17 00:00:00 2001 From: fabriziopandini Date: Fri, 10 Apr 2020 17:49:02 +0200 Subject: [PATCH] e2e dump all resources --- test/framework/convenience.go | 4 +- test/framework/discovery/discovery.go | 123 ++++++++++++++++++++++++++ test/framework/dump_resources.go | 2 + 3 files changed, 128 insertions(+), 1 deletion(-) diff --git a/test/framework/convenience.go b/test/framework/convenience.go index eaed509fab0e..a5283ceab60f 100644 --- a/test/framework/convenience.go +++ b/test/framework/convenience.go @@ -26,7 +26,8 @@ import ( appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" rbacv1 "k8s.io/api/rbac/v1" - apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1" + apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" + apiextensionsv1beta "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1" apierrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" @@ -110,6 +111,7 @@ func TryAddDefaultSchemes(scheme *runtime.Scheme) { _ = controlplanev1.AddToScheme(scheme) // Add the api extensions (CRD) to the scheme. + _ = apiextensionsv1beta.AddToScheme(scheme) _ = apiextensionsv1.AddToScheme(scheme) // Add rbac to the scheme. diff --git a/test/framework/discovery/discovery.go b/test/framework/discovery/discovery.go index 583e3b2176fc..f77123b37de4 100644 --- a/test/framework/discovery/discovery.go +++ b/test/framework/discovery/discovery.go @@ -20,14 +20,27 @@ package discovery import ( "context" + "fmt" + "io/ioutil" + "os" + "path" + "path/filepath" + "github.com/onsi/ginkgo" . "github.com/onsi/gomega" appsv1 "k8s.io/api/apps/v1" + apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" + apierrors "k8s.io/apimachinery/pkg/api/errors" + apimeta "k8s.io/apimachinery/pkg/api/meta" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/runtime" clusterv1 "sigs.k8s.io/cluster-api/api/v1alpha3" controlplanev1 "sigs.k8s.io/cluster-api/controlplane/kubeadm/api/v1alpha3" "sigs.k8s.io/cluster-api/test/framework" "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/yaml" ) // Provides methods for discovering Cluster API objects existing in the management cluster. @@ -49,6 +62,70 @@ func GetControllerDeployments(ctx context.Context, input GetControllerDeployment return deployments } +// GetCAPIResourcesInput is the input for GetCAPIResources. +type GetCAPIResourcesInput struct { + Lister framework.Lister + Namespace string +} + +// GetCAPIResources reads all the CAPI resources in a namespace. +// This list includes all the types belonging to CAPI providers. +func GetCAPIResources(ctx context.Context, input GetCAPIResourcesInput) []*unstructured.Unstructured { + Expect(ctx).NotTo(BeNil(), "ctx is required for GetCAPIResources") + Expect(input.Lister).NotTo(BeNil(), "input.Deleter is required for GetCAPIResources") + Expect(input.Namespace).NotTo(BeEmpty(), "input.Namespace is required for GetCAPIResources") + + types := getClusterAPITypes(ctx, input.Lister) + + objList := []*unstructured.Unstructured{} + for i := range types { + typeMeta := types[i] + typeList := new(unstructured.UnstructuredList) + typeList.SetAPIVersion(typeMeta.APIVersion) + typeList.SetKind(typeMeta.Kind) + + if err := input.Lister.List(ctx, typeList, client.InNamespace(input.Namespace)); err != nil { + if apierrors.IsNotFound(err) { + continue + } + ginkgo.Fail(fmt.Sprintf("failed to list %q resources: %v", typeList.GroupVersionKind(), err)) + } + for i := range typeList.Items { + obj := typeList.Items[i] + objList = append(objList, &obj) + } + } + + return objList +} + +// getClusterAPITypes returns the list of TypeMeta to be considered for the the move discovery phase. +// This list includes all the types belonging to CAPI providers. +func getClusterAPITypes(ctx context.Context, lister framework.Lister) []metav1.TypeMeta { + discoveredTypes := []metav1.TypeMeta{} + + crdList := &apiextensionsv1.CustomResourceDefinitionList{} + err := lister.List(ctx, crdList, capiProviderOptions()...) + Expect(err).ToNot(HaveOccurred(), "failed to list CRDs for CAPI providers") + + for _, crd := range crdList.Items { + for _, version := range crd.Spec.Versions { + if !version.Storage { + continue + } + + discoveredTypes = append(discoveredTypes, metav1.TypeMeta{ + Kind: crd.Spec.Names.Kind, + APIVersion: metav1.GroupVersion{ + Group: crd.Spec.Group, + Version: version.Name, + }.String(), + }) + } + } + return discoveredTypes +} + // GetClusterByNameInput is the input for GetClusterByName. type GetClusterByNameInput struct { Getter framework.Getter @@ -108,6 +185,52 @@ func GetMachineDeploymentsByCluster(ctx context.Context, input GetMachineDeploym return deployments } +// DumpAllResourcesInput is the input for DumpAllResources. +type DumpAllResourcesInput struct { + Lister framework.Lister + Namespace string + LogPath string +} + +// DumpAllResources dumps Cluster API related resources to YAML +// This dump includes all the types belonging to CAPI providers. +func DumpAllResources(ctx context.Context, input DumpAllResourcesInput) { + Expect(ctx).NotTo(BeNil(), "ctx is required for DumpAllResources") + Expect(input.Lister).NotTo(BeNil(), "input.Deleter is required for DumpAllResources") + Expect(input.Namespace).NotTo(BeEmpty(), "input.Namespace is required for DumpAllResources") + + resources := GetCAPIResources(ctx, GetCAPIResourcesInput{ + Lister: input.Lister, + Namespace: input.Namespace, + }) + + for i := range resources { + r := resources[i] + dumpObject(r, input.LogPath) + } +} + +func dumpObject(resource runtime.Object, logPath string) { + resourceYAML, err := yaml.Marshal(resource) + Expect(err).ToNot(HaveOccurred(), "Failed to marshal %s", resource.GetObjectKind().GroupVersionKind().String()) + + metaObj, err := apimeta.Accessor(resource) + Expect(err).ToNot(HaveOccurred(), "Failed to get accessor for %s", resource.GetObjectKind().GroupVersionKind().String()) + + kind := resource.GetObjectKind().GroupVersionKind().Kind + namespace := metaObj.GetNamespace() + name := metaObj.GetName() + + resourceFilePath := path.Join(logPath, namespace, kind, name+".yaml") + Expect(os.MkdirAll(filepath.Dir(resourceFilePath), 0755)).To(Succeed(), "Failed to create folder %s", filepath.Dir(resourceFilePath)) + + f, err := os.OpenFile(resourceFilePath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) + Expect(err).ToNot(HaveOccurred(), "Failed to open %s", resourceFilePath) + defer f.Close() + + Expect(ioutil.WriteFile(f.Name(), resourceYAML, 0644)).To(Succeed(), "Failed to write %s", resourceFilePath) +} + // capiProviderOptions returns a set of ListOptions that allows to identify all the objects belonging to Cluster API providers. func capiProviderOptions() []client.ListOption { return []client.ListOption{ diff --git a/test/framework/dump_resources.go b/test/framework/dump_resources.go index 3510191e704f..0150dfd2a390 100644 --- a/test/framework/dump_resources.go +++ b/test/framework/dump_resources.go @@ -37,6 +37,7 @@ import ( ) // DumpResources dump cluster API related resources to YAML +// Deprecated. Please use DumpAllResources instead func DumpResources(mgmt ManagementCluster, resourcePath string, writer io.Writer) error { resources := map[string]runtime.Object{ "Cluster": &clusterv1.ClusterList{}, @@ -53,6 +54,7 @@ func DumpResources(mgmt ManagementCluster, resourcePath string, writer io.Writer } // DumpProviderResources dump provider specific API related resources to YAML +// Deprecated. Please use DumpAllResources instead func DumpProviderResources(mgmt ManagementCluster, resources map[string]runtime.Object, resourcePath string, writer io.Writer) error { return dumpResources(mgmt, resources, resourcePath, writer) }