Skip to content

Commit 29de4ac

Browse files
committed
Merge branch 'main' into mrajagopal-unit-tests
2 parents 7bb7833 + e9183f0 commit 29de4ac

File tree

4 files changed

+175
-23
lines changed

4 files changed

+175
-23
lines changed

cmd/nginx-supportpkg.go

Lines changed: 46 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,10 @@ package cmd
2121
import (
2222
"fmt"
2323
"os"
24+
"path/filepath"
2425
"slices"
26+
"strings"
27+
"time"
2528

2629
"github.com/nginxinc/nginx-k8s-supportpkg/pkg/data_collector"
2730
"github.com/nginxinc/nginx-k8s-supportpkg/pkg/jobs"
@@ -40,7 +43,7 @@ func Execute() {
4043
Short: "nginx-supportpkg - a tool to create Ingress Controller diagnostics package",
4144
Long: `nginx-supportpkg - a tool to create Ingress Controller diagnostics package`,
4245
Run: func(cmd *cobra.Command, args []string) {
43-
46+
startTime := time.Now()
4447
err := data_collector.NewDataCollector(&collector)
4548
if err != nil {
4649
fmt.Println(fmt.Errorf("unable to start data collector: %s", err))
@@ -66,17 +69,56 @@ func Execute() {
6669

6770
if collector.AllNamespacesExist() {
6871
failedJobs := 0
72+
totalJobs := len(jobList)
73+
var jobTimings []data_collector.JobInfo
74+
const jobNameWidth = 30
75+
6976
for _, job := range jobList {
70-
fmt.Printf("Running job %s...", job.Name)
71-
err, Skipped := job.Collect(&collector)
72-
if Skipped {
77+
fmt.Printf("Running job %s", job.Name)
78+
// Calculate number of dots needed
79+
dots := jobNameWidth - len(job.Name)
80+
if dots < 0 {
81+
dots = 0
82+
}
83+
fmt.Printf("%s ", strings.Repeat(".", dots))
84+
// Record job start and end time to calculate duration
85+
jobStartTime := time.Now()
86+
err, skipped, files := job.Collect(&collector)
87+
jobEndTime := time.Now()
88+
duration := jobEndTime.Sub(jobStartTime)
89+
90+
// Create job info record
91+
jobInfo := data_collector.JobInfo{
92+
Name: job.Name,
93+
StartTime: jobStartTime.UTC().Format(time.RFC3339Nano),
94+
EndTime: jobEndTime.UTC().Format(time.RFC3339Nano),
95+
Duration: duration.String(),
96+
Files: files,
97+
}
98+
99+
if skipped {
73100
fmt.Print(" SKIPPED\n")
74101
} else if err != nil {
75102
fmt.Printf(" FAILED: %s\n", err)
76103
failedJobs++
77104
} else {
78105
fmt.Print(" COMPLETED\n")
79106
}
107+
108+
jobTimings = append(jobTimings, jobInfo)
109+
}
110+
111+
// Generate manifest with job timings
112+
manifestData, err := collector.GenerateManifest(product, startTime, totalJobs, failedJobs, jobTimings)
113+
if err != nil {
114+
fmt.Printf("Warning: Failed to generate manifest: %v\n", err)
115+
} else {
116+
// Save manifest to base directory
117+
manifestPath := filepath.Join(collector.BaseDir, "manifest.json")
118+
err = os.WriteFile(manifestPath, manifestData, 0644)
119+
if err != nil {
120+
fmt.Printf("Warning: Failed to write manifest: %v\n", err)
121+
}
80122
}
81123

82124
tarFile, err := collector.WrapUp(product)

pkg/data_collector/data_collector.go

Lines changed: 102 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import (
2323
"bytes"
2424
"compress/gzip"
2525
"context"
26+
"encoding/json"
2627
"fmt"
2728
"io"
2829
"log"
@@ -33,6 +34,7 @@ import (
3334

3435
helmClient "github.com/mittwald/go-helm-client"
3536
"github.com/nginxinc/nginx-k8s-supportpkg/pkg/crds"
37+
"github.com/nginxinc/nginx-k8s-supportpkg/pkg/version"
3638
corev1 "k8s.io/api/core/v1"
3739
crdClient "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
3840
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -62,6 +64,64 @@ type DataCollector struct {
6264
QueryCRD func(crd crds.Crd, namespace string, ctx context.Context) ([]byte, error)
6365
}
6466

67+
type Manifest struct {
68+
Version string `json:"version"`
69+
Timestamp TimestampInfo `json:"ts"`
70+
PackageType string `json:"package_type"`
71+
RootDir string `json:"root_dir,omitempty"`
72+
Commands []Command `json:"commands,omitempty"`
73+
ProductInfo ProductInfo `json:"product_info"`
74+
PlatformInfo PlatformInfo `json:"platform_info"`
75+
Packages []SubPackage `json:"packages,omitempty"`
76+
}
77+
78+
type TimestampInfo struct {
79+
Start string `json:"start"`
80+
Stop string `json:"stop"`
81+
}
82+
83+
type Command struct {
84+
Name string `json:"name"`
85+
Cwd string `json:"cwd"`
86+
Ts CommandTiming `json:"ts"`
87+
Output string `json:"output"`
88+
RetCode int `json:"retcode,omitempty"`
89+
}
90+
91+
type CommandTiming struct {
92+
Start string `json:"start"`
93+
End string `json:"end"`
94+
}
95+
96+
type ProductInfo struct {
97+
Product string `json:"product"`
98+
Version string `json:"version"`
99+
}
100+
101+
type PlatformInfo struct {
102+
// Add platform-specific fields as needed
103+
K8sVersion string `json:"k8s_version,omitempty"`
104+
Namespaces []string `json:"namespaces,omitempty"`
105+
}
106+
107+
type SubPackage struct {
108+
Path string `json:"path"`
109+
Ts TimestampInfo `json:"ts"`
110+
SubPackageType string `json:"sub_package_type"`
111+
Name string `json:"name,omitempty"`
112+
ID string `json:"id,omitempty"`
113+
}
114+
115+
type JobInfo struct {
116+
Name string `json:"name"`
117+
StartTime string `json:"start_time"`
118+
EndTime string `json:"end_time"`
119+
Duration string `json:"duration"`
120+
Status string `json:"status"` // "completed", "failed", "skipped"
121+
Error string `json:"error,omitempty"`
122+
Files []string `json:"files,omitempty"` // List of files generated by the job
123+
}
124+
65125
func NewDataCollector(collector *DataCollector) error {
66126

67127
tmpDir, err := os.MkdirTemp("", "-pkg-diag")
@@ -112,7 +172,7 @@ func (c *DataCollector) WrapUp(product string) (string, error) {
112172
unixTime := time.Now().Unix()
113173
unixTimeString := strconv.FormatInt(unixTime, 10)
114174
tarballName := fmt.Sprintf("%s-supportpkg-%s.tar.gz", product, unixTimeString)
115-
tarballRootDirName := fmt.Sprintf("%s-supportpkg-%s", product, unixTimeString)
175+
tarballRootDirName := "."
116176

117177
err := c.LogFile.Close()
118178
if err != nil {
@@ -270,3 +330,44 @@ func (c *DataCollector) AllNamespacesExist() bool {
270330

271331
return allExist
272332
}
333+
334+
func (c *DataCollector) GenerateManifest(product string, startTime time.Time, jobsRun, jobsFailed int, jobTimings []JobInfo) ([]byte, error) {
335+
manifest := Manifest{
336+
Version: "1.2", // Match the schema version
337+
Timestamp: TimestampInfo{
338+
Start: startTime.UTC().Format(time.RFC3339Nano),
339+
Stop: time.Now().UTC().Format(time.RFC3339Nano),
340+
},
341+
PackageType: "root", // As defined in schema enum
342+
RootDir: ".",
343+
ProductInfo: ProductInfo{
344+
Product: product,
345+
Version: version.Version,
346+
},
347+
PlatformInfo: PlatformInfo{
348+
Namespaces: c.Namespaces,
349+
},
350+
Commands: []Command{},
351+
}
352+
353+
// Convert job timings to commands format
354+
for _, job := range jobTimings {
355+
for _, filename := range job.Files {
356+
command := Command{
357+
Name: job.Name,
358+
Cwd: ".",
359+
Ts: CommandTiming{
360+
Start: job.StartTime,
361+
End: job.EndTime,
362+
},
363+
Output: filename,
364+
}
365+
if job.Status == "failed" {
366+
command.RetCode = 1
367+
}
368+
manifest.Commands = append(manifest.Commands, command)
369+
}
370+
}
371+
372+
return json.MarshalIndent(manifest, "", " ")
373+
}

pkg/jobs/job.go

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import (
2323
"fmt"
2424
"os"
2525
"path/filepath"
26+
"strings"
2627
"time"
2728

2829
"github.com/nginxinc/nginx-k8s-supportpkg/pkg/data_collector"
@@ -40,7 +41,7 @@ type JobResult struct {
4041
Skipped bool
4142
}
4243

43-
func (j Job) Collect(dc *data_collector.DataCollector) (error, bool) {
44+
func (j Job) Collect(dc *data_collector.DataCollector) (error, bool, []string) {
4445
ch := make(chan JobResult, 1)
4546

4647
ctx, cancel := context.WithTimeout(context.Background(), j.Timeout)
@@ -52,32 +53,47 @@ func (j Job) Collect(dc *data_collector.DataCollector) (error, bool) {
5253
select {
5354
case <-ctx.Done():
5455
dc.Logger.Printf("\tJob %s has timed out: %s\n---\n", j.Name, ctx.Err())
55-
return fmt.Errorf("Context cancelled: %v", ctx.Err()), false
56+
return fmt.Errorf("Context cancelled: %v", ctx.Err()), false, nil
5657

5758
case jobResults := <-ch:
59+
files := j.GetFilesFromJobResult(dc, jobResults)
5860
if jobResults.Skipped {
5961
dc.Logger.Printf("\tJob %s has been skipped\n---\n", j.Name)
60-
return nil, true
61-
}
62-
if jobResults.Error != nil {
63-
dc.Logger.Printf("\tJob %s has failed: %s\n", j.Name, jobResults.Error)
64-
return jobResults.Error, false
62+
return nil, true, files
6563
}
6664

6765
for fileName, fileValue := range jobResults.Files {
6866
err := os.MkdirAll(filepath.Dir(fileName), os.ModePerm)
6967
if err != nil {
70-
return fmt.Errorf("MkdirAll failed: %v", err), jobResults.Skipped
68+
return fmt.Errorf("MkdirAll failed: %v", err), jobResults.Skipped, files
7169
}
7270
file, _ := os.Create(fileName)
7371
_, err = file.Write(fileValue)
7472
if err != nil {
75-
return fmt.Errorf("Write failed: %v", err), jobResults.Skipped
73+
return fmt.Errorf("Write failed: %v", err), jobResults.Skipped, files
7674
}
7775
_ = file.Close()
7876
dc.Logger.Printf("\tJob %s wrote %d bytes to %s\n", j.Name, len(fileValue), fileName)
7977
}
78+
79+
if jobResults.Error != nil {
80+
dc.Logger.Printf("\tJob %s has failed: %s\n", j.Name, jobResults.Error)
81+
fmt.Printf("Files collected so far: %v\n", files)
82+
return jobResults.Error, false, files
83+
}
84+
8085
dc.Logger.Printf("\tJob %s completed successfully\n---\n", j.Name)
81-
return nil, jobResults.Skipped
86+
return nil, false, files
87+
}
88+
}
89+
90+
func (j Job) GetFilesFromJobResult(dc *data_collector.DataCollector, jobResult JobResult) []string {
91+
files := make([]string, 0, len(jobResult.Files))
92+
for filename := range jobResult.Files {
93+
if len(filename) > 0 {
94+
packagePath := strings.TrimPrefix(filename, dc.BaseDir)
95+
files = append(files, packagePath)
96+
}
8297
}
98+
return files
8399
}

pkg/jobs/nim_job_list.go

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ package jobs
2020

2121
import (
2222
"context"
23-
"os"
2423
"path/filepath"
2524
"strings"
2625
"time"
@@ -246,13 +245,7 @@ func NIMJobList() []Job {
246245
jobResult.Error = err
247246
dc.Logger.Printf("\tFailed to copy dumped file %s from pod %s in namespace %s to %s: %v\n", config.outputFile, pod.Name, namespace, destPathFilename, err)
248247
} else {
249-
err = os.WriteFile(destPathFilename, fileContent, 0644)
250-
if err != nil {
251-
jobResult.Error = err
252-
dc.Logger.Printf("\tFailed to write file to %s: %v\n", destPathFilename, err)
253-
} else {
254-
dc.Logger.Printf("\tSuccessfully copied dumped file %s from pod %s in namespace %s to %s\n", config.outputFile, pod.Name, namespace, destPathFilename)
255-
}
248+
jobResult.Files[destPathFilename] = fileContent
256249
}
257250

258251
// Remove/delete the dumped file from the pod

0 commit comments

Comments
 (0)