Commit 16640d89 authored by Kubernetes Submit Queue's avatar Kubernetes Submit Queue Committed by GitHub

Merge pull request #46135 from krousey/upgrades

Automatic merge from submit-queue (batch tested with PRs 47470, 47260, 47411, 46852, 46135) Write reports for each upgrade test Due to the way Ginkgo runs individual test cases and the level of coordination required for the upgrade tests, they were all run under a single Ginkgo test case. This PR generates and auxiliary report that break out the results of each upgrade test. This is accomplished by: 1) Wrapping `ginkgo.Fail` and `ginkgo.Skip` to get the actual failure or skip messages. 2) Recovering that info in the upgrade test to generate an auxiliary report. I suggest reviewing commit by commit. Sample report: https://storage.googleapis.com/krouseytestreports/logs/results/1/artifacts/junit_upgrades.xml Fixes: #47371
parents d0678360 32db61da
...@@ -457,6 +457,7 @@ staging/src/k8s.io/metrics/pkg/apis/custom_metrics/install ...@@ -457,6 +457,7 @@ staging/src/k8s.io/metrics/pkg/apis/custom_metrics/install
staging/src/k8s.io/metrics/pkg/apis/metrics/install staging/src/k8s.io/metrics/pkg/apis/metrics/install
staging/src/k8s.io/sample-apiserver staging/src/k8s.io/sample-apiserver
staging/src/k8s.io/sample-apiserver/pkg/apis/wardle/install staging/src/k8s.io/sample-apiserver/pkg/apis/wardle/install
test/e2e/framework/ginkgowrapper
test/e2e/perftype test/e2e/perftype
test/e2e_node/perftype test/e2e_node/perftype
test/e2e_node/runner/local test/e2e_node/runner/local
...@@ -495,3 +496,4 @@ test/integration/thirdparty ...@@ -495,3 +496,4 @@ test/integration/thirdparty
test/integration/ttlcontroller test/integration/ttlcontroller
test/soak/cauldron test/soak/cauldron
test/soak/serve_hostnames test/soak/serve_hostnames
test/utils/junit
...@@ -164,6 +164,7 @@ go_library( ...@@ -164,6 +164,7 @@ go_library(
"//test/e2e/chaosmonkey:go_default_library", "//test/e2e/chaosmonkey:go_default_library",
"//test/e2e/common:go_default_library", "//test/e2e/common:go_default_library",
"//test/e2e/framework:go_default_library", "//test/e2e/framework:go_default_library",
"//test/e2e/framework/ginkgowrapper:go_default_library",
"//test/e2e/generated:go_default_library", "//test/e2e/generated:go_default_library",
"//test/e2e/perf:go_default_library", "//test/e2e/perf:go_default_library",
"//test/e2e/scheduling:go_default_library", "//test/e2e/scheduling:go_default_library",
...@@ -171,6 +172,7 @@ go_library( ...@@ -171,6 +172,7 @@ go_library(
"//test/e2e_federation:go_default_library", "//test/e2e_federation:go_default_library",
"//test/images/net/nat:go_default_library", "//test/images/net/nat:go_default_library",
"//test/utils:go_default_library", "//test/utils:go_default_library",
"//test/utils/junit:go_default_library",
"//vendor/github.com/davecgh/go-spew/spew:go_default_library", "//vendor/github.com/davecgh/go-spew/spew:go_default_library",
"//vendor/github.com/elazarl/goproxy:go_default_library", "//vendor/github.com/elazarl/goproxy:go_default_library",
"//vendor/github.com/ghodss/yaml:go_default_library", "//vendor/github.com/ghodss/yaml:go_default_library",
......
...@@ -237,7 +237,7 @@ var _ = framework.KubeDescribe("Probing container", func() { ...@@ -237,7 +237,7 @@ var _ = framework.KubeDescribe("Probing container", func() {
It("should be restarted with a docker exec liveness probe with timeout [Conformance]", func() { It("should be restarted with a docker exec liveness probe with timeout [Conformance]", func() {
// TODO: enable this test once the default exec handler supports timeout. // TODO: enable this test once the default exec handler supports timeout.
Skip("The default exec handler, dockertools.NativeExecHandler, does not support timeouts due to a limitation in the Docker Remote API") framework.Skipf("The default exec handler, dockertools.NativeExecHandler, does not support timeouts due to a limitation in the Docker Remote API")
runLivenessTest(f, &v1.Pod{ runLivenessTest(f, &v1.Pod{
ObjectMeta: metav1.ObjectMeta{ ObjectMeta: metav1.ObjectMeta{
Name: "liveness-exec", Name: "liveness-exec",
......
...@@ -196,7 +196,7 @@ var _ = framework.KubeDescribe("Pods", func() { ...@@ -196,7 +196,7 @@ var _ = framework.KubeDescribe("Pods", func() {
framework.Failf("Failed to observe pod creation: %v", event) framework.Failf("Failed to observe pod creation: %v", event)
} }
case <-time.After(framework.PodStartTimeout): case <-time.After(framework.PodStartTimeout):
Fail("Timeout while waiting for pod creation") framework.Failf("Timeout while waiting for pod creation")
} }
// We need to wait for the pod to be running, otherwise the deletion // We need to wait for the pod to be running, otherwise the deletion
...@@ -245,14 +245,14 @@ var _ = framework.KubeDescribe("Pods", func() { ...@@ -245,14 +245,14 @@ var _ = framework.KubeDescribe("Pods", func() {
deleted = true deleted = true
case watch.Error: case watch.Error:
framework.Logf("received a watch error: %v", event.Object) framework.Logf("received a watch error: %v", event.Object)
Fail("watch closed with error") framework.Failf("watch closed with error")
} }
case <-timer: case <-timer:
Fail("timed out waiting for pod deletion") framework.Failf("timed out waiting for pod deletion")
} }
} }
if !deleted { if !deleted {
Fail("Failed to observe pod deletion") framework.Failf("Failed to observe pod deletion")
} }
Expect(lastPod.DeletionTimestamp).ToNot(BeNil()) Expect(lastPod.DeletionTimestamp).ToNot(BeNil())
......
...@@ -44,6 +44,7 @@ import ( ...@@ -44,6 +44,7 @@ import (
"k8s.io/kubernetes/pkg/util/logs" "k8s.io/kubernetes/pkg/util/logs"
commontest "k8s.io/kubernetes/test/e2e/common" commontest "k8s.io/kubernetes/test/e2e/common"
"k8s.io/kubernetes/test/e2e/framework" "k8s.io/kubernetes/test/e2e/framework"
"k8s.io/kubernetes/test/e2e/framework/ginkgowrapper"
"k8s.io/kubernetes/test/e2e/generated" "k8s.io/kubernetes/test/e2e/generated"
federationtest "k8s.io/kubernetes/test/e2e_federation" federationtest "k8s.io/kubernetes/test/e2e_federation"
testutils "k8s.io/kubernetes/test/utils" testutils "k8s.io/kubernetes/test/utils"
...@@ -318,7 +319,7 @@ func RunE2ETests(t *testing.T) { ...@@ -318,7 +319,7 @@ func RunE2ETests(t *testing.T) {
logs.InitLogs() logs.InitLogs()
defer logs.FlushLogs() defer logs.FlushLogs()
gomega.RegisterFailHandler(ginkgo.Fail) gomega.RegisterFailHandler(ginkgowrapper.Fail)
// Disable skipped tests unless they are explicitly requested. // Disable skipped tests unless they are explicitly requested.
if config.GinkgoConfig.FocusString == "" && config.GinkgoConfig.SkipString == "" { if config.GinkgoConfig.FocusString == "" && config.GinkgoConfig.SkipString == "" {
config.GinkgoConfig.SkipString = `\[Flaky\]|\[Feature:.+\]` config.GinkgoConfig.SkipString = `\[Flaky\]|\[Feature:.+\]`
......
...@@ -86,6 +86,7 @@ go_library( ...@@ -86,6 +86,7 @@ go_library(
"//pkg/volume/util/volumehelper:go_default_library", "//pkg/volume/util/volumehelper:go_default_library",
"//plugin/pkg/scheduler/algorithm/predicates:go_default_library", "//plugin/pkg/scheduler/algorithm/predicates:go_default_library",
"//plugin/pkg/scheduler/schedulercache:go_default_library", "//plugin/pkg/scheduler/schedulercache:go_default_library",
"//test/e2e/framework/ginkgowrapper:go_default_library",
"//test/e2e/generated:go_default_library", "//test/e2e/generated:go_default_library",
"//test/e2e/perftype:go_default_library", "//test/e2e/perftype:go_default_library",
"//test/utils:go_default_library", "//test/utils:go_default_library",
...@@ -145,6 +146,9 @@ filegroup( ...@@ -145,6 +146,9 @@ filegroup(
filegroup( filegroup(
name = "all-srcs", name = "all-srcs",
srcs = [":package-srcs"], srcs = [
":package-srcs",
"//test/e2e/framework/ginkgowrapper:all-srcs",
],
tags = ["automanaged"], tags = ["automanaged"],
) )
package(default_visibility = ["//visibility:public"])
licenses(["notice"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = ["wrapper.go"],
tags = ["automanaged"],
deps = ["//vendor/github.com/onsi/ginkgo:go_default_library"],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
)
/*
Copyright 2017 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Package ginkgowrapper wraps Ginkgo Fail and Skip functions to panic
// with structured data instead of a constant string.
package ginkgowrapper
import (
"bufio"
"bytes"
"regexp"
"runtime"
"runtime/debug"
"strings"
"github.com/onsi/ginkgo"
)
// FailurePanic is the value that will be panicked from Fail.
type FailurePanic struct {
Message string // The failure message passed to Fail
Filename string // The filename that is the source of the failure
Line int // The line number of the filename that is the source of the failure
FullStackTrace string // A full stack trace starting at the source of the failure
}
// String makes FailurePanic look like the old Ginkgo panic when printed.
func (FailurePanic) String() string { return ginkgo.GINKGO_PANIC }
// Fail wraps ginkgo.Fail so that it panics with more useful
// information about the failure. This function will panic with a
// FailurePanic.
func Fail(message string, callerSkip ...int) {
skip := 1
if len(callerSkip) > 0 {
skip += callerSkip[0]
}
_, file, line, _ := runtime.Caller(skip)
fp := FailurePanic{
Message: message,
Filename: file,
Line: line,
FullStackTrace: pruneStack(skip),
}
defer func() {
e := recover()
if e != nil {
panic(fp)
}
}()
ginkgo.Fail(message, skip)
}
// SkipPanic is the value that will be panicked from Skip.
type SkipPanic struct {
Message string // The failure message passed to Fail
Filename string // The filename that is the source of the failure
Line int // The line number of the filename that is the source of the failure
FullStackTrace string // A full stack trace starting at the source of the failure
}
// String makes SkipPanic look like the old Ginkgo panic when printed.
func (SkipPanic) String() string { return ginkgo.GINKGO_PANIC }
// Skip wraps ginkgo.Skip so that it panics with more useful
// information about why the test is being skipped. This function will
// panic with a SkipPanic.
func Skip(message string, callerSkip ...int) {
skip := 1
if len(callerSkip) > 0 {
skip += callerSkip[0]
}
_, file, line, _ := runtime.Caller(skip)
sp := SkipPanic{
Message: message,
Filename: file,
Line: line,
FullStackTrace: pruneStack(skip),
}
defer func() {
e := recover()
if e != nil {
panic(sp)
}
}()
ginkgo.Skip(message, skip)
}
// ginkgo adds a lot of test running infrastructure to the stack, so
// we filter those out
var stackSkipPattern = regexp.MustCompile(`onsi/ginkgo`)
func pruneStack(skip int) string {
skip += 2 // one for pruneStack and one for debug.Stack
stack := debug.Stack()
scanner := bufio.NewScanner(bytes.NewBuffer(stack))
var prunedStack []string
// skip the top of the stack
for i := 0; i < 2*skip+1; i++ {
scanner.Scan()
}
for scanner.Scan() {
if stackSkipPattern.Match(scanner.Bytes()) {
scanner.Scan() // these come in pairs
} else {
prunedStack = append(prunedStack, scanner.Text())
scanner.Scan() // these come in pairs
prunedStack = append(prunedStack, scanner.Text())
}
}
return strings.Join(prunedStack, "\n")
}
...@@ -98,6 +98,7 @@ import ( ...@@ -98,6 +98,7 @@ import (
utilversion "k8s.io/kubernetes/pkg/util/version" utilversion "k8s.io/kubernetes/pkg/util/version"
"k8s.io/kubernetes/plugin/pkg/scheduler/algorithm/predicates" "k8s.io/kubernetes/plugin/pkg/scheduler/algorithm/predicates"
"k8s.io/kubernetes/plugin/pkg/scheduler/schedulercache" "k8s.io/kubernetes/plugin/pkg/scheduler/schedulercache"
"k8s.io/kubernetes/test/e2e/framework/ginkgowrapper"
testutil "k8s.io/kubernetes/test/utils" testutil "k8s.io/kubernetes/test/utils"
) )
...@@ -293,13 +294,13 @@ func Failf(format string, args ...interface{}) { ...@@ -293,13 +294,13 @@ func Failf(format string, args ...interface{}) {
func FailfWithOffset(offset int, format string, args ...interface{}) { func FailfWithOffset(offset int, format string, args ...interface{}) {
msg := fmt.Sprintf(format, args...) msg := fmt.Sprintf(format, args...)
log("INFO", msg) log("INFO", msg)
Fail(nowStamp()+": "+msg, 1+offset) ginkgowrapper.Fail(nowStamp()+": "+msg, 1+offset)
} }
func Skipf(format string, args ...interface{}) { func Skipf(format string, args ...interface{}) {
msg := fmt.Sprintf(format, args...) msg := fmt.Sprintf(format, args...)
log("INFO", msg) log("INFO", msg)
Skip(nowStamp() + ": " + msg) ginkgowrapper.Skip(nowStamp() + ": " + msg)
} }
func SkipUnlessNodeCountIsAtLeast(minNodeCount int) { func SkipUnlessNodeCountIsAtLeast(minNodeCount int) {
......
...@@ -958,7 +958,7 @@ metadata: ...@@ -958,7 +958,7 @@ metadata:
return false, nil return false, nil
} }
if len(uidToPort) > 1 { if len(uidToPort) > 1 {
Fail("Too many endpoints found") framework.Failf("Too many endpoints found")
} }
for _, port := range uidToPort { for _, port := range uidToPort {
if port[0] != redisPort { if port[0] != redisPort {
......
...@@ -127,7 +127,7 @@ var _ = framework.KubeDescribe("PodPreset", func() { ...@@ -127,7 +127,7 @@ var _ = framework.KubeDescribe("PodPreset", func() {
framework.Failf("Failed to observe pod creation: %v", event) framework.Failf("Failed to observe pod creation: %v", event)
} }
case <-time.After(framework.PodStartTimeout): case <-time.After(framework.PodStartTimeout):
Fail("Timeout while waiting for pod creation") framework.Failf("Timeout while waiting for pod creation")
} }
// We need to wait for the pod to be running, otherwise the deletion // We need to wait for the pod to be running, otherwise the deletion
...@@ -233,7 +233,7 @@ var _ = framework.KubeDescribe("PodPreset", func() { ...@@ -233,7 +233,7 @@ var _ = framework.KubeDescribe("PodPreset", func() {
framework.Failf("Failed to observe pod creation: %v", event) framework.Failf("Failed to observe pod creation: %v", event)
} }
case <-time.After(framework.PodStartTimeout): case <-time.After(framework.PodStartTimeout):
Fail("Timeout while waiting for pod creation") framework.Failf("Timeout while waiting for pod creation")
} }
// We need to wait for the pod to be running, otherwise the deletion // We need to wait for the pod to be running, otherwise the deletion
......
...@@ -98,7 +98,7 @@ var _ = framework.KubeDescribe("Pods Extended", func() { ...@@ -98,7 +98,7 @@ var _ = framework.KubeDescribe("Pods Extended", func() {
framework.Failf("Failed to observe pod creation: %v", event) framework.Failf("Failed to observe pod creation: %v", event)
} }
case <-time.After(framework.PodStartTimeout): case <-time.After(framework.PodStartTimeout):
Fail("Timeout while waiting for pod creation") framework.Failf("Timeout while waiting for pod creation")
} }
// We need to wait for the pod to be running, otherwise the deletion // We need to wait for the pod to be running, otherwise the deletion
...@@ -178,7 +178,7 @@ var _ = framework.KubeDescribe("Pods Extended", func() { ...@@ -178,7 +178,7 @@ var _ = framework.KubeDescribe("Pods Extended", func() {
} }
} }
if !deleted { if !deleted {
Fail("Failed to observe pod deletion") framework.Failf("Failed to observe pod deletion")
} }
Expect(lastPod.DeletionTimestamp).ToNot(BeNil()) Expect(lastPod.DeletionTimestamp).ToNot(BeNil())
......
...@@ -269,7 +269,7 @@ var _ = framework.KubeDescribe("Proxy", func() { ...@@ -269,7 +269,7 @@ var _ = framework.KubeDescribe("Proxy", func() {
framework.Logf("Pod %s has the following error logs: %s", pods[0].Name, body) framework.Logf("Pod %s has the following error logs: %s", pods[0].Name, body)
} }
Fail(strings.Join(errs, "\n")) framework.Failf(strings.Join(errs, "\n"))
} }
}) })
}) })
......
...@@ -50,7 +50,7 @@ var _ = framework.KubeDescribe("Opaque resources [Feature:OpaqueResources]", fun ...@@ -50,7 +50,7 @@ var _ = framework.KubeDescribe("Opaque resources [Feature:OpaqueResources]", fun
} }
} }
if node == nil { if node == nil {
Fail("unable to select a non-master node") framework.Failf("unable to select a non-master node")
} }
} }
......
...@@ -85,7 +85,7 @@ var _ = framework.KubeDescribe("Service endpoints latency", func() { ...@@ -85,7 +85,7 @@ var _ = framework.KubeDescribe("Service endpoints latency", func() {
} }
if n < 2 { if n < 2 {
failing.Insert("Less than two runs succeeded; aborting.") failing.Insert("Less than two runs succeeded; aborting.")
Fail(strings.Join(failing.List(), "\n")) framework.Failf(strings.Join(failing.List(), "\n"))
} }
percentile := func(p int) time.Duration { percentile := func(p int) time.Duration {
est := n * p / 100 est := n * p / 100
...@@ -112,7 +112,7 @@ var _ = framework.KubeDescribe("Service endpoints latency", func() { ...@@ -112,7 +112,7 @@ var _ = framework.KubeDescribe("Service endpoints latency", func() {
if failing.Len() > 0 { if failing.Len() > 0 {
errList := strings.Join(failing.List(), "\n") errList := strings.Join(failing.List(), "\n")
helpfulInfo := fmt.Sprintf("\n50, 90, 99 percentiles: %v %v %v", p50, p90, p99) helpfulInfo := fmt.Sprintf("\n50, 90, 99 percentiles: %v %v %v", p50, p90, p99)
Fail(errList + helpfulInfo) framework.Failf(errList + helpfulInfo)
} }
}) })
}) })
......
...@@ -59,6 +59,9 @@ filegroup( ...@@ -59,6 +59,9 @@ filegroup(
filegroup( filegroup(
name = "all-srcs", name = "all-srcs",
srcs = [":package-srcs"], srcs = [
":package-srcs",
"//test/utils/junit:all-srcs",
],
tags = ["automanaged"], tags = ["automanaged"],
) )
package(default_visibility = ["//visibility:public"])
licenses(["notice"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = ["junit.go"],
tags = ["automanaged"],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
)
/*
Copyright 2017 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Package junit provides data structures to allow easy XML encoding
// and decoding of JUnit test results.
package junit
import (
"encoding/xml"
"time"
)
// TestSuite is a top-level test suite containing test cases.
type TestSuite struct {
XMLName xml.Name `xml:"testsuite"`
Name string `xml:"name,attr"`
Tests int `xml:"tests,attr"`
Disabled int `xml:"disabled,attr,omitempty"`
Errors int `xml:"errors,attr"`
Failures int `xml:"failures,attr"`
Skipped int `xml:"skipped,attr,omitempty"`
Time float64 `xml:"time,attr"`
Timestamp time.Time `xml:"timestamp,attr"`
ID int `xml:"id,attr,omitempty"`
Package string `xml:"package,attr,omitempty"`
Hostname string `xml:"hostname,attr"`
Properties []*Property `xml:"properties,omitempty"`
TestCases []*TestCase `xml:"testcase"`
SystemOut string `xml:"system-out,omitempty"`
SystemErr string `xml:"system-err,omitempty"`
}
// Update iterates through the TestCases and updates Tests, Errors,
// Failures, and Skipped top level attributes.
func (t *TestSuite) Update() {
t.Tests = len(t.TestCases)
for _, tc := range t.TestCases {
t.Errors += len(tc.Errors)
t.Failures += len(tc.Failures)
if len(tc.Skipped) > 0 {
t.Skipped++
}
}
}
// Property is a simple key-value property that can be attached to a TestSuite.
type Property struct {
XMLName xml.Name `xml:"property"`
Name string `xml:"name,attr"`
Value string `xml:"value,attr"`
}
// Error represents the errors in a test case.
type Error struct {
XMLName xml.Name `xml:"error"`
Message string `xml:"message,attr,omitempty"`
Type string `xml:"type,attr"`
Value string `xml:",cdata"`
}
// Failure represents the failures in a test case.
type Failure struct {
XMLName xml.Name `xml:"failure"`
Message string `xml:"message,attr,omitempty"`
Type string `xml:"type,attr"`
Value string `xml:",cdata"`
}
// TestCase represents a single test case within a suite.
type TestCase struct {
XMLName xml.Name `xml:"testcase"`
Name string `xml:"name,attr"`
Classname string `xml:"classname,attr"`
Status string `xml:"status,attr,omitempty"`
Assertions int `xml:"assertions,attr,omitempty"`
Time float64 `xml:"time,attr"`
Skipped string `xml:"skipped,omitempty"`
Errors []*Error `xml:"error,omitempty"`
Failures []*Failure `xml:"failure,omitempty"`
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment