github.com/terramate-io/tf@v0.0.0-20230830114523-fce866b4dfcd/lang/funcs/cidr_test.go (about) 1 // Copyright (c) HashiCorp, Inc. 2 // SPDX-License-Identifier: MPL-2.0 3 4 package funcs 5 6 import ( 7 "fmt" 8 "testing" 9 10 "github.com/zclconf/go-cty/cty" 11 ) 12 13 func TestCidrHost(t *testing.T) { 14 tests := []struct { 15 Prefix cty.Value 16 Hostnum cty.Value 17 Want cty.Value 18 Err bool 19 }{ 20 { 21 cty.StringVal("192.168.1.0/24"), 22 cty.NumberIntVal(5), 23 cty.StringVal("192.168.1.5"), 24 false, 25 }, 26 { 27 cty.StringVal("192.168.1.0/24"), 28 cty.NumberIntVal(-5), 29 cty.StringVal("192.168.1.251"), 30 false, 31 }, 32 { 33 cty.StringVal("192.168.1.0/24"), 34 cty.NumberIntVal(-256), 35 cty.StringVal("192.168.1.0"), 36 false, 37 }, 38 { 39 // We inadvertently inherited a pre-Go1.17 standard library quirk 40 // if parsing zero-prefix parts as decimal rather than octal. 41 // Go 1.17 resolved that quirk by making zero-prefix invalid, but 42 // we've preserved our existing behavior for backward compatibility, 43 // on the grounds that these functions are for generating addresses 44 // rather than validating or processing them. We do always generate 45 // a canonical result regardless of the input, though. 46 cty.StringVal("010.001.0.0/24"), 47 cty.NumberIntVal(6), 48 cty.StringVal("10.1.0.6"), 49 false, 50 }, 51 { 52 cty.StringVal("192.168.1.0/30"), 53 cty.NumberIntVal(255), 54 cty.UnknownVal(cty.String), 55 true, // 255 doesn't fit in two bits 56 }, 57 { 58 cty.StringVal("192.168.1.0/30"), 59 cty.NumberIntVal(-255), 60 cty.UnknownVal(cty.String), 61 true, // 255 doesn't fit in two bits 62 }, 63 { 64 cty.StringVal("not-a-cidr"), 65 cty.NumberIntVal(6), 66 cty.UnknownVal(cty.String), 67 true, // not a valid CIDR mask 68 }, 69 { 70 cty.StringVal("10.256.0.0/8"), 71 cty.NumberIntVal(6), 72 cty.UnknownVal(cty.String), 73 true, // can't have an octet >255 74 }, 75 { // fractions are Not Ok 76 cty.StringVal("10.256.0.0/8"), 77 cty.NumberFloatVal(.75), 78 cty.UnknownVal(cty.String), 79 true, 80 }, 81 } 82 83 for _, test := range tests { 84 t.Run(fmt.Sprintf("cidrhost(%#v, %#v)", test.Prefix, test.Hostnum), func(t *testing.T) { 85 got, err := CidrHost(test.Prefix, test.Hostnum) 86 87 if test.Err { 88 if err == nil { 89 t.Fatal("succeeded; want error") 90 } 91 return 92 } else if err != nil { 93 t.Fatalf("unexpected error: %s", err) 94 } 95 96 if !got.RawEquals(test.Want) { 97 t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.Want) 98 } 99 }) 100 } 101 } 102 103 func TestCidrNetmask(t *testing.T) { 104 tests := []struct { 105 Prefix cty.Value 106 Want cty.Value 107 Err bool 108 }{ 109 { 110 cty.StringVal("192.168.1.0/24"), 111 cty.StringVal("255.255.255.0"), 112 false, 113 }, 114 { 115 cty.StringVal("192.168.1.0/32"), 116 cty.StringVal("255.255.255.255"), 117 false, 118 }, 119 { 120 cty.StringVal("0.0.0.0/0"), 121 cty.StringVal("0.0.0.0"), 122 false, 123 }, 124 { 125 // We inadvertently inherited a pre-Go1.17 standard library quirk 126 // if parsing zero-prefix parts as decimal rather than octal. 127 // Go 1.17 resolved that quirk by making zero-prefix invalid, but 128 // we've preserved our existing behavior for backward compatibility, 129 // on the grounds that these functions are for generating addresses 130 // rather than validating or processing them. 131 cty.StringVal("010.001.0.0/24"), 132 cty.StringVal("255.255.255.0"), 133 false, 134 }, 135 { 136 cty.StringVal("not-a-cidr"), 137 cty.UnknownVal(cty.String), 138 true, // not a valid CIDR mask 139 }, 140 { 141 cty.StringVal("110.256.0.0/8"), 142 cty.UnknownVal(cty.String), 143 true, // can't have an octet >255 144 }, 145 { 146 cty.StringVal("1::/64"), 147 cty.UnknownVal(cty.String), 148 true, // IPv6 is invalid 149 }, 150 } 151 152 for _, test := range tests { 153 t.Run(fmt.Sprintf("cidrnetmask(%#v)", test.Prefix), func(t *testing.T) { 154 got, err := CidrNetmask(test.Prefix) 155 156 if test.Err { 157 if err == nil { 158 t.Fatal("succeeded; want error") 159 } 160 return 161 } else if err != nil { 162 t.Fatalf("unexpected error: %s", err) 163 } 164 165 if !got.RawEquals(test.Want) { 166 t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.Want) 167 } 168 }) 169 } 170 } 171 172 func TestCidrSubnet(t *testing.T) { 173 tests := []struct { 174 Prefix cty.Value 175 Newbits cty.Value 176 Netnum cty.Value 177 Want cty.Value 178 Err bool 179 }{ 180 { 181 cty.StringVal("192.168.2.0/20"), 182 cty.NumberIntVal(4), 183 cty.NumberIntVal(6), 184 cty.StringVal("192.168.6.0/24"), 185 false, 186 }, 187 { 188 cty.StringVal("fe80::/48"), 189 cty.NumberIntVal(16), 190 cty.NumberIntVal(6), 191 cty.StringVal("fe80:0:0:6::/64"), 192 false, 193 }, 194 { // IPv4 address encoded in IPv6 syntax gets normalized 195 cty.StringVal("::ffff:192.168.0.0/112"), 196 cty.NumberIntVal(8), 197 cty.NumberIntVal(6), 198 cty.StringVal("192.168.6.0/24"), 199 false, 200 }, 201 { 202 cty.StringVal("fe80::/48"), 203 cty.NumberIntVal(33), 204 cty.NumberIntVal(6), 205 cty.StringVal("fe80::3:0:0:0/81"), 206 false, 207 }, 208 { 209 // We inadvertently inherited a pre-Go1.17 standard library quirk 210 // if parsing zero-prefix parts as decimal rather than octal. 211 // Go 1.17 resolved that quirk by making zero-prefix invalid, but 212 // we've preserved our existing behavior for backward compatibility, 213 // on the grounds that these functions are for generating addresses 214 // rather than validating or processing them. We do always generate 215 // a canonical result regardless of the input, though. 216 cty.StringVal("010.001.0.0/24"), 217 cty.NumberIntVal(4), 218 cty.NumberIntVal(1), 219 cty.StringVal("10.1.0.16/28"), 220 false, 221 }, 222 { // not enough bits left 223 cty.StringVal("192.168.0.0/30"), 224 cty.NumberIntVal(4), 225 cty.NumberIntVal(6), 226 cty.UnknownVal(cty.String), 227 true, 228 }, 229 { // can't encode 16 in 2 bits 230 cty.StringVal("192.168.0.0/168"), 231 cty.NumberIntVal(2), 232 cty.NumberIntVal(16), 233 cty.UnknownVal(cty.String), 234 true, 235 }, 236 { // not a valid CIDR mask 237 cty.StringVal("not-a-cidr"), 238 cty.NumberIntVal(4), 239 cty.NumberIntVal(6), 240 cty.UnknownVal(cty.String), 241 true, 242 }, 243 { // can't have an octet >255 244 cty.StringVal("10.256.0.0/8"), 245 cty.NumberIntVal(4), 246 cty.NumberIntVal(6), 247 cty.UnknownVal(cty.String), 248 true, 249 }, 250 { // fractions are Not Ok 251 cty.StringVal("10.256.0.0/8"), 252 cty.NumberFloatVal(2.0 / 3.0), 253 cty.NumberFloatVal(.75), 254 cty.UnknownVal(cty.String), 255 true, 256 }, 257 } 258 259 for _, test := range tests { 260 t.Run(fmt.Sprintf("cidrsubnet(%#v, %#v, %#v)", test.Prefix, test.Newbits, test.Netnum), func(t *testing.T) { 261 got, err := CidrSubnet(test.Prefix, test.Newbits, test.Netnum) 262 263 if test.Err { 264 if err == nil { 265 t.Fatal("succeeded; want error") 266 } 267 return 268 } else if err != nil { 269 t.Fatalf("unexpected error: %s", err) 270 } 271 272 if !got.RawEquals(test.Want) { 273 t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.Want) 274 } 275 }) 276 } 277 } 278 func TestCidrSubnets(t *testing.T) { 279 tests := []struct { 280 Prefix cty.Value 281 Newbits []cty.Value 282 Want cty.Value 283 Err string 284 }{ 285 { 286 cty.StringVal("10.0.0.0/21"), 287 []cty.Value{ 288 cty.NumberIntVal(3), 289 cty.NumberIntVal(3), 290 cty.NumberIntVal(3), 291 cty.NumberIntVal(4), 292 cty.NumberIntVal(4), 293 cty.NumberIntVal(4), 294 cty.NumberIntVal(7), 295 cty.NumberIntVal(7), 296 cty.NumberIntVal(7), 297 }, 298 cty.ListVal([]cty.Value{ 299 cty.StringVal("10.0.0.0/24"), 300 cty.StringVal("10.0.1.0/24"), 301 cty.StringVal("10.0.2.0/24"), 302 cty.StringVal("10.0.3.0/25"), 303 cty.StringVal("10.0.3.128/25"), 304 cty.StringVal("10.0.4.0/25"), 305 cty.StringVal("10.0.4.128/28"), 306 cty.StringVal("10.0.4.144/28"), 307 cty.StringVal("10.0.4.160/28"), 308 }), 309 ``, 310 }, 311 { 312 // We inadvertently inherited a pre-Go1.17 standard library quirk 313 // if parsing zero-prefix parts as decimal rather than octal. 314 // Go 1.17 resolved that quirk by making zero-prefix invalid, but 315 // we've preserved our existing behavior for backward compatibility, 316 // on the grounds that these functions are for generating addresses 317 // rather than validating or processing them. We do always generate 318 // a canonical result regardless of the input, though. 319 cty.StringVal("010.0.0.0/21"), 320 []cty.Value{ 321 cty.NumberIntVal(3), 322 }, 323 cty.ListVal([]cty.Value{ 324 cty.StringVal("10.0.0.0/24"), 325 }), 326 ``, 327 }, 328 { 329 cty.StringVal("10.0.0.0/30"), 330 []cty.Value{ 331 cty.NumberIntVal(1), 332 cty.NumberIntVal(3), 333 }, 334 cty.UnknownVal(cty.List(cty.String)), 335 `would extend prefix to 33 bits, which is too long for an IPv4 address`, 336 }, 337 { 338 cty.StringVal("10.0.0.0/8"), 339 []cty.Value{ 340 cty.NumberIntVal(1), 341 cty.NumberIntVal(1), 342 cty.NumberIntVal(1), 343 }, 344 cty.UnknownVal(cty.List(cty.String)), 345 `not enough remaining address space for a subnet with a prefix of 9 bits after 10.128.0.0/9`, 346 }, 347 { 348 cty.StringVal("10.0.0.0/8"), 349 []cty.Value{ 350 cty.NumberIntVal(1), 351 cty.NumberIntVal(0), 352 }, 353 cty.UnknownVal(cty.List(cty.String)), 354 `must extend prefix by at least one bit`, 355 }, 356 { 357 cty.StringVal("10.0.0.0/8"), 358 []cty.Value{ 359 cty.NumberIntVal(1), 360 cty.NumberIntVal(-1), 361 }, 362 cty.UnknownVal(cty.List(cty.String)), 363 `must extend prefix by at least one bit`, 364 }, 365 { 366 cty.StringVal("fe80::/48"), 367 []cty.Value{ 368 cty.NumberIntVal(1), 369 cty.NumberIntVal(33), 370 }, 371 cty.UnknownVal(cty.List(cty.String)), 372 `may not extend prefix by more than 32 bits`, 373 }, 374 } 375 376 for _, test := range tests { 377 t.Run(fmt.Sprintf("cidrsubnets(%#v, %#v)", test.Prefix, test.Newbits), func(t *testing.T) { 378 got, err := CidrSubnets(test.Prefix, test.Newbits...) 379 wantErr := test.Err != "" 380 381 if wantErr { 382 if err == nil { 383 t.Fatal("succeeded; want error") 384 } 385 if err.Error() != test.Err { 386 t.Fatalf("wrong error\ngot: %s\nwant: %s", err.Error(), test.Err) 387 } 388 return 389 } else if err != nil { 390 t.Fatalf("unexpected error: %s", err) 391 } 392 393 if !got.RawEquals(test.Want) { 394 t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.Want) 395 } 396 }) 397 } 398 }