github.com/rumpl/bof@v23.0.0-rc.2+incompatible/libnetwork/etchosts/etchosts_test.go (about) 1 package etchosts 2 3 import ( 4 "bytes" 5 "fmt" 6 "os" 7 "testing" 8 9 "golang.org/x/sync/errgroup" 10 ) 11 12 func TestBuildDefault(t *testing.T) { 13 file, err := os.CreateTemp("", "") 14 if err != nil { 15 t.Fatal(err) 16 } 17 defer os.Remove(file.Name()) 18 19 // check that /etc/hosts has consistent ordering 20 for i := 0; i <= 5; i++ { 21 err = Build(file.Name(), "", "", "", nil) 22 if err != nil { 23 t.Fatal(err) 24 } 25 26 content, err := os.ReadFile(file.Name()) 27 if err != nil { 28 t.Fatal(err) 29 } 30 expected := "127.0.0.1\tlocalhost\n::1\tlocalhost ip6-localhost ip6-loopback\nfe00::0\tip6-localnet\nff00::0\tip6-mcastprefix\nff02::1\tip6-allnodes\nff02::2\tip6-allrouters\n" 31 32 if expected != string(content) { 33 t.Fatalf("Expected to find '%s' got '%s'", expected, content) 34 } 35 } 36 } 37 38 func TestBuildHostnameDomainname(t *testing.T) { 39 file, err := os.CreateTemp("", "") 40 if err != nil { 41 t.Fatal(err) 42 } 43 defer os.Remove(file.Name()) 44 45 err = Build(file.Name(), "10.11.12.13", "testhostname", "testdomainname", nil) 46 if err != nil { 47 t.Fatal(err) 48 } 49 50 content, err := os.ReadFile(file.Name()) 51 if err != nil { 52 t.Fatal(err) 53 } 54 55 if expected := "10.11.12.13\ttesthostname.testdomainname testhostname\n"; !bytes.Contains(content, []byte(expected)) { 56 t.Fatalf("Expected to find '%s' got '%s'", expected, content) 57 } 58 } 59 60 func TestBuildHostname(t *testing.T) { 61 file, err := os.CreateTemp("", "") 62 if err != nil { 63 t.Fatal(err) 64 } 65 defer os.Remove(file.Name()) 66 67 err = Build(file.Name(), "10.11.12.13", "testhostname", "", nil) 68 if err != nil { 69 t.Fatal(err) 70 } 71 72 content, err := os.ReadFile(file.Name()) 73 if err != nil { 74 t.Fatal(err) 75 } 76 77 if expected := "10.11.12.13\ttesthostname\n"; !bytes.Contains(content, []byte(expected)) { 78 t.Fatalf("Expected to find '%s' got '%s'", expected, content) 79 } 80 } 81 82 func TestBuildHostnameFQDN(t *testing.T) { 83 file, err := os.CreateTemp("", "") 84 if err != nil { 85 t.Fatal(err) 86 } 87 defer os.Remove(file.Name()) 88 89 err = Build(file.Name(), "10.11.12.13", "testhostname.testdomainname.com", "", nil) 90 if err != nil { 91 t.Fatal(err) 92 } 93 94 content, err := os.ReadFile(file.Name()) 95 if err != nil { 96 t.Fatal(err) 97 } 98 99 if expected := "10.11.12.13\ttesthostname.testdomainname.com testhostname\n"; !bytes.Contains(content, []byte(expected)) { 100 t.Fatalf("Expected to find '%s' got '%s'", expected, content) 101 } 102 } 103 104 func TestBuildNoIP(t *testing.T) { 105 file, err := os.CreateTemp("", "") 106 if err != nil { 107 t.Fatal(err) 108 } 109 defer os.Remove(file.Name()) 110 111 err = Build(file.Name(), "", "testhostname", "", nil) 112 if err != nil { 113 t.Fatal(err) 114 } 115 116 content, err := os.ReadFile(file.Name()) 117 if err != nil { 118 t.Fatal(err) 119 } 120 121 if expected := ""; !bytes.Contains(content, []byte(expected)) { 122 t.Fatalf("Expected to find '%s' got '%s'", expected, content) 123 } 124 } 125 126 func TestUpdate(t *testing.T) { 127 file, err := os.CreateTemp("", "") 128 if err != nil { 129 t.Fatal(err) 130 } 131 defer os.Remove(file.Name()) 132 133 if err := Build(file.Name(), "10.11.12.13", "testhostname", "testdomainname", nil); err != nil { 134 t.Fatal(err) 135 } 136 137 content, err := os.ReadFile(file.Name()) 138 if err != nil { 139 t.Fatal(err) 140 } 141 142 if expected := "10.11.12.13\ttesthostname.testdomainname testhostname\n"; !bytes.Contains(content, []byte(expected)) { 143 t.Fatalf("Expected to find '%s' got '%s'", expected, content) 144 } 145 146 if err := Update(file.Name(), "1.1.1.1", "testhostname"); err != nil { 147 t.Fatal(err) 148 } 149 150 content, err = os.ReadFile(file.Name()) 151 if err != nil { 152 t.Fatal(err) 153 } 154 155 if expected := "1.1.1.1\ttesthostname.testdomainname testhostname\n"; !bytes.Contains(content, []byte(expected)) { 156 t.Fatalf("Expected to find '%s' got '%s'", expected, content) 157 } 158 } 159 160 // This regression test ensures that when a host is given a new IP 161 // via the Update function that other hosts which start with the 162 // same name as the targeted host are not erroneously updated as well. 163 // In the test example, if updating a host called "prefix", unrelated 164 // hosts named "prefixAndMore" or "prefix2" or anything else starting 165 // with "prefix" should not be changed. For more information see 166 // GitHub issue #603. 167 func TestUpdateIgnoresPrefixedHostname(t *testing.T) { 168 file, err := os.CreateTemp("", "") 169 if err != nil { 170 t.Fatal(err) 171 } 172 defer os.Remove(file.Name()) 173 174 if err := Build(file.Name(), "10.11.12.13", "testhostname", "testdomainname", []Record{ 175 { 176 Hosts: "prefix", 177 IP: "2.2.2.2", 178 }, 179 { 180 Hosts: "prefixAndMore", 181 IP: "3.3.3.3", 182 }, 183 { 184 Hosts: "unaffectedHost", 185 IP: "4.4.4.4", 186 }, 187 }); err != nil { 188 t.Fatal(err) 189 } 190 191 content, err := os.ReadFile(file.Name()) 192 if err != nil { 193 t.Fatal(err) 194 } 195 196 if expected := "2.2.2.2\tprefix\n3.3.3.3\tprefixAndMore\n4.4.4.4\tunaffectedHost\n"; !bytes.Contains(content, []byte(expected)) { 197 t.Fatalf("Expected to find '%s' got '%s'", expected, content) 198 } 199 200 if err := Update(file.Name(), "5.5.5.5", "prefix"); err != nil { 201 t.Fatal(err) 202 } 203 204 content, err = os.ReadFile(file.Name()) 205 if err != nil { 206 t.Fatal(err) 207 } 208 209 if expected := "5.5.5.5\tprefix\n3.3.3.3\tprefixAndMore\n4.4.4.4\tunaffectedHost\n"; !bytes.Contains(content, []byte(expected)) { 210 t.Fatalf("Expected to find '%s' got '%s'", expected, content) 211 } 212 213 } 214 215 // This regression test covers the host prefix issue for the 216 // Delete function. In the test example, if deleting a host called 217 // "prefix", an unrelated host called "prefixAndMore" should not 218 // be deleted. For more information see GitHub issue #603. 219 func TestDeleteIgnoresPrefixedHostname(t *testing.T) { 220 file, err := os.CreateTemp("", "") 221 if err != nil { 222 t.Fatal(err) 223 } 224 defer os.Remove(file.Name()) 225 226 err = Build(file.Name(), "", "", "", nil) 227 if err != nil { 228 t.Fatal(err) 229 } 230 231 if err := Add(file.Name(), []Record{ 232 { 233 Hosts: "prefix", 234 IP: "1.1.1.1", 235 }, 236 { 237 Hosts: "prefixAndMore", 238 IP: "2.2.2.2", 239 }, 240 }); err != nil { 241 t.Fatal(err) 242 } 243 244 if err := Delete(file.Name(), []Record{ 245 { 246 Hosts: "prefix", 247 IP: "1.1.1.1", 248 }, 249 }); err != nil { 250 t.Fatal(err) 251 } 252 253 content, err := os.ReadFile(file.Name()) 254 if err != nil { 255 t.Fatal(err) 256 } 257 258 if expected := "2.2.2.2\tprefixAndMore\n"; !bytes.Contains(content, []byte(expected)) { 259 t.Fatalf("Expected to find '%s' got '%s'", expected, content) 260 } 261 262 if expected := "1.1.1.1\tprefix\n"; bytes.Contains(content, []byte(expected)) { 263 t.Fatalf("Did not expect to find '%s' got '%s'", expected, content) 264 } 265 } 266 267 func TestAddEmpty(t *testing.T) { 268 file, err := os.CreateTemp("", "") 269 if err != nil { 270 t.Fatal(err) 271 } 272 defer os.Remove(file.Name()) 273 274 err = Build(file.Name(), "", "", "", nil) 275 if err != nil { 276 t.Fatal(err) 277 } 278 279 if err := Add(file.Name(), []Record{}); err != nil { 280 t.Fatal(err) 281 } 282 } 283 284 func TestAdd(t *testing.T) { 285 file, err := os.CreateTemp("", "") 286 if err != nil { 287 t.Fatal(err) 288 } 289 defer os.Remove(file.Name()) 290 291 err = Build(file.Name(), "", "", "", nil) 292 if err != nil { 293 t.Fatal(err) 294 } 295 296 if err := Add(file.Name(), []Record{ 297 { 298 Hosts: "testhostname", 299 IP: "2.2.2.2", 300 }, 301 }); err != nil { 302 t.Fatal(err) 303 } 304 305 content, err := os.ReadFile(file.Name()) 306 if err != nil { 307 t.Fatal(err) 308 } 309 310 if expected := "2.2.2.2\ttesthostname\n"; !bytes.Contains(content, []byte(expected)) { 311 t.Fatalf("Expected to find '%s' got '%s'", expected, content) 312 } 313 } 314 315 func TestDeleteEmpty(t *testing.T) { 316 file, err := os.CreateTemp("", "") 317 if err != nil { 318 t.Fatal(err) 319 } 320 defer os.Remove(file.Name()) 321 322 err = Build(file.Name(), "", "", "", nil) 323 if err != nil { 324 t.Fatal(err) 325 } 326 327 if err := Delete(file.Name(), []Record{}); err != nil { 328 t.Fatal(err) 329 } 330 } 331 332 func TestDeleteNewline(t *testing.T) { 333 file, err := os.CreateTemp("", "") 334 if err != nil { 335 t.Fatal(err) 336 } 337 defer os.Remove(file.Name()) 338 339 b := []byte("\n") 340 if _, err := file.Write(b); err != nil { 341 t.Fatal(err) 342 } 343 344 rec := []Record{ 345 { 346 Hosts: "prefix", 347 IP: "2.2.2.2", 348 }, 349 } 350 if err := Delete(file.Name(), rec); err != nil { 351 t.Fatal(err) 352 } 353 } 354 355 func TestDelete(t *testing.T) { 356 file, err := os.CreateTemp("", "") 357 if err != nil { 358 t.Fatal(err) 359 } 360 defer os.Remove(file.Name()) 361 362 err = Build(file.Name(), "", "", "", nil) 363 if err != nil { 364 t.Fatal(err) 365 } 366 367 if err := Add(file.Name(), []Record{ 368 { 369 Hosts: "testhostname1", 370 IP: "1.1.1.1", 371 }, 372 { 373 Hosts: "testhostname2", 374 IP: "2.2.2.2", 375 }, 376 { 377 Hosts: "testhostname3", 378 IP: "3.3.3.3", 379 }, 380 }); err != nil { 381 t.Fatal(err) 382 } 383 384 if err := Delete(file.Name(), []Record{ 385 { 386 Hosts: "testhostname1", 387 IP: "1.1.1.1", 388 }, 389 { 390 Hosts: "testhostname3", 391 IP: "3.3.3.3", 392 }, 393 }); err != nil { 394 t.Fatal(err) 395 } 396 397 content, err := os.ReadFile(file.Name()) 398 if err != nil { 399 t.Fatal(err) 400 } 401 402 if expected := "2.2.2.2\ttesthostname2\n"; !bytes.Contains(content, []byte(expected)) { 403 t.Fatalf("Expected to find '%s' got '%s'", expected, content) 404 } 405 406 if expected := "1.1.1.1\ttesthostname1\n"; bytes.Contains(content, []byte(expected)) { 407 t.Fatalf("Did not expect to find '%s' got '%s'", expected, content) 408 } 409 } 410 411 func TestConcurrentWrites(t *testing.T) { 412 file, err := os.CreateTemp("", "") 413 if err != nil { 414 t.Fatal(err) 415 } 416 defer os.Remove(file.Name()) 417 418 err = Build(file.Name(), "", "", "", nil) 419 if err != nil { 420 t.Fatal(err) 421 } 422 423 if err := Add(file.Name(), []Record{ 424 { 425 Hosts: "inithostname", 426 IP: "172.17.0.1", 427 }, 428 }); err != nil { 429 t.Fatal(err) 430 } 431 432 group := new(errgroup.Group) 433 for i := 0; i < 10; i++ { 434 i := i 435 group.Go(func() error { 436 rec := []Record{ 437 { 438 IP: fmt.Sprintf("%d.%d.%d.%d", i, i, i, i), 439 Hosts: fmt.Sprintf("testhostname%d", i), 440 }, 441 } 442 443 for j := 0; j < 25; j++ { 444 if err := Add(file.Name(), rec); err != nil { 445 return err 446 } 447 448 if err := Delete(file.Name(), rec); err != nil { 449 return err 450 } 451 } 452 return nil 453 }) 454 } 455 456 if err := group.Wait(); err != nil { 457 t.Fatal(err) 458 } 459 460 content, err := os.ReadFile(file.Name()) 461 if err != nil { 462 t.Fatal(err) 463 } 464 465 if expected := "172.17.0.1\tinithostname\n"; !bytes.Contains(content, []byte(expected)) { 466 t.Fatalf("Expected to find '%s' got '%s'", expected, content) 467 } 468 } 469 470 func benchDelete(b *testing.B) { 471 b.StopTimer() 472 file, err := os.CreateTemp("", "") 473 if err != nil { 474 b.Fatal(err) 475 } 476 defer func() { 477 b.StopTimer() 478 file.Close() 479 os.Remove(file.Name()) 480 b.StartTimer() 481 }() 482 483 err = Build(file.Name(), "", "", "", nil) 484 if err != nil { 485 b.Fatal(err) 486 } 487 488 var records []Record 489 var toDelete []Record 490 for i := 0; i < 255; i++ { 491 record := Record{ 492 Hosts: fmt.Sprintf("testhostname%d", i), 493 IP: fmt.Sprintf("%d.%d.%d.%d", i, i, i, i), 494 } 495 records = append(records, record) 496 if i%2 == 0 { 497 toDelete = append(records, record) 498 } 499 } 500 501 if err := Add(file.Name(), records); err != nil { 502 b.Fatal(err) 503 } 504 505 b.StartTimer() 506 if err := Delete(file.Name(), toDelete); err != nil { 507 b.Fatal(err) 508 } 509 } 510 511 func BenchmarkDelete(b *testing.B) { 512 for i := 0; i < b.N; i++ { 513 benchDelete(b) 514 } 515 }