Merge pull request #132049 from googs1025/fix/data_race_patch
fix: data race for patchResource func Kubernetes-commit: 0a51ae22542b6e78fec51a573d95a922592f8273
This commit is contained in:
commit
3637a3a5aa
2
go.mod
2
go.mod
|
|
@ -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
4
go.sum
|
|
@ -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=
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
Loading…
Reference in New Issue