Skip to content

Commit

Permalink
feat(analyzer): allow templating for Node Resources Analyzer (#1605)
Browse files Browse the repository at this point in the history
* feat(analyzer): allow templating for Node Resources Analyzer
  • Loading branch information
DexterYan authored Sep 1, 2024
1 parent e685cb9 commit 0a2c9c7
Show file tree
Hide file tree
Showing 9 changed files with 234 additions and 23 deletions.
4 changes: 2 additions & 2 deletions config/crds/troubleshoot.sh_analyzers.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -1166,10 +1166,10 @@ spec:
type: BoolString
filters:
properties:
architecture:
type: string
cpuAllocatable:
type: string
cpuArchitecture:
type: string
cpuCapacity:
type: string
ephemeralStorageAllocatable:
Expand Down
4 changes: 2 additions & 2 deletions config/crds/troubleshoot.sh_preflights.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -1166,10 +1166,10 @@ spec:
type: BoolString
filters:
properties:
architecture:
type: string
cpuAllocatable:
type: string
cpuArchitecture:
type: string
cpuCapacity:
type: string
ephemeralStorageAllocatable:
Expand Down
4 changes: 2 additions & 2 deletions config/crds/troubleshoot.sh_supportbundles.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -1197,10 +1197,10 @@ spec:
type: BoolString
filters:
properties:
architecture:
type: string
cpuAllocatable:
type: string
cpuArchitecture:
type: string
cpuCapacity:
type: string
ephemeralStorageAllocatable:
Expand Down
32 changes: 25 additions & 7 deletions pkg/analyze/node_resources.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"strings"

"github.com/pkg/errors"
util "github.com/replicatedhq/troubleshoot/internal/util"
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
"github.com/replicatedhq/troubleshoot/pkg/constants"
corev1 "k8s.io/api/core/v1"
Expand All @@ -18,12 +19,16 @@ type AnalyzeNodeResources struct {
analyzer *troubleshootv1beta2.NodeResources
}

type NodeResourceMsg struct {
*troubleshootv1beta2.NodeResourceFilters
NodeCount int
}

func (a *AnalyzeNodeResources) Title() string {
title := a.analyzer.CheckName
if title == "" {
title = "Node Resources"
}

return title
}

Expand All @@ -41,6 +46,7 @@ func (a *AnalyzeNodeResources) Analyze(getFile getCollectedFileContents, findFil
}

func (a *AnalyzeNodeResources) analyzeNodeResources(analyzer *troubleshootv1beta2.NodeResources, getCollectedFileContents func(string) ([]byte, error)) (*AnalyzeResult, error) {

collected, err := getCollectedFileContents(fmt.Sprintf("%s/%s.json", constants.CLUSTER_RESOURCES_DIR, constants.CLUSTER_RESOURCES_NODES))
if err != nil {
return nil, errors.Wrap(err, "failed to get contents of nodes.json")
Expand Down Expand Up @@ -70,6 +76,10 @@ func (a *AnalyzeNodeResources) analyzeNodeResources(analyzer *troubleshootv1beta
IconURI: "https://troubleshoot.sh/images/analyzer-icons/node-resources.svg?w=16&h=18",
}

nodeMsg := NodeResourceMsg{
analyzer.Filters, len(matchingNodes),
}

for _, outcome := range analyzer.Outcomes {
if outcome.Fail != nil {
isWhenMatch, err := compareNodeResourceConditionalToActual(outcome.Fail.When, matchingNodes)
Expand All @@ -79,9 +89,11 @@ func (a *AnalyzeNodeResources) analyzeNodeResources(analyzer *troubleshootv1beta

if isWhenMatch {
result.IsFail = true
result.Message = outcome.Fail.Message
result.Message, err = util.RenderTemplate(outcome.Fail.Message, nodeMsg)
if err != nil {
return nil, errors.Wrap(err, "failed to render message template")
}
result.URI = outcome.Fail.URI

return result, nil
}
} else if outcome.Warn != nil {
Expand All @@ -92,7 +104,10 @@ func (a *AnalyzeNodeResources) analyzeNodeResources(analyzer *troubleshootv1beta

if isWhenMatch {
result.IsWarn = true
result.Message = outcome.Warn.Message
result.Message, err = util.RenderTemplate(outcome.Warn.Message, nodeMsg)
if err != nil {
return nil, errors.Wrap(err, "failed to render message template")
}
result.URI = outcome.Warn.URI

return result, nil
Expand All @@ -105,7 +120,10 @@ func (a *AnalyzeNodeResources) analyzeNodeResources(analyzer *troubleshootv1beta

if isWhenMatch {
result.IsPass = true
result.Message = outcome.Pass.Message
result.Message, err = util.RenderTemplate(outcome.Pass.Message, nodeMsg)
if err != nil {
return nil, errors.Wrap(err, "failed to render message template")
}
result.URI = outcome.Pass.URI

return result, nil
Expand Down Expand Up @@ -373,8 +391,8 @@ func nodeMatchesFilters(node corev1.Node, filters *troubleshootv1beta2.NodeResou
}
}

if filters.Architecture != "" {
parsed := filters.Architecture
if filters.CPUArchitecture != "" {
parsed := filters.CPUArchitecture

if !strings.EqualFold(node.Status.NodeInfo.Architecture, parsed) {
return false, nil
Expand Down
199 changes: 196 additions & 3 deletions pkg/analyze/node_resources_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -445,15 +445,15 @@ func Test_nodeMatchesFilters(t *testing.T) {
name: "true when cpu arch is amd64",
node: node,
filters: &troubleshootv1beta2.NodeResourceFilters{
Architecture: "amd64",
CPUArchitecture: "amd64",
},
expectResult: true,
},
{
name: "false when cpu arch is not amd64",
node: node,
filters: &troubleshootv1beta2.NodeResourceFilters{
Architecture: "armhf",
CPUArchitecture: "armhf",
},
expectResult: false,
},
Expand Down Expand Up @@ -751,7 +751,44 @@ func Test_analyzeNodeResources(t *testing.T) {
},
},
Filters: &troubleshootv1beta2.NodeResourceFilters{
Architecture: "amd64",
CPUArchitecture: "amd64",
},
},
want: &AnalyzeResult{
IsPass: true,
IsFail: false,
IsWarn: false,
Title: "amd64-exists",
Message: "There is a node with at least 8 cores on amd64 arch",
URI: "",
IconKey: "kubernetes_node_resources",
IconURI: "https://troubleshoot.sh/images/analyzer-icons/node-resources.svg?w=16&h=18",
},
},
{
name: "at least 8 cores on amd64 with message templating", // filter for a node with enough amd64 cores with message templating
analyzer: &troubleshootv1beta2.NodeResources{
AnalyzeMeta: troubleshootv1beta2.AnalyzeMeta{
CheckName: "amd64-exists",
},
Outcomes: []*troubleshootv1beta2.Outcome{
{
Fail: &troubleshootv1beta2.SingleOutcome{
When: "max(cpuCapacity) < 8",
Message: "There isn't a node with 8 or more cores on {{ .CPUArchitecture }} arch",
URI: "",
},
},
{
Pass: &troubleshootv1beta2.SingleOutcome{
When: "max(cpuCapacity) >= 8",
Message: "There is a node with at least 8 cores on {{ .CPUArchitecture }} arch",
URI: "",
},
},
},
Filters: &troubleshootv1beta2.NodeResourceFilters{
CPUArchitecture: "amd64",
},
},
want: &AnalyzeResult{
Expand Down Expand Up @@ -898,6 +935,162 @@ func Test_analyzeNodeResources(t *testing.T) {
IconURI: "https://troubleshoot.sh/images/analyzer-icons/node-resources.svg?w=16&h=18",
},
},
{
name: "8 cores in nodes with at least 8gb of ram with message templating", // validate that filtering based on memory capacity works with message templating
analyzer: &troubleshootv1beta2.NodeResources{
AnalyzeMeta: troubleshootv1beta2.AnalyzeMeta{
CheckName: "memory filter",
},
Outcomes: []*troubleshootv1beta2.Outcome{
{
Fail: &troubleshootv1beta2.SingleOutcome{
When: "sum(cpuCapacity) < 8",
Message: "less than 8 CPUs in nodes with {{ .MemoryCapacity }} of ram",
URI: "",
},
},
{
Warn: &troubleshootv1beta2.SingleOutcome{
When: "sum(cpuCapacity) = 8",
Message: "exactly 8 CPUs total in nodes with {{ .MemoryCapacity }} of ram",
URI: "",
},
},
{
Pass: &troubleshootv1beta2.SingleOutcome{
When: "sum(cpuCapacity) > 8",
Message: "more than 8 CPUs in nodes with {{ .MemoryCapacity }} of ram",
URI: "",
},
},
},
Filters: &troubleshootv1beta2.NodeResourceFilters{
MemoryCapacity: "8Gi",
},
},
want: &AnalyzeResult{
IsPass: true,
IsFail: false,
IsWarn: false,
Title: "memory filter",
Message: "more than 8 CPUs in nodes with 8Gi of ram",
URI: "",
IconKey: "kubernetes_node_resources",
IconURI: "https://troubleshoot.sh/images/analyzer-icons/node-resources.svg?w=16&h=18",
},
},
{
name: "at least 1 node on arm64 with message templating", // filter for arm64 nodes with message templating
analyzer: &troubleshootv1beta2.NodeResources{
AnalyzeMeta: troubleshootv1beta2.AnalyzeMeta{
CheckName: "arm64-exists",
},
Outcomes: []*troubleshootv1beta2.Outcome{
{
Fail: &troubleshootv1beta2.SingleOutcome{
When: "count() < 3",
Message: "This application requires at least 3 nodes. {{ .CPUArchitecture }}, it should only return the {{ .NodeCount }} nodes that match that filter",
URI: "",
},
},
{
Pass: &troubleshootv1beta2.SingleOutcome{
When: "count() >= 3",
Message: "There are {{ .NodeCount }} nodes that match that filter",
URI: "",
},
},
},
Filters: &troubleshootv1beta2.NodeResourceFilters{
CPUArchitecture: "arm64",
},
},
want: &AnalyzeResult{
IsPass: false,
IsFail: true,
IsWarn: false,
Title: "arm64-exists",
Message: "This application requires at least 3 nodes. arm64, it should only return the 0 nodes that match that filter",
URI: "",
IconKey: "kubernetes_node_resources",
IconURI: "https://troubleshoot.sh/images/analyzer-icons/node-resources.svg?w=16&h=18",
},
},
{
name: "at least 1 node on amd64 with message templating", // filter for amd64 nodes with message templating
analyzer: &troubleshootv1beta2.NodeResources{
AnalyzeMeta: troubleshootv1beta2.AnalyzeMeta{
CheckName: "amd64-exists",
},
Outcomes: []*troubleshootv1beta2.Outcome{
{
Fail: &troubleshootv1beta2.SingleOutcome{
When: "count() < 3",
Message: "This application requires at least 3 nodes. {{ .CPUArchitecture }}, it should only return the {{ .NodeCount }} nodes that match that filter",
URI: "",
},
},
{
Pass: &troubleshootv1beta2.SingleOutcome{
When: "count() >= 3",
Message: "There are {{ .NodeCount }} nodes that match that filter",
URI: "",
},
},
},
Filters: &troubleshootv1beta2.NodeResourceFilters{
CPUArchitecture: "amd64",
},
},
want: &AnalyzeResult{
IsPass: true,
IsFail: false,
IsWarn: false,
Title: "amd64-exists",
Message: "There are 6 nodes that match that filter",
URI: "",
IconKey: "kubernetes_node_resources",
IconURI: "https://troubleshoot.sh/images/analyzer-icons/node-resources.svg?w=16&h=18",
},
},
{
name: "Only 5 Nodes with amd64 and 2 CPU with message templating", // filter for amd64 and 2 CPU nodes with message templating
analyzer: &troubleshootv1beta2.NodeResources{
AnalyzeMeta: troubleshootv1beta2.AnalyzeMeta{
CheckName: "amd64-exists",
},
Outcomes: []*troubleshootv1beta2.Outcome{
{
Fail: &troubleshootv1beta2.SingleOutcome{
When: "count() < 3",
Message: "This application requires at least 3 nodes. {{ .CPUArchitecture }}, it should only return the {{ .NodeCount }} nodes that match that filter",
URI: "",
},
},
{
Pass: &troubleshootv1beta2.SingleOutcome{
When: "count() >= 3",
Message: "There are {{ .NodeCount }} nodes that match that filter {{ .CPUArchitecture }} and {{ .CPUCapacity }} CPU cores",
URI: "",
},
},
},
Filters: &troubleshootv1beta2.NodeResourceFilters{
CPUArchitecture: "amd64",
CPUCapacity: "2",
},
},
want: &AnalyzeResult{
IsPass: true,
IsFail: false,
IsWarn: false,
Title: "amd64-exists",
Message: "There are 5 nodes that match that filter amd64 and 2 CPU cores",
URI: "",
IconKey: "kubernetes_node_resources",
IconURI: "https://troubleshoot.sh/images/analyzer-icons/node-resources.svg?w=16&h=18",
},
},

{
name: "no pass or fail", // validate that the pass message is not always shown
Expand Down
2 changes: 1 addition & 1 deletion pkg/apis/troubleshoot/v1beta2/analyzer_shared.go
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ type NodeResources struct {
}

type NodeResourceFilters struct {
Architecture string `json:"architecture,omitempty" yaml:"cpuArchitecture,omitempty"`
CPUArchitecture string `json:"cpuArchitecture,omitempty" yaml:"cpuArchitecture,omitempty"`
CPUCapacity string `json:"cpuCapacity,omitempty" yaml:"cpuCapacity,omitempty"`
CPUAllocatable string `json:"cpuAllocatable,omitempty" yaml:"cpuAllocatable,omitempty"`
MemoryCapacity string `json:"memoryCapacity,omitempty" yaml:"memoryCapacity,omitempty"`
Expand Down
4 changes: 2 additions & 2 deletions schemas/analyzer-troubleshoot-v1beta2.json
Original file line number Diff line number Diff line change
Expand Up @@ -1755,10 +1755,10 @@
"filters": {
"type": "object",
"properties": {
"architecture": {
"cpuAllocatable": {
"type": "string"
},
"cpuAllocatable": {
"cpuArchitecture": {
"type": "string"
},
"cpuCapacity": {
Expand Down
4 changes: 2 additions & 2 deletions schemas/preflight-troubleshoot-v1beta2.json
Original file line number Diff line number Diff line change
Expand Up @@ -1755,10 +1755,10 @@
"filters": {
"type": "object",
"properties": {
"architecture": {
"cpuAllocatable": {
"type": "string"
},
"cpuAllocatable": {
"cpuArchitecture": {
"type": "string"
},
"cpuCapacity": {
Expand Down
Loading

0 comments on commit 0a2c9c7

Please sign in to comment.