k8s.io/apiserver@v0.31.1/pkg/cel/library/cidr_test.go (about) 1 /* 2 Copyright 2023 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 library_test 18 19 import ( 20 "net/netip" 21 "regexp" 22 "testing" 23 24 "github.com/google/cel-go/cel" 25 "github.com/google/cel-go/common/types" 26 "github.com/google/cel-go/common/types/ref" 27 "github.com/stretchr/testify/require" 28 "k8s.io/apimachinery/pkg/util/sets" 29 apiservercel "k8s.io/apiserver/pkg/cel" 30 "k8s.io/apiserver/pkg/cel/library" 31 ) 32 33 func testCIDR(t *testing.T, expr string, expectResult ref.Val, expectRuntimeErr string, expectCompileErrs []string) { 34 env, err := cel.NewEnv( 35 library.IP(), 36 library.CIDR(), 37 ) 38 if err != nil { 39 t.Fatalf("%v", err) 40 } 41 42 compiled, issues := env.Compile(expr) 43 44 if len(expectCompileErrs) > 0 { 45 missingCompileErrs := []string{} 46 matchedCompileErrs := sets.New[int]() 47 for _, expectedCompileErr := range expectCompileErrs { 48 compiledPattern, err := regexp.Compile(expectedCompileErr) 49 if err != nil { 50 t.Fatalf("failed to compile expected err regex: %v", err) 51 } 52 53 didMatch := false 54 55 for i, compileError := range issues.Errors() { 56 if compiledPattern.Match([]byte(compileError.Message)) { 57 didMatch = true 58 matchedCompileErrs.Insert(i) 59 } 60 } 61 62 if !didMatch { 63 missingCompileErrs = append(missingCompileErrs, expectedCompileErr) 64 } else if len(matchedCompileErrs) != len(issues.Errors()) { 65 unmatchedErrs := []cel.Error{} 66 for i, issue := range issues.Errors() { 67 if !matchedCompileErrs.Has(i) { 68 unmatchedErrs = append(unmatchedErrs, *issue) 69 } 70 } 71 require.Empty(t, unmatchedErrs, "unexpected compilation errors") 72 } 73 } 74 75 require.Empty(t, missingCompileErrs, "expected compilation errors") 76 return 77 } else if len(issues.Errors()) > 0 { 78 t.Fatalf("%v", issues.Errors()) 79 } 80 81 prog, err := env.Program(compiled) 82 if err != nil { 83 t.Fatalf("%v", err) 84 } 85 res, _, err := prog.Eval(map[string]interface{}{}) 86 if len(expectRuntimeErr) > 0 { 87 if err == nil { 88 t.Fatalf("no runtime error thrown. Expected: %v", expectRuntimeErr) 89 } else if expectRuntimeErr != err.Error() { 90 t.Fatalf("unexpected err: %v", err) 91 } 92 } else if err != nil { 93 t.Fatalf("%v", err) 94 } else if expectResult != nil { 95 converted := res.Equal(expectResult).Value().(bool) 96 require.True(t, converted, "expectation not equal to output") 97 } else { 98 t.Fatal("expected result must not be nil") 99 } 100 } 101 102 func TestCIDR(t *testing.T) { 103 ipv4CIDR, _ := netip.ParsePrefix("192.168.0.0/24") 104 ipv4Addr, _ := netip.ParseAddr("192.168.0.0") 105 106 ipv6CIDR, _ := netip.ParsePrefix("2001:db8::/32") 107 ipv6Addr, _ := netip.ParseAddr("2001:db8::") 108 109 trueVal := types.Bool(true) 110 falseVal := types.Bool(false) 111 112 cases := []struct { 113 name string 114 expr string 115 expectResult ref.Val 116 expectRuntimeErr string 117 expectCompileErrs []string 118 }{ 119 { 120 name: "parse ipv4", 121 expr: `cidr("192.168.0.0/24")`, 122 expectResult: apiservercel.CIDR{Prefix: ipv4CIDR}, 123 }, 124 { 125 name: "parse invalid ipv4", 126 expr: `cidr("192.168.0.0/")`, 127 expectRuntimeErr: "network address parse error during conversion from string: network address parse error during conversion from string: netip.ParsePrefix(\"192.168.0.0/\"): bad bits after slash: \"\"", 128 }, 129 { 130 name: "contains IP ipv4 (IP)", 131 expr: `cidr("192.168.0.0/24").containsIP(ip("192.168.0.1"))`, 132 expectResult: trueVal, 133 }, 134 { 135 name: "does not contain IP ipv4 (IP)", 136 expr: `cidr("192.168.0.0/24").containsIP(ip("192.168.1.1"))`, 137 expectResult: falseVal, 138 }, 139 { 140 name: "contains IP ipv4 (string)", 141 expr: `cidr("192.168.0.0/24").containsIP("192.168.0.1")`, 142 expectResult: trueVal, 143 }, 144 { 145 name: "does not contain IP ipv4 (string)", 146 expr: `cidr("192.168.0.0/24").containsIP("192.168.1.1")`, 147 expectResult: falseVal, 148 }, 149 { 150 name: "contains CIDR ipv4 (CIDR)", 151 expr: `cidr("192.168.0.0/24").containsCIDR(cidr("192.168.0.0/25"))`, 152 expectResult: trueVal, 153 }, 154 { 155 name: "does not contain IP ipv4 (CIDR)", 156 expr: `cidr("192.168.0.0/24").containsCIDR(cidr("192.168.0.0/23"))`, 157 expectResult: falseVal, 158 }, 159 { 160 name: "contains CIDR ipv4 (string)", 161 expr: `cidr("192.168.0.0/24").containsCIDR("192.168.0.0/25")`, 162 expectResult: trueVal, 163 }, 164 { 165 name: "does not contain CIDR ipv4 (string)", 166 expr: `cidr("192.168.0.0/24").containsCIDR("192.168.0.0/23")`, 167 expectResult: falseVal, 168 }, 169 { 170 name: "returns IP ipv4", 171 expr: `cidr("192.168.0.0/24").ip()`, 172 expectResult: apiservercel.IP{Addr: ipv4Addr}, 173 }, 174 { 175 name: "masks masked ipv4", 176 expr: `cidr("192.168.0.0/24").masked()`, 177 expectResult: apiservercel.CIDR{Prefix: netip.PrefixFrom(ipv4Addr, 24)}, 178 }, 179 { 180 name: "masks unmasked ipv4", 181 expr: `cidr("192.168.0.1/24").masked()`, 182 expectResult: apiservercel.CIDR{Prefix: netip.PrefixFrom(ipv4Addr, 24)}, 183 }, 184 { 185 name: "returns prefix length ipv4", 186 expr: `cidr("192.168.0.0/24").prefixLength()`, 187 expectResult: types.Int(24), 188 }, 189 { 190 name: "parse ipv6", 191 expr: `cidr("2001:db8::/32")`, 192 expectResult: apiservercel.CIDR{Prefix: ipv6CIDR}, 193 }, 194 { 195 name: "parse invalid ipv6", 196 expr: `cidr("2001:db8::/")`, 197 expectRuntimeErr: "network address parse error during conversion from string: network address parse error during conversion from string: netip.ParsePrefix(\"2001:db8::/\"): bad bits after slash: \"\"", 198 }, 199 { 200 name: "contains IP ipv6 (IP)", 201 expr: `cidr("2001:db8::/32").containsIP(ip("2001:db8::1"))`, 202 expectResult: trueVal, 203 }, 204 { 205 name: "does not contain IP ipv6 (IP)", 206 expr: `cidr("2001:db8::/32").containsIP(ip("2001:dc8::1"))`, 207 expectResult: falseVal, 208 }, 209 { 210 name: "contains IP ipv6 (string)", 211 expr: `cidr("2001:db8::/32").containsIP("2001:db8::1")`, 212 expectResult: trueVal, 213 }, 214 { 215 name: "does not contain IP ipv6 (string)", 216 expr: `cidr("2001:db8::/32").containsIP("2001:dc8::1")`, 217 expectResult: falseVal, 218 }, 219 { 220 name: "contains CIDR ipv6 (CIDR)", 221 expr: `cidr("2001:db8::/32").containsCIDR(cidr("2001:db8::/33"))`, 222 expectResult: trueVal, 223 }, 224 { 225 name: "does not contain IP ipv6 (CIDR)", 226 expr: `cidr("2001:db8::/32").containsCIDR(cidr("2001:db8::/31"))`, 227 expectResult: falseVal, 228 }, 229 { 230 name: "contains CIDR ipv6 (string)", 231 expr: `cidr("2001:db8::/32").containsCIDR("2001:db8::/33")`, 232 expectResult: trueVal, 233 }, 234 { 235 name: "does not contain CIDR ipv6 (string)", 236 expr: `cidr("2001:db8::/32").containsCIDR("2001:db8::/31")`, 237 expectResult: falseVal, 238 }, 239 { 240 name: "returns IP ipv6", 241 expr: `cidr("2001:db8::/32").ip()`, 242 expectResult: apiservercel.IP{Addr: ipv6Addr}, 243 }, 244 { 245 name: "masks masked ipv6", 246 expr: `cidr("2001:db8::/32").masked()`, 247 expectResult: apiservercel.CIDR{Prefix: netip.PrefixFrom(ipv6Addr, 32)}, 248 }, 249 { 250 name: "masks unmasked ipv6", 251 expr: `cidr("2001:db8:1::/32").masked()`, 252 expectResult: apiservercel.CIDR{Prefix: netip.PrefixFrom(ipv6Addr, 32)}, 253 }, 254 { 255 name: "returns prefix length ipv6", 256 expr: `cidr("2001:db8::/32").prefixLength()`, 257 expectResult: types.Int(32), 258 }, 259 { 260 name: "converting a CIDR to a string", 261 expr: `string(cidr("192.168.0.0/24"))`, 262 expectResult: types.String("192.168.0.0/24"), 263 }, 264 { 265 name: "type of CIDR is net.CIDR", 266 expr: `type(cidr("192.168.0.0/24")) == net.CIDR`, 267 expectResult: trueVal, 268 }, 269 } 270 271 for _, tc := range cases { 272 t.Run(tc.name, func(t *testing.T) { 273 testCIDR(t, tc.expr, tc.expectResult, tc.expectRuntimeErr, tc.expectCompileErrs) 274 }) 275 } 276 }