Change:
Running kubectl create with dry-run returns unwanted uid metadata field, as well as a generated name when using generateName. This is inconsistent with the expectations as defined in which states I expected uid, and a generated name to be empty when using the dry-run create option. Expected metadata: Actual metadata: No response <detailsI've tested this from 1.19.0 through 1.23.0 </details<details</details<details</details<details</details<details</details<details</details
/sig api-machinery
/assign /triage accepted
reopening, is reverted in to make sure requests with both name+generateName are handled correctly
The Kubernetes project currently lacks enough contributors to adequately respond to all issues and PRs. This bot triages issues and PRs according to the following rules: After 90d of inactivity, is applied After 30d of inactivity since was applied, is applied After 30d of inactivity since was applied, the issue is closed You can: Mark this issue or PR as fresh with Mark this issue or PR as rotten with Close this issue or PR with Offer to help out with [Issue Triage][1] Please send feedback to sig-contributor-experience at . /lifecycle stale [1]:
The Kubernetes project currently lacks enough active contributors to adequately respond to all issues and PRs. This bot triages issues and PRs according to the following rules: After 90d of inactivity, is applied After 30d of inactivity since was applied, is applied After 30d of inactivity since was applied, the issue is closed You can: Mark this issue or PR as fresh with Close this issue or PR with Offer to help out with [Issue Triage][1] Please send feedback to sig-contributor-experience at . /lifecycle rotten [1]:
The Kubernetes project currently lacks enough active contributors to adequately respond to all issues and PRs. This bot triages issues and PRs according to the following rules: After 90d of inactivity, is applied After 30d of inactivity since was applied, is applied After 30d of inactivity since was applied, the issue is closed You can: Reopen this issue or PR with Mark this issue or PR as fresh with Offer to help out with [Issue Triage][1] Please send feedback to sig-contributor-experience at . /close [1]:
Closing this issue. <detailsIn response to : The Kubernetes project currently lacks enough active contributors to adequately respond to all issues and PRs. After 90d of inactivity, is applied After 30d of inactivity since was applied, is applied After 30d of inactivity since was applied, the issue is closedReopen this issue or PR with Mark this issue or PR as fresh with Offer to help out with [Issue Triage][1] Instructions for interacting with me using PR comments are available . If you have questions or suggestions related to my behavior, please file an issue against the repository. </details

Discussion:
if e.Decorator != nil { e.Decorator(out) } <ins> if dryrun.IsDryRun(options.DryRun) { if err := dryrun.ResetMetadata(obj, out); err != nil { return nil, err } } </ins> return out, nil }

package dryrun <ins> import ( "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/api/meta" "k8s.io/apimachinery/pkg/runtime" ) </ins> // IsDryRun returns true if the DryRun flag is an actual dry-run. func IsDryRun(flag []string) bool { return len(flag) > 0 } <ins> // ResetMetadata resets metadata fields that are not allowed to be set by dry-run. func ResetMetadata(originalObj, newObj runtime.Object) error { originalObjMeta, err := meta.Accessor(originalObj) if err != nil { return errors.NewInternalError(err) } newObjMeta, err := meta.Accessor(newObj) if err != nil { return errors.NewInternalError(err) } // If a resource is created with dry-run enabled where generateName is set, the // store will set the name to the generated name. We need to reset the name and restore // the generateName metadata fields in order for the returned object to match the intent // of the original template. if originalObjMeta.GetGenerateName() != "" { newObjMeta.SetName("") } newObjMeta.SetGenerateName(originalObjMeta.GetGenerateName()) // If UID is set in the dry-run output then that output cannot be used to create a resource. Reset // the UID to allow the output to be used to create resources. newObjMeta.SetUID("") // If the resourceVersion is set in the dry-run output then that output cannot be used to create // a resource. Reset the resourceVersion to allow the output to be used to create resources. newObjMeta.SetResourceVersion("") return nil } </ins>

import ( "context" <ins> "strings" </ins> "testing" v1 "k8s.io/api/core/v1"

// namespace used for all tests, do not change this const testNamespace = "dryrunnamespace" <ins> func DryRunCreateWithGenerateNameTest(t *testing.T, rsc dynamic.ResourceInterface, obj *unstructured.Unstructured, gvResource schema.GroupVersionResource) { // special resources with dots in the name cannot use generateName if strings.Contains(obj.GetName(), ".") { return } // Create a new object with generateName to ensure we don't taint the original object gnObj := obj.DeepCopy() gnObj.SetGenerateName(obj.GetName() + "-") gnObj.SetName("") DryRunCreateTest(t, rsc, gnObj, gvResource) } </ins> func DryRunCreateTest(t *testing.T, rsc dynamic.ResourceInterface, obj *unstructured.Unstructured, gvResource schema.GroupVersionResource) { createdObj, err := rsc.Create(context.TODO(), obj, metav1.CreateOptions{DryRun: []string{metav1.DryRunAll}}) if err != nil { <del> t.Fatalf("failed to dry-run create stub for %s: %#v", gvResource, err) </del> <ins> t.Fatalf("failed to dry-run create stub for %s: %#v: %v", gvResource, err, obj) </ins> } if obj.GroupVersionKind() != createdObj.GroupVersionKind() { t.Fatalf("created object doesn't have the same gvk as original object: got %v, expected %v", createdObj.GroupVersionKind(), obj.GroupVersionKind()) } <ins> if createdObj.GetUID() != "" { t.Fatalf("created object shouldn't have a uid: %v", createdObj) } if createdObj.GetResourceVersion() != "" { t.Fatalf("created object shouldn't have a resource version: %v", createdObj) } if obj.GetGenerateName() != "" && createdObj.GetName() != "" { t.Fatalf("created object's name should be an empty string if using GenerateName: %v", createdObj) } </ins> <del> if _, err := rsc.Get(context.TODO(), obj.GetName(), metav1.GetOptions{}); !apierrors.IsNotFound(err) { t.Fatalf("object shouldn't exist: %v", err) </del> <ins> // we won't have a generated name here, so we won't check for this case if obj.GetGenerateName() == "" { if _, err := rsc.Get(context.TODO(), obj.GetName(), metav1.GetOptions{}); !apierrors.IsNotFound(err) { t.Fatalf("object shouldn't exist: %v, %v", obj, err) } </ins> } }

name := obj.GetName() DryRunCreateTest(t, rsc, obj, gvResource) <ins> DryRunCreateWithGenerateNameTest(t, rsc, obj, gvResource) </ins> if _, err := rsc.Create(context.TODO(), obj, metav1.CreateOptions{}); err != nil { t.Fatalf("failed to create stub for %s: %#v", gvResource, err)