Repository: datreeio/datree
Pull request #64 (2021-06-13 09:17:22)

staging


Patch:
diff --git a/bl/evaluation/evaluator.go b/bl/evaluation/evaluator.go
index 24ce8a7..7766f03 100644
--- a/bl/evaluation/evaluator.go
+++ b/bl/evaluation/evaluator.go
@@ -28,7 +28,6 @@ func New(c CLIClient) *Evaluator {
 type EvaluationResults struct {
 	FileNameRuleMapper map[string]map[int]*Rule
 	Summary            struct {
-		RulesCount       int
 		TotalFailedRules int
 		FilesCount       int
 		TotalPassedCount int
@@ -50,7 +49,7 @@ func (e *Evaluator) CreateEvaluation(cliId string, cliVersion string, k8sVersion
 	return createEvaluationResponse, err
 }
 
-func (e *Evaluator) UpdateFailedYamlValidation(invalidFiles []*validation.InvalidFile, evaluationId int, stopEvaluation bool) error {
+func (e *Evaluator) UpdateFailedYamlValidation(invalidFiles []*validation.InvalidYamlFile, evaluationId int, stopEvaluation bool) error {
 	invalidFilesPaths := []*string{}
 	for _, file := range invalidFiles {
 		invalidFilesPaths = append(invalidFilesPaths, &file.Path)
@@ -63,7 +62,7 @@ func (e *Evaluator) UpdateFailedYamlValidation(invalidFiles []*validation.Invali
 	return err
 }
 
-func (e *Evaluator) UpdateFailedK8sValidation(invalidFiles []*validation.InvalidFile, evaluationId int, stopEvaluation bool) error {
+func (e *Evaluator) UpdateFailedK8sValidation(invalidFiles []*validation.InvalidK8sFile, evaluationId int, stopEvaluation bool) error {
 	invalidFilesPaths := []*string{}
 	for _, file := range invalidFiles {
 		invalidFilesPaths = append(invalidFilesPaths, &file.Path)
@@ -97,7 +96,6 @@ func (e *Evaluator) Evaluate(filesConfigurations []*extractor.FileConfigurations
 func (e *Evaluator) formatEvaluationResults(evaluationResults []*cliClient.EvaluationResult, filesCount int) *EvaluationResults {
 	mapper := make(map[string]map[int]*Rule)
 
-	totalRulesCount := len(evaluationResults)
 	totalFailedCount := 0
 	totalPassedCount := filesCount
 
@@ -122,12 +120,10 @@ func (e *Evaluator) formatEvaluationResults(evaluationResults []*cliClient.Evalu
 	results := &EvaluationResults{
 		FileNameRuleMapper: mapper,
 		Summary: struct {
-			RulesCount       int
 			TotalFailedRules int
 			FilesCount       int
 			TotalPassedCount int
 		}{
-			RulesCount:       totalRulesCount,
 			TotalFailedRules: totalFailedCount,
 			FilesCount:       filesCount,
 			TotalPassedCount: totalPassedCount,
diff --git a/bl/evaluation/printer.go b/bl/evaluation/printer.go
index 11b493d..0fb3942 100644
--- a/bl/evaluation/printer.go
+++ b/bl/evaluation/printer.go
@@ -19,18 +19,20 @@ type Printer interface {
 }
 
 type FormattedOutput struct {
-	Results           *EvaluationResults
+	EvaluationResults *EvaluationResults
 	EvaluationSummary printer.EvaluationSummary
+	InvalidYamlFiles  []*validation.InvalidYamlFile
+	InvalidK8sFiles   []*validation.InvalidK8sFile
 }
 
-func PrintResults(results *EvaluationResults, invalidFiles []*validation.InvalidFile, evaluationSummary printer.EvaluationSummary, loginURL string, outputFormat string, printer Printer, k8sVersion string) error {
+func PrintResults(results *EvaluationResults, invalidYamlFiles []*validation.InvalidYamlFile, invalidK8sFiles []*validation.InvalidK8sFile, evaluationSummary printer.EvaluationSummary, loginURL string, outputFormat string, printer Printer, k8sVersion string) error {
 	switch {
 	case outputFormat == "json":
-		return jsonOutput(&FormattedOutput{Results: results, EvaluationSummary: evaluationSummary})
+		return jsonOutput(&FormattedOutput{EvaluationResults: results, EvaluationSummary: evaluationSummary, InvalidYamlFiles: invalidYamlFiles, InvalidK8sFiles: invalidK8sFiles})
 	case outputFormat == "yaml":
-		return yamlOutput(&FormattedOutput{Results: results, EvaluationSummary: evaluationSummary})
+		return yamlOutput(&FormattedOutput{EvaluationResults: results, EvaluationSummary: evaluationSummary, InvalidYamlFiles: invalidYamlFiles, InvalidK8sFiles: invalidK8sFiles})
 	default:
-		return textOutput(results, invalidFiles, evaluationSummary, loginURL, printer, k8sVersion)
+		return textOutput(results, invalidYamlFiles, invalidK8sFiles, evaluationSummary, loginURL, printer, k8sVersion)
 	}
 }
 
@@ -56,13 +58,13 @@ func yamlOutput(formattedOutput *FormattedOutput) error {
 	return nil
 }
 
-func textOutput(results *EvaluationResults, invalidFiles []*validation.InvalidFile, evaluationSummary printer.EvaluationSummary, url string, printer Printer, k8sVersion string) error {
+func textOutput(results *EvaluationResults, invalidYamlFiles []*validation.InvalidYamlFile, invalidK8sFiles []*validation.InvalidK8sFile, evaluationSummary printer.EvaluationSummary, url string, printer Printer, k8sVersion string) error {
 	pwd, err := os.Getwd()
 	if err != nil {
 		return err
 	}
 
-	warnings, err := parseToPrinterWarnings(results, invalidFiles, pwd, k8sVersion)
+	warnings, err := parseToPrinterWarnings(results, invalidYamlFiles, invalidK8sFiles, pwd, k8sVersion)
 	if err != nil {
 		fmt.Println(err)
 		return err
@@ -79,16 +81,24 @@ func textOutput(results *EvaluationResults, invalidFiles []*validation.InvalidFi
 	return nil
 }
 
-func parseToPrinterWarnings(results *EvaluationResults, invalidFiles []*validation.InvalidFile, pwd string, k8sVersion string) ([]printer.Warning, error) {
+func parseToPrinterWarnings(results *EvaluationResults, invalidYamlFiles []*validation.InvalidYamlFile, invalidK8sFiles []*validation.InvalidK8sFile, pwd string, k8sVersion string) ([]printer.Warning, error) {
 	var warnings = []printer.Warning{}
 
-	for _, invalidFile := range invalidFiles {
+	for _, invalidFile := range invalidYamlFiles {
 		warnings = append(warnings, printer.Warning{
 			Title:   fmt.Sprintf(">>  File: %s\n", invalidFile.Path),
 			Details: []printer.WarningInfo{},
-			ValidationInfo: printer.ValidationInfo{
-				IsValid:          false,
-				ValidationStatus: invalidFile.ValidationStatus,
+			InvalidYamlInfo: printer.InvalidYamlInfo{
+				ValidationErrors: invalidFile.ValidationErrors,
+			},
+		})
+	}
+
+	for _, invalidFile := range invalidK8sFiles {
+		warnings = append(warnings, printer.Warning{
+			Title:   fmt.Sprintf(">>  File: %s\n", invalidFile.Path),
+			Details: []printer.WarningInfo{},
+			InvalidK8sInfo: printer.InvalidK8sInfo{
 				ValidationErrors: invalidFile.ValidationErrors,
 				K8sVersion:       k8sVersion,
 			},
@@ -111,13 +121,10 @@ func parseToPrinterWarnings(results *EvaluationResults, invalidFiles []*validati
 			relativePath, _ := filepath.Rel(pwd, fileName)
 
 			warnings = append(warnings, printer.Warning{
-				Title:   fmt.Sprintf(">>  File: %s\n", relativePath),
-				Details: warningDetails,
-				ValidationInfo: printer.ValidationInfo{
-					IsValid:          true,
-					ValidationErrors: []error{},
-					K8sVersion:       k8sVersion,
-				},
+				Title:           fmt.Sprintf(">>  File: %s\n", relativePath),
+				Details:         warningDetails,
+				InvalidYamlInfo: printer.InvalidYamlInfo{},
+				InvalidK8sInfo:  printer.InvalidK8sInfo{},
 			})
 		}
 	}
@@ -147,27 +154,18 @@ func (t OutputTitle) String() string {
 }
 func parseEvaluationResultsToSummary(results *EvaluationResults, evaluationSummary printer.EvaluationSummary, loginURL string) printer.Summary {
 	configsCount := evaluationSummary.ConfigsCount
-	rulesCount := 0
 	totalRulesEvaluated := 0
 	totalFailedRules := 0
 	totalPassedRules := 0
 
 	if results != nil {
-		rulesCount = results.Summary.RulesCount
-		totalRulesEvaluated = results.Summary.RulesCount * results.Summary.FilesCount
+		totalRulesEvaluated = evaluationSummary.RulesCount * results.Summary.FilesCount
 		totalFailedRules = results.Summary.TotalFailedRules
 		totalPassedRules = totalRulesEvaluated - totalFailedRules
 	}
 
-	var rulesCountRightCol string
-	if rulesCount == 0 {
-		rulesCountRightCol = fmt.Sprint("N/A")
-	} else {
-		rulesCountRightCol = fmt.Sprint(rulesCount)
-	}
-
 	plainRows := []printer.SummaryItem{
-		{LeftCol: EnabledRules.String(), RightCol: rulesCountRightCol, RowIndex: 0},
+		{LeftCol: EnabledRules.String(), RightCol: fmt.Sprint(evaluationSummary.RulesCount), RowIndex: 0},
 		{LeftCol: EvaluatedConfigurations.String(), RightCol: fmt.Sprint(configsCount), RowIndex: 1},
 		{LeftCol: TotalRulesEvaluated.String(), RightCol: fmt.Sprint(totalRulesEvaluated), RowIndex: 2},
 		{LeftCol: SeeAll.String(), RightCol: loginURL, RowIndex: 5},
diff --git a/bl/files/files.go b/bl/files/files.go
index 822e572..b14c011 100644
--- a/bl/files/files.go
+++ b/bl/files/files.go
@@ -23,9 +23,9 @@ func ToAbsolutePath(path string) (string, error) {
 	return "", fmt.Errorf("failed parsing absolute path %s", path)
 }
 
-func ExtractFilesConfigurations(paths []string, concurrency int) (chan *extractor.FileConfigurations, chan *validation.InvalidFile) {
+func ExtractFilesConfigurations(paths []string, concurrency int) (chan *extractor.FileConfigurations, chan *validation.InvalidYamlFile) {
 	filesConfigurationsChan := make(chan *extractor.FileConfigurations, concurrency)
-	invalidFilesChan := make(chan *validation.InvalidFile, concurrency)
+	invalidFilesChan := make(chan *validation.InvalidYamlFile, concurrency)
 
 	go func() {
 		defer func() {
@@ -37,19 +37,19 @@ func ExtractFilesConfigurations(paths []string, concurrency int) (chan *extracto
 
 			absolutePath, err := ToAbsolutePath(path)
 			if err != nil {
-				invalidFilesChan <- &validation.InvalidFile{Path: path, ValidationStatus: validation.InvalidYamlFile, ValidationErrors: []error{err}}
+				invalidFilesChan <- &validation.InvalidYamlFile{Path: path, ValidationErrors: []error{&validation.InvalidYamlError{ErrorMessage: err.Error()}}}
 				continue
 			}
 
 			content, err := extractor.ReadFileContent(absolutePath)
 			if err != nil {
-				invalidFilesChan <- &validation.InvalidFile{Path: absolutePath, ValidationStatus: validation.InvalidYamlFile, ValidationErrors: []error{err}}
+				invalidFilesChan <- &validation.InvalidYamlFile{Path: absolutePath, ValidationErrors: []error{&validation.InvalidYamlError{ErrorMessage: err.Error()}}}
 				continue
 			}
 
 			configurations, err := extractor.ParseYaml(content)
 			if err != nil {
-				invalidFilesChan <- &validation.InvalidFile{Path: absolutePath, ValidationStatus: validation.InvalidYamlFile, ValidationErrors: []error{err}}
+				invalidFilesChan <- &validation.InvalidYamlFile{Path: absolutePath, ValidationErrors: []error{&validation.InvalidYamlError{ErrorMessage: err.Error()}}}
 				continue
 			}
 
diff --git a/bl/validation/k8sValidator.go b/bl/validation/k8sValidator.go
index deafaa9..bfbfcc2 100644
--- a/bl/validation/k8sValidator.go
+++ b/bl/validation/k8sValidator.go
@@ -21,30 +21,21 @@ func New() *K8sValidator {
 	return &K8sValidator{}
 }
 
-type ValidationStatus int
-
-const (
-	InvalidYamlFile ValidationStatus = iota + 1
-	InvalidK8sFile
-)
-
-func (validationStatus ValidationStatus) String() string {
-	return [...]string{"InvalidYamlFile", "InvalidK8sFile"}[validationStatus]
-}
-
 type InvalidFile struct {
 	Path             string
-	ValidationStatus ValidationStatus
 	ValidationErrors []error
 }
+type InvalidYamlFile InvalidFile
+
+type InvalidK8sFile InvalidFile
 
 func (val *K8sValidator) InitClient(k8sVersion string) {
 	val.validationClient = newKubconformValidator(k8sVersion)
 }
 
-func (val *K8sValidator) ValidateResources(filesConfigurationsChan chan *extractor.FileConfigurations, concurrency int) (chan *extractor.FileConfigurations, chan *InvalidFile) {
+func (val *K8sValidator) ValidateResources(filesConfigurationsChan chan *extractor.FileConfigurations, concurrency int) (chan *extractor.FileConfigurations, chan *InvalidK8sFile) {
 	validK8sFilesConfigurationsChan := make(chan *extractor.FileConfigurations, concurrency)
-	invalidK8sFilesChan := make(chan *InvalidFile, concurrency)
+	invalidK8sFilesChan := make(chan *InvalidK8sFile, concurrency)
 
 	go func() {
 		defer func() {
@@ -53,12 +44,10 @@ func (val *K8sValidator) ValidateResources(filesConfigurationsChan chan *extract
 		}()
 
 		for fileConfigurations := range filesConfigurationsChan {
-
 			isValid, validationErrors, err := val.validateResource(fileConfigurations.FileName)
 			if err != nil {
-				invalidK8sFilesChan <- &InvalidFile{
+				invalidK8sFilesChan <- &InvalidK8sFile{
 					Path:             fileConfigurations.FileName,
-					ValidationStatus: InvalidK8sFile,
 					ValidationErrors: []error{err},
 				}
 				continue
@@ -66,9 +55,8 @@ func (val *K8sValidator) ValidateResources(filesConfigurationsChan chan *extract
 			if isValid {
 				validK8sFilesConfigurationsChan <- fileConfigurations
 			} else {
-				invalidK8sFilesChan <- &InvalidFile{
+				invalidK8sFilesChan <- &InvalidK8sFile{
 					Path:             fileConfigurations.FileName,
-					ValidationStatus: InvalidK8sFile,
 					ValidationErrors: validationErrors,
 				}
 			}
@@ -80,7 +68,7 @@ func (val *K8sValidator) ValidateResources(filesConfigurationsChan chan *extract
 func (val *K8sValidator) validateResource(filepath string) (bool, []error, error) {
 	f, err := os.Open(filepath)
 	if err != nil {
-		return false, []error{}, fmt.Errorf("failed opening %s: %s", filepath, err)
+		return false, []error{}, fmt.Errorf("failed opening %s: %s", filepath, &InvalidK8sSchemaError{ErrorMessage: err.Error()})
 	}
 
 	results := val.validationClient.Validate(filepath, f)
@@ -92,7 +80,7 @@ func (val *K8sValidator) validateResource(filepath string) (bool, []error, error
 		// File starts with ---, the parser assumes a first empty resource
 		if res.Status == kubeconformValidator.Invalid || res.Status == kubeconformValidator.Error {
 			isValid = false
-			validationErrors = append(validationErrors, res.Err)
+			validationErrors = append(validationErrors, &InvalidK8sSchemaError{ErrorMessage: res.Err.Error()})
 		}
 	}
 
diff --git a/bl/validation/main.go b/bl/validation/main.go
new file mode 100644
index 0000000..7a8256c
--- /dev/null
+++ b/bl/validation/main.go
@@ -0,0 +1,19 @@
+package validation
+
+import "fmt"
+
+type InvalidK8sSchemaError struct {
+	ErrorMessage string
+}
+
+func (e *InvalidK8sSchemaError) Error() string {
+	return fmt.Sprintf("k8s schema validation error: %s\n", e.ErrorMessage)
+}
+
+type InvalidYamlError struct {
+	ErrorMessage string
+}
+
+func (e *InvalidYamlError) Error() string {
+	return fmt.Sprintf("yaml validation error: %s\n", e.ErrorMessage)
+}
diff --git a/pkg/cliClient/evaluation.go b/pkg/cliClient/evaluation.go
index 3e55344..9121cc2 100644
--- a/pkg/cliClient/evaluation.go
+++ b/pkg/cliClient/evaluation.go
@@ -23,6 +23,7 @@ type CreateEvaluationRequest struct {
 type CreateEvaluationResponse struct {
 	EvaluationId int    `json:"evaluationId"`
 	K8sVersion   string `json:"k8sVersion"`
+	RulesCount   int    `json:"rulesCount"`
 }
 
 func (c *CliClient) CreateEvaluation(request *CreateEvaluationRequest) (*CreateEvaluationResponse, error) {
diff --git a/pkg/printer/printer.go b/pkg/printer/printer.go
index 5e9c759..8f1a49c 100644
--- a/pkg/printer/printer.go
+++ b/pkg/printer/printer.go
@@ -4,7 +4,6 @@ import (
 	"fmt"
 	"os"
 
-	"github.com/datreeio/datree/bl/validation"
 	"github.com/fatih/color"
 	"github.com/olekukonko/tablewriter"
 )
@@ -26,16 +25,18 @@ type WarningInfo struct {
 	Suggestion  string
 }
 
-type ValidationInfo struct {
-	IsValid          bool
-	ValidationStatus validation.ValidationStatus
+type InvalidYamlInfo struct {
+	ValidationErrors []error
+}
+type InvalidK8sInfo struct {
 	ValidationErrors []error
 	K8sVersion       string
 }
 type Warning struct {
-	Title          string
-	Details        []WarningInfo
-	ValidationInfo ValidationInfo
+	Title           string
+	Details         []WarningInfo
+	InvalidYamlInfo InvalidYamlInfo
+	InvalidK8sInfo  InvalidK8sInfo
 }
 
 func (p *Printer) PrintWarnings(warnings []Warning) {
@@ -43,36 +44,32 @@ func (p *Printer) PrintWarnings(warnings []Warning) {
 		p.printInColor(warning.Title, p.theme.Colors.Yellow)
 		fmt.Println()
 
-		if !warning.ValidationInfo.IsValid {
-			if warning.ValidationInfo.ValidationStatus == validation.InvalidYamlFile {
-				p.printInColor("[X] YAML validation\n", p.theme.Colors.White)
-				fmt.Println()
-				for _, validationError := range warning.ValidationInfo.ValidationErrors {
-					validationError := p.theme.Colors.Red.Sprint(validationError.Error())
-					fmt.Printf("%v %v\n", p.theme.Emoji.Error, validationError)
-				}
-				fmt.Println()
-
-				p.printInColor("[?] Kubernetes schema validation\n", p.theme.Colors.White)
-				p.printInColor("[?] Policy check didn’t run for this file\n", p.theme.Colors.White)
-
-				fmt.Println()
+		if len(warning.InvalidYamlInfo.ValidationErrors) > 0 {
+			p.printInColor("[X] YAML validation\n", p.theme.Colors.White)
+			fmt.Println()
+			for _, validationError := range warning.InvalidYamlInfo.ValidationErrors {
+				validationError := p.theme.Colors.Red.Sprint(validationError.Error())
+				fmt.Printf("%v %v\n", p.theme.Emoji.Error, validationError)
+			}
+			fmt.Println()
 
-			} else if warning.ValidationInfo.ValidationStatus == validation.InvalidK8sFile {
-				p.printInColor("[V] YAML validation\n", p.theme.Colors.Green)
-				p.printInColor("[X] Kubernetes schema validation\n", p.theme.Colors.White)
-				fmt.Println()
+			p.printInColor("[?] Kubernetes schema validation didn’t run for this file\n", p.theme.Colors.White)
+			p.printSkippedPolicyCheck()
+			fmt.Println()
+		} else if len(warning.InvalidK8sInfo.ValidationErrors) > 0 {
+			p.printPassedYamlValidation()
+			p.printInColor("[X] Kubernetes schema validation\n", p.theme.Colors.White)
+			fmt.Println()
 
-				for _, validationError := range warning.ValidationInfo.ValidationErrors {
-					validationError := p.theme.Colors.Red.Sprint(validationError.Error())
-					fmt.Printf("%v %v\n", p.theme.Emoji.Error, validationError)
-				}
-				fmt.Println()
-				p.printInColor("[?] Policy check didn’t run for this file\n", p.theme.Colors.White)
-				fmt.Println()
+			for _, validationError := range warning.InvalidK8sInfo.ValidationErrors {
+				validationError := p.theme.Colors.Red.Sprint(validationError.Error())
+				fmt.Printf("%v %v\n", p.theme.Emoji.Error, validationError)
 			}
+			fmt.Println()
+			p.printSkippedPolicyCheck()
+			fmt.Println()
 		} else {
-			p.printInColor("[V] YAML validation\n", p.theme.Colors.Green)
+			p.printPassedYamlValidation()
 			p.printInColor("[V] Kubernetes schema validation\n", p.theme.Colors.Green)
 
 			fmt.Println()
@@ -110,6 +107,7 @@ type Summary struct {
 
 type EvaluationSummary struct {
 	ConfigsCount              int
+	RulesCount                int
 	FilesCount                int
 	PassedYamlValidationCount int
 	PassedK8sValidationCount  int
@@ -181,3 +179,11 @@ func (p *Printer) PrintMessage(messageText string, messageColor string) {
 	colorPrintFn := p.createNewColor(messageColor)
 	p.printInColor(messageText, colorPrintFn)
 }
+
+func (p *Printer) printPassedYamlValidation() {
+	p.printInColor("[V] YAML validation\n", p.theme.Colors.Green)
+}
+
+func (p *Printer) printSkippedPolicyCheck() {
+	p.printInColor("[?] Policy check didn’t run for this file\n", p.theme.Colors.White)
+}


Test patch:
diff --git a/bl/evaluation/evaluator_test.go b/bl/evaluation/evaluator_test.go
index e70efa5..378395f 100644
--- a/bl/evaluation/evaluator_test.go
+++ b/bl/evaluation/evaluator_test.go
@@ -123,6 +123,7 @@ type evaluateArgs struct {
 	validFilesConfigurations []*extractor.FileConfigurations
 	evaluationId             int
 	osInfo                   *OSInfo
+	rulesCount               int
 }
 
 type evaluateExpected struct {
@@ -189,12 +190,10 @@ func request_evaluation_all_valid() *evaluateTestCase {
 			response: &EvaluationResults{
 				FileNameRuleMapper: make(map[string]map[int]*Rule),
 				Summary: struct {
-					RulesCount       int
 					TotalFailedRules int
 					FilesCount       int
 					TotalPassedCount int
 				}{
-					RulesCount:       0,
 					TotalFailedRules: 0,
 					FilesCount:       1,
 					TotalPassedCount: 1,
@@ -253,12 +252,10 @@ func request_evaluation_all_invalid() *evaluateTestCase {
 			response: &EvaluationResults{
 				FileNameRuleMapper: make(map[string]map[int]*Rule),
 				Summary: struct {
-					RulesCount       int
 					TotalFailedRules int
 					FilesCount       int
 					TotalPassedCount int
 				}{
-					RulesCount:       0,
 					TotalFailedRules: 0,
 					FilesCount:       1,
 					TotalPassedCount: 0,
diff --git a/bl/evaluation/printer_test.go b/bl/evaluation/printer_test.go
index d252fd7..d6ac9c5 100644
--- a/bl/evaluation/printer_test.go
+++ b/bl/evaluation/printer_test.go
@@ -28,7 +28,8 @@ func (c *mockPrinter) PrintEvaluationSummary(summary printer.EvaluationSummary,
 
 type printResultsTestCaseArgs struct {
 	results           *EvaluationResults
-	invalidFiles      []*validation.InvalidFile
+	invalidYamlFiles  []*validation.InvalidYamlFile
+	invalidK8sFiles   []*validation.InvalidK8sFile
 	evaluationSummary printer.EvaluationSummary
 	loginURL          string
 	outputFormat      string
@@ -49,12 +50,12 @@ func TestPrintResults(t *testing.T) {
 	}
 	for _, tt := range tests {
 		mockedPrinter := &mockPrinter{}
-		mockedPrinter.On("PrintWarnings", mock.Anything)
+		mockedPrinter.On("PrintWarnings", mock.Anything, mock.Anything, mock.Anything)
 		mockedPrinter.On("PrintSummaryTable", mock.Anything)
 		mockedPrinter.On("PrintEvaluationSummary", mock.Anything, mock.Anything)
 
 		t.Run(tt.name, func(t *testing.T) {
-			_ = PrintResults(tt.args.results, tt.args.invalidFiles, tt.args.evaluationSummary, tt.args.loginURL, tt.args.outputFormat, mockedPrinter, "1.18.0")
+			_ = PrintResults(tt.args.results, tt.args.invalidYamlFiles, tt.args.invalidK8sFiles, tt.args.evaluationSummary, tt.args.loginURL, tt.args.outputFormat, mockedPrinter, "1.18.0")
 
 			if tt.args.outputFormat == "json" {
 				mockedPrinter.AssertNotCalled(t, "PrintWarnings")
@@ -63,8 +64,8 @@ func TestPrintResults(t *testing.T) {
 
 			} else {
 				pwd, _ := os.Getwd()
-				warnings, _ := parseToPrinterWarnings(tt.args.results, tt.args.invalidFiles, pwd, "1.18.0")
-				mockedPrinter.AssertCalled(t, "PrintWarnings", warnings)
+				wagnings, _ := parseToPrinterWarnings(tt.args.results, tt.args.invalidYamlFiles, tt.args.invalidK8sFiles, pwd, "1.18.0")
+				mockedPrinter.AssertCalled(t, "PrintWarnings", wagnings)
 			}
 		})
 	}
@@ -77,22 +78,21 @@ func print_resultst(outputFormat string) *printResultsTestCase {
 			results: &EvaluationResults{
 				FileNameRuleMapper: map[string]map[int]*Rule{},
 				Summary: struct {
-					RulesCount       int
 					TotalFailedRules int
 					FilesCount       int
 					TotalPassedCount int
 				}{
-					RulesCount:       0,
 					TotalFailedRules: 0,
 					FilesCount:       0,
 					TotalPassedCount: 0,
 				},
 			},
-			invalidFiles:      []*validation.InvalidFile{},
+			invalidYamlFiles:  []*validation.InvalidYamlFile{},
+			invalidK8sFiles:   []*validation.InvalidK8sFile{},
 			evaluationSummary: printer.EvaluationSummary{},
 			loginURL:          "login/url",
-			outputFormat: outputFormat,
+			outputFormat:      outputFormat,
 		},
 		expected: nil,
 	}
-}
\ No newline at end of file
+}
diff --git a/bl/validation/k8sValidator_test.go b/bl/validation/k8sValidator_test.go
index 5a80dba..79af51e 100644
--- a/bl/validation/k8sValidator_test.go
+++ b/bl/validation/k8sValidator_test.go
@@ -1,6 +1,7 @@
 package validation
 
 import (
+	"fmt"
 	"io"
 	"testing"
 
@@ -51,7 +52,7 @@ func test_valid_multiple_configurations(t *testing.T) {
 func test_invalid_file(t *testing.T) {
 	validationClient := &mockValidationClient{}
 	validationClient.On("Validate", mock.Anything, mock.Anything).Return([]kubeconformValidator.Result{
-		{Status: kubeconformValidator.Invalid},
+		{Status: kubeconformValidator.Invalid, Err: fmt.Errorf("missing 'apiVersion' key")},
 	})
 	k8sValidator := K8sValidator{
 		validationClient: validationClient,
diff --git a/cmd/test/main.go b/cmd/test/main.go
index 43212c7..db76ab2 100644
--- a/cmd/test/main.go
+++ b/cmd/test/main.go
@@ -19,8 +19,8 @@ import (
 type Evaluator interface {
 	Evaluate(filesConfigurationsChan []*extractor.FileConfigurations, evaluationId int) (*evaluation.EvaluationResults, error)
 	CreateEvaluation(cliId string, cliVersion string, k8sVersion string) (*cliClient.CreateEvaluationResponse, error)
-	UpdateFailedYamlValidation(invalidFiles []*validation.InvalidFile, evaluationId int, stopEvaluation bool) error
-	UpdateFailedK8sValidation(invalidFiles []*validation.InvalidFile, evaluationId int, stopEvaluation bool) error
+	UpdateFailedYamlValidation(invalidFiles []*validation.InvalidYamlFile, evaluationId int, stopEvaluation bool) error
+	UpdateFailedK8sValidation(invalidFiles []*validation.InvalidK8sFile, evaluationId int, stopEvaluation bool) error
 }
 
 type Messager interface {
@@ -28,7 +28,7 @@ type Messager interface {
 }
 
 type K8sValidator interface {
-	ValidateResources(filesConfigurations chan *extractor.FileConfigurations, concurrency int) (chan *extractor.FileConfigurations, chan *validation.InvalidFile)
+	ValidateResources(filesConfigurations chan *extractor.FileConfigurations, concurrency int) (chan *extractor.FileConfigurations, chan *validation.InvalidK8sFile)
 	InitClient(k8sVersion string)
 }
 
@@ -133,7 +133,7 @@ func test(ctx *TestCommandContext, paths []string, flags TestCommandFlags) error
 	ctx.K8sValidator.InitClient(createEvaluationResponse.K8sVersion)
 	validK8sFilesConfigurationsChan, invalidK8sFilesChan := ctx.K8sValidator.ValidateResources(validYamlFilesConfigurationsChan, concurrency)
 
-	invalidYamlFiles := aggregateInvalidFiles(invalidYamlFilesChan)
+	invalidYamlFiles := aggregateInvalidYamlFiles(invalidYamlFilesChan)
 
 	invalidYamlFilesLen := len(invalidYamlFiles)
 
@@ -144,7 +144,7 @@ func test(ctx *TestCommandContext, paths []string, flags TestCommandFlags) error
 		return err
 	}
 
-	invalidK8sFiles := aggregateInvalidFiles(invalidK8sFilesChan)
+	invalidK8sFiles := aggregateInvalidK8sFiles(invalidK8sFilesChan)
 
 	invalidK8sFilesLen := len(invalidK8sFiles)
 	stopEvaluation = invalidYamlFilesLen+invalidK8sFilesLen == filesPathsLen
@@ -180,22 +180,21 @@ func test(ctx *TestCommandContext, paths []string, flags TestCommandFlags) error
 
 	evaluationSummary := printer.EvaluationSummary{
 		FilesCount:                filesPathsLen,
+		RulesCount:                createEvaluationResponse.RulesCount,
 		PassedYamlValidationCount: passedYamlValidationCount,
 		PassedK8sValidationCount:  passedK8sValidationCount,
 		ConfigsCount:              configsCount,
 		PassedPolicyCheckCount:    passedPolicyCheckCount,
 	}
 
-	invalidFiles := append(invalidYamlFiles, invalidK8sFiles...)
-
-	err = evaluation.PrintResults(results, invalidFiles, evaluationSummary, fmt.Sprintf("https://app.datree.io/login?cliId=%s", ctx.LocalConfig.CliId), flags.Output, ctx.Printer, createEvaluationResponse.K8sVersion)
+	err = evaluation.PrintResults(results, invalidYamlFiles, invalidK8sFiles, evaluationSummary, fmt.Sprintf("https://app.datree.io/login?cliId=%s", ctx.LocalConfig.CliId), flags.Output, ctx.Printer, createEvaluationResponse.K8sVersion)
 
 	var invocationFailedErr error = nil
 
 	if err != nil {
 		fmt.Println(err.Error())
 		invocationFailedErr = err
-	} else if len(invalidFiles) > 0 || results.Summary.TotalFailedRules > 0 {
+	} else if len(invalidYamlFiles) > 0 || len(invalidK8sFiles) > 0 || results.Summary.TotalFailedRules > 0 {
 		invocationFailedErr = fmt.Errorf("Evaluation failed")
 	}
 
diff --git a/cmd/test/main_test.go b/cmd/test/main_test.go
index c7e4c25..1a7dbbc 100644
--- a/cmd/test/main_test.go
+++ b/cmd/test/main_test.go
@@ -28,13 +28,13 @@ func (m *mockEvaluator) CreateEvaluation(cliId string, cliVersion string, k8sVer
 	return args.Get(0).(*cliClient.CreateEvaluationResponse), args.Error(1)
 }
 
-func (m *mockEvaluator) UpdateFailedYamlValidation(invalidFiles []*validation.InvalidFile, evaluationId int, stopEvaluation bool) error {
-	args := m.Called(invalidFiles, evaluationId, stopEvaluation)
+func (m *mockEvaluator) UpdateFailedYamlValidation(invalidYamlFiles []*validation.InvalidYamlFile, evaluationId int, stopEvaluation bool) error {
+	args := m.Called(invalidYamlFiles, evaluationId, stopEvaluation)
 	return args.Error(0)
 }
 
-func (m *mockEvaluator) UpdateFailedK8sValidation(invalidFiles []*validation.InvalidFile, evaluationId int, stopEvaluation bool) error {
-	args := m.Called(invalidFiles, evaluationId, stopEvaluation)
+func (m *mockEvaluator) UpdateFailedK8sValidation(invalidK8sFiles []*validation.InvalidK8sFile, evaluationId int, stopEvaluation bool) error {
+	args := m.Called(invalidK8sFiles, evaluationId, stopEvaluation)
 	return args.Error(0)
 }
 
@@ -62,9 +62,9 @@ type K8sValidatorMock struct {
 	mock.Mock
 }
 
-func (kv *K8sValidatorMock) ValidateResources(filesConfigurationsChan chan *extractor.FileConfigurations, concurrency int) (chan *extractor.FileConfigurations, chan *validation.InvalidFile) {
+func (kv *K8sValidatorMock) ValidateResources(filesConfigurationsChan chan *extractor.FileConfigurations, concurrency int) (chan *extractor.FileConfigurations, chan *validation.InvalidK8sFile) {
 	args := kv.Called(filesConfigurationsChan, concurrency)
-	return args.Get(0).(chan *extractor.FileConfigurations), args.Get(1).(chan *validation.InvalidFile)
+	return args.Get(0).(chan *extractor.FileConfigurations), args.Get(1).(chan *validation.InvalidK8sFile)
 }
 
 func (kv *K8sValidatorMock) InitClient(k8sVersion string) {
@@ -104,16 +104,15 @@ func TestTestCommand(t *testing.T) {
 
 	evaluationResults := &evaluation.EvaluationResults{
 		FileNameRuleMapper: map[string]map[int]*evaluation.Rule{}, Summary: struct {
-			RulesCount       int
 			TotalFailedRules int
 			FilesCount       int
 			TotalPassedCount int
-		}{RulesCount: 1, TotalFailedRules: 0, FilesCount: 0, TotalPassedCount: 1},
+		}{TotalFailedRules: 0, FilesCount: 0, TotalPassedCount: 1},
 	}
 
 	mockedEvaluator := &mockEvaluator{}
 	mockedEvaluator.On("Evaluate", mock.Anything, mock.Anything, mock.Anything).Return(evaluationResults, nil)
-	mockedEvaluator.On("CreateEvaluation", mock.Anything, mock.Anything, mock.Anything).Return(&cliClient.CreateEvaluationResponse{EvaluationId: evaluationId, K8sVersion: "1.18.0"}, nil)
+	mockedEvaluator.On("CreateEvaluation", mock.Anything, mock.Anything, mock.Anything).Return(&cliClient.CreateEvaluationResponse{EvaluationId: evaluationId, K8sVersion: "1.18.0", RulesCount: 21}, nil)
 	mockedEvaluator.On("UpdateFailedYamlValidation", mock.Anything, mock.Anything, mock.Anything).Return(nil)
 	mockedEvaluator.On("UpdateFailedK8sValidation", mock.Anything, mock.Anything, mock.Anything).Return(nil)
 	messager := &mockMessager{}
@@ -125,9 +124,9 @@ func TestTestCommand(t *testing.T) {
 	filesConfigurationsChan := newFilesConfigurationsChan(path)
 	filesConfigurations := newFilesConfigurations(path)
 
-	invalidFilesChan := newInvalidFilesChan()
+	invelidK8sFilesChan := newInvalidK8sFilesChan()
 
-	k8sValidatorMock.On("ValidateResources", mock.Anything, mock.Anything).Return(filesConfigurationsChan, invalidFilesChan, newErrorsChan())
+	k8sValidatorMock.On("ValidateResources", mock.Anything, mock.Anything).Return(filesConfigurationsChan, invelidK8sFilesChan, newErrorsChan())
 	k8sValidatorMock.On("InitClient", mock.Anything).Return()
 
 	printerMock := &PrinterMock{}
@@ -193,10 +192,10 @@ func newFilesConfigurations(path string) []*extractor.FileConfigurations {
 	return filesConfigurations
 }
 
-func newInvalidFilesChan() chan *validation.InvalidFile {
-	invalidFilesChan := make(chan *validation.InvalidFile, 1)
+func newInvalidK8sFilesChan() chan *validation.InvalidK8sFile {
+	invalidFilesChan := make(chan *validation.InvalidK8sFile, 1)
 
-	invalidFile := &validation.InvalidFile{
+	invalidFile := &validation.InvalidK8sFile{
 		Path:             "invalid/path",
 		ValidationErrors: []error{},
 	}
diff --git a/cmd/test/testCommandHelper.go b/cmd/test/testCommandHelper.go
index ddcd045..6e9376f 100644
--- a/cmd/test/testCommandHelper.go
+++ b/cmd/test/testCommandHelper.go
@@ -15,8 +15,15 @@ func createSpinner(text string, color string) *spinner.Spinner {
 	return s
 }
 
-func aggregateInvalidFiles(invalidFilesChan chan *validation.InvalidFile) []*validation.InvalidFile {
-	var invalidFiles []*validation.InvalidFile
+func aggregateInvalidK8sFiles(invalidFilesChan chan *validation.InvalidK8sFile) []*validation.InvalidK8sFile {
+	var invalidFiles []*validation.InvalidK8sFile
+	for invalidFile := range invalidFilesChan {
+		invalidFiles = append(invalidFiles, invalidFile)
+	}
+	return invalidFiles
+}
+func aggregateInvalidYamlFiles(invalidFilesChan chan *validation.InvalidYamlFile) []*validation.InvalidYamlFile {
+	var invalidFiles []*validation.InvalidYamlFile
 	for invalidFile := range invalidFilesChan {
 		invalidFiles = append(invalidFiles, invalidFile)
 	}