Remove dead restart policy migration path
- commit
- 95b3044a727b00c25d6d6d323f93364050b03cfb
- author
- brice · · 2026-05-10 16:59 UTC
- parent
- 53f9355
- signature
- GPG Verified
CD88F9E564E0102646FB6077A1A07C7C7C3E349D· arch-gpg Verified for the committer email
6 files · +3 -358
M internal/adapters/out/docker/runtime.go +0 -36
| @@ -363,42 +363,6 @@ func (r *Runtime) RenameContainer(ctx context.Context, containerID, newName stri | |||
| 363 | 363 | return nil | |
| 364 | 364 | } | |
| 365 | 365 | ||
| 366 | - | // EnsureContainerRestartPolicy updates a container restart policy when it does not match. | |
| 367 | - | func (r *Runtime) EnsureContainerRestartPolicy(ctx context.Context, containerID, policy string) error { | |
| 368 | - | ctx = zerowrap.CtxWithFields(ctx, map[string]any{ | |
| 369 | - | zerowrap.FieldLayer: "adapter", | |
| 370 | - | zerowrap.FieldAdapter: "docker", | |
| 371 | - | zerowrap.FieldAction: "EnsureContainerRestartPolicy", | |
| 372 | - | zerowrap.FieldEntityID: containerID, | |
| 373 | - | "restart_policy": policy, | |
| 374 | - | }) | |
| 375 | - | log := zerowrap.FromCtx(ctx) | |
| 376 | - | ||
| 377 | - | inspect, err := r.client.ContainerInspect(ctx, containerID) | |
| 378 | - | if err != nil { | |
| 379 | - | return log.WrapErr(err, "failed to inspect container") | |
| 380 | - | } | |
| 381 | - | if inspect.HostConfig == nil { | |
| 382 | - | return fmt.Errorf("container inspect response missing host config: %s", containerID) | |
| 383 | - | } | |
| 384 | - | if string(inspect.HostConfig.RestartPolicy.Name) == policy { | |
| 385 | - | return nil | |
| 386 | - | } | |
| 387 | - | ||
| 388 | - | updateConfig := container.UpdateConfig{ | |
| 389 | - | Resources: inspect.HostConfig.Resources, | |
| 390 | - | RestartPolicy: container.RestartPolicy{Name: container.RestartPolicyMode(policy)}, | |
| 391 | - | } | |
| 392 | - | resp, err := r.client.ContainerUpdate(ctx, containerID, updateConfig) | |
| 393 | - | if err != nil { | |
| 394 | - | return log.WrapErr(err, "failed to update container restart policy") | |
| 395 | - | } | |
| 396 | - | for _, warning := range resp.Warnings { | |
| 397 | - | log.Warn().Str("warning", warning).Msg("container restart policy update warning") | |
| 398 | - | } | |
| 399 | - | return nil | |
| 400 | - | } | |
| 401 | - | ||
| 402 | 366 | // ListContainers lists containers. | |
| 403 | 367 | func (r *Runtime) ListContainers(ctx context.Context, all bool) ([]*domain.Container, error) { | |
| 404 | 368 | ctx = zerowrap.CtxWithFields(ctx, map[string]any{ | |
| @@ -363,42 +363,6 @@ func (r *Runtime) RenameContainer(ctx context.Context, containerID, newName stri | |||||
| 363 | return nil | 363 | return nil | ||
| 364 | } | 364 | } | ||
| 365 | 365 | ||||
| 366 | - | // EnsureContainerRestartPolicy updates a container restart policy when it does not match. | |||
| 367 | - | func (r *Runtime) EnsureContainerRestartPolicy(ctx context.Context, containerID, policy string) error { | |||
| 368 | - | ctx = zerowrap.CtxWithFields(ctx, map[string]any{ | |||
| 369 | - | zerowrap.FieldLayer: "adapter", | |||
| 370 | - | zerowrap.FieldAdapter: "docker", | |||
| 371 | - | zerowrap.FieldAction: "EnsureContainerRestartPolicy", | |||
| 372 | - | zerowrap.FieldEntityID: containerID, | |||
| 373 | - | "restart_policy": policy, | |||
| 374 | - | }) | |||
| 375 | - | log := zerowrap.FromCtx(ctx) | |||
| 376 | - | ||||
| 377 | - | inspect, err := r.client.ContainerInspect(ctx, containerID) | |||
| 378 | - | if err != nil { | |||
| 379 | - | return log.WrapErr(err, "failed to inspect container") | |||
| 380 | - | } | |||
| 381 | - | if inspect.HostConfig == nil { | |||
| 382 | - | return fmt.Errorf("container inspect response missing host config: %s", containerID) | |||
| 383 | - | } | |||
| 384 | - | if string(inspect.HostConfig.RestartPolicy.Name) == policy { | |||
| 385 | - | return nil | |||
| 386 | - | } | |||
| 387 | - | ||||
| 388 | - | updateConfig := container.UpdateConfig{ | |||
| 389 | - | Resources: inspect.HostConfig.Resources, | |||
| 390 | - | RestartPolicy: container.RestartPolicy{Name: container.RestartPolicyMode(policy)}, | |||
| 391 | - | } | |||
| 392 | - | resp, err := r.client.ContainerUpdate(ctx, containerID, updateConfig) | |||
| 393 | - | if err != nil { | |||
| 394 | - | return log.WrapErr(err, "failed to update container restart policy") | |||
| 395 | - | } | |||
| 396 | - | for _, warning := range resp.Warnings { | |||
| 397 | - | log.Warn().Str("warning", warning).Msg("container restart policy update warning") | |||
| 398 | - | } | |||
| 399 | - | return nil | |||
| 400 | - | } | |||
| 401 | - | ||||
| 402 | // ListContainers lists containers. | 366 | // ListContainers lists containers. | ||
| 403 | func (r *Runtime) ListContainers(ctx context.Context, all bool) ([]*domain.Container, error) { | 367 | func (r *Runtime) ListContainers(ctx context.Context, all bool) ([]*domain.Container, error) { | ||
| 404 | ctx = zerowrap.CtxWithFields(ctx, map[string]any{ | 368 | ctx = zerowrap.CtxWithFields(ctx, map[string]any{ | ||
D internal/adapters/out/docker/runtime_restart_policy_test.go +0 -129
| @@ -1,129 +0,0 @@ | |||
| 1 | - | package docker | |
| 2 | - | ||
| 3 | - | import ( | |
| 4 | - | "context" | |
| 5 | - | "encoding/json" | |
| 6 | - | "net/http" | |
| 7 | - | "net/http/httptest" | |
| 8 | - | "strings" | |
| 9 | - | "testing" | |
| 10 | - | ||
| 11 | - | "github.com/docker/docker/client" | |
| 12 | - | "github.com/stretchr/testify/assert" | |
| 13 | - | "github.com/stretchr/testify/require" | |
| 14 | - | ||
| 15 | - | "github.com/bnema/gordon/internal/domain" | |
| 16 | - | ) | |
| 17 | - | ||
| 18 | - | func TestRuntime_EnsureContainerRestartPolicy_SkipsWhenAlreadySet(t *testing.T) { | |
| 19 | - | var updateCalled bool | |
| 20 | - | server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | |
| 21 | - | switch { | |
| 22 | - | case r.Method == http.MethodGet && r.URL.Path == "/v1.41/containers/abc123/json": | |
| 23 | - | w.Header().Set("Content-Type", "application/json") | |
| 24 | - | _, _ = w.Write([]byte(`{ | |
| 25 | - | "Id": "abc123", | |
| 26 | - | "HostConfig": { | |
| 27 | - | "RestartPolicy": { | |
| 28 | - | "Name": "always" | |
| 29 | - | } | |
| 30 | - | } | |
| 31 | - | }`)) | |
| 32 | - | case r.Method == http.MethodPost && r.URL.Path == "/v1.41/containers/abc123/update": | |
| 33 | - | updateCalled = true | |
| 34 | - | w.WriteHeader(http.StatusInternalServerError) | |
| 35 | - | default: | |
| 36 | - | t.Fatalf("unexpected request: %s %s", r.Method, r.URL.Path) | |
| 37 | - | } | |
| 38 | - | })) | |
| 39 | - | defer server.Close() | |
| 40 | - | ||
| 41 | - | runtime := newRuntimeForRestartPolicyHTTPServer(t, server) | |
| 42 | - | err := runtime.EnsureContainerRestartPolicy(context.Background(), "abc123", domain.RestartPolicyAlways) | |
| 43 | - | require.NoError(t, err) | |
| 44 | - | assert.False(t, updateCalled, "update should not be called when restart policy is already set") | |
| 45 | - | } | |
| 46 | - | ||
| 47 | - | func TestRuntime_EnsureContainerRestartPolicy_UpdatesAndPreservesResources(t *testing.T) { | |
| 48 | - | var updateBody map[string]any | |
| 49 | - | server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | |
| 50 | - | switch { | |
| 51 | - | case r.Method == http.MethodGet && r.URL.Path == "/v1.41/containers/abc123/json": | |
| 52 | - | w.Header().Set("Content-Type", "application/json") | |
| 53 | - | _, _ = w.Write([]byte(`{ | |
| 54 | - | "Id": "abc123", | |
| 55 | - | "HostConfig": { | |
| 56 | - | "RestartPolicy": { | |
| 57 | - | "Name": "" | |
| 58 | - | }, | |
| 59 | - | "Memory": 268435456, | |
| 60 | - | "NanoCpus": 500000000, | |
| 61 | - | "PidsLimit": 128, | |
| 62 | - | "Ulimits": [ | |
| 63 | - | { | |
| 64 | - | "Name": "nofile", | |
| 65 | - | "Soft": 65536, | |
| 66 | - | "Hard": 65536 | |
| 67 | - | } | |
| 68 | - | ] | |
| 69 | - | } | |
| 70 | - | }`)) | |
| 71 | - | case r.Method == http.MethodPost && r.URL.Path == "/v1.41/containers/abc123/update": | |
| 72 | - | require.NoError(t, json.NewDecoder(r.Body).Decode(&updateBody)) | |
| 73 | - | w.Header().Set("Content-Type", "application/json") | |
| 74 | - | _, _ = w.Write([]byte(`{"Warnings":null}`)) | |
| 75 | - | default: | |
| 76 | - | t.Fatalf("unexpected request: %s %s", r.Method, r.URL.Path) | |
| 77 | - | } | |
| 78 | - | })) | |
| 79 | - | defer server.Close() | |
| 80 | - | ||
| 81 | - | runtime := newRuntimeForRestartPolicyHTTPServer(t, server) | |
| 82 | - | err := runtime.EnsureContainerRestartPolicy(context.Background(), "abc123", domain.RestartPolicyAlways) | |
| 83 | - | require.NoError(t, err) | |
| 84 | - | require.NotNil(t, updateBody) | |
| 85 | - | assert.Equal(t, domain.RestartPolicyAlways, updateBody["RestartPolicy"].(map[string]any)["Name"]) | |
| 86 | - | assert.Equal(t, float64(268435456), updateBody["Memory"]) | |
| 87 | - | assert.Equal(t, float64(500000000), updateBody["NanoCpus"]) | |
| 88 | - | assert.Equal(t, float64(128), updateBody["PidsLimit"]) | |
| 89 | - | ulimits, ok := updateBody["Ulimits"].([]any) | |
| 90 | - | require.True(t, ok, "Ulimits should be an array") | |
| 91 | - | require.Greater(t, len(ulimits), 0) | |
| 92 | - | firstUlimit, ok := ulimits[0].(map[string]any) | |
| 93 | - | require.True(t, ok, "first Ulimit should be a map") | |
| 94 | - | assert.Equal(t, "nofile", firstUlimit["Name"]) | |
| 95 | - | assert.Equal(t, float64(65536), firstUlimit["Soft"]) | |
| 96 | - | assert.Equal(t, float64(65536), firstUlimit["Hard"]) | |
| 97 | - | } | |
| 98 | - | ||
| 99 | - | func TestRuntime_EnsureContainerRestartPolicy_ReturnsErrorWhenHostConfigMissing(t *testing.T) { | |
| 100 | - | var updateCalled bool | |
| 101 | - | server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | |
| 102 | - | switch { | |
| 103 | - | case r.Method == http.MethodGet && r.URL.Path == "/v1.41/containers/abc123/json": | |
| 104 | - | w.Header().Set("Content-Type", "application/json") | |
| 105 | - | _, _ = w.Write([]byte(`{ | |
| 106 | - | "Id": "abc123" | |
| 107 | - | }`)) | |
| 108 | - | case r.Method == http.MethodPost && r.URL.Path == "/v1.41/containers/abc123/update": | |
| 109 | - | updateCalled = true | |
| 110 | - | w.WriteHeader(http.StatusInternalServerError) | |
| 111 | - | default: | |
| 112 | - | t.Fatalf("unexpected request: %s %s", r.Method, r.URL.Path) | |
| 113 | - | } | |
| 114 | - | })) | |
| 115 | - | defer server.Close() | |
| 116 | - | ||
| 117 | - | runtime := newRuntimeForRestartPolicyHTTPServer(t, server) | |
| 118 | - | err := runtime.EnsureContainerRestartPolicy(context.Background(), "abc123", domain.RestartPolicyAlways) | |
| 119 | - | require.Error(t, err) | |
| 120 | - | assert.Contains(t, err.Error(), "missing host config") | |
| 121 | - | assert.False(t, updateCalled, "update should not be called when host config is missing") | |
| 122 | - | } | |
| 123 | - | ||
| 124 | - | func newRuntimeForRestartPolicyHTTPServer(t *testing.T, server *httptest.Server) *Runtime { | |
| 125 | - | host := strings.TrimPrefix(server.URL, "http://") | |
| 126 | - | cli, err := client.NewClientWithOpts(client.WithHost("tcp://"+host), client.WithVersion("1.41"), client.WithHTTPClient(server.Client())) | |
| 127 | - | require.NoError(t, err) | |
| 128 | - | return NewRuntimeWithClient(cli) | |
| 129 | - | } | |
| @@ -1,129 +0,0 @@ | |||||
| 1 | - | package docker | |||
| 2 | - | ||||
| 3 | - | import ( | |||
| 4 | - | "context" | |||
| 5 | - | "encoding/json" | |||
| 6 | - | "net/http" | |||
| 7 | - | "net/http/httptest" | |||
| 8 | - | "strings" | |||
| 9 | - | "testing" | |||
| 10 | - | ||||
| 11 | - | "github.com/docker/docker/client" | |||
| 12 | - | "github.com/stretchr/testify/assert" | |||
| 13 | - | "github.com/stretchr/testify/require" | |||
| 14 | - | ||||
| 15 | - | "github.com/bnema/gordon/internal/domain" | |||
| 16 | - | ) | |||
| 17 | - | ||||
| 18 | - | func TestRuntime_EnsureContainerRestartPolicy_SkipsWhenAlreadySet(t *testing.T) { | |||
| 19 | - | var updateCalled bool | |||
| 20 | - | server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | |||
| 21 | - | switch { | |||
| 22 | - | case r.Method == http.MethodGet && r.URL.Path == "/v1.41/containers/abc123/json": | |||
| 23 | - | w.Header().Set("Content-Type", "application/json") | |||
| 24 | - | _, _ = w.Write([]byte(`{ | |||
| 25 | - | "Id": "abc123", | |||
| 26 | - | "HostConfig": { | |||
| 27 | - | "RestartPolicy": { | |||
| 28 | - | "Name": "always" | |||
| 29 | - | } | |||
| 30 | - | } | |||
| 31 | - | }`)) | |||
| 32 | - | case r.Method == http.MethodPost && r.URL.Path == "/v1.41/containers/abc123/update": | |||
| 33 | - | updateCalled = true | |||
| 34 | - | w.WriteHeader(http.StatusInternalServerError) | |||
| 35 | - | default: | |||
| 36 | - | t.Fatalf("unexpected request: %s %s", r.Method, r.URL.Path) | |||
| 37 | - | } | |||
| 38 | - | })) | |||
| 39 | - | defer server.Close() | |||
| 40 | - | ||||
| 41 | - | runtime := newRuntimeForRestartPolicyHTTPServer(t, server) | |||
| 42 | - | err := runtime.EnsureContainerRestartPolicy(context.Background(), "abc123", domain.RestartPolicyAlways) | |||
| 43 | - | require.NoError(t, err) | |||
| 44 | - | assert.False(t, updateCalled, "update should not be called when restart policy is already set") | |||
| 45 | - | } | |||
| 46 | - | ||||
| 47 | - | func TestRuntime_EnsureContainerRestartPolicy_UpdatesAndPreservesResources(t *testing.T) { | |||
| 48 | - | var updateBody map[string]any | |||
| 49 | - | server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | |||
| 50 | - | switch { | |||
| 51 | - | case r.Method == http.MethodGet && r.URL.Path == "/v1.41/containers/abc123/json": | |||
| 52 | - | w.Header().Set("Content-Type", "application/json") | |||
| 53 | - | _, _ = w.Write([]byte(`{ | |||
| 54 | - | "Id": "abc123", | |||
| 55 | - | "HostConfig": { | |||
| 56 | - | "RestartPolicy": { | |||
| 57 | - | "Name": "" | |||
| 58 | - | }, | |||
| 59 | - | "Memory": 268435456, | |||
| 60 | - | "NanoCpus": 500000000, | |||
| 61 | - | "PidsLimit": 128, | |||
| 62 | - | "Ulimits": [ | |||
| 63 | - | { | |||
| 64 | - | "Name": "nofile", | |||
| 65 | - | "Soft": 65536, | |||
| 66 | - | "Hard": 65536 | |||
| 67 | - | } | |||
| 68 | - | ] | |||
| 69 | - | } | |||
| 70 | - | }`)) | |||
| 71 | - | case r.Method == http.MethodPost && r.URL.Path == "/v1.41/containers/abc123/update": | |||
| 72 | - | require.NoError(t, json.NewDecoder(r.Body).Decode(&updateBody)) | |||
| 73 | - | w.Header().Set("Content-Type", "application/json") | |||
| 74 | - | _, _ = w.Write([]byte(`{"Warnings":null}`)) | |||
| 75 | - | default: | |||
| 76 | - | t.Fatalf("unexpected request: %s %s", r.Method, r.URL.Path) | |||
| 77 | - | } | |||
| 78 | - | })) | |||
| 79 | - | defer server.Close() | |||
| 80 | - | ||||
| 81 | - | runtime := newRuntimeForRestartPolicyHTTPServer(t, server) | |||
| 82 | - | err := runtime.EnsureContainerRestartPolicy(context.Background(), "abc123", domain.RestartPolicyAlways) | |||
| 83 | - | require.NoError(t, err) | |||
| 84 | - | require.NotNil(t, updateBody) | |||
| 85 | - | assert.Equal(t, domain.RestartPolicyAlways, updateBody["RestartPolicy"].(map[string]any)["Name"]) | |||
| 86 | - | assert.Equal(t, float64(268435456), updateBody["Memory"]) | |||
| 87 | - | assert.Equal(t, float64(500000000), updateBody["NanoCpus"]) | |||
| 88 | - | assert.Equal(t, float64(128), updateBody["PidsLimit"]) | |||
| 89 | - | ulimits, ok := updateBody["Ulimits"].([]any) | |||
| 90 | - | require.True(t, ok, "Ulimits should be an array") | |||
| 91 | - | require.Greater(t, len(ulimits), 0) | |||
| 92 | - | firstUlimit, ok := ulimits[0].(map[string]any) | |||
| 93 | - | require.True(t, ok, "first Ulimit should be a map") | |||
| 94 | - | assert.Equal(t, "nofile", firstUlimit["Name"]) | |||
| 95 | - | assert.Equal(t, float64(65536), firstUlimit["Soft"]) | |||
| 96 | - | assert.Equal(t, float64(65536), firstUlimit["Hard"]) | |||
| 97 | - | } | |||
| 98 | - | ||||
| 99 | - | func TestRuntime_EnsureContainerRestartPolicy_ReturnsErrorWhenHostConfigMissing(t *testing.T) { | |||
| 100 | - | var updateCalled bool | |||
| 101 | - | server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | |||
| 102 | - | switch { | |||
| 103 | - | case r.Method == http.MethodGet && r.URL.Path == "/v1.41/containers/abc123/json": | |||
| 104 | - | w.Header().Set("Content-Type", "application/json") | |||
| 105 | - | _, _ = w.Write([]byte(`{ | |||
| 106 | - | "Id": "abc123" | |||
| 107 | - | }`)) | |||
| 108 | - | case r.Method == http.MethodPost && r.URL.Path == "/v1.41/containers/abc123/update": | |||
| 109 | - | updateCalled = true | |||
| 110 | - | w.WriteHeader(http.StatusInternalServerError) | |||
| 111 | - | default: | |||
| 112 | - | t.Fatalf("unexpected request: %s %s", r.Method, r.URL.Path) | |||
| 113 | - | } | |||
| 114 | - | })) | |||
| 115 | - | defer server.Close() | |||
| 116 | - | ||||
| 117 | - | runtime := newRuntimeForRestartPolicyHTTPServer(t, server) | |||
| 118 | - | err := runtime.EnsureContainerRestartPolicy(context.Background(), "abc123", domain.RestartPolicyAlways) | |||
| 119 | - | require.Error(t, err) | |||
| 120 | - | assert.Contains(t, err.Error(), "missing host config") | |||
| 121 | - | assert.False(t, updateCalled, "update should not be called when host config is missing") | |||
| 122 | - | } | |||
| 123 | - | ||||
| 124 | - | func newRuntimeForRestartPolicyHTTPServer(t *testing.T, server *httptest.Server) *Runtime { | |||
| 125 | - | host := strings.TrimPrefix(server.URL, "http://") | |||
| 126 | - | cli, err := client.NewClientWithOpts(client.WithHost("tcp://"+host), client.WithVersion("1.41"), client.WithHTTPClient(server.Client())) | |||
| 127 | - | require.NoError(t, err) | |||
| 128 | - | return NewRuntimeWithClient(cli) | |||
| 129 | - | } | |||
M internal/boundaries/out/mocks/mock_container_runtime.go +0 -63
| @@ -428,69 +428,6 @@ func (_c *MockContainerRuntime_DisconnectContainerFromNetwork_Call) RunAndReturn | |||
| 428 | 428 | return _c | |
| 429 | 429 | } | |
| 430 | 430 | ||
| 431 | - | // EnsureContainerRestartPolicy provides a mock function for the type MockContainerRuntime | |
| 432 | - | func (_mock *MockContainerRuntime) EnsureContainerRestartPolicy(ctx context.Context, containerID string, policy string) error { | |
| 433 | - | ret := _mock.Called(ctx, containerID, policy) | |
| 434 | - | ||
| 435 | - | if len(ret) == 0 { | |
| 436 | - | panic("no return value specified for EnsureContainerRestartPolicy") | |
| 437 | - | } | |
| 438 | - | ||
| 439 | - | var r0 error | |
| 440 | - | if returnFunc, ok := ret.Get(0).(func(context.Context, string, string) error); ok { | |
| 441 | - | r0 = returnFunc(ctx, containerID, policy) | |
| 442 | - | } else { | |
| 443 | - | r0 = ret.Error(0) | |
| 444 | - | } | |
| 445 | - | return r0 | |
| 446 | - | } | |
| 447 | - | ||
| 448 | - | // MockContainerRuntime_EnsureContainerRestartPolicy_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'EnsureContainerRestartPolicy' | |
| 449 | - | type MockContainerRuntime_EnsureContainerRestartPolicy_Call struct { | |
| 450 | - | *mock.Call | |
| 451 | - | } | |
| 452 | - | ||
| 453 | - | // EnsureContainerRestartPolicy is a helper method to define mock.On call | |
| 454 | - | // - ctx context.Context | |
| 455 | - | // - containerID string | |
| 456 | - | // - policy string | |
| 457 | - | func (_e *MockContainerRuntime_Expecter) EnsureContainerRestartPolicy(ctx interface{}, containerID interface{}, policy interface{}) *MockContainerRuntime_EnsureContainerRestartPolicy_Call { | |
| 458 | - | return &MockContainerRuntime_EnsureContainerRestartPolicy_Call{Call: _e.mock.On("EnsureContainerRestartPolicy", ctx, containerID, policy)} | |
| 459 | - | } | |
| 460 | - | ||
| 461 | - | func (_c *MockContainerRuntime_EnsureContainerRestartPolicy_Call) Run(run func(ctx context.Context, containerID string, policy string)) *MockContainerRuntime_EnsureContainerRestartPolicy_Call { | |
| 462 | - | _c.Call.Run(func(args mock.Arguments) { | |
| 463 | - | var arg0 context.Context | |
| 464 | - | if args[0] != nil { | |
| 465 | - | arg0 = args[0].(context.Context) | |
| 466 | - | } | |
| 467 | - | var arg1 string | |
| 468 | - | if args[1] != nil { | |
| 469 | - | arg1 = args[1].(string) | |
| 470 | - | } | |
| 471 | - | var arg2 string | |
| 472 | - | if args[2] != nil { | |
| 473 | - | arg2 = args[2].(string) | |
| 474 | - | } | |
| 475 | - | run( | |
| 476 | - | arg0, | |
| 477 | - | arg1, | |
| 478 | - | arg2, | |
| 479 | - | ) | |
| 480 | - | }) | |
| 481 | - | return _c | |
| 482 | - | } | |
| 483 | - | ||
| 484 | - | func (_c *MockContainerRuntime_EnsureContainerRestartPolicy_Call) Return(err error) *MockContainerRuntime_EnsureContainerRestartPolicy_Call { | |
| 485 | - | _c.Call.Return(err) | |
| 486 | - | return _c | |
| 487 | - | } | |
| 488 | - | ||
| 489 | - | func (_c *MockContainerRuntime_EnsureContainerRestartPolicy_Call) RunAndReturn(run func(ctx context.Context, containerID string, policy string) error) *MockContainerRuntime_EnsureContainerRestartPolicy_Call { | |
| 490 | - | _c.Call.Return(run) | |
| 491 | - | return _c | |
| 492 | - | } | |
| 493 | - | ||
| 494 | 431 | // ExecInContainer provides a mock function for the type MockContainerRuntime | |
| 495 | 432 | func (_mock *MockContainerRuntime) ExecInContainer(ctx context.Context, containerID string, cmd []string) (*out.ExecResult, error) { | |
| 496 | 433 | ret := _mock.Called(ctx, containerID, cmd) | |
| @@ -428,69 +428,6 @@ func (_c *MockContainerRuntime_DisconnectContainerFromNetwork_Call) RunAndReturn | |||||
| 428 | return _c | 428 | return _c | ||
| 429 | } | 429 | } | ||
| 430 | 430 | ||||
| 431 | - | // EnsureContainerRestartPolicy provides a mock function for the type MockContainerRuntime | |||
| 432 | - | func (_mock *MockContainerRuntime) EnsureContainerRestartPolicy(ctx context.Context, containerID string, policy string) error { | |||
| 433 | - | ret := _mock.Called(ctx, containerID, policy) | |||
| 434 | - | ||||
| 435 | - | if len(ret) == 0 { | |||
| 436 | - | panic("no return value specified for EnsureContainerRestartPolicy") | |||
| 437 | - | } | |||
| 438 | - | ||||
| 439 | - | var r0 error | |||
| 440 | - | if returnFunc, ok := ret.Get(0).(func(context.Context, string, string) error); ok { | |||
| 441 | - | r0 = returnFunc(ctx, containerID, policy) | |||
| 442 | - | } else { | |||
| 443 | - | r0 = ret.Error(0) | |||
| 444 | - | } | |||
| 445 | - | return r0 | |||
| 446 | - | } | |||
| 447 | - | ||||
| 448 | - | // MockContainerRuntime_EnsureContainerRestartPolicy_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'EnsureContainerRestartPolicy' | |||
| 449 | - | type MockContainerRuntime_EnsureContainerRestartPolicy_Call struct { | |||
| 450 | - | *mock.Call | |||
| 451 | - | } | |||
| 452 | - | ||||
| 453 | - | // EnsureContainerRestartPolicy is a helper method to define mock.On call | |||
| 454 | - | // - ctx context.Context | |||
| 455 | - | // - containerID string | |||
| 456 | - | // - policy string | |||
| 457 | - | func (_e *MockContainerRuntime_Expecter) EnsureContainerRestartPolicy(ctx interface{}, containerID interface{}, policy interface{}) *MockContainerRuntime_EnsureContainerRestartPolicy_Call { | |||
| 458 | - | return &MockContainerRuntime_EnsureContainerRestartPolicy_Call{Call: _e.mock.On("EnsureContainerRestartPolicy", ctx, containerID, policy)} | |||
| 459 | - | } | |||
| 460 | - | ||||
| 461 | - | func (_c *MockContainerRuntime_EnsureContainerRestartPolicy_Call) Run(run func(ctx context.Context, containerID string, policy string)) *MockContainerRuntime_EnsureContainerRestartPolicy_Call { | |||
| 462 | - | _c.Call.Run(func(args mock.Arguments) { | |||
| 463 | - | var arg0 context.Context | |||
| 464 | - | if args[0] != nil { | |||
| 465 | - | arg0 = args[0].(context.Context) | |||
| 466 | - | } | |||
| 467 | - | var arg1 string | |||
| 468 | - | if args[1] != nil { | |||
| 469 | - | arg1 = args[1].(string) | |||
| 470 | - | } | |||
| 471 | - | var arg2 string | |||
| 472 | - | if args[2] != nil { | |||
| 473 | - | arg2 = args[2].(string) | |||
| 474 | - | } | |||
| 475 | - | run( | |||
| 476 | - | arg0, | |||
| 477 | - | arg1, | |||
| 478 | - | arg2, | |||
| 479 | - | ) | |||
| 480 | - | }) | |||
| 481 | - | return _c | |||
| 482 | - | } | |||
| 483 | - | ||||
| 484 | - | func (_c *MockContainerRuntime_EnsureContainerRestartPolicy_Call) Return(err error) *MockContainerRuntime_EnsureContainerRestartPolicy_Call { | |||
| 485 | - | _c.Call.Return(err) | |||
| 486 | - | return _c | |||
| 487 | - | } | |||
| 488 | - | ||||
| 489 | - | func (_c *MockContainerRuntime_EnsureContainerRestartPolicy_Call) RunAndReturn(run func(ctx context.Context, containerID string, policy string) error) *MockContainerRuntime_EnsureContainerRestartPolicy_Call { | |||
| 490 | - | _c.Call.Return(run) | |||
| 491 | - | return _c | |||
| 492 | - | } | |||
| 493 | - | ||||
| 494 | // ExecInContainer provides a mock function for the type MockContainerRuntime | 431 | // ExecInContainer provides a mock function for the type MockContainerRuntime | ||
| 495 | func (_mock *MockContainerRuntime) ExecInContainer(ctx context.Context, containerID string, cmd []string) (*out.ExecResult, error) { | 432 | func (_mock *MockContainerRuntime) ExecInContainer(ctx context.Context, containerID string, cmd []string) (*out.ExecResult, error) { | ||
| 496 | ret := _mock.Called(ctx, containerID, cmd) | 433 | ret := _mock.Called(ctx, containerID, cmd) | ||
M internal/boundaries/out/runtime.go +0 -1
| @@ -20,7 +20,6 @@ type ContainerRuntime interface { | |||
| 20 | 20 | RestartContainer(ctx context.Context, containerID string) error | |
| 21 | 21 | RemoveContainer(ctx context.Context, containerID string, force bool) error | |
| 22 | 22 | RenameContainer(ctx context.Context, containerID, newName string) error | |
| 23 | - | EnsureContainerRestartPolicy(ctx context.Context, containerID, policy string) error | |
| 24 | 23 | ||
| 25 | 24 | // Container inspection | |
| 26 | 25 | ListContainers(ctx context.Context, all bool) ([]*domain.Container, error) | |
| @@ -20,7 +20,6 @@ type ContainerRuntime interface { | |||||
| 20 | RestartContainer(ctx context.Context, containerID string) error | 20 | RestartContainer(ctx context.Context, containerID string) error | ||
| 21 | RemoveContainer(ctx context.Context, containerID string, force bool) error | 21 | RemoveContainer(ctx context.Context, containerID string, force bool) error | ||
| 22 | RenameContainer(ctx context.Context, containerID, newName string) error | 22 | RenameContainer(ctx context.Context, containerID, newName string) error | ||
| 23 | - | EnsureContainerRestartPolicy(ctx context.Context, containerID, policy string) error | |||
| 24 | 23 | ||||
| 25 | // Container inspection | 24 | // Container inspection | ||
| 26 | ListContainers(ctx context.Context, all bool) ([]*domain.Container, error) | 25 | ListContainers(ctx context.Context, all bool) ([]*domain.Container, error) | ||
M internal/usecase/container/service.go +3 -38
| @@ -1401,41 +1401,6 @@ func (s *Service) HealthCheck(ctx context.Context) map[string]bool { | |||
| 1401 | 1401 | return health | |
| 1402 | 1402 | } | |
| 1403 | 1403 | ||
| 1404 | - | // EnsureManagedContainerRestartPolicies updates restart policy for all managed containers. | |
| 1405 | - | func (s *Service) EnsureManagedContainerRestartPolicies(ctx context.Context) error { | |
| 1406 | - | ctx = zerowrap.CtxWithFields(ctx, map[string]any{ | |
| 1407 | - | zerowrap.FieldLayer: "usecase", | |
| 1408 | - | zerowrap.FieldUseCase: "EnsureManagedContainerRestartPolicies", | |
| 1409 | - | }) | |
| 1410 | - | log := zerowrap.FromCtx(ctx) | |
| 1411 | - | ||
| 1412 | - | allContainers, err := s.runtime.ListContainers(ctx, true) | |
| 1413 | - | if err != nil { | |
| 1414 | - | return log.WrapErr(err, "failed to list containers for restart policy migration") | |
| 1415 | - | } | |
| 1416 | - | ||
| 1417 | - | return s.ensureManagedContainerRestartPolicies(ctx, allContainers) | |
| 1418 | - | } | |
| 1419 | - | ||
| 1420 | - | func (s *Service) ensureManagedContainerRestartPolicies(ctx context.Context, allContainers []*domain.Container) error { | |
| 1421 | - | log := zerowrap.FromCtx(ctx) | |
| 1422 | - | ||
| 1423 | - | var errs []error | |
| 1424 | - | for _, c := range allContainers { | |
| 1425 | - | if c.Labels == nil || c.Labels[domain.LabelManaged] != "true" { | |
| 1426 | - | continue | |
| 1427 | - | } | |
| 1428 | - | if err := s.runtime.EnsureContainerRestartPolicy(ctx, c.ID, domain.RestartPolicyAlways); err != nil { | |
| 1429 | - | log.Warn().Err(err). | |
| 1430 | - | Str(zerowrap.FieldEntityID, c.ID). | |
| 1431 | - | Str("container_name", c.Name). | |
| 1432 | - | Msg("failed to ensure managed container restart policy") | |
| 1433 | - | errs = append(errs, fmt.Errorf("container %s: %w", c.ID, err)) | |
| 1434 | - | } | |
| 1435 | - | } | |
| 1436 | - | return errors.Join(errs...) | |
| 1437 | - | } | |
| 1438 | - | ||
| 1439 | 1404 | // SyncContainers synchronizes containers with runtime state. | |
| 1440 | 1405 | func (s *Service) SyncContainers(ctx context.Context) error { | |
| 1441 | 1406 | ctx = zerowrap.CtxWithFields(ctx, map[string]any{ | |
| @@ -1444,9 +1409,9 @@ func (s *Service) SyncContainers(ctx context.Context) error { | |||
| 1444 | 1409 | }) | |
| 1445 | 1410 | log := zerowrap.FromCtx(ctx) | |
| 1446 | 1411 | ||
| 1447 | - | // List containers without holding lock to avoid blocking during Docker API call. | |
| 1448 | - | // Use all=true so the same runtime snapshot can migrate stopped containers and | |
| 1449 | - | // rebuild the running-container maps without a second Docker list call. | |
| 1412 | + | // List containers without holding lock to avoid blocking during the runtime call. | |
| 1413 | + | // Use all=true so we can rebuild the running-container maps from a single | |
| 1414 | + | // snapshot without a second list call. | |
| 1450 | 1415 | allContainers, err := s.runtime.ListContainers(ctx, true) | |
| 1451 | 1416 | if err != nil { | |
| 1452 | 1417 | return log.WrapErr(err, "failed to list containers") | |
| @@ -1401,41 +1401,6 @@ func (s *Service) HealthCheck(ctx context.Context) map[string]bool { | |||||
| 1401 | return health | 1401 | return health | ||
| 1402 | } | 1402 | } | ||
| 1403 | 1403 | ||||
| 1404 | - | // EnsureManagedContainerRestartPolicies updates restart policy for all managed containers. | |||
| 1405 | - | func (s *Service) EnsureManagedContainerRestartPolicies(ctx context.Context) error { | |||
| 1406 | - | ctx = zerowrap.CtxWithFields(ctx, map[string]any{ | |||
| 1407 | - | zerowrap.FieldLayer: "usecase", | |||
| 1408 | - | zerowrap.FieldUseCase: "EnsureManagedContainerRestartPolicies", | |||
| 1409 | - | }) | |||
| 1410 | - | log := zerowrap.FromCtx(ctx) | |||
| 1411 | - | ||||
| 1412 | - | allContainers, err := s.runtime.ListContainers(ctx, true) | |||
| 1413 | - | if err != nil { | |||
| 1414 | - | return log.WrapErr(err, "failed to list containers for restart policy migration") | |||
| 1415 | - | } | |||
| 1416 | - | ||||
| 1417 | - | return s.ensureManagedContainerRestartPolicies(ctx, allContainers) | |||
| 1418 | - | } | |||
| 1419 | - | ||||
| 1420 | - | func (s *Service) ensureManagedContainerRestartPolicies(ctx context.Context, allContainers []*domain.Container) error { | |||
| 1421 | - | log := zerowrap.FromCtx(ctx) | |||
| 1422 | - | ||||
| 1423 | - | var errs []error | |||
| 1424 | - | for _, c := range allContainers { | |||
| 1425 | - | if c.Labels == nil || c.Labels[domain.LabelManaged] != "true" { | |||
| 1426 | - | continue | |||
| 1427 | - | } | |||
| 1428 | - | if err := s.runtime.EnsureContainerRestartPolicy(ctx, c.ID, domain.RestartPolicyAlways); err != nil { | |||
| 1429 | - | log.Warn().Err(err). | |||
| 1430 | - | Str(zerowrap.FieldEntityID, c.ID). | |||
| 1431 | - | Str("container_name", c.Name). | |||
| 1432 | - | Msg("failed to ensure managed container restart policy") | |||
| 1433 | - | errs = append(errs, fmt.Errorf("container %s: %w", c.ID, err)) | |||
| 1434 | - | } | |||
| 1435 | - | } | |||
| 1436 | - | return errors.Join(errs...) | |||
| 1437 | - | } | |||
| 1438 | - | ||||
| 1439 | // SyncContainers synchronizes containers with runtime state. | 1404 | // SyncContainers synchronizes containers with runtime state. | ||
| 1440 | func (s *Service) SyncContainers(ctx context.Context) error { | 1405 | func (s *Service) SyncContainers(ctx context.Context) error { | ||
| 1441 | ctx = zerowrap.CtxWithFields(ctx, map[string]any{ | 1406 | ctx = zerowrap.CtxWithFields(ctx, map[string]any{ | ||
| @@ -1444,9 +1409,9 @@ func (s *Service) SyncContainers(ctx context.Context) error { | |||||
| 1444 | }) | 1409 | }) | ||
| 1445 | log := zerowrap.FromCtx(ctx) | 1410 | log := zerowrap.FromCtx(ctx) | ||
| 1446 | 1411 | ||||
| 1447 | - | // List containers without holding lock to avoid blocking during Docker API call. | 1412 | + | // List containers without holding lock to avoid blocking during the runtime call. |
| 1448 | - | // Use all=true so the same runtime snapshot can migrate stopped containers and | 1413 | + | // Use all=true so we can rebuild the running-container maps from a single |
| 1449 | - | // rebuild the running-container maps without a second Docker list call. | 1414 | + | // snapshot without a second list call. |
| 1450 | allContainers, err := s.runtime.ListContainers(ctx, true) | 1415 | allContainers, err := s.runtime.ListContainers(ctx, true) | ||
| 1451 | if err != nil { | 1416 | if err != nil { | ||
| 1452 | return log.WrapErr(err, "failed to list containers") | 1417 | return log.WrapErr(err, "failed to list containers") | ||
M internal/usecase/container/service_test.go +0 -91
| @@ -1237,97 +1237,6 @@ func TestService_HealthCheck(t *testing.T) { | |||
| 1237 | 1237 | assert.False(t, result["unhealthy.example.com"]) | |
| 1238 | 1238 | } | |
| 1239 | 1239 | ||
| 1240 | - | func TestService_EnsureManagedContainerRestartPolicies_EnsuresRoutesAttachmentsAndStoppedContainers(t *testing.T) { | |
| 1241 | - | runtime := mocks.NewMockContainerRuntime(t) | |
| 1242 | - | envLoader := mocks.NewMockEnvLoader(t) | |
| 1243 | - | eventBus := mocks.NewMockEventPublisher(t) | |
| 1244 | - | ||
| 1245 | - | svc := NewService(runtime, envLoader, eventBus, nil, Config{}, nil) | |
| 1246 | - | ctx := testContext() | |
| 1247 | - | ||
| 1248 | - | routeContainer := &domain.Container{ | |
| 1249 | - | ID: "route-1", | |
| 1250 | - | Name: "gordon-app.example.com", | |
| 1251 | - | Status: string(domain.ContainerStatusRunning), | |
| 1252 | - | Labels: map[string]string{ | |
| 1253 | - | domain.LabelManaged: "true", | |
| 1254 | - | domain.LabelDomain: "app.example.com", | |
| 1255 | - | }, | |
| 1256 | - | } | |
| 1257 | - | attachmentContainer := &domain.Container{ | |
| 1258 | - | ID: "attachment-1", | |
| 1259 | - | Name: "gordon-app-example-com-postgres", | |
| 1260 | - | Status: string(domain.ContainerStatusRunning), | |
| 1261 | - | Labels: map[string]string{ | |
| 1262 | - | domain.LabelManaged: "true", | |
| 1263 | - | domain.LabelAttachment: "true", | |
| 1264 | - | domain.LabelAttachedTo: "app.example.com", | |
| 1265 | - | }, | |
| 1266 | - | } | |
| 1267 | - | stoppedContainer := &domain.Container{ | |
| 1268 | - | ID: "stopped-1", | |
| 1269 | - | Name: "gordon-stopped.example.com", | |
| 1270 | - | Status: string(domain.ContainerStatusExited), | |
| 1271 | - | Labels: map[string]string{ | |
| 1272 | - | domain.LabelManaged: "true", | |
| 1273 | - | domain.LabelDomain: "stopped.example.com", | |
| 1274 | - | }, | |
| 1275 | - | } | |
| 1276 | - | unmanagedContainer := &domain.Container{ | |
| 1277 | - | ID: "other-1", | |
| 1278 | - | Labels: map[string]string{}, | |
| 1279 | - | } | |
| 1280 | - | ||
| 1281 | - | runtime.EXPECT().ListContainers(mock.Anything, true).Return([]*domain.Container{ | |
| 1282 | - | routeContainer, | |
| 1283 | - | attachmentContainer, | |
| 1284 | - | stoppedContainer, | |
| 1285 | - | unmanagedContainer, | |
| 1286 | - | }, nil).Once() | |
| 1287 | - | runtime.EXPECT().EnsureContainerRestartPolicy(mock.Anything, "route-1", domain.RestartPolicyAlways).Return(nil).Once() | |
| 1288 | - | runtime.EXPECT().EnsureContainerRestartPolicy(mock.Anything, "attachment-1", domain.RestartPolicyAlways).Return(nil).Once() | |
| 1289 | - | runtime.EXPECT().EnsureContainerRestartPolicy(mock.Anything, "stopped-1", domain.RestartPolicyAlways).Return(nil).Once() | |
| 1290 | - | err := svc.EnsureManagedContainerRestartPolicies(ctx) | |
| 1291 | - | ||
| 1292 | - | require.NoError(t, err) | |
| 1293 | - | runtime.AssertNumberOfCalls(t, "EnsureContainerRestartPolicy", 3) | |
| 1294 | - | } | |
| 1295 | - | ||
| 1296 | - | func TestService_EnsureManagedContainerRestartPolicies_ContinuesAndReturnsError(t *testing.T) { | |
| 1297 | - | runtime := mocks.NewMockContainerRuntime(t) | |
| 1298 | - | envLoader := mocks.NewMockEnvLoader(t) | |
| 1299 | - | eventBus := mocks.NewMockEventPublisher(t) | |
| 1300 | - | ||
| 1301 | - | svc := NewService(runtime, envLoader, eventBus, nil, Config{}, nil) | |
| 1302 | - | ctx := testContext() | |
| 1303 | - | ||
| 1304 | - | routeContainer := &domain.Container{ | |
| 1305 | - | ID: "route-1", | |
| 1306 | - | Name: "gordon-app.example.com", | |
| 1307 | - | Labels: map[string]string{ | |
| 1308 | - | domain.LabelManaged: "true", | |
| 1309 | - | domain.LabelDomain: "app.example.com", | |
| 1310 | - | }, | |
| 1311 | - | } | |
| 1312 | - | routeContainer2 := &domain.Container{ | |
| 1313 | - | ID: "route-2", | |
| 1314 | - | Name: "gordon-api.example.com", | |
| 1315 | - | Labels: map[string]string{ | |
| 1316 | - | domain.LabelManaged: "true", | |
| 1317 | - | domain.LabelDomain: "api.example.com", | |
| 1318 | - | }, | |
| 1319 | - | } | |
| 1320 | - | ||
| 1321 | - | runtime.EXPECT().ListContainers(mock.Anything, true).Return([]*domain.Container{routeContainer, routeContainer2}, nil).Once() | |
| 1322 | - | runtime.EXPECT().EnsureContainerRestartPolicy(mock.Anything, "route-1", domain.RestartPolicyAlways).Return(errors.New("update failed")).Once() | |
| 1323 | - | runtime.EXPECT().EnsureContainerRestartPolicy(mock.Anything, "route-2", domain.RestartPolicyAlways).Return(nil).Once() | |
| 1324 | - | err := svc.EnsureManagedContainerRestartPolicies(ctx) | |
| 1325 | - | ||
| 1326 | - | require.Error(t, err) | |
| 1327 | - | assert.Contains(t, err.Error(), "route-1") | |
| 1328 | - | assert.Contains(t, err.Error(), "update failed") | |
| 1329 | - | } | |
| 1330 | - | ||
| 1331 | 1240 | func TestService_SyncContainers(t *testing.T) { | |
| 1332 | 1241 | runtime := mocks.NewMockContainerRuntime(t) | |
| 1333 | 1242 | envLoader := mocks.NewMockEnvLoader(t) | |
| @@ -1237,97 +1237,6 @@ func TestService_HealthCheck(t *testing.T) { | |||||
| 1237 | assert.False(t, result["unhealthy.example.com"]) | 1237 | assert.False(t, result["unhealthy.example.com"]) | ||
| 1238 | } | 1238 | } | ||
| 1239 | 1239 | ||||
| 1240 | - | func TestService_EnsureManagedContainerRestartPolicies_EnsuresRoutesAttachmentsAndStoppedContainers(t *testing.T) { | |||
| 1241 | - | runtime := mocks.NewMockContainerRuntime(t) | |||
| 1242 | - | envLoader := mocks.NewMockEnvLoader(t) | |||
| 1243 | - | eventBus := mocks.NewMockEventPublisher(t) | |||
| 1244 | - | ||||
| 1245 | - | svc := NewService(runtime, envLoader, eventBus, nil, Config{}, nil) | |||
| 1246 | - | ctx := testContext() | |||
| 1247 | - | ||||
| 1248 | - | routeContainer := &domain.Container{ | |||
| 1249 | - | ID: "route-1", | |||
| 1250 | - | Name: "gordon-app.example.com", | |||
| 1251 | - | Status: string(domain.ContainerStatusRunning), | |||
| 1252 | - | Labels: map[string]string{ | |||
| 1253 | - | domain.LabelManaged: "true", | |||
| 1254 | - | domain.LabelDomain: "app.example.com", | |||
| 1255 | - | }, | |||
| 1256 | - | } | |||
| 1257 | - | attachmentContainer := &domain.Container{ | |||
| 1258 | - | ID: "attachment-1", | |||
| 1259 | - | Name: "gordon-app-example-com-postgres", | |||
| 1260 | - | Status: string(domain.ContainerStatusRunning), | |||
| 1261 | - | Labels: map[string]string{ | |||
| 1262 | - | domain.LabelManaged: "true", | |||
| 1263 | - | domain.LabelAttachment: "true", | |||
| 1264 | - | domain.LabelAttachedTo: "app.example.com", | |||
| 1265 | - | }, | |||
| 1266 | - | } | |||
| 1267 | - | stoppedContainer := &domain.Container{ | |||
| 1268 | - | ID: "stopped-1", | |||
| 1269 | - | Name: "gordon-stopped.example.com", | |||
| 1270 | - | Status: string(domain.ContainerStatusExited), | |||
| 1271 | - | Labels: map[string]string{ | |||
| 1272 | - | domain.LabelManaged: "true", | |||
| 1273 | - | domain.LabelDomain: "stopped.example.com", | |||
| 1274 | - | }, | |||
| 1275 | - | } | |||
| 1276 | - | unmanagedContainer := &domain.Container{ | |||
| 1277 | - | ID: "other-1", | |||
| 1278 | - | Labels: map[string]string{}, | |||
| 1279 | - | } | |||
| 1280 | - | ||||
| 1281 | - | runtime.EXPECT().ListContainers(mock.Anything, true).Return([]*domain.Container{ | |||
| 1282 | - | routeContainer, | |||
| 1283 | - | attachmentContainer, | |||
| 1284 | - | stoppedContainer, | |||
| 1285 | - | unmanagedContainer, | |||
| 1286 | - | }, nil).Once() | |||
| 1287 | - | runtime.EXPECT().EnsureContainerRestartPolicy(mock.Anything, "route-1", domain.RestartPolicyAlways).Return(nil).Once() | |||
| 1288 | - | runtime.EXPECT().EnsureContainerRestartPolicy(mock.Anything, "attachment-1", domain.RestartPolicyAlways).Return(nil).Once() | |||
| 1289 | - | runtime.EXPECT().EnsureContainerRestartPolicy(mock.Anything, "stopped-1", domain.RestartPolicyAlways).Return(nil).Once() | |||
| 1290 | - | err := svc.EnsureManagedContainerRestartPolicies(ctx) | |||
| 1291 | - | ||||
| 1292 | - | require.NoError(t, err) | |||
| 1293 | - | runtime.AssertNumberOfCalls(t, "EnsureContainerRestartPolicy", 3) | |||
| 1294 | - | } | |||
| 1295 | - | ||||
| 1296 | - | func TestService_EnsureManagedContainerRestartPolicies_ContinuesAndReturnsError(t *testing.T) { | |||
| 1297 | - | runtime := mocks.NewMockContainerRuntime(t) | |||
| 1298 | - | envLoader := mocks.NewMockEnvLoader(t) | |||
| 1299 | - | eventBus := mocks.NewMockEventPublisher(t) | |||
| 1300 | - | ||||
| 1301 | - | svc := NewService(runtime, envLoader, eventBus, nil, Config{}, nil) | |||
| 1302 | - | ctx := testContext() | |||
| 1303 | - | ||||
| 1304 | - | routeContainer := &domain.Container{ | |||
| 1305 | - | ID: "route-1", | |||
| 1306 | - | Name: "gordon-app.example.com", | |||
| 1307 | - | Labels: map[string]string{ | |||
| 1308 | - | domain.LabelManaged: "true", | |||
| 1309 | - | domain.LabelDomain: "app.example.com", | |||
| 1310 | - | }, | |||
| 1311 | - | } | |||
| 1312 | - | routeContainer2 := &domain.Container{ | |||
| 1313 | - | ID: "route-2", | |||
| 1314 | - | Name: "gordon-api.example.com", | |||
| 1315 | - | Labels: map[string]string{ | |||
| 1316 | - | domain.LabelManaged: "true", | |||
| 1317 | - | domain.LabelDomain: "api.example.com", | |||
| 1318 | - | }, | |||
| 1319 | - | } | |||
| 1320 | - | ||||
| 1321 | - | runtime.EXPECT().ListContainers(mock.Anything, true).Return([]*domain.Container{routeContainer, routeContainer2}, nil).Once() | |||
| 1322 | - | runtime.EXPECT().EnsureContainerRestartPolicy(mock.Anything, "route-1", domain.RestartPolicyAlways).Return(errors.New("update failed")).Once() | |||
| 1323 | - | runtime.EXPECT().EnsureContainerRestartPolicy(mock.Anything, "route-2", domain.RestartPolicyAlways).Return(nil).Once() | |||
| 1324 | - | err := svc.EnsureManagedContainerRestartPolicies(ctx) | |||
| 1325 | - | ||||
| 1326 | - | require.Error(t, err) | |||
| 1327 | - | assert.Contains(t, err.Error(), "route-1") | |||
| 1328 | - | assert.Contains(t, err.Error(), "update failed") | |||
| 1329 | - | } | |||
| 1330 | - | ||||
| 1331 | func TestService_SyncContainers(t *testing.T) { | 1240 | func TestService_SyncContainers(t *testing.T) { | ||
| 1332 | runtime := mocks.NewMockContainerRuntime(t) | 1241 | runtime := mocks.NewMockContainerRuntime(t) | ||
| 1333 | envLoader := mocks.NewMockEnvLoader(t) | 1242 | envLoader := mocks.NewMockEnvLoader(t) | ||