|
4 | 4 | package statuses |
5 | 5 |
|
6 | 6 | import ( |
| 7 | + "bytes" |
7 | 8 | "context" |
8 | 9 | "encoding/json" |
9 | 10 | "errors" |
@@ -233,6 +234,14 @@ func TestFileStatusManager_GetWorkload_FileAndRuntimeCombination(t *testing.T) { |
233 | 234 | return io.NopCloser(strings.NewReader(`{"name": "running-workload", "transport": "sse"}`)), nil |
234 | 235 | }).AnyTimes() |
235 | 236 |
|
| 237 | + // Mock runtime name for isOwnedByActiveRuntime and migrateRuntimeName |
| 238 | + mockRuntime.EXPECT().Name().Return("docker").AnyTimes() |
| 239 | + |
| 240 | + // Mock GetWriter for migrateRuntimeName (legacy workload with no runtime_name gets migrated) |
| 241 | + mockRunConfigStore.EXPECT().GetWriter(gomock.Any(), "running-workload").DoAndReturn(func(context.Context, string) (io.WriteCloser, error) { |
| 242 | + return nopWriteCloser{&bytes.Buffer{}}, nil |
| 243 | + }).AnyTimes() |
| 244 | + |
236 | 245 | // Create a workload status file and set it to running |
237 | 246 | err := manager.SetWorkloadStatus(ctx, "running-workload", rt.WorkloadStatusStarting, "") |
238 | 247 | require.NoError(t, err) |
@@ -915,6 +924,14 @@ func TestFileStatusManager_GetWorkload_HealthyRunningWorkload(t *testing.T) { |
915 | 924 | return io.NopCloser(strings.NewReader(`{"name": "healthy-workload", "transport": "sse"}`)), nil |
916 | 925 | }).AnyTimes() |
917 | 926 |
|
| 927 | + // Mock runtime name for isOwnedByActiveRuntime and migrateRuntimeName |
| 928 | + mockRuntime.EXPECT().Name().Return("docker").AnyTimes() |
| 929 | + |
| 930 | + // Mock GetWriter for migrateRuntimeName (legacy workload with no runtime_name gets migrated) |
| 931 | + mockRunConfigStore.EXPECT().GetWriter(gomock.Any(), "healthy-workload").DoAndReturn(func(context.Context, string) (io.WriteCloser, error) { |
| 932 | + return nopWriteCloser{&bytes.Buffer{}}, nil |
| 933 | + }).AnyTimes() |
| 934 | + |
918 | 935 | // Set the workload status to running in the file |
919 | 936 | err := manager.SetWorkloadStatus(ctx, "healthy-workload", rt.WorkloadStatusRunning, "container started") |
920 | 937 | require.NoError(t, err) |
@@ -964,6 +981,9 @@ func TestFileStatusManager_GetWorkload_ProxyNotRunning(t *testing.T) { |
964 | 981 | return io.NopCloser(strings.NewReader(`{"name": "proxy-down-workload", "transport": "sse"}`)), nil |
965 | 982 | }).AnyTimes() |
966 | 983 |
|
| 984 | + // Mock runtime name for isOwnedByActiveRuntime |
| 985 | + mockRuntime.EXPECT().Name().Return("docker").AnyTimes() |
| 986 | + |
967 | 987 | // First, create a status file manually to ensure file is found |
968 | 988 | statusFile := workloadStatusFile{ |
969 | 989 | Status: rt.WorkloadStatusRunning, |
@@ -1041,6 +1061,14 @@ func TestFileStatusManager_GetWorkload_HealthyWithProxy(t *testing.T) { |
1041 | 1061 | return io.NopCloser(strings.NewReader(`{"name": "healthy-with-proxy", "transport": "sse"}`)), nil |
1042 | 1062 | }).AnyTimes() |
1043 | 1063 |
|
| 1064 | + // Mock runtime name for isOwnedByActiveRuntime and migrateRuntimeName |
| 1065 | + mockRuntime.EXPECT().Name().Return("docker").AnyTimes() |
| 1066 | + |
| 1067 | + // Mock GetWriter for migrateRuntimeName (legacy workload with no runtime_name gets migrated) |
| 1068 | + mockRunConfigStore.EXPECT().GetWriter(gomock.Any(), "healthy-with-proxy").DoAndReturn(func(context.Context, string) (io.WriteCloser, error) { |
| 1069 | + return nopWriteCloser{&bytes.Buffer{}}, nil |
| 1070 | + }).AnyTimes() |
| 1071 | + |
1044 | 1072 | // Set the workload status to running in the file |
1045 | 1073 | err := manager.SetWorkloadStatus(ctx, "healthy-with-proxy", rt.WorkloadStatusRunning, "container started") |
1046 | 1074 | require.NoError(t, err) |
@@ -2353,3 +2381,135 @@ func TestFileStatusManager_CrossRuntimeProtection(t *testing.T) { |
2353 | 2381 | }) |
2354 | 2382 | } |
2355 | 2383 | } |
| 2384 | + |
| 2385 | +// nopWriteCloser wraps a bytes.Buffer with a no-op Close. |
| 2386 | +type nopWriteCloser struct { |
| 2387 | + *bytes.Buffer |
| 2388 | +} |
| 2389 | + |
| 2390 | +func (nopWriteCloser) Close() error { return nil } |
| 2391 | + |
| 2392 | +func TestMigrateRuntimeName(t *testing.T) { |
| 2393 | + t.Parallel() |
| 2394 | + |
| 2395 | + tests := []struct { |
| 2396 | + name string |
| 2397 | + runtimeName string |
| 2398 | + runConfigJSON string |
| 2399 | + expectWrite bool |
| 2400 | + expectedRuntime string |
| 2401 | + getReaderErr bool |
| 2402 | + getWriterErr bool |
| 2403 | + }{ |
| 2404 | + { |
| 2405 | + name: "migrates empty runtime_name", |
| 2406 | + runtimeName: "docker", |
| 2407 | + runConfigJSON: `{"name": "my-workload", "image": "test:latest"}`, |
| 2408 | + expectWrite: true, |
| 2409 | + expectedRuntime: "docker", |
| 2410 | + }, |
| 2411 | + { |
| 2412 | + name: "migrates explicit empty runtime_name", |
| 2413 | + runtimeName: "docker", |
| 2414 | + runConfigJSON: `{"name": "my-workload", "runtime_name": "", "image": "test:latest"}`, |
| 2415 | + expectWrite: true, |
| 2416 | + expectedRuntime: "docker", |
| 2417 | + }, |
| 2418 | + { |
| 2419 | + name: "no-op when runtime_name already set", |
| 2420 | + runtimeName: "docker", |
| 2421 | + runConfigJSON: `{"name": "my-workload", "runtime_name": "docker", "image": "test:latest"}`, |
| 2422 | + expectWrite: false, |
| 2423 | + }, |
| 2424 | + { |
| 2425 | + name: "no-op when runtime_name set to different runtime", |
| 2426 | + runtimeName: "docker", |
| 2427 | + runConfigJSON: `{"name": "my-workload", "runtime_name": "go-microvm", "image": "test:latest"}`, |
| 2428 | + expectWrite: false, |
| 2429 | + }, |
| 2430 | + { |
| 2431 | + name: "graceful on GetReader error", |
| 2432 | + runtimeName: "docker", |
| 2433 | + getReaderErr: true, |
| 2434 | + expectWrite: false, |
| 2435 | + }, |
| 2436 | + { |
| 2437 | + name: "graceful on GetWriter error", |
| 2438 | + runtimeName: "docker", |
| 2439 | + runConfigJSON: `{"name": "my-workload"}`, |
| 2440 | + getWriterErr: true, |
| 2441 | + expectWrite: false, |
| 2442 | + }, |
| 2443 | + { |
| 2444 | + name: "graceful on malformed JSON", |
| 2445 | + runtimeName: "docker", |
| 2446 | + runConfigJSON: `{bad json`, |
| 2447 | + expectWrite: false, |
| 2448 | + }, |
| 2449 | + { |
| 2450 | + name: "migrates non-string runtime_name", |
| 2451 | + runtimeName: "docker", |
| 2452 | + runConfigJSON: `{"name": "my-workload", "runtime_name": 42}`, |
| 2453 | + expectWrite: true, |
| 2454 | + expectedRuntime: "docker", |
| 2455 | + }, |
| 2456 | + } |
| 2457 | + |
| 2458 | + for _, tt := range tests { |
| 2459 | + t.Run(tt.name, func(t *testing.T) { |
| 2460 | + t.Parallel() |
| 2461 | + |
| 2462 | + ctrl := gomock.NewController(t) |
| 2463 | + defer ctrl.Finish() |
| 2464 | + |
| 2465 | + manager, mockRuntime, mockRunConfigStore := newTestFileStatusManager(t, ctrl) |
| 2466 | + ctx := t.Context() |
| 2467 | + |
| 2468 | + workloadName := "test-workload" |
| 2469 | + mockRuntime.EXPECT().Name().Return(tt.runtimeName).AnyTimes() |
| 2470 | + |
| 2471 | + if tt.getReaderErr { |
| 2472 | + mockRunConfigStore.EXPECT().GetReader(gomock.Any(), workloadName). |
| 2473 | + Return(nil, errors.New("store error")) |
| 2474 | + } else { |
| 2475 | + mockRunConfigStore.EXPECT().GetReader(gomock.Any(), workloadName).DoAndReturn( |
| 2476 | + func(context.Context, string) (io.ReadCloser, error) { |
| 2477 | + return io.NopCloser(strings.NewReader(tt.runConfigJSON)), nil |
| 2478 | + }, |
| 2479 | + ) |
| 2480 | + } |
| 2481 | + |
| 2482 | + var writeBuf nopWriteCloser |
| 2483 | + if tt.expectWrite { |
| 2484 | + if tt.getWriterErr { |
| 2485 | + mockRunConfigStore.EXPECT().GetWriter(gomock.Any(), workloadName). |
| 2486 | + Return(nil, errors.New("write error")) |
| 2487 | + } else { |
| 2488 | + writeBuf = nopWriteCloser{&bytes.Buffer{}} |
| 2489 | + mockRunConfigStore.EXPECT().GetWriter(gomock.Any(), workloadName). |
| 2490 | + Return(writeBuf, nil) |
| 2491 | + } |
| 2492 | + } else if tt.getWriterErr { |
| 2493 | + mockRunConfigStore.EXPECT().GetWriter(gomock.Any(), workloadName). |
| 2494 | + Return(nil, errors.New("write error")) |
| 2495 | + } |
| 2496 | + |
| 2497 | + manager.migrateRuntimeName(ctx, workloadName) |
| 2498 | + |
| 2499 | + if tt.expectWrite && !tt.getWriterErr { |
| 2500 | + var written map[string]json.RawMessage |
| 2501 | + require.NoError(t, json.Unmarshal(writeBuf.Bytes(), &written)) |
| 2502 | + |
| 2503 | + var runtimeName string |
| 2504 | + require.NoError(t, json.Unmarshal(written["runtime_name"], &runtimeName)) |
| 2505 | + assert.Equal(t, tt.expectedRuntime, runtimeName, |
| 2506 | + "migrated runtime_name should match active runtime") |
| 2507 | + |
| 2508 | + // Verify other fields are preserved |
| 2509 | + var name string |
| 2510 | + require.NoError(t, json.Unmarshal(written["name"], &name)) |
| 2511 | + assert.Equal(t, "my-workload", name, "other fields should be preserved") |
| 2512 | + } |
| 2513 | + }) |
| 2514 | + } |
| 2515 | +} |
0 commit comments