Repository: datreeio/datree
Pull request #153 (2021-08-30 09:56:18)

staging


Patch:
diff --git a/.dockerignore b/.dockerignore
index 72e8ffc..7a89d7d 100644
--- a/.dockerignore
+++ b/.dockerignore
@@ -1,1 +1,5 @@
-*
+dist
+.vscode
+.github
+internal
+*install.sh
diff --git a/Dockerfile b/Dockerfile
index 97e3136..8751696 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,4 +1,4 @@
-FROM ubuntu:18.04
+FROM golang:1.15-alpine
 
 WORKDIR /go/src/app
 COPY . .
@@ -7,4 +7,4 @@ RUN go get -d -v ./...
 RUN go build -tags main -ldflags="-X github.com/datreeio/datree/cmd.CliVersion=___CLI_VERSION" -v
 RUN go install -v ./...
 
-ENTRYPOINT ["/go/bin/datree"]
+ENTRYPOINT ["./datree"]
diff --git a/README.md b/README.md
index abbcdef..c8a8174 100644
--- a/README.md
+++ b/README.md
@@ -3,54 +3,62 @@
 ![Travis (.com) branch](https://img.shields.io/travis/com/datreeio/datree/staging?label=build-staging)
 ![Travis (.com) branch](https://img.shields.io/travis/com/datreeio/datree/main?label=build-main)
 [![Hits](https://hits.seeyoufarm.com/api/count/incr/badge.svg?url=https%3A%2F%2Fgithub.com%2Fdatreeio%2Fdatree&count_bg=%2379C83D&title_bg=%23555555&icon=github.svg&icon_color=%23E7E7E7&title=views+%28today+%2F+total%29&edge_flat=false)](https://hits.seeyoufarm.com)
+[![Go Report Card](https://goreportcard.com/badge/github.com/datreeio/datree)](https://goreportcard.com/report/github.com/datreeio/datree)
 ![open issues](https://img.shields.io/github/issues-raw/datreeio/datree)
 
 ## What is Datree?
-[Datree](https://datree.io/?utm_source=github&utm_medium=organic_oss) is a CLI tool that supports Kubernetes admins in their roles, by preventing developers from making errors in Kubernetes configurations that can cause clusters to fail in production. Our CLI tool is open source, enabling it to be supported by the Kubernetes community.  
+[Datree](https://datree.io/#utm_source=github&utm_medium=organic_oss) is a CLI tool that supports Kubernetes admins in their roles, by preventing developers from making errors in Kubernetes configurations that can cause clusters to fail in production. Our CLI tool is open source, enabling it to be supported by the Kubernetes community.  
 
 It’s far more effective than manual processes, such as sending an email to a slew of developers, begging them to set various limits, which likely falls on deaf ears because developers are already overwhelmed.
 
+### ⭐️ [Star this repository](https://github.com/datreeio/datree/stargazers) to get updates ⭐️   
+
 ## How it Works
 The CLI integration provides a policy enforcement solution for Kubernetes to run automatic checks on every code change for rule violations and misconfigurations. When rule violations are found, Datree produces an alert which guides the developer to fix the issue inside the CI process — or even earlier as a pre-commit hook — while explaining the reason behind the rule.
 
 ## Quick start in two steps
 #### 1. Install the latest release on your CLI  
-**Linux & MacOS:** `curl https://get.datree.io | /bin/bash`  
-**Windows:** `iwr -useb https://get.datree.io/windows_install.ps1 | iex`  
+_Linux & MacOS:_ `curl https://get.datree.io | /bin/bash`  
+_Windows:_ `iwr -useb https://get.datree.io/windows_install.ps1 | iex`  
 
 #### 2. Pass datree a Kuberntes manifest file to scan
 `datree test <k8s-manifest-file>`  
 
 ...and voilà, you just ran your first invocation! 🥳    
 
-In your command line, you’ll see something like this:
-<img src="https://clipublic.s3.amazonaws.com/live.gif" alt="Datree" width="600" height="400">  
+## [Command Line Interface](https://hub.datree.io/cli-output/#utm_source=github&utm_medium=organic_oss)
+<img src="https://clipublic.s3.amazonaws.com/live.gif" alt="Datree-cli" width="600" height="400">  
+
+## [Web Application Interface](https://hub.datree.io/centralized-policy/#utm_source=github&utm_medium=organic_oss)
+<img src="https://user-images.githubusercontent.com/19731161/130956287-ca44e831-46ba-48fa-96eb-be8e23d43bdf.png" alt="Datree-saas" width="600" height="400">  
+
+<img src="https://user-images.githubusercontent.com/19731161/130957021-4b825b82-01e1-47ba-bf6f-68003f08a532.png" alt="Datree-saas" width="600" height="400"> 
 
-#### Playground
-[![katacoda-logo](https://raw.githubusercontent.com/datreeio/datree/main/images/katacoda-logo.png)](#)  
+## Playground
+[![katacoda-logo](https://raw.githubusercontent.com/datreeio/datree/main/images/katacoda-logo.png)](https://www.katacoda.com/datree/scenarios/datree-demo)  
 You can also checkout our [interactive demo scenario](https://www.katacoda.com/datree/scenarios/datree-demo) on Katacoda without having to install anything on your machine.  
 
-#### Ready to review our "Getting Started" guide?
-All the information needed to get started, as well as a bunch of other cool features (including how to set up your policy), can be found in [**our docs**](https://hub.datree.io/getting-started/?utm_source=github&utm_medium=organic_oss).
+## Ready to review our "Getting Started" guide?
+All the information needed to get started, as well as a bunch of other cool features (including how to set up your policy), can be found in [**our docs**](https://hub.datree.io/getting-started/#utm_source=github&utm_medium=organic_oss).
 
 ## Helm plugin
-[Datree's Helm plugin](https://hub.datree.io/helm-plugin/?utm_source=github&utm_medium=organic_oss) can be accessed through the helm CLI, to provide a seamless experience to Helm users:  
+[Datree's Helm plugin](https://hub.datree.io/helm-plugin/#utm_source=github&utm_medium=organic_oss) can be accessed through the helm CLI, to provide a seamless experience to Helm users:  
 
 `helm plugin install https://github.com/datreeio/helm-datree`  
 
 ## Built-in rules
 Right now, there are 30 battle-tested rules for you to choose from.    
 The rules cover different Kubernetes resources and use cases:
-* [Workload](https://hub.datree.io/workload/?utm_source=github&utm_medium=organic_oss)
-* [CronJob](https://hub.datree.io/cronjob/?utm_source=github&utm_medium=organic_oss)
-* [Containers](https://hub.datree.io/containers/?utm_source=github&utm_medium=organic_oss)
-* [Networking](https://hub.datree.io/networking/?utm_source=github&utm_medium=organic_oss)
-* [Deprecation](https://hub.datree.io/deprecation/?utm_source=github&utm_medium=organic_oss)
-* [Others](https://hub.datree.io/other/?utm_source=github&utm_medium=organic_oss)
+* [Workload](https://hub.datree.io/workload/#utm_source=github&utm_medium=organic_oss)
+* [CronJob](https://hub.datree.io/cronjob/#utm_source=github&utm_medium=organic_oss)
+* [Containers](https://hub.datree.io/containers/#utm_source=github&utm_medium=organic_oss)
+* [Networking](https://hub.datree.io/networking/#utm_source=github&utm_medium=organic_oss)
+* [Deprecation](https://hub.datree.io/deprecation/#utm_source=github&utm_medium=organic_oss)
+* [Others](https://hub.datree.io/other/#utm_source=github&utm_medium=organic_oss)
 
 ## Support
 
-[Datree](https://datree.io/?utm_source=github&utm_medium=organic_oss) builds and maintains this project to make Kubernetes policies simple and accessible. Start with our [documentations](https://hub.datree.io/?utm_source=github&utm_medium=organic_oss) for quick tutorials and examples.
+[Datree](https://datree.io/#utm_source=github&utm_medium=organic_oss) builds and maintains this project to make Kubernetes policies simple and accessible. Start with our [documentations](https://hub.datree.io/#utm_source=github&utm_medium=organic_oss) for quick tutorials and examples.
 
 ## Disclaimer
 
diff --git a/bl/evaluation/evaluator.go b/bl/evaluation/evaluator.go
index 169d6f3..45f497a 100644
--- a/bl/evaluation/evaluator.go
+++ b/bl/evaluation/evaluator.go
@@ -25,8 +25,10 @@ func New(c CLIClient) *Evaluator {
 	}
 }
 
+type FileNameRuleMapper map[string]map[int]*Rule 
+
 type EvaluationResults struct {
-	FileNameRuleMapper map[string]map[int]*Rule
+	FileNameRuleMapper FileNameRuleMapper
 	Summary            struct {
 		TotalFailedRules int
 		FilesCount       int
diff --git a/bl/evaluation/printer.go b/bl/evaluation/printer.go
index 8aec229..3395be8 100644
--- a/bl/evaluation/printer.go
+++ b/bl/evaluation/printer.go
@@ -2,6 +2,7 @@ package evaluation
 
 import (
 	"encoding/json"
+	"encoding/xml"
 	"fmt"
 	"os"
 	"path/filepath"
@@ -33,6 +34,8 @@ func PrintResults(results *EvaluationResults, invalidYamlFiles []*validation.Inv
 		return jsonOutput(&FormattedOutput{EvaluationResults: results, EvaluationSummary: evaluationSummary, InvalidYamlFiles: invalidYamlFiles, InvalidK8sFiles: invalidK8sFiles})
 	case outputFormat == "yaml":
 		return yamlOutput(&FormattedOutput{EvaluationResults: results, EvaluationSummary: evaluationSummary, InvalidYamlFiles: invalidYamlFiles, InvalidK8sFiles: invalidK8sFiles})
+	case outputFormat == "xml":
+		return xmlOutput(&FormattedOutput{EvaluationResults: results, EvaluationSummary: evaluationSummary, InvalidYamlFiles: invalidYamlFiles, InvalidK8sFiles: invalidK8sFiles})
 	default:
 		return textOutput(results, invalidYamlFiles, invalidK8sFiles, evaluationSummary, loginURL, printer, k8sVersion, policyName)
 	}
@@ -60,6 +63,18 @@ func yamlOutput(formattedOutput *FormattedOutput) error {
 	return nil
 }
 
+func xmlOutput(formmattedOutput *FormattedOutput) error {
+	xmlOutput, err := xml.MarshalIndent(formmattedOutput, "", "\t")
+	xmlOutput = []byte(xml.Header + string(xmlOutput))
+	if err != nil {
+		fmt.Println(err)
+		return err
+	}
+
+	fmt.Println(string(xmlOutput))
+	return nil
+}
+
 func textOutput(results *EvaluationResults, invalidYamlFiles []*validation.InvalidYamlFile, invalidK8sFiles []*validation.InvalidK8sFile, evaluationSummary printer.EvaluationSummary, url string, printer Printer, k8sVersion string, policyName string) error {
 	pwd, err := os.Getwd()
 	if err != nil {
@@ -227,3 +242,39 @@ func parseEvaluationResultsToSummary(results *EvaluationResults, evaluationSumma
 	}
 	return *summary
 }
+
+func (mapper FileNameRuleMapper) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
+	if len(mapper) == 0 {
+		return nil
+	}
+
+	tokens := []xml.Token{start}
+	fileXMLName := xml.Name{Space: "", Local: "File"}
+	filenameXMLName := xml.Name{Space: "", Local: "filename"}
+
+	// Iterate over mapper and create XML tokens for all entries
+	for filePath, encapsulatedRule := range mapper {
+		for _, rule := range encapsulatedRule {	// Since rule is encapsulated by its id (int), we don't add is a tag
+			startToken := xml.StartElement{Name: fileXMLName, Attr: []xml.Attr{{Name: filenameXMLName, Value: filePath}}}
+			endToken := xml.EndElement{Name: fileXMLName}
+			tokens = append(tokens, startToken, rule, endToken)
+		}
+	}
+
+	tokens = append(tokens, xml.EndElement{Name: start.Name})
+
+	for _, t := range tokens {
+		var err error
+		switch t.(type) {
+		default:
+			err = e.EncodeToken(t)
+		case *Rule:
+			err = e.EncodeElement(t, xml.StartElement{Name: xml.Name{Space: "", Local: "Rule"}})
+		}
+		if err != nil {
+			return err
+		}
+	}
+
+	return e.Flush()
+}
diff --git a/internal/release/deploy.sh b/internal/release/deploy.sh
index cc4c4cd..d093cec 100644
--- a/internal/release/deploy.sh
+++ b/internal/release/deploy.sh
@@ -1,9 +1,9 @@
 set -ex
-if [ $TRAVIS_BRANCH == "main" ]; then 
-  export DATREE_BUILD_VERSION=$SEMVER_NUMBER; 
+if [ $TRAVIS_BRANCH == "main" ]; then
+  export DATREE_BUILD_VERSION=$SEMVER_NUMBER
   export DATREE_BREW_REPO_NAME=homebrew-datree
-else 
-  export DATREE_BUILD_VERSION=$SEMVER_NUMBER-$TRAVIS_BRANCH; 
+else
+  export DATREE_BUILD_VERSION=$SEMVER_NUMBER-$TRAVIS_BRANCH
   export DATREE_BREW_REPO_NAME=homebrew-datree-staging
 fi
 
@@ -15,6 +15,7 @@ git commit -m "ci: dynamic variable updates"
 export GIT_TAG=$DATREE_BUILD_VERSION
 git tag $GIT_TAG -a -m "Generated tag from TravisCI for build $TRAVIS_BUILD_NUMBER"
 git push --tags
+
 git stash save --keep-index --include-untracked
 
 # Secure private key
diff --git a/pkg/cliClient/publish.go b/pkg/cliClient/publish.go
index 3ff11d0..290da77 100644
--- a/pkg/cliClient/publish.go
+++ b/pkg/cliClient/publish.go
@@ -6,6 +6,7 @@ import (
 )
 
 func (c *CliClient) PublishPolicies(policiesConfiguration files.UnknownStruct, cliId string) error {
-	_, err := c.httpClient.Request(http.MethodPut, "/cli/policy/publish/cliIds/"+cliId, policiesConfiguration, nil)
+	headers := map[string]string{"x-cli-id": cliId}
+	_, err := c.httpClient.Request(http.MethodPut, "/cli/policy/publish", policiesConfiguration, headers)
 	return err
 }


Test patch:
diff --git a/cmd/test/main.go b/cmd/test/main.go
index d725608..7b826d6 100644
--- a/cmd/test/main.go
+++ b/cmd/test/main.go
@@ -154,7 +154,7 @@ func test(ctx *TestCommandContext, paths []string, flags TestCommandFlags) error
 	if flags.Output == "simple" {
 		ctx.Printer.SetTheme(printer.CreateSimpleTheme())
 	}
-	isInteractiveMode := (flags.Output != "json") && (flags.Output != "yaml")
+	isInteractiveMode := (flags.Output != "json") && (flags.Output != "yaml") && (flags.Output != "xml")
 
 	if isInteractiveMode == true {
 		messages := make(chan *messager.VersionMessage, 1)
diff --git a/cmd/test/main_test.go b/cmd/test/main_test.go
index c016dd2..ae6e38c 100644
--- a/cmd/test/main_test.go
+++ b/cmd/test/main_test.go
@@ -170,6 +170,7 @@ func TestTestCommand(t *testing.T) {
 	test_testCommand_no_flags(t, mockedEvaluator, filesConfigurations, evaluationId, ctx)
 	test_testCommand_json_output(t, mockedEvaluator, filesConfigurations, evaluationId, ctx)
 	test_testCommand_yaml_output(t, mockedEvaluator, filesConfigurations, evaluationId, ctx)
+	test_testCommand_xml_output(t, mockedEvaluator, filesConfigurations, evaluationId, ctx)
 }
 
 func test_testCommand_no_flags(t *testing.T, evaluator *mockEvaluator, filesConfigurations []*extractor.FileConfigurations, evaluationId int, ctx *TestCommandContext) {
@@ -191,6 +192,12 @@ func test_testCommand_yaml_output(t *testing.T, evaluator *mockEvaluator, filesC
 	evaluator.AssertCalled(t, "Evaluate", filesConfigurations, evaluationId)
 }
 
+func test_testCommand_xml_output(t *testing.T, evaluator *mockEvaluator, filesConfigurations []*extractor.FileConfigurations, evaluationId int, ctx *TestCommandContext) {
+	test(ctx, []string{"8/*"}, TestCommandFlags{Output: "xml"})
+
+	evaluator.AssertCalled(t, "Evaluate", filesConfigurations, evaluationId)
+}
+
 func newFilesConfigurationsChan(path string) chan *extractor.FileConfigurations {
 	filesConfigurationsChan := make(chan *extractor.FileConfigurations, 1)
 
diff --git a/pkg/cliClient/cliClient_test.go b/pkg/cliClient/cliClient_test.go
index ab676c7..0690f34 100644
--- a/pkg/cliClient/cliClient_test.go
+++ b/pkg/cliClient/cliClient_test.go
@@ -438,6 +438,8 @@ func test_createEvaluation_success() *CreateEvaluationTestCase {
 }
 
 func test_publishPolicies_success() *PublishPoliciesTestCase {
+	expectedPublishHeaders := map[string]string{"x-cli-id": "cli_id"}
+
 	requestPoliciesConfigurationArg := files.UnknownStruct{}
 	return &PublishPoliciesTestCase{
 		name: "success - publish policies",
@@ -479,9 +481,9 @@ func test_publishPolicies_success() *PublishPoliciesTestCase {
 				headers map[string]string
 			}{
 				method:  http.MethodPut,
-				uri:     "/cli/policy/publish/cliIds/cli_id",
+				uri:     "/cli/policy/publish",
 				body:    requestPoliciesConfigurationArg,
-				headers: nil,
+				headers: expectedPublishHeaders,
 			},
 			response: nil,
 		},
@@ -489,6 +491,8 @@ func test_publishPolicies_success() *PublishPoliciesTestCase {
 }
 
 func test_publishPolicies_schemaError() *PublishPoliciesTestCase {
+	expectedPublishHeaders := map[string]string{"x-cli-id": "cli_id"}
+
 	requestPoliciesConfigurationArg := files.UnknownStruct{}
 	return &PublishPoliciesTestCase{
 		name: "schema error - publish policies",
@@ -530,9 +534,9 @@ func test_publishPolicies_schemaError() *PublishPoliciesTestCase {
 				headers map[string]string
 			}{
 				method:  http.MethodPut,
-				uri:     "/cli/policy/publish/cliIds/cli_id",
+				uri:     "/cli/policy/publish",
 				body:    requestPoliciesConfigurationArg,
-				headers: nil,
+				headers: expectedPublishHeaders,
 			},
 			response: errors.New("error from cli-service"),
 		},