-
Notifications
You must be signed in to change notification settings - Fork 229
Track runtime ownership to prevent cross-runtime status corruption #4434
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
b6c69ab
85babb6
094fb11
d30f994
8d7851a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,62 @@ | ||
| // SPDX-FileCopyrightText: Copyright 2025 Stacklok, Inc. | ||
| // SPDX-License-Identifier: Apache-2.0 | ||
|
|
||
| package docker | ||
|
|
||
| import ( | ||
| "testing" | ||
|
|
||
| "github.com/stretchr/testify/assert" | ||
|
|
||
| rt "github.com/stacklok/toolhive/pkg/container/runtime" | ||
| ) | ||
|
|
||
| // TestClientName verifies that Name reports the concrete detected runtime type | ||
| // rather than the static factory-registration constant. Distinct identities are | ||
| // required so that workload-ownership tracking does not let Docker- and | ||
| // Podman-owned workloads (separate daemons) corrupt each other's status. | ||
| func TestClientName(t *testing.T) { | ||
| t.Parallel() | ||
|
|
||
| tests := []struct { | ||
| name string | ||
| runtimeType rt.Type | ||
| want string | ||
| }{ | ||
| {name: "docker", runtimeType: rt.TypeDocker, want: "docker"}, | ||
| {name: "podman", runtimeType: rt.TypePodman, want: "podman"}, | ||
| {name: "colima", runtimeType: rt.TypeColima, want: "colima"}, | ||
| } | ||
|
|
||
| for _, tt := range tests { | ||
| t.Run(tt.name, func(t *testing.T) { | ||
| t.Parallel() | ||
|
|
||
| c := &Client{runtimeType: tt.runtimeType} | ||
| assert.Equal(t, tt.want, c.Name()) | ||
| }) | ||
| } | ||
| } | ||
|
|
||
| // TestClientNameOwnershipRoundTrip asserts the property the cross-runtime | ||
| // protection relies on: the identity stamped at create time and the identity | ||
| // compared at reconcile time come from the same Name call, so a workload created | ||
| // under Podman is recognized as Podman-owned (and not as Docker-owned) later. | ||
| func TestClientNameOwnershipRoundTrip(t *testing.T) { | ||
| t.Parallel() | ||
|
|
||
| podman := &Client{runtimeType: rt.TypePodman} | ||
| docker := &Client{runtimeType: rt.TypeDocker} | ||
|
|
||
| // Identity recorded on the RunConfig when the workload is created. | ||
| stampedAtCreate := podman.Name() | ||
|
|
||
| // Same Podman runtime later reconciling: must recognize ownership. | ||
| assert.Equal(t, stampedAtCreate, podman.Name(), | ||
| "podman must recognize its own workloads across calls") | ||
|
|
||
| // A different runtime (Docker) reconciling the same workload: must NOT | ||
| // claim ownership, otherwise it would corrupt the Podman workload's status. | ||
| assert.NotEqual(t, stampedAtCreate, docker.Name(), | ||
| "docker must not claim ownership of a podman-created workload") | ||
| } |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note: existing workloads are never auto-migrated.
RuntimeNameis stamped only at creation time (SaveState). Existing workloads on disk haveRuntimeName = ""and will never be updated unless the workload is deleted and recreated. This is fine operationally, but means the cross-runtime protection only applies to workloads created after this PR is deployed — which ties back to the correctness concern inisOwnedByActiveRuntime.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Addressed this in the follow-up commit. Legacy workloads now get their
RuntimeNamestamped opportunistically during reconciliation, but only when the runtime confirms the workload is healthy. So we're not blindly claiming ownership... we only stamp when we have strong evidence the workload actually belongs to the active runtime. The empty-string fallback inisOwnedByActiveRuntimestays as a safety net, but it should be a transient state now rather than permanent.