Skip to content

Commit e9183f0

Browse files
authored
Merge pull request #168 from nginx/ihealth-integration
ihealth integration
2 parents e875ba9 + 43694fc commit e9183f0

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"
@@ -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 JobInfo 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")
@@ -108,7 +168,7 @@ func (c *DataCollector) WrapUp(product string) (string, error) {
108168
unixTime := time.Now().Unix()
109169
unixTimeString := strconv.FormatInt(unixTime, 10)
110170
tarballName := fmt.Sprintf("%s-supportpkg-%s.tar.gz", product, unixTimeString)
111-
tarballRootDirName := fmt.Sprintf("%s-supportpkg-%s", product, unixTimeString)
171+
tarballRootDirName := "."
112172

113173
err := c.LogFile.Close()
114174
if err != nil {
@@ -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 []JobInfo) ([]byte, error) {
331+
manifest := Manifest{
332+
Version: "1.2", // Match the schema version
333+
Timestamp: TimestampInfo{
334+
Start: startTime.UTC().Format(time.RFC3339Nano),
335+
Stop: time.Now().UTC().Format(time.RFC3339Nano),
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: 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)