Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add e2e runner (go variant) #95

Merged
merged 29 commits into from
Jul 3, 2019
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
6e16875
add e2e runner v2
OlegLoewen Jun 14, 2019
7478197
Add support for testcase groups. Merge test results of junit.xml file…
OlegLoewen Jun 24, 2019
e8e0134
Remove unneccessary env variables in test-defs and adjust readme.md
OlegLoewen Jun 25, 2019
c65ad62
Remove unsed functions
OlegLoewen Jun 25, 2019
f1d5a7c
Fix: test summary wrongly calculated if e2e.log file size is big
OlegLoewen Jun 25, 2019
d1a1233
Fix testgrid metadata format
OlegLoewen Jun 26, 2019
15ddde6
Introduce command line flags: kubeconfig, k8sVersion, cloudprovider, …
OlegLoewen Jun 26, 2019
0f5b4df
Move root/test/e2etest to root/integration-tests/e2e
OlegLoewen Jun 28, 2019
305a3f6
Add working.json files to k8s version 1.10, 1.11, 1.12, 1.13
OlegLoewen Jun 28, 2019
f38f8f3
Fix description file generation for untracked testcases
OlegLoewen Jul 1, 2019
cbadeda
Use different archive package which support shifting and overwriting
OlegLoewen Jul 1, 2019
66cdd7c
Fix desc generator calculating untracked testcases
OlegLoewen Jul 1, 2019
e58a9df
Add k8s 1.15.0 description files. Remove not existing testcases.
OlegLoewen Jul 1, 2019
7c8f463
Add newly identified skipped testcases to corresponding skip description
OlegLoewen Jul 1, 2019
124e9fa
Fix e2e test summary calculation
OlegLoewen Jul 1, 2019
72bf452
Trim cmd log to 300 characters
OlegLoewen Jul 1, 2019
07ff2c9
Fix summary failed testcase names array in file
OlegLoewen Jul 1, 2019
2b4e2fa
Get corresponding kubectl
OlegLoewen Jul 1, 2019
6ad549d
Update documentation
OlegLoewen Jul 1, 2019
c0a4969
Fix test defs
OlegLoewen Jul 1, 2019
c7ab20a
Fix path to e2e tests in test-defs
OlegLoewen Jul 1, 2019
69bb5f7
Use golang images isntead of base-step images
OlegLoewen Jul 2, 2019
8a99a65
Move downloaded kubectl statically to /usr/local/bin/kubectl, since k…
OlegLoewen Jul 2, 2019
9d87051
Use customized moveFile function because of invalid cross-device link…
OlegLoewen Jul 2, 2019
22db8cc
add debug arguments
OlegLoewen Jul 2, 2019
98cef82
Improve kubectl installation
OlegLoewen Jul 2, 2019
b441f8c
Remove debug flags
OlegLoewen Jul 2, 2019
af86976
Remove depricated k8s-e2e 1.15 files
OlegLoewen Jul 3, 2019
593df1c
move networktest to integration-tests folder
OlegLoewen Jul 3, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Remove unsed functions
Use TM export path and kubeconfig path
Reuse test-infra util functions
Additional minor fixes
  • Loading branch information
OlegLoewen committed Jul 3, 2019
commit c65ad62d30838301e616301ef83125f19f06854f
6 changes: 5 additions & 1 deletion .test-defs/allE2eTestgrid.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -41,5 +41,9 @@ spec:
value: 'fast,slow'

command: [bash, -c]
args: go run $GOPATH/src/github.com/gardener/test-infra/test/e2etest
args:
- >-
export E2E_EXPORT_PATH=$TM_EXPORT_PATH
export E2E_KUBECONFIG_PATH=$TM_KUBECONFIG_PATH
go run $GOPATH/src/github.com/gardener/test-infra/test/e2etest
image: eu.gcr.io/gardener-project/gardener/testmachinery/base-step
10 changes: 7 additions & 3 deletions .test-defs/conformanceTestgrid.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@ kind: TestDefinition
metadata:
name: testgrid
spec:
owner: DL_5C5BE3E2970B9F404D0E2F50@sap.com
owner: gardener-oq@listserv.sap.com
recipientsOnFailure:
- DL_5C5BE3E2970B9F404D0E2F50@sap.com
- gardener-oq@listserv.sap.com

