// ResourceSingularizer implements RESTMapper // It converts a resource name from plural to singular (e.g., from pods to pod) func (m *DefaultRESTMapper) ResourceSingularizer(resourceType string) (string, error) { partialResource := schema.GroupVersionResource{Resource: resourceType} resources, err := m.ResourcesFor(partialResource) if err != nil { return resourceType, err } singular := schema.GroupVersionResource{} for _, curr := range resources { currSingular, ok := m.pluralToSingular[curr] if !ok { continue } if singular.Empty() { singular = currSingular continue } if currSingular.Resource != singular.Resource { return resourceType, fmt.Errorf("multiple possible singular resources (%v) found for %v", resources, resourceType) } } if singular.Empty() { return resourceType, fmt.Errorf("no singular of resource %v has been defined", resourceType) } return singular.Resource, nil }
// expandResourceShortcut will return the expanded version of resource // (something that a pkg/api/meta.RESTMapper can understand), if it is // indeed a shortcut. If no match has been found, we will match on group prefixing. // Lastly we will return resource unmodified. func (e ShortcutExpander) expandResourceShortcut(resource schema.GroupVersionResource) schema.GroupVersionResource { // get the shortcut mappings and return on first match. if resources, err := e.getShortcutMappings(); err == nil { for _, item := range resources { if len(resource.Group) != 0 && resource.Group != item.ShortForm.Group { continue } if resource.Resource == item.ShortForm.Resource { resource.Resource = item.LongForm.Resource return resource } } // we didn't find exact match so match on group prefixing. This allows autoscal to match autoscaling if len(resource.Group) == 0 { return resource } for _, item := range resources { if !strings.HasPrefix(item.ShortForm.Group, resource.Group) { continue } if resource.Resource == item.ShortForm.Resource { resource.Resource = item.LongForm.Resource return resource } } } return resource }
// coerceResourceForMatching makes the resource lower case and converts internal versions to unspecified (legacy behavior) func coerceResourceForMatching(resource schema.GroupVersionResource) schema.GroupVersionResource { resource.Resource = strings.ToLower(resource.Resource) if resource.Version == runtime.APIVersionInternal { resource.Version = "" } return resource }
func (gc *GarbageCollector) monitorFor(resource schema.GroupVersionResource, kind schema.GroupVersionKind) (monitor, error) { // TODO: consider store in one storage. glog.V(6).Infof("create storage for resource %s", resource) var monitor monitor client, err := gc.metaOnlyClientPool.ClientForGroupVersionKind(kind) if err != nil { return monitor, err } gc.registeredRateLimiterForMonitors.registerIfNotPresent(resource.GroupVersion(), client, "garbage_collector_monitoring") setObjectTypeMeta := func(obj interface{}) { runtimeObject, ok := obj.(runtime.Object) if !ok { utilruntime.HandleError(fmt.Errorf("expected runtime.Object, got %#v", obj)) } runtimeObject.GetObjectKind().SetGroupVersionKind(kind) } monitor.store, monitor.controller = cache.NewInformer( gcListWatcher(client, resource), nil, ResourceResyncTime, cache.ResourceEventHandlerFuncs{ // add the event to the propagator's eventQueue. AddFunc: func(obj interface{}) { setObjectTypeMeta(obj) event := &event{ eventType: addEvent, obj: obj, } gc.propagator.eventQueue.Add(&workqueue.TimedWorkQueueItem{StartTime: gc.clock.Now(), Object: event}) }, UpdateFunc: func(oldObj, newObj interface{}) { setObjectTypeMeta(newObj) event := &event{updateEvent, newObj, oldObj} gc.propagator.eventQueue.Add(&workqueue.TimedWorkQueueItem{StartTime: gc.clock.Now(), Object: event}) }, DeleteFunc: func(obj interface{}) { // delta fifo may wrap the object in a cache.DeletedFinalStateUnknown, unwrap it if deletedFinalStateUnknown, ok := obj.(cache.DeletedFinalStateUnknown); ok { obj = deletedFinalStateUnknown.Obj } setObjectTypeMeta(obj) event := &event{ eventType: deleteEvent, obj: obj, } gc.propagator.eventQueue.Add(&workqueue.TimedWorkQueueItem{StartTime: gc.clock.Now(), Object: event}) }, }, ) return monitor, nil }
// ForResource gives generic access to a shared informer of the matching type // TODO extend this to unknown resources with a client pool func (f *sharedInformerFactory) ForResource(resource schema.GroupVersionResource) (GenericInformer, error) { switch resource { // Group=Apiregistration, Version=InternalVersion case apiregistration.SchemeGroupVersion.WithResource("apiservices"): return &genericInformer{resource: resource.GroupResource(), informer: f.Apiregistration().InternalVersion().APIServices().Informer()}, nil // Group=Apiregistration, Version=V1alpha1 case v1alpha1.SchemeGroupVersion.WithResource("apiservices"): return &genericInformer{resource: resource.GroupResource(), informer: f.Apiregistration().V1alpha1().APIServices().Informer()}, nil } return nil, fmt.Errorf("no informer found for %v", resource) }
func (o *ResourceConfig) ResourceEnabled(resource schema.GroupVersionResource) bool { versionOverride, versionExists := o.GroupVersionResourceConfigs[resource.GroupVersion()] if !versionExists { return false } if !versionOverride.Enable { return false } if versionOverride.DisabledResources.Has(resource.Resource) { return false } if len(versionOverride.EnabledResources) > 0 { return versionOverride.EnabledResources.Has(resource.Resource) } return true }
// estimateGrracefulTermination will estimate the graceful termination required for the specific entity in the namespace func estimateGracefulTermination(kubeClient clientset.Interface, groupVersionResource schema.GroupVersionResource, ns string, namespaceDeletedAt metav1.Time) (int64, error) { groupResource := groupVersionResource.GroupResource() glog.V(5).Infof("namespace controller - estimateGracefulTermination - group %s, resource: %s", groupResource.Group, groupResource.Resource) estimate := int64(0) var err error switch groupResource { case schema.GroupResource{Group: "", Resource: "pods"}: estimate, err = estimateGracefulTerminationForPods(kubeClient, ns) } if err != nil { return estimate, err } // determine if the estimate is greater than the deletion timestamp duration := time.Since(namespaceDeletedAt.Time) allowedEstimate := time.Duration(estimate) * time.Second if duration >= allowedEstimate { estimate = int64(0) } return estimate, nil }
// ForResource gives generic access to a shared informer of the matching type // TODO extend this to unknown resources with a client pool func (f *sharedInformerFactory) ForResource(resource schema.GroupVersionResource) (GenericInformer, error) { switch resource { // Group=Apps, Version=InternalVersion case apps.SchemeGroupVersion.WithResource("statefulsets"): return &genericInformer{resource: resource.GroupResource(), informer: f.Apps().InternalVersion().StatefulSets().Informer()}, nil // Group=Apps, Version=V1beta1 case v1beta1.SchemeGroupVersion.WithResource("statefulsets"): return &genericInformer{resource: resource.GroupResource(), informer: f.Apps().V1beta1().StatefulSets().Informer()}, nil // Group=Autoscaling, Version=InternalVersion case autoscaling.SchemeGroupVersion.WithResource("horizontalpodautoscalers"): return &genericInformer{resource: resource.GroupResource(), informer: f.Autoscaling().InternalVersion().HorizontalPodAutoscalers().Informer()}, nil // Group=Autoscaling, Version=V1 case v1.SchemeGroupVersion.WithResource("horizontalpodautoscalers"): return &genericInformer{resource: resource.GroupResource(), informer: f.Autoscaling().V1().HorizontalPodAutoscalers().Informer()}, nil // Group=Batch, Version=InternalVersion case batch.SchemeGroupVersion.WithResource("cronjobs"): return &genericInformer{resource: resource.GroupResource(), informer: f.Batch().InternalVersion().CronJobs().Informer()}, nil case batch.SchemeGroupVersion.WithResource("jobs"): return &genericInformer{resource: resource.GroupResource(), informer: f.Batch().InternalVersion().Jobs().Informer()}, nil // Group=Batch, Version=V1 case batch_v1.SchemeGroupVersion.WithResource("jobs"): return &genericInformer{resource: resource.GroupResource(), informer: f.Batch().V1().Jobs().Informer()}, nil // Group=Batch, Version=V2alpha1 case v2alpha1.SchemeGroupVersion.WithResource("cronjobs"): return &genericInformer{resource: resource.GroupResource(), informer: f.Batch().V2alpha1().CronJobs().Informer()}, nil case v2alpha1.SchemeGroupVersion.WithResource("jobs"): return &genericInformer{resource: resource.GroupResource(), informer: f.Batch().V2alpha1().Jobs().Informer()}, nil // Group=Certificates, Version=InternalVersion case certificates.SchemeGroupVersion.WithResource("certificatesigningrequests"): return &genericInformer{resource: resource.GroupResource(), informer: f.Certificates().InternalVersion().CertificateSigningRequests().Informer()}, nil // Group=Certificates, Version=V1beta1 case certificates_v1beta1.SchemeGroupVersion.WithResource("certificatesigningrequests"): return &genericInformer{resource: resource.GroupResource(), informer: f.Certificates().V1beta1().CertificateSigningRequests().Informer()}, nil // Group=Core, Version=InternalVersion case api.SchemeGroupVersion.WithResource("componentstatuses"): return &genericInformer{resource: resource.GroupResource(), informer: f.Core().InternalVersion().ComponentStatuses().Informer()}, nil case api.SchemeGroupVersion.WithResource("configmaps"): return &genericInformer{resource: resource.GroupResource(), informer: f.Core().InternalVersion().ConfigMaps().Informer()}, nil case api.SchemeGroupVersion.WithResource("endpoints"): return &genericInformer{resource: resource.GroupResource(), informer: f.Core().InternalVersion().Endpoints().Informer()}, nil case api.SchemeGroupVersion.WithResource("events"): return &genericInformer{resource: resource.GroupResource(), informer: f.Core().InternalVersion().Events().Informer()}, nil case api.SchemeGroupVersion.WithResource("limitranges"): return &genericInformer{resource: resource.GroupResource(), informer: f.Core().InternalVersion().LimitRanges().Informer()}, nil case api.SchemeGroupVersion.WithResource("namespaces"): return &genericInformer{resource: resource.GroupResource(), informer: f.Core().InternalVersion().Namespaces().Informer()}, nil case api.SchemeGroupVersion.WithResource("nodes"): return &genericInformer{resource: resource.GroupResource(), informer: f.Core().InternalVersion().Nodes().Informer()}, nil case api.SchemeGroupVersion.WithResource("persistentvolumes"): return &genericInformer{resource: resource.GroupResource(), informer: f.Core().InternalVersion().PersistentVolumes().Informer()}, nil case api.SchemeGroupVersion.WithResource("persistentvolumeclaims"): return &genericInformer{resource: resource.GroupResource(), informer: f.Core().InternalVersion().PersistentVolumeClaims().Informer()}, nil case api.SchemeGroupVersion.WithResource("pods"): return &genericInformer{resource: resource.GroupResource(), informer: f.Core().InternalVersion().Pods().Informer()}, nil case api.SchemeGroupVersion.WithResource("podtemplates"): return &genericInformer{resource: resource.GroupResource(), informer: f.Core().InternalVersion().PodTemplates().Informer()}, nil case api.SchemeGroupVersion.WithResource("replicationcontrollers"): return &genericInformer{resource: resource.GroupResource(), informer: f.Core().InternalVersion().ReplicationControllers().Informer()}, nil case api.SchemeGroupVersion.WithResource("resourcequotas"): return &genericInformer{resource: resource.GroupResource(), informer: f.Core().InternalVersion().ResourceQuotas().Informer()}, nil case api.SchemeGroupVersion.WithResource("secrets"): return &genericInformer{resource: resource.GroupResource(), informer: f.Core().InternalVersion().Secrets().Informer()}, nil case api.SchemeGroupVersion.WithResource("services"): return &genericInformer{resource: resource.GroupResource(), informer: f.Core().InternalVersion().Services().Informer()}, nil case api.SchemeGroupVersion.WithResource("serviceaccounts"): return &genericInformer{resource: resource.GroupResource(), informer: f.Core().InternalVersion().ServiceAccounts().Informer()}, nil // Group=Core, Version=V1 case api_v1.SchemeGroupVersion.WithResource("componentstatuses"): return &genericInformer{resource: resource.GroupResource(), informer: f.Core().V1().ComponentStatuses().Informer()}, nil case api_v1.SchemeGroupVersion.WithResource("configmaps"): return &genericInformer{resource: resource.GroupResource(), informer: f.Core().V1().ConfigMaps().Informer()}, nil case api_v1.SchemeGroupVersion.WithResource("endpoints"): return &genericInformer{resource: resource.GroupResource(), informer: f.Core().V1().Endpoints().Informer()}, nil case api_v1.SchemeGroupVersion.WithResource("events"): return &genericInformer{resource: resource.GroupResource(), informer: f.Core().V1().Events().Informer()}, nil case api_v1.SchemeGroupVersion.WithResource("limitranges"): return &genericInformer{resource: resource.GroupResource(), informer: f.Core().V1().LimitRanges().Informer()}, nil case api_v1.SchemeGroupVersion.WithResource("namespaces"): return &genericInformer{resource: resource.GroupResource(), informer: f.Core().V1().Namespaces().Informer()}, nil case api_v1.SchemeGroupVersion.WithResource("nodes"): return &genericInformer{resource: resource.GroupResource(), informer: f.Core().V1().Nodes().Informer()}, nil case api_v1.SchemeGroupVersion.WithResource("persistentvolumes"): return &genericInformer{resource: resource.GroupResource(), informer: f.Core().V1().PersistentVolumes().Informer()}, nil case api_v1.SchemeGroupVersion.WithResource("persistentvolumeclaims"): return &genericInformer{resource: resource.GroupResource(), informer: f.Core().V1().PersistentVolumeClaims().Informer()}, nil case api_v1.SchemeGroupVersion.WithResource("pods"): return &genericInformer{resource: resource.GroupResource(), informer: f.Core().V1().Pods().Informer()}, nil case api_v1.SchemeGroupVersion.WithResource("podtemplates"): return &genericInformer{resource: resource.GroupResource(), informer: f.Core().V1().PodTemplates().Informer()}, nil case api_v1.SchemeGroupVersion.WithResource("replicationcontrollers"): return &genericInformer{resource: resource.GroupResource(), informer: f.Core().V1().ReplicationControllers().Informer()}, nil case api_v1.SchemeGroupVersion.WithResource("resourcequotas"): return &genericInformer{resource: resource.GroupResource(), informer: f.Core().V1().ResourceQuotas().Informer()}, nil case api_v1.SchemeGroupVersion.WithResource("secrets"): return &genericInformer{resource: resource.GroupResource(), informer: f.Core().V1().Secrets().Informer()}, nil case api_v1.SchemeGroupVersion.WithResource("services"): return &genericInformer{resource: resource.GroupResource(), informer: f.Core().V1().Services().Informer()}, nil case api_v1.SchemeGroupVersion.WithResource("serviceaccounts"): return &genericInformer{resource: resource.GroupResource(), informer: f.Core().V1().ServiceAccounts().Informer()}, nil // Group=Extensions, Version=InternalVersion case extensions.SchemeGroupVersion.WithResource("daemonsets"): return &genericInformer{resource: resource.GroupResource(), informer: f.Extensions().InternalVersion().DaemonSets().Informer()}, nil case extensions.SchemeGroupVersion.WithResource("deployments"): return &genericInformer{resource: resource.GroupResource(), informer: f.Extensions().InternalVersion().Deployments().Informer()}, nil case extensions.SchemeGroupVersion.WithResource("ingresses"): return &genericInformer{resource: resource.GroupResource(), informer: f.Extensions().InternalVersion().Ingresses().Informer()}, nil case extensions.SchemeGroupVersion.WithResource("networkpolicies"): return &genericInformer{resource: resource.GroupResource(), informer: f.Extensions().InternalVersion().NetworkPolicies().Informer()}, nil case extensions.SchemeGroupVersion.WithResource("podsecuritypolicies"): return &genericInformer{resource: resource.GroupResource(), informer: f.Extensions().InternalVersion().PodSecurityPolicies().Informer()}, nil case extensions.SchemeGroupVersion.WithResource("replicasets"): return &genericInformer{resource: resource.GroupResource(), informer: f.Extensions().InternalVersion().ReplicaSets().Informer()}, nil case extensions.SchemeGroupVersion.WithResource("thirdpartyresources"): return &genericInformer{resource: resource.GroupResource(), informer: f.Extensions().InternalVersion().ThirdPartyResources().Informer()}, nil // Group=Extensions, Version=V1beta1 case extensions_v1beta1.SchemeGroupVersion.WithResource("daemonsets"): return &genericInformer{resource: resource.GroupResource(), informer: f.Extensions().V1beta1().DaemonSets().Informer()}, nil case extensions_v1beta1.SchemeGroupVersion.WithResource("deployments"): return &genericInformer{resource: resource.GroupResource(), informer: f.Extensions().V1beta1().Deployments().Informer()}, nil case extensions_v1beta1.SchemeGroupVersion.WithResource("ingresses"): return &genericInformer{resource: resource.GroupResource(), informer: f.Extensions().V1beta1().Ingresses().Informer()}, nil case extensions_v1beta1.SchemeGroupVersion.WithResource("podsecuritypolicies"): return &genericInformer{resource: resource.GroupResource(), informer: f.Extensions().V1beta1().PodSecurityPolicies().Informer()}, nil case extensions_v1beta1.SchemeGroupVersion.WithResource("replicasets"): return &genericInformer{resource: resource.GroupResource(), informer: f.Extensions().V1beta1().ReplicaSets().Informer()}, nil case extensions_v1beta1.SchemeGroupVersion.WithResource("thirdpartyresources"): return &genericInformer{resource: resource.GroupResource(), informer: f.Extensions().V1beta1().ThirdPartyResources().Informer()}, nil // Group=Policy, Version=InternalVersion case policy.SchemeGroupVersion.WithResource("poddisruptionbudgets"): return &genericInformer{resource: resource.GroupResource(), informer: f.Policy().InternalVersion().PodDisruptionBudgets().Informer()}, nil // Group=Policy, Version=V1beta1 case policy_v1beta1.SchemeGroupVersion.WithResource("poddisruptionbudgets"): return &genericInformer{resource: resource.GroupResource(), informer: f.Policy().V1beta1().PodDisruptionBudgets().Informer()}, nil // Group=Rbac, Version=InternalVersion case rbac.SchemeGroupVersion.WithResource("clusterroles"): return &genericInformer{resource: resource.GroupResource(), informer: f.Rbac().InternalVersion().ClusterRoles().Informer()}, nil case rbac.SchemeGroupVersion.WithResource("clusterrolebindings"): return &genericInformer{resource: resource.GroupResource(), informer: f.Rbac().InternalVersion().ClusterRoleBindings().Informer()}, nil case rbac.SchemeGroupVersion.WithResource("roles"): return &genericInformer{resource: resource.GroupResource(), informer: f.Rbac().InternalVersion().Roles().Informer()}, nil case rbac.SchemeGroupVersion.WithResource("rolebindings"): return &genericInformer{resource: resource.GroupResource(), informer: f.Rbac().InternalVersion().RoleBindings().Informer()}, nil // Group=Rbac, Version=V1alpha1 case v1alpha1.SchemeGroupVersion.WithResource("clusterroles"): return &genericInformer{resource: resource.GroupResource(), informer: f.Rbac().V1alpha1().ClusterRoles().Informer()}, nil case v1alpha1.SchemeGroupVersion.WithResource("clusterrolebindings"): return &genericInformer{resource: resource.GroupResource(), informer: f.Rbac().V1alpha1().ClusterRoleBindings().Informer()}, nil case v1alpha1.SchemeGroupVersion.WithResource("roles"): return &genericInformer{resource: resource.GroupResource(), informer: f.Rbac().V1alpha1().Roles().Informer()}, nil case v1alpha1.SchemeGroupVersion.WithResource("rolebindings"): return &genericInformer{resource: resource.GroupResource(), informer: f.Rbac().V1alpha1().RoleBindings().Informer()}, nil // Group=Rbac, Version=V1beta1 case rbac_v1beta1.SchemeGroupVersion.WithResource("clusterroles"): return &genericInformer{resource: resource.GroupResource(), informer: f.Rbac().V1beta1().ClusterRoles().Informer()}, nil case rbac_v1beta1.SchemeGroupVersion.WithResource("clusterrolebindings"): return &genericInformer{resource: resource.GroupResource(), informer: f.Rbac().V1beta1().ClusterRoleBindings().Informer()}, nil case rbac_v1beta1.SchemeGroupVersion.WithResource("roles"): return &genericInformer{resource: resource.GroupResource(), informer: f.Rbac().V1beta1().Roles().Informer()}, nil case rbac_v1beta1.SchemeGroupVersion.WithResource("rolebindings"): return &genericInformer{resource: resource.GroupResource(), informer: f.Rbac().V1beta1().RoleBindings().Informer()}, nil // Group=Storage, Version=InternalVersion case storage.SchemeGroupVersion.WithResource("storageclasses"): return &genericInformer{resource: resource.GroupResource(), informer: f.Storage().InternalVersion().StorageClasses().Informer()}, nil // Group=Storage, Version=V1beta1 case storage_v1beta1.SchemeGroupVersion.WithResource("storageclasses"): return &genericInformer{resource: resource.GroupResource(), informer: f.Storage().V1beta1().StorageClasses().Informer()}, nil } return nil, fmt.Errorf("no informer found for %v", resource) }
// TODO turn this into reusable method checking available resources func contains(resourcesList []*metav1.APIResourceList, resource schema.GroupVersionResource) bool { resources := discovery.FilteredBy(discovery.ResourcePredicateFunc(func(gv string, r *metav1.APIResource) bool { return resource.GroupVersion().String() == gv && resource.Resource == r.Name }), resourcesList) return len(resources) != 0 }
// patchResource divides PatchResource for easier unit testing func patchResource( ctx request.Context, admit updateAdmissionFunc, timeout time.Duration, versionedObj runtime.Object, patcher rest.Patcher, name string, patchType types.PatchType, patchJS []byte, namer ScopeNamer, copier runtime.ObjectCopier, resource schema.GroupVersionResource, codec runtime.Codec, ) (runtime.Object, error) { namespace := request.NamespaceValue(ctx) var ( originalObjJS []byte originalPatchedObjJS []byte originalObjMap map[string]interface{} originalPatchMap map[string]interface{} lastConflictErr error ) // applyPatch is called every time GuaranteedUpdate asks for the updated object, // and is given the currently persisted object as input. applyPatch := func(_ request.Context, _, currentObject runtime.Object) (runtime.Object, error) { // Make sure we actually have a persisted currentObject if hasUID, err := hasUID(currentObject); err != nil { return nil, err } else if !hasUID { return nil, errors.NewNotFound(resource.GroupResource(), name) } switch { case originalObjJS == nil && originalObjMap == nil: // first time through, // 1. apply the patch // 2. save the original and patched to detect whether there were conflicting changes on retries objToUpdate := patcher.New() // For performance reasons, in case of strategicpatch, we avoid json // marshaling and unmarshaling and operate just on map[string]interface{}. // In case of other patch types, we still have to operate on JSON // representations. switch patchType { case types.JSONPatchType, types.MergePatchType: originalJS, patchedJS, err := patchObjectJSON(patchType, codec, currentObject, patchJS, objToUpdate, versionedObj) if err != nil { return nil, err } originalObjJS, originalPatchedObjJS = originalJS, patchedJS case types.StrategicMergePatchType: originalMap, patchMap, err := strategicPatchObject(codec, currentObject, patchJS, objToUpdate, versionedObj) if err != nil { return nil, err } originalObjMap, originalPatchMap = originalMap, patchMap } if err := checkName(objToUpdate, name, namespace, namer); err != nil { return nil, err } return objToUpdate, nil default: // on a conflict, // 1. build a strategic merge patch from originalJS and the patchedJS. Different patch types can // be specified, but a strategic merge patch should be expressive enough handle them. Build the // patch with this type to handle those cases. // 2. build a strategic merge patch from originalJS and the currentJS // 3. ensure no conflicts between the two patches // 4. apply the #1 patch to the currentJS object // TODO: This should be one-step conversion that doesn't require // json marshaling and unmarshaling once #39017 is fixed. data, err := runtime.Encode(codec, currentObject) if err != nil { return nil, err } currentObjMap := make(map[string]interface{}) if err := json.Unmarshal(data, ¤tObjMap); err != nil { return nil, err } var currentPatchMap map[string]interface{} if originalObjMap != nil { var err error currentPatchMap, err = strategicpatch.CreateTwoWayMergeMapPatch(originalObjMap, currentObjMap, versionedObj) if err != nil { return nil, err } } else { if originalPatchMap == nil { // Compute original patch, if we already didn't do this in previous retries. originalPatch, err := strategicpatch.CreateTwoWayMergePatch(originalObjJS, originalPatchedObjJS, versionedObj) if err != nil { return nil, err } originalPatchMap = make(map[string]interface{}) if err := json.Unmarshal(originalPatch, &originalPatchMap); err != nil { return nil, err } } // Compute current patch. currentObjJS, err := runtime.Encode(codec, currentObject) if err != nil { return nil, err } currentPatch, err := strategicpatch.CreateTwoWayMergePatch(originalObjJS, currentObjJS, versionedObj) if err != nil { return nil, err } currentPatchMap = make(map[string]interface{}) if err := json.Unmarshal(currentPatch, ¤tPatchMap); err != nil { return nil, err } } hasConflicts, err := strategicpatch.HasConflicts(originalPatchMap, currentPatchMap) if err != nil { return nil, err } if hasConflicts { if glog.V(4) { diff1, _ := json.Marshal(currentPatchMap) diff2, _ := json.Marshal(originalPatchMap) glog.Infof("patchResource failed for resource %s, because there is a meaningful conflict.\n diff1=%v\n, diff2=%v\n", name, diff1, diff2) } // Return the last conflict error we got if we have one if lastConflictErr != nil { return nil, lastConflictErr } // Otherwise manufacture one of our own return nil, errors.NewConflict(resource.GroupResource(), name, nil) } objToUpdate := patcher.New() if err := applyPatchToObject(codec, currentObjMap, originalPatchMap, objToUpdate, versionedObj); err != nil { return nil, err } return objToUpdate, nil } } // applyAdmission is called every time GuaranteedUpdate asks for the updated object, // and is given the currently persisted object and the patched object as input. applyAdmission := func(ctx request.Context, patchedObject runtime.Object, currentObject runtime.Object) (runtime.Object, error) { return patchedObject, admit(patchedObject, currentObject) } updatedObjectInfo := rest.DefaultUpdatedObjectInfo(nil, copier, applyPatch, applyAdmission) return finishRequest(timeout, func() (runtime.Object, error) { updateObject, _, updateErr := patcher.Update(ctx, name, updatedObjectInfo) for i := 0; i < maxRetryWhenPatchConflicts && (errors.IsConflict(updateErr)); i++ { lastConflictErr = updateErr updateObject, _, updateErr = patcher.Update(ctx, name, updatedObjectInfo) } return updateObject, updateErr }) }