istio.io/istio@v0.0.0-20240520182934-d79c90f27776/pilot/pkg/config/kube/ingress/status_test.go (about)

     1  // Copyright Istio Authors
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package ingress
    16  
    17  import (
    18  	"testing"
    19  
    20  	corev1 "k8s.io/api/core/v1"
    21  	knetworking "k8s.io/api/networking/v1"
    22  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    23  	"k8s.io/apimachinery/pkg/runtime"
    24  
    25  	"istio.io/api/annotation"
    26  	"istio.io/istio/pkg/config/mesh"
    27  	kubelib "istio.io/istio/pkg/kube"
    28  	"istio.io/istio/pkg/kube/kclient/clienttest"
    29  	istiolog "istio.io/istio/pkg/log"
    30  	"istio.io/istio/pkg/test"
    31  	"istio.io/istio/pkg/test/util/assert"
    32  )
    33  
    34  var (
    35  	serviceIP = "1.2.3.4"
    36  	hostname  = "foo.bar.com"
    37  	nodeIP    = "10.0.0.2"
    38  )
    39  
    40  var ingressService = &corev1.Service{
    41  	ObjectMeta: metav1.ObjectMeta{
    42  		Name:      "istio-ingress",
    43  		Namespace: IngressNamespace,
    44  	},
    45  	Status: corev1.ServiceStatus{
    46  		LoadBalancer: corev1.LoadBalancerStatus{
    47  			Ingress: []corev1.LoadBalancerIngress{{
    48  				IP: serviceIP,
    49  			}},
    50  		},
    51  	},
    52  }
    53  
    54  var testObjects = []runtime.Object{
    55  	&corev1.Pod{
    56  		ObjectMeta: metav1.ObjectMeta{
    57  			Name:      "ingressgateway",
    58  			Namespace: "istio-system",
    59  			Labels: map[string]string{
    60  				"istio": "ingressgateway",
    61  			},
    62  		},
    63  		Spec: corev1.PodSpec{
    64  			NodeName: "foo_node",
    65  		},
    66  		Status: corev1.PodStatus{
    67  			Phase: corev1.PodRunning,
    68  		},
    69  	},
    70  	ingressService,
    71  	&corev1.Service{
    72  		ObjectMeta: metav1.ObjectMeta{
    73  			Name:      "istio-ingress-hostname",
    74  			Namespace: IngressNamespace,
    75  		},
    76  		Status: corev1.ServiceStatus{
    77  			LoadBalancer: corev1.LoadBalancerStatus{
    78  				Ingress: []corev1.LoadBalancerIngress{{
    79  					Hostname: hostname,
    80  				}},
    81  			},
    82  		},
    83  	},
    84  	&corev1.Node{
    85  		ObjectMeta: metav1.ObjectMeta{
    86  			Name: "foo_node",
    87  		},
    88  		Status: corev1.NodeStatus{
    89  			Addresses: []corev1.NodeAddress{
    90  				{
    91  					Type:    corev1.NodeExternalIP,
    92  					Address: nodeIP,
    93  				},
    94  			},
    95  		},
    96  	},
    97  }
    98  
    99  func fakeMeshHolder(ingressService string) mesh.Watcher {
   100  	config := mesh.DefaultMeshConfig()
   101  	config.IngressService = ingressService
   102  	return mesh.NewFixedWatcher(config)
   103  }
   104  
   105  func makeStatusSyncer(t *testing.T, name string) *StatusSyncer {
   106  	client := kubelib.NewFakeClient(testObjects...)
   107  	sync := NewStatusSyncer(fakeMeshHolder(name), client)
   108  	client.RunAndWait(test.NewStop(t))
   109  	go sync.Run(test.NewStop(t))
   110  	return sync
   111  }
   112  
   113  // nolint: unparam
   114  func getIPs(ing clienttest.TestClient[*knetworking.Ingress], name string, ns string) func() []string {
   115  	return func() []string {
   116  		i := ing.Get(name, ns)
   117  		if i == nil {
   118  			return nil
   119  		}
   120  		res := []string{}
   121  		for _, v := range i.Status.LoadBalancer.Ingress {
   122  			if v.IP != "" {
   123  				res = append(res, v.IP)
   124  			} else {
   125  				res = append(res, v.Hostname)
   126  			}
   127  		}
   128  		return res
   129  	}
   130  }
   131  
   132  func TestStatusController(t *testing.T) {
   133  	statusLog.SetOutputLevel(istiolog.DebugLevel)
   134  	c := makeStatusSyncer(t, "istio-ingress")
   135  	ing := clienttest.Wrap(t, c.ingresses)
   136  	svc := clienttest.Wrap(t, c.services)
   137  	ingc := clienttest.Wrap(t, c.ingressClasses)
   138  	ing.Create(&knetworking.Ingress{
   139  		ObjectMeta: metav1.ObjectMeta{
   140  			Name:        "ingress",
   141  			Namespace:   "default",
   142  			Annotations: map[string]string{annotation.IoKubernetesIngressClass.Name: "istio"},
   143  		},
   144  	})
   145  
   146  	// Test initial state
   147  	assert.EventuallyEqual(t, getIPs(ing, "ingress", "default"), []string{serviceIP})
   148  
   149  	// Update service IP
   150  	updated := ingressService.DeepCopy()
   151  	updated.Status.LoadBalancer.Ingress = []corev1.LoadBalancerIngress{{IP: "5.6.7.8"}}
   152  	svc.Update(updated)
   153  	assert.EventuallyEqual(t, getIPs(ing, "ingress", "default"), []string{"5.6.7.8"})
   154  
   155  	// Remove service
   156  	svc.Delete(ingressService.Name, ingressService.Namespace)
   157  	assert.EventuallyEqual(t, getIPs(ing, "ingress", "default"), []string{})
   158  
   159  	// Add it back
   160  	svc.Create(updated)
   161  	assert.EventuallyEqual(t, getIPs(ing, "ingress", "default"), []string{"5.6.7.8"})
   162  
   163  	// Remove ingress class
   164  	ing.Update(&knetworking.Ingress{
   165  		ObjectMeta: metav1.ObjectMeta{
   166  			Name:      "ingress",
   167  			Namespace: "default",
   168  		},
   169  	})
   170  	assert.EventuallyEqual(t, getIPs(ing, "ingress", "default"), []string{})
   171  
   172  	ingressClassName := "istio"
   173  	// Set IngressClassName
   174  	ing.Update(&knetworking.Ingress{
   175  		ObjectMeta: metav1.ObjectMeta{
   176  			Name:      "ingress",
   177  			Namespace: "default",
   178  		},
   179  		Spec: knetworking.IngressSpec{
   180  			IngressClassName: &ingressClassName,
   181  		},
   182  	})
   183  	assert.EventuallyEqual(t, getIPs(ing, "ingress", "default"), []string{})
   184  
   185  	// Create IngressClass
   186  	ingc.Create(&knetworking.IngressClass{
   187  		ObjectMeta: metav1.ObjectMeta{
   188  			Name: ingressClassName,
   189  		},
   190  		Spec: knetworking.IngressClassSpec{
   191  			Controller: IstioIngressController,
   192  		},
   193  	})
   194  	assert.EventuallyEqual(t, getIPs(ing, "ingress", "default"), []string{"5.6.7.8"})
   195  }
   196  
   197  func TestRunningAddresses(t *testing.T) {
   198  	t.Run("service", testRunningAddressesWithService)
   199  	t.Run("hostname", testRunningAddressesWithHostname)
   200  }
   201  
   202  func testRunningAddressesWithService(t *testing.T) {
   203  	syncer := makeStatusSyncer(t, "istio-ingress")
   204  	address := syncer.runningAddresses()
   205  
   206  	if len(address) != 1 || address[0] != serviceIP {
   207  		t.Errorf("Address is not correctly set to service ip")
   208  	}
   209  }
   210  
   211  func testRunningAddressesWithHostname(t *testing.T) {
   212  	syncer := makeStatusSyncer(t, "istio-ingress-hostname")
   213  
   214  	address := syncer.runningAddresses()
   215  
   216  	if len(address) != 1 || address[0] != hostname {
   217  		t.Errorf("Address is not correctly set to hostname")
   218  	}
   219  }
   220  
   221  func TestRunningAddressesWithPod(t *testing.T) {
   222  	syncer := makeStatusSyncer(t, "")
   223  
   224  	address := syncer.runningAddresses()
   225  
   226  	if len(address) != 1 || address[0] != nodeIP {
   227  		t.Errorf("Address is not correctly set to node ip %v %v", address, nodeIP)
   228  	}
   229  }