Skip to content

Commit ccfcb82

Browse files
committed
Feat: Generate a manifest file for ihealth ingestion
1 parent e875ba9 commit ccfcb82

File tree

4 files changed

+160
-20
lines changed

4 files changed

+160
-20
lines changed

cmd/nginx-supportpkg.go

Lines changed: 51 additions & 5 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,60 @@ func Execute() {
6669

6770
if collector.AllNamespacesExist() {
6871
failedJobs := 0
72+
totalJobs := len(jobList)
73+
var jobTimings []data_collector.JobTiming
74+
6975
for _, job := range jobList {
7076
fmt.Printf("Running job %s...", job.Name)
71-
err, Skipped := job.Collect(&collector)
72-
if Skipped {
77+
78+
// Record job start time
79+
jobStartTime := time.Now()
80+
jobResult := job.Collect(&collector)
81+
82+
// Record job end time and calculate duration
83+
jobEndTime := time.Now()
84+
duration := jobEndTime.Sub(jobStartTime)
85+
86+
// Create job timing record
87+
files := make([]string, 0, len(jobResult.Files))
88+
for filename := range jobResult.Files {
89+
if len(filename) > 0 {
90+
packagePath := strings.TrimPrefix(filename, collector.BaseDir)
91+
files = append(files, packagePath)
92+
}
93+
}
94+
95+
jobTiming := data_collector.JobTiming{
96+
Name: job.Name,
97+
StartTime: jobStartTime.UTC().Format(time.RFC3339),
98+
EndTime: jobEndTime.UTC().Format(time.RFC3339),
99+
Duration: duration.String(),
100+
Files: files,
101+
}
102+
103+
if jobResult.Skipped {
73104
fmt.Print(" SKIPPED\n")
74-
} else if err != nil {
75-
fmt.Printf(" FAILED: %s\n", err)
105+
} else if jobResult.Error != nil {
106+
fmt.Printf(" FAILED: %s\n", jobResult.Error)
76107
failedJobs++
77108
} else {
78109
fmt.Print(" COMPLETED\n")
79110
}
111+
112+
jobTimings = append(jobTimings, jobTiming)
113+
}
114+
115+
// Generate manifest with job timings - UPDATE THIS LINE
116+
manifestData, err := collector.GenerateManifest(product, startTime, totalJobs, failedJobs, jobTimings)
117+
if err != nil {
118+
fmt.Printf("Warning: Failed to generate manifest: %v\n", err)
119+
} else {
120+
// Save manifest to base directory
121+
manifestPath := filepath.Join(collector.BaseDir, "manifest.json")
122+
err = os.WriteFile(manifestPath, manifestData, 0644)
123+
if err != nil {
124+
fmt.Printf("Warning: Failed to write manifest: %v\n", err)
125+
}
80126
}
81127

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

pkg/data_collector/data_collector.go

Lines changed: 101 additions & 0 deletions
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"
@@ -60,6 +62,64 @@ type DataCollector struct {
6062
ExcludeTimeSeriesData bool
6163
}
6264

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

65125
tmpDir, err := os.MkdirTemp("", "-pkg-diag")
@@ -266,3 +326,44 @@ func (c *DataCollector) AllNamespacesExist() bool {
266326

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

pkg/jobs/job.go

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ type JobResult struct {
4040
Skipped bool
4141
}
4242

43-
func (j Job) Collect(dc *data_collector.DataCollector) (error, bool) {
43+
func (j Job) Collect(dc *data_collector.DataCollector) JobResult {
4444
ch := make(chan JobResult, 1)
4545

4646
ctx, cancel := context.WithTimeout(context.Background(), j.Timeout)
@@ -52,32 +52,32 @@ func (j Job) Collect(dc *data_collector.DataCollector) (error, bool) {
5252
select {
5353
case <-ctx.Done():
5454
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
55+
return JobResult{Error: fmt.Errorf("Context cancelled: %v", ctx.Err()), Skipped: false}
5656

5757
case jobResults := <-ch:
5858
if jobResults.Skipped {
5959
dc.Logger.Printf("\tJob %s has been skipped\n---\n", j.Name)
60-
return nil, true
60+
return JobResult{Error: nil, Skipped: true}
6161
}
6262
if jobResults.Error != nil {
6363
dc.Logger.Printf("\tJob %s has failed: %s\n", j.Name, jobResults.Error)
64-
return jobResults.Error, false
64+
return JobResult{Error: jobResults.Error, Skipped: false}
6565
}
6666

6767
for fileName, fileValue := range jobResults.Files {
6868
err := os.MkdirAll(filepath.Dir(fileName), os.ModePerm)
6969
if err != nil {
70-
return fmt.Errorf("MkdirAll failed: %v", err), jobResults.Skipped
70+
return JobResult{Error: fmt.Errorf("MkdirAll failed: %v", err), Skipped: jobResults.Skipped}
7171
}
7272
file, _ := os.Create(fileName)
7373
_, err = file.Write(fileValue)
7474
if err != nil {
75-
return fmt.Errorf("Write failed: %v", err), jobResults.Skipped
75+
return JobResult{Error: fmt.Errorf("Write failed: %v", err), Skipped: jobResults.Skipped}
7676
}
7777
_ = file.Close()
7878
dc.Logger.Printf("\tJob %s wrote %d bytes to %s\n", j.Name, len(fileValue), fileName)
7979
}
8080
dc.Logger.Printf("\tJob %s completed successfully\n---\n", j.Name)
81-
return nil, jobResults.Skipped
81+
return JobResult{Files: jobResults.Files, Error: nil, Skipped: false}
8282
}
8383
}

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)