Skip to content

Commit

Permalink
test: added dumping of pod info from workloafd clusters
Browse files Browse the repository at this point in the history
Signed-off-by: Richard Case <richard.case@outlook.com>
  • Loading branch information
richardcase committed Nov 22, 2022
1 parent 6ebafab commit 8e23d11
Show file tree
Hide file tree
Showing 3 changed files with 174 additions and 2 deletions.
3 changes: 2 additions & 1 deletion test/e2e/shared/cluster.go
Expand Up @@ -54,7 +54,7 @@ func createClusterctlLocalRepository(e2eCtx *E2EContext, repositoryFolder string
}

// setupBootstrapCluster installs Cluster API components via clusterctl.
func setupBootstrapCluster(config *clusterctl.E2EConfig, scheme *runtime.Scheme, useExistingCluster bool) (bootstrap.ClusterProvider, framework.ClusterProxy) {
func setupBootstrapCluster(config *clusterctl.E2EConfig, scheme *runtime.Scheme, useExistingCluster bool, artifactFolder string) (bootstrap.ClusterProvider, framework.ClusterProxy) {
var clusterProvider bootstrap.ClusterProvider
kubeconfigPath := ""
if !useExistingCluster {
Expand All @@ -63,6 +63,7 @@ func setupBootstrapCluster(config *clusterctl.E2EConfig, scheme *runtime.Scheme,
KubernetesVersion: config.GetVariable(KubernetesVersionManagement),
RequiresDockerSock: config.HasDockerProvider(),
Images: config.Images,
LogFolder: filepath.Join(artifactFolder, "kind"),
})
Expect(clusterProvider).ToNot(BeNil(), "Failed to create a bootstrap cluster")

Expand Down
170 changes: 170 additions & 0 deletions test/e2e/shared/debug.go
@@ -0,0 +1,170 @@
package shared

import (
"context"
"fmt"
"io"
"os"
"path/filepath"
"strings"
"sync"

. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"

corev1 "k8s.io/api/core/v1"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
"sigs.k8s.io/yaml"

"sigs.k8s.io/cluster-api/test/framework"
)

type podDumper func(ctx context.Context, artifactDir string, clusterClient *kubernetes.Clientset, pods ...corev1.Pod)

var defaultPodDumpers = []podDumper{dumpPodState, dumpPodEvents}

func DumpWorkloadClusterResources(ctx context.Context, e2eCtx *E2EContext) {
By("Getting all namespaces in bootstrap cluster")
namespaces := corev1.NamespaceList{}
clusterClient := e2eCtx.Environment.BootstrapClusterProxy.GetClient()
err := clusterClient.List(ctx, &namespaces)
Expect(err).NotTo(HaveOccurred())

for _, ns := range namespaces.Items {
clusters := framework.GetAllClustersByNamespace(ctx, framework.GetAllClustersByNamespaceInput{
Lister: e2eCtx.Environment.BootstrapClusterProxy.GetClient(),
Namespace: ns.Name,
})

for _, cluster := range clusters {
clusterClient := e2eCtx.Environment.BootstrapClusterProxy.GetWorkloadCluster(ctx, cluster.Namespace, cluster.Name).GetClientSet()
dumpClusterWorkloads(ctx, e2eCtx, cluster.Name, clusterClient)
}
}
}

func dumpClusterWorkloads(ctx context.Context, e2eCtx *E2EContext, name string, clusterClient *kubernetes.Clientset) {
Byf("Dumping workloads for cluster %s", name)

logPath := filepath.Join(e2eCtx.Settings.ArtifactFolder, "clusters", name)
if err := os.MkdirAll(logPath, os.ModePerm); err != nil {
fmt.Fprintf(GinkgoWriter, "couldn't create directory: path=%s, err=%s", logPath, err)
}
fmt.Fprintf(GinkgoWriter, "folder created for cluster: %s\n", logPath)

//TODO: do we need the ability to filter these in the e2e config?
namespacesToDump, err := clusterClient.CoreV1().Namespaces().List(ctx, v1.ListOptions{})
Expect(err).NotTo(HaveOccurred())

for _, ns := range namespacesToDump.Items {
dumpPodsForNamespace(ctx, &ns, logPath, clusterClient)
//TODO: add any additional resource dumping here
}
}

func dumpPodsForNamespace(ctx context.Context, ns *corev1.Namespace, artifactFolder string, clusterClient *kubernetes.Clientset) {
logPath := filepath.Join(artifactFolder, ns.Name, "pods")
if err := os.MkdirAll(logPath, os.ModePerm); err != nil {
fmt.Fprintf(GinkgoWriter, "ERROR: couldn't create directory: path=%s, err=%s", logPath, err)
return
}
fmt.Fprintf(GinkgoWriter, "folder created for cluster pods: %s\n", logPath)

pods, err := clusterClient.CoreV1().Pods(ns.Name).List(ctx, v1.ListOptions{})
Expect(err).NotTo(HaveOccurred())

if len(pods.Items) == 0 {
fmt.Fprintf(GinkgoWriter, "no pods in namespace: %s\n", ns.Name)
return
}

wg := sync.WaitGroup{}
for _, dumpFunc := range defaultPodDumpers {
wg.Add(1)
go func() {
defer wg.Done()
dumpFunc(ctx, logPath, clusterClient, pods.Items...)
}()
}
wg.Wait()
}

func dumpPodState(ctx context.Context, artifactFolder string, clusterClient *kubernetes.Clientset, pods ...corev1.Pod) {
for _, pod := range pods {
logPath := fmt.Sprintf("%s/%s_state.yaml", artifactFolder, pod.Name)

data, err := yaml.Marshal(pod)
if err != nil {
fmt.Fprintf(GinkgoWriter, "ERROR: couldn't yaml marshal pod: pod=%s, ns=%s, err=%s", pod.Name, pod.Namespace, err)
return
}
err = os.WriteFile(logPath, data, os.ModePerm)
if err != nil {
fmt.Fprintf(GinkgoWriter, "ERROR: couldn't save yaml: path=%s, err=%s", logPath, err)
return
}
}
}

func dumpPodEvents(ctx context.Context, artifactFolder string, clusterClient *kubernetes.Clientset, pods ...corev1.Pod) {
for _, pod := range pods {
logPath := fmt.Sprintf("%s/%s_events.yaml", artifactFolder, pod.Name)

events, err := clusterClient.CoreV1().Events(pod.Namespace).List(ctx, v1.ListOptions{
FieldSelector: fmt.Sprintf("involvedObject.name=%s", pod.Name),
})
if err != nil {
fmt.Fprintf(GinkgoWriter, "ERROR: list events for pod: pod=%s, err=%s", pod.Name, err)
continue
}
for i := range events.Items {
e := events.Items[i]
e.ManagedFields = nil
events.Items[i] = e
}

data, err := yaml.Marshal(events)
if err != nil {
fmt.Fprintf(GinkgoWriter, "ERROR: couldn't yaml marshal pod events: pod=%s, ns=%s, err=%s", pod.Name, pod.Namespace, err)
continue
}
err = os.WriteFile(logPath, data, os.ModePerm)
if err != nil {
fmt.Fprintf(GinkgoWriter, "ERROR: couldn't save yaml: path=%s, err=%s", logPath, err)
continue
}
}
}

func dumpPodLogs(ctx context.Context, artifactFolder string, clusterClient *kubernetes.Clientset, pods ...corev1.Pod) {
for _, pod := range pods {
containers := append(pod.Spec.Containers, pod.Spec.InitContainers...)
for _, container := range containers {
opts := &corev1.PodLogOptions{
Container: container.Name,
Previous: false,
}

res, err := clusterClient.CoreV1().Pods(pod.Namespace).GetLogs(pod.Name, opts).Stream(ctx)
if err != nil {
fmt.Fprintf(GinkgoWriter, "ERROR: couldn't get pod logs: pod=%s, err=%s", pod.Name, err)
continue
}
defer res.Close()

builder := &strings.Builder{}
if _, err = io.Copy(builder, res); err != nil {
fmt.Fprintf(GinkgoWriter, "ERROR: couldn't stream pod logs: pod=%s, err=%s", pod.Name, err)
continue
}

logPath := fmt.Sprintf("%s/%s_%s.log", artifactFolder, pod.Name, &container.Name)
err = os.WriteFile(logPath, []byte(builder.String()), os.ModePerm)
if err != nil {
fmt.Fprintf(GinkgoWriter, "ERROR: couldn't save pod logs: path=%s, err=%s", logPath, err)
continue
}
}
}
}
3 changes: 2 additions & 1 deletion test/e2e/shared/suite.go
Expand Up @@ -151,7 +151,7 @@ func Node1BeforeSuite(e2eCtx *E2EContext) []byte {
e2eCtx.Environment.ClusterctlConfigPath = createClusterctlLocalRepository(e2eCtx, filepath.Join(e2eCtx.Settings.ArtifactFolder, "repository"))

By("Setting up the bootstrap cluster")
e2eCtx.Environment.BootstrapClusterProvider, e2eCtx.Environment.BootstrapClusterProxy = setupBootstrapCluster(e2eCtx.E2EConfig, e2eCtx.Environment.Scheme, e2eCtx.Settings.UseExistingCluster)
e2eCtx.Environment.BootstrapClusterProvider, e2eCtx.Environment.BootstrapClusterProxy = setupBootstrapCluster(e2eCtx.E2EConfig, e2eCtx.Environment.Scheme, e2eCtx.Settings.UseExistingCluster, e2eCtx.Settings.ArtifactFolder)

base64EncodedCredentials := encodeCredentials(e2eCtx.Environment.BootstrapAccessKey, boostrapTemplate.Spec.Region)
SetEnvVar("AWS_B64ENCODED_CREDENTIALS", base64EncodedCredentials, true)
Expand Down Expand Up @@ -262,6 +262,7 @@ func AllNodesBeforeSuite(e2eCtx *E2EContext, data []byte) {
// Node1AfterSuite is cleanup that runs on the first ginkgo node after the test suite finishes.
func Node1AfterSuite(e2eCtx *E2EContext) {
ctx, cancel := context.WithTimeout(context.TODO(), 15*time.Minute)
DumpWorkloadClusterResources(ctx, e2eCtx)
DumpEKSClusters(ctx, e2eCtx)
DumpCloudTrailEvents(e2eCtx)

Expand Down

0 comments on commit 8e23d11

Please sign in to comment.