...

Source file src/k8s.io/kubernetes/pkg/controller/namespace/deletion/status_condition_utils.go

Documentation: k8s.io/kubernetes/pkg/controller/namespace/deletion

     1  /*
     2  Copyright 2019 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package deletion
    18  
    19  import (
    20  	"fmt"
    21  	"sort"
    22  	"strings"
    23  
    24  	v1 "k8s.io/api/core/v1"
    25  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    26  	"k8s.io/client-go/discovery"
    27  )
    28  
    29  // NamespaceConditionUpdater interface that translates namespace deleter errors
    30  // into namespace status conditions.
    31  type NamespaceConditionUpdater interface {
    32  	ProcessDiscoverResourcesErr(e error)
    33  	ProcessGroupVersionErr(e error)
    34  	ProcessDeleteContentErr(e error)
    35  	Update(*v1.Namespace) bool
    36  }
    37  
    38  type namespaceConditionUpdater struct {
    39  	newConditions       []v1.NamespaceCondition
    40  	deleteContentErrors []error
    41  }
    42  
    43  var _ NamespaceConditionUpdater = &namespaceConditionUpdater{}
    44  
    45  var (
    46  	// conditionTypes Namespace condition types that are maintained by namespace_deleter controller.
    47  	conditionTypes = []v1.NamespaceConditionType{
    48  		v1.NamespaceDeletionDiscoveryFailure,
    49  		v1.NamespaceDeletionGVParsingFailure,
    50  		v1.NamespaceDeletionContentFailure,
    51  		v1.NamespaceContentRemaining,
    52  		v1.NamespaceFinalizersRemaining,
    53  	}
    54  	okMessages = map[v1.NamespaceConditionType]string{
    55  		v1.NamespaceDeletionDiscoveryFailure: "All resources successfully discovered",
    56  		v1.NamespaceDeletionGVParsingFailure: "All legacy kube types successfully parsed",
    57  		v1.NamespaceDeletionContentFailure:   "All content successfully deleted, may be waiting on finalization",
    58  		v1.NamespaceContentRemaining:         "All content successfully removed",
    59  		v1.NamespaceFinalizersRemaining:      "All content-preserving finalizers finished",
    60  	}
    61  	okReasons = map[v1.NamespaceConditionType]string{
    62  		v1.NamespaceDeletionDiscoveryFailure: "ResourcesDiscovered",
    63  		v1.NamespaceDeletionGVParsingFailure: "ParsedGroupVersions",
    64  		v1.NamespaceDeletionContentFailure:   "ContentDeleted",
    65  		v1.NamespaceContentRemaining:         "ContentRemoved",
    66  		v1.NamespaceFinalizersRemaining:      "ContentHasNoFinalizers",
    67  	}
    68  )
    69  
    70  // ProcessGroupVersionErr creates error condition if parsing GroupVersion of resources fails.
    71  func (u *namespaceConditionUpdater) ProcessGroupVersionErr(err error) {
    72  	d := v1.NamespaceCondition{
    73  		Type:               v1.NamespaceDeletionGVParsingFailure,
    74  		Status:             v1.ConditionTrue,
    75  		LastTransitionTime: metav1.Now(),
    76  		Reason:             "GroupVersionParsingFailed",
    77  		Message:            err.Error(),
    78  	}
    79  	u.newConditions = append(u.newConditions, d)
    80  }
    81  
    82  // ProcessDiscoverResourcesErr creates error condition from ErrGroupDiscoveryFailed.
    83  func (u *namespaceConditionUpdater) ProcessDiscoverResourcesErr(err error) {
    84  	var msg string
    85  	if derr, ok := err.(*discovery.ErrGroupDiscoveryFailed); ok {
    86  		msg = fmt.Sprintf("Discovery failed for some groups, %d failing: %v", len(derr.Groups), err)
    87  	} else {
    88  		msg = err.Error()
    89  	}
    90  	d := v1.NamespaceCondition{
    91  		Type:               v1.NamespaceDeletionDiscoveryFailure,
    92  		Status:             v1.ConditionTrue,
    93  		LastTransitionTime: metav1.Now(),
    94  		Reason:             "DiscoveryFailed",
    95  		Message:            msg,
    96  	}
    97  	u.newConditions = append(u.newConditions, d)
    98  
    99  }
   100  
   101  // ProcessContentTotals may create conditions for NamespaceContentRemaining and NamespaceFinalizersRemaining.
   102  func (u *namespaceConditionUpdater) ProcessContentTotals(contentTotals allGVRDeletionMetadata) {
   103  	if len(contentTotals.gvrToNumRemaining) != 0 {
   104  		remainingResources := []string{}
   105  		for gvr, numRemaining := range contentTotals.gvrToNumRemaining {
   106  			if numRemaining == 0 {
   107  				continue
   108  			}
   109  			remainingResources = append(remainingResources, fmt.Sprintf("%s.%s has %d resource instances", gvr.Resource, gvr.Group, numRemaining))
   110  		}
   111  		// sort for stable updates
   112  		sort.Strings(remainingResources)
   113  		u.newConditions = append(u.newConditions, v1.NamespaceCondition{
   114  			Type:               v1.NamespaceContentRemaining,
   115  			Status:             v1.ConditionTrue,
   116  			LastTransitionTime: metav1.Now(),
   117  			Reason:             "SomeResourcesRemain",
   118  			Message:            fmt.Sprintf("Some resources are remaining: %s", strings.Join(remainingResources, ", ")),
   119  		})
   120  	}
   121  
   122  	if len(contentTotals.finalizersToNumRemaining) != 0 {
   123  		remainingByFinalizer := []string{}
   124  		for finalizer, numRemaining := range contentTotals.finalizersToNumRemaining {
   125  			if numRemaining == 0 {
   126  				continue
   127  			}
   128  			remainingByFinalizer = append(remainingByFinalizer, fmt.Sprintf("%s in %d resource instances", finalizer, numRemaining))
   129  		}
   130  		// sort for stable updates
   131  		sort.Strings(remainingByFinalizer)
   132  		u.newConditions = append(u.newConditions, v1.NamespaceCondition{
   133  			Type:               v1.NamespaceFinalizersRemaining,
   134  			Status:             v1.ConditionTrue,
   135  			LastTransitionTime: metav1.Now(),
   136  			Reason:             "SomeFinalizersRemain",
   137  			Message:            fmt.Sprintf("Some content in the namespace has finalizers remaining: %s", strings.Join(remainingByFinalizer, ", ")),
   138  		})
   139  	}
   140  }
   141  
   142  // ProcessDeleteContentErr creates error condition from multiple delete content errors.
   143  func (u *namespaceConditionUpdater) ProcessDeleteContentErr(err error) {
   144  	u.deleteContentErrors = append(u.deleteContentErrors, err)
   145  }
   146  
   147  // Update compiles processed errors from namespace deletion into status conditions.
   148  func (u *namespaceConditionUpdater) Update(ns *v1.Namespace) bool {
   149  	if c := getCondition(u.newConditions, v1.NamespaceDeletionContentFailure); c == nil {
   150  		if c := makeDeleteContentCondition(u.deleteContentErrors); c != nil {
   151  			u.newConditions = append(u.newConditions, *c)
   152  		}
   153  	}
   154  	return updateConditions(&ns.Status, u.newConditions)
   155  }
   156  
   157  func makeDeleteContentCondition(err []error) *v1.NamespaceCondition {
   158  	if len(err) == 0 {
   159  		return nil
   160  	}
   161  	msgs := make([]string, 0, len(err))
   162  	for _, e := range err {
   163  		msgs = append(msgs, e.Error())
   164  	}
   165  	sort.Strings(msgs)
   166  	return &v1.NamespaceCondition{
   167  		Type:               v1.NamespaceDeletionContentFailure,
   168  		Status:             v1.ConditionTrue,
   169  		LastTransitionTime: metav1.Now(),
   170  		Reason:             "ContentDeletionFailed",
   171  		Message:            fmt.Sprintf("Failed to delete all resource types, %d remaining: %v", len(err), strings.Join(msgs, ", ")),
   172  	}
   173  }
   174  
   175  func updateConditions(status *v1.NamespaceStatus, newConditions []v1.NamespaceCondition) (hasChanged bool) {
   176  	for _, conditionType := range conditionTypes {
   177  		newCondition := getCondition(newConditions, conditionType)
   178  		// if we weren't failing, then this returned nil.  We should set the "ok" variant of the condition
   179  		if newCondition == nil {
   180  			newCondition = newSuccessfulCondition(conditionType)
   181  		}
   182  		oldCondition := getCondition(status.Conditions, conditionType)
   183  
   184  		// only new condition of this type exists, add to the list
   185  		if oldCondition == nil {
   186  			status.Conditions = append(status.Conditions, *newCondition)
   187  			hasChanged = true
   188  
   189  		} else if oldCondition.Status != newCondition.Status || oldCondition.Message != newCondition.Message || oldCondition.Reason != newCondition.Reason {
   190  			// old condition needs to be updated
   191  			if oldCondition.Status != newCondition.Status {
   192  				oldCondition.LastTransitionTime = metav1.Now()
   193  			}
   194  			oldCondition.Type = newCondition.Type
   195  			oldCondition.Status = newCondition.Status
   196  			oldCondition.Reason = newCondition.Reason
   197  			oldCondition.Message = newCondition.Message
   198  			hasChanged = true
   199  		}
   200  	}
   201  	return
   202  }
   203  
   204  func newSuccessfulCondition(conditionType v1.NamespaceConditionType) *v1.NamespaceCondition {
   205  	return &v1.NamespaceCondition{
   206  		Type:               conditionType,
   207  		Status:             v1.ConditionFalse,
   208  		LastTransitionTime: metav1.Now(),
   209  		Reason:             okReasons[conditionType],
   210  		Message:            okMessages[conditionType],
   211  	}
   212  }
   213  
   214  func getCondition(conditions []v1.NamespaceCondition, conditionType v1.NamespaceConditionType) *v1.NamespaceCondition {
   215  	for i := range conditions {
   216  		if conditions[i].Type == conditionType {
   217  			return &(conditions[i])
   218  		}
   219  	}
   220  	return nil
   221  }
   222  

View as plain text