in pkg/cmd/util/editor/editoptions.go [609:716]
func (o *EditOptions) visitToPatch(originalInfos []*resource.Info, patchVisitor resource.Visitor, results *editResults) error {
err := patchVisitor.Visit(func(info *resource.Info, incomingErr error) error {
editObjUID, err := meta.NewAccessor().UID(info.Object)
if err != nil {
return err
}
var originalInfo *resource.Info
for _, i := range originalInfos {
originalObjUID, err := meta.NewAccessor().UID(i.Object)
if err != nil {
return err
}
if editObjUID == originalObjUID {
originalInfo = i
break
}
}
if originalInfo == nil {
return fmt.Errorf("no original object found for %#v", info.Object)
}
originalJS, err := encodeToJSON(originalInfo.Object.(runtime.Unstructured))
if err != nil {
return err
}
editedJS, err := encodeToJSON(info.Object.(runtime.Unstructured))
if err != nil {
return err
}
if reflect.DeepEqual(originalJS, editedJS) {
// no edit, so just skip it.
printer, err := o.ToPrinter("skipped")
if err != nil {
return err
}
return printer.PrintObj(info.Object, o.Out)
}
preconditions := []mergepatch.PreconditionFunc{
mergepatch.RequireKeyUnchanged("apiVersion"),
mergepatch.RequireKeyUnchanged("kind"),
mergepatch.RequireMetadataKeyUnchanged("name"),
mergepatch.RequireKeyUnchanged("managedFields"),
}
// Create the versioned struct from the type defined in the mapping
// (which is the API version we'll be submitting the patch to)
versionedObject, err := scheme.Scheme.New(info.Mapping.GroupVersionKind)
var patchType types.PatchType
var patch []byte
switch {
case runtime.IsNotRegisteredError(err):
// fall back to generic JSON merge patch
patchType = types.MergePatchType
patch, err = jsonpatch.CreateMergePatch(originalJS, editedJS)
if err != nil {
klog.V(4).Infof("Unable to calculate diff, no merge is possible: %v", err)
return err
}
var patchMap map[string]interface{}
err = json.Unmarshal(patch, &patchMap)
if err != nil {
klog.V(4).Infof("Unable to calculate diff, no merge is possible: %v", err)
return err
}
for _, precondition := range preconditions {
if !precondition(patchMap) {
klog.V(4).Infof("Unable to calculate diff, no merge is possible: %v", err)
return fmt.Errorf("%s", "At least one of apiVersion, kind and name was changed")
}
}
case err != nil:
return err
default:
patchType = types.StrategicMergePatchType
patch, err = strategicpatch.CreateTwoWayMergePatch(originalJS, editedJS, versionedObject, preconditions...)
if err != nil {
klog.V(4).Infof("Unable to calculate diff, no merge is possible: %v", err)
if mergepatch.IsPreconditionFailed(err) {
return fmt.Errorf("%s", "At least one of apiVersion, kind and name was changed")
}
return err
}
}
if o.OutputPatch {
fmt.Fprintf(o.Out, "Patch: %s\n", string(patch))
}
patched, err := resource.NewHelper(info.Client, info.Mapping).
WithFieldManager(o.FieldManager).
Patch(info.Namespace, info.Name, patchType, patch, nil)
if err != nil {
fmt.Fprintln(o.ErrOut, results.addError(err, info))
return nil
}
info.Refresh(patched, true)
printer, err := o.ToPrinter("edited")
if err != nil {
return err
}
return printer.PrintObj(info.Object, o.Out)
})
return err
}