Merge pull request #132049 from googs1025/fix/data_race_patch

fix: data race for patchResource func

Kubernetes-commit: 0a51ae22542b6e78fec51a573d95a922592f8273
This commit is contained in:
Kubernetes Publisher 2025-07-02 11:27:24 -07:00
commit 3637a3a5aa
4 changed files with 58 additions and 4 deletions

2
go.mod
View File

@ -124,3 +124,5 @@ require (
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
replace k8s.io/client-go => k8s.io/client-go v0.0.0-20250702155524-6980728f27f4

4
go.sum
View File

@ -301,8 +301,8 @@ k8s.io/api v0.0.0-20250702170451-bfa25161ab14 h1:ru1qVDjtx6BP62PQjJ+Wl9raUn0a8nj
k8s.io/api v0.0.0-20250702170451-bfa25161ab14/go.mod h1:K0G717YoOyRoxuW09o2hgVeUK6NK3vcAzQBtcQRnL9o=
k8s.io/apimachinery v0.0.0-20250702170156-7548d4da2f38 h1:8O5idX0pTbbeSayup4l3BUsPhHYxITx/6XFc149kAJw=
k8s.io/apimachinery v0.0.0-20250702170156-7548d4da2f38/go.mod h1:RkaUIZxsmnceX2hEfn44FGiTXiv/8cUWaqjCh1kDzZg=
k8s.io/client-go v0.0.0-20250702170847-0746d1b7322b h1:XzzMTQNm3JMz6dh3c5oJbRfPO5Np0+SQcI8Q+ADq7Sg=
k8s.io/client-go v0.0.0-20250702170847-0746d1b7322b/go.mod h1:jmp8BGXr3fP6+ho7p/Bf+Of4YND9LrF1B1a+zlgpS0k=
k8s.io/client-go v0.0.0-20250702155524-6980728f27f4 h1:YvA2fLi5nrfeeQdq9A0Cp8bTUqXU8ltCTjKNHgNTwDQ=
k8s.io/client-go v0.0.0-20250702155524-6980728f27f4/go.mod h1:jmp8BGXr3fP6+ho7p/Bf+Of4YND9LrF1B1a+zlgpS0k=
k8s.io/component-base v0.0.0-20250702172159-a7fbca963794 h1:cLqIWi8ntbwsZPPskQ7OpluDkZFQkFdeKrkiv4HSuwk=
k8s.io/component-base v0.0.0-20250702172159-a7fbca963794/go.mod h1:pQj4ARJ2y3YY/TinuQWxpU0A7bWNZxKbMusfiSHV3mA=
k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk=

View File

@ -726,6 +726,14 @@ func (p *patcher) patchResource(ctx context.Context, scope *RequestScope) (runti
}
return result, err
})
// In case of a timeout error, the goroutine handling the request is still running.
// https://github.com/kubernetes/kubernetes/blob/d2c12afa4593e50a187075157d38748292b02733/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/finisher/finisher.go#L127-L146
// We cannot reliably read the variable (data race!) and have to assume that
// the object was not created.
if errors.IsTimeout(err) {
return result, false, err
}
return result, wasCreated, err
}

View File

@ -323,6 +323,25 @@ func TestPatchCustomResource(t *testing.T) {
}
}
type testTimeoutPatcher struct {
t *testing.T
}
func (p *testTimeoutPatcher) New() runtime.Object {
return &example.Pod{}
}
func (p *testTimeoutPatcher) Update(ctx context.Context, _ string, _ rest.UpdatedObjectInfo, _ rest.ValidateObjectFunc, _ rest.ValidateObjectUpdateFunc, _ bool, _ *metav1.UpdateOptions) (runtime.Object, bool, error) {
// Block until the context is canceled to simulate a timeout scenario.
<-ctx.Done()
return nil, false, ctx.Err()
}
func (p *testTimeoutPatcher) Get(ctx context.Context, name string, options *metav1.GetOptions) (runtime.Object, error) {
p.t.Fatal("unexpected call to testPatcher.Get")
return nil, errors.New("unexpected call to testPatcher.Get")
}
type testPatcher struct {
t *testing.T
@ -432,6 +451,9 @@ type patchTestCase struct {
expectedError string
// if set, indicates the number of times patching was expected to be attempted
expectedTries int
// isTimeout for this test case
isTimeout bool
}
func (tc *patchTestCase) Run(t *testing.T) {
@ -564,8 +586,16 @@ func (tc *patchTestCase) Run(t *testing.T) {
FieldManager: "test-manager",
},
}
ctx, cancel := context.WithTimeout(ctx, time.Second)
var timeout time.Duration
if tc.isTimeout {
// Simulate timeout by using a zero-duration timeout and the timeout patcher
// replace testPatcher with the timeout simulator
timeout = 0
p.restPatcher = &testTimeoutPatcher{t: t}
} else {
timeout = time.Second
}
ctx, cancel := context.WithTimeout(ctx, timeout)
resultObj, _, err := p.patchResource(ctx, &RequestScope{
FieldManager: fieldmanager,
})
@ -679,6 +709,20 @@ func TestPatchResourceNumberConversion(t *testing.T) {
tc.Run(t)
}
func TestPatchResourceTimeout(t *testing.T) {
tc := &patchTestCase{
name: "TestPatchResourceTimeout",
startingPod: &example.Pod{},
changedPod: &example.Pod{},
updatePod: &example.Pod{},
expectedPod: nil,
isTimeout: true,
expectedError: "Timeout: request did not complete within requested timeout - context deadline exceeded",
}
tc.Run(t)
}
func TestPatchResourceWithVersionConflict(t *testing.T) {
namespace := "bar"
name := "foo"