description: Run kubernetes conformance tests and push result files (e2e.log and junit_01.xml) to testgrid repository.

Expand All @@ -44,5 +44,9 @@ spec:
value: "false"

command: [bash, -c]
args: go run $GOPATH/src/github.com/gardener/test-infra/test/e2etest
args:
- >-
export E2E_EXPORT_PATH=$TM_EXPORT_PATH
export E2E_KUBECONFIG_PATH=$TM_KUBECONFIG_PATH
go run $GOPATH/src/github.com/gardener/test-infra/test/e2etest
image: eu.gcr.io/gardener-project/gardener/testmachinery/base-step
12 changes: 8 additions & 4 deletions .test-defs/e2eFast.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,14 @@ spec:
labels: ["default"]

config:
- type: env
name: TESTCASE_GROUPS
value: 'fast'
- type: env
name: TESTCASE_GROUPS
value: 'fast'

command: [bash, -c]
args: go run $GOPATH/src/github.com/gardener/test-infra/test/e2etest
args:
- >-
export E2E_EXPORT_PATH=$TM_EXPORT_PATH
export E2E_KUBECONFIG_PATH=$TM_KUBECONFIG_PATH
go run $GOPATH/src/github.com/gardener/test-infra/test/e2etest
image: eu.gcr.io/gardener-project/gardener/testmachinery/base-step
6 changes: 5 additions & 1 deletion .test-defs/e2eSlow.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -32,5 +32,9 @@ spec:
value: 'slow'

command: [bash, -c]
args: go run $GOPATH/src/github.com/gardener/test-infra/test/e2etest
args:
- >-
export E2E_EXPORT_PATH=$TM_EXPORT_PATH
export E2E_KUBECONFIG_PATH=$TM_KUBECONFIG_PATH
go run $GOPATH/src/github.com/gardener/test-infra/test/e2etest
image: eu.gcr.io/gardener-project/gardener/testmachinery/base-step
3 changes: 2 additions & 1 deletion test/e2etest/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,15 @@ Ensure all required environment variables have been set. Create a `shoot.config`
| TESTCASE_GROUPS | | **[Required]** testcases groups to run (comma separated). E.g. `fast,slow` |
| CLOUDPROVIDER | | **[Required]** Cloud provider (supported: aws, gcp, azure, alicloud, openstack) |

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe also add a default/skeleton cloudprovider to run tests that should be supported on all, e.g when trying to run tests on packet, metal as a service etc

