mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-25 04:33:26 +00:00
Merge pull request #91873 from kwiesmueller/fix-crd-update-bug
Fix FieldManager Conversion Error for CRD Updates
This commit is contained in:
commit
7a68eac8f7
@ -473,6 +473,16 @@ func (e *Store) Update(ctx context.Context, name string, objInfo rest.UpdatedObj
|
|||||||
// deleteObj is only used in case a deletion is carried out
|
// deleteObj is only used in case a deletion is carried out
|
||||||
var deleteObj runtime.Object
|
var deleteObj runtime.Object
|
||||||
err = e.Storage.GuaranteedUpdate(ctx, key, out, true, storagePreconditions, func(existing runtime.Object, res storage.ResponseMeta) (runtime.Object, *uint64, error) {
|
err = e.Storage.GuaranteedUpdate(ctx, key, out, true, storagePreconditions, func(existing runtime.Object, res storage.ResponseMeta) (runtime.Object, *uint64, error) {
|
||||||
|
existingResourceVersion, err := e.Storage.Versioner().ObjectResourceVersion(existing)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
if existingResourceVersion == 0 {
|
||||||
|
if !e.UpdateStrategy.AllowCreateOnUpdate() && !forceAllowCreate {
|
||||||
|
return nil, nil, apierrors.NewNotFound(qualifiedResource, name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Given the existing object, get the new object
|
// Given the existing object, get the new object
|
||||||
obj, err := objInfo.UpdatedObject(ctx, existing)
|
obj, err := objInfo.UpdatedObject(ctx, existing)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -483,20 +493,13 @@ func (e *Store) Update(ctx context.Context, name string, objInfo rest.UpdatedObj
|
|||||||
// the user does not have a resource version, then we populate it with
|
// the user does not have a resource version, then we populate it with
|
||||||
// the latest version. Else, we check that the version specified by
|
// the latest version. Else, we check that the version specified by
|
||||||
// the user matches the version of latest storage object.
|
// the user matches the version of latest storage object.
|
||||||
resourceVersion, err := e.Storage.Versioner().ObjectResourceVersion(obj)
|
newResourceVersion, err := e.Storage.Versioner().ObjectResourceVersion(obj)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
doUnconditionalUpdate := resourceVersion == 0 && e.UpdateStrategy.AllowUnconditionalUpdate()
|
doUnconditionalUpdate := newResourceVersion == 0 && e.UpdateStrategy.AllowUnconditionalUpdate()
|
||||||
|
|
||||||
version, err := e.Storage.Versioner().ObjectResourceVersion(existing)
|
if existingResourceVersion == 0 {
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
if version == 0 {
|
|
||||||
if !e.UpdateStrategy.AllowCreateOnUpdate() && !forceAllowCreate {
|
|
||||||
return nil, nil, apierrors.NewNotFound(qualifiedResource, name)
|
|
||||||
}
|
|
||||||
creating = true
|
creating = true
|
||||||
creatingObj = obj
|
creatingObj = obj
|
||||||
if err := rest.BeforeCreate(e.CreateStrategy, ctx, obj); err != nil {
|
if err := rest.BeforeCreate(e.CreateStrategy, ctx, obj); err != nil {
|
||||||
@ -529,15 +532,15 @@ func (e *Store) Update(ctx context.Context, name string, objInfo rest.UpdatedObj
|
|||||||
} else {
|
} else {
|
||||||
// Check if the object's resource version matches the latest
|
// Check if the object's resource version matches the latest
|
||||||
// resource version.
|
// resource version.
|
||||||
if resourceVersion == 0 {
|
if newResourceVersion == 0 {
|
||||||
// TODO: The Invalid error should have a field for Resource.
|
// TODO: The Invalid error should have a field for Resource.
|
||||||
// After that field is added, we should fill the Resource and
|
// After that field is added, we should fill the Resource and
|
||||||
// leave the Kind field empty. See the discussion in #18526.
|
// leave the Kind field empty. See the discussion in #18526.
|
||||||
qualifiedKind := schema.GroupKind{Group: qualifiedResource.Group, Kind: qualifiedResource.Resource}
|
qualifiedKind := schema.GroupKind{Group: qualifiedResource.Group, Kind: qualifiedResource.Resource}
|
||||||
fieldErrList := field.ErrorList{field.Invalid(field.NewPath("metadata").Child("resourceVersion"), resourceVersion, "must be specified for an update")}
|
fieldErrList := field.ErrorList{field.Invalid(field.NewPath("metadata").Child("resourceVersion"), newResourceVersion, "must be specified for an update")}
|
||||||
return nil, nil, apierrors.NewInvalid(qualifiedKind, name, fieldErrList)
|
return nil, nil, apierrors.NewInvalid(qualifiedKind, name, fieldErrList)
|
||||||
}
|
}
|
||||||
if resourceVersion != version {
|
if newResourceVersion != existingResourceVersion {
|
||||||
return nil, nil, apierrors.NewConflict(qualifiedResource, name, fmt.Errorf(OptimisticLockErrorMsg))
|
return nil, nil, apierrors.NewConflict(qualifiedResource, name, fmt.Errorf(OptimisticLockErrorMsg))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -369,6 +369,39 @@ spec:
|
|||||||
t.Fatalf("failed to add a new list item to the object as a different applier: %v:\n%v", err, string(result))
|
t.Fatalf("failed to add a new list item to the object as a different applier: %v:\n%v", err, string(result))
|
||||||
}
|
}
|
||||||
verifyNumPorts(t, result, 2)
|
verifyNumPorts(t, result, 2)
|
||||||
|
|
||||||
|
// UpdateOnCreate
|
||||||
|
notExistingYAMLBody := []byte(fmt.Sprintf(`
|
||||||
|
{
|
||||||
|
"apiVersion": "%s",
|
||||||
|
"kind": "%s",
|
||||||
|
"metadata": {
|
||||||
|
"name": "%s",
|
||||||
|
"finalizers": [
|
||||||
|
"test-finalizer"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"spec": {
|
||||||
|
"cronSpec": "* * * * */5",
|
||||||
|
"replicas": 1,
|
||||||
|
"ports": [
|
||||||
|
{
|
||||||
|
"name": "x",
|
||||||
|
"containerPort": 80
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"protocol": "TCP"
|
||||||
|
}`, apiVersion, kind, "should-not-exist"))
|
||||||
|
_, err = rest.Put().
|
||||||
|
AbsPath("/apis", noxuDefinition.Spec.Group, noxuDefinition.Spec.Version, noxuDefinition.Spec.Names.Plural).
|
||||||
|
Name("should-not-exist").
|
||||||
|
Param("fieldManager", "apply_test").
|
||||||
|
Body(notExistingYAMLBody).
|
||||||
|
DoRaw(context.TODO())
|
||||||
|
if !apierrors.IsNotFound(err) {
|
||||||
|
t.Fatalf("create on update should fail with notFound, got %v", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestApplyCRDNonStructuralSchema tests that when a CRD has a non-structural schema in its validation field,
|
// TestApplyCRDNonStructuralSchema tests that when a CRD has a non-structural schema in its validation field,
|
||||||
|
@ -480,7 +480,7 @@ func TestNodeAuthorizer(t *testing.T) {
|
|||||||
expectNotFound(t, deleteNode2MirrorPod(node1Client))
|
expectNotFound(t, deleteNode2MirrorPod(node1Client))
|
||||||
expectNotFound(t, createNode2MirrorPodEviction(node1Client))
|
expectNotFound(t, createNode2MirrorPodEviction(node1Client))
|
||||||
expectForbidden(t, createNode2(node1Client))
|
expectForbidden(t, createNode2(node1Client))
|
||||||
expectForbidden(t, updateNode2Status(node1Client))
|
expectNotFound(t, updateNode2Status(node1Client))
|
||||||
expectForbidden(t, deleteNode2(node1Client))
|
expectForbidden(t, deleteNode2(node1Client))
|
||||||
|
|
||||||
// related object requests from node2 fail
|
// related object requests from node2 fail
|
||||||
@ -500,6 +500,8 @@ func TestNodeAuthorizer(t *testing.T) {
|
|||||||
expectAllowed(t, updateNode2Status(node2Client))
|
expectAllowed(t, updateNode2Status(node2Client))
|
||||||
// self deletion is not allowed
|
// self deletion is not allowed
|
||||||
expectForbidden(t, deleteNode2(node2Client))
|
expectForbidden(t, deleteNode2(node2Client))
|
||||||
|
// modification of another node's status is not allowed
|
||||||
|
expectForbidden(t, updateNode2Status(node1Client))
|
||||||
// clean up node2
|
// clean up node2
|
||||||
expectAllowed(t, deleteNode2(superuserClient))
|
expectAllowed(t, deleteNode2(superuserClient))
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user