k8s.io/kubernetes@v1.29.3/pkg/api/service/warnings_test.go (about) 1 /* 2 Copyright 2022 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 service 18 19 import ( 20 "reflect" 21 "testing" 22 23 "github.com/google/go-cmp/cmp" 24 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 25 "k8s.io/apimachinery/pkg/util/validation/field" 26 api "k8s.io/kubernetes/pkg/apis/core" 27 ) 28 29 func TestGetWarningsForService(t *testing.T) { 30 testCases := []struct { 31 name string 32 tweakSvc func(svc *api.Service) // Given a basic valid service, each test case can customize it. 33 numWarnings int 34 }{{ 35 name: "new topology mode set", 36 tweakSvc: func(s *api.Service) { 37 s.Annotations = map[string]string{api.AnnotationTopologyMode: "foo"} 38 }, 39 numWarnings: 0, 40 }, { 41 name: "deprecated hints annotation set", 42 tweakSvc: func(s *api.Service) { 43 s.Annotations = map[string]string{api.DeprecatedAnnotationTopologyAwareHints: "foo"} 44 }, 45 numWarnings: 1, 46 }, { 47 name: "externalIPs set when type is ExternalName", 48 tweakSvc: func(s *api.Service) { 49 s.Spec.Type = api.ServiceTypeExternalName 50 s.Spec.ExternalIPs = []string{"1.2.3.4"} 51 }, 52 numWarnings: 1, 53 }, { 54 name: "externalName set when type is not ExternalName", 55 tweakSvc: func(s *api.Service) { 56 s.Spec.Type = api.ServiceTypeClusterIP 57 s.Spec.ExternalName = "example.com" 58 }, 59 numWarnings: 1, 60 }} 61 62 for _, tc := range testCases { 63 t.Run(tc.name, func(t *testing.T) { 64 svc := &api.Service{} 65 tc.tweakSvc(svc) 66 warnings := GetWarningsForService(svc, svc) 67 if want, got := tc.numWarnings, len(warnings); got != want { 68 t.Errorf("Unexpected warning list: expected %d, got %d\n%q", want, got, warnings) 69 } 70 }) 71 } 72 } 73 74 func TestGetWarningsForServiceClusterIPs(t *testing.T) { 75 service := func(clusterIPs []string) *api.Service { 76 svc := api.Service{ 77 ObjectMeta: metav1.ObjectMeta{ 78 Name: "svc-test", 79 Namespace: "ns", 80 }, 81 Spec: api.ServiceSpec{ 82 Type: api.ServiceTypeClusterIP, 83 }, 84 } 85 86 if len(clusterIPs) > 0 { 87 svc.Spec.ClusterIP = clusterIPs[0] 88 svc.Spec.ClusterIPs = clusterIPs 89 } 90 return &svc 91 } 92 93 tests := []struct { 94 name string 95 service *api.Service 96 oldService *api.Service 97 want []string 98 }{ 99 { 100 name: "IPv4 No failures", 101 service: service([]string{"192.12.2.2"}), 102 }, 103 { 104 name: "IPv6 No failures", 105 service: service([]string{"2001:db8::2"}), 106 }, 107 { 108 name: "IPv4 with leading zeros", 109 service: service([]string{"192.012.2.2"}), 110 want: []string{ 111 `spec.clusterIPs[0]: IP address was accepted, but will be invalid in a future Kubernetes release: ParseAddr("192.012.2.2"): IPv4 field has octet with leading zero`, 112 }, 113 }, 114 { 115 name: "Dual Stack IPv4-IPv6 and IPv4 with leading zeros", 116 service: service([]string{"192.012.2.2", "2001:db8::2"}), 117 want: []string{ 118 `spec.clusterIPs[0]: IP address was accepted, but will be invalid in a future Kubernetes release: ParseAddr("192.012.2.2"): IPv4 field has octet with leading zero`, 119 }, 120 }, 121 { 122 name: "Dual Stack IPv6-IPv4 and IPv4 with leading zeros", 123 service: service([]string{"2001:db8::2", "192.012.2.2"}), 124 want: []string{ 125 `spec.clusterIPs[1]: IP address was accepted, but will be invalid in a future Kubernetes release: ParseAddr("192.012.2.2"): IPv4 field has octet with leading zero`, 126 }, 127 }, 128 { 129 name: "IPv6 non canonical format", 130 service: service([]string{"2001:db8:0:0::2"}), 131 want: []string{ 132 `spec.clusterIPs[0]: IPv6 address "2001:db8:0:0::2" is not in RFC 5952 canonical format ("2001:db8::2"), which may cause controller apply-loops`, 133 }, 134 }, 135 { 136 name: "Dual Stack IPv4-IPv6 and IPv6 non-canonical format", 137 service: service([]string{"192.12.2.2", "2001:db8:0:0::2"}), 138 want: []string{ 139 `spec.clusterIPs[1]: IPv6 address "2001:db8:0:0::2" is not in RFC 5952 canonical format ("2001:db8::2"), which may cause controller apply-loops`, 140 }, 141 }, 142 { 143 name: "Dual Stack IPv6-IPv4 and IPv6 non-canonical formats", 144 service: service([]string{"2001:db8:0:0::2", "192.12.2.2"}), 145 want: []string{ 146 `spec.clusterIPs[0]: IPv6 address "2001:db8:0:0::2" is not in RFC 5952 canonical format ("2001:db8::2"), which may cause controller apply-loops`, 147 }, 148 }, 149 { 150 name: "Dual Stack IPv4-IPv6 and IPv4 with leading zeros and IPv6 non-canonical format", 151 service: service([]string{"192.012.2.2", "2001:db8:0:0::2"}), 152 want: []string{ 153 `spec.clusterIPs[0]: IP address was accepted, but will be invalid in a future Kubernetes release: ParseAddr("192.012.2.2"): IPv4 field has octet with leading zero`, 154 `spec.clusterIPs[1]: IPv6 address "2001:db8:0:0::2" is not in RFC 5952 canonical format ("2001:db8::2"), which may cause controller apply-loops`, 155 }, 156 }, 157 { 158 name: "Dual Stack IPv6-IPv4 and IPv4 with leading zeros and IPv6 non-canonical format", 159 service: service([]string{"2001:db8:0:0::2", "192.012.2.2"}), 160 want: []string{ 161 `spec.clusterIPs[0]: IPv6 address "2001:db8:0:0::2" is not in RFC 5952 canonical format ("2001:db8::2"), which may cause controller apply-loops`, 162 `spec.clusterIPs[1]: IP address was accepted, but will be invalid in a future Kubernetes release: ParseAddr("192.012.2.2"): IPv4 field has octet with leading zero`, 163 }, 164 }, 165 { 166 name: "Service with all IPs fields with errors", 167 service: &api.Service{ 168 ObjectMeta: metav1.ObjectMeta{ 169 Name: "svc-test", 170 Namespace: "ns", 171 }, 172 Spec: api.ServiceSpec{ 173 ClusterIP: "2001:db8:0:0::2", 174 ClusterIPs: []string{"2001:db8:0:0::2", "192.012.2.2"}, 175 ExternalIPs: []string{"2001:db8:1:0::2", "10.012.2.2"}, 176 LoadBalancerIP: "10.001.1.1", 177 LoadBalancerSourceRanges: []string{"2001:db8:1:0::/64", "10.012.2.0/24"}, 178 Type: api.ServiceTypeClusterIP, 179 }, 180 }, 181 want: []string{ 182 `spec.clusterIPs[0]: IPv6 address "2001:db8:0:0::2" is not in RFC 5952 canonical format ("2001:db8::2"), which may cause controller apply-loops`, 183 `spec.clusterIPs[1]: IP address was accepted, but will be invalid in a future Kubernetes release: ParseAddr("192.012.2.2"): IPv4 field has octet with leading zero`, 184 `spec.externalIPs[0]: IPv6 address "2001:db8:1:0::2" is not in RFC 5952 canonical format ("2001:db8:1::2"), which may cause controller apply-loops`, 185 `spec.externalIPs[1]: IP address was accepted, but will be invalid in a future Kubernetes release: ParseAddr("10.012.2.2"): IPv4 field has octet with leading zero`, 186 `spec.loadBalancerIP: IP address was accepted, but will be invalid in a future Kubernetes release: ParseAddr("10.001.1.1"): IPv4 field has octet with leading zero`, 187 `spec.loadBalancerSourceRanges[0]: IPv6 prefix "2001:db8:1:0::/64" is not in RFC 5952 canonical format ("2001:db8:1::/64"), which may cause controller apply-loops`, 188 `spec.loadBalancerSourceRanges[1]: IP prefix was accepted, but will be invalid in a future Kubernetes release: netip.ParsePrefix("10.012.2.0/24"): ParseAddr("10.012.2.0"): IPv4 field has octet with leading zero`, 189 }, 190 }, 191 } 192 for _, tt := range tests { 193 t.Run(tt.name, func(t *testing.T) { 194 if got := GetWarningsForService(tt.service, tt.oldService); !reflect.DeepEqual(got, tt.want) { 195 t.Errorf("GetWarningsForService() = %v", cmp.Diff(got, tt.want)) 196 } 197 }) 198 } 199 } 200 201 func Test_getWarningsForIP(t *testing.T) { 202 tests := []struct { 203 name string 204 fieldPath *field.Path 205 address string 206 want []string 207 }{ 208 { 209 name: "IPv4 No failures", 210 address: "192.12.2.2", 211 fieldPath: field.NewPath("spec").Child("clusterIPs").Index(0), 212 want: []string{}, 213 }, 214 { 215 name: "IPv6 No failures", 216 address: "2001:db8::2", 217 fieldPath: field.NewPath("spec").Child("clusterIPs").Index(0), 218 want: []string{}, 219 }, 220 { 221 name: "IPv4 with leading zeros", 222 address: "192.012.2.2", 223 fieldPath: field.NewPath("spec").Child("clusterIPs").Index(0), 224 want: []string{ 225 `spec.clusterIPs[0]: IP address was accepted, but will be invalid in a future Kubernetes release: ParseAddr("192.012.2.2"): IPv4 field has octet with leading zero`, 226 }, 227 }, 228 { 229 name: "IPv6 non-canonical format", 230 address: "2001:db8:0:0::2", 231 fieldPath: field.NewPath("spec").Child("loadBalancerIP"), 232 want: []string{ 233 `spec.loadBalancerIP: IPv6 address "2001:db8:0:0::2" is not in RFC 5952 canonical format ("2001:db8::2"), which may cause controller apply-loops`, 234 }, 235 }, 236 } 237 for _, tt := range tests { 238 t.Run(tt.name, func(t *testing.T) { 239 if got := getWarningsForIP(tt.fieldPath, tt.address); !reflect.DeepEqual(got, tt.want) { 240 t.Errorf("getWarningsForIP() = %v, want %v", got, tt.want) 241 } 242 }) 243 } 244 } 245 246 func Test_getWarningsForCIDR(t *testing.T) { 247 tests := []struct { 248 name string 249 fieldPath *field.Path 250 cidr string 251 want []string 252 }{ 253 { 254 name: "IPv4 No failures", 255 cidr: "192.12.2.0/24", 256 fieldPath: field.NewPath("spec").Child("loadBalancerSourceRanges").Index(0), 257 want: []string{}, 258 }, 259 { 260 name: "IPv6 No failures", 261 cidr: "2001:db8::/64", 262 fieldPath: field.NewPath("spec").Child("loadBalancerSourceRanges").Index(0), 263 want: []string{}, 264 }, 265 { 266 name: "IPv4 with leading zeros", 267 cidr: "192.012.2.0/24", 268 fieldPath: field.NewPath("spec").Child("loadBalancerSourceRanges").Index(0), 269 want: []string{ 270 `spec.loadBalancerSourceRanges[0]: IP prefix was accepted, but will be invalid in a future Kubernetes release: netip.ParsePrefix("192.012.2.0/24"): ParseAddr("192.012.2.0"): IPv4 field has octet with leading zero`, 271 }, 272 }, 273 { 274 name: "IPv6 non-canonical format", 275 cidr: "2001:db8:0:0::/64", 276 fieldPath: field.NewPath("spec").Child("loadBalancerSourceRanges").Index(0), 277 want: []string{ 278 `spec.loadBalancerSourceRanges[0]: IPv6 prefix "2001:db8:0:0::/64" is not in RFC 5952 canonical format ("2001:db8::/64"), which may cause controller apply-loops`, 279 }, 280 }, 281 } 282 for _, tt := range tests { 283 t.Run(tt.name, func(t *testing.T) { 284 if got := getWarningsForCIDR(tt.fieldPath, tt.cidr); !reflect.DeepEqual(got, tt.want) { 285 t.Errorf("getWarningsForCIDR() = %v, want %v", got, tt.want) 286 } 287 }) 288 } 289 }