Skip to content

Commit f5fed1a

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

File tree

4 files changed

+191
-2
lines changed

4 files changed

+191
-2
lines changed

Makefile

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@ export RUKPAK_VERSION=$(shell go list -mod=mod -m -f "{{.Version}}" github.com/o
1212
export WAIT_TIMEOUT ?= 60s
1313
IMG?=$(IMAGE_REPO):$(IMAGE_TAG)
1414

15+
# ARTIFACT_PATH is the absolute path to the directory where the operator-controller e2e tests will store the artifacts
16+
# for example: ARTIFACT_PATH=/tmp/artifacts make test
17+
export ARTIFACT_PATH ?=
18+
1519
OPERATOR_CONTROLLER_NAMESPACE ?= operator-controller-system
1620
KIND_CLUSTER_NAME ?= operator-controller
1721

go.mod

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ require (
1111
github.com/operator-framework/operator-registry v1.26.3
1212
github.com/operator-framework/rukpak v0.12.0
1313
go.uber.org/zap v1.24.0
14+
gopkg.in/yaml.v2 v2.4.0
15+
k8s.io/api v0.26.1
1416
k8s.io/apimachinery v0.26.1
1517
k8s.io/client-go v0.26.1
1618
k8s.io/utils v0.0.0-20221128185143-99ec85e7a448
@@ -67,9 +69,7 @@ require (
6769
google.golang.org/appengine v1.6.7 // indirect
6870
google.golang.org/protobuf v1.28.1 // indirect
6971
gopkg.in/inf.v0 v0.9.1 // indirect
70-
gopkg.in/yaml.v2 v2.4.0 // indirect
7172
gopkg.in/yaml.v3 v3.0.1 // indirect
72-
k8s.io/api v0.26.1 // indirect
7373
k8s.io/apiextensions-apiserver v0.26.1 // indirect
7474
k8s.io/component-base v0.26.1 // indirect
7575
k8s.io/klog/v2 v2.80.1 // indirect

test/e2e/e2e_suite_test.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ import (
66

77
. "github.com/onsi/ginkgo/v2"
88
. "github.com/onsi/gomega"
9+
appsv1 "k8s.io/api/apps/v1"
10+
corev1 "k8s.io/api/core/v1"
911
"k8s.io/apimachinery/pkg/runtime"
1012
"k8s.io/client-go/rest"
1113
ctrl "sigs.k8s.io/controller-runtime"
@@ -40,6 +42,13 @@ var _ = BeforeSuite(func() {
4042

4143
err = catalogd.AddToScheme(scheme)
4244
Expect(err).ToNot(HaveOccurred())
45+
46+
err = appsv1.AddToScheme(scheme)
47+
Expect(err).ToNot(HaveOccurred())
48+
49+
err = corev1.AddToScheme(scheme)
50+
Expect(err).ToNot(HaveOccurred())
51+
4352
c, err = client.New(cfg, client.Options{Scheme: scheme})
4453
Expect(err).To(Not(HaveOccurred()))
4554
})

test/e2e/install_test.go

Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,22 +3,34 @@ package e2e
33
import (
44
"context"
55
"fmt"
6+
"io"
7+
"os"
8+
"path/filepath"
9+
"strings"
610
"time"
711

812
. "github.com/onsi/ginkgo/v2"
913
. "github.com/onsi/gomega"
1014
catalogd "github.com/operator-framework/catalogd/pkg/apis/core/v1beta1"
1115
operatorv1alpha1 "github.com/operator-framework/operator-controller/api/v1alpha1"
1216
rukpakv1alpha1 "github.com/operator-framework/rukpak/api/v1alpha1"
17+
"gopkg.in/yaml.v2"
18+
appsv1 "k8s.io/api/apps/v1"
19+
corev1 "k8s.io/api/core/v1"
20+
v1 "k8s.io/api/core/v1"
1321
apimeta "k8s.io/apimachinery/pkg/api/meta"
1422
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
1523
"k8s.io/apimachinery/pkg/types"
1624
"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"
1728
)
1829

1930
const (
2031
defaultTimeout = 30 * time.Second
2132
defaultPoll = 1 * time.Second
33+
artifactName = "operator-controller-e2e"
2234
)
2335

2436
var _ = Describe("Operator Install", func() {
@@ -104,10 +116,174 @@ var _ = Describe("Operator Install", func() {
104116

105117
})
106118
AfterEach(func() {
119+
if basePath := env.GetString("ARTIFACT_PATH", ""); basePath != "" && CurrentSpecReport().Failed() {
120+
// get all the artifacts from the test run and save them to the artifact path
121+
getArtifactsOutput(ctx, basePath)
122+
}
107123
err := c.Delete(ctx, operatorCatalog)
108124
Expect(err).ToNot(HaveOccurred())
109125
err = c.Delete(ctx, operator)
110126
Expect(err).ToNot(HaveOccurred())
111127
})
112128
})
113129
})
130+
131+
// getArtifactsOutput gets all the artifacts from the test run and saves them to the artifact path.
132+
// right now it will save:
133+
// - operators
134+
// - pods logs
135+
// - deployments
136+
// - bundle
137+
// - bundledeployments
138+
// - catalogsources
139+
140+
func getArtifactsOutput(ctx context.Context, basePath string) {
141+
kubeClient, err := kubeclient.NewForConfig(cfg)
142+
Expect(err).To(Not(HaveOccurred()))
143+
144+
// sanitize the artifact name for use as a directory name
145+
testName := strings.ReplaceAll(strings.ToLower(CurrentSpecReport().LeafNodeText), " ", "-")
146+
// Get the test description and sanitize it for use as a directory name
147+
artifactPath := filepath.Join(basePath, artifactName, fmt.Sprint(time.Now().UnixNano()), testName)
148+
149+
// Create the full artifact path
150+
err = os.MkdirAll(artifactPath, 0755)
151+
Expect(err).To(Not(HaveOccurred()))
152+
153+
// Get all namespaces
154+
namespaces := corev1.NamespaceList{}
155+
if err := c.List(ctx, &namespaces); err != nil {
156+
GinkgoWriter.Printf("Failed to list namespaces %w", err)
157+
}
158+
159+
// get all operators save them to the artifact path.
160+
operators := operatorv1alpha1.OperatorList{}
161+
if err := c.List(ctx, &operators, client.InNamespace("")); err != nil {
162+
GinkgoWriter.Printf("Failed to list operators %w", err)
163+
}
164+
for _, operator := range operators.Items {
165+
// Save operator to artifact path
166+
operatorYaml, err := yaml.Marshal(operator)
167+
if err != nil {
168+
GinkgoWriter.Printf("Failed to marshal operator %w", err)
169+
continue
170+
}
171+
if err := os.WriteFile(filepath.Join(artifactPath, operator.Name+"-operator.yaml"), operatorYaml, 0644); err != nil {
172+
GinkgoWriter.Printf("Failed to write operator to file %w", err)
173+
}
174+
}
175+
176+
// get all catalogsources save them to the artifact path.
177+
catalogsources := catalogd.CatalogList{}
178+
if err := c.List(ctx, &catalogsources, client.InNamespace("")); err != nil {
179+
GinkgoWriter.Printf("Failed to list catalogsources %w", err)
180+
}
181+
for _, catalogsource := range catalogsources.Items {
182+
// Save catalogsource to artifact path
183+
catalogsourceYaml, err := yaml.Marshal(catalogsource)
184+
if err != nil {
185+
GinkgoWriter.Printf("Failed to marshal catalogsource %w", err)
186+
continue
187+
}
188+
if err := os.WriteFile(filepath.Join(artifactPath, catalogsource.Name+"-catalogsource.yaml"), catalogsourceYaml, 0644); err != nil {
189+
GinkgoWriter.Printf("Failed to write catalogsource to file %w", err)
190+
}
191+
}
192+
193+
// Get all Bundles in the namespace and save them to the artifact path.
194+
bundles := rukpakv1alpha1.BundleList{}
195+
if err := c.List(ctx, &bundles, client.InNamespace("")); err != nil {
196+
GinkgoWriter.Printf("Failed to list bundles %w", err)
197+
}
198+
for _, bundle := range bundles.Items {
199+
// Save bundle to artifact path
200+
bundleYaml, err := yaml.Marshal(bundle)
201+
if err != nil {
202+
GinkgoWriter.Printf("Failed to marshal bundle %w", err)
203+
continue
204+
}
205+
if err := os.WriteFile(filepath.Join(artifactPath, bundle.Name+"-bundle.yaml"), bundleYaml, 0644); err != nil {
206+
GinkgoWriter.Printf("Failed to write bundle to file %w", err)
207+
}
208+
}
209+
210+
// Get all BundleDeployments in the namespace and save them to the artifact path.
211+
bundleDeployments := rukpakv1alpha1.BundleDeploymentList{}
212+
if err := c.List(ctx, &bundleDeployments, client.InNamespace("")); err != nil {
213+
GinkgoWriter.Printf("Failed to list bundleDeployments %w", err)
214+
}
215+
for _, bundleDeployment := range bundleDeployments.Items {
216+
// Save bundleDeployment to artifact path
217+
bundleDeploymentYaml, err := yaml.Marshal(bundleDeployment)
218+
if err != nil {
219+
GinkgoWriter.Printf("Failed to marshal bundleDeployment %w", err)
220+
continue
221+
}
222+
if err := os.WriteFile(filepath.Join(artifactPath, bundleDeployment.Name+"-bundleDeployment.yaml"), bundleDeploymentYaml, 0644); err != nil {
223+
GinkgoWriter.Printf("Failed to write bundleDeployment to file %w", err)
224+
}
225+
}
226+
227+
for _, namespace := range namespaces.Items {
228+
// let's ignore kube-* namespaces.
229+
if strings.Contains(namespace.Name, "kube-") {
230+
continue
231+
}
232+
233+
namespacedArtifactPath := filepath.Join(artifactPath, namespace.Name)
234+
if err := os.Mkdir(namespacedArtifactPath, 0755); err != nil {
235+
GinkgoWriter.Printf("Failed to create namespaced artifact path %w", err)
236+
continue
237+
}
238+
239+
// get all deployments in the namespace and save them to the artifact path.
240+
deployments := appsv1.DeploymentList{}
241+
if err := c.List(ctx, &deployments, client.InNamespace(namespace.Name)); err != nil {
242+
GinkgoWriter.Printf("Failed to list deployments %w in namespace: %q", err, namespace.Name)
243+
continue
244+
}
245+
246+
for _, deployment := range deployments.Items {
247+
// Save deployment to artifact path
248+
deploymentYaml, err := yaml.Marshal(deployment)
249+
if err != nil {
250+
GinkgoWriter.Printf("Failed to marshal deployment %w", err)
251+
continue
252+
}
253+
if err := os.WriteFile(filepath.Join(namespacedArtifactPath, deployment.Name+"-deployment.yaml"), deploymentYaml, 0644); err != nil {
254+
GinkgoWriter.Printf("Failed to write deployment to file %w", err)
255+
}
256+
}
257+
258+
// Get logs from all pods in all namespaces
259+
pods := corev1.PodList{}
260+
if err := c.List(ctx, &pods, client.InNamespace(namespace.Name)); err != nil {
261+
GinkgoWriter.Printf("Failed to list pods %w in namespace: %q", err, namespace.Name)
262+
}
263+
for _, pod := range pods.Items {
264+
if pod.Status.Phase != v1.PodRunning && pod.Status.Phase != v1.PodSucceeded && pod.Status.Phase != v1.PodFailed {
265+
continue
266+
}
267+
for _, container := range pod.Spec.Containers {
268+
logs, err := kubeClient.CoreV1().Pods(namespace.Name).GetLogs(pod.Name, &v1.PodLogOptions{Container: container.Name}).Stream(ctx)
269+
if err != nil {
270+
GinkgoWriter.Printf("Failed to get logs for pod %q in namespace %q: %w", pod.Name, namespace.Name, err)
271+
continue
272+
}
273+
defer logs.Close()
274+
275+
outFile, err := os.Create(filepath.Join(namespacedArtifactPath, pod.Name+"-"+container.Name+"-logs.txt"))
276+
if err != nil {
277+
GinkgoWriter.Printf("Failed to create file for pod %q in namespace %q: %w", pod.Name, namespace.Name, err)
278+
continue
279+
}
280+
defer outFile.Close()
281+
282+
if _, err := io.Copy(outFile, logs); err != nil {
283+
GinkgoWriter.Printf("Failed to copy logs for pod %q in namespace %q: %w", pod.Name, namespace.Name, err)
284+
continue
285+
}
286+
}
287+
}
288+
}
289+
}

0 commit comments

Comments
 (0)