github.com/tilt-dev/tilt@v0.33.15-0.20240515162809-0a22ed45d8a0/internal/controllers/core/kubernetesdiscovery/portforwards_test.go (about) 1 package kubernetesdiscovery 2 3 import ( 4 "testing" 5 6 "github.com/stretchr/testify/assert" 7 "github.com/stretchr/testify/require" 8 v1 "k8s.io/api/core/v1" 9 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 10 "k8s.io/apimachinery/pkg/types" 11 12 "github.com/tilt-dev/tilt/pkg/apis/core/v1alpha1" 13 ) 14 15 func TestPortForwardCreateAndUpdate(t *testing.T) { 16 f := newFixture(t) 17 18 pod := f.buildPod("pod-ns", "pod", nil, nil) 19 key := types.NamespacedName{Name: "kd"} 20 kd := &v1alpha1.KubernetesDiscovery{ 21 ObjectMeta: metav1.ObjectMeta{Name: "kd"}, 22 Spec: v1alpha1.KubernetesDiscoverySpec{ 23 Watches: []v1alpha1.KubernetesWatchRef{ 24 { 25 UID: string(pod.UID), 26 Namespace: pod.Namespace, 27 Name: pod.Name, 28 }, 29 }, 30 PortForwardTemplateSpec: &v1alpha1.PortForwardTemplateSpec{ 31 Forwards: []v1alpha1.Forward{ 32 v1alpha1.Forward{LocalPort: 4000, ContainerPort: 4000}, 33 }, 34 }, 35 }, 36 } 37 38 f.Create(kd) 39 f.injectK8sObjects(*kd, pod) 40 41 f.requireObservedPods(key, ancestorMap{pod.UID: pod.UID}, nil) 42 43 // Simulate the reconcile (which would normally be invoked by the manager on status update). 44 f.MustReconcile(key) 45 46 var pf v1alpha1.PortForward 47 f.MustGet(types.NamespacedName{Name: "kd-pod"}, &pf) 48 require.Equal(t, 1, len(pf.Spec.Forwards)) 49 assert.Equal(t, 4000, int(pf.Spec.Forwards[0].LocalPort)) 50 51 f.MustGet(key, kd) 52 kd.Spec.PortForwardTemplateSpec.Forwards[0].LocalPort = 4001 53 f.Update(kd) 54 55 f.MustReconcile(key) 56 57 f.MustGet(types.NamespacedName{Name: "kd-pod"}, &pf) 58 require.Equal(t, 1, len(pf.Spec.Forwards)) 59 assert.Equal(t, 4001, int(pf.Spec.Forwards[0].LocalPort)) 60 } 61 62 func TestPortForwardIdempotent(t *testing.T) { 63 f := newFixture(t) 64 65 pod := f.buildPod("pod-ns", "pod", nil, nil) 66 key := types.NamespacedName{Name: "kd"} 67 kd := &v1alpha1.KubernetesDiscovery{ 68 ObjectMeta: metav1.ObjectMeta{Name: "kd"}, 69 Spec: v1alpha1.KubernetesDiscoverySpec{ 70 Watches: []v1alpha1.KubernetesWatchRef{ 71 { 72 UID: string(pod.UID), 73 Namespace: pod.Namespace, 74 Name: pod.Name, 75 }, 76 }, 77 PortForwardTemplateSpec: &v1alpha1.PortForwardTemplateSpec{ 78 Forwards: []v1alpha1.Forward{ 79 v1alpha1.Forward{LocalPort: 4000, ContainerPort: 4000}, 80 }, 81 }, 82 }, 83 } 84 85 f.Create(kd) 86 f.injectK8sObjects(*kd, pod) 87 f.requireObservedPods(key, ancestorMap{pod.UID: pod.UID}, nil) 88 89 // Simulate the reconcile (which would normally be invoked by the manager on status update). 90 f.MustReconcile(key) 91 92 var pf1 v1alpha1.PortForward 93 f.MustGet(types.NamespacedName{Name: "kd-pod"}, &pf1) 94 95 f.MustReconcile(key) 96 97 var pf2 v1alpha1.PortForward 98 f.MustGet(types.NamespacedName{Name: "kd-pod"}, &pf2) 99 100 assert.Equal(t, pf1.ObjectMeta, pf2.ObjectMeta) 101 } 102 103 func TestPortForwardCreateAndDelete(t *testing.T) { 104 f := newFixture(t) 105 106 pod := f.buildPod("pod-ns", "pod", nil, nil) 107 key := types.NamespacedName{Name: "kd"} 108 kd := &v1alpha1.KubernetesDiscovery{ 109 ObjectMeta: metav1.ObjectMeta{Name: key.Name}, 110 Spec: v1alpha1.KubernetesDiscoverySpec{ 111 Watches: []v1alpha1.KubernetesWatchRef{ 112 { 113 UID: string(pod.UID), 114 Namespace: pod.Namespace, 115 Name: pod.Name, 116 }, 117 }, 118 PortForwardTemplateSpec: &v1alpha1.PortForwardTemplateSpec{ 119 Forwards: []v1alpha1.Forward{ 120 v1alpha1.Forward{LocalPort: 4000, ContainerPort: 4000}, 121 }, 122 }, 123 }, 124 } 125 126 f.Create(kd) 127 f.injectK8sObjects(*kd, pod) 128 129 f.requireObservedPods(key, ancestorMap{pod.UID: pod.UID}, nil) 130 131 // Simulate the reconcile (which would normally be invoked by the manager on status update). 132 f.MustReconcile(key) 133 134 var pf v1alpha1.PortForward 135 f.MustGet(types.NamespacedName{Name: "kd-pod"}, &pf) 136 137 f.MustGet(key, kd) 138 kd.Spec.PortForwardTemplateSpec = nil 139 f.Update(kd) 140 141 f.MustReconcile(key) 142 assert.False(t, f.Get(types.NamespacedName{Name: "kd-pod"}, &pf)) 143 } 144 145 func TestPortForwardCreateAndDeleteOwner(t *testing.T) { 146 f := newFixture(t) 147 148 pod := f.buildPod("pod-ns", "pod", nil, nil) 149 key := types.NamespacedName{Name: "kd"} 150 kd := &v1alpha1.KubernetesDiscovery{ 151 ObjectMeta: metav1.ObjectMeta{Name: key.Name}, 152 Spec: v1alpha1.KubernetesDiscoverySpec{ 153 Watches: []v1alpha1.KubernetesWatchRef{ 154 { 155 UID: string(pod.UID), 156 Namespace: pod.Namespace, 157 Name: pod.Name, 158 }, 159 }, 160 PortForwardTemplateSpec: &v1alpha1.PortForwardTemplateSpec{ 161 Forwards: []v1alpha1.Forward{ 162 v1alpha1.Forward{LocalPort: 4000, ContainerPort: 4000}, 163 }, 164 }, 165 }, 166 } 167 168 f.Create(kd) 169 f.injectK8sObjects(*kd, pod) 170 171 f.requireObservedPods(key, ancestorMap{pod.UID: pod.UID}, nil) 172 173 // Simulate the reconcile (which would normally be invoked by the manager on status update). 174 f.MustReconcile(key) 175 176 var pf v1alpha1.PortForward 177 assert.True(t, f.Get(types.NamespacedName{Name: "kd-pod"}, &pf)) 178 179 f.Delete(kd) 180 181 f.MustReconcile(key) 182 assert.False(t, f.Get(types.NamespacedName{Name: "kd-pod"}, &pf)) 183 } 184 185 func TestPortForwardNotForPending(t *testing.T) { 186 f := newFixture(t) 187 188 pod := f.buildPod("pod-ns", "pod", nil, nil) 189 pod.Status.Phase = v1.PodPending 190 191 key := types.NamespacedName{Name: "kd"} 192 kd := &v1alpha1.KubernetesDiscovery{ 193 ObjectMeta: metav1.ObjectMeta{Name: "kd"}, 194 Spec: v1alpha1.KubernetesDiscoverySpec{ 195 Watches: []v1alpha1.KubernetesWatchRef{ 196 { 197 UID: string(pod.UID), 198 Namespace: pod.Namespace, 199 Name: pod.Name, 200 }, 201 }, 202 PortForwardTemplateSpec: &v1alpha1.PortForwardTemplateSpec{ 203 Forwards: []v1alpha1.Forward{ 204 v1alpha1.Forward{LocalPort: 4000, ContainerPort: 4000}, 205 }, 206 }, 207 }, 208 } 209 210 f.Create(kd) 211 f.injectK8sObjects(*kd, pod) 212 213 f.MustReconcile(key) 214 215 var pf v1alpha1.PortForward 216 assert.False(t, f.Get(types.NamespacedName{Name: "kd-pod"}, &pf)) 217 218 // Assert that setting the pod to running will lead to the port forward 219 // being created. 220 pod.Status.Phase = v1.PodRunning 221 f.injectK8sObjects(*kd, pod) 222 223 f.requireState(key, func(kd *v1alpha1.KubernetesDiscovery) bool { 224 return len(kd.Status.Pods) > 0 && kd.Status.Pods[0].Phase == string(v1.PodRunning) 225 }, "pod phase did not change to Running") 226 f.MustReconcile(key) 227 228 assert.True(t, f.Get(types.NamespacedName{Name: "kd-pod"}, &pf)) 229 }