Compare commits

...
28 Commits
Author SHA1 Message Date
Diamon Wiggins d6a17951d3 Merge pull request #564 from replicatedhq/diamonwiggins/sc-44286/run-pod-spec
RunPod Collector
2022-05-06 11:27:21 -04:00
Diamon Wiggins 9f527ee6a5 Merge branch 'main' into diamonwiggins/sc-44286/run-pod-spec 2022-05-06 11:08:15 -04:00
Ethan Mosbaugh 2c9a37a4f1 BoolOrString pollutes marshalling, does not respect omitempty (#566)
* BoolOrString pollutes marshalling, does not respect omitempty

* fix panic
2022-05-05 16:10:05 -07:00
Ethan Mosbaugh e6e9df1773 Merge pull request #565 from replicatedhq/mysql-collector-example
Mysql collector example
2022-05-04 11:14:25 -07:00
Ethan Mosbaugh 755220cd4c Mysql collector example 2022-05-04 17:45:10 +00:00
Edgar OchoaandEthan Mosbaugh 7289134757 Add Mysql variables to collector (#562)
* Add Mysql variables to collector

* Cleanup row scanning and a few updates based on feedback

* Close db connection

* Move defer db.close

* Updates based on feedback

* Use vars in loop instead of struct

* Only pull parameters specified in collector config

Co-authored-by: Ethan Mosbaugh <ethan@replicated.com>
2022-05-04 10:42:37 -07:00
Jalaja Ganapathy 63362d32ee Include osd disk usage (#563)
* Include osd disk usage

* add timeouts to ceph commands

* removed df, collect from host
2022-05-02 09:51:49 -07:00
diamonwiggins 6cdcb36127 change naming for runPod collector object 2022-05-02 03:12:07 +00:00
diamonwiggins dfe5538132 remove podLabels from run function 2022-05-02 02:53:29 +00:00
diamonwiggins 42902405cd adding new runpod collector and refactoring old run collector to use new code 2022-05-02 02:44:02 +00:00
diamonwiggins 2516924a92 moving imagepullsecret creation into non pod spec declaration 2022-04-19 20:41:28 +00:00
diamonwiggins ce4165f69e fixing typo 2022-04-19 20:29:28 +00:00
diamonwiggins 648f9b8d35 allow entire podspec to be passed in run collector 2022-04-19 16:25:59 +00:00
Ethan Mosbaugh aeb89e57a7 Merge pull request #556 from replicatedhq/emosbaugh/sc-45934/longhorn-logs-collector-all-files-are-empty
Fix missing longhorn logs
2022-04-01 15:21:56 -07:00
Ethan Mosbaugh 8d1a0f6d24 return ReadCloser 2022-04-01 14:22:44 -07:00
Ethan Mosbaugh 0cfd431274 Fix missing longhorn logs 2022-04-01 14:13:32 -07:00
Andrew Lavery fe5dd3ee8f Merge pull request #554 from replicatedhq/laverya/pods-with-notready-containers-are-unhealthy
pods with containers that are not 'ready' are unhealthy
2022-03-25 22:31:25 -04:00
Andrew Lavery 7eb1d5a5fb better parse namespaces, and parse legacy list-of-pods storage format 2022-03-25 22:13:52 -04:00
Andrew Lavery 40cdc6805e pods with containers that are not 'ready' are unhealthy 2022-03-25 21:36:22 -04:00
Pavan Sokke Nagaraj 3d7a255e32 update: add missing database collector code for func GetName() (#553) 2022-03-23 22:00:25 -04:00
Pavan Sokke Nagaraj 77a2475bd2 fix: return true when one of analyzers strict field is true (#552)
* fix: return true when one of analyzers is true

* fix: return true when one of analyzers is true

* update: add unit test

* update: log errors while processing analyzers

* fix: return parse error instead of logging

* fix: update err message

* fix: evaluate strict check err separately
2022-03-23 20:24:21 -04:00
Pavan Sokke Nagaraj 7bfb54360c map strict flag to analyze result (#551)
* add util functions

* rename func HasStrictAnalyzersFailed

* map strict flag to analyze err result
2022-03-22 20:47:50 -04:00
Diamon Wiggins ca0b3c31c1 Merge pull request #544 from replicatedhq/diamonwiggins/sc-44015/run-pod-service-account
Add ServiceAccountName parameter to Run collector
2022-03-03 10:46:31 -05:00
diamonwiggins 2b774e16d7 adding serviceaccountname parameter to run collector 2022-03-03 06:12:42 +00:00
Salah Al Saleh 9d41d4a7be Fix getting pod details for new support bundle formats (#543) 2022-03-01 15:55:47 -08:00
e248ab0f97 Fix strict flag mapping (#542)
* add func BoolOrDefaultFalse and Bool

* use strict.BoolOrDefaultFalse

* Update pkg/multitype/boolstring.go

Co-authored-by: Andrew Lavery <laverya@umich.edu>

* Update pkg/multitype/boolstring_test.go

Co-authored-by: Andrew Lavery <laverya@umich.edu>

* Update pkg/multitype/boolstring_test.go

Co-authored-by: Andrew Lavery <laverya@umich.edu>

* Update boolstring_test.go

* remove duplicate test

* Update pkg/multitype/boolstring_test.go

Co-authored-by: garcialuis <garcialuisdev@gmail.com>

Co-authored-by: Andrew Lavery <laverya@umich.edu>
Co-authored-by: garcialuis <garcialuisdev@gmail.com>
2022-02-24 13:31:51 -05:00
Pavan Sokke Nagaraj 942234da80 Add strict flag to Analyzers and ResultAnalyzers (#539)
* add strict flag to Analyzer/AnalyzerMeta

and regenerate schemas and controller-gen code

* map analyzer strict to result

* Update stdout for human and json format

* fix review comment

* update interactive result

* update interactive results

* Update types.go

* Update upload_results.go

* print strict when only true
2022-02-23 15:07:51 -05:00
Craig O'Donnell 0a2ed01a46 improvement: added --output flag for preflight and support bundle (#538)
* improvement: added --output flag for preflight and support bundle

* improvement: added datetime to preflight default file name
2022-02-17 17:28:28 -05:00
49 changed files with 2216 additions and 361 deletions
+24 -5
View File
@@ -4,7 +4,6 @@ import (
"fmt"
"io/ioutil"
"os"
"path"
"time"
"github.com/pkg/errors"
@@ -12,6 +11,7 @@ import (
"github.com/replicatedhq/termui/v3/widgets"
"github.com/replicatedhq/troubleshoot/cmd/util"
analyzerunner "github.com/replicatedhq/troubleshoot/pkg/analyze"
"github.com/replicatedhq/troubleshoot/pkg/convert"
)
var (
@@ -20,7 +20,7 @@ var (
isShowingSaved = false
)
func showInteractiveResults(preflightName string, analyzeResults []*analyzerunner.AnalyzeResult) error {
func showInteractiveResults(preflightName string, outputPath string, analyzeResults []*analyzerunner.AnalyzeResult) error {
if err := ui.Init(); err != nil {
return errors.Wrap(err, "failed to create terminal ui")
}
@@ -44,7 +44,7 @@ func showInteractiveResults(preflightName string, analyzeResults []*analyzerunne
return nil
}
case "s":
filename, err := save(preflightName, analyzeResults)
filename, err := save(preflightName, outputPath, analyzeResults)
if err != nil {
// show
} else {
@@ -139,6 +139,9 @@ func drawPreflightTable(analyzeResults []*analyzerunner.AnalyzeResult) {
for i, analyzeResult := range analyzeResults {
title := analyzeResult.Title
if analyzeResult.Strict {
title = title + fmt.Sprintf(" (Strict: %t)", analyzeResult.Strict)
}
if analyzeResult.IsPass {
title = fmt.Sprintf("✔ %s", title)
} else if analyzeResult.IsWarn {
@@ -217,8 +220,20 @@ func estimateNumberOfLines(text string, width int) int {
return lines
}
func save(preflightName string, analyzeResults []*analyzerunner.AnalyzeResult) (string, error) {
filename := path.Join(util.HomeDir(), fmt.Sprintf("%s-results.txt", preflightName))
func save(preflightName string, outputPath string, analyzeResults []*analyzerunner.AnalyzeResult) (string, error) {
filename := ""
if outputPath != "" {
// use override output path
overridePath, err := convert.ValidateOutputPath(outputPath)
if err != nil {
return "", errors.Wrap(err, "override output file path")
}
filename = overridePath
} else {
// use default output path
filename = fmt.Sprintf("%s-results-%s.txt", preflightName, time.Now().Format("2006-01-02T15_04_05"))
}
_, err := os.Stat(filename)
if err == nil {
os.Remove(filename)
@@ -243,6 +258,10 @@ func save(preflightName string, analyzeResults []*analyzerunner.AnalyzeResult) (
result = result + fmt.Sprintf("URI: %s\n", analyzeResult.URI)
}
if analyzeResult.Strict {
result = result + fmt.Sprintf("Strict: %t\n", analyzeResult.Strict)
}
result = result + "\n------------\n"
results = results + result
+1
View File
@@ -38,6 +38,7 @@ that a cluster meets the requirements to run an application.`,
cmd.Flags().String("selector", "", "selector (label query) to filter remote collection nodes on.")
cmd.Flags().String("since-time", "", "force pod logs collectors to return logs after a specific date (RFC3339)")
cmd.Flags().String("since", "", "force pod logs collectors to return logs newer than a relative duration like 5s, 2m, or 3h.")
cmd.Flags().StringP("output", "o", "", "specify the output file path for the preflight checks")
viper.SetEnvKeyReplacer(strings.NewReplacer("-", "_"))
+1 -1
View File
@@ -167,7 +167,7 @@ func runPreflights(v *viper.Viper, arg string) error {
if len(analyzeResults) == 0 {
return errors.New("no data has been collected")
}
return showInteractiveResults(preflightSpecName, analyzeResults)
return showInteractiveResults(preflightSpecName, v.GetString("output"), analyzeResults)
}
return showStdoutResults(v.GetString("format"), preflightSpecName, analyzeResults)
+12
View File
@@ -42,6 +42,7 @@ func showStdoutResultsJSON(preflightName string, analyzeResults []*analyzerunner
Title string `json:"title"`
Message string `json:"message"`
URI string `json:"uri,omitempty"`
Strict bool `json:"strict,omitempty"`
}
type Output struct {
Pass []ResultOutput `json:"pass,omitempty"`
@@ -62,6 +63,10 @@ func showStdoutResultsJSON(preflightName string, analyzeResults []*analyzerunner
URI: analyzeResult.URI,
}
if analyzeResult.Strict {
resultOutput.Strict = analyzeResult.Strict
}
if analyzeResult.IsPass {
output.Pass = append(output.Pass, resultOutput)
} else if analyzeResult.IsWarn {
@@ -91,6 +96,13 @@ func outputResult(analyzeResult *analyzerunner.AnalyzeResult) bool {
} else if analyzeResult.IsFail {
fmt.Printf(" --- FAIL: %s\n", analyzeResult.Title)
fmt.Printf(" --- %s\n", analyzeResult.Message)
}
if analyzeResult.Strict {
fmt.Printf(" --- Strict: %t\n", analyzeResult.Strict)
}
if analyzeResult.IsFail {
return true
}
return false
+1
View File
@@ -17,6 +17,7 @@ func uploadResults(uri string, analyzeResults []*analyzerunner.AnalyzeResult) er
}
for _, analyzeResult := range analyzeResults {
uploadPreflightResult := &preflight.UploadPreflightResult{
Strict: analyzeResult.Strict,
IsFail: analyzeResult.IsFail,
IsWarn: analyzeResult.IsWarn,
IsPass: analyzeResult.IsPass,
+1
View File
@@ -42,6 +42,7 @@ from a server that can be used to assist when troubleshooting a Kubernetes clust
cmd.Flags().Bool("collect-without-permissions", true, "always generate a support bundle, even if it some require additional permissions")
cmd.Flags().String("since-time", "", "force pod logs collectors to return logs after a specific date (RFC3339)")
cmd.Flags().String("since", "", "force pod logs collectors to return logs newer than a relative duration like 5s, 2m, or 3h.")
cmd.Flags().StringP("output", "o", "", "specify the output file path for the support bundle")
// hidden in favor of the `insecure-skip-tls-verify` flag
cmd.Flags().Bool("allow-insecure-connections", false, "when set, do not verify TLS certs when retrieving spec and reporting results")
+1
View File
@@ -181,6 +181,7 @@ func runTroubleshoot(v *viper.Viper, arg string) error {
Namespace: v.GetString("namespace"),
ProgressChan: progressChan,
SinceTime: sinceTime,
OutputPath: v.GetString("output"),
Redact: v.GetBool("redact"),
FromCLI: true,
}
@@ -81,6 +81,8 @@ spec:
type: object
type: object
type: array
strict:
type: BoolString
required:
- namespace
- outcomes
@@ -127,6 +129,8 @@ spec:
type: object
type: object
type: array
strict:
type: BoolString
required:
- outcomes
type: object
@@ -168,6 +172,8 @@ spec:
type: object
type: object
type: array
strict:
type: BoolString
required:
- outcomes
type: object
@@ -215,6 +221,8 @@ spec:
type: object
type: object
type: array
strict:
type: BoolString
required:
- configMapName
- namespace
@@ -258,6 +266,8 @@ spec:
type: object
type: object
type: array
strict:
type: BoolString
required:
- outcomes
type: object
@@ -301,6 +311,8 @@ spec:
type: object
type: object
type: array
strict:
type: BoolString
required:
- customResourceDefinitionName
- outcomes
@@ -351,6 +363,8 @@ spec:
type: object
type: object
type: array
strict:
type: BoolString
required:
- name
- outcomes
@@ -393,6 +407,8 @@ spec:
type: object
type: object
type: array
strict:
type: BoolString
required:
- outcomes
type: object
@@ -436,6 +452,8 @@ spec:
type: array
registryName:
type: string
strict:
type: BoolString
required:
- outcomes
- registryName
@@ -482,6 +500,8 @@ spec:
type: object
type: object
type: array
strict:
type: BoolString
required:
- ingressName
- namespace
@@ -533,6 +553,8 @@ spec:
type: object
type: object
type: array
strict:
type: BoolString
required:
- name
- outcomes
@@ -579,6 +601,8 @@ spec:
type: object
type: object
type: array
strict:
type: BoolString
required:
- namespace
- outcomes
@@ -625,6 +649,8 @@ spec:
type: object
type: object
type: array
strict:
type: BoolString
required:
- collectorName
- outcomes
@@ -693,6 +719,8 @@ spec:
type: object
type: object
type: array
strict:
type: BoolString
required:
- outcomes
type: object
@@ -738,6 +766,8 @@ spec:
type: object
type: object
type: array
strict:
type: BoolString
required:
- collectorName
- outcomes
@@ -784,6 +814,8 @@ spec:
type: object
type: object
type: array
strict:
type: BoolString
required:
- collectorName
- outcomes
@@ -828,6 +860,8 @@ spec:
type: object
type: object
type: array
strict:
type: BoolString
required:
- collectorName
- outcomes
@@ -882,6 +916,8 @@ spec:
items:
type: string
type: array
strict:
type: BoolString
required:
- name
- outcomes
@@ -931,6 +967,8 @@ spec:
type: array
secretName:
type: string
strict:
type: BoolString
required:
- namespace
- outcomes
@@ -982,6 +1020,8 @@ spec:
type: object
type: object
type: array
strict:
type: BoolString
required:
- name
- outcomes
@@ -1026,6 +1066,8 @@ spec:
type: array
storageClassName:
type: string
strict:
type: BoolString
required:
- outcomes
- storageClassName
@@ -1068,6 +1110,8 @@ spec:
type: object
type: object
type: array
strict:
type: BoolString
required:
- outcomes
type: object
@@ -1119,6 +1163,8 @@ spec:
type: string
regexGroups:
type: string
strict:
type: BoolString
required:
- outcomes
type: object
@@ -1130,6 +1176,8 @@ spec:
type: BoolString
reportFileGlob:
type: string
strict:
type: BoolString
required:
- reportFileGlob
type: object
@@ -82,6 +82,8 @@ spec:
type: object
type: object
type: array
strict:
type: BoolString
required:
- includeUnmountedPartitions
- minimumAcceptableSize
@@ -127,6 +129,8 @@ spec:
type: object
type: object
type: array
strict:
type: BoolString
required:
- outcomes
type: object
@@ -168,6 +172,8 @@ spec:
type: object
type: object
type: array
strict:
type: BoolString
required:
- outcomes
type: object
@@ -211,6 +217,8 @@ spec:
type: object
type: object
type: array
strict:
type: BoolString
required:
- outcomes
type: object
@@ -254,6 +262,8 @@ spec:
type: object
type: object
type: array
strict:
type: BoolString
required:
- outcomes
type: object
@@ -295,6 +305,8 @@ spec:
type: object
type: object
type: array
strict:
type: BoolString
required:
- outcomes
type: object
@@ -336,6 +348,8 @@ spec:
type: object
type: object
type: array
strict:
type: BoolString
required:
- outcomes
type: object
@@ -379,6 +393,8 @@ spec:
type: object
type: object
type: array
strict:
type: BoolString
required:
- outcomes
type: object
@@ -422,6 +438,8 @@ spec:
type: object
type: object
type: array
strict:
type: BoolString
required:
- outcomes
type: object
@@ -463,6 +481,8 @@ spec:
type: object
type: object
type: array
strict:
type: BoolString
required:
- outcomes
type: object
@@ -504,6 +524,8 @@ spec:
type: object
type: object
type: array
strict:
type: BoolString
required:
- outcomes
type: object
@@ -545,6 +567,8 @@ spec:
type: object
type: object
type: array
strict:
type: BoolString
required:
- outcomes
type: object
@@ -588,6 +612,8 @@ spec:
type: object
type: object
type: array
strict:
type: BoolString
required:
- outcomes
type: object
@@ -631,6 +657,8 @@ spec:
type: object
type: object
type: array
strict:
type: BoolString
required:
- outcomes
type: object
@@ -674,6 +702,8 @@ spec:
type: object
type: object
type: array
strict:
type: BoolString
required:
- outcomes
type: object
@@ -717,6 +747,8 @@ spec:
type: object
type: object
type: array
strict:
type: BoolString
required:
- outcomes
type: object
@@ -758,6 +790,8 @@ spec:
type: object
type: object
type: array
strict:
type: BoolString
required:
- outcomes
type: object
@@ -82,6 +82,8 @@ spec:
type: object
type: object
type: array
strict:
type: BoolString
required:
- includeUnmountedPartitions
- minimumAcceptableSize
@@ -127,6 +129,8 @@ spec:
type: object
type: object
type: array
strict:
type: BoolString
required:
- outcomes
type: object
@@ -168,6 +172,8 @@ spec:
type: object
type: object
type: array
strict:
type: BoolString
required:
- outcomes
type: object
@@ -211,6 +217,8 @@ spec:
type: object
type: object
type: array
strict:
type: BoolString
required:
- outcomes
type: object
@@ -254,6 +262,8 @@ spec:
type: object
type: object
type: array
strict:
type: BoolString
required:
- outcomes
type: object
@@ -295,6 +305,8 @@ spec:
type: object
type: object
type: array
strict:
type: BoolString
required:
- outcomes
type: object
@@ -336,6 +348,8 @@ spec:
type: object
type: object
type: array
strict:
type: BoolString
required:
- outcomes
type: object
@@ -379,6 +393,8 @@ spec:
type: object
type: object
type: array
strict:
type: BoolString
required:
- outcomes
type: object
@@ -422,6 +438,8 @@ spec:
type: object
type: object
type: array
strict:
type: BoolString
required:
- outcomes
type: object
@@ -463,6 +481,8 @@ spec:
type: object
type: object
type: array
strict:
type: BoolString
required:
- outcomes
type: object
@@ -504,6 +524,8 @@ spec:
type: object
type: object
type: array
strict:
type: BoolString
required:
- outcomes
type: object
@@ -545,6 +567,8 @@ spec:
type: object
type: object
type: array
strict:
type: BoolString
required:
- outcomes
type: object
@@ -588,6 +612,8 @@ spec:
type: object
type: object
type: array
strict:
type: BoolString
required:
- outcomes
type: object
@@ -631,6 +657,8 @@ spec:
type: object
type: object
type: array
strict:
type: BoolString
required:
- outcomes
type: object
@@ -674,6 +702,8 @@ spec:
type: object
type: object
type: array
strict:
type: BoolString
required:
- outcomes
type: object
@@ -717,6 +747,8 @@ spec:
type: object
type: object
type: array
strict:
type: BoolString
required:
- outcomes
type: object
@@ -758,6 +790,8 @@ spec:
type: object
type: object
type: array
strict:
type: BoolString
required:
- outcomes
type: object
@@ -81,6 +81,8 @@ spec:
type: object
type: object
type: array
strict:
type: BoolString
required:
- namespace
- outcomes
@@ -127,6 +129,8 @@ spec:
type: object
type: object
type: array
strict:
type: BoolString
required:
- outcomes
type: object
@@ -168,6 +172,8 @@ spec:
type: object
type: object
type: array
strict:
type: BoolString
required:
- outcomes
type: object
@@ -215,6 +221,8 @@ spec:
type: object
type: object
type: array
strict:
type: BoolString
required:
- configMapName
- namespace
@@ -258,6 +266,8 @@ spec:
type: object
type: object
type: array
strict:
type: BoolString
required:
- outcomes
type: object
@@ -301,6 +311,8 @@ spec:
type: object
type: object
type: array
strict:
type: BoolString
required:
- customResourceDefinitionName
- outcomes
@@ -351,6 +363,8 @@ spec:
type: object
type: object
type: array
strict:
type: BoolString
required:
- name
- outcomes
@@ -393,6 +407,8 @@ spec:
type: object
type: object
type: array
strict:
type: BoolString
required:
- outcomes
type: object
@@ -436,6 +452,8 @@ spec:
type: array
registryName:
type: string
strict:
type: BoolString
required:
- outcomes
- registryName
@@ -482,6 +500,8 @@ spec:
type: object
type: object
type: array
strict:
type: BoolString
required:
- ingressName
- namespace
@@ -533,6 +553,8 @@ spec:
type: object
type: object
type: array
strict:
type: BoolString
required:
- name
- outcomes
@@ -579,6 +601,8 @@ spec:
type: object
type: object
type: array
strict:
type: BoolString
required:
- namespace
- outcomes
@@ -625,6 +649,8 @@ spec:
type: object
type: object
type: array
strict:
type: BoolString
required:
- collectorName
- outcomes
@@ -693,6 +719,8 @@ spec:
type: object
type: object
type: array
strict:
type: BoolString
required:
- outcomes
type: object
@@ -738,6 +766,8 @@ spec:
type: object
type: object
type: array
strict:
type: BoolString
required:
- collectorName
- outcomes
@@ -784,6 +814,8 @@ spec:
type: object
type: object
type: array
strict:
type: BoolString
required:
- collectorName
- outcomes
@@ -828,6 +860,8 @@ spec:
type: object
type: object
type: array
strict:
type: BoolString
required:
- collectorName
- outcomes
@@ -882,6 +916,8 @@ spec:
items:
type: string
type: array
strict:
type: BoolString
required:
- name
- outcomes
@@ -931,6 +967,8 @@ spec:
type: array
secretName:
type: string
strict:
type: BoolString
required:
- namespace
- outcomes
@@ -982,6 +1020,8 @@ spec:
type: object
type: object
type: array
strict:
type: BoolString
required:
- name
- outcomes
@@ -1026,6 +1066,8 @@ spec:
type: array
storageClassName:
type: string
strict:
type: BoolString
required:
- outcomes
- storageClassName
@@ -1068,6 +1110,8 @@ spec:
type: object
type: object
type: array
strict:
type: BoolString
required:
- outcomes
type: object
@@ -1119,6 +1163,8 @@ spec:
type: string
regexGroups:
type: string
strict:
type: BoolString
required:
- outcomes
type: object
@@ -1130,6 +1176,8 @@ spec:
type: BoolString
reportFileGlob:
type: string
strict:
type: BoolString
required:
- reportFileGlob
type: object
@@ -112,6 +112,8 @@ spec:
type: object
type: object
type: array
strict:
type: BoolString
required:
- namespace
- outcomes
@@ -158,6 +160,8 @@ spec:
type: object
type: object
type: array
strict:
type: BoolString
required:
- outcomes
type: object
@@ -199,6 +203,8 @@ spec:
type: object
type: object
type: array
strict:
type: BoolString
required:
- outcomes
type: object
@@ -246,6 +252,8 @@ spec:
type: object
type: object
type: array
strict:
type: BoolString
required:
- configMapName
- namespace
@@ -289,6 +297,8 @@ spec:
type: object
type: object
type: array
strict:
type: BoolString
required:
- outcomes
type: object
@@ -332,6 +342,8 @@ spec:
type: object
type: object
type: array
strict:
type: BoolString
required:
- customResourceDefinitionName
- outcomes
@@ -382,6 +394,8 @@ spec:
type: object
type: object
type: array
strict:
type: BoolString
required:
- name
- outcomes
@@ -424,6 +438,8 @@ spec:
type: object
type: object
type: array
strict:
type: BoolString
required:
- outcomes
type: object
@@ -467,6 +483,8 @@ spec:
type: array
registryName:
type: string
strict:
type: BoolString
required:
- outcomes
- registryName
@@ -513,6 +531,8 @@ spec:
type: object
type: object
type: array
strict:
type: BoolString
required:
- ingressName
- namespace
@@ -564,6 +584,8 @@ spec:
type: object
type: object
type: array
strict:
type: BoolString
required:
- name
- outcomes
@@ -610,6 +632,8 @@ spec:
type: object
type: object
type: array
strict:
type: BoolString
required:
- namespace
- outcomes
@@ -656,6 +680,8 @@ spec:
type: object
type: object
type: array
strict:
type: BoolString
required:
- collectorName
- outcomes
@@ -724,6 +750,8 @@ spec:
type: object
type: object
type: array
strict:
type: BoolString
required:
- outcomes
type: object
@@ -769,6 +797,8 @@ spec:
type: object
type: object
type: array
strict:
type: BoolString
required:
- collectorName
- outcomes
@@ -815,6 +845,8 @@ spec:
type: object
type: object
type: array
strict:
type: BoolString
required:
- collectorName
- outcomes
@@ -859,6 +891,8 @@ spec:
type: object
type: object
type: array
strict:
type: BoolString
required:
- collectorName
- outcomes
@@ -913,6 +947,8 @@ spec:
items:
type: string
type: array
strict:
type: BoolString
required:
- name
- outcomes
@@ -962,6 +998,8 @@ spec:
type: array
secretName:
type: string
strict:
type: BoolString
required:
- namespace
- outcomes
@@ -1013,6 +1051,8 @@ spec:
type: object
type: object
type: array
strict:
type: BoolString
required:
- name
- outcomes
@@ -1057,6 +1097,8 @@ spec:
type: array
storageClassName:
type: string
strict:
type: BoolString
required:
- outcomes
- storageClassName
@@ -1099,6 +1141,8 @@ spec:
type: object
type: object
type: array
strict:
type: BoolString
required:
- outcomes
type: object
@@ -1150,6 +1194,8 @@ spec:
type: string
regexGroups:
type: string
strict:
type: BoolString
required:
- outcomes
type: object
@@ -1161,6 +1207,8 @@ spec:
type: BoolString
reportFileGlob:
type: string
strict:
type: BoolString
required:
- reportFileGlob
type: object
@@ -0,0 +1,17 @@
apiVersion: troubleshoot.sh/v1beta2
kind: SupportBundle
metadata:
name: mysql
spec:
collectors:
- mysql:
collectorName: mysql
uri: 'root:my-secret-pw@tcp(localhost:3306)/mysql'
parameters:
- character_set_server
- collation_server
- init_connect
- innodb_file_format
- innodb_large_prefix
- innodb_strict_mode
- log_bin_trust_function_creators
@@ -75,7 +75,10 @@ func (c *Clientset) Tracker() testing.ObjectTracker {
return c.tracker
}
var _ clientset.Interface = &Clientset{}
var (
_ clientset.Interface = &Clientset{}
_ testing.FakeClient = &Clientset{}
)
// TroubleshootV1beta1 retrieves the TroubleshootV1beta1Client
func (c *Clientset) TroubleshootV1beta1() troubleshootv1beta1.TroubleshootV1beta1Interface {
@@ -29,7 +29,7 @@ import (
var scheme = runtime.NewScheme()
var codecs = serializer.NewCodecFactory(scheme)
var parameterCodec = runtime.NewParameterCodec(scheme)
var localSchemeBuilder = runtime.SchemeBuilder{
troubleshootv1beta1.AddToScheme,
troubleshootv1beta2.AddToScheme,
+53 -4
View File
@@ -14,6 +14,7 @@ type AnalyzeResult struct {
IsPass bool
IsFail bool
IsWarn bool
Strict bool
Title string
Message string
@@ -27,7 +28,11 @@ type AnalyzeResult struct {
type getCollectedFileContents func(string) ([]byte, error)
type getChildCollectedFileContents func(string) (map[string][]byte, error)
func isExcluded(excludeVal multitype.BoolOrString) (bool, error) {
func isExcluded(excludeVal *multitype.BoolOrString) (bool, error) {
if excludeVal == nil {
return false, nil
}
if excludeVal.Type == multitype.Bool {
return excludeVal.BoolVal, nil
}
@@ -94,6 +99,7 @@ func Analyze(analyzer *troubleshootv1beta2.Analyze, getFile getCollectedFileCont
if err != nil {
return nil, err
}
result.Strict = analyzer.ClusterVersion.Strict.BoolOrDefaultFalse()
return []*AnalyzeResult{result}, nil
}
if analyzer.StorageClass != nil {
@@ -108,6 +114,7 @@ func Analyze(analyzer *troubleshootv1beta2.Analyze, getFile getCollectedFileCont
if err != nil {
return nil, err
}
result.Strict = analyzer.StorageClass.Strict.BoolOrDefaultFalse()
return []*AnalyzeResult{result}, nil
}
if analyzer.CustomResourceDefinition != nil {
@@ -122,6 +129,7 @@ func Analyze(analyzer *troubleshootv1beta2.Analyze, getFile getCollectedFileCont
if err != nil {
return nil, err
}
result.Strict = analyzer.CustomResourceDefinition.Strict.BoolOrDefaultFalse()
return []*AnalyzeResult{result}, nil
}
if analyzer.Ingress != nil {
@@ -136,6 +144,7 @@ func Analyze(analyzer *troubleshootv1beta2.Analyze, getFile getCollectedFileCont
if err != nil {
return nil, err
}
result.Strict = analyzer.Ingress.Strict.BoolOrDefaultFalse()
return []*AnalyzeResult{result}, nil
}
if analyzer.Secret != nil {
@@ -150,6 +159,7 @@ func Analyze(analyzer *troubleshootv1beta2.Analyze, getFile getCollectedFileCont
if err != nil {
return nil, err
}
result.Strict = analyzer.Secret.Strict.BoolOrDefaultFalse()
return []*AnalyzeResult{result}, nil
}
if analyzer.ConfigMap != nil {
@@ -164,6 +174,7 @@ func Analyze(analyzer *troubleshootv1beta2.Analyze, getFile getCollectedFileCont
if err != nil {
return nil, err
}
result.Strict = analyzer.ConfigMap.Strict.BoolOrDefaultFalse()
return []*AnalyzeResult{result}, nil
}
if analyzer.ImagePullSecret != nil {
@@ -178,6 +189,7 @@ func Analyze(analyzer *troubleshootv1beta2.Analyze, getFile getCollectedFileCont
if err != nil {
return nil, err
}
result.Strict = analyzer.ImagePullSecret.Strict.BoolOrDefaultFalse()
return []*AnalyzeResult{result}, nil
}
if analyzer.DeploymentStatus != nil {
@@ -192,6 +204,9 @@ func Analyze(analyzer *troubleshootv1beta2.Analyze, getFile getCollectedFileCont
if err != nil {
return nil, err
}
for i := range results {
results[i].Strict = analyzer.DeploymentStatus.Strict.BoolOrDefaultFalse()
}
return results, nil
}
if analyzer.StatefulsetStatus != nil {
@@ -206,6 +221,9 @@ func Analyze(analyzer *troubleshootv1beta2.Analyze, getFile getCollectedFileCont
if err != nil {
return nil, err
}
for i := range results {
results[i].Strict = analyzer.StatefulsetStatus.Strict.BoolOrDefaultFalse()
}
return results, nil
}
if analyzer.JobStatus != nil {
@@ -220,6 +238,9 @@ func Analyze(analyzer *troubleshootv1beta2.Analyze, getFile getCollectedFileCont
if err != nil {
return nil, err
}
for i := range results {
results[i].Strict = analyzer.JobStatus.Strict.BoolOrDefaultFalse()
}
return results, nil
}
if analyzer.ReplicaSetStatus != nil {
@@ -234,6 +255,9 @@ func Analyze(analyzer *troubleshootv1beta2.Analyze, getFile getCollectedFileCont
if err != nil {
return nil, err
}
for i := range results {
results[i].Strict = analyzer.ReplicaSetStatus.Strict.BoolOrDefaultFalse()
}
return results, nil
}
if analyzer.ClusterPodStatuses != nil {
@@ -248,6 +272,9 @@ func Analyze(analyzer *troubleshootv1beta2.Analyze, getFile getCollectedFileCont
if err != nil {
return nil, err
}
for i := range results {
results[i].Strict = analyzer.ClusterPodStatuses.Strict.BoolOrDefaultFalse()
}
return results, nil
}
if analyzer.ContainerRuntime != nil {
@@ -262,6 +289,7 @@ func Analyze(analyzer *troubleshootv1beta2.Analyze, getFile getCollectedFileCont
if err != nil {
return nil, err
}
result.Strict = analyzer.ContainerRuntime.Strict.BoolOrDefaultFalse()
return []*AnalyzeResult{result}, nil
}
if analyzer.Distribution != nil {
@@ -276,6 +304,7 @@ func Analyze(analyzer *troubleshootv1beta2.Analyze, getFile getCollectedFileCont
if err != nil {
return nil, err
}
result.Strict = analyzer.Distribution.Strict.BoolOrDefaultFalse()
return []*AnalyzeResult{result}, nil
}
if analyzer.NodeResources != nil {
@@ -290,6 +319,7 @@ func Analyze(analyzer *troubleshootv1beta2.Analyze, getFile getCollectedFileCont
if err != nil {
return nil, err
}
result.Strict = analyzer.NodeResources.Strict.BoolOrDefaultFalse()
return []*AnalyzeResult{result}, nil
}
if analyzer.TextAnalyze != nil {
@@ -300,11 +330,14 @@ func Analyze(analyzer *troubleshootv1beta2.Analyze, getFile getCollectedFileCont
if isExcluded {
return nil, nil
}
multiResult, err := analyzeTextAnalyze(analyzer.TextAnalyze, findFiles)
results, err := analyzeTextAnalyze(analyzer.TextAnalyze, findFiles)
if err != nil {
return nil, err
}
return multiResult, nil
for i := range results {
results[i].Strict = analyzer.TextAnalyze.Strict.BoolOrDefaultFalse()
}
return results, nil
}
if analyzer.Postgres != nil {
isExcluded, err := isExcluded(analyzer.Postgres.Exclude)
@@ -318,6 +351,7 @@ func Analyze(analyzer *troubleshootv1beta2.Analyze, getFile getCollectedFileCont
if err != nil {
return nil, err
}
result.Strict = analyzer.Postgres.Strict.BoolOrDefaultFalse()
return []*AnalyzeResult{result}, nil
}
if analyzer.Mysql != nil {
@@ -332,6 +366,7 @@ func Analyze(analyzer *troubleshootv1beta2.Analyze, getFile getCollectedFileCont
if err != nil {
return nil, err
}
result.Strict = analyzer.Mysql.Strict.BoolOrDefaultFalse()
return []*AnalyzeResult{result}, nil
}
if analyzer.Redis != nil {
@@ -346,6 +381,7 @@ func Analyze(analyzer *troubleshootv1beta2.Analyze, getFile getCollectedFileCont
if err != nil {
return nil, err
}
result.Strict = analyzer.Redis.Strict.BoolOrDefaultFalse()
return []*AnalyzeResult{result}, nil
}
if analyzer.CephStatus != nil {
@@ -360,6 +396,7 @@ func Analyze(analyzer *troubleshootv1beta2.Analyze, getFile getCollectedFileCont
if err != nil {
return nil, err
}
result.Strict = analyzer.CephStatus.Strict.BoolOrDefaultFalse()
return []*AnalyzeResult{result}, nil
}
if analyzer.Longhorn != nil {
@@ -370,7 +407,14 @@ func Analyze(analyzer *troubleshootv1beta2.Analyze, getFile getCollectedFileCont
if isExcluded {
return nil, nil
}
return longhorn(analyzer.Longhorn, getFile, findFiles)
results, err := longhorn(analyzer.Longhorn, getFile, findFiles)
if err != nil {
return nil, err
}
for i := range results {
results[i].Strict = analyzer.Longhorn.Strict.BoolOrDefaultFalse()
}
return results, nil
}
if analyzer.RegistryImages != nil {
@@ -385,6 +429,7 @@ func Analyze(analyzer *troubleshootv1beta2.Analyze, getFile getCollectedFileCont
if err != nil {
return nil, err
}
result.Strict = analyzer.RegistryImages.Strict.BoolOrDefaultFalse()
return []*AnalyzeResult{result}, nil
}
@@ -400,6 +445,9 @@ func Analyze(analyzer *troubleshootv1beta2.Analyze, getFile getCollectedFileCont
if err != nil {
return nil, err
}
for i := range results {
results[i].Strict = analyzer.WeaveReport.Strict.BoolOrDefaultFalse()
}
return results, nil
}
@@ -418,6 +466,7 @@ func Analyze(analyzer *troubleshootv1beta2.Analyze, getFile getCollectedFileCont
if result == nil {
return []*AnalyzeResult{}, nil
}
result.Strict = analyzer.Sysctl.Strict.BoolOrDefaultFalse()
return []*AnalyzeResult{result}, nil
}
+8 -3
View File
@@ -22,7 +22,7 @@ func clusterPodStatuses(analyzer *troubleshootv1beta2.ClusterPodStatuses, getChi
var pods []corev1.Pod
for fileName, fileContent := range collected {
podsNs := strings.TrimSuffix(fileName, ".json")
podsNs := strings.TrimSuffix(filepath.Base(fileName), ".json")
include := len(analyzer.Namespaces) == 0
for _, ns := range analyzer.Namespaces {
if ns == podsNs {
@@ -33,9 +33,14 @@ func clusterPodStatuses(analyzer *troubleshootv1beta2.ClusterPodStatuses, getChi
if include {
var nsPods corev1.PodList
if err := json.Unmarshal(fileContent, &nsPods); err != nil {
return nil, errors.Wrapf(err, "failed to unmarshal pods list for namespace %s", podsNs)
var nsPodsArr []corev1.Pod
if err := json.Unmarshal(fileContent, &nsPodsArr); err != nil {
return nil, errors.Wrapf(err, "failed to unmarshal pods list for namespace %s", podsNs)
}
pods = append(pods, nsPodsArr...)
} else {
pods = append(pods, nsPods.Items...)
}
pods = append(pods, nsPods.Items...)
}
}
@@ -116,8 +116,8 @@ type DatabaseAnalyze struct {
}
type AnalyzeMeta struct {
CheckName string `json:"checkName,omitempty" yaml:"checkName,omitempty"`
Exclude multitype.BoolOrString `json:"exclude,omitempty" yaml:"exclude,omitempty"`
CheckName string `json:"checkName,omitempty" yaml:"checkName,omitempty"`
Exclude *multitype.BoolOrString `json:"exclude,omitempty" yaml:"exclude,omitempty"`
}
type Analyze struct {
@@ -11,7 +11,7 @@ import (
type CollectorMeta struct {
CollectorName string `json:"collectorName,omitempty" yaml:"collectorName,omitempty"`
// +optional
Exclude multitype.BoolOrString `json:"exclude,omitempty" yaml:"exclude,omitempty"`
Exclude *multitype.BoolOrString `json:"exclude,omitempty" yaml:"exclude,omitempty"`
}
type ClusterInfo struct {
@@ -22,6 +22,7 @@ limitations under the License.
package v1beta1
import (
"github.com/replicatedhq/troubleshoot/pkg/multitype"
runtime "k8s.io/apimachinery/pkg/runtime"
)
@@ -143,7 +144,11 @@ func (in *Analyze) DeepCopy() *Analyze {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *AnalyzeMeta) DeepCopyInto(out *AnalyzeMeta) {
*out = *in
out.Exclude = in.Exclude
if in.Exclude != nil {
in, out := &in.Exclude, &out.Exclude
*out = new(multitype.BoolOrString)
**out = **in
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AnalyzeMeta.
@@ -159,7 +164,7 @@ func (in *AnalyzeMeta) DeepCopy() *AnalyzeMeta {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *AnalyzeSecret) DeepCopyInto(out *AnalyzeSecret) {
*out = *in
out.AnalyzeMeta = in.AnalyzeMeta
in.AnalyzeMeta.DeepCopyInto(&out.AnalyzeMeta)
if in.Outcomes != nil {
in, out := &in.Outcomes, &out.Outcomes
*out = make([]*Outcome, len(*in))
@@ -286,7 +291,7 @@ func (in *AnalyzerStatus) DeepCopy() *AnalyzerStatus {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ClusterInfo) DeepCopyInto(out *ClusterInfo) {
*out = *in
out.CollectorMeta = in.CollectorMeta
in.CollectorMeta.DeepCopyInto(&out.CollectorMeta)
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterInfo.
@@ -302,7 +307,7 @@ func (in *ClusterInfo) DeepCopy() *ClusterInfo {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ClusterResources) DeepCopyInto(out *ClusterResources) {
*out = *in
out.CollectorMeta = in.CollectorMeta
in.CollectorMeta.DeepCopyInto(&out.CollectorMeta)
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterResources.
@@ -318,7 +323,7 @@ func (in *ClusterResources) DeepCopy() *ClusterResources {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ClusterVersion) DeepCopyInto(out *ClusterVersion) {
*out = *in
out.AnalyzeMeta = in.AnalyzeMeta
in.AnalyzeMeta.DeepCopyInto(&out.AnalyzeMeta)
if in.Outcomes != nil {
in, out := &in.Outcomes, &out.Outcomes
*out = make([]*Outcome, len(*in))
@@ -348,17 +353,17 @@ func (in *Collect) DeepCopyInto(out *Collect) {
if in.ClusterInfo != nil {
in, out := &in.ClusterInfo, &out.ClusterInfo
*out = new(ClusterInfo)
**out = **in
(*in).DeepCopyInto(*out)
}
if in.ClusterResources != nil {
in, out := &in.ClusterResources, &out.ClusterResources
*out = new(ClusterResources)
**out = **in
(*in).DeepCopyInto(*out)
}
if in.Secret != nil {
in, out := &in.Secret, &out.Secret
*out = new(Secret)
**out = **in
(*in).DeepCopyInto(*out)
}
if in.Logs != nil {
in, out := &in.Logs, &out.Logs
@@ -378,7 +383,7 @@ func (in *Collect) DeepCopyInto(out *Collect) {
if in.Data != nil {
in, out := &in.Data, &out.Data
*out = new(Data)
**out = **in
(*in).DeepCopyInto(*out)
}
if in.Copy != nil {
in, out := &in.Copy, &out.Copy
@@ -393,17 +398,17 @@ func (in *Collect) DeepCopyInto(out *Collect) {
if in.Postgres != nil {
in, out := &in.Postgres, &out.Postgres
*out = new(Database)
**out = **in
(*in).DeepCopyInto(*out)
}
if in.Mysql != nil {
in, out := &in.Mysql, &out.Mysql
*out = new(Database)
**out = **in
(*in).DeepCopyInto(*out)
}
if in.Redis != nil {
in, out := &in.Redis, &out.Redis
*out = new(Database)
**out = **in
(*in).DeepCopyInto(*out)
}
}
@@ -479,7 +484,11 @@ func (in *CollectorList) DeepCopyObject() runtime.Object {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *CollectorMeta) DeepCopyInto(out *CollectorMeta) {
*out = *in
out.Exclude = in.Exclude
if in.Exclude != nil {
in, out := &in.Exclude, &out.Exclude
*out = new(multitype.BoolOrString)
**out = **in
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CollectorMeta.
@@ -547,7 +556,7 @@ func (in *CollectorStatus) DeepCopy() *CollectorStatus {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ContainerRuntime) DeepCopyInto(out *ContainerRuntime) {
*out = *in
out.AnalyzeMeta = in.AnalyzeMeta
in.AnalyzeMeta.DeepCopyInto(&out.AnalyzeMeta)
if in.Outcomes != nil {
in, out := &in.Outcomes, &out.Outcomes
*out = make([]*Outcome, len(*in))
@@ -574,7 +583,7 @@ func (in *ContainerRuntime) DeepCopy() *ContainerRuntime {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Copy) DeepCopyInto(out *Copy) {
*out = *in
out.CollectorMeta = in.CollectorMeta
in.CollectorMeta.DeepCopyInto(&out.CollectorMeta)
if in.Selector != nil {
in, out := &in.Selector, &out.Selector
*out = make([]string, len(*in))
@@ -595,7 +604,7 @@ func (in *Copy) DeepCopy() *Copy {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *CustomResourceDefinition) DeepCopyInto(out *CustomResourceDefinition) {
*out = *in
out.AnalyzeMeta = in.AnalyzeMeta
in.AnalyzeMeta.DeepCopyInto(&out.AnalyzeMeta)
if in.Outcomes != nil {
in, out := &in.Outcomes, &out.Outcomes
*out = make([]*Outcome, len(*in))
@@ -622,7 +631,7 @@ func (in *CustomResourceDefinition) DeepCopy() *CustomResourceDefinition {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Data) DeepCopyInto(out *Data) {
*out = *in
out.CollectorMeta = in.CollectorMeta
in.CollectorMeta.DeepCopyInto(&out.CollectorMeta)
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Data.
@@ -638,7 +647,7 @@ func (in *Data) DeepCopy() *Data {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Database) DeepCopyInto(out *Database) {
*out = *in
out.CollectorMeta = in.CollectorMeta
in.CollectorMeta.DeepCopyInto(&out.CollectorMeta)
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Database.
@@ -654,7 +663,7 @@ func (in *Database) DeepCopy() *Database {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *DatabaseAnalyze) DeepCopyInto(out *DatabaseAnalyze) {
*out = *in
out.AnalyzeMeta = in.AnalyzeMeta
in.AnalyzeMeta.DeepCopyInto(&out.AnalyzeMeta)
if in.Outcomes != nil {
in, out := &in.Outcomes, &out.Outcomes
*out = make([]*Outcome, len(*in))
@@ -681,7 +690,7 @@ func (in *DatabaseAnalyze) DeepCopy() *DatabaseAnalyze {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *DeploymentStatus) DeepCopyInto(out *DeploymentStatus) {
*out = *in
out.AnalyzeMeta = in.AnalyzeMeta
in.AnalyzeMeta.DeepCopyInto(&out.AnalyzeMeta)
if in.Outcomes != nil {
in, out := &in.Outcomes, &out.Outcomes
*out = make([]*Outcome, len(*in))
@@ -708,7 +717,7 @@ func (in *DeploymentStatus) DeepCopy() *DeploymentStatus {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Distribution) DeepCopyInto(out *Distribution) {
*out = *in
out.AnalyzeMeta = in.AnalyzeMeta
in.AnalyzeMeta.DeepCopyInto(&out.AnalyzeMeta)
if in.Outcomes != nil {
in, out := &in.Outcomes, &out.Outcomes
*out = make([]*Outcome, len(*in))
@@ -735,7 +744,7 @@ func (in *Distribution) DeepCopy() *Distribution {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Exec) DeepCopyInto(out *Exec) {
*out = *in
out.CollectorMeta = in.CollectorMeta
in.CollectorMeta.DeepCopyInto(&out.CollectorMeta)
if in.Selector != nil {
in, out := &in.Selector, &out.Selector
*out = make([]string, len(*in))
@@ -808,7 +817,7 @@ func (in *Get) DeepCopy() *Get {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *HTTP) DeepCopyInto(out *HTTP) {
*out = *in
out.CollectorMeta = in.CollectorMeta
in.CollectorMeta.DeepCopyInto(&out.CollectorMeta)
if in.Get != nil {
in, out := &in.Get, &out.Get
*out = new(Get)
@@ -839,7 +848,7 @@ func (in *HTTP) DeepCopy() *HTTP {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ImagePullSecret) DeepCopyInto(out *ImagePullSecret) {
*out = *in
out.AnalyzeMeta = in.AnalyzeMeta
in.AnalyzeMeta.DeepCopyInto(&out.AnalyzeMeta)
if in.Outcomes != nil {
in, out := &in.Outcomes, &out.Outcomes
*out = make([]*Outcome, len(*in))
@@ -888,7 +897,7 @@ func (in *ImagePullSecrets) DeepCopy() *ImagePullSecrets {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Ingress) DeepCopyInto(out *Ingress) {
*out = *in
out.AnalyzeMeta = in.AnalyzeMeta
in.AnalyzeMeta.DeepCopyInto(&out.AnalyzeMeta)
if in.Outcomes != nil {
in, out := &in.Outcomes, &out.Outcomes
*out = make([]*Outcome, len(*in))
@@ -930,7 +939,7 @@ func (in *LogLimits) DeepCopy() *LogLimits {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Logs) DeepCopyInto(out *Logs) {
*out = *in
out.CollectorMeta = in.CollectorMeta
in.CollectorMeta.DeepCopyInto(&out.CollectorMeta)
if in.Selector != nil {
in, out := &in.Selector, &out.Selector
*out = make([]string, len(*in))
@@ -1003,7 +1012,7 @@ func (in *NodeResourceSelectors) DeepCopy() *NodeResourceSelectors {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *NodeResources) DeepCopyInto(out *NodeResources) {
*out = *in
out.AnalyzeMeta = in.AnalyzeMeta
in.AnalyzeMeta.DeepCopyInto(&out.AnalyzeMeta)
if in.Outcomes != nil {
in, out := &in.Outcomes, &out.Outcomes
*out = make([]*Outcome, len(*in))
@@ -1397,7 +1406,7 @@ func (in *ResultRequest) DeepCopy() *ResultRequest {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Run) DeepCopyInto(out *Run) {
*out = *in
out.CollectorMeta = in.CollectorMeta
in.CollectorMeta.DeepCopyInto(&out.CollectorMeta)
if in.Command != nil {
in, out := &in.Command, &out.Command
*out = make([]string, len(*in))
@@ -1428,7 +1437,7 @@ func (in *Run) DeepCopy() *Run {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Secret) DeepCopyInto(out *Secret) {
*out = *in
out.CollectorMeta = in.CollectorMeta
in.CollectorMeta.DeepCopyInto(&out.CollectorMeta)
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Secret.
@@ -1459,7 +1468,7 @@ func (in *SingleOutcome) DeepCopy() *SingleOutcome {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *StatefulsetStatus) DeepCopyInto(out *StatefulsetStatus) {
*out = *in
out.AnalyzeMeta = in.AnalyzeMeta
in.AnalyzeMeta.DeepCopyInto(&out.AnalyzeMeta)
if in.Outcomes != nil {
in, out := &in.Outcomes, &out.Outcomes
*out = make([]*Outcome, len(*in))
@@ -1486,7 +1495,7 @@ func (in *StatefulsetStatus) DeepCopy() *StatefulsetStatus {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *StorageClass) DeepCopyInto(out *StorageClass) {
*out = *in
out.AnalyzeMeta = in.AnalyzeMeta
in.AnalyzeMeta.DeepCopyInto(&out.AnalyzeMeta)
if in.Outcomes != nil {
in, out := &in.Outcomes, &out.Outcomes
*out = make([]*Outcome, len(*in))
@@ -1666,7 +1675,7 @@ func (in *SupportBundleVersionSpec) DeepCopy() *SupportBundleVersionSpec {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *TextAnalyze) DeepCopyInto(out *TextAnalyze) {
*out = *in
out.AnalyzeMeta = in.AnalyzeMeta
in.AnalyzeMeta.DeepCopyInto(&out.AnalyzeMeta)
if in.Outcomes != nil {
in, out := &in.Outcomes, &out.Outcomes
*out = make([]*Outcome, len(*in))
@@ -175,8 +175,9 @@ type SysctlAnalyze struct {
}
type AnalyzeMeta struct {
CheckName string `json:"checkName,omitempty" yaml:"checkName,omitempty"`
Exclude multitype.BoolOrString `json:"exclude,omitempty" yaml:"exclude,omitempty"`
CheckName string `json:"checkName,omitempty" yaml:"checkName,omitempty"`
Exclude *multitype.BoolOrString `json:"exclude,omitempty" yaml:"exclude,omitempty"`
Strict *multitype.BoolOrString `json:"strict,omitempty" yaml:"strict,omitempty"`
}
type Analyze struct {
@@ -6,13 +6,14 @@ import (
"github.com/replicatedhq/troubleshoot/pkg/multitype"
authorizationv1 "k8s.io/api/authorization/v1"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
type CollectorMeta struct {
CollectorName string `json:"collectorName,omitempty" yaml:"collectorName,omitempty"`
// +optional
Exclude multitype.BoolOrString `json:"exclude,omitempty" yaml:"exclude,omitempty"`
Exclude *multitype.BoolOrString `json:"exclude,omitempty" yaml:"exclude,omitempty"`
}
type ClusterInfo struct {
@@ -65,15 +66,25 @@ type Data struct {
}
type Run struct {
CollectorMeta `json:",inline" yaml:",inline"`
Name string `json:"name,omitempty" yaml:"name,omitempty"`
Namespace string `json:"namespace" yaml:"namespace"`
Image string `json:"image" yaml:"image"`
Command []string `json:"command,omitempty" yaml:"command,omitempty"`
Args []string `json:"args,omitempty" yaml:"args,omitempty"`
Timeout string `json:"timeout,omitempty" yaml:"timeout,omitempty"`
ImagePullPolicy string `json:"imagePullPolicy,omitempty" yaml:"imagePullPolicy,omitempty"`
ImagePullSecret *ImagePullSecrets `json:"imagePullSecret,omitempty" yaml:"imagePullSecret,omitempty"`
ServiceAccountName string `json:"serviceAccountName,omitempty" yaml:"serviceAccountName,omitempty"`
}
type RunPod struct {
CollectorMeta `json:",inline" yaml:",inline"`
Name string `json:"name,omitempty" yaml:"name,omitempty"`
Namespace string `json:"namespace" yaml:"namespace"`
Image string `json:"image" yaml:"image"`
Command []string `json:"command,omitempty" yaml:"command,omitempty"`
Args []string `json:"args,omitempty" yaml:"args,omitempty"`
Timeout string `json:"timeout,omitempty" yaml:"timeout,omitempty"`
ImagePullPolicy string `json:"imagePullPolicy,omitempty" yaml:"imagePullPolicy,omitempty"`
ImagePullSecret *ImagePullSecrets `json:"imagePullSecret,omitempty" yaml:"imagePullSecret,omitempty"`
PodSpec corev1.PodSpec `json:"podSpec,omitempty" yaml:"podSpec,omitempty"`
}
type ImagePullSecrets struct {
@@ -155,7 +166,8 @@ type Put struct {
type Database struct {
CollectorMeta `json:",inline" yaml:",inline"`
URI string `json:"uri" yaml:"uri"`
URI string `json:"uri" yaml:"uri"`
Parameters []string `json:"parameters,omitempty"`
}
type Collectd struct {
@@ -194,6 +206,7 @@ type Collect struct {
ConfigMap *ConfigMap `json:"configMap,omitempty" yaml:"configMap,omitempty"`
Logs *Logs `json:"logs,omitempty" yaml:"logs,omitempty"`
Run *Run `json:"run,omitempty" yaml:"run,omitempty"`
RunPod *RunPod `json:"runPod,omitempty" yaml:"runPod,omitempty"`
Exec *Exec `json:"exec,omitempty" yaml:"exec,omitempty"`
Data *Data `json:"data,omitempty" yaml:"data,omitempty"`
Copy *Copy `json:"copy,omitempty" yaml:"copy,omitempty"`
@@ -327,6 +340,19 @@ func (c *Collect) AccessReviewSpecs(overrideNS string) []authorizationv1.SelfSub
},
NonResourceAttributes: nil,
})
} else if c.RunPod != nil {
result = append(result, authorizationv1.SelfSubjectAccessReviewSpec{
ResourceAttributes: &authorizationv1.ResourceAttributes{
Namespace: pickNamespaceOrDefault(c.RunPod.Namespace, overrideNS),
Verb: "create",
Group: "",
Version: "",
Resource: "pods",
Subresource: "",
Name: "",
},
NonResourceAttributes: nil,
})
} else if c.Exec != nil {
result = append(result, authorizationv1.SelfSubjectAccessReviewSpec{
ResourceAttributes: &authorizationv1.ResourceAttributes{
@@ -432,6 +458,10 @@ func (c *Collect) GetName() string {
collector = "run"
name = c.Run.CollectorName
}
if c.RunPod != nil {
collector = "run-pod"
name = c.RunPod.CollectorName
}
if c.Exec != nil {
collector = "exec"
name = c.Exec.CollectorName
@@ -454,6 +484,18 @@ func (c *Collect) GetName() string {
collector = "http"
name = c.HTTP.CollectorName
}
if c.Postgres != nil {
collector = "postgres"
name = c.Postgres.CollectorName
}
if c.Mysql != nil {
collector = "mysql"
name = c.Mysql.CollectorName
}
if c.Redis != nil {
collector = "redis"
name = c.Redis.CollectorName
}
if c.Collectd != nil {
collector = "collectd"
name = c.Collectd.CollectorName
@@ -7,7 +7,7 @@ import (
type HostCollectorMeta struct {
CollectorName string `json:"collectorName,omitempty" yaml:"collectorName,omitempty"`
// +optional
Exclude multitype.BoolOrString `json:"exclude,omitempty" yaml:"exclude,omitempty"`
Exclude *multitype.BoolOrString `json:"exclude,omitempty" yaml:"exclude,omitempty"`
}
type CPU struct {
@@ -10,7 +10,7 @@ import (
type RemoteCollectorMeta struct {
CollectorName string `json:"collectorName,omitempty" yaml:"collectorName,omitempty"`
// +optional
Exclude multitype.BoolOrString `json:"exclude,omitempty" yaml:"exclude,omitempty"`
Exclude *multitype.BoolOrString `json:"exclude,omitempty" yaml:"exclude,omitempty"`
}
type RemoteCPU struct {
File diff suppressed because it is too large Load Diff
+54 -42
View File
@@ -3,7 +3,6 @@ package collect
import (
"context"
"fmt"
"io"
"os"
"path"
"strings"
@@ -28,52 +27,60 @@ type CephCommand struct {
var CephCommands = []CephCommand{
{
ID: "status",
Command: []string{"ceph", "status"},
Args: []string{"-f", "json-pretty"},
Format: "json",
ID: "status",
Command: []string{"ceph", "status"},
Args: []string{"-f", "json-pretty"},
Format: "json",
DefaultTimeout: "30s",
},
{
ID: "fs",
Command: []string{"ceph", "fs", "status"},
Args: []string{"-f", "json-pretty"},
Format: "json",
ID: "fs",
Command: []string{"ceph", "fs", "status"},
Args: []string{"-f", "json-pretty"},
Format: "json",
DefaultTimeout: "30s",
},
{
ID: "fs-ls",
Command: []string{"ceph", "fs", "ls"},
Args: []string{"-f", "json-pretty"},
Format: "json",
ID: "fs-ls",
Command: []string{"ceph", "fs", "ls"},
Args: []string{"-f", "json-pretty"},
Format: "json",
DefaultTimeout: "30s",
},
{
ID: "osd-status",
Command: []string{"ceph", "osd", "status"},
Args: []string{"-f", "json-pretty"},
Format: "txt",
ID: "osd-status",
Command: []string{"ceph", "osd", "status"},
Args: []string{"-f", "json-pretty"},
Format: "txt",
DefaultTimeout: "30s",
},
{
ID: "osd-tree",
Command: []string{"ceph", "osd", "tree"},
Args: []string{"-f", "json-pretty"},
Format: "json",
ID: "osd-tree",
Command: []string{"ceph", "osd", "tree"},
Args: []string{"-f", "json-pretty"},
Format: "json",
DefaultTimeout: "30s",
},
{
ID: "osd-pool",
Command: []string{"ceph", "osd", "pool", "ls", "detail"},
Args: []string{"-f", "json-pretty"},
Format: "json",
ID: "osd-pool",
Command: []string{"ceph", "osd", "pool", "ls", "detail"},
Args: []string{"-f", "json-pretty"},
Format: "json",
DefaultTimeout: "30s",
},
{
ID: "health",
Command: []string{"ceph", "health", "detail"},
Args: []string{"-f", "json-pretty"},
Format: "json",
ID: "health",
Command: []string{"ceph", "health", "detail"},
Args: []string{"-f", "json-pretty"},
Format: "json",
DefaultTimeout: "30s",
},
{
ID: "auth",
Command: []string{"ceph", "auth", "ls"},
Args: []string{"-f", "json-pretty"},
Format: "json",
ID: "auth",
Command: []string{"ceph", "auth", "ls"},
Args: []string{"-f", "json-pretty"},
Format: "json",
DefaultTimeout: "30s",
},
{
ID: "rgw-stats", // the disk usage (and other stats) of each object store bucket
@@ -83,10 +90,18 @@ var CephCommands = []CephCommand{
DefaultTimeout: "30s", // include a default timeout because this command will hang if the RGW daemon isn't running/is unhealthy
},
{
ID: "rbd-du", // the disk usage of each PVC
Command: []string{"rbd", "du"},
Args: []string{"--pool=replicapool"},
Format: "txt",
ID: "rbd-du", // the disk usage of each PVC
Command: []string{"rbd", "du"},
Args: []string{"--pool=replicapool"},
Format: "txt",
DefaultTimeout: "30s",
},
{
ID: "osd-df",
Command: []string{"ceph", "osd", "df"},
Args: []string{"-f", "json-pretty"},
Format: "json",
DefaultTimeout: "30s",
},
}
@@ -133,8 +148,8 @@ func cephCommandExec(ctx context.Context, c *Collector, cephCollector *troublesh
return errors.Wrap(err, "failed to exec command")
}
pathPrefix := GetCephCollectorFilepath(cephCollector.CollectorName, cephCollector.Namespace)
for srcFilename, _ := range results {
pathPrefix := GetCephCollectorFilepath(cephCollector.CollectorName, cephCollector.Namespace)
var dstFileName string
switch {
case strings.HasSuffix(srcFilename, "-stdout.txt"):
@@ -203,10 +218,7 @@ func copyResult(srcResult CollectorResult, dstResult CollectorResult, bundlePath
}
return errors.Wrap(err, "failed to get reader")
}
if reader, ok := reader.(io.ReadCloser); ok {
defer reader.Close()
}
defer reader.Close()
err = dstResult.SaveResult(bundlePath, dstKey, reader)
if err != nil {
+15 -1
View File
@@ -26,7 +26,11 @@ type Collector struct {
type Collectors []*Collector
func isExcluded(excludeVal multitype.BoolOrString) (bool, error) {
func isExcluded(excludeVal *multitype.BoolOrString) (bool, error) {
if excludeVal == nil {
return false, nil
}
if excludeVal.Type == multitype.Bool {
return excludeVal.BoolVal, nil
}
@@ -93,6 +97,14 @@ func (c *Collector) IsExcluded() bool {
if isExcludedResult {
return true
}
} else if c.Collect.RunPod != nil {
isExcludedResult, err := isExcluded(c.Collect.RunPod.Exclude)
if err != nil {
return true
}
if isExcludedResult {
return true
}
} else if c.Collect.Exec != nil {
isExcludedResult, err := isExcluded(c.Collect.Exec.Exclude)
if err != nil {
@@ -221,6 +233,8 @@ func (c *Collector) RunCollectorSync(clientConfig *rest.Config, client kubernete
result, err = Logs(c, c.Collect.Logs)
} else if c.Collect.Run != nil {
result, err = Run(c, c.Collect.Run)
} else if c.Collect.RunPod != nil {
result, err = RunPod(c, c.Collect.RunPod)
} else if c.Collect.Exec != nil {
result, err = Exec(c, c.Collect.Exec)
} else if c.Collect.Data != nil {
+1 -9
View File
@@ -21,7 +21,6 @@ func TestCollector_RunCollectorSyncNoRedact(t *testing.T) {
Data: &troubleshootv1beta2.Data{
CollectorMeta: troubleshootv1beta2.CollectorMeta{
CollectorName: "datacollectorname",
Exclude: multitype.BoolOrString{},
},
Name: "data",
Data: `abc 123
@@ -54,7 +53,6 @@ pwd=***HIDDEN***;
Data: &troubleshootv1beta2.Data{
CollectorMeta: troubleshootv1beta2.CollectorMeta{
CollectorName: "datacollectorname",
Exclude: multitype.BoolOrString{},
},
Name: "data",
Data: `abc 123
@@ -89,7 +87,6 @@ pwd=***HIDDEN***;
Data: &troubleshootv1beta2.Data{
CollectorMeta: troubleshootv1beta2.CollectorMeta{
CollectorName: "datacollectorname",
Exclude: multitype.BoolOrString{},
},
Name: "data",
Data: `abc 123
@@ -124,7 +121,6 @@ pwd=***HIDDEN***;
Data: &troubleshootv1beta2.Data{
CollectorMeta: troubleshootv1beta2.CollectorMeta{
CollectorName: "datacollectorname",
Exclude: multitype.BoolOrString{},
},
Name: "data",
Data: `abc 123
@@ -162,7 +158,6 @@ pwd=***HIDDEN***;
Data: &troubleshootv1beta2.Data{
CollectorMeta: troubleshootv1beta2.CollectorMeta{
CollectorName: "data/collectorname",
Exclude: multitype.BoolOrString{},
},
Name: "data",
Data: `abc 123
@@ -200,7 +195,6 @@ pwd=***HIDDEN***;
Data: &troubleshootv1beta2.Data{
CollectorMeta: troubleshootv1beta2.CollectorMeta{
CollectorName: "datacollectorname",
Exclude: multitype.BoolOrString{},
},
Name: "data",
Data: `abc 123
@@ -228,7 +222,6 @@ another line here
Data: &troubleshootv1beta2.Data{
CollectorMeta: troubleshootv1beta2.CollectorMeta{
CollectorName: "datacollectorname",
Exclude: multitype.BoolOrString{},
},
Name: "data",
Data: `xyz123
@@ -265,7 +258,7 @@ abc
Data: &troubleshootv1beta2.Data{
CollectorMeta: troubleshootv1beta2.CollectorMeta{
CollectorName: "datacollectorname",
Exclude: multitype.BoolOrString{Type: multitype.String, StrVal: "true"},
Exclude: multitype.FromString("true"),
},
Name: "data",
Data: `abc 123
@@ -310,7 +303,6 @@ func TestCollector_RunCollectorSync(t *testing.T) {
Data: &troubleshootv1beta2.Data{
CollectorMeta: troubleshootv1beta2.CollectorMeta{
CollectorName: "datacollectorname",
Exclude: multitype.BoolOrString{},
},
Name: "data",
Data: `abc 123
+4 -3
View File
@@ -1,7 +1,8 @@
package collect
type DatabaseConnection struct {
IsConnected bool `json:"isConnected"`
Error string `json:"error,omitempty"`
Version string `json:"version,omitempty"`
IsConnected bool `json:"isConnected"`
Error string `json:"error,omitempty"`
Version string `json:"version,omitempty"`
Variables map[string]string `json:"variables,omitempty"`
}
+7 -3
View File
@@ -5,6 +5,7 @@ import (
"bytes"
"context"
"fmt"
"path"
"path/filepath"
"regexp"
"sync"
@@ -205,9 +206,12 @@ func Longhorn(c *Collector, longhornCollector *troubleshootv1beta2.Longhorn) (Co
return nil, errors.Wrap(err, "collect longhorn logs")
}
logsDir := GetLonghornLogsDirectory(ns)
for key, log := range logs {
key = filepath.Join(logsDir, key)
output.SaveResult(c.BundlePath, key, bytes.NewBuffer(log))
for srcFilename, _ := range logs {
dstFileName := path.Join(logsDir, srcFilename)
err := copyResult(logs, output, c.BundlePath, srcFilename, dstFileName)
if err != nil {
logger.Printf("Failed to copy file %s; %v", srcFilename, err)
}
}
// https://longhorn.io/docs/1.1.1/advanced-resources/data-recovery/corrupted-replica/
+34
View File
@@ -18,8 +18,10 @@ func Mysql(c *Collector, databaseCollector *troubleshootv1beta2.Database) (Colle
if err != nil {
databaseConnection.Error = err.Error()
} else {
defer db.Close()
query := `select version()`
row := db.QueryRow(query)
version := ""
if err := row.Scan(&version); err != nil {
databaseConnection.Error = err.Error()
@@ -27,6 +29,38 @@ func Mysql(c *Collector, databaseCollector *troubleshootv1beta2.Database) (Colle
databaseConnection.IsConnected = true
databaseConnection.Version = version
}
requestedParameters := databaseCollector.Parameters
if len(requestedParameters) > 0 {
rows, err := db.Query("SHOW VARIABLES")
if err != nil {
databaseConnection.Error = err.Error()
} else {
defer rows.Close()
variables := map[string]string{}
for rows.Next() {
var key, value string
err = rows.Scan(&key, &value)
if err != nil {
databaseConnection.Error = err.Error()
break
}
variables[key] = value
}
filteredVariables := map[string]string{}
for _, key := range requestedParameters {
if value, ok := variables[key]; ok {
filteredVariables[key] = value
}
}
databaseConnection.Variables = filteredVariables
}
}
}
b, err := json.Marshal(databaseConnection)
+1 -3
View File
@@ -26,9 +26,7 @@ func redactResult(bundlePath string, input CollectorResult, additionalRedactors
}
return errors.Wrap(err, "failed to get reader")
}
if r, ok := r.(io.ReadCloser); ok {
defer r.Close()
}
defer r.Close()
reader = r
} else {
+2 -2
View File
@@ -85,9 +85,9 @@ func (r CollectorResult) ReplaceResult(bundlePath string, relativePath string, r
return nil
}
func (r CollectorResult) GetReader(bundlePath string, relativePath string) (io.Reader, error) {
func (r CollectorResult) GetReader(bundlePath string, relativePath string) (io.ReadCloser, error) {
if r[relativePath] != nil {
return bytes.NewReader(r[relativePath]), nil
return ioutil.NopCloser(bytes.NewReader(r[relativePath])), nil
}
if bundlePath == "" {
+97 -63
View File
@@ -16,6 +16,45 @@ import (
)
func Run(c *Collector, runCollector *troubleshootv1beta2.Run) (CollectorResult, error) {
pullPolicy := corev1.PullIfNotPresent
if runCollector.ImagePullPolicy != "" {
pullPolicy = corev1.PullPolicy(runCollector.ImagePullPolicy)
}
namespace := "default"
if runCollector.Namespace != "" {
namespace = runCollector.Namespace
}
serviceAccountName := "default"
if runCollector.ServiceAccountName != "" {
serviceAccountName = runCollector.ServiceAccountName
}
runPodCollector := &troubleshootv1beta2.RunPod{
Name: runCollector.CollectorName,
Namespace: namespace,
Timeout: runCollector.Timeout,
ImagePullSecret: runCollector.ImagePullSecret,
PodSpec: corev1.PodSpec{
RestartPolicy: corev1.RestartPolicyNever,
ServiceAccountName: serviceAccountName,
Containers: []corev1.Container{
{
Image: runCollector.Image,
ImagePullPolicy: pullPolicy,
Name: "collector",
Command: runCollector.Command,
Args: runCollector.Args,
},
},
},
}
return RunPod(c, runPodCollector)
}
func RunPod(c *Collector, runPodCollector *troubleshootv1beta2.RunPod) (CollectorResult, error) {
ctx := context.Background()
client, err := kubernetes.NewForConfig(c.ClientConfig)
@@ -23,7 +62,7 @@ func Run(c *Collector, runCollector *troubleshootv1beta2.Run) (CollectorResult,
return nil, errors.Wrap(err, "failed to create client from config")
}
pod, err := runPod(ctx, client, runCollector, c.Namespace)
pod, err := runPodWithSpec(ctx, client, runPodCollector)
if err != nil {
return nil, errors.Wrap(err, "failed to run pod")
}
@@ -33,7 +72,7 @@ func Run(c *Collector, runCollector *troubleshootv1beta2.Run) (CollectorResult,
logger.Printf("Failed to delete pod %s: %v", pod.Name, err)
}
}()
if runCollector.ImagePullSecret != nil && runCollector.ImagePullSecret.Data != nil {
if runPodCollector.ImagePullSecret != nil && runPodCollector.ImagePullSecret.Data != nil {
defer func() {
for _, k := range pod.Spec.ImagePullSecrets {
if err := client.CoreV1().Secrets(pod.Namespace).Delete(ctx, k.Name, metav1.DeleteOptions{}); err != nil {
@@ -42,11 +81,11 @@ func Run(c *Collector, runCollector *troubleshootv1beta2.Run) (CollectorResult,
}
}()
}
if runCollector.Timeout == "" {
return runWithoutTimeout(ctx, c, pod, runCollector)
if runPodCollector.Timeout == "" {
return runWithoutTimeout(ctx, c, pod, runPodCollector)
}
timeout, err := time.ParseDuration(runCollector.Timeout)
timeout, err := time.ParseDuration(runPodCollector.Timeout)
if err != nil {
return nil, errors.Wrap(err, "failed to parse timeout")
}
@@ -58,7 +97,7 @@ func Run(c *Collector, runCollector *troubleshootv1beta2.Run) (CollectorResult,
defer cancel()
go func() {
b, err := runWithoutTimeout(timeoutCtx, c, pod, runCollector)
b, err := runWithoutTimeout(timeoutCtx, c, pod, runPodCollector)
if err != nil {
errCh <- err
} else {
@@ -76,7 +115,52 @@ func Run(c *Collector, runCollector *troubleshootv1beta2.Run) (CollectorResult,
}
}
func runWithoutTimeout(ctx context.Context, c *Collector, pod *corev1.Pod, runCollector *troubleshootv1beta2.Run) (CollectorResult, error) {
func runPodWithSpec(ctx context.Context, client *kubernetes.Clientset, runPodCollector *troubleshootv1beta2.RunPod) (*corev1.Pod, error) {
podLabels := make(map[string]string)
podLabels["troubleshoot-role"] = "run-collector"
namespace := "default"
if runPodCollector.Namespace != "" {
namespace = runPodCollector.Namespace
}
podName := "run-pod"
if runPodCollector.CollectorName != "" {
podName = runPodCollector.CollectorName
} else if runPodCollector.Name != "" {
podName = runPodCollector.Name
}
pod := corev1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: podName,
Namespace: namespace,
Labels: podLabels,
},
TypeMeta: metav1.TypeMeta{
APIVersion: "v1",
Kind: "Pod",
},
Spec: runPodCollector.PodSpec,
}
if runPodCollector.ImagePullSecret != nil && runPodCollector.ImagePullSecret.Data != nil {
secretName, err := createSecret(ctx, client, pod.Namespace, runPodCollector.ImagePullSecret)
if err != nil {
return nil, errors.Wrap(err, "failed to create secret")
}
pod.Spec.ImagePullSecrets = append(pod.Spec.ImagePullSecrets, corev1.LocalObjectReference{Name: secretName})
}
created, err := client.CoreV1().Pods(namespace).Create(ctx, &pod, metav1.CreateOptions{})
if err != nil {
return nil, errors.Wrap(err, "failed to create pod")
}
return created, nil
}
func runWithoutTimeout(ctx context.Context, c *Collector, pod *corev1.Pod, runPodCollector *troubleshootv1beta2.RunPod) (CollectorResult, error) {
client, err := kubernetes.NewForConfig(c.ClientConfig)
if err != nil {
return nil, errors.Wrap(err, "failed create client from config")
@@ -104,10 +188,15 @@ func runWithoutTimeout(ctx context.Context, c *Collector, pod *corev1.Pod, runCo
output := NewResult()
collectorName := runPodCollector.Name
if collectorName == "" {
collectorName = runPodCollector.CollectorName
}
limits := troubleshootv1beta2.LogLimits{
MaxLines: 10000,
}
podLogs, err := savePodLogs(ctx, c.BundlePath, client, *pod, runCollector.Name, "", &limits, true)
podLogs, err := savePodLogs(ctx, c.BundlePath, client, *pod, collectorName, "", &limits, true)
if err != nil {
return nil, errors.Wrap(err, "failed to get pod logs")
}
@@ -119,61 +208,6 @@ func runWithoutTimeout(ctx context.Context, c *Collector, pod *corev1.Pod, runCo
return output, nil
}
func runPod(ctx context.Context, client *kubernetes.Clientset, runCollector *troubleshootv1beta2.Run, namespace string) (*corev1.Pod, error) {
podLabels := make(map[string]string)
podLabels["troubleshoot-role"] = "run-collector"
pullPolicy := corev1.PullIfNotPresent
if runCollector.ImagePullPolicy != "" {
pullPolicy = corev1.PullPolicy(runCollector.ImagePullPolicy)
}
if namespace == "" {
namespace = runCollector.Namespace
}
if namespace == "" {
namespace = "default"
}
pod := corev1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: runCollector.CollectorName,
Namespace: namespace,
Labels: podLabels,
},
TypeMeta: metav1.TypeMeta{
APIVersion: "v1",
Kind: "Pod",
},
Spec: corev1.PodSpec{
RestartPolicy: corev1.RestartPolicyNever,
Containers: []corev1.Container{
{
Image: runCollector.Image,
ImagePullPolicy: pullPolicy,
Name: "collector",
Command: runCollector.Command,
Args: runCollector.Args,
},
},
},
}
if runCollector.ImagePullSecret != nil && runCollector.ImagePullSecret.Data != nil {
secretName, err := createSecret(ctx, client, pod.Namespace, runCollector.ImagePullSecret)
if err != nil {
return nil, errors.Wrap(err, "failed to create secret")
}
pod.Spec.ImagePullSecrets = append(pod.Spec.ImagePullSecrets, corev1.LocalObjectReference{Name: secretName})
}
created, err := client.CoreV1().Pods(namespace).Create(ctx, &pod, metav1.CreateOptions{})
if err != nil {
return nil, errors.Wrap(err, "failed to create pod")
}
return created, nil
}
func createSecret(ctx context.Context, client kubernetes.Interface, namespace string, imagePullSecret *troubleshootv1beta2.ImagePullSecrets) (string, error) {
if imagePullSecret.Data == nil {
return "", nil
+19
View File
@@ -0,0 +1,19 @@
package convert
import (
"os"
"path/filepath"
)
// ValidateOutputPath takes an output file path and returns it as an absolute path.
// It returns an error if the absolute path cannot be determined or if the parent directory does not exist.
func ValidateOutputPath(outputPath string) (string, error) {
outputPath, err := filepath.Abs(outputPath)
if err != nil {
return "", err
}
if _, err := os.Stat(filepath.Dir(outputPath)); err != nil {
return "", err
}
return outputPath, nil
}
+14 -7
View File
@@ -114,13 +114,20 @@ func IsPodUnhealthy(pod *corev1.Pod) bool {
}
reason := GetPodStatusReason(pod)
switch PodStatusReason(reason) {
case PodStatusReasonRunning:
fallthrough
case PodStatusReasonCompleted:
return false
if PodStatusReason(reason) == PodStatusReasonCompleted {
return false // completed pods are healthy pods
}
return true
if PodStatusReason(reason) != PodStatusReasonRunning {
return true // pods that are not completed or running are unhealthy
}
// running pods with unready containers are not healthy
for _, containerStatus := range pod.Status.ContainerStatuses {
if !containerStatus.Ready {
return true
}
}
return false
}
+101
View File
@@ -0,0 +1,101 @@
package k8sutil
import (
"github.com/stretchr/testify/require"
corev1 "k8s.io/api/core/v1"
"testing"
)
func TestIsPodUnhealthy(t *testing.T) {
andTrue := true
tests := []struct {
name string
pod *corev1.Pod
want bool
}{
{
name: "healthy pod with init containers",
want: false,
pod: &corev1.Pod{
Status: corev1.PodStatus{
Phase: "Running",
Conditions: []corev1.PodCondition{
// ignored here
},
InitContainerStatuses: []corev1.ContainerStatus{
{
Name: "init",
State: corev1.ContainerState{
Terminated: &corev1.ContainerStateTerminated{
ExitCode: 0,
Reason: "Completed",
},
},
Ready: true,
RestartCount: 2,
Image: "projectcontour/contour:v1.11.0",
ImageID: "docker://sha256:12878e02b6f969de1456b51b0093f289fd195db956837ccd75aa679e9dac24d9",
ContainerID: "docker://50fc0794fa1402b1a48521e2fd380a92521a69db70ad25c9699c91fccd839d70",
},
},
ContainerStatuses: []corev1.ContainerStatus{
{
Name: "contour",
State: corev1.ContainerState{
Running: &corev1.ContainerStateRunning{},
},
Ready: true,
RestartCount: 5,
Image: "projectcontour/contour:v1.11.0",
ImageID: "docker://sha256:12878e02b6f969de1456b51b0093f289fd195db956837ccd75aa679e9dac24d9",
ContainerID: "docker://50fc0794fa1402b1a48521e2fd380a92521a69db70ad25c9699c91fccd839d70",
Started: &andTrue,
},
},
QOSClass: corev1.PodQOSBestEffort,
},
},
},
{
name: "running pod with one unhealthy container",
want: true,
pod: &corev1.Pod{
Status: corev1.PodStatus{
Phase: "Running",
Conditions: []corev1.PodCondition{
// ignored here
},
ContainerStatuses: []corev1.ContainerStatus{
{
Name: "envoy",
State: corev1.ContainerState{
Running: &corev1.ContainerStateRunning{},
},
Ready: false,
RestartCount: 5,
Started: &andTrue,
},
{
Name: "shutdown-manager",
State: corev1.ContainerState{
Running: &corev1.ContainerStateRunning{},
},
Ready: true,
RestartCount: 5,
Started: &andTrue,
},
},
QOSClass: corev1.PodQOSBestEffort,
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
req := require.New(t)
got := IsPodUnhealthy(tt.pod)
req.Equal(tt.want, got)
})
}
}
+47 -7
View File
@@ -5,6 +5,7 @@ package multitype
import (
"encoding/json"
"fmt"
"strconv"
fuzz "github.com/google/gofuzz"
)
@@ -33,22 +34,25 @@ const (
)
// FromBool creates an BoolOrString object with a bool value.
func FromBool(val bool) BoolOrString {
return BoolOrString{Type: Bool, BoolVal: val}
func FromBool(val bool) *BoolOrString {
return &BoolOrString{Type: Bool, BoolVal: val}
}
// FromString creates an BoolOrString object with a string value.
func FromString(val string) BoolOrString {
return BoolOrString{Type: String, StrVal: val}
func FromString(val string) *BoolOrString {
return &BoolOrString{Type: String, StrVal: val}
}
// Parse the given string
func Parse(val string) BoolOrString {
func Parse(val string) *BoolOrString {
return FromString(val)
}
// UnmarshalJSON implements the json.Unmarshaller boolerface.
func (boolstr *BoolOrString) UnmarshalJSON(value []byte) error {
if boolstr == nil {
return nil
}
if value[0] == '"' {
boolstr.Type = String
return json.Unmarshal(value, &boolstr.StrVal)
@@ -59,6 +63,9 @@ func (boolstr *BoolOrString) UnmarshalJSON(value []byte) error {
// String returns the string value, '1' for true, or '0' for false.
func (boolstr *BoolOrString) String() string {
if boolstr == nil {
return "0"
}
if boolstr.Type == String {
return boolstr.StrVal
} else if boolstr.BoolVal {
@@ -69,7 +76,10 @@ func (boolstr *BoolOrString) String() string {
}
// MarshalJSON implements the json.Marshaller interface.
func (boolstr BoolOrString) MarshalJSON() ([]byte, error) {
func (boolstr *BoolOrString) MarshalJSON() ([]byte, error) {
if boolstr == nil {
return []byte{}, nil
}
switch boolstr.Type {
case Bool:
return json.Marshal(boolstr.BoolVal)
@@ -81,7 +91,10 @@ func (boolstr BoolOrString) MarshalJSON() ([]byte, error) {
}
// MarshalYAML implements the yaml.Marshaller interface https://godoc.org/gopkg.in/yaml.v3#Marshaler
func (boolstr BoolOrString) MarshalYAML() (interface{}, error) {
func (boolstr *BoolOrString) MarshalYAML() (interface{}, error) {
if boolstr == nil {
return []byte{}, nil
}
switch boolstr.Type {
case Bool:
return boolstr.BoolVal, nil
@@ -116,3 +129,30 @@ func (boolstr *BoolOrString) Fuzz(c fuzz.Continue) {
c.Fuzz(&boolstr.StrVal)
}
}
// BoolOrDefaultFalse returns bool val, if strValu is parsed returns parsed value else false as default when parse error
func (boolstr *BoolOrString) BoolOrDefaultFalse() bool {
if boolstr == nil {
return false
}
val, err := boolstr.Bool()
if err != nil {
return false
}
return val
}
// Bool returns bool val, if strValu is parsed returns parsed value else false with parse error
func (boolstr *BoolOrString) Bool() (bool, error) {
if boolstr == nil {
return false, nil
}
if boolstr.Type == Bool {
return boolstr.BoolVal, nil
}
parsed, err := strconv.ParseBool(boolstr.StrVal)
if err != nil {
return false, fmt.Errorf("failed to parse bool string(err: %v)", err)
}
return parsed, nil
}
+147
View File
@@ -0,0 +1,147 @@
// Based on https://github.com/kubernetes/apimachinery/blob/455a99f/pkg/util/intstr/intstr.go
package multitype
import (
"encoding/json"
"strings"
"testing"
"github.com/stretchr/testify/require"
"gopkg.in/yaml.v2"
)
func TestBoolOrString_Bool(t *testing.T) {
type fields struct {
Type BoolOrStringType
BoolVal bool
StrVal string
}
tests := []struct {
name string
fields fields
want bool
wantErr bool
}{
{
name: "expect true when BoolVal is true",
fields: fields{Type: Bool, BoolVal: true},
want: true,
wantErr: false,
}, {
name: "expect false when BoolVal is false",
fields: fields{Type: Bool, BoolVal: false},
want: false,
wantErr: false,
}, {
name: "expect false when StrVal is 'false'",
fields: fields{Type: String, StrVal: "false"},
want: false,
wantErr: false,
}, {
name: "expect true when StrVal is 'true'",
fields: fields{Type: String, StrVal: "true"},
want: true,
wantErr: false,
}, {
name: "expect false, error when StrVal is ''",
fields: fields{Type: String, StrVal: "''"},
want: false,
wantErr: true,
},
{
name: "expect false, error when StrVal is '123'",
fields: fields{Type: String, StrVal: "123"},
want: false,
wantErr: true,
}, {
name: "expect true, nil when Type is not specified, StrVal is 'true'",
fields: fields{StrVal: "true"},
want: true,
wantErr: false,
}, {
name: "expect false, nil when Type is not specified, StrVal is 'false'",
fields: fields{StrVal: "false"},
want: false,
wantErr: false,
}, {
name: "expect false, nil when Type is not specified, StrVal is 'false' and BoolVal is true",
fields: fields{StrVal: "false", BoolVal: true},
want: false,
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
req := require.New(t)
boolstr := &BoolOrString{
Type: tt.fields.Type,
BoolVal: tt.fields.BoolVal,
StrVal: tt.fields.StrVal,
}
got, err := boolstr.Bool()
req.Equal(tt.want, boolstr.BoolOrDefaultFalse())
if tt.wantErr {
req.Error(err)
return
}
req.NoError(err)
req.Equal(tt.want, got)
})
}
}
func TestBoolOrString_MarshalOmitempty(t *testing.T) {
type S struct {
String string `json:"string,omitempty" yaml:"string,omitempty"`
Multi *BoolOrString `json:"multi,omitempty" yaml:"multi,omitempty"`
}
tests := []struct {
name string
wantJSON string
wantYAML string
}{
{
wantJSON: `{"string":"string"}`,
wantYAML: "string: string",
},
{
wantJSON: `{"string":"string","multi":false}`,
wantYAML: "string: string\nmulti: false",
},
{
wantJSON: `{"string":"string","multi":"false"}`,
wantYAML: "string: string\nmulti: \"false\"",
},
{
wantJSON: `{"string":"string","multi":true}`,
wantYAML: "string: string\nmulti: true",
},
{
wantJSON: `{"string":"string","multi":"true"}`,
wantYAML: "string: string\nmulti: \"true\"",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
req := require.New(t)
ass := require.New(t)
var sJSON S
err := json.Unmarshal([]byte(tt.wantJSON), &sJSON)
req.NoError(err)
out, err := json.Marshal(sJSON)
req.NoError(err)
ass.Equal(tt.wantJSON, strings.TrimSpace(string(out)))
var sYAML S
err = json.Unmarshal([]byte(tt.wantJSON), &sYAML)
req.NoError(err)
out, err = yaml.Marshal(sYAML)
req.NoError(err)
ass.Equal(tt.wantYAML, strings.TrimSpace(string(out)))
})
}
}
+7
View File
@@ -8,6 +8,7 @@ import (
analyze "github.com/replicatedhq/troubleshoot/pkg/analyze"
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
"github.com/replicatedhq/troubleshoot/pkg/logger"
)
// Analyze runs the analyze phase of preflight checks
@@ -77,8 +78,14 @@ func doAnalyze(allCollectedData map[string][]byte, analyzers []*troubleshootv1be
for _, analyzer := range analyzers {
analyzeResult, err := analyze.Analyze(analyzer, getCollectedFileContents, getChildCollectedFileContents)
if err != nil {
strict, strictErr := HasStrictAnalyzer(analyzer)
if strictErr != nil {
logger.Printf("failed to determine if analyzer %v is strict: %s", analyzer, strictErr)
}
analyzeResult = []*analyze.AnalyzeResult{
{
Strict: strict,
IsFail: true,
Title: "Analyzer Failed",
Message: err.Error(),
+1
View File
@@ -1,6 +1,7 @@
package preflight
type UploadPreflightResult struct {
Strict bool `json:"strict,omitempty"`
IsFail bool `json:"isFail,omitempty"`
IsWarn bool `json:"isWarn,omitempty"`
IsPass bool `json:"isPass,omitempty"`
+88
View File
@@ -0,0 +1,88 @@
package preflight
import (
"encoding/json"
"github.com/pkg/errors"
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
)
// HasStrictAnalyzers - checks and returns true if a preflight's analyzer has strict:true, else false
func HasStrictAnalyzers(preflight *troubleshootv1beta2.Preflight) (bool, error) {
if preflight == nil {
return false, nil
}
marshalledAnalyzers, err := json.Marshal(preflight.Spec.Analyzers) // marshall and remove nil Analyzers eg result: "[{\"clusterVersion\":{\"exclude\":\"\",\"strict\":\"false\",\"outcomes\":null}}]"
if err != nil {
return false, errors.Wrap(err, "failed to marshal analyzers")
}
analyzersMap := []map[string]interface{}{}
err = json.Unmarshal(marshalledAnalyzers, &analyzersMap) // Unmarshall again so we can loop over non nil analyzers
if err != nil {
return false, errors.Wrap(err, "failed to unmarshal analyzers")
}
// analyzerMap will ignore empty Analyzers and loop around Analyzer with data
for _, analyzers := range analyzersMap { // for each analyzer: map["clusterVersion": map[string]interface{} ["exclude": "", "strict": "true", "outcomes": nil]
hasStrictAnalyzer, err := hasStrictAnalyzer(analyzers)
if err != nil {
return false, errors.Wrap(err, "failed to check if analyzer has strict:true")
}
if hasStrictAnalyzer {
return true, nil
}
}
return false, nil
}
// HasStrictAnalyzers - checks and returns true if a preflight's analyzer has strict:true, else false
func HasStrictAnalyzer(analyzer *troubleshootv1beta2.Analyze) (bool, error) {
marshalledAnalyzer, err := json.Marshal(analyzer)
if err != nil {
return false, errors.Wrap(err, "error while marshalling analyzer")
}
analyzerMap := make(map[string]interface{})
err = json.Unmarshal(marshalledAnalyzer, &analyzerMap) // Unmarshall again so we can loop over non nil analyzers
if err != nil {
return false, errors.Wrap(err, "failed to unmarshal analyzers")
}
return hasStrictAnalyzer(analyzerMap)
}
func hasStrictAnalyzer(analyzerMap map[string]interface{}) (bool, error) {
for _, analyzer := range analyzerMap { // for each analyzerMeta: map[string]interface{} ["exclude": "", "strict": "true", "outcomes": nil]
marshalledAnalyzer, err := json.Marshal(analyzer)
if err != nil {
return false, errors.Wrap(err, "error while marshalling analyzer")
}
// return Analyzer.Strict which can be extracted from AnalyzeMeta
analyzeMeta := troubleshootv1beta2.AnalyzeMeta{}
err = json.Unmarshal(marshalledAnalyzer, &analyzeMeta)
if err != nil {
return false, errors.Wrap(err, "error while un-marshalling marshalledAnalyzers")
}
if analyzeMeta.Strict.BoolOrDefaultFalse() {
return true, nil
}
}
return false, nil
}
// HasStrictAnalyzersFailed - checks if preflight analyzer's result is strict:true and isFail:true, then returns true else false
func HasStrictAnalyzersFailed(preflightResult *UploadPreflightResults) bool {
hasStrictAnalyzersFailed := false
// if results are empty, treat as failure
if preflightResult == nil || len(preflightResult.Results) == 0 {
hasStrictAnalyzersFailed = true
} else {
for _, result := range preflightResult.Results {
if result.IsFail && result.Strict {
hasStrictAnalyzersFailed = true
}
}
}
return hasStrictAnalyzersFailed
}
+384
View File
@@ -0,0 +1,384 @@
package preflight
import (
"testing"
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
"github.com/replicatedhq/troubleshoot/pkg/multitype"
)
var (
analyzeMetaStrictFalseStr = troubleshootv1beta2.AnalyzeMeta{
Strict: &multitype.BoolOrString{
StrVal: "false",
},
}
analyzeMetaStrictInvalidStr = troubleshootv1beta2.AnalyzeMeta{
Strict: &multitype.BoolOrString{
StrVal: "invalid",
},
}
analyzeMetaStrictTrueStr = troubleshootv1beta2.AnalyzeMeta{
Strict: &multitype.BoolOrString{
StrVal: "true",
},
}
analyzeMetaStrictFalseBool = troubleshootv1beta2.AnalyzeMeta{
Strict: &multitype.BoolOrString{
Type: multitype.Bool,
BoolVal: false,
},
}
analyzeMetaStrictTrueBool = troubleshootv1beta2.AnalyzeMeta{
Strict: &multitype.BoolOrString{
Type: multitype.Bool,
BoolVal: true,
},
}
analyzeMetaStrictFalseInt = troubleshootv1beta2.AnalyzeMeta{
Strict: &multitype.BoolOrString{
StrVal: "0",
},
}
analyzeMetaStrictTrueInt = troubleshootv1beta2.AnalyzeMeta{
Strict: &multitype.BoolOrString{
Type: multitype.String,
StrVal: "1",
},
}
)
func TestHasStrictAnalyzers(t *testing.T) {
tests := []struct {
name string
preflight *troubleshootv1beta2.Preflight
want bool
wantErr bool
}{
{
name: "expect false when preflight is nil",
preflight: nil,
want: false,
wantErr: false,
}, {
name: "expect false when preflight spec is empty",
preflight: &troubleshootv1beta2.Preflight{
Spec: troubleshootv1beta2.PreflightSpec{},
},
want: false,
wantErr: false,
}, {
name: "expect false when preflight spec's analyzers is nil",
preflight: &troubleshootv1beta2.Preflight{
Spec: troubleshootv1beta2.PreflightSpec{
Analyzers: nil,
},
},
want: false,
wantErr: false,
}, {
name: "expect false when preflight spec's analyzers is empty",
preflight: &troubleshootv1beta2.Preflight{
Spec: troubleshootv1beta2.PreflightSpec{
Analyzers: []*troubleshootv1beta2.Analyze{},
},
},
want: false,
wantErr: false,
}, {
name: "expect false when preflight spec's analyzer has nil analyzer",
preflight: &troubleshootv1beta2.Preflight{
Spec: troubleshootv1beta2.PreflightSpec{
Analyzers: []*troubleshootv1beta2.Analyze{
{
ClusterVersion: nil,
},
},
},
},
want: false,
wantErr: false,
}, {
name: "expect false when preflight spec's analyzer has analyzer with strict str: false",
preflight: &troubleshootv1beta2.Preflight{
Spec: troubleshootv1beta2.PreflightSpec{
Analyzers: []*troubleshootv1beta2.Analyze{
{
ClusterVersion: &troubleshootv1beta2.ClusterVersion{AnalyzeMeta: analyzeMetaStrictFalseStr},
},
},
},
},
want: false,
wantErr: false,
}, {
name: "expect false when preflight spec's analyzer has analyzer with strict bool: false",
preflight: &troubleshootv1beta2.Preflight{
Spec: troubleshootv1beta2.PreflightSpec{
Analyzers: []*troubleshootv1beta2.Analyze{
{
ClusterVersion: &troubleshootv1beta2.ClusterVersion{AnalyzeMeta: analyzeMetaStrictFalseBool},
},
},
},
},
want: false,
wantErr: false,
}, {
name: "expect false when preflight spec's analyzer has analyzer with strict str: invalid",
preflight: &troubleshootv1beta2.Preflight{
Spec: troubleshootv1beta2.PreflightSpec{
Analyzers: []*troubleshootv1beta2.Analyze{
{
ClusterVersion: &troubleshootv1beta2.ClusterVersion{AnalyzeMeta: analyzeMetaStrictInvalidStr},
},
},
},
},
want: false,
wantErr: false,
}, {
name: "expect true when preflight spec's analyzer has analyzer with strict str: true",
preflight: &troubleshootv1beta2.Preflight{
Spec: troubleshootv1beta2.PreflightSpec{
Analyzers: []*troubleshootv1beta2.Analyze{
{
ClusterVersion: &troubleshootv1beta2.ClusterVersion{AnalyzeMeta: analyzeMetaStrictTrueStr},
},
},
},
},
want: true,
wantErr: false,
}, {
name: "expect true when preflight spec's analyzer has analyzer with strict bool: true",
preflight: &troubleshootv1beta2.Preflight{
Spec: troubleshootv1beta2.PreflightSpec{
Analyzers: []*troubleshootv1beta2.Analyze{
{
ClusterVersion: &troubleshootv1beta2.ClusterVersion{AnalyzeMeta: analyzeMetaStrictTrueBool},
},
},
},
},
want: true,
wantErr: false,
}, {
name: "expect true when preflight spec's analyzer has analyzer with strict int: 1",
preflight: &troubleshootv1beta2.Preflight{
Spec: troubleshootv1beta2.PreflightSpec{
Analyzers: []*troubleshootv1beta2.Analyze{
{
ClusterVersion: &troubleshootv1beta2.ClusterVersion{AnalyzeMeta: analyzeMetaStrictTrueInt},
},
},
},
},
want: true,
wantErr: false,
}, {
name: "expect false when preflight spec's analyzer has analyzer with strict int: 0",
preflight: &troubleshootv1beta2.Preflight{
Spec: troubleshootv1beta2.PreflightSpec{
Analyzers: []*troubleshootv1beta2.Analyze{
{
ClusterVersion: &troubleshootv1beta2.ClusterVersion{AnalyzeMeta: analyzeMetaStrictFalseInt},
},
},
},
},
want: false,
wantErr: false,
}, {
name: "expect true when preflight spec's analyzer has analyzer with strict true in one of multiple analyzers",
preflight: &troubleshootv1beta2.Preflight{
Spec: troubleshootv1beta2.PreflightSpec{
Analyzers: []*troubleshootv1beta2.Analyze{
{
ClusterVersion: &troubleshootv1beta2.ClusterVersion{AnalyzeMeta: analyzeMetaStrictFalseInt},
}, {
ClusterVersion: &troubleshootv1beta2.ClusterVersion{AnalyzeMeta: analyzeMetaStrictTrueBool},
},
},
},
},
want: true,
wantErr: false,
}, {
name: "expect true when preflight spec's analyzer has analyzer with strict true in one of multiple analyzers",
preflight: &troubleshootv1beta2.Preflight{
Spec: troubleshootv1beta2.PreflightSpec{
Analyzers: []*troubleshootv1beta2.Analyze{
{
ClusterVersion: &troubleshootv1beta2.ClusterVersion{AnalyzeMeta: analyzeMetaStrictFalseInt},
}, {
ClusterVersion: &troubleshootv1beta2.ClusterVersion{AnalyzeMeta: analyzeMetaStrictTrueBool},
}, {
ClusterVersion: &troubleshootv1beta2.ClusterVersion{AnalyzeMeta: analyzeMetaStrictInvalidStr},
},
},
},
},
want: true,
wantErr: false,
}, {
name: "expect true when preflight spec's analyzer has analyzer with strict true in one of multiple analyzers",
preflight: &troubleshootv1beta2.Preflight{
Spec: troubleshootv1beta2.PreflightSpec{
Analyzers: []*troubleshootv1beta2.Analyze{
{
ClusterVersion: &troubleshootv1beta2.ClusterVersion{AnalyzeMeta: analyzeMetaStrictFalseInt},
StorageClass: &troubleshootv1beta2.StorageClass{AnalyzeMeta: analyzeMetaStrictFalseInt},
Secret: &troubleshootv1beta2.AnalyzeSecret{AnalyzeMeta: analyzeMetaStrictTrueBool},
},
},
},
},
want: true,
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := HasStrictAnalyzers(tt.preflight)
if (err != nil) != tt.wantErr {
t.Errorf("HasStrictAnalyzers error = %v, wantErr %v", err, tt.wantErr)
return
}
if got != tt.want {
t.Errorf("HasStrictAnalyzers() = %v, want %v", got, tt.want)
}
})
}
}
func TestHasStrictAnalyzerFailed(t *testing.T) {
tests := []struct {
name string
preflightResult *UploadPreflightResults
want bool
}{
{
name: "expect true when preflightResult is nil",
preflightResult: nil,
want: true,
}, {
name: "expect true when preflightResult.Results is nil",
preflightResult: &UploadPreflightResults{
Results: nil,
},
want: true,
}, {
name: "expect true when preflightResult.Results is empty",
preflightResult: &UploadPreflightResults{
Results: []*UploadPreflightResult{},
},
want: true,
}, {
name: "expect false when preflightResult.Results has result with strict false, IsFail false",
preflightResult: &UploadPreflightResults{
Results: []*UploadPreflightResult{
{Strict: false, IsFail: false},
},
},
want: false,
}, {
name: "expect false when preflightResult.Results has result with strict false, IsFail true",
preflightResult: &UploadPreflightResults{
Results: []*UploadPreflightResult{
{Strict: false, IsFail: true},
},
},
want: false,
}, {
name: "expect true when preflightResult.Results has result with strict true, IsFail true",
preflightResult: &UploadPreflightResults{
Results: []*UploadPreflightResult{
{Strict: true, IsFail: true},
},
},
want: true,
}, {
name: "expect true when preflightResult.Results has multiple results where atleast result has strict true, IsFail true",
preflightResult: &UploadPreflightResults{
Results: []*UploadPreflightResult{
{Strict: true, IsFail: true},
{Strict: false, IsFail: true},
{Strict: true, IsFail: false},
},
},
want: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := HasStrictAnalyzersFailed(tt.preflightResult); got != tt.want {
t.Errorf("HasStrictAnalyzersFailed() = %v, want %v", got, tt.want)
}
})
}
}
func TestHasStrictAnalyzer(t *testing.T) {
tests := []struct {
name string
analyzer *troubleshootv1beta2.Analyze
want bool
wantErr bool
}{
{
name: "expect strict=false, err=nil when analyzer is nil",
analyzer: nil,
want: false,
wantErr: false,
}, {
name: "expect strict=false, err=nil when analyzer is empty",
analyzer: &troubleshootv1beta2.Analyze{},
want: false,
wantErr: false,
}, {
name: "expect strict=false, err=nil when ClusterVersion analyzer has strict=1",
analyzer: &troubleshootv1beta2.Analyze{
ClusterVersion: &troubleshootv1beta2.ClusterVersion{AnalyzeMeta: analyzeMetaStrictFalseInt},
},
want: false,
wantErr: false,
}, {
name: "expect strict=true, err=nil when ClusterVersion analyzer has strict=true",
analyzer: &troubleshootv1beta2.Analyze{
ClusterVersion: &troubleshootv1beta2.ClusterVersion{AnalyzeMeta: analyzeMetaStrictTrueInt},
},
want: true,
wantErr: false,
}, {
name: "expect strict=false, err=nil when ClusterVersion analyzer is nil",
analyzer: &troubleshootv1beta2.Analyze{
ClusterVersion: nil,
},
want: false,
wantErr: false,
}, {
name: "expect strict=true, err=nil when one of the analyzers has strict=true",
analyzer: &troubleshootv1beta2.Analyze{
ClusterVersion: &troubleshootv1beta2.ClusterVersion{AnalyzeMeta: analyzeMetaStrictFalseInt},
StorageClass: &troubleshootv1beta2.StorageClass{AnalyzeMeta: analyzeMetaStrictFalseInt},
Secret: &troubleshootv1beta2.AnalyzeSecret{AnalyzeMeta: analyzeMetaStrictTrueBool},
},
want: true,
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := HasStrictAnalyzer(tt.analyzer)
if (err != nil) != tt.wantErr {
t.Errorf("HasStrictAnalyzer() error = %v, wantErr %v", err, tt.wantErr)
return
}
if got != tt.want {
t.Errorf("HasStrictAnalyzer() = %v, want %v", got, tt.want)
}
})
}
}
+37 -8
View File
@@ -19,20 +19,44 @@ var (
SupportBundleNameRegex = regexp.MustCompile(`^\/?support-bundle-(\d{4})-(\d{2})-(\d{2})T(\d{2})_(\d{2})_(\d{2})\/?`)
)
func GetPodDetails(bundleArchive string, podNamespace string, podName string) (*types.PodDetails, error) {
podDetails := types.PodDetails{}
func getPodsFilePath(namespace string) string {
return filepath.Join("cluster-resources", "pods", fmt.Sprintf("%s.json", namespace))
}
nsPodsFilePath := filepath.Join("cluster-resources", "pods", fmt.Sprintf("%s.json", podNamespace))
nsEventsFilePath := filepath.Join("cluster-resources", "events", fmt.Sprintf("%s.json", podNamespace))
func getContainerLogsFilePath(namespace string, podName string, containerName string) string {
return filepath.Join("cluster-resources", "pods", "logs", namespace, podName, fmt.Sprintf("%s.log", containerName))
}
func getEventsFilePath(namespace string) string {
return filepath.Join("cluster-resources", "events", fmt.Sprintf("%s.json", namespace))
}
func GetPodDetails(bundleArchive string, podNamespace string, podName string) (*types.PodDetails, error) {
nsPodsFilePath := getPodsFilePath(podNamespace)
nsEventsFilePath := getEventsFilePath(podNamespace)
files, err := GetFilesContents(bundleArchive, []string{nsPodsFilePath, nsEventsFilePath})
if err != nil {
return nil, errors.Wrap(err, "failed to get files contents")
}
return getPodDetailsFromFiles(files, podNamespace, podName)
}
func getPodDetailsFromFiles(files map[string][]byte, podNamespace string, podName string) (*types.PodDetails, error) {
podDetails := types.PodDetails{}
nsPodsFilePath := getPodsFilePath(podNamespace)
nsEventsFilePath := getEventsFilePath(podNamespace)
var nsEvents []corev1.Event
if err := json.Unmarshal(files[nsEventsFilePath], &nsEvents); err != nil {
return nil, errors.Wrap(err, "failed to unmarshal events")
// try new format
var nsEventsList corev1.EventList
if err := json.Unmarshal(files[nsEventsFilePath], &nsEventsList); err != nil {
return nil, errors.Wrap(err, "failed to unmarshal events")
}
nsEvents = nsEventsList.Items
}
podEvents := []corev1.Event{}
for _, event := range nsEvents {
@@ -44,7 +68,12 @@ func GetPodDetails(bundleArchive string, podNamespace string, podName string) (*
var podsArr []corev1.Pod
if err := json.Unmarshal(files[nsPodsFilePath], &podsArr); err != nil {
return nil, errors.Wrap(err, "failed to unmarshal pods")
// try new format
var podsList corev1.PodList
if err := json.Unmarshal(files[nsPodsFilePath], &podsList); err != nil {
return nil, errors.Wrap(err, "failed to unmarshal pods")
}
podsArr = podsList.Items
}
for _, pod := range podsArr {
if pod.Name == podName && pod.Namespace == podNamespace {
@@ -53,14 +82,14 @@ func GetPodDetails(bundleArchive string, podNamespace string, podName string) (*
for _, i := range pod.Spec.InitContainers {
podDetails.PodContainers = append(podDetails.PodContainers, types.PodContainer{
Name: i.Name,
LogsFilePath: filepath.Join("cluster-resources", "pods", "logs", pod.Namespace, pod.Name, fmt.Sprintf("%s.log", i.Name)),
LogsFilePath: getContainerLogsFilePath(pod.Namespace, pod.Name, i.Name),
IsInitContainer: true,
})
}
for _, c := range pod.Spec.Containers {
podDetails.PodContainers = append(podDetails.PodContainers, types.PodContainer{
Name: c.Name,
LogsFilePath: filepath.Join("cluster-resources", "pods", "logs", pod.Namespace, pod.Name, fmt.Sprintf("%s.log", c.Name)),
LogsFilePath: getContainerLogsFilePath(pod.Namespace, pod.Name, c.Name),
IsInitContainer: false,
})
}
+230
View File
@@ -0,0 +1,230 @@
package supportbundle
import (
"fmt"
"testing"
types "github.com/replicatedhq/troubleshoot/pkg/supportbundle/types"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
func Test_getPodDetails(t *testing.T) {
podNamespace := "default"
podName := "hello-27436131-pxhk9"
podsFileContentOldFormat := `[
{
"kind": "Pod",
"apiVersion": "v1",
"metadata": {
"name": "hello-27436131-pxhk9",
"namespace": "default"
},
"spec": {
"initContainers": [
{
"name": "remove-lost-found"
}
],
"containers": [
{
"name": "hello"
}
]
}
}
]`
podsFileContentNewFormat := fmt.Sprintf(`{
"kind": "PodList",
"apiVersion": "v1",
"metadata": {
"resourceVersion": "5389414"
},
"items": %s
}`, podsFileContentOldFormat)
eventsFileContentOldFormat := `[
{
"kind": "Event",
"apiVersion": "v1",
"metadata": {
"name": "example-nginx.16d85cebe302a9b1",
"namespace": "default"
},
"involvedObject": {
"kind": "Deployment",
"namespace": "default",
"name": "example-nginx"
}
},
{
"kind": "Event",
"apiVersion": "v1",
"metadata": {
"name": "hello-27436131-pxhk9.16d85cf27380b4fa",
"namespace": "default"
},
"involvedObject": {
"kind": "Pod",
"namespace": "default",
"name": "hello-27436131-pxhk9"
}
}
]`
eventsFileContentNewFormat := fmt.Sprintf(`{
"kind": "EventList",
"apiVersion": "v1",
"metadata": {
"resourceVersion": "5389515"
},
"items": %s
}`, eventsFileContentOldFormat)
removeLostAndFoundInitContainerLogs := `some logs here`
helloContainerLogs := `Tue Mar 1 20:53:00 UTC 2022 Hello`
tests := []struct {
name string
files map[string][]byte
expect *types.PodDetails
}{
{
name: "old support bundle format",
files: map[string][]byte{
getPodsFilePath(podNamespace): []byte(podsFileContentOldFormat),
getEventsFilePath(podNamespace): []byte(eventsFileContentOldFormat),
getContainerLogsFilePath(podNamespace, podName, "remove-lost-found"): []byte(removeLostAndFoundInitContainerLogs),
getContainerLogsFilePath(podNamespace, podName, "hello"): []byte(helloContainerLogs),
},
expect: &types.PodDetails{
PodDefinition: corev1.Pod{
TypeMeta: metav1.TypeMeta{
APIVersion: "v1",
Kind: "Pod",
},
ObjectMeta: metav1.ObjectMeta{
Name: podName,
Namespace: podNamespace,
},
Spec: corev1.PodSpec{
InitContainers: []corev1.Container{
{
Name: "remove-lost-found",
},
},
Containers: []corev1.Container{
{
Name: "hello",
},
},
},
},
PodEvents: []corev1.Event{
{
TypeMeta: metav1.TypeMeta{
APIVersion: "v1",
Kind: "Event",
},
ObjectMeta: metav1.ObjectMeta{
Name: "hello-27436131-pxhk9.16d85cf27380b4fa",
Namespace: podNamespace,
},
InvolvedObject: corev1.ObjectReference{
Kind: "Pod",
Namespace: podNamespace,
Name: podName,
},
},
},
PodContainers: []types.PodContainer{
{
Name: "remove-lost-found",
LogsFilePath: getContainerLogsFilePath(podNamespace, podName, "remove-lost-found"),
IsInitContainer: true,
},
{
Name: "hello",
LogsFilePath: getContainerLogsFilePath(podNamespace, podName, "hello"),
},
},
},
},
{
name: "new support bundle format",
files: map[string][]byte{
getPodsFilePath(podNamespace): []byte(podsFileContentNewFormat),
getEventsFilePath(podNamespace): []byte(eventsFileContentNewFormat),
getContainerLogsFilePath(podNamespace, podName, "remove-lost-found"): []byte(removeLostAndFoundInitContainerLogs),
getContainerLogsFilePath(podNamespace, podName, "hello"): []byte(helloContainerLogs),
},
expect: &types.PodDetails{
PodDefinition: corev1.Pod{
TypeMeta: metav1.TypeMeta{
APIVersion: "v1",
Kind: "Pod",
},
ObjectMeta: metav1.ObjectMeta{
Name: podName,
Namespace: podNamespace,
},
Spec: corev1.PodSpec{
InitContainers: []corev1.Container{
{
Name: "remove-lost-found",
},
},
Containers: []corev1.Container{
{
Name: "hello",
},
},
},
},
PodEvents: []corev1.Event{
{
TypeMeta: metav1.TypeMeta{
APIVersion: "v1",
Kind: "Event",
},
ObjectMeta: metav1.ObjectMeta{
Name: "hello-27436131-pxhk9.16d85cf27380b4fa",
Namespace: podNamespace,
},
InvolvedObject: corev1.ObjectReference{
Kind: "Pod",
Namespace: podNamespace,
Name: podName,
},
},
},
PodContainers: []types.PodContainer{
{
Name: "remove-lost-found",
LogsFilePath: getContainerLogsFilePath(podNamespace, podName, "remove-lost-found"),
IsInitContainer: true,
},
{
Name: "hello",
LogsFilePath: getContainerLogsFilePath(podNamespace, podName, "hello"),
},
},
},
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
req := require.New(t)
actual, err := getPodDetailsFromFiles(test.files, podNamespace, podName)
req.NoError(err)
assert.Equal(t, test.expect, actual)
})
}
}
+16 -3
View File
@@ -15,6 +15,7 @@ import (
analyzer "github.com/replicatedhq/troubleshoot/pkg/analyze"
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
"github.com/replicatedhq/troubleshoot/pkg/collect"
"github.com/replicatedhq/troubleshoot/pkg/convert"
"k8s.io/client-go/rest"
)
@@ -26,6 +27,7 @@ type SupportBundleCreateOpts struct {
Namespace string
ProgressChan chan interface{}
SinceTime *time.Time
OutputPath string
Redact bool
FromCLI bool
}
@@ -57,9 +59,20 @@ func CollectSupportBundleFromSpec(spec *troubleshootv1beta2.SupportBundleSpec, a
}
defer os.RemoveAll(tmpDir)
basename := fmt.Sprintf("support-bundle-%s", time.Now().Format("2006-01-02T15_04_05"))
if !opts.FromCLI {
basename = filepath.Join(os.TempDir(), basename)
basename := ""
if opts.OutputPath != "" {
// use override output path
overridePath, err := convert.ValidateOutputPath(opts.OutputPath)
if err != nil {
return nil, errors.Wrap(err, "override output file path")
}
basename = strings.TrimSuffix(overridePath, ".tar.gz")
} else {
// use default output path
basename = fmt.Sprintf("support-bundle-%s", time.Now().Format("2006-01-02T15_04_05"))
if !opts.FromCLI {
basename = filepath.Join(os.TempDir(), basename)
}
}
filename, err := findFileName(basename, "tar.gz")
+96 -5
View File
@@ -90,13 +90,15 @@
}
}
}
},
"strict": {
"oneOf": [{"type": "string"},{"type": "boolean"}]
}
}
},
"clusterPodStatuses": {
"type": "object",
"required": [
"namespaces",
"outcomes"
],
"properties": {
@@ -161,6 +163,9 @@
}
}
}
},
"strict": {
"oneOf": [{"type": "string"},{"type": "boolean"}]
}
}
},
@@ -225,6 +230,9 @@
}
}
}
},
"strict": {
"oneOf": [{"type": "string"},{"type": "boolean"}]
}
}
},
@@ -300,6 +308,9 @@
}
}
}
},
"strict": {
"oneOf": [{"type": "string"},{"type": "boolean"}]
}
}
},
@@ -364,6 +375,9 @@
}
}
}
},
"strict": {
"oneOf": [{"type": "string"},{"type": "boolean"}]
}
}
},
@@ -432,6 +446,9 @@
}
}
}
},
"strict": {
"oneOf": [{"type": "string"},{"type": "boolean"}]
}
}
},
@@ -439,7 +456,6 @@
"type": "object",
"required": [
"name",
"namespace",
"outcomes"
],
"properties": {
@@ -455,6 +471,12 @@
"namespace": {
"type": "string"
},
"namespaces": {
"type": "array",
"items": {
"type": "string"
}
},
"outcomes": {
"type": "array",
"items": {
@@ -504,6 +526,9 @@
}
}
}
},
"strict": {
"oneOf": [{"type": "string"},{"type": "boolean"}]
}
}
},
@@ -568,6 +593,9 @@
}
}
}
},
"strict": {
"oneOf": [{"type": "string"},{"type": "boolean"}]
}
}
},
@@ -636,6 +664,9 @@
},
"registryName": {
"type": "string"
},
"strict": {
"oneOf": [{"type": "string"},{"type": "boolean"}]
}
}
},
@@ -708,6 +739,9 @@
}
}
}
},
"strict": {
"oneOf": [{"type": "string"},{"type": "boolean"}]
}
}
},
@@ -715,7 +749,6 @@
"type": "object",
"required": [
"name",
"namespace",
"outcomes"
],
"properties": {
@@ -731,6 +764,12 @@
"namespace": {
"type": "string"
},
"namespaces": {
"type": "array",
"items": {
"type": "string"
}
},
"outcomes": {
"type": "array",
"items": {
@@ -780,6 +819,9 @@
}
}
}
},
"strict": {
"oneOf": [{"type": "string"},{"type": "boolean"}]
}
}
},
@@ -851,6 +893,9 @@
}
}
}
},
"strict": {
"oneOf": [{"type": "string"},{"type": "boolean"}]
}
}
},
@@ -922,6 +967,9 @@
}
}
}
},
"strict": {
"oneOf": [{"type": "string"},{"type": "boolean"}]
}
}
},
@@ -1026,6 +1074,9 @@
}
}
}
},
"strict": {
"oneOf": [{"type": "string"},{"type": "boolean"}]
}
}
},
@@ -1097,6 +1148,9 @@
}
}
}
},
"strict": {
"oneOf": [{"type": "string"},{"type": "boolean"}]
}
}
},
@@ -1168,6 +1222,9 @@
}
}
}
},
"strict": {
"oneOf": [{"type": "string"},{"type": "boolean"}]
}
}
},
@@ -1236,6 +1293,9 @@
}
}
}
},
"strict": {
"oneOf": [{"type": "string"},{"type": "boolean"}]
}
}
},
@@ -1243,7 +1303,6 @@
"type": "object",
"required": [
"name",
"namespace",
"outcomes",
"selector"
],
@@ -1260,6 +1319,12 @@
"namespace": {
"type": "string"
},
"namespaces": {
"type": "array",
"items": {
"type": "string"
}
},
"outcomes": {
"type": "array",
"items": {
@@ -1315,6 +1380,9 @@
"items": {
"type": "string"
}
},
"strict": {
"oneOf": [{"type": "string"},{"type": "boolean"}]
}
}
},
@@ -1390,6 +1458,9 @@
},
"secretName": {
"type": "string"
},
"strict": {
"oneOf": [{"type": "string"},{"type": "boolean"}]
}
}
},
@@ -1397,7 +1468,6 @@
"type": "object",
"required": [
"name",
"namespace",
"outcomes"
],
"properties": {
@@ -1413,6 +1483,12 @@
"namespace": {
"type": "string"
},
"namespaces": {
"type": "array",
"items": {
"type": "string"
}
},
"outcomes": {
"type": "array",
"items": {
@@ -1462,6 +1538,9 @@
}
}
}
},
"strict": {
"oneOf": [{"type": "string"},{"type": "boolean"}]
}
}
},
@@ -1530,6 +1609,9 @@
},
"storageClassName": {
"type": "string"
},
"strict": {
"oneOf": [{"type": "string"},{"type": "boolean"}]
}
}
},
@@ -1594,6 +1676,9 @@
}
}
}
},
"strict": {
"oneOf": [{"type": "string"},{"type": "boolean"}]
}
}
},
@@ -1673,6 +1758,9 @@
},
"regexGroups": {
"type": "string"
},
"strict": {
"oneOf": [{"type": "string"},{"type": "boolean"}]
}
}
},
@@ -1690,6 +1778,9 @@
},
"reportFileGlob": {
"type": "string"
},
"strict": {
"oneOf": [{"type": "string"},{"type": "boolean"}]
}
}
}
+96 -5
View File
@@ -90,13 +90,15 @@
}
}
}
},
"strict": {
"oneOf": [{"type": "string"},{"type": "boolean"}]
}
}
},
"clusterPodStatuses": {
"type": "object",
"required": [
"namespaces",
"outcomes"
],
"properties": {
@@ -161,6 +163,9 @@
}
}
}
},
"strict": {
"oneOf": [{"type": "string"},{"type": "boolean"}]
}
}
},
@@ -225,6 +230,9 @@
}
}
}
},
"strict": {
"oneOf": [{"type": "string"},{"type": "boolean"}]
}
}
},
@@ -300,6 +308,9 @@
}
}
}
},
"strict": {
"oneOf": [{"type": "string"},{"type": "boolean"}]
}
}
},
@@ -364,6 +375,9 @@
}
}
}
},
"strict": {
"oneOf": [{"type": "string"},{"type": "boolean"}]
}
}
},
@@ -432,6 +446,9 @@
}
}
}
},
"strict": {
"oneOf": [{"type": "string"},{"type": "boolean"}]
}
}
},
@@ -439,7 +456,6 @@
"type": "object",
"required": [
"name",
"namespace",
"outcomes"
],
"properties": {
@@ -455,6 +471,12 @@
"namespace": {
"type": "string"
},
"namespaces": {
"type": "array",
"items": {
"type": "string"
}
},
"outcomes": {
"type": "array",
"items": {
@@ -504,6 +526,9 @@
}
}
}
},
"strict": {
"oneOf": [{"type": "string"},{"type": "boolean"}]
}
}
},
@@ -568,6 +593,9 @@
}
}
}
},
"strict": {
"oneOf": [{"type": "string"},{"type": "boolean"}]
}
}
},
@@ -636,6 +664,9 @@
},
"registryName": {
"type": "string"
},
"strict": {
"oneOf": [{"type": "string"},{"type": "boolean"}]
}
}
},
@@ -708,6 +739,9 @@
}
}
}
},
"strict": {
"oneOf": [{"type": "string"},{"type": "boolean"}]
}
}
},
@@ -715,7 +749,6 @@
"type": "object",
"required": [
"name",
"namespace",
"outcomes"
],
"properties": {
@@ -731,6 +764,12 @@
"namespace": {
"type": "string"
},
"namespaces": {
"type": "array",
"items": {
"type": "string"
}
},
"outcomes": {
"type": "array",
"items": {
@@ -780,6 +819,9 @@
}
}
}
},
"strict": {
"oneOf": [{"type": "string"},{"type": "boolean"}]
}
}
},
@@ -851,6 +893,9 @@
}
}
}
},
"strict": {
"oneOf": [{"type": "string"},{"type": "boolean"}]
}
}
},
@@ -922,6 +967,9 @@
}
}
}
},
"strict": {
"oneOf": [{"type": "string"},{"type": "boolean"}]
}
}
},
@@ -1026,6 +1074,9 @@
}
}
}
},
"strict": {
"oneOf": [{"type": "string"},{"type": "boolean"}]
}
}
},
@@ -1097,6 +1148,9 @@
}
}
}
},
"strict": {
"oneOf": [{"type": "string"},{"type": "boolean"}]
}
}
},
@@ -1168,6 +1222,9 @@
}
}
}
},
"strict": {
"oneOf": [{"type": "string"},{"type": "boolean"}]
}
}
},
@@ -1236,6 +1293,9 @@
}
}
}
},
"strict": {
"oneOf": [{"type": "string"},{"type": "boolean"}]
}
}
},
@@ -1243,7 +1303,6 @@
"type": "object",
"required": [
"name",
"namespace",
"outcomes",
"selector"
],
@@ -1260,6 +1319,12 @@
"namespace": {
"type": "string"
},
"namespaces": {
"type": "array",
"items": {
"type": "string"
}
},
"outcomes": {
"type": "array",
"items": {
@@ -1315,6 +1380,9 @@
"items": {
"type": "string"
}
},
"strict": {
"oneOf": [{"type": "string"},{"type": "boolean"}]
}
}
},
@@ -1390,6 +1458,9 @@
},
"secretName": {
"type": "string"
},
"strict": {
"oneOf": [{"type": "string"},{"type": "boolean"}]
}
}
},
@@ -1397,7 +1468,6 @@
"type": "object",
"required": [
"name",
"namespace",
"outcomes"
],
"properties": {
@@ -1413,6 +1483,12 @@
"namespace": {
"type": "string"
},
"namespaces": {
"type": "array",
"items": {
"type": "string"
}
},
"outcomes": {
"type": "array",
"items": {
@@ -1462,6 +1538,9 @@
}
}
}
},
"strict": {
"oneOf": [{"type": "string"},{"type": "boolean"}]
}
}
},
@@ -1530,6 +1609,9 @@
},
"storageClassName": {
"type": "string"
},
"strict": {
"oneOf": [{"type": "string"},{"type": "boolean"}]
}
}
},
@@ -1594,6 +1676,9 @@
}
}
}
},
"strict": {
"oneOf": [{"type": "string"},{"type": "boolean"}]
}
}
},
@@ -1673,6 +1758,9 @@
},
"regexGroups": {
"type": "string"
},
"strict": {
"oneOf": [{"type": "string"},{"type": "boolean"}]
}
}
},
@@ -1690,6 +1778,9 @@
},
"reportFileGlob": {
"type": "string"
},
"strict": {
"oneOf": [{"type": "string"},{"type": "boolean"}]
}
}
}
@@ -136,13 +136,15 @@
}
}
}
},
"strict": {
"oneOf": [{"type": "string"},{"type": "boolean"}]
}
}
},
"clusterPodStatuses": {
"type": "object",
"required": [
"namespaces",
"outcomes"
],
"properties": {
@@ -207,6 +209,9 @@
}
}
}
},
"strict": {
"oneOf": [{"type": "string"},{"type": "boolean"}]
}
}
},
@@ -271,6 +276,9 @@
}
}
}
},
"strict": {
"oneOf": [{"type": "string"},{"type": "boolean"}]
}
}
},
@@ -346,6 +354,9 @@
}
}
}
},
"strict": {
"oneOf": [{"type": "string"},{"type": "boolean"}]
}
}
},
@@ -410,6 +421,9 @@
}
}
}
},
"strict": {
"oneOf": [{"type": "string"},{"type": "boolean"}]
}
}
},
@@ -478,6 +492,9 @@
}
}
}
},
"strict": {
"oneOf": [{"type": "string"},{"type": "boolean"}]
}
}
},
@@ -485,7 +502,6 @@
"type": "object",
"required": [
"name",
"namespace",
"outcomes"
],
"properties": {
@@ -501,6 +517,12 @@
"namespace": {
"type": "string"
},
"namespaces": {
"type": "array",
"items": {
"type": "string"
}
},
"outcomes": {
"type": "array",
"items": {
@@ -550,6 +572,9 @@
}
}
}
},
"strict": {
"oneOf": [{"type": "string"},{"type": "boolean"}]
}
}
},
@@ -614,6 +639,9 @@
}
}
}
},
"strict": {
"oneOf": [{"type": "string"},{"type": "boolean"}]
}
}
},
@@ -682,6 +710,9 @@
},
"registryName": {
"type": "string"
},
"strict": {
"oneOf": [{"type": "string"},{"type": "boolean"}]
}
}
},
@@ -754,6 +785,9 @@
}
}
}
},
"strict": {
"oneOf": [{"type": "string"},{"type": "boolean"}]
}
}
},
@@ -761,7 +795,6 @@
"type": "object",
"required": [
"name",
"namespace",
"outcomes"
],
"properties": {
@@ -777,6 +810,12 @@
"namespace": {
"type": "string"
},
"namespaces": {
"type": "array",
"items": {
"type": "string"
}
},
"outcomes": {
"type": "array",
"items": {
@@ -826,6 +865,9 @@
}
}
}
},
"strict": {
"oneOf": [{"type": "string"},{"type": "boolean"}]
}
}
},
@@ -897,6 +939,9 @@
}
}
}
},
"strict": {
"oneOf": [{"type": "string"},{"type": "boolean"}]
}
}
},
@@ -968,6 +1013,9 @@
}
}
}
},
"strict": {
"oneOf": [{"type": "string"},{"type": "boolean"}]
}
}
},
@@ -1072,6 +1120,9 @@
}
}
}
},
"strict": {
"oneOf": [{"type": "string"},{"type": "boolean"}]
}
}
},
@@ -1143,6 +1194,9 @@
}
}
}
},
"strict": {
"oneOf": [{"type": "string"},{"type": "boolean"}]
}
}
},
@@ -1214,6 +1268,9 @@
}
}
}
},
"strict": {
"oneOf": [{"type": "string"},{"type": "boolean"}]
}
}
},
@@ -1282,6 +1339,9 @@
}
}
}
},
"strict": {
"oneOf": [{"type": "string"},{"type": "boolean"}]
}
}
},
@@ -1289,7 +1349,6 @@
"type": "object",
"required": [
"name",
"namespace",
"outcomes",
"selector"
],
@@ -1306,6 +1365,12 @@
"namespace": {
"type": "string"
},
"namespaces": {
"type": "array",
"items": {
"type": "string"
}
},
"outcomes": {
"type": "array",
"items": {
@@ -1361,6 +1426,9 @@
"items": {
"type": "string"
}
},
"strict": {
"oneOf": [{"type": "string"},{"type": "boolean"}]
}
}
},
@@ -1436,6 +1504,9 @@
},
"secretName": {
"type": "string"
},
"strict": {
"oneOf": [{"type": "string"},{"type": "boolean"}]
}
}
},
@@ -1443,7 +1514,6 @@
"type": "object",
"required": [
"name",
"namespace",
"outcomes"
],
"properties": {
@@ -1459,6 +1529,12 @@
"namespace": {
"type": "string"
},
"namespaces": {
"type": "array",
"items": {
"type": "string"
}
},
"outcomes": {
"type": "array",
"items": {
@@ -1508,6 +1584,9 @@
}
}
}
},
"strict": {
"oneOf": [{"type": "string"},{"type": "boolean"}]
}
}
},
@@ -1576,6 +1655,9 @@
},
"storageClassName": {
"type": "string"
},
"strict": {
"oneOf": [{"type": "string"},{"type": "boolean"}]
}
}
},
@@ -1640,6 +1722,9 @@
}
}
}
},
"strict": {
"oneOf": [{"type": "string"},{"type": "boolean"}]
}
}
},
@@ -1719,6 +1804,9 @@
},
"regexGroups": {
"type": "string"
},
"strict": {
"oneOf": [{"type": "string"},{"type": "boolean"}]
}
}
},
@@ -1736,6 +1824,9 @@
},
"reportFileGlob": {
"type": "string"
},
"strict": {
"oneOf": [{"type": "string"},{"type": "boolean"}]
}
}
}