Skip to content

Commit 70abdf0

Browse files
committed
Expose logs/resources after test run
Signed-off-by: Joaquim Moreno Prusi <joaquim@redhat.com>
1 parent fd7cfb5 commit 70abdf0

File tree

4 files changed

+204
-5
lines changed

4 files changed

+204
-5
lines changed

Makefile

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,10 @@ export XDG_DATA_HOME ?= /tmp/.local/share
1919
# bingo manages consistent tooling versions for things like kind, kustomize, etc.
2020
include .bingo/Variables.mk
2121

22+
# ARTIFACT_PATH is the absolute path to the directory where the operator-controller e2e tests will store the artifacts
23+
# for example: ARTIFACT_PATH=/tmp/artifacts make test
24+
export ARTIFACT_PATH ?=
25+
2226
OPERATOR_CONTROLLER_NAMESPACE ?= operator-controller-system
2327
KIND_CLUSTER_NAME ?= operator-controller
2428

go.mod

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ require (
1515
github.com/spf13/pflag v1.0.5
1616
github.com/stretchr/testify v1.8.4
1717
go.uber.org/zap v1.25.0
18+
gopkg.in/yaml.v2 v2.4.0
19+
k8s.io/api v0.26.1
1820
k8s.io/apiextensions-apiserver v0.26.1
1921
k8s.io/apimachinery v0.26.1
2022
k8s.io/client-go v0.26.1
@@ -132,9 +134,7 @@ require (
132134
google.golang.org/protobuf v1.28.1 // indirect
133135
gopkg.in/inf.v0 v0.9.1 // indirect
134136
gopkg.in/warnings.v0 v0.1.2 // indirect
135-
gopkg.in/yaml.v2 v2.4.0 // indirect
136137
gopkg.in/yaml.v3 v3.0.1 // indirect
137-
k8s.io/api v0.26.1 // indirect
138138
k8s.io/apiserver v0.26.1 // indirect
139139
k8s.io/klog/v2 v2.80.1 // indirect
140140
k8s.io/kube-openapi v0.0.0-20221012153701-172d655c2280 // indirect

test/e2e/e2e_suite_test.go

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@ import (
88

99
. "github.com/onsi/ginkgo/v2"
1010
. "github.com/onsi/gomega"
11+
appsv1 "k8s.io/api/apps/v1"
12+
corev1 "k8s.io/api/core/v1"
13+
"k8s.io/utils/env"
1114

1215
"k8s.io/apimachinery/pkg/api/errors"
1316
apimeta "k8s.io/apimachinery/pkg/api/meta"
@@ -61,6 +64,13 @@ var _ = BeforeSuite(func() {
6164
Expect(catalogd.AddToScheme(scheme)).To(Succeed())
6265

6366
var err error
67+
68+
err = appsv1.AddToScheme(scheme)
69+
Expect(err).ToNot(HaveOccurred())
70+
71+
err = corev1.AddToScheme(scheme)
72+
Expect(err).ToNot(HaveOccurred())
73+
6474
c, err = client.New(cfg, client.Options{Scheme: scheme})
6575
Expect(err).To(Not(HaveOccurred()))
6676

@@ -87,7 +97,10 @@ var _ = BeforeSuite(func() {
8797

8898
var _ = AfterSuite(func() {
8999
ctx := context.Background()
90-
100+
if basePath := env.GetString("ARTIFACT_PATH", ""); basePath != "" {
101+
// get all the artifacts from the test run and save them to the artifact path
102+
getArtifactsOutput(ctx, basePath)
103+
}
91104
Expect(c.Delete(ctx, operatorCatalog)).To(Succeed())
92105
Eventually(func(g Gomega) {
93106
err := c.Get(ctx, types.NamespacedName{Name: operatorCatalog.Name}, &catalogd.Catalog{})

test/e2e/install_test.go

Lines changed: 184 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,20 +3,38 @@ package e2e
33
import (
44
"context"
55
"fmt"
6+
"io"
7+
"os"
8+
"path/filepath"
9+
"strings"
610

711
. "github.com/onsi/ginkgo/v2"
812
. "github.com/onsi/gomega"
9-
13+
catalogd "github.com/operator-framework/catalogd/pkg/apis/core/v1beta1"
14+
operatorv1alpha1 "github.com/operator-framework/operator-controller/api/v1alpha1"
15+
rukpakv1alpha1 "github.com/operator-framework/rukpak/api/v1alpha1"
16+
"gopkg.in/yaml.v2"
17+
appsv1 "k8s.io/api/apps/v1"
18+
corev1 "k8s.io/api/core/v1"
19+
v1 "k8s.io/api/core/v1"
1020
"k8s.io/apimachinery/pkg/api/errors"
1121
apimeta "k8s.io/apimachinery/pkg/api/meta"
1222
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
1323
"k8s.io/apimachinery/pkg/types"
1424
"k8s.io/apimachinery/pkg/util/rand"
25+
kubeclient "k8s.io/client-go/kubernetes"
26+
"k8s.io/utils/env"
27+
"sigs.k8s.io/controller-runtime/pkg/client"
1528

1629
catalogd "github.com/operator-framework/catalogd/api/core/v1alpha1"
1730
rukpakv1alpha1 "github.com/operator-framework/rukpak/api/v1alpha1"
1831

19-
operatorv1alpha1 "github.com/operator-framework/operator-controller/api/v1alpha1"
32+
const (
33+
defaultTimeout = 30 * time.Second
34+
defaultPoll = 1 * time.Second
35+
testCatalogRef = "localhost/testdata/catalogs/test-catalog:e2e"
36+
testCatalogName = "test-catalog"
37+
artifactName = "operator-controller-e2e"
2038
)
2139

2240
var _ = Describe("Operator Install", func() {
@@ -213,6 +231,10 @@ var _ = Describe("Operator Install", func() {
213231
})
214232

215233
AfterEach(func() {
234+
if basePath := env.GetString("ARTIFACT_PATH", ""); basePath != "" {
235+
// get all the artifacts from the test run and save them to the artifact path
236+
getArtifactsOutput(ctx, basePath)
237+
}
216238
Expect(c.Delete(ctx, operator)).To(Succeed())
217239
Eventually(func(g Gomega) {
218240
err := c.Get(ctx, types.NamespacedName{Name: operator.Name}, &operatorv1alpha1.Operator{})
@@ -222,3 +244,163 @@ var _ = Describe("Operator Install", func() {
222244

223245
})
224246
})
247+
248+
// getArtifactsOutput gets all the artifacts from the test run and saves them to the artifact path.
249+
// right now it will save:
250+
// - operators
251+
// - pods logs
252+
// - deployments
253+
// - bundle
254+
// - bundledeployments
255+
// - catalogsources
256+
257+
func getArtifactsOutput(ctx context.Context, basePath string) {
258+
kubeClient, err := kubeclient.NewForConfig(cfg)
259+
Expect(err).To(Not(HaveOccurred()))
260+
261+
// sanitize the artifact name for use as a directory name
262+
testName := strings.ReplaceAll(strings.ToLower(CurrentSpecReport().LeafNodeText), " ", "-")
263+
// Get the test description and sanitize it for use as a directory name
264+
artifactPath := filepath.Join(basePath, artifactName, fmt.Sprint(time.Now().UnixNano()), testName)
265+
266+
// Create the full artifact path
267+
err = os.MkdirAll(artifactPath, 0755)
268+
Expect(err).To(Not(HaveOccurred()))
269+
270+
// Get all namespaces
271+
namespaces := corev1.NamespaceList{}
272+
if err := c.List(ctx, &namespaces); err != nil {
273+
GinkgoWriter.Printf("Failed to list namespaces %w", err)
274+
}
275+
276+
// get all operators save them to the artifact path.
277+
operators := operatorv1alpha1.OperatorList{}
278+
if err := c.List(ctx, &operators, client.InNamespace("")); err != nil {
279+
GinkgoWriter.Printf("Failed to list operators %w", err)
280+
}
281+
for _, operator := range operators.Items {
282+
// Save operator to artifact path
283+
operatorYaml, err := yaml.Marshal(operator)
284+
if err != nil {
285+
GinkgoWriter.Printf("Failed to marshal operator %w", err)
286+
continue
287+
}
288+
if err := os.WriteFile(filepath.Join(artifactPath, operator.Name+"-operator.yaml"), operatorYaml, 0644); err != nil {
289+
GinkgoWriter.Printf("Failed to write operator to file %w", err)
290+
}
291+
}
292+
293+
// get all catalogsources save them to the artifact path.
294+
catalogsources := catalogd.CatalogList{}
295+
if err := c.List(ctx, &catalogsources, client.InNamespace("")); err != nil {
296+
GinkgoWriter.Printf("Failed to list catalogsources %w", err)
297+
}
298+
for _, catalogsource := range catalogsources.Items {
299+
// Save catalogsource to artifact path
300+
catalogsourceYaml, err := yaml.Marshal(catalogsource)
301+
if err != nil {
302+
GinkgoWriter.Printf("Failed to marshal catalogsource %w", err)
303+
continue
304+
}
305+
if err := os.WriteFile(filepath.Join(artifactPath, catalogsource.Name+"-catalogsource.yaml"), catalogsourceYaml, 0644); err != nil {
306+
GinkgoWriter.Printf("Failed to write catalogsource to file %w", err)
307+
}
308+
}
309+
310+
// Get all Bundles in the namespace and save them to the artifact path.
311+
bundles := rukpakv1alpha1.BundleList{}
312+
if err := c.List(ctx, &bundles, client.InNamespace("")); err != nil {
313+
GinkgoWriter.Printf("Failed to list bundles %w", err)
314+
}
315+
for _, bundle := range bundles.Items {
316+
// Save bundle to artifact path
317+
bundleYaml, err := yaml.Marshal(bundle)
318+
if err != nil {
319+
GinkgoWriter.Printf("Failed to marshal bundle %w", err)
320+
continue
321+
}
322+
if err := os.WriteFile(filepath.Join(artifactPath, bundle.Name+"-bundle.yaml"), bundleYaml, 0644); err != nil {
323+
GinkgoWriter.Printf("Failed to write bundle to file %w", err)
324+
}
325+
}
326+
327+
// Get all BundleDeployments in the namespace and save them to the artifact path.
328+
bundleDeployments := rukpakv1alpha1.BundleDeploymentList{}
329+
if err := c.List(ctx, &bundleDeployments, client.InNamespace("")); err != nil {
330+
GinkgoWriter.Printf("Failed to list bundleDeployments %w", err)
331+
}
332+
for _, bundleDeployment := range bundleDeployments.Items {
333+
// Save bundleDeployment to artifact path
334+
bundleDeploymentYaml, err := yaml.Marshal(bundleDeployment)
335+
if err != nil {
336+
GinkgoWriter.Printf("Failed to marshal bundleDeployment %w", err)
337+
continue
338+
}
339+
if err := os.WriteFile(filepath.Join(artifactPath, bundleDeployment.Name+"-bundleDeployment.yaml"), bundleDeploymentYaml, 0644); err != nil {
340+
GinkgoWriter.Printf("Failed to write bundleDeployment to file %w", err)
341+
}
342+
}
343+
344+
for _, namespace := range namespaces.Items {
345+
// let's ignore kube-* namespaces.
346+
if strings.Contains(namespace.Name, "kube-") {
347+
continue
348+
}
349+
350+
namespacedArtifactPath := filepath.Join(artifactPath, namespace.Name)
351+
if err := os.Mkdir(namespacedArtifactPath, 0755); err != nil {
352+
GinkgoWriter.Printf("Failed to create namespaced artifact path %w", err)
353+
continue
354+
}
355+
356+
// get all deployments in the namespace and save them to the artifact path.
357+
deployments := appsv1.DeploymentList{}
358+
if err := c.List(ctx, &deployments, client.InNamespace(namespace.Name)); err != nil {
359+
GinkgoWriter.Printf("Failed to list deployments %w in namespace: %q", err, namespace.Name)
360+
continue
361+
}
362+
363+
for _, deployment := range deployments.Items {
364+
// Save deployment to artifact path
365+
deploymentYaml, err := yaml.Marshal(deployment)
366+
if err != nil {
367+
GinkgoWriter.Printf("Failed to marshal deployment %w", err)
368+
continue
369+
}
370+
if err := os.WriteFile(filepath.Join(namespacedArtifactPath, deployment.Name+"-deployment.yaml"), deploymentYaml, 0644); err != nil {
371+
GinkgoWriter.Printf("Failed to write deployment to file %w", err)
372+
}
373+
}
374+
375+
// Get logs from all pods in all namespaces
376+
pods := corev1.PodList{}
377+
if err := c.List(ctx, &pods, client.InNamespace(namespace.Name)); err != nil {
378+
GinkgoWriter.Printf("Failed to list pods %w in namespace: %q", err, namespace.Name)
379+
}
380+
for _, pod := range pods.Items {
381+
if pod.Status.Phase != v1.PodRunning && pod.Status.Phase != v1.PodSucceeded && pod.Status.Phase != v1.PodFailed {
382+
continue
383+
}
384+
for _, container := range pod.Spec.Containers {
385+
logs, err := kubeClient.CoreV1().Pods(namespace.Name).GetLogs(pod.Name, &v1.PodLogOptions{Container: container.Name}).Stream(ctx)
386+
if err != nil {
387+
GinkgoWriter.Printf("Failed to get logs for pod %q in namespace %q: %w", pod.Name, namespace.Name, err)
388+
continue
389+
}
390+
defer logs.Close()
391+
392+
outFile, err := os.Create(filepath.Join(namespacedArtifactPath, pod.Name+"-"+container.Name+"-logs.txt"))
393+
if err != nil {
394+
GinkgoWriter.Printf("Failed to create file for pod %q in namespace %q: %w", pod.Name, namespace.Name, err)
395+
continue
396+
}
397+
defer outFile.Close()
398+
399+
if _, err := io.Copy(outFile, logs); err != nil {
400+
GinkgoWriter.Printf("Failed to copy logs for pod %q in namespace %q: %w", pod.Name, namespace.Name, err)
401+
continue
402+
}
403+
}
404+
}
405+
}
406+
}

0 commit comments

Comments
 (0)