github.com/cilium/cilium@v1.16.2/test/controlplane/services/nodeport/nodeport.go (about)

     1  // SPDX-License-Identifier: Apache-2.0
     2  // Copyright Authors of Cilium
     3  
     4  package nodeport
     5  
     6  import (
     7  	"fmt"
     8  	"net"
     9  	"os"
    10  	"path"
    11  	"testing"
    12  
    13  	datapathTables "github.com/cilium/cilium/pkg/datapath/tables"
    14  	lb "github.com/cilium/cilium/pkg/loadbalancer"
    15  	agentOption "github.com/cilium/cilium/pkg/option"
    16  	"github.com/cilium/cilium/test/controlplane"
    17  	"github.com/cilium/cilium/test/controlplane/services/helpers"
    18  	"github.com/cilium/cilium/test/controlplane/suite"
    19  )
    20  
    21  func init() {
    22  	suite.AddTestCase("Services/NodePort", func(t *testing.T) {
    23  		cwd, err := os.Getwd()
    24  		if err != nil {
    25  			t.Fatal(err)
    26  		}
    27  
    28  		modConfig := func(cfg *agentOption.DaemonConfig) {
    29  			cfg.EnableNodePort = true
    30  		}
    31  
    32  		for _, version := range controlplane.K8sVersions() {
    33  			abs := func(f string) string { return path.Join(cwd, "services", "nodeport", "v"+version, f) }
    34  
    35  			// Run the test from each nodes perspective.
    36  			for _, nodeName := range []string{"nodeport-control-plane", "nodeport-worker", "nodeport-worker2"} {
    37  				t.Run("v"+version+"/"+nodeName, func(t *testing.T) {
    38  					test := suite.NewControlPlaneTest(t, nodeName, version)
    39  
    40  					// Feed in initial state and start the agent.
    41  					test.
    42  						UpdateObjectsFromFile(abs("init.yaml")).
    43  						SetupEnvironment().
    44  						StartAgent(modConfig).
    45  						EnsureWatchers("endpointslices", "pods", "services").
    46  						UpdateObjectsFromFile(abs("state1.yaml")).
    47  						Eventually(func() error { return validate(test, abs("lbmap1_"+nodeName+".golden")) }).
    48  						StopAgent().
    49  						ClearEnvironment()
    50  				})
    51  			}
    52  		}
    53  	})
    54  }
    55  
    56  func validate(test *suite.ControlPlaneTest, goldenFile string) error {
    57  	if err := helpers.ValidateLBMapGoldenFile(goldenFile, test.Datapath); err != nil {
    58  		return err
    59  	}
    60  	if err := validateExternalTrafficPolicyLocal(test); err != nil {
    61  		return err
    62  	}
    63  	return nil
    64  }
    65  
    66  func validateExternalTrafficPolicyLocal(test *suite.ControlPlaneTest) error {
    67  	dp := test.Datapath
    68  	lbmap := dp.LBMockMap()
    69  	lbmap.Lock()
    70  	defer lbmap.Unlock()
    71  
    72  	// Collect all echo-local services with internal ("local") scope.
    73  	localServices := []*lb.SVC{}
    74  	for _, svc := range dp.LBMockMap().ServiceByID {
    75  		if svc.Name.Name == "echo-local" && svc.Frontend.Scope == lb.ScopeInternal {
    76  			localServices = append(localServices, svc)
    77  		}
    78  	}
    79  
    80  	expectedFrontendIPs := map[string]bool{}
    81  
    82  	db, nodeAddrs := test.AgentDB()
    83  	iter := nodeAddrs.List(db.ReadTxn(), datapathTables.NodeAddressNodePortIndex.Query(true))
    84  	for addr, _, ok := iter.Next(); ok; addr, _, ok = iter.Next() {
    85  		if addr.NodePort && addr.Addr.Is4() {
    86  			expectedFrontendIPs[addr.Addr.String()] = true
    87  		}
    88  	}
    89  	expectedFrontendIPs[net.IPv4zero.String()] = true
    90  
    91  	// Check that all expected service entries exist with the expected frontends.
    92  	for _, svc := range localServices {
    93  		ip := svc.Frontend.AddrCluster.String()
    94  		if _, ok := expectedFrontendIPs[ip]; !ok {
    95  			return fmt.Errorf("unexpected frontend IP %q for service %s, expected one of %v", ip, svc.Name, expectedFrontendIPs)
    96  		}
    97  		delete(expectedFrontendIPs, ip)
    98  		if len(svc.Backends) != 1 {
    99  			return fmt.Errorf("missing backend for %s, expected 1, got %d", svc.Name, len(svc.Backends))
   100  		}
   101  	}
   102  	if len(expectedFrontendIPs) > 0 {
   103  		return fmt.Errorf("missing services for frontends: %v", expectedFrontendIPs)
   104  	}
   105  
   106  	return nil
   107  }