| DESCRIPTION_FILE | working.json | Path to description json file, which lists the testcases to run |
| EXPORT_PATH | /tmp/e2e/export | Location of `shoot.config` file and test results |
| E2E_EXPORT_PATH | /tmp/e2e/export | Location of `shoot.config` file and test results |
| GINKGO_PARALLEL | true | Whether to run kubetest in parallel way. Testcases that consist of the `[Serial] tag are executed serially. |
| IGNORE_FALSE_POSITIVE_LIST | false | Ignores exclusion of testcases that are listed in `false_positive.json` |
| IGNORE_SKIP_LIST | false | Ignores exclusion of testcases that are listed in `skip.json` |
| INCLUDE_UNTRACKED_TESTS | false | Executes testcases that are not mentioned in description files for given provider and kubernetes release version |
| FLAKE_ATTEMPTS | 2 | Flake attempts for kubetest: how many time a failed test should be rerun |
| PUBLISH_RESULTS_TO_TESTGRID | false | Whether to push test results to google cloud storage, for testgrid |
| RETEST_FLAGGED_ONLY | false | Runs testcases with retest flag only. Value of `DESCRIPTION_FILE` is ignored |
| E2E_KUBECONFIG_PATH | $E2E_EXPORT_PATH | Dir of `shoot.config` file |

### Description Files
Example:
Expand Down
24 changes: 12 additions & 12 deletions test/e2etest/config/config.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package config

import (
"github.com/gardener/test-infra/test/e2etest/util"
tiutil "github.com/gardener/test-infra/pkg/util"
"github.com/pkg/errors"
log "github.com/sirupsen/logrus"
"os"
Expand Down Expand Up @@ -56,8 +56,8 @@ func init() {
if GoPath == "" {
log.Fatal("GOPATH environment variable not found")
}
GardenerVersion = util.GetEnv("GARDENER_VERSION", "")
ExportPath = util.GetEnv("EXPORT_PATH", path.Join(TmpDir, "export"))
GardenerVersion = tiutil.Getenv("GARDENER_VERSION", "")
ExportPath = tiutil.Getenv("E2E_EXPORT_PATH", path.Join(TmpDir, "export"))
if _, err := os.Stat(ExportPath); os.IsNotExist(err) {
if err := os.MkdirAll(ExportPath, os.FileMode(0777)); err != nil {
log.Fatal(err)
Expand All @@ -67,12 +67,12 @@ func init() {
K8sRoot = filepath.Join(GoPath, "src/k8s.io")
KubernetesPath = filepath.Join(K8sRoot, "kubernetes")
TestInfraPath = filepath.Join(K8sRoot, "test-infra")
ShootKubeconfigPath = filepath.Join(ExportPath, "shoot.config")
ShootKubeconfigPath = tiutil.Getenv("E2E_KUBECONFIG_PATH", filepath.Join(ExportPath, "shoot.config"))
if _, err := os.Stat(ShootKubeconfigPath); err != nil {
log.Fatal(errors.Wrapf(err, "file %s does not exist: ", ShootKubeconfigPath))
}
GinkgoParallel, _ = strconv.ParseBool(util.GetEnv("GINKGO_PARALLEL", "true"))
DescriptionFile = util.GetEnv("DESCRIPTION_FILE", WORKING_DESC_FILE)
GinkgoParallel = tiutil.GetenvBool("GINKGO_PARALLEL", true)
DescriptionFile = tiutil.Getenv("DESCRIPTION_FILE", WORKING_DESC_FILE)
K8sRelease = os.Getenv("K8S_VERSION")
if K8sRelease == "" {
log.Fatal("K8S_VERSION environment variable not found")
Expand All @@ -86,18 +86,18 @@ func init() {
if CloudProvider == "" {
log.Fatal("CLOUDPROVIDER environment variable not found")
}
IgnoreFalsePositiveList, _ = strconv.ParseBool(util.GetEnv("IGNORE_FALSE_POSITIVE_LIST", "false"))
IncludeUntrackedTests, _ = strconv.ParseBool(util.GetEnv("INCLUDE_UNTRACKED_TESTS", "false"))
IgnoreFalsePositiveList = tiutil.GetenvBool("IGNORE_FALSE_POSITIVE_LIST", false)
IncludeUntrackedTests = tiutil.GetenvBool("INCLUDE_UNTRACKED_TESTS", false)
K8sReleaseMajorMinor = string(regexp.MustCompile(`^(\d+\.\d+)`).FindSubmatch([]byte(K8sRelease))[1])
DescriptionsPath = path.Join(OwnDir, "kubetest", "description", K8sReleaseMajorMinor)
DescriptionFilePath = path.Join(DescriptionsPath, DescriptionFile)
if _, err := os.Stat(DescriptionFilePath); err != nil {
log.Fatal(errors.Wrapf(err, "file %s does not exist: ", DescriptionFilePath))
}
FlakeAttempts, _ = strconv.Atoi(util.GetEnv("FLAKE_ATTEMPTS", "2"))
PublishResultsToTestgrid, _ = strconv.ParseBool(util.GetEnv("PUBLISH_RESULTS_TO_TESTGRID", "false"))
IgnoreSkipList, _ = strconv.ParseBool(util.GetEnv("IGNORE_SKIP_LIST", "false"))
RetestFlaggedOnly, _ = strconv.ParseBool(util.GetEnv("RETEST_FLAGGED_ONLY", "false"))
FlakeAttempts, _ = strconv.Atoi(tiutil.Getenv("FLAKE_ATTEMPTS", "2"))
PublishResultsToTestgrid = tiutil.GetenvBool("PUBLISH_RESULTS_TO_TESTGRID", false)
IgnoreSkipList = tiutil.GetenvBool("IGNORE_SKIP_LIST", false)
RetestFlaggedOnly = tiutil.GetenvBool("RETEST_FLAGGED_ONLY", false)

log.Debugf("GoPath: %s", GoPath)
log.Debugf("K8sRoot: %s", K8sRoot)
Expand Down
8 changes: 3 additions & 5 deletions test/e2etest/kubetest/kubetest_runner.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,9 @@ const (
)

func init() {
kubectlPath, err := exec.Command("which", "kubectl").Output()
if err != nil {
log.Fatal(errors.Wrapf(err, "kubectl command unknown"))
}
if err = os.Setenv("KUBECTL_PATH", strings.TrimSpace(string(kubectlPath))); err != nil {
var err error
kubectlPath, _ := exec.Command("which", "kubectl").Output() // error is checked in previous steps
if err := os.Setenv("KUBECTL_PATH", strings.TrimSpace(string(kubectlPath))); err != nil {
log.Fatal(errors.Wrapf(err, "Couldn't set environment variable KUBECTL_PATH"))
}
log.Debugf("KUBECTL_PATH: '%s'", os.Getenv("KUBECTL_PATH"))
Expand Down
71 changes: 53 additions & 18 deletions test/e2etest/kubetest/results_evaluator.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,25 +35,30 @@ var MergedE2eLogFilePath = filepath.Join(config.ExportPath, MergedE2eLogFile)
func Analyze(kubetestResultsPath string) Summary {
log.Info("Analyze e2e.log and junit.xml files")
e2eLogFilePaths := util.GetFilesByPattern(kubetestResultsPath, E2eLogFileNamePattern)
summary := analyzeE2eLogs(e2eLogFilePaths)
summary, err := analyzeE2eLogs(e2eLogFilePaths)
if err != nil {
log.Fatal(errors.Wrapf(err, "results analysis failed at e2e.log analysis"))
}
junitXMLFilePaths := util.GetFilesByPattern(kubetestResultsPath, JunitXmlFileNamePattern)
analyzeJunitXMLs(junitXMLFilePaths, summary.TestsuiteDuration)
if err := analyzeJunitXMLs(junitXMLFilePaths, summary.TestsuiteDuration); err != nil {
log.Fatal(errors.Wrapf(err, "results analysis failed at junit.xml analysis"))
}
log.Infof("Check out result files in %s", kubetestResultsPath)
return summary
}

func analyzeJunitXMLs(junitXMLFilePaths []string, durationSec int) {
func analyzeJunitXMLs(junitXMLFilePaths []string, durationSec int) error {
var mergedJunitXmlResult = &JunitXMLResult{FailedTests: 0, ExecutedTests: 0, DurationFloat: 0, SuccessfulTests: 0, DurationInt: durationSec}
testcaseNameToTestcase := make(map[string]TestcaseResult)
for _, junitXMLPath := range junitXMLFilePaths {
file, err := os.Open(junitXMLPath)
if err != nil {
log.Fatal(err)
return err
}
defer file.Close()
junitXml, err := UnmarshalJunitXMLResult(file.Name())
if err != nil {
log.Fatal(errors.Wrapf(err, "Couldn't unmarshal %s", file.Name()))
return errors.Wrapf(err, "Couldn't unmarshal %s", file.Name())
}
mergedJunitXmlResult.FailedTests += junitXml.FailedTests
mergedJunitXmlResult.ExecutedTests += junitXml.ExecutedTests
Expand All @@ -68,38 +73,42 @@ func analyzeJunitXMLs(junitXMLFilePaths []string, durationSec int) {
testcaseNameToTestcase[testcase.Name] = testcase
testcaseJSON, err := json.MarshalIndent(testcase, "", " ")
if err != nil {
log.Fatal(errors.Wrapf(err, "Couldn't marshal testsuite summary %s", testcaseJSON))
return errors.Wrapf(err, "Couldn't marshal testsuite summary %s", testcaseJSON)
}

jsonFileName := fmt.Sprintf("test-%s.json", strconv.FormatInt(time.Now().UnixNano(), 10))
testcaseJsonFilePath := path.Join(config.ExportPath, jsonFileName)
if err := ioutil.WriteFile(testcaseJsonFilePath, testcaseJSON, 0644); err != nil {
log.Fatal(errors.Wrapf(err, "Couldn't write %s to file", testcaseJsonFilePath))
return errors.Wrapf(err, "Couldn't write %s to file", testcaseJsonFilePath)
}
}
}
for _, testcase := range testcaseNameToTestcase {
schrodit marked this conversation as resolved.
Show resolved Hide resolved
mergedJunitXmlResult.Testcases = append(mergedJunitXmlResult.Testcases, testcase)
}
saveJunitXmlToFile(mergedJunitXmlResult)
if err := saveJunitXmlToFile(mergedJunitXmlResult); err != nil {
return err
}
return nil
}

func saveJunitXmlToFile(mergedJunitXmlResult *JunitXMLResult) {
func saveJunitXmlToFile(mergedJunitXmlResult *JunitXMLResult) error {
output, err := xml.MarshalIndent(mergedJunitXmlResult, " ", " ")
if err != nil {
fmt.Printf("error: %v\n", err)
return err
}
output = append([]byte(xml.Header), output...)

file, _ := os.Create(mergedJunitXmlFilePath)
defer file.Close()
if _, err = file.Write(output); err != nil {
log.Fatal(err)
return err
}
return nil
}

func analyzeE2eLogs(e2eLogFilePaths []string) Summary {
summary := Summary{TestsuiteSuccessful: false, FailedTestcases: 0, SuccessfulTestcases: 0, ExecutedTestcases: 0, TestsuiteDuration: 0, FlakedTestcases: 0, DescriptionFile: config.DescriptionFile, Flaked: false}
func analyzeE2eLogs(e2eLogFilePaths []string) (Summary, error) {
summary := Summary{DescriptionFile: config.DescriptionFile}
regexpRanSpecs := regexp.MustCompile(`Ran (?P<TestcasesRan>\d+).*Specs.in (?P<TestSuiteDuration>\d+)`)
regexpPassedFailed := regexp.MustCompile(`(?P<Passed>\d+) Passed.*(?P<Failed>\d+) Failed.*Pending`)

Expand All @@ -114,17 +123,26 @@ func analyzeE2eLogs(e2eLogFilePaths []string) Summary {
for scanner.Scan() {
if regexpRanSpecs.MatchString(scanner.Text()) {
groupToValue, _ := util.GetGroupMapOfRegexMatches(regexpRanSpecs, scanner.Text())
summary.ExecutedTestcases += util.SilentStrToInt(groupToValue["TestcasesRan"])
summary.TestsuiteDuration += util.SilentStrToInt(groupToValue["TestSuiteDuration"])
groupToValueInt, err := convertValuesToInt(groupToValue)
if err != nil {
return summary, errors.Errorf("Empty or non integer values in map, for regexp %s", regexpRanSpecs.String())
}
summary.ExecutedTestcases += groupToValueInt["TestcasesRan"]
summary.TestsuiteDuration += groupToValueInt["TestSuiteDuration"]
}
if regexpPassedFailed.MatchString(scanner.Text()) {
groupToValue, _ := util.GetGroupMapOfRegexMatches(regexpPassedFailed, scanner.Text())
summary.SuccessfulTestcases += util.SilentStrToInt(groupToValue["Passed"])
summary.FailedTestcases += util.SilentStrToInt(groupToValue["Failed"])
groupToValueInt, err := convertValuesToInt(groupToValue)
if err != nil {
return summary, errors.Errorf("Empty or non integer values in map, for regexp %s", regexpRanSpecs.String())
}
summary.SuccessfulTestcases += groupToValueInt["Passed"]
summary.FailedTestcases += groupToValueInt["Failed"]
summary.TestsuiteSuccessful = summary.FailedTestcases == 0
}
}

//TODO
summary.Flaked = summary.FlakedTestcases != 0
}
summary.ExecutionGroup = strings.Join(config.TestcaseGroup, ",")
Expand All @@ -142,7 +160,24 @@ func analyzeE2eLogs(e2eLogFilePaths []string) Summary {
}

mergeE2eLogFiles(MergedE2eLogFilePath, e2eLogFilePaths)
return summary
return summary, nil
}

func convertValuesToInt(m map[string]string) (map[string]int, error) {
convertedMap := make(map[string]int, len(m))
first := true
for key, value := range m {
if first {
first = false
continue // first element is always the whole match
}
convertedValue, err := strconv.Atoi(value)
if err != nil {
return nil, err
}
convertedMap[key] = convertedValue
}
return convertedMap, nil
}

func mergeE2eLogFiles(dst string, e2eLogFilePaths []string) {
Expand Down
5 changes: 5 additions & 0 deletions test/e2etest/kubetest/setup/setup.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,11 @@ import (
"github.com/gardener/test-infra/test/e2etest/kubetest"
"github.com/gardener/test-infra/test/e2etest/util"
"github.com/mholt/archiver"
"github.com/pkg/errors"
log "github.com/sirupsen/logrus"
"net/http"
"os"
"os/exec"
"path"
"path/filepath"
"runtime"
Expand Down Expand Up @@ -50,6 +52,9 @@ func cleanUpPreviousRuns() {

func areTestUtilitiesReady() bool {
log.Info("checking whether any test utility is not ready")
if _, err := exec.Command("which", "kubectl").Output(); err != nil {
log.Fatal(errors.Wrapf(err, "kubectl command unknown"))
}
e2eTestPath := path.Join(k8sOutputBinDir, "e2e.test")
if _, err := os.Stat(e2eTestPath); os.IsNotExist(err) {
log.Infof("test utility not ready: %s", e2eTestPath)
Expand Down
18 changes: 9 additions & 9 deletions test/e2etest/kubetest/xml_junit_result.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,23 +39,23 @@ type JunitXMLResult struct {
ExecutedTests int `xml:"tests,attr"`
FailedTests int `xml:"failures,attr"`
DurationFloat float32 `xml:"time,attr"`
DurationInt int `xml:"-"`
Testcases []TestcaseResult `xml:"testcase"`
SuccessfulTests int `xml:"-"`
DurationInt int `xml:"-"` // calculated
SuccessfulTests int `xml:"-"` // calculated
}

type TestcaseResult struct {
XMLName xml.Name `xml:"testcase" json:"-"`
Name string `xml:"name,attr" json:"name"`
Status string `xml:"-" json:"status"`
Status string `xml:"-" json:"status"` // calculated
SkippedRaw *struct{} `xml:"skipped" json:"-"`
Skipped bool `xml:"-" json:"-"`
Skipped bool `xml:"-" json:"-"` // calculated
FailureText string `xml:"failure,omitempty" json:"failure.text,omitempty"`
SystemOutput string `xml:"system-out,omitempty" json:"system-out,omitempty"`
DurationFloat float32 `xml:"time,attr" json:"-"`
DurationInt int `xml:"-" json:"duration"`
SigGroup string `xml:"-" json:"sig"`
TestDesc string `xml:"-" json:"test_desc_file"`
ExecutionGroup string `xml:"-" json:"execution_group"`
Successful bool `xml:"-" json:"successful"`
DurationInt int `xml:"-" json:"duration"` // calculated
SigGroup string `xml:"-" json:"sig"` // calculated
TestDesc string `xml:"-" json:"test_desc_file"` // calculated
ExecutionGroup string `xml:"-" json:"execution_group"` // calculated
Successful bool `xml:"-" json:"successful"` // calculated
}
Loading