k8s.io/kubernetes@v1.29.3/pkg/controller/namespace/deletion/status_condition_utils_test.go (about) 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 "reflect" 21 "testing" 22 23 v1 "k8s.io/api/core/v1" 24 "k8s.io/apimachinery/pkg/runtime/schema" 25 ) 26 27 func TestUpdateConditions(t *testing.T) { 28 tests := []struct { 29 name string 30 31 newConditions []v1.NamespaceCondition 32 startingStatus *v1.NamespaceStatus 33 34 expecteds []v1.NamespaceCondition 35 }{ 36 { 37 name: "leave unknown", 38 39 newConditions: []v1.NamespaceCondition{}, 40 startingStatus: &v1.NamespaceStatus{ 41 Conditions: []v1.NamespaceCondition{ 42 {Type: "unknown", Status: v1.ConditionTrue}, 43 }, 44 }, 45 expecteds: []v1.NamespaceCondition{ 46 {Type: "unknown", Status: v1.ConditionTrue}, 47 *newSuccessfulCondition(v1.NamespaceDeletionDiscoveryFailure), 48 *newSuccessfulCondition(v1.NamespaceDeletionGVParsingFailure), 49 *newSuccessfulCondition(v1.NamespaceDeletionContentFailure), 50 *newSuccessfulCondition(v1.NamespaceContentRemaining), 51 *newSuccessfulCondition(v1.NamespaceFinalizersRemaining), 52 }, 53 }, 54 { 55 name: "replace with success", 56 57 newConditions: []v1.NamespaceCondition{}, 58 startingStatus: &v1.NamespaceStatus{ 59 Conditions: []v1.NamespaceCondition{ 60 {Type: v1.NamespaceDeletionDiscoveryFailure, Status: v1.ConditionTrue}, 61 }, 62 }, 63 expecteds: []v1.NamespaceCondition{ 64 *newSuccessfulCondition(v1.NamespaceDeletionDiscoveryFailure), 65 *newSuccessfulCondition(v1.NamespaceDeletionGVParsingFailure), 66 *newSuccessfulCondition(v1.NamespaceDeletionContentFailure), 67 *newSuccessfulCondition(v1.NamespaceContentRemaining), 68 *newSuccessfulCondition(v1.NamespaceFinalizersRemaining), 69 }, 70 }, 71 { 72 name: "leave different order", 73 74 newConditions: []v1.NamespaceCondition{}, 75 startingStatus: &v1.NamespaceStatus{ 76 Conditions: []v1.NamespaceCondition{ 77 {Type: v1.NamespaceDeletionGVParsingFailure, Status: v1.ConditionTrue}, 78 {Type: v1.NamespaceDeletionDiscoveryFailure, Status: v1.ConditionTrue}, 79 }, 80 }, 81 expecteds: []v1.NamespaceCondition{ 82 *newSuccessfulCondition(v1.NamespaceDeletionGVParsingFailure), 83 *newSuccessfulCondition(v1.NamespaceDeletionDiscoveryFailure), 84 *newSuccessfulCondition(v1.NamespaceDeletionContentFailure), 85 *newSuccessfulCondition(v1.NamespaceContentRemaining), 86 *newSuccessfulCondition(v1.NamespaceFinalizersRemaining), 87 }, 88 }, 89 { 90 name: "overwrite with failure", 91 92 newConditions: []v1.NamespaceCondition{ 93 {Type: v1.NamespaceDeletionGVParsingFailure, Status: v1.ConditionTrue, Reason: "foo", Message: "bar"}, 94 }, 95 startingStatus: &v1.NamespaceStatus{ 96 Conditions: []v1.NamespaceCondition{ 97 {Type: v1.NamespaceDeletionGVParsingFailure, Status: v1.ConditionFalse}, 98 {Type: v1.NamespaceDeletionDiscoveryFailure, Status: v1.ConditionTrue}, 99 }, 100 }, 101 expecteds: []v1.NamespaceCondition{ 102 {Type: v1.NamespaceDeletionGVParsingFailure, Status: v1.ConditionTrue, Reason: "foo", Message: "bar"}, 103 *newSuccessfulCondition(v1.NamespaceDeletionDiscoveryFailure), 104 *newSuccessfulCondition(v1.NamespaceDeletionContentFailure), 105 *newSuccessfulCondition(v1.NamespaceContentRemaining), 106 *newSuccessfulCondition(v1.NamespaceFinalizersRemaining), 107 }, 108 }, 109 { 110 name: "write new failure", 111 112 newConditions: []v1.NamespaceCondition{ 113 {Type: v1.NamespaceDeletionGVParsingFailure, Status: v1.ConditionTrue, Reason: "foo", Message: "bar"}, 114 }, 115 startingStatus: &v1.NamespaceStatus{ 116 Conditions: []v1.NamespaceCondition{ 117 {Type: v1.NamespaceDeletionDiscoveryFailure, Status: v1.ConditionTrue}, 118 }, 119 }, 120 expecteds: []v1.NamespaceCondition{ 121 *newSuccessfulCondition(v1.NamespaceDeletionDiscoveryFailure), 122 {Type: v1.NamespaceDeletionGVParsingFailure, Status: v1.ConditionTrue, Reason: "foo", Message: "bar"}, 123 *newSuccessfulCondition(v1.NamespaceDeletionContentFailure), 124 *newSuccessfulCondition(v1.NamespaceContentRemaining), 125 *newSuccessfulCondition(v1.NamespaceFinalizersRemaining), 126 }, 127 }, 128 } 129 130 for _, test := range tests { 131 t.Run(test.name, func(t *testing.T) { 132 updateConditions(test.startingStatus, test.newConditions) 133 134 actuals := test.startingStatus.Conditions 135 if len(actuals) != len(test.expecteds) { 136 t.Fatal(actuals) 137 } 138 for i := range actuals { 139 actual := actuals[i] 140 expected := test.expecteds[i] 141 expected.LastTransitionTime = actual.LastTransitionTime 142 if !reflect.DeepEqual(expected, actual) { 143 t.Error(actual) 144 } 145 } 146 }) 147 } 148 } 149 150 func TestProcessContentTotals(t *testing.T) { 151 tests := []struct { 152 name string 153 154 contentTotals allGVRDeletionMetadata 155 expecteds []v1.NamespaceCondition 156 }{ 157 { 158 name: "nothing", 159 160 contentTotals: allGVRDeletionMetadata{ 161 gvrToNumRemaining: map[schema.GroupVersionResource]int{}, 162 finalizersToNumRemaining: map[string]int{}, 163 }, 164 expecteds: []v1.NamespaceCondition{}, 165 }, 166 { 167 name: "just remaining", 168 169 contentTotals: allGVRDeletionMetadata{ 170 gvrToNumRemaining: map[schema.GroupVersionResource]int{ 171 {Group: "apps.k8s.io", Resource: "daemonsets"}: 5, 172 {Group: "apps.k8s.io", Resource: "deployments"}: 5, 173 }, 174 finalizersToNumRemaining: map[string]int{}, 175 }, 176 expecteds: []v1.NamespaceCondition{ 177 {Type: v1.NamespaceContentRemaining, Status: v1.ConditionTrue, Reason: "SomeResourcesRemain", Message: `Some resources are remaining: daemonsets.apps.k8s.io has 5 resource instances, deployments.apps.k8s.io has 5 resource instances`}, 178 }, 179 }, 180 { 181 name: "just finalizers", // this shouldn't happen 182 183 contentTotals: allGVRDeletionMetadata{ 184 gvrToNumRemaining: map[schema.GroupVersionResource]int{}, 185 finalizersToNumRemaining: map[string]int{ 186 "service-catalog": 6, 187 "kubedb": 5, 188 }, 189 }, 190 expecteds: []v1.NamespaceCondition{ 191 {Type: v1.NamespaceFinalizersRemaining, Status: v1.ConditionTrue, Reason: "SomeFinalizersRemain", Message: `Some content in the namespace has finalizers remaining: kubedb in 5 resource instances, service-catalog in 6 resource instances`}, 192 }, 193 }, 194 { 195 name: "both", 196 197 contentTotals: allGVRDeletionMetadata{ 198 gvrToNumRemaining: map[schema.GroupVersionResource]int{ 199 {Group: "apps.k8s.io", Resource: "daemonsets"}: 5, 200 {Group: "apps.k8s.io", Resource: "deployments"}: 5, 201 }, 202 finalizersToNumRemaining: map[string]int{ 203 "service-catalog": 6, 204 "kubedb": 5, 205 }, 206 }, 207 expecteds: []v1.NamespaceCondition{ 208 {Type: v1.NamespaceContentRemaining, Status: v1.ConditionTrue, Reason: "SomeResourcesRemain", Message: `Some resources are remaining: daemonsets.apps.k8s.io has 5 resource instances, deployments.apps.k8s.io has 5 resource instances`}, 209 {Type: v1.NamespaceFinalizersRemaining, Status: v1.ConditionTrue, Reason: "SomeFinalizersRemain", Message: `Some content in the namespace has finalizers remaining: kubedb in 5 resource instances, service-catalog in 6 resource instances`}, 210 }, 211 }, 212 } 213 214 for _, test := range tests { 215 t.Run(test.name, func(t *testing.T) { 216 u := namespaceConditionUpdater{} 217 218 u.ProcessContentTotals(test.contentTotals) 219 220 actuals := u.newConditions 221 if len(actuals) != len(test.expecteds) { 222 t.Fatal(actuals) 223 } 224 for i := range actuals { 225 actual := actuals[i] 226 expected := test.expecteds[i] 227 expected.LastTransitionTime = actual.LastTransitionTime 228 if !reflect.DeepEqual(expected, actual) { 229 t.Error(actual) 230 } 231 } 232 }) 233 } 234 }