github.com/grahambrereton-form3/tilt@v0.10.18/internal/engine/portforwardcontroller_test.go (about) 1 package engine 2 3 import ( 4 "context" 5 "fmt" 6 "testing" 7 "time" 8 9 "github.com/stretchr/testify/assert" 10 v1 "k8s.io/api/core/v1" 11 12 "github.com/windmilleng/tilt/internal/k8s" 13 "github.com/windmilleng/tilt/internal/store" 14 "github.com/windmilleng/tilt/internal/testutils/bufsync" 15 "github.com/windmilleng/tilt/internal/testutils/tempdir" 16 "github.com/windmilleng/tilt/pkg/logger" 17 "github.com/windmilleng/tilt/pkg/model" 18 ) 19 20 func TestPortForward(t *testing.T) { 21 f := newPLCFixture(t) 22 defer f.TearDown() 23 24 state := f.st.LockMutableStateForTesting() 25 m := model.Manifest{ 26 Name: "fe", 27 } 28 m = m.WithDeployTarget(model.K8sTarget{ 29 PortForwards: []model.PortForward{ 30 { 31 LocalPort: 8080, 32 ContainerPort: 8081, 33 }, 34 }, 35 }) 36 state.UpsertManifestTarget(store.NewManifestTarget(m)) 37 f.st.UnlockMutableState() 38 39 f.onChange() 40 assert.Equal(t, 0, len(f.plc.activeForwards)) 41 42 state = f.st.LockMutableStateForTesting() 43 state.ManifestTargets["fe"].State.RuntimeState = store.NewK8sRuntimeState(store.Pod{PodID: "pod-id", Phase: v1.PodRunning}) 44 f.st.UnlockMutableState() 45 46 f.onChange() 47 assert.Equal(t, 1, len(f.plc.activeForwards)) 48 assert.Equal(t, "pod-id", f.kCli.LastForwardPortPodID.String()) 49 firstPodForwardCtx := f.kCli.LastForwardContext 50 51 state = f.st.LockMutableStateForTesting() 52 state.ManifestTargets["fe"].State.RuntimeState = store.NewK8sRuntimeState(store.Pod{PodID: "pod-id2", Phase: v1.PodRunning}) 53 f.st.UnlockMutableState() 54 55 f.onChange() 56 assert.Equal(t, 1, len(f.plc.activeForwards)) 57 assert.Equal(t, "pod-id2", f.kCli.LastForwardPortPodID.String()) 58 59 state = f.st.LockMutableStateForTesting() 60 state.ManifestTargets["fe"].State.RuntimeState = store.NewK8sRuntimeState(store.Pod{PodID: "pod-id2", Phase: v1.PodPending}) 61 f.st.UnlockMutableState() 62 63 f.onChange() 64 assert.Equal(t, 0, len(f.plc.activeForwards)) 65 66 assert.Equal(t, context.Canceled, firstPodForwardCtx.Err(), 67 "Expected first port-forward to be canceled") 68 } 69 70 func TestPortForwardAutoDiscovery(t *testing.T) { 71 f := newPLCFixture(t) 72 defer f.TearDown() 73 74 state := f.st.LockMutableStateForTesting() 75 m := model.Manifest{ 76 Name: "fe", 77 } 78 m = m.WithDeployTarget(model.K8sTarget{ 79 PortForwards: []model.PortForward{ 80 { 81 LocalPort: 8080, 82 }, 83 }, 84 }) 85 state.UpsertManifestTarget(store.NewManifestTarget(m)) 86 state.ManifestTargets["fe"].State.RuntimeState = store.NewK8sRuntimeState(store.Pod{PodID: "pod-id", Phase: v1.PodRunning}) 87 f.st.UnlockMutableState() 88 89 f.onChange() 90 assert.Equal(t, 0, len(f.plc.activeForwards)) 91 state = f.st.LockMutableStateForTesting() 92 state.ManifestTargets["fe"].State.K8sRuntimeState().Pods["pod-id"].Containers = []store.Container{ 93 store.Container{Ports: []int32{8000}}, 94 } 95 f.st.UnlockMutableState() 96 97 f.onChange() 98 assert.Equal(t, 1, len(f.plc.activeForwards)) 99 assert.Equal(t, 8000, f.kCli.LastForwardPortRemotePort) 100 } 101 102 func TestPortForwardAutoDiscovery2(t *testing.T) { 103 f := newPLCFixture(t) 104 defer f.TearDown() 105 106 state := f.st.LockMutableStateForTesting() 107 m := model.Manifest{ 108 Name: "fe", 109 } 110 m = m.WithDeployTarget(model.K8sTarget{ 111 PortForwards: []model.PortForward{ 112 { 113 LocalPort: 8080, 114 }, 115 }, 116 }) 117 state.UpsertManifestTarget(store.NewManifestTarget(m)) 118 state.ManifestTargets["fe"].State.RuntimeState = store.NewK8sRuntimeState(store.Pod{ 119 PodID: "pod-id", 120 Phase: v1.PodRunning, 121 Containers: []store.Container{ 122 store.Container{Ports: []int32{8000, 8080}}, 123 }, 124 }) 125 f.st.UnlockMutableState() 126 127 f.onChange() 128 assert.Equal(t, 1, len(f.plc.activeForwards)) 129 assert.Equal(t, 8080, f.kCli.LastForwardPortRemotePort) 130 } 131 132 func TestPortForwardChangePort(t *testing.T) { 133 f := newPLCFixture(t) 134 defer f.TearDown() 135 136 state := f.st.LockMutableStateForTesting() 137 m := model.Manifest{Name: "fe"}.WithDeployTarget(model.K8sTarget{ 138 PortForwards: []model.PortForward{ 139 { 140 LocalPort: 8080, 141 ContainerPort: 8081, 142 }, 143 }, 144 }) 145 state.UpsertManifestTarget(store.NewManifestTarget(m)) 146 state.ManifestTargets["fe"].State.RuntimeState = store.NewK8sRuntimeState(store.Pod{PodID: "pod-id", Phase: v1.PodRunning}) 147 f.st.UnlockMutableState() 148 149 f.onChange() 150 assert.Equal(t, 1, len(f.plc.activeForwards)) 151 assert.Equal(t, 8081, f.kCli.LastForwardPortRemotePort) 152 153 state = f.st.LockMutableStateForTesting() 154 kTarget := state.ManifestTargets["fe"].Manifest.K8sTarget() 155 kTarget.PortForwards[0].ContainerPort = 8082 156 f.st.UnlockMutableState() 157 158 f.onChange() 159 assert.Equal(t, 1, len(f.plc.activeForwards)) 160 assert.Equal(t, 8082, f.kCli.LastForwardPortRemotePort) 161 } 162 163 func TestPortForwardRestart(t *testing.T) { 164 f := newPLCFixture(t) 165 defer f.TearDown() 166 167 state := f.st.LockMutableStateForTesting() 168 m := model.Manifest{Name: "fe"}.WithDeployTarget(model.K8sTarget{ 169 PortForwards: []model.PortForward{ 170 { 171 LocalPort: 8080, 172 ContainerPort: 8081, 173 }, 174 }, 175 }) 176 state.UpsertManifestTarget(store.NewManifestTarget(m)) 177 state.ManifestTargets["fe"].State.RuntimeState = store.NewK8sRuntimeState(store.Pod{PodID: "pod-id", Phase: v1.PodRunning}) 178 f.st.UnlockMutableState() 179 180 f.onChange() 181 assert.Equal(t, 1, len(f.plc.activeForwards)) 182 assert.Equal(t, 1, f.kCli.CreatePortForwardCallCount) 183 184 err := fmt.Errorf("unique-error") 185 f.kCli.LastForwarder.Done <- err 186 187 assert.Contains(t, "unique-error", f.out.String()) 188 time.Sleep(100 * time.Millisecond) 189 190 assert.Equal(t, 1, len(f.plc.activeForwards)) 191 assert.Equal(t, 2, f.kCli.CreatePortForwardCallCount) 192 } 193 194 type portForwardTestCase struct { 195 spec []model.PortForward 196 containerPorts []int32 197 expected []model.PortForward 198 } 199 200 func TestPopulatePortForward(t *testing.T) { 201 cases := []portForwardTestCase{ 202 { 203 spec: []model.PortForward{{LocalPort: 8080}}, 204 containerPorts: []int32{8080}, 205 expected: []model.PortForward{{ContainerPort: 8080, LocalPort: 8080}}, 206 }, 207 { 208 spec: []model.PortForward{{LocalPort: 8080}}, 209 containerPorts: []int32{8000, 8080, 8001}, 210 expected: []model.PortForward{{ContainerPort: 8080, LocalPort: 8080}}, 211 }, 212 { 213 spec: []model.PortForward{{LocalPort: 8080}, {LocalPort: 8000}}, 214 containerPorts: []int32{8000, 8080, 8001}, 215 expected: []model.PortForward{ 216 {ContainerPort: 8080, LocalPort: 8080}, 217 {ContainerPort: 8000, LocalPort: 8000}, 218 }, 219 }, 220 { 221 spec: []model.PortForward{{ContainerPort: 8000, LocalPort: 8080}}, 222 containerPorts: []int32{8000, 8080, 8001}, 223 expected: []model.PortForward{{ContainerPort: 8000, LocalPort: 8080}}, 224 }, 225 } 226 227 for i, c := range cases { 228 t.Run(fmt.Sprintf("Case%d", i), func(t *testing.T) { 229 m := model.Manifest{Name: "fe"}.WithDeployTarget(model.K8sTarget{ 230 PortForwards: c.spec, 231 }) 232 pod := store.Pod{ 233 Containers: []store.Container{ 234 store.Container{Ports: c.containerPorts}, 235 }, 236 } 237 238 actual := populatePortForwards(m, pod) 239 assert.Equal(t, c.expected, actual) 240 }) 241 } 242 } 243 244 type plcFixture struct { 245 *tempdir.TempDirFixture 246 ctx context.Context 247 cancel func() 248 kCli *k8s.FakeK8sClient 249 st *store.Store 250 plc *PortForwardController 251 out *bufsync.ThreadSafeBuffer 252 } 253 254 func newPLCFixture(t *testing.T) *plcFixture { 255 f := tempdir.NewTempDirFixture(t) 256 st, _ := store.NewStoreForTesting() 257 kCli := k8s.NewFakeK8sClient() 258 plc := NewPortForwardController(kCli) 259 260 out := bufsync.NewThreadSafeBuffer() 261 l := logger.NewLogger(logger.DebugLvl, out) 262 ctx, cancel := context.WithCancel(context.Background()) 263 ctx = logger.WithLogger(ctx, l) 264 return &plcFixture{ 265 TempDirFixture: f, 266 ctx: ctx, 267 cancel: cancel, 268 st: st, 269 kCli: kCli, 270 plc: plc, 271 out: out, 272 } 273 } 274 275 func (f *plcFixture) onChange() { 276 f.plc.OnChange(f.ctx, f.st) 277 time.Sleep(10 * time.Millisecond) 278 } 279 280 func (f *plcFixture) TearDown() { 281 f.kCli.TearDown() 282 f.TempDirFixture.TearDown() 283 f.cancel() 284 }