k8s.io/kubernetes@v1.29.3/test/e2e/upgrades/network/services.go (about)

     1  /*
     2  Copyright 2017 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package network
    18  
    19  import (
    20  	"context"
    21  
    22  	v1 "k8s.io/api/core/v1"
    23  	"k8s.io/apimachinery/pkg/util/wait"
    24  	"k8s.io/kubernetes/test/e2e/framework"
    25  	e2eservice "k8s.io/kubernetes/test/e2e/framework/service"
    26  	"k8s.io/kubernetes/test/e2e/upgrades"
    27  
    28  	"github.com/onsi/ginkgo/v2"
    29  )
    30  
    31  // ServiceUpgradeTest tests that a service is available before and
    32  // after a cluster upgrade. During a master-only upgrade, it will test
    33  // that a service remains available during the upgrade.
    34  type ServiceUpgradeTest struct {
    35  	jig          *e2eservice.TestJig
    36  	tcpService   *v1.Service
    37  	tcpIngressIP string
    38  	svcPort      int
    39  }
    40  
    41  // Name returns the tracking name of the test.
    42  func (ServiceUpgradeTest) Name() string { return "service-upgrade" }
    43  
    44  func shouldTestPDBs() bool { return framework.ProviderIs("gce", "gke") }
    45  
    46  // Setup creates a service with a load balancer and makes sure it's reachable.
    47  func (t *ServiceUpgradeTest) Setup(ctx context.Context, f *framework.Framework) {
    48  	serviceName := "service-test"
    49  	jig := e2eservice.NewTestJig(f.ClientSet, f.Namespace.Name, serviceName)
    50  
    51  	ns := f.Namespace
    52  	cs := f.ClientSet
    53  
    54  	ginkgo.By("creating a TCP service " + serviceName + " with type=LoadBalancer in namespace " + ns.Name)
    55  	_, err := jig.CreateTCPService(ctx, func(s *v1.Service) {
    56  		s.Spec.Type = v1.ServiceTypeLoadBalancer
    57  	})
    58  	framework.ExpectNoError(err)
    59  	tcpService, err := jig.WaitForLoadBalancer(ctx, e2eservice.GetServiceLoadBalancerCreationTimeout(ctx, cs))
    60  	framework.ExpectNoError(err)
    61  
    62  	// Get info to hit it with
    63  	tcpIngressIP := e2eservice.GetIngressPoint(&tcpService.Status.LoadBalancer.Ingress[0])
    64  	svcPort := int(tcpService.Spec.Ports[0].Port)
    65  
    66  	ginkgo.By("creating pod to be part of service " + serviceName)
    67  	rc, err := jig.Run(ctx, jig.AddRCAntiAffinity)
    68  	framework.ExpectNoError(err)
    69  
    70  	if shouldTestPDBs() {
    71  		ginkgo.By("creating a PodDisruptionBudget to cover the ReplicationController")
    72  		_, err = jig.CreatePDB(ctx, rc)
    73  		framework.ExpectNoError(err)
    74  	}
    75  
    76  	// Hit it once before considering ourselves ready
    77  	ginkgo.By("hitting the pod through the service's LoadBalancer")
    78  	timeout := e2eservice.LoadBalancerLagTimeoutDefault
    79  	if framework.ProviderIs("aws") {
    80  		timeout = e2eservice.LoadBalancerLagTimeoutAWS
    81  	}
    82  	e2eservice.TestReachableHTTP(ctx, tcpIngressIP, svcPort, timeout)
    83  
    84  	t.jig = jig
    85  	t.tcpService = tcpService
    86  	t.tcpIngressIP = tcpIngressIP
    87  	t.svcPort = svcPort
    88  }
    89  
    90  // Test runs a connectivity check to the service.
    91  func (t *ServiceUpgradeTest) Test(ctx context.Context, f *framework.Framework, done <-chan struct{}, upgrade upgrades.UpgradeType) {
    92  	switch upgrade {
    93  	case upgrades.MasterUpgrade, upgrades.ClusterUpgrade:
    94  		t.test(ctx, f, done, true, true)
    95  	case upgrades.NodeUpgrade:
    96  		// Node upgrades should test during disruption only on GCE/GKE for now.
    97  		t.test(ctx, f, done, shouldTestPDBs(), false)
    98  	default:
    99  		t.test(ctx, f, done, false, false)
   100  	}
   101  }
   102  
   103  // Teardown cleans up any remaining resources.
   104  func (t *ServiceUpgradeTest) Teardown(ctx context.Context, f *framework.Framework) {
   105  	// rely on the namespace deletion to clean up everything
   106  }
   107  
   108  func (t *ServiceUpgradeTest) test(ctx context.Context, f *framework.Framework, done <-chan struct{}, testDuringDisruption, testFinalizer bool) {
   109  	if testDuringDisruption {
   110  		// Continuous validation
   111  		ginkgo.By("continuously hitting the pod through the service's LoadBalancer")
   112  		// TODO (pohly): add context support
   113  		wait.Until(func() {
   114  			e2eservice.TestReachableHTTP(ctx, t.tcpIngressIP, t.svcPort, e2eservice.LoadBalancerLagTimeoutDefault)
   115  		}, framework.Poll, done)
   116  	} else {
   117  		// Block until upgrade is done
   118  		ginkgo.By("waiting for upgrade to finish without checking if service remains up")
   119  		<-done
   120  	}
   121  
   122  	// Hit it once more
   123  	ginkgo.By("hitting the pod through the service's LoadBalancer")
   124  	e2eservice.TestReachableHTTP(ctx, t.tcpIngressIP, t.svcPort, e2eservice.LoadBalancerLagTimeoutDefault)
   125  	if testFinalizer {
   126  		defer func() {
   127  			ginkgo.By("Check that service can be deleted with finalizer")
   128  			e2eservice.WaitForServiceDeletedWithFinalizer(ctx, t.jig.Client, t.tcpService.Namespace, t.tcpService.Name)
   129  		}()
   130  		ginkgo.By("Check that finalizer is present on loadBalancer type service")
   131  		e2eservice.WaitForServiceUpdatedWithFinalizer(ctx, t.jig.Client, t.tcpService.Namespace, t.tcpService.Name, true)
   132  	}
   133  }