You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@skywalking.apache.org by ho...@apache.org on 2021/02/06 12:56:58 UTC

[skywalking-infra-e2e] 03/04: Implement the verify of actual data file

This is an automated email from the ASF dual-hosted git repository.

hoshea pushed a commit to branch verifier
in repository https://gitbox.apache.org/repos/asf/skywalking-infra-e2e.git

commit c1f23bd9b8daaf6b0d52327ea3387260a5c154e2
Author: Hoshea <fg...@gmail.com>
AuthorDate: Sat Feb 6 20:54:59 2021 +0800

    Implement the verify of actual data file
---
 commands/verify/verify.go                |  18 +++-
 internal/components/verifier/funcs.go    |  54 ++++++++++++
 internal/components/verifier/verifier.go |  89 +++++++++++++++++++
 internal/config/e2eConfig.go             |   9 +-
 internal/util/utils.go                   |  14 +++
 test.go                                  | 142 -------------------------------
 test/verify/1.actual.yaml                |  24 ++++++
 test/verify/1.expected.yaml              |  24 ++++++
 8 files changed, 230 insertions(+), 144 deletions(-)

diff --git a/commands/verify/verify.go b/commands/verify/verify.go
index 34c122c..06b908b 100644
--- a/commands/verify/verify.go
+++ b/commands/verify/verify.go
@@ -19,14 +19,30 @@ package verify
 
 import (
 	"fmt"
+	"github.com/apache/skywalking-infra-e2e/internal/components/verifier"
 
 	"github.com/spf13/cobra"
 )
 
