@@ -13,8 +13,12 @@ import (
1313
1414 . "github.com/onsi/ginkgo/v2"
1515 . "github.com/onsi/gomega"
16+ "gopkg.in/yaml.v3"
1617 corev1 "k8s.io/api/core/v1"
18+ apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
1719 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
20+ "k8s.io/apimachinery/pkg/runtime/schema"
21+ "k8s.io/client-go/dynamic"
1822 "k8s.io/client-go/kubernetes"
1923 "k8s.io/kubectl/pkg/describe"
2024 clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
@@ -99,19 +103,98 @@ func (Metal3LogCollector) CollectMachinePoolLog(_ context.Context, _ client.Clie
99103 return fmt .Errorf ("CollectMachinePoolLog not implemented" )
100104}
101105
102- func FetchManifests (_ framework.ClusterProxy ) error {
103- return fmt .Errorf ("FetchManifests not implemented" )
106+ // FetchManifests fetches relevant Metal3, CAPI, and Kubernetes core resources
107+ // and dumps them to a file.
108+ func FetchManifests (clusterProxy framework.ClusterProxy , outputPath string ) error {
109+ outputPath = filepath .Join (outputPath , clusterProxy .GetName ())
110+ ctx := context .Background ()
111+ restConfig := clusterProxy .GetRESTConfig ()
112+ dynamicClient , err := dynamic .NewForConfig (restConfig )
113+ if err != nil {
114+ return fmt .Errorf ("could not create dynamic client: %v" , err )
115+ }
116+
117+ k8sCoreManifests := []string {
118+ // This list must contain the plural form of the Kubernetes core
119+ // resources
120+ "deployments" ,
121+ "replicasets" ,
122+ }
123+
124+ // Set group and version to get Kubernetes core resources and dump them
125+ for _ , manifest := range k8sCoreManifests {
126+ gvr := schema.GroupVersionResource {
127+ Group : "apps" ,
128+ Version : "v1" ,
129+ Resource : manifest ,
130+ }
131+
132+ if err := DumpGVR (ctx , dynamicClient , gvr , outputPath ); err != nil {
133+ return err
134+ }
135+ }
136+
137+ manifests := []string {
138+ // This list contains all resources of interest that are NOT Kubernetes
139+ // core resources.
140+ "bmh" ,
141+ "hardwaredata" ,
142+ "cluster" ,
143+ "machine" ,
144+ "machinedeployment" ,
145+ "machinehealthchecks" ,
146+ "machinesets" ,
147+ "machinepools" ,
148+ "m3cluster" ,
149+ "m3machine" ,
150+ "metal3machinetemplate" ,
151+ "kubeadmconfig" ,
152+ "kubeadmconfigtemplates" ,
153+ "kubeadmcontrolplane" ,
154+ "ippool" ,
155+ "ipclaim" ,
156+ "ipaddress" ,
157+ "m3data" ,
158+ "m3dataclaim" ,
159+ "m3datatemplate" ,
160+ }
161+ client := clusterProxy .GetClient ()
162+
163+ // Get all CustomResourceDefinitions (CRDs)
164+ crds := apiextensionsv1.CustomResourceDefinitionList {}
165+ if err := client .List (ctx , & crds ); err != nil {
166+ return fmt .Errorf ("could not list CRDs: %v" , err )
167+ }
168+ if len (crds .Items ) == 0 {
169+ return nil
170+ }
171+
172+ // Check if the resource is in the manifest list.
173+ // If it is, dump it to a file
174+ for _ , crd := range crds .Items {
175+ if crdIsInList (crd , manifests ) {
176+ gvr := schema.GroupVersionResource {
177+ Group : crd .Spec .Group ,
178+ Version : crd .Status .StoredVersions [0 ],
179+ Resource : crd .Spec .Names .Plural ,
180+ }
181+
182+ if err := DumpGVR (ctx , dynamicClient , gvr , outputPath ); err != nil {
183+ return err
184+ }
185+ }
186+ }
187+ fmt .Printf ("Successfully collected manifests for cluster %s." , clusterProxy .GetName ())
188+ return nil
104189}
105190
191+ // FetchClusterLogs fetches logs from all pods in the cluster and writes them
192+ // to files.
106193func FetchClusterLogs (clusterProxy framework.ClusterProxy , outputPath string ) error {
107194 ctx := context .Background ()
108195 baseDir := filepath .Join (outputPath , clusterProxy .GetName ())
109- err := os .MkdirAll (baseDir , 0775 )
110- if err != nil {
111- return fmt .Errorf ("couldn't create directory: %v" , err )
112- }
113196
114- // get the clientset
197+ // Get the clientset
115198 clientset := clusterProxy .GetClientSet ()
116199
117200 // Print the Pods' information to file
@@ -165,16 +248,6 @@ func FetchClusterLogs(clusterProxy framework.ClusterProxy, outputPath string) er
165248 continue
166249 }
167250 for _ , pod := range pods .Items {
168- machineName := pod .Spec .NodeName
169- // Create a directory for each pod and the path to it if
170- // it does not exist
171- podDir := filepath .Join (baseDir , "machines" , machineName , namespace .Name , pod .Name )
172- err = os .MkdirAll (podDir , 0775 )
173- if err != nil {
174- fmt .Printf ("couldn't write to file: %v" , err )
175- continue
176- }
177-
178251 // Get detailed information about the Pod
179252 // This does the same thing as:
180253 // kubectl --kubeconfig="${KUBECONFIG_WORKLOAD}" describe pods -n "${NAMESPACE}" "${POD}"
@@ -190,13 +263,9 @@ func FetchClusterLogs(clusterProxy framework.ClusterProxy, outputPath string) er
190263 continue
191264 }
192265
193- // Print the Pod information to file
194- file := filepath .Join (podDir , "stdout_describe.log" )
195- err = os .WriteFile (file , []byte (podDescription ), 0600 )
196- if err != nil {
197- fmt .Printf ("couldn't write to file: %v" , err )
198- continue
199- }
266+ machineName := pod .Spec .NodeName
267+ podDir := filepath .Join (baseDir , "machines" , machineName , namespace .Name , pod .Name )
268+ writeToFile ([]byte (podDescription ), "stdout_describe.log" , podDir )
200269
201270 // Get containers of the Pod
202271 for _ , container := range pod .Spec .Containers {
@@ -215,12 +284,9 @@ func FetchClusterLogs(clusterProxy framework.ClusterProxy, outputPath string) er
215284 return nil
216285}
217286
287+ // CollectContainerLogs fetches logs from a specific container in a pod and
288+ // writes them to a file.
218289func CollectContainerLogs (ctx context.Context , namespace string , podName string , containerName string , clientset * kubernetes.Clientset , outputPath string ) error {
219- err := os .MkdirAll (outputPath , 0775 )
220- if err != nil {
221- return fmt .Errorf ("couldn't create directory: %v" , err )
222- }
223-
224290 // Get logs of a container
225291 // Does the same thing as:
226292 // kubectl --kubeconfig="${KUBECONFIG_WORKLOAD}" logs -n "${NAMESPACE}" "${POD}" "${CONTAINER}"
@@ -241,12 +307,70 @@ func CollectContainerLogs(ctx context.Context, namespace string, podName string,
241307 }
242308 podStr := buf .String ()
243309
244- // Print the Pod information to file
245- file := filepath .Join (outputPath , "stdout.log" )
246- err = os .WriteFile (file , []byte (podStr ), 0600 )
310+ writeToFile ([]byte (podStr ), "stdout.log" , outputPath )
311+
312+ return nil
313+ }
314+
315+ // writeToFile writes content to a file,
316+ // creating any missing directories in the path.
317+ func writeToFile (content []byte , fileName string , filePath string ) {
318+ // Create any missing directories in the path
319+ err := os .MkdirAll (filePath , 0775 )
320+ if err != nil {
321+ fmt .Printf ("couldn't create directory: %v" , err )
322+ }
323+ // Write content to file
324+ file := filepath .Join (filePath , fileName )
325+ err = os .WriteFile (file , content , 0600 )
326+ if err != nil {
327+ fmt .Printf ("couldn't write to file: %v" , err )
328+ }
329+ }
330+
331+ // crdIsInList checks if a CustomResourceDefinition is in the provided list of
332+ // resource names.
333+ func crdIsInList (crd apiextensionsv1.CustomResourceDefinition , list []string ) bool {
334+ plural := crd .Spec .Names .Plural
335+ singular := crd .Spec .Names .Singular
336+ shortNames := crd .Spec .Names .ShortNames
337+
338+ for _ , name := range list {
339+ if name == plural {
340+ return true
341+ }
342+ if name == singular {
343+ return true
344+ }
345+ for _ , shortName := range shortNames {
346+ if name == shortName {
347+ return true
348+ }
349+ }
350+ }
351+ return false
352+ }
353+
354+ // DumpGVR fetches resources of a given GroupVersionResource
355+ // and writes them to files.
356+ func DumpGVR (ctx context.Context , dynamicClient * dynamic.DynamicClient , gvr schema.GroupVersionResource , outputPath string ) error {
357+ resources , err := dynamicClient .Resource (gvr ).List (ctx , metav1.ListOptions {})
247358 if err != nil {
248- return fmt .Errorf ("couldn't write to file: %v" , err )
359+ return fmt .Errorf ("could not get resources: %v" , err )
360+ }
361+ if len (resources .Items ) == 0 {
362+ return nil
249363 }
250364
365+ // Write resource to file
366+ for _ , resource := range resources .Items {
367+ filePath := filepath .Join (outputPath , resource .GetNamespace (), resource .GetKind ())
368+ fileName := fmt .Sprintf ("%s.yaml" , resource .GetName ())
369+ content , err := yaml .Marshal (resource )
370+ if err != nil {
371+ return fmt .Errorf ("could not marshal content: %v" , err )
372+ }
373+ writeToFile (content , fileName , filePath )
374+ }
251375 return nil
252376}
0 commit comments