@@ -3,22 +3,34 @@ package e2e
33import (
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
1930const (
2031 defaultTimeout = 30 * time .Second
2132 defaultPoll = 1 * time .Second
33+ artifactName = "operator-controller-e2e"
2234)
2335
2436var _ = 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