...

Source file src/k8s.io/kubernetes/test/e2e/framework/internal/unittests/cleanup/cleanup_test.go

Documentation: k8s.io/kubernetes/test/e2e/framework/internal/unittests/cleanup

     1  //go:build linux && amd64
     2  
     3  /*
     4  Copyright 2022 The Kubernetes Authors.
     5  
     6  Licensed under the Apache License, Version 2.0 (the "License");
     7  you may not use this file except in compliance with the License.
     8  You may obtain a copy of the License at
     9  
    10      http://www.apache.org/licenses/LICENSE-2.0
    11  
    12  Unless required by applicable law or agreed to in writing, software
    13  distributed under the License is distributed on an "AS IS" BASIS,
    14  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    15  See the License for the specific language governing permissions and
    16  limitations under the License.
    17  */
    18  
    19  // This test uses etcd that is only fully supported for AMD64 and Linux
    20  // https://etcd.io/docs/v3.5/op-guide/supported-platform/#support-tiers
    21  
    22  package cleanup
    23  
    24  import (
    25  	"context"
    26  	"flag"
    27  	"fmt"
    28  	"regexp"
    29  	"testing"
    30  
    31  	"github.com/onsi/ginkgo/v2"
    32  	"github.com/onsi/ginkgo/v2/reporters"
    33  
    34  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    35  	"k8s.io/klog/v2"
    36  	"k8s.io/klog/v2/ktesting"
    37  	"k8s.io/kubernetes/test/e2e/framework"
    38  	"k8s.io/kubernetes/test/e2e/framework/internal/output"
    39  	testapiserver "k8s.io/kubernetes/test/utils/apiserver"
    40  )
    41  
    42  // The line number of the following code is checked in TestFailureOutput below.
    43  // Be careful when moving it around or changing the import statements above.
    44  // Here are some intentionally blank lines that can be removed to compensate
    45  // for future additional import statements.
    46  //
    47  //
    48  //
    49  //
    50  // This must be line #50.
    51  
    52  func init() {
    53  	framework.NewFrameworkExtensions = append(framework.NewFrameworkExtensions,
    54  		// This callback runs directly after NewDefaultFramework is done.
    55  		func(f *framework.Framework) {
    56  			ginkgo.BeforeEach(func() { framework.Logf("extension before") })
    57  			ginkgo.AfterEach(func() { framework.Logf("extension after") })
    58  		},
    59  	)
    60  }
    61  
    62  var _ = ginkgo.Describe("e2e", func() {
    63  	ginkgo.BeforeEach(func() {
    64  		logBeforeHelper()
    65  	})
    66  
    67  	f := framework.NewDefaultFramework("test-namespace")
    68  
    69  	// BeforeEach/AfterEach run in first-in-first-out order.
    70  
    71  	ginkgo.BeforeEach(func() {
    72  		framework.Logf("before #1")
    73  	})
    74  
    75  	ginkgo.BeforeEach(func() {
    76  		framework.Logf("before #2")
    77  	})
    78  
    79  	ginkgo.AfterEach(func() {
    80  		framework.Logf("after #1")
    81  		if f.ClientSet == nil {
    82  			framework.Fail("Wrong order of cleanup operations: framework.AfterEach already ran and cleared f.ClientSet.")
    83  		}
    84  	})
    85  
    86  	ginkgo.AfterEach(func() {
    87  		framework.Logf("after #2")
    88  	})
    89  
    90  	ginkgo.It("works", func(ctx context.Context) {
    91  		// DeferCleanup invokes in first-in-last-out order
    92  		ginkgo.DeferCleanup(func() {
    93  			framework.Logf("cleanup last")
    94  		})
    95  		ginkgo.DeferCleanup(func() {
    96  			framework.Logf("cleanup first")
    97  		})
    98  
    99  		ginkgo.DeferCleanup(framework.IgnoreNotFound(f.ClientSet.CoreV1().PersistentVolumes().Delete), "simple", metav1.DeleteOptions{})
   100  		fail := func(ctx context.Context, name string) error {
   101  			return fmt.Errorf("fake error for %q", name)
   102  		}
   103  		ginkgo.DeferCleanup(framework.IgnoreNotFound(fail), "failure") // Without a failure the output would not be shown in JUnit.
   104  
   105  		// More test cases can be added here without affeccting line numbering
   106  		// of existing tests.
   107  	})
   108  })
   109  
   110  // logBeforeHelper must be skipped when doing stack unwinding in the logging
   111  // implementation.
   112  func logBeforeHelper() {
   113  	ginkgo.GinkgoHelper()
   114  	framework.Logf("before")
   115  }
   116  
   117  const (
   118  	ginkgoOutput = `> Enter [BeforeEach] e2e - cleanup_test.go:63 <time>
   119  <klog> cleanup_test.go:64] before
   120  < Exit [BeforeEach] e2e - cleanup_test.go:63 <time>
   121  > Enter [BeforeEach] e2e - set up framework | framework.go:xxx <time>
   122  STEP: Creating a kubernetes client - framework.go:xxx <time>
   123  <klog> util.go:xxx] >>> kubeConfig: yyy/kube.config
   124  STEP: Building a namespace api object, basename test-namespace - framework.go:xxx <time>
   125  <klog> framework.go:xxx] Skipping waiting for service account
   126  < Exit [BeforeEach] e2e - set up framework | framework.go:xxx <time>
   127  > Enter [BeforeEach] e2e - cleanup_test.go:56 <time>
   128  <klog> cleanup_test.go:56] extension before
   129  < Exit [BeforeEach] e2e - cleanup_test.go:56 <time>
   130  > Enter [BeforeEach] e2e - cleanup_test.go:71 <time>
   131  <klog> cleanup_test.go:72] before #1
   132  < Exit [BeforeEach] e2e - cleanup_test.go:71 <time>
   133  > Enter [BeforeEach] e2e - cleanup_test.go:75 <time>
   134  <klog> cleanup_test.go:76] before #2
   135  < Exit [BeforeEach] e2e - cleanup_test.go:75 <time>
   136  > Enter [It] works - cleanup_test.go:90 <time>
   137  < Exit [It] works - cleanup_test.go:90 <time>
   138  > Enter [AfterEach] e2e - cleanup_test.go:57 <time>
   139  <klog> cleanup_test.go:57] extension after
   140  < Exit [AfterEach] e2e - cleanup_test.go:57 <time>
   141  > Enter [AfterEach] e2e - cleanup_test.go:79 <time>
   142  <klog> cleanup_test.go:80] after #1
   143  < Exit [AfterEach] e2e - cleanup_test.go:79 <time>
   144  > Enter [AfterEach] e2e - cleanup_test.go:86 <time>
   145  <klog> cleanup_test.go:87] after #2
   146  < Exit [AfterEach] e2e - cleanup_test.go:86 <time>
   147  > Enter [DeferCleanup (Each)] e2e - cleanup_test.go:103 <time>
   148  [FAILED] DeferCleanup callback returned error: fake error for "failure"
   149  In [DeferCleanup (Each)] at: cleanup_test.go:103 <time>
   150  < Exit [DeferCleanup (Each)] e2e - cleanup_test.go:103 <time>
   151  > Enter [DeferCleanup (Each)] e2e - cleanup_test.go:99 <time>
   152  < Exit [DeferCleanup (Each)] e2e - cleanup_test.go:99 <time>
   153  > Enter [DeferCleanup (Each)] e2e - cleanup_test.go:95 <time>
   154  <klog> cleanup_test.go:96] cleanup first
   155  < Exit [DeferCleanup (Each)] e2e - cleanup_test.go:95 <time>
   156  > Enter [DeferCleanup (Each)] e2e - cleanup_test.go:92 <time>
   157  <klog> cleanup_test.go:93] cleanup last
   158  < Exit [DeferCleanup (Each)] e2e - cleanup_test.go:92 <time>
   159  > Enter [DeferCleanup (Each)] e2e - dump namespaces | framework.go:xxx <time>
   160  < Exit [DeferCleanup (Each)] e2e - dump namespaces | framework.go:xxx <time>
   161  > Enter [DeferCleanup (Each)] e2e - tear down framework | framework.go:xxx <time>
   162  STEP: Destroying namespace "test-namespace-zzz" for this suite. - framework.go:xxx <time>
   163  < Exit [DeferCleanup (Each)] e2e - tear down framework | framework.go:xxx <time>
   164  `
   165  )
   166  
   167  func TestCleanup(t *testing.T) {
   168  	// The control plane is noisy and randomly logs through klog, for example:
   169  	// E0912 07:08:46.100164   75466 controller.go:254] unable to sync kubernetes service: Endpoints "kubernetes" is invalid: subsets[0].addresses[0].ip: Invalid value: "127.0.0.1": may not be in the loopback range (127.0.0.0/8, ::1/128)
   170  	//
   171  	// By creating a ktesting logger and registering that as global
   172  	// default logger we get the control plane output into the
   173  	// "go test" output in case of a failure (useful for debugging!)
   174  	// while keeping it out of the captured Ginkgo output that
   175  	// the test is comparing below.
   176  	//
   177  	// There are some small drawbacks:
   178  	// - The source code location for control plane log messages
   179  	//   is shown as klog.go because klog does not properly
   180  	//   skip its own helper functions. That's okay, normally
   181  	//   ktesting should not be installed as logging backend like this.
   182  	// - klog.Infof messages are printed with an extra newline.
   183  	logger, _ := ktesting.NewTestContext(t)
   184  	klog.SetLogger(logger)
   185  
   186  	apiServer := testapiserver.StartAPITestServer(t)
   187  
   188  	// This simulates how test/e2e uses the framework and how users
   189  	// invoke test/e2e.
   190  	framework.RegisterCommonFlags(flag.CommandLine)
   191  	framework.RegisterClusterFlags(flag.CommandLine)
   192  	for flagname, value := range map[string]string{
   193  		"kubeconfig": apiServer.KubeConfigFile,
   194  		// Some features are not supported by the fake cluster.
   195  		"e2e-verify-service-account": "false",
   196  		"allowed-not-ready-nodes":    "-1",
   197  		// This simplifies the text comparison.
   198  		"ginkgo.no-color": "true",
   199  	} {
   200  		if err := flag.Set(flagname, value); err != nil {
   201  			t.Fatalf("set %s: %v", flagname, err)
   202  		}
   203  	}
   204  	framework.AfterReadingAllFlags(&framework.TestContext)
   205  	suiteConfig, reporterConfig := framework.CreateGinkgoConfig()
   206  
   207  	expected := output.TestResult{
   208  		NormalizeOutput: normalizeOutput,
   209  		Suite: reporters.JUnitTestSuite{
   210  			Tests:    1,
   211  			Failures: 1,
   212  			Errors:   0,
   213  			Disabled: 0,
   214  			Skipped:  0,
   215  
   216  			TestCases: []reporters.JUnitTestCase{
   217  				{
   218  					Name:   "[It] e2e works",
   219  					Status: "failed",
   220  					Failure: &reporters.JUnitFailure{
   221  						Type: "failed",
   222  						Description: `[FAILED] DeferCleanup callback returned error: fake error for "failure"
   223  In [DeferCleanup (Each)] at: cleanup_test.go:103 <time>
   224  `,
   225  					},
   226  					SystemErr: ginkgoOutput,
   227  				},
   228  			},
   229  		},
   230  	}
   231  
   232  	output.TestGinkgoOutput(t, expected, suiteConfig, reporterConfig)
   233  }
   234  
   235  func normalizeOutput(output string) string {
   236  	for exp, replacement := range map[string]string{
   237  		// Ignore line numbers inside framework source code (likely to change).
   238  		`(framework|util)\.go:\d+`: `$1.go:xxx`,
   239  		// Config file name varies for each run.
   240  		`kubeConfig: .*/kube.config`: `kubeConfig: yyy/kube.config`,
   241  		// Random suffix for namespace.
   242  		`test-namespace-\d+`: `test-namespace-zzz`,
   243  	} {
   244  		output = regexp.MustCompile(exp).ReplaceAllString(output, replacement)
   245  	}
   246  	return output
   247  }
   248  

View as plain text