github.com/rck/u-root@v0.0.0-20180106144920-7eb602e381bb/pkg/pxe/pxe_test.go (about) 1 package pxe 2 3 import ( 4 "fmt" 5 "io/ioutil" 6 "net" 7 "net/url" 8 "reflect" 9 "testing" 10 ) 11 12 func TestProbeFiles(t *testing.T) { 13 // Anyone got some ideas for other test cases? 14 for _, tt := range []struct { 15 mac net.HardwareAddr 16 ip net.IP 17 files []string 18 }{ 19 { 20 mac: []byte{0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff}, 21 ip: []byte{192, 168, 0, 1}, 22 files: []string{ 23 "01-aa-bb-cc-dd-ee-ff", 24 "C0A80001", 25 "C0A8000", 26 "C0A800", 27 "C0A80", 28 "C0A8", 29 "C0A", 30 "C0", 31 "C", 32 "default", 33 }, 34 }, 35 { 36 mac: []byte{0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd}, 37 ip: []byte{192, 168, 2, 91}, 38 files: []string{ 39 "01-88-99-aa-bb-cc-dd", 40 "C0A8025B", 41 "C0A8025", 42 "C0A802", 43 "C0A80", 44 "C0A8", 45 "C0A", 46 "C0", 47 "C", 48 "default", 49 }, 50 }, 51 } { 52 got := probeFiles(tt.mac, tt.ip) 53 if !reflect.DeepEqual(got, tt.files) { 54 t.Errorf("probeFiles(%s, %s) = %v, want %v", tt.mac, tt.ip, got, tt.files) 55 } 56 } 57 } 58 59 func TestAppendFile(t *testing.T) { 60 content1 := "1111" 61 content2 := "2222" 62 content3 := "3333" 63 content4 := "4444" 64 65 type label struct { 66 kernel string 67 kernelErr error 68 initrd string 69 initrdErr error 70 cmdline string 71 } 72 type config struct { 73 defaultEntry string 74 labels map[string]label 75 } 76 77 for i, tt := range []struct { 78 desc string 79 configFileURI string 80 schemeFunc func() Schemes 81 wd *url.URL 82 config *Config 83 want config 84 err error 85 }{ 86 { 87 desc: "all files exist, simple config with cmdline initrd", 88 configFileURI: "default", 89 schemeFunc: func() Schemes { 90 s := make(Schemes) 91 fs := NewMockScheme("tftp") 92 fs.Add("1.2.3.4", "/foobar/pxelinux.0", "") 93 conf := `default foo 94 label foo 95 kernel ./pxefiles/kernel 96 append initrd=./pxefiles/initrd` 97 fs.Add("1.2.3.4", "/foobar/pxelinux.cfg/default", conf) 98 fs.Add("1.2.3.4", "/foobar/pxefiles/kernel", content1) 99 fs.Add("1.2.3.4", "/foobar/pxefiles/initrd", content2) 100 s.Register(fs.scheme, fs) 101 return s 102 }, 103 wd: &url.URL{ 104 Scheme: "tftp", 105 Host: "1.2.3.4", 106 Path: "/foobar", 107 }, 108 want: config{ 109 defaultEntry: "foo", 110 labels: map[string]label{ 111 "foo": label{ 112 kernel: content1, 113 initrd: content2, 114 cmdline: "initrd=./pxefiles/initrd", 115 }, 116 }, 117 }, 118 }, 119 { 120 desc: "all files exist, simple config with directive initrd", 121 configFileURI: "default", 122 schemeFunc: func() Schemes { 123 s := make(Schemes) 124 fs := NewMockScheme("tftp") 125 fs.Add("1.2.3.4", "/foobar/pxelinux.0", "") 126 conf := `default foo 127 label foo 128 kernel ./pxefiles/kernel 129 initrd ./pxefiles/initrd 130 append foo=bar` 131 fs.Add("1.2.3.4", "/foobar/pxelinux.cfg/default", conf) 132 fs.Add("1.2.3.4", "/foobar/pxefiles/kernel", content1) 133 fs.Add("1.2.3.4", "/foobar/pxefiles/initrd", content2) 134 s.Register(fs.scheme, fs) 135 return s 136 }, 137 wd: &url.URL{ 138 Scheme: "tftp", 139 Host: "1.2.3.4", 140 Path: "/foobar", 141 }, 142 want: config{ 143 defaultEntry: "foo", 144 labels: map[string]label{ 145 "foo": label{ 146 kernel: content1, 147 initrd: content2, 148 cmdline: "foo=bar", 149 }, 150 }, 151 }, 152 }, 153 { 154 desc: "all files exist, simple config, no initrd", 155 configFileURI: "default", 156 schemeFunc: func() Schemes { 157 s := make(Schemes) 158 fs := NewMockScheme("tftp") 159 fs.Add("1.2.3.4", "/foobar/pxelinux.0", "") 160 conf := `default foo 161 label foo 162 kernel ./pxefiles/kernel` 163 fs.Add("1.2.3.4", "/foobar/pxelinux.cfg/default", conf) 164 fs.Add("1.2.3.4", "/foobar/pxefiles/kernel", content1) 165 s.Register(fs.scheme, fs) 166 return s 167 }, 168 wd: &url.URL{ 169 Scheme: "tftp", 170 Host: "1.2.3.4", 171 Path: "/foobar", 172 }, 173 want: config{ 174 defaultEntry: "foo", 175 labels: map[string]label{ 176 "foo": label{ 177 kernel: content1, 178 initrd: "", 179 cmdline: "", 180 }, 181 }, 182 }, 183 }, 184 { 185 desc: "kernel does not exist, simple config", 186 configFileURI: "default", 187 schemeFunc: func() Schemes { 188 s := make(Schemes) 189 fs := NewMockScheme("tftp") 190 fs.Add("1.2.3.4", "/foobar/pxelinux.0", "") 191 conf := `default foo 192 label foo 193 kernel ./pxefiles/kernel` 194 fs.Add("1.2.3.4", "/foobar/pxelinux.cfg/default", conf) 195 s.Register(fs.scheme, fs) 196 return s 197 }, 198 wd: &url.URL{ 199 Scheme: "tftp", 200 Host: "1.2.3.4", 201 Path: "/foobar", 202 }, 203 want: config{ 204 defaultEntry: "foo", 205 labels: map[string]label{ 206 "foo": label{ 207 kernelErr: errNoSuchFile, 208 initrd: "", 209 cmdline: "", 210 }, 211 }, 212 }, 213 }, 214 { 215 desc: "config file does not exist", 216 configFileURI: "default", 217 schemeFunc: func() Schemes { 218 s := make(Schemes) 219 fs := NewMockScheme("tftp") 220 s.Register(fs.scheme, fs) 221 return s 222 }, 223 wd: &url.URL{ 224 Scheme: "tftp", 225 Host: "1.2.3.4", 226 Path: "/foobar", 227 }, 228 err: ErrConfigNotFound, 229 }, 230 { 231 desc: "empty config", 232 configFileURI: "default", 233 schemeFunc: func() Schemes { 234 s := make(Schemes) 235 fs := NewMockScheme("tftp") 236 fs.Add("1.2.3.4", "/foobar/pxelinux.cfg/default", "") 237 s.Register(fs.scheme, fs) 238 return s 239 }, 240 wd: &url.URL{ 241 Scheme: "tftp", 242 Host: "1.2.3.4", 243 Path: "/foobar", 244 }, 245 want: config{ 246 defaultEntry: "", 247 }, 248 }, 249 { 250 desc: "valid config with two labels", 251 configFileURI: "default", 252 schemeFunc: func() Schemes { 253 s := make(Schemes) 254 fs := NewMockScheme("tftp") 255 fs.Add("1.2.3.4", "/foobar/pxelinux.0", "") 256 conf := `default foo 257 258 label foo 259 kernel ./pxefiles/fookernel 260 append earlyprintk=ttyS0 printk=ttyS0 261 262 label bar 263 kernel ./pxefiles/barkernel 264 append console=ttyS0` 265 fs.Add("1.2.3.4", "/foobar/pxelinux.cfg/default", conf) 266 fs.Add("1.2.3.4", "/foobar/pxefiles/fookernel", content1) 267 fs.Add("1.2.3.4", "/foobar/pxefiles/barkernel", content2) 268 s.Register(fs.scheme, fs) 269 return s 270 }, 271 wd: &url.URL{ 272 Scheme: "tftp", 273 Host: "1.2.3.4", 274 Path: "/foobar", 275 }, 276 want: config{ 277 defaultEntry: "foo", 278 labels: map[string]label{ 279 "foo": label{ 280 kernel: content1, 281 cmdline: "earlyprintk=ttyS0 printk=ttyS0", 282 }, 283 "bar": label{ 284 kernel: content2, 285 cmdline: "console=ttyS0", 286 }, 287 }, 288 }, 289 }, 290 { 291 desc: "valid config with global APPEND directive", 292 configFileURI: "default", 293 schemeFunc: func() Schemes { 294 s := make(Schemes) 295 fs := NewMockScheme("tftp") 296 fs.Add("1.2.3.4", "/foobar/pxelinux.0", "") 297 conf := `default foo 298 append foo=bar 299 300 label foo 301 kernel ./pxefiles/fookernel 302 append earlyprintk=ttyS0 printk=ttyS0 303 304 label bar 305 kernel ./pxefiles/barkernel 306 307 label baz 308 kernel ./pxefiles/barkernel 309 append -` 310 fs.Add("1.2.3.4", "/foobar/pxelinux.cfg/default", conf) 311 fs.Add("1.2.3.4", "/foobar/pxefiles/fookernel", content1) 312 fs.Add("1.2.3.4", "/foobar/pxefiles/barkernel", content2) 313 s.Register(fs.scheme, fs) 314 return s 315 }, 316 wd: &url.URL{ 317 Scheme: "tftp", 318 Host: "1.2.3.4", 319 Path: "/foobar", 320 }, 321 want: config{ 322 defaultEntry: "foo", 323 labels: map[string]label{ 324 "foo": label{ 325 kernel: content1, 326 // Does not contain global APPEND. 327 cmdline: "earlyprintk=ttyS0 printk=ttyS0", 328 }, 329 "bar": label{ 330 kernel: content2, 331 // Contains only global APPEND. 332 cmdline: "foo=bar", 333 }, 334 "baz": label{ 335 kernel: content2, 336 // "APPEND -" means ignore global APPEND. 337 cmdline: "", 338 }, 339 }, 340 }, 341 }, 342 { 343 desc: "valid config with global APPEND with initrd", 344 configFileURI: "default", 345 schemeFunc: func() Schemes { 346 s := make(Schemes) 347 fs := NewMockScheme("tftp") 348 fs.Add("1.2.3.4", "/foobar/pxelinux.0", "") 349 conf := `default mcnulty 350 append initrd=./pxefiles/normal_person 351 352 label mcnulty 353 kernel ./pxefiles/copkernel 354 append earlyprintk=ttyS0 printk=ttyS0 355 356 label lester 357 kernel ./pxefiles/copkernel 358 359 label omar 360 kernel ./pxefiles/drugkernel 361 append - 362 363 label stringer 364 kernel ./pxefiles/drugkernel 365 initrd ./pxefiles/criminal 366 ` 367 fs.Add("1.2.3.4", "/foobar/pxelinux.cfg/default", conf) 368 fs.Add("1.2.3.4", "/foobar/pxefiles/copkernel", content1) 369 fs.Add("1.2.3.4", "/foobar/pxefiles/drugkernel", content2) 370 fs.Add("1.2.3.4", "/foobar/pxefiles/normal_person", content3) 371 fs.Add("1.2.3.4", "/foobar/pxefiles/criminal", content4) 372 s.Register(fs.scheme, fs) 373 return s 374 }, 375 wd: &url.URL{ 376 Scheme: "tftp", 377 Host: "1.2.3.4", 378 Path: "/foobar", 379 }, 380 want: config{ 381 defaultEntry: "mcnulty", 382 labels: map[string]label{ 383 "mcnulty": label{ 384 kernel: content1, 385 // Does not contain global APPEND. 386 cmdline: "earlyprintk=ttyS0 printk=ttyS0", 387 }, 388 "lester": label{ 389 kernel: content1, 390 initrd: content3, 391 // Contains only global APPEND. 392 cmdline: "initrd=./pxefiles/normal_person", 393 }, 394 "omar": label{ 395 kernel: content2, 396 // "APPEND -" means ignore global APPEND. 397 cmdline: "", 398 }, 399 "stringer": label{ 400 kernel: content2, 401 // See TODO in pxe.go initrd handling. 402 initrd: content4, 403 cmdline: "initrd=./pxefiles/normal_person", 404 }, 405 }, 406 }, 407 }, 408 { 409 desc: "default label does not exist", 410 configFileURI: "default", 411 schemeFunc: func() Schemes { 412 s := make(Schemes) 413 fs := NewMockScheme("tftp") 414 conf := `default avon` 415 416 fs.Add("1.2.3.4", "/foobar/pxelinux.cfg/default", conf) 417 s.Register(fs.scheme, fs) 418 return s 419 }, 420 wd: &url.URL{ 421 Scheme: "tftp", 422 Host: "1.2.3.4", 423 Path: "/foobar", 424 }, 425 err: ErrDefaultEntryNotFound, 426 want: config{ 427 defaultEntry: "avon", 428 }, 429 }, 430 { 431 desc: "multi-scheme valid config", 432 configFileURI: "default", 433 schemeFunc: func() Schemes { 434 conf := `default sheeeit 435 436 label sheeeit 437 kernel ./pxefiles/kernel 438 initrd http://someplace.com/someinitrd.gz` 439 440 tftp := NewMockScheme("tftp") 441 tftp.Add("1.2.3.4", "/foobar/pxelinux.0", "") 442 tftp.Add("1.2.3.4", "/foobar/pxelinux.cfg/default", conf) 443 tftp.Add("1.2.3.4", "/foobar/pxefiles/kernel", content2) 444 445 http := NewMockScheme("http") 446 http.Add("someplace.com", "/someinitrd.gz", content3) 447 448 s := make(Schemes) 449 s.Register(tftp.scheme, tftp) 450 s.Register(http.scheme, http) 451 return s 452 }, 453 wd: &url.URL{ 454 Scheme: "tftp", 455 Host: "1.2.3.4", 456 Path: "/foobar", 457 }, 458 want: config{ 459 defaultEntry: "sheeeit", 460 labels: map[string]label{ 461 "sheeeit": label{ 462 kernel: content2, 463 initrd: content3, 464 }, 465 }, 466 }, 467 }, 468 } { 469 t.Run(fmt.Sprintf("Test [%02d] %s", i, tt.desc), func(t *testing.T) { 470 s := tt.schemeFunc() 471 c := NewConfig(tt.wd) 472 c.schemes = s 473 474 if err := c.AppendFile(tt.configFileURI); err != tt.err { 475 t.Errorf("AppendFile() got %v, want %v", err, tt.err) 476 } else if err != nil { 477 return 478 } 479 480 if got, want := c.DefaultEntry, tt.want.defaultEntry; got != want { 481 t.Errorf("DefaultEntry got %v, want %v", got, want) 482 } 483 484 for labelName, want := range tt.want.labels { 485 t.Run(fmt.Sprintf("label %s", labelName), func(t *testing.T) { 486 label, ok := c.Entries[labelName] 487 if !ok { 488 t.Errorf("Config label %v does not exist", labelName) 489 return 490 } 491 492 // Same kernel? 493 if label.Kernel == nil && (len(want.kernel) > 0 || want.kernelErr != nil) { 494 t.Errorf("want kernel, got none") 495 } 496 if label.Kernel != nil { 497 k, err := ioutil.ReadAll(label.Kernel) 498 if err != want.kernelErr { 499 t.Errorf("could not read kernel of label %q: %v, want %v", labelName, err, want.kernelErr) 500 } 501 if got, want := string(k), want.kernel; got != want { 502 t.Errorf("got kernel %s, want %s", got, want) 503 } 504 } 505 506 // Same initrd? 507 if label.Initrd == nil && (len(want.initrd) > 0 || want.initrdErr != nil) { 508 t.Errorf("want initrd, got none") 509 } 510 if label.Initrd != nil { 511 i, err := ioutil.ReadAll(label.Initrd) 512 if err != want.initrdErr { 513 t.Errorf("could not read initrd of label %q: %v, want %v", labelName, err, want.initrdErr) 514 } 515 if got, want := string(i), want.initrd; got != want { 516 t.Errorf("got initrd %s, want %s", got, want) 517 } 518 } 519 520 // Same cmdline? 521 if got, want := label.Cmdline, want.cmdline; got != want { 522 t.Errorf("got cmdline %s, want %s", got, want) 523 } 524 }) 525 } 526 527 // Check that the parser didn't make up labels. 528 for labelName := range c.Entries { 529 if _, ok := tt.want.labels[labelName]; !ok { 530 t.Errorf("config has extra label %s, but not wanted", labelName) 531 } 532 } 533 }) 534 } 535 }