GC fallback to jsonmerge patch when SMP is not supported

This commit is contained in:
Chao Xu
2017-11-28 17:49:24 -08:00
committed by Haowei Cai
parent 880dbd339b
commit 29d72a7134
6 changed files with 240 additions and 20 deletions

View File

@@ -21,12 +21,16 @@ import (
"fmt"
"strings"
"k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types"
"k8s.io/kubernetes/pkg/controller/garbagecollector/metaonly"
)
func deleteOwnerRefPatch(dependentUID types.UID, ownerUIDs ...types.UID) []byte {
func deleteOwnerRefStrategicMergePatch(dependentUID types.UID, ownerUIDs ...types.UID) []byte {
var pieces []string
for _, ownerUID := range ownerUIDs {
pieces = append(pieces, fmt.Sprintf(`{"$patch":"delete","uid":"%s"}`, ownerUID))
@@ -35,9 +39,97 @@ func deleteOwnerRefPatch(dependentUID types.UID, ownerUIDs ...types.UID) []byte
return []byte(patch)
}
// generate a patch that unsets the BlockOwnerDeletion field of all
// getMetadata tries getting object metadata from local cache, and sends GET request to apiserver when
// local cache is not available or not latest.
func (gc *GarbageCollector) getMetadata(apiVersion, kind, namespace, name string) (metav1.Object, error) {
apiResource, _, err := gc.apiResource(apiVersion, kind)
if err != nil {
return nil, err
}
gc.dependencyGraphBuilder.monitorLock.RLock()
defer gc.dependencyGraphBuilder.monitorLock.RUnlock()
m, ok := gc.dependencyGraphBuilder.monitors[apiResource]
if !ok || m == nil {
// If local cache doesn't exist for mapping.Resource, send a GET request to API server
return gc.dynamicClient.Resource(apiResource).Namespace(namespace).Get(name, metav1.GetOptions{})
}
key := name
if len(namespace) != 0 {
key = namespace + "/" + name
}
raw, exist, err := m.store.GetByKey(key)
if err != nil {
return nil, err
}
if !exist {
// If local cache doesn't contain the object, send a GET request to API server
return gc.dynamicClient.Resource(apiResource).Namespace(namespace).Get(name, metav1.GetOptions{})
}
obj, ok := raw.(runtime.Object)
if !ok {
return nil, fmt.Errorf("expect a runtime.Object, got %v", raw)
}
return meta.Accessor(obj)
}
type objectForPatch struct {
ObjectMetaForPatch `json:"metadata"`
}
type ObjectMetaForPatch struct {
ResourceVersion string `json:"resourceVersion"`
OwnerReferences []metav1.OwnerReference `json:"ownerReferences"`
}
// jsonMergePatchFunc defines the interface for functions that construct json merge patches that manipulate
// owner reference array.
type jsonMergePatchFunc func(*node) ([]byte, error)
// patch tries strategic merge patch on item first, and if SMP is not supported, it fallbacks to JSON merge
// patch.
func (gc *GarbageCollector) patch(item *node, smp []byte, jmp jsonMergePatchFunc) (*unstructured.Unstructured, error) {
smpResult, err := gc.patchObject(item.identity, smp, types.StrategicMergePatchType)
if err == nil {
return smpResult, nil
}
if !errors.IsUnsupportedMediaType(err) {
return nil, err
}
// StrategicMergePatch is not supported, use JSON merge patch instead
patch, err := jmp(item)
if err != nil {
return nil, err
}
return gc.patchObject(item.identity, patch, types.MergePatchType)
}
// Returns JSON merge patch that removes the ownerReferences matching ownerUIDs.
func (gc *GarbageCollector) deleteOwnerRefJSONMergePatch(item *node, ownerUIDs ...types.UID) ([]byte, error) {
accessor, err := gc.getMetadata(item.identity.APIVersion, item.identity.Kind, item.identity.Namespace, item.identity.Name)
if err != nil {
return nil, err
}
expectedObjectMeta := ObjectMetaForPatch{}
expectedObjectMeta.ResourceVersion = accessor.GetResourceVersion()
refs := accessor.GetOwnerReferences()
for _, ref := range refs {
var skip bool
for _, ownerUID := range ownerUIDs {
if ref.UID == ownerUID {
skip = true
break
}
}
if !skip {
expectedObjectMeta.OwnerReferences = append(expectedObjectMeta.OwnerReferences, ref)
}
}
return json.Marshal(objectForPatch{expectedObjectMeta})
}
// Generate a patch that unsets the BlockOwnerDeletion field of all
// ownerReferences of node.
func (n *node) patchToUnblockOwnerReferences() ([]byte, error) {
func (n *node) unblockOwnerReferencesStrategicMergePatch() ([]byte, error) {
var dummy metaonly.MetadataOnlyObject
var blockingRefs []metav1.OwnerReference
falseVar := false
@@ -52,3 +144,22 @@ func (n *node) patchToUnblockOwnerReferences() ([]byte, error) {
dummy.ObjectMeta.UID = n.identity.UID
return json.Marshal(dummy)
}
// Generate a JSON merge patch that unsets the BlockOwnerDeletion field of all
// ownerReferences of node.
func (gc *GarbageCollector) unblockOwnerReferencesJSONMergePatch(n *node) ([]byte, error) {
accessor, err := gc.getMetadata(n.identity.APIVersion, n.identity.Kind, n.identity.Namespace, n.identity.Name)
if err != nil {
return nil, err
}
expectedObjectMeta := ObjectMetaForPatch{}
expectedObjectMeta.ResourceVersion = accessor.GetResourceVersion()
var expectedOwners []metav1.OwnerReference
falseVar := false
for _, owner := range n.owners {
owner.BlockOwnerDeletion = &falseVar
expectedOwners = append(expectedOwners, owner)
}
expectedObjectMeta.OwnerReferences = expectedOwners
return json.Marshal(objectForPatch{expectedObjectMeta})
}