+var (
+	query    string
+	actual   string
+	expected string
+)
+
+func init() {
+	Verify.Flags().StringVarP(&query, "query", "q", "", "the query to get the actual data, the result of the query should in YAML format")
+	Verify.Flags().StringVarP(&actual, "actual", "a", "", "the actual data file, only YAML file format is supported")
+	Verify.Flags().StringVarP(&expected, "expected", "e", "", "the expected data file, only YAML file format is supported")
+}
+
 var Verify = &cobra.Command{
 	Use:   "verify",
-	Short: "",
+	Short: "verify if the actual data match the expected data",
 	RunE: func(cmd *cobra.Command, args []string) error {
+		if actual != "" && expected != "" {
+			return verifier.VerifyDataFile(actual, expected)
+		}
 		fmt.Println("Not implemented.")
 		return nil
 	},
diff --git a/internal/components/verifier/funcs.go b/internal/components/verifier/funcs.go
new file mode 100644
index 0000000..597bb89
--- /dev/null
+++ b/internal/components/verifier/funcs.go
@@ -0,0 +1,54 @@
+package verifier
+
+import (
+	"encoding/base64"
+	"fmt"
+	"regexp"
+	"strings"
+
+	"github.com/apache/skywalking-infra-e2e/third-party/template"
+)
+
+// funcMap produces the custom function map.
+// Use this to pass the functions into the template engine:
+// 	tpl := template.New("foo").Funcs(funcMap()))
+func funcMap() template.FuncMap {
+	fm := make(map[string]interface{}, len(customFuncMap))
+	for k, v := range customFuncMap {
+		fm[k] = v
+	}
+	return template.FuncMap(fm)
+}
+
+var customFuncMap = map[string]interface{}{
+	// Basic:
+	"notEmpty": notEmpty,
+
+	// Encoding:
+	"b64enc": base64encode,
+
+	// Regex:
+	"regexp": regexpMatch,
+}
+
+func base64encode(s string) string {
+	return base64.StdEncoding.EncodeToString([]byte(s))
+}
+
+func notEmpty(s string) string {
+	if len(strings.TrimSpace(s)) > 0 {
+		return s
+	}
+	return fmt.Sprintf(`<"%s" is empty, wanted is not empty>`, s)
+}
+
+func regexpMatch(s, pattern string) string {
+	matched, err := regexp.MatchString(pattern, s)
+	if err != nil {
+		return fmt.Sprintf(`<"%s">`, err)
+	}
+	if !matched {
+		return fmt.Sprintf(`<"%s" does not match the pattern %s">`, s, pattern)
+	}
+	return s
+}
diff --git a/internal/components/verifier/verifier.go b/internal/components/verifier/verifier.go
new file mode 100644
index 0000000..4c46263
--- /dev/null
+++ b/internal/components/verifier/verifier.go
@@ -0,0 +1,89 @@
+package verifier
+
+import (
+	"bytes"
+	"errors"
+	"fmt"
+
+	"github.com/apache/skywalking-infra-e2e/internal/logger"
+	"github.com/apache/skywalking-infra-e2e/internal/util"
+	"github.com/apache/skywalking-infra-e2e/third-party/template"
+
+	"github.com/google/go-cmp/cmp"
+	"gopkg.in/yaml.v2"
+)
+
+// MismatchError is the error type returned by the verify functions.
+// Then the caller will know if there is a mismatch.
+type MismatchError struct {
+	Err error
+}
+
+func (e *MismatchError) Unwrap() error { return e.Err }
+
+func (e *MismatchError) Error() string {
+	if e == nil {
+		return "<nil>"
+	}
+	return "the actual data does not match the expected data"
+}
+
+// VerifyDataFile reads the actual data from the file and verifies.
+func VerifyDataFile(actualFile, expectedFile string) error {
+	actualData, err := util.ReadFileContent(actualFile)
+	if err != nil {
+		logger.Log.Error("failed to read the actual data file")
+		return err
+	}
+
+	expectedTemplate, err := util.ReadFileContent(expectedFile)
+	if err != nil {
+		logger.Log.Error("failed to read the expected data file")
+		return err
+	}
+
+	return verify(actualData, expectedTemplate)
+}
+
+// VerifyQuery gets the actual data from the query and then verifies.
+func VerifyQuery(query, expectedFile string) error {
+	return errors.New("not implemented")
+}
+
+// verify checks if the actual data match the expected template.
+// It will print the diff if the actual data does not match.
+func verify(actualData, expectedTemplate string) error {
+	var actual interface{}
+	if err := yaml.Unmarshal([]byte(actualData), &actual); err != nil {
+		logger.Log.Error("failed to unmarshal actual data")
+		return err
+	}
+
+	tmpl, err := template.New("test").Funcs(funcMap()).Parse(expectedTemplate)
+	if err != nil {
+		logger.Log.Error("failed to parse template")
+		return err
+	}
+
+	var b bytes.Buffer
+	if err := tmpl.Execute(&b, actual); err != nil {
+		logger.Log.Error("failed to execute template")
+		return err
+	}
+
+	var expected interface{}
+	if err := yaml.Unmarshal(b.Bytes(), &expected); err != nil {
+		logger.Log.Error("failed to unmarshal expected data")
+		return err
+	}
+
+	if !cmp.Equal(expected, actual) {
+		diff := cmp.Diff(expected, actual)
+		fmt.Println(diff)
+		return &MismatchError{}
+	} else {
+		logger.Log.Info("the actual data matches the expected data")
+	}
+
+	return nil
+}
diff --git a/internal/config/e2eConfig.go b/internal/config/e2eConfig.go
index 836e582..c242947 100644
--- a/internal/config/e2eConfig.go
+++ b/internal/config/e2eConfig.go
@@ -20,7 +20,8 @@ package config
 
 // E2EConfig corresponds to configuration file e2e.yaml.
 type E2EConfig struct {
-	Setup Setup `yaml:"setup"`
+	Setup  Setup        `yaml:"setup"`
+	Verify []VerifyCase `yaml:"verify"`
 }
 
 type Setup struct {
@@ -48,3 +49,9 @@ type Wait struct {
 	LabelSelector string `yaml:"label-selector"`
 	For           string `yaml:"for"`
 }
+
+type VerifyCase struct {
+	Query    string `yaml:"query"`
+	Actual   string `yaml:"actual"`
+	Expected string `yaml:"expected"`
+}
diff --git a/internal/util/utils.go b/internal/util/utils.go
index 50030d7..0c2a2f0 100644
--- a/internal/util/utils.go
+++ b/internal/util/utils.go
@@ -19,6 +19,8 @@
 package util
 
 import (
+	"errors"
+	"io/ioutil"
 	"os"
 	"os/exec"
 )
@@ -38,3 +40,15 @@ func PathExist(_path string) bool {
 	}
 	return true
 }
+
+// ReadFileContent reads the file content.
+func ReadFileContent(filename string) (string, error) {
+	if PathExist(filename) {
+		content, err := ioutil.ReadFile(filename)
+		if err != nil {
+			return "", err
+		}
+		return string(content), nil
+	}
+	return "", errors.New("the file does not exist")
+}
diff --git a/test.go b/test.go
deleted file mode 100644
index cbdfe8e..0000000
--- a/test.go
+++ /dev/null
@@ -1,142 +0,0 @@
-package main
-
-import (
-	"bytes"
-	"encoding/base64"
-	"fmt"
-	"github.com/apache/skywalking-infra-e2e/third-party/template"
-	"github.com/google/go-cmp/cmp"
-	"gopkg.in/yaml.v3"
-	"log"
-	"strings"
-)
-
-var funcMap = template.FuncMap{
-	"b64enc": func(s string) string {
-		return base64.StdEncoding.EncodeToString([]byte(s))
-	},
-	"notEmpty": func(s string) string {
-		if len(strings.TrimSpace(s)) > 0 {
-			return s
-		} else {
-			return fmt.Sprintf(`<"%s" is empty, wanted is not empty>`, s)
-		}
-	},
-}
-
-func main() {
-	test(`
-nodes:
-  - id: VXNlcg==.0
-    name: User
-    type: USER
-    isReal: false
-  - id: WW91cl9BcHBsaWNhdGlvbk5hbWU=.1
-    name: Your_ApplicationName
-    type: Tomcat
-    isReal: true
-  - id: bG9jYWxob3N0Oi0x.0
-    name: localhost:-1
-    type: H2
-    isReal: false
-calls:
-  - id: WW91cl9BcHBsaWNhdGlvbk5hbWU=.1-bG9jYWxob3N0Oi0x.0
-    source: WW91cl9BcHBsaWNhdGlvbk5hbWU=.1
-    detectPoints:
-      - CLIENT
-    target: bG9jYWxob3N0Oi0x.0
-  - id: VXNlcg==.0-WW91cl9BcHBsaWNhdGlvbk5hbWU=.1
-    source: VXNlcg==.0
-    detectPoints:
-      - SERVER
-    target: WW91cl9BcHBsaWNhdGlvbk5hbWU=.1
-`, `
-nodes:
-  - id: {{ b64enc "User" }}.0
-    name: User
-    type: USER
-    isReal: false
-  - id: {{ b64enc "Your_ApplicationName" }}.1
-    name: Your_ApplicationName
-    type: Tomcat
-    isReal: true
-  - id: {{ $h2ID := (index .nodes 2).id }}{{ notEmpty $h2ID }}
-    name: localhost:-1
-    type: H2
-    isReal: false
-calls:
-  - id: {{ notEmpty (index .calls 0).id }}
-    source: {{ b64enc "Your_ApplicationName" }}.1
-    target: {{ $h2ID }}
-    detectPoints:
-      - CLIENT
-  - id: {{ b64enc "User" }}.0-{{ b64enc "Your_ApplicationName" }}.1
-    source: {{ b64enc "User" }}.0
-    target: {{ b64enc "Your_ApplicationName" }}.1
-    detectPoints:
-      - SERVER
-`)
-
-	test(`
-metrics:
-  - name: business-zone::projectA
-    id: YnVzaW5lc3Mtem9uZTo6cHJvamVjdEE=.1
-    value: 1
-  - name: system::load balancer1
-    id: c3lzdGVtOjpsb2FkIGJhbGFuY2VyMQ==.1
-    value: 0
-  - name: system::load balancer2
-    id: c3lzdGVtOjpsb2FkIGJhbGFuY2VyMg==.1
-    value: 0
-`, `
-metrics:
-{{- atLeastOnce .metrics }}
-  name: {{ notEmpty .name }}
-  id: {{ notEmpty .id }}
-  value: {{ gt .value 0 }}
-{{- end }}
-`)
-	test(`
-metrics:
-  - name: business-zone::projectA
-    id: YnVzaW5lc3Mtem9uZTo6cHJvamVjdEE=.1
-    value: 0
-  - name: system::load balancer1
-    id: c3lzdGVtOjpsb2FkIGJhbGFuY2VyMQ==.1
-    value: 0
-  - name: system::load balancer2
-    id: c3lzdGVtOjpsb2FkIGJhbGFuY2VyMg==.1
-    value: 0
-`, `
-metrics:
-{{- atLeastOnce .metrics }}
-  name: {{ notEmpty .name }}
-  id: {{ notEmpty .id }}
-  value: {{ gt .value 0 }}
-{{- end }}
-`)
-}
-
-func test(actualData string, expectedTemplate string) {
-	var actual interface{}
-	yaml.Unmarshal([]byte(actualData), &actual)
-
-	tmpl, err := template.New("test").Funcs(funcMap).Parse(expectedTemplate)
-	if err != nil {
-		log.Fatalf("parsing: %s", err)
-	}
-
-	var b bytes.Buffer
-	err = tmpl.Execute(&b, actual)
-	if err != nil {
-		log.Fatalf("execution: %s", err)
-	}
-
-	var expected interface{}
-	yaml.Unmarshal(b.Bytes(), &expected)
-
-	if !cmp.Equal(expected, actual) {
-		diff := cmp.Diff(expected, actual)
-		println(diff)
-	}
-}
diff --git a/test/verify/1.actual.yaml b/test/verify/1.actual.yaml
new file mode 100644
index 0000000..1e3d89d
--- /dev/null
+++ b/test/verify/1.actual.yaml
@@ -0,0 +1,24 @@
+nodes:
+  - id: VXNlcg==.0
+    name: User
+    type: USER
+    isReal: false
+  - id: WW91cl9BcHBsaWNhdGlvbk5hbWU=.1
+    name: Your_ApplicationName
+    type: Tomcat
+    isReal: true
+  - id: bG9jYWxob3N0Oi0x.0
+    name: localhost:-1
+    type: H2
+    isReal: false
+calls:
+  - id: WW91cl9BcHBsaWNhdGlvbk5hbWU=.1-bG9jYWxob3N0Oi0x.0
+    source: WW91cl9BcHBsaWNhdGlvbk5hbWU=.1
+    detectPoints:
+      - CLIENT
+    target: bG9jYWxob3N0Oi0x.0
+  - id: VXNlcg==.0-WW91cl9BcHBsaWNhdGlvbk5hbWU=.1
+    source: VXNlcg==.0
+    detectPoints:
+      - SERVER
+    target: WW91cl9BcHBsaWNhdGlvbk5hbWU=.1
\ No newline at end of file
diff --git a/test/verify/1.expected.yaml b/test/verify/1.expected.yaml
new file mode 100644
index 0000000..23b8076
--- /dev/null
+++ b/test/verify/1.expected.yaml
@@ -0,0 +1,24 @@
+nodes:
+  - id: {{ b64enc "User" }}.0
+    name: User
+    type: USER
+    isReal: false
+  - id: {{ b64enc "Your_ApplicationName" }}.1
+    name: Your_ApplicationName
+    type: Tomcat
+    isReal: true
+  - id: {{ $h2ID := (index .nodes 2).id }}{{ notEmpty $h2ID }}
+    name: localhost:-1
+    type: H2
+    isReal: false
+calls:
+  - id: {{ notEmpty (index .calls 0).id }}
+    source: {{ b64enc "Your_ApplicationName" }}.1
+    target: {{ $h2ID }}
+    detectPoints:
+      - CLIENT
+  - id: {{ b64enc "User" }}.0-{{ b64enc "Your_ApplicationName" }}.1
+    source: {{ b64enc "User" }}.0
+    target: {{ b64enc "Your_ApplicationName" }}.1
+    detectPoints:
+      - SERVER
\ No newline at end of file