@@ -3,6 +3,7 @@ package jobs
33import (
44 "bytes"
55 "context"
6+ "encoding/json"
67 "fmt"
78 "log"
89 "path/filepath"
@@ -14,6 +15,8 @@ import (
1415 "github.com/nginxinc/nginx-k8s-supportpkg/pkg/mock"
1516 "github.com/stretchr/testify/assert"
1617 "go.uber.org/mock/gomock"
18+ corev1 "k8s.io/api/core/v1"
19+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
1720 "k8s.io/apimachinery/pkg/runtime"
1821 "k8s.io/client-go/kubernetes/fake"
1922 k8stesting "k8s.io/client-go/testing" // Add this line
@@ -142,3 +145,370 @@ func TestCommonJobList_PodListError(t *testing.T) {
142145 assert .Nil (t , result .Error ) // The job itself doesn't fail, just logs errors
143146 }
144147}
148+
149+ func TestCommonJobList_FileCreation (t * testing.T ) {
150+ tests := []struct {
151+ name string
152+ jobName string
153+ jobIndex int
154+ expectedFiles []string
155+ setupMockObjects func () []runtime.Object
156+ verifyFileContent func (t * testing.T , files map [string ][]byte , tmpDir string )
157+ }{
158+ {
159+ name : "pod-list creates pods.json files" ,
160+ jobName : "pod-list" ,
161+ jobIndex : 0 ,
162+ expectedFiles : []string {
163+ "resources/default/pods.json" ,
164+ "resources/test-ns/pods.json" ,
165+ },
166+ setupMockObjects : func () []runtime.Object {
167+ return []runtime.Object {
168+ & corev1.Pod {
169+ ObjectMeta : metav1.ObjectMeta {Name : "test-pod" , Namespace : "default" },
170+ },
171+ }
172+ },
173+ verifyFileContent : func (t * testing.T , files map [string ][]byte , tmpDir string ) {
174+ // Verify JSON structure
175+ for path , content := range files {
176+ if strings .Contains (path , "pods.json" ) {
177+ var podList corev1.PodList
178+ err := json .Unmarshal (content , & podList )
179+ assert .NoError (t , err , "Should be valid JSON" )
180+ assert .GreaterOrEqual (t , len (podList .Items ), 0 , "Should contain pod items" )
181+ }
182+ }
183+ },
184+ },
185+ // Add more test cases for other jobs...
186+ }
187+
188+ for _ , tt := range tests {
189+ t .Run (tt .name , func (t * testing.T ) {
190+ tmpDir := t .TempDir ()
191+ var logOutput bytes.Buffer
192+
193+ // Setup mock objects
194+ mockObjects := tt .setupMockObjects ()
195+ client := fake .NewSimpleClientset (mockObjects ... )
196+
197+ dc := & data_collector.DataCollector {
198+ BaseDir : tmpDir ,
199+ Namespaces : []string {"default" , "test-ns" },
200+ Logger : log .New (& logOutput , "" , 0 ),
201+ K8sCoreClientSet : client ,
202+ }
203+
204+ // Execute the specific job
205+ jobs := CommonJobList ()
206+ job := jobs [tt .jobIndex ]
207+ assert .Equal (t , tt .jobName , job .Name )
208+
209+ ctx := context .Background ()
210+ ch := make (chan JobResult , 1 )
211+ job .Execute (dc , ctx , ch )
212+
213+ result := <- ch
214+
215+ // Verify expected number of files
216+ assert .Len (t , result .Files , len (tt .expectedFiles ),
217+ "Should create expected number of files" )
218+
219+ // Verify each expected file exists
220+ for _ , expectedFile := range tt .expectedFiles {
221+ expectedPath := filepath .Join (tmpDir , expectedFile )
222+ content , exists := result .Files [expectedPath ]
223+ assert .True (t , exists , "Expected file should exist: %s" , expectedFile )
224+ assert .NotEmpty (t , content , "File content should not be empty: %s" , expectedFile )
225+ }
226+
227+ // Custom content verification
228+ if tt .verifyFileContent != nil {
229+ tt .verifyFileContent (t , result .Files , tmpDir )
230+ }
231+
232+ // Verify no errors for successful operations
233+ if len (tt .expectedFiles ) > 0 {
234+ assert .Nil (t , result .Error , "Should not have errors for successful execution" )
235+ }
236+ })
237+ }
238+ }
239+
240+ func TestCommonJobList_ResourceListJobs (t * testing.T ) {
241+ resourceTests := []struct {
242+ jobName string
243+ jobIndex int
244+ resourceType string
245+ fileName string
246+ setupObjects func () []runtime.Object
247+ }{
248+ {
249+ jobName : "pod-list" ,
250+ jobIndex : 0 ,
251+ resourceType : "pods" ,
252+ fileName : "pods.json" ,
253+ setupObjects : func () []runtime.Object {
254+ return []runtime.Object {
255+ & corev1.Pod {ObjectMeta : metav1.ObjectMeta {Name : "test-pod" , Namespace : "default" }},
256+ }
257+ },
258+ },
259+ {
260+ jobName : "service-list" ,
261+ jobIndex : 9 ,
262+ resourceType : "services" ,
263+ fileName : "services.json" ,
264+ setupObjects : func () []runtime.Object {
265+ return []runtime.Object {
266+ & corev1.Service {ObjectMeta : metav1.ObjectMeta {Name : "test-svc" , Namespace : "default" }},
267+ }
268+ },
269+ },
270+ {
271+ jobName : "configmap-list" ,
272+ jobIndex : 8 ,
273+ resourceType : "configmaps" ,
274+ fileName : "configmaps.json" ,
275+ setupObjects : func () []runtime.Object {
276+ return []runtime.Object {
277+ & corev1.ConfigMap {ObjectMeta : metav1.ObjectMeta {Name : "test-cm" , Namespace : "default" }},
278+ }
279+ },
280+ },
281+ }
282+
283+ for _ , tt := range resourceTests {
284+ t .Run (tt .jobName , func (t * testing.T ) {
285+ tmpDir := t .TempDir ()
286+ var logOutput bytes.Buffer
287+
288+ client := fake .NewSimpleClientset (tt .setupObjects ()... )
289+
290+ dc := & data_collector.DataCollector {
291+ BaseDir : tmpDir ,
292+ Namespaces : []string {"default" },
293+ Logger : log .New (& logOutput , "" , 0 ),
294+ K8sCoreClientSet : client ,
295+ }
296+
297+ jobs := CommonJobList ()
298+ job := jobs [tt .jobIndex ]
299+
300+ ctx := context .Background ()
301+ ch := make (chan JobResult , 1 )
302+ job .Execute (dc , ctx , ch )
303+
304+ result := <- ch
305+
306+ // Verify file creation
307+ expectedPath := filepath .Join (tmpDir , "resources/default" , tt .fileName )
308+ content , exists := result .Files [expectedPath ]
309+ assert .True (t , exists , "Expected %s file should exist" , tt .fileName )
310+ assert .NotEmpty (t , content , "File content should not be empty" )
311+
312+ // Verify JSON structure
313+ var jsonData map [string ]interface {}
314+ err := json .Unmarshal (content , & jsonData )
315+ assert .NoError (t , err , "Content should be valid JSON" )
316+ assert .Contains (t , jsonData , "items" , "Should contain 'items' field" )
317+ })
318+ }
319+ }
320+
321+ func TestCommonJobList_CollectPodsLogs_FileCreation (t * testing.T ) {
322+ tmpDir := t .TempDir ()
323+ var logOutput bytes.Buffer
324+
325+ testPod := & corev1.Pod {
326+ ObjectMeta : metav1.ObjectMeta {
327+ Name : "nginx-pod" ,
328+ Namespace : "default" ,
329+ },
330+ Spec : corev1.PodSpec {
331+ Containers : []corev1.Container {
332+ {Name : "nginx" , Image : "nginx:latest" },
333+ {Name : "sidecar" , Image : "busybox:latest" },
334+ },
335+ },
336+ }
337+
338+ client := fake .NewSimpleClientset (testPod )
339+
340+ dc := & data_collector.DataCollector {
341+ BaseDir : tmpDir ,
342+ Namespaces : []string {"default" },
343+ Logger : log .New (& logOutput , "" , 0 ),
344+ K8sCoreClientSet : client ,
345+ }
346+
347+ jobs := CommonJobList ()
348+ collectLogsJob := jobs [1 ] // collect-pods-logs
349+
350+ ctx := context .Background ()
351+ ch := make (chan JobResult , 1 )
352+ collectLogsJob .Execute (dc , ctx , ch )
353+
354+ result := <- ch
355+
356+ // Expected log files (will fail with fake client, but we can verify the attempt)
357+ expectedLogFiles := []string {
358+ "logs/default/nginx-pod__nginx.txt" ,
359+ "logs/default/nginx-pod__sidecar.txt" ,
360+ }
361+
362+ // Verify expected file paths would be constructed correctly
363+ for _ , expectedFile := range expectedLogFiles {
364+ expectedFullPath := filepath .Join (tmpDir , expectedFile )
365+ _ , exists := result .Files [expectedFullPath ]
366+ assert .True (t , exists , "Expected file should exist: %s" , expectedFile )
367+ }
368+ }
369+
370+ func TestCommonJobList_ClusterLevelJobs (t * testing.T ) {
371+ clusterTests := []struct {
372+ name string
373+ jobName string
374+ jobIndex int
375+ expectedFile string
376+ }{
377+ {
378+ name : "k8s-version creates version.json" ,
379+ jobName : "k8s-version" ,
380+ jobIndex : 18 ,
381+ expectedFile : "k8s/version.json" ,
382+ },
383+ {
384+ name : "clusterroles-info creates clusterroles.json" ,
385+ jobName : "clusterroles-info" ,
386+ jobIndex : 20 ,
387+ expectedFile : "k8s/rbac/clusterroles.json" ,
388+ },
389+ {
390+ name : "nodes-info creates nodes.json and platform_info.json" ,
391+ jobName : "nodes-info" ,
392+ jobIndex : 22 ,
393+ expectedFile : "k8s/nodes.json" ,
394+ },
395+ }
396+
397+ for _ , tt := range clusterTests {
398+ t .Run (tt .name , func (t * testing.T ) {
399+ tmpDir := t .TempDir ()
400+ var logOutput bytes.Buffer
401+
402+ // Setup mock objects based on job type
403+ var mockObjects []runtime.Object
404+ if tt .jobName == "nodes-info" {
405+ mockObjects = []runtime.Object {
406+ & corev1.Node {
407+ ObjectMeta : metav1.ObjectMeta {
408+ Name : "test-node" ,
409+ Labels : map [string ]string {
410+ "node-role.kubernetes.io/control-plane" : "" ,
411+ },
412+ },
413+ Status : corev1.NodeStatus {
414+ NodeInfo : corev1.NodeSystemInfo {
415+ OSImage : "Ubuntu 20.04" ,
416+ OperatingSystem : "linux" ,
417+ Architecture : "amd64" ,
418+ },
419+ },
420+ },
421+ }
422+ }
423+
424+ client := fake .NewSimpleClientset (mockObjects ... )
425+
426+ dc := & data_collector.DataCollector {
427+ BaseDir : tmpDir ,
428+ Namespaces : []string {"default" },
429+ Logger : log .New (& logOutput , "" , 0 ),
430+ K8sCoreClientSet : client ,
431+ }
432+
433+ jobs := CommonJobList ()
434+ job := jobs [tt .jobIndex ]
435+ assert .Equal (t , tt .jobName , job .Name )
436+
437+ ctx := context .Background ()
438+ ch := make (chan JobResult , 1 )
439+ job .Execute (dc , ctx , ch )
440+
441+ result := <- ch
442+
443+ // Verify main file creation
444+ expectedPath := filepath .Join (tmpDir , tt .expectedFile )
445+ content , exists := result .Files [expectedPath ]
446+ assert .True (t , exists , "Expected file should exist: %s" , tt .expectedFile )
447+ assert .NotEmpty (t , content , "File content should not be empty" )
448+
449+ // Special case for nodes-info which creates additional platform_info.json
450+ if tt .jobName == "nodes-info" {
451+ platformInfoPath := filepath .Join (tmpDir , "platform_info.json" )
452+ platformContent , platformExists := result .Files [platformInfoPath ]
453+ assert .True (t , platformExists , "platform_info.json should exist" )
454+ assert .NotEmpty (t , platformContent , "Platform info should not be empty" )
455+
456+ // Verify platform info structure
457+ var platformInfo data_collector.PlatformInfo
458+ err := json .Unmarshal (platformContent , & platformInfo )
459+ assert .NoError (t , err , "Platform info should be valid JSON" )
460+ assert .NotEmpty (t , platformInfo .PlatformType , "Platform type should be set" )
461+ assert .NotEmpty (t , platformInfo .Hostname , "Hostname should be set" )
462+ }
463+ })
464+ }
465+ }
466+
467+ func TestCommonJobList_AllJobsFileCreation (t * testing.T ) {
468+ dc := mock .SetupMockDataCollector (t )
469+ jobs := CommonJobList ()
470+
471+ // Track all created files across all jobs
472+ allFiles := make (map [string ][]byte )
473+
474+ for i , job := range jobs {
475+ t .Run (fmt .Sprintf ("job_%d_%s" , i , job .Name ), func (t * testing.T ) {
476+ ctx := context .Background ()
477+ ch := make (chan JobResult , 1 )
478+ job .Execute (dc , ctx , ch )
479+
480+ result := <- ch
481+
482+ // Collect files from this job
483+ for path , content := range result .Files {
484+ allFiles [path ] = content
485+ }
486+
487+ // Verify files are within base directory
488+ for filePath := range result .Files {
489+ assert .True (t , strings .HasPrefix (filePath , dc .BaseDir ),
490+ "File should be within base directory: %s" , filePath )
491+ }
492+ })
493+ }
494+
495+ // Verify overall file structure
496+ t .Run ("verify_overall_structure" , func (t * testing.T ) {
497+ // Check that we have files in expected directories
498+ expectedDirs := []string {"resources" , "k8s" , "k8s/rbac" }
499+
500+ for _ , expectedDir := range expectedDirs {
501+ found := false
502+ for filePath := range allFiles {
503+ if strings .Contains (filePath , expectedDir ) {
504+ found = true
505+ break
506+ }
507+ }
508+ assert .True (t , found , "Should have files in directory: %s" , expectedDir )
509+ }
510+
511+ // Verify minimum number of files created
512+ assert .Greater (t , len (allFiles ), 10 , "Should create multiple files across all jobs" )
513+ })
514+ }
0 commit comments