github.com/hugelgupf/u-root@v0.0.0-20191023214958-4807c632154c/pkg/syslinux/syslinux_test.go (about) 1 // Copyright 2017-2018 the u-root Authors. All rights reserved 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package syslinux 6 7 import ( 8 "fmt" 9 "io" 10 "net/url" 11 "reflect" 12 "strings" 13 "testing" 14 15 "github.com/u-root/u-root/pkg/boot" 16 "github.com/u-root/u-root/pkg/uio" 17 "github.com/u-root/u-root/pkg/urlfetch" 18 ) 19 20 func mustReadAll(r io.ReaderAt) string { 21 if r == nil { 22 return "" 23 } 24 b, err := uio.ReadAll(r) 25 if err != nil { 26 return fmt.Sprintf("read error: %s", err) 27 } 28 return string(b) 29 } 30 31 type errorReader struct { 32 err error 33 } 34 35 func (e errorReader) ReadAt(p []byte, n int64) (int, error) { 36 return 0, e.err 37 } 38 39 func TestAppendFile(t *testing.T) { 40 content1 := "1111" 41 content2 := "2222" 42 content3 := "3333" 43 content4 := "4444" 44 45 for i, tt := range []struct { 46 desc string 47 configFileURI string 48 schemeFunc func() urlfetch.Schemes 49 wd *url.URL 50 want *Config 51 err error 52 }{ 53 { 54 desc: "all files exist, simple config with cmdline initrd", 55 configFileURI: "pxelinux.cfg/default", 56 schemeFunc: func() urlfetch.Schemes { 57 s := make(urlfetch.Schemes) 58 fs := urlfetch.NewMockScheme("tftp") 59 fs.Add("1.2.3.4", "/foobar/pxelinux.0", "") 60 conf := `default foo 61 label foo 62 kernel ./pxefiles/kernel 63 append initrd=./pxefiles/initrd` 64 fs.Add("1.2.3.4", "/foobar/pxelinux.cfg/default", conf) 65 fs.Add("1.2.3.4", "/foobar/pxefiles/kernel", content1) 66 fs.Add("1.2.3.4", "/foobar/pxefiles/initrd", content2) 67 s.Register(fs.Scheme, fs) 68 return s 69 }, 70 wd: &url.URL{ 71 Scheme: "tftp", 72 Host: "1.2.3.4", 73 Path: "/foobar", 74 }, 75 want: &Config{ 76 DefaultEntry: "foo", 77 Entries: map[string]*boot.LinuxImage{ 78 "foo": { 79 Kernel: strings.NewReader(content1), 80 Initrd: strings.NewReader(content2), 81 Cmdline: "initrd=./pxefiles/initrd", 82 }, 83 }, 84 }, 85 }, 86 { 87 desc: "all files exist, simple config with directive initrd", 88 configFileURI: "pxelinux.cfg/default", 89 schemeFunc: func() urlfetch.Schemes { 90 s := make(urlfetch.Schemes) 91 fs := urlfetch.NewMockScheme("tftp") 92 fs.Add("1.2.3.4", "/foobar/pxelinux.0", "") 93 conf := `default foo 94 label foo 95 kernel ./pxefiles/kernel 96 initrd ./pxefiles/initrd 97 append foo=bar` 98 fs.Add("1.2.3.4", "/foobar/pxelinux.cfg/default", conf) 99 fs.Add("1.2.3.4", "/foobar/pxefiles/kernel", content1) 100 fs.Add("1.2.3.4", "/foobar/pxefiles/initrd", content2) 101 s.Register(fs.Scheme, fs) 102 return s 103 }, 104 wd: &url.URL{ 105 Scheme: "tftp", 106 Host: "1.2.3.4", 107 Path: "/foobar", 108 }, 109 want: &Config{ 110 DefaultEntry: "foo", 111 Entries: map[string]*boot.LinuxImage{ 112 "foo": { 113 Kernel: strings.NewReader(content1), 114 Initrd: strings.NewReader(content2), 115 Cmdline: "foo=bar", 116 }, 117 }, 118 }, 119 }, 120 { 121 desc: "all files exist, simple config, no initrd", 122 configFileURI: "pxelinux.cfg/default", 123 schemeFunc: func() urlfetch.Schemes { 124 s := make(urlfetch.Schemes) 125 fs := urlfetch.NewMockScheme("tftp") 126 fs.Add("1.2.3.4", "/foobar/pxelinux.0", "") 127 conf := `default foo 128 label foo 129 kernel ./pxefiles/kernel` 130 fs.Add("1.2.3.4", "/foobar/pxelinux.cfg/default", conf) 131 fs.Add("1.2.3.4", "/foobar/pxefiles/kernel", content1) 132 s.Register(fs.Scheme, fs) 133 return s 134 }, 135 wd: &url.URL{ 136 Scheme: "tftp", 137 Host: "1.2.3.4", 138 Path: "/foobar", 139 }, 140 want: &Config{ 141 DefaultEntry: "foo", 142 Entries: map[string]*boot.LinuxImage{ 143 "foo": { 144 Kernel: strings.NewReader(content1), 145 Initrd: nil, 146 Cmdline: "", 147 }, 148 }, 149 }, 150 }, 151 { 152 desc: "kernel does not exist, simple config", 153 configFileURI: "pxelinux.cfg/default", 154 schemeFunc: func() urlfetch.Schemes { 155 s := make(urlfetch.Schemes) 156 fs := urlfetch.NewMockScheme("tftp") 157 fs.Add("1.2.3.4", "/foobar/pxelinux.0", "") 158 conf := `default foo 159 label foo 160 kernel ./pxefiles/kernel` 161 fs.Add("1.2.3.4", "/foobar/pxelinux.cfg/default", conf) 162 s.Register(fs.Scheme, fs) 163 return s 164 }, 165 wd: &url.URL{ 166 Scheme: "tftp", 167 Host: "1.2.3.4", 168 Path: "/foobar", 169 }, 170 want: &Config{ 171 DefaultEntry: "foo", 172 Entries: map[string]*boot.LinuxImage{ 173 "foo": { 174 Kernel: errorReader{&urlfetch.URLError{ 175 URL: &url.URL{ 176 Scheme: "tftp", 177 Host: "1.2.3.4", 178 Path: "/foobar/pxefiles/kernel", 179 }, 180 Err: urlfetch.ErrNoSuchFile, 181 }}, 182 Initrd: nil, 183 Cmdline: "", 184 }, 185 }, 186 }, 187 }, 188 { 189 desc: "config file does not exist", 190 configFileURI: "pxelinux.cfg/default", 191 schemeFunc: func() urlfetch.Schemes { 192 s := make(urlfetch.Schemes) 193 fs := urlfetch.NewMockScheme("tftp") 194 s.Register(fs.Scheme, fs) 195 return s 196 }, 197 wd: &url.URL{ 198 Scheme: "tftp", 199 Host: "1.2.3.4", 200 Path: "/foobar", 201 }, 202 err: &urlfetch.URLError{ 203 URL: &url.URL{ 204 Scheme: "tftp", 205 Host: "1.2.3.4", 206 Path: "/foobar/pxelinux.cfg/default", 207 }, 208 Err: urlfetch.ErrNoSuchHost, 209 }, 210 }, 211 { 212 desc: "empty config", 213 configFileURI: "pxelinux.cfg/default", 214 schemeFunc: func() urlfetch.Schemes { 215 s := make(urlfetch.Schemes) 216 fs := urlfetch.NewMockScheme("tftp") 217 fs.Add("1.2.3.4", "/foobar/pxelinux.cfg/default", "") 218 s.Register(fs.Scheme, fs) 219 return s 220 }, 221 wd: &url.URL{ 222 Scheme: "tftp", 223 Host: "1.2.3.4", 224 Path: "/foobar", 225 }, 226 want: &Config{ 227 DefaultEntry: "", 228 }, 229 }, 230 { 231 desc: "valid config with two Entries", 232 configFileURI: "pxelinux.cfg/default", 233 schemeFunc: func() urlfetch.Schemes { 234 s := make(urlfetch.Schemes) 235 fs := urlfetch.NewMockScheme("tftp") 236 fs.Add("1.2.3.4", "/foobar/pxelinux.0", "") 237 conf := `default foo 238 239 label foo 240 kernel ./pxefiles/fookernel 241 append earlyprintk=ttyS0 printk=ttyS0 242 243 label bar 244 kernel ./pxefiles/barkernel 245 append console=ttyS0` 246 fs.Add("1.2.3.4", "/foobar/pxelinux.cfg/default", conf) 247 fs.Add("1.2.3.4", "/foobar/pxefiles/fookernel", content1) 248 fs.Add("1.2.3.4", "/foobar/pxefiles/barkernel", content2) 249 s.Register(fs.Scheme, fs) 250 return s 251 }, 252 wd: &url.URL{ 253 Scheme: "tftp", 254 Host: "1.2.3.4", 255 Path: "/foobar", 256 }, 257 want: &Config{ 258 DefaultEntry: "foo", 259 Entries: map[string]*boot.LinuxImage{ 260 "foo": { 261 Kernel: strings.NewReader(content1), 262 Cmdline: "earlyprintk=ttyS0 printk=ttyS0", 263 }, 264 "bar": { 265 Kernel: strings.NewReader(content2), 266 Cmdline: "console=ttyS0", 267 }, 268 }, 269 }, 270 }, 271 { 272 desc: "valid config with global APPEND directive", 273 configFileURI: "pxelinux.cfg/default", 274 schemeFunc: func() urlfetch.Schemes { 275 s := make(urlfetch.Schemes) 276 fs := urlfetch.NewMockScheme("tftp") 277 fs.Add("1.2.3.4", "/foobar/pxelinux.0", "") 278 conf := `default foo 279 append foo=bar 280 281 label foo 282 kernel ./pxefiles/fookernel 283 append earlyprintk=ttyS0 printk=ttyS0 284 285 label bar 286 kernel ./pxefiles/barkernel 287 288 label baz 289 kernel ./pxefiles/barkernel 290 append -` 291 fs.Add("1.2.3.4", "/foobar/pxelinux.cfg/default", conf) 292 fs.Add("1.2.3.4", "/foobar/pxefiles/fookernel", content1) 293 fs.Add("1.2.3.4", "/foobar/pxefiles/barkernel", content2) 294 s.Register(fs.Scheme, fs) 295 return s 296 }, 297 wd: &url.URL{ 298 Scheme: "tftp", 299 Host: "1.2.3.4", 300 Path: "/foobar", 301 }, 302 want: &Config{ 303 DefaultEntry: "foo", 304 Entries: map[string]*boot.LinuxImage{ 305 "foo": { 306 Kernel: strings.NewReader(content1), 307 // Does not contain global APPEND. 308 Cmdline: "earlyprintk=ttyS0 printk=ttyS0", 309 }, 310 "bar": { 311 Kernel: strings.NewReader(content2), 312 // Contains only global APPEND. 313 Cmdline: "foo=bar", 314 }, 315 "baz": { 316 Kernel: strings.NewReader(content2), 317 // "APPEND -" means ignore global APPEND. 318 Cmdline: "", 319 }, 320 }, 321 }, 322 }, 323 { 324 desc: "valid config with global APPEND with initrd", 325 configFileURI: "pxelinux.cfg/default", 326 schemeFunc: func() urlfetch.Schemes { 327 s := make(urlfetch.Schemes) 328 fs := urlfetch.NewMockScheme("tftp") 329 fs.Add("1.2.3.4", "/foobar/pxelinux.0", "") 330 conf := `default mcnulty 331 append initrd=./pxefiles/normal_person 332 333 label mcnulty 334 kernel ./pxefiles/copkernel 335 append earlyprintk=ttyS0 printk=ttyS0 336 337 label lester 338 kernel ./pxefiles/copkernel 339 340 label omar 341 kernel ./pxefiles/drugkernel 342 append - 343 344 label stringer 345 kernel ./pxefiles/drugkernel 346 initrd ./pxefiles/criminal 347 ` 348 fs.Add("1.2.3.4", "/foobar/pxelinux.cfg/default", conf) 349 fs.Add("1.2.3.4", "/foobar/pxefiles/copkernel", content1) 350 fs.Add("1.2.3.4", "/foobar/pxefiles/drugkernel", content2) 351 fs.Add("1.2.3.4", "/foobar/pxefiles/normal_person", content3) 352 fs.Add("1.2.3.4", "/foobar/pxefiles/criminal", content4) 353 s.Register(fs.Scheme, fs) 354 return s 355 }, 356 wd: &url.URL{ 357 Scheme: "tftp", 358 Host: "1.2.3.4", 359 Path: "/foobar", 360 }, 361 want: &Config{ 362 DefaultEntry: "mcnulty", 363 Entries: map[string]*boot.LinuxImage{ 364 "mcnulty": { 365 Kernel: strings.NewReader(content1), 366 // Does not contain global APPEND. 367 Cmdline: "earlyprintk=ttyS0 printk=ttyS0", 368 }, 369 "lester": { 370 Kernel: strings.NewReader(content1), 371 Initrd: strings.NewReader(content3), 372 // Contains only global APPEND. 373 Cmdline: "initrd=./pxefiles/normal_person", 374 }, 375 "omar": { 376 Kernel: strings.NewReader(content2), 377 // "APPEND -" means ignore global APPEND. 378 Cmdline: "", 379 }, 380 "stringer": { 381 Kernel: strings.NewReader(content2), 382 // See TODO in pxe.go initrd handling. 383 Initrd: strings.NewReader(content4), 384 Cmdline: "initrd=./pxefiles/normal_person", 385 }, 386 }, 387 }, 388 }, 389 { 390 desc: "default label does not exist", 391 configFileURI: "pxelinux.cfg/default", 392 schemeFunc: func() urlfetch.Schemes { 393 s := make(urlfetch.Schemes) 394 fs := urlfetch.NewMockScheme("tftp") 395 conf := `default avon` 396 397 fs.Add("1.2.3.4", "/foobar/pxelinux.cfg/default", conf) 398 s.Register(fs.Scheme, fs) 399 return s 400 }, 401 wd: &url.URL{ 402 Scheme: "tftp", 403 Host: "1.2.3.4", 404 Path: "/foobar", 405 }, 406 err: ErrDefaultEntryNotFound, 407 want: &Config{ 408 DefaultEntry: "avon", 409 }, 410 }, 411 { 412 desc: "multi-scheme valid config", 413 configFileURI: "pxelinux.cfg/default", 414 schemeFunc: func() urlfetch.Schemes { 415 conf := `default sheeeit 416 417 label sheeeit 418 kernel ./pxefiles/kernel 419 initrd http://someplace.com/someinitrd.gz` 420 421 tftp := urlfetch.NewMockScheme("tftp") 422 tftp.Add("1.2.3.4", "/foobar/pxelinux.0", "") 423 tftp.Add("1.2.3.4", "/foobar/pxelinux.cfg/default", conf) 424 tftp.Add("1.2.3.4", "/foobar/pxefiles/kernel", content2) 425 426 http := urlfetch.NewMockScheme("http") 427 http.Add("someplace.com", "/someinitrd.gz", content3) 428 429 s := make(urlfetch.Schemes) 430 s.Register(tftp.Scheme, tftp) 431 s.Register(http.Scheme, http) 432 return s 433 }, 434 wd: &url.URL{ 435 Scheme: "tftp", 436 Host: "1.2.3.4", 437 Path: "/foobar", 438 }, 439 want: &Config{ 440 DefaultEntry: "sheeeit", 441 Entries: map[string]*boot.LinuxImage{ 442 "sheeeit": { 443 Kernel: strings.NewReader(content2), 444 Initrd: strings.NewReader(content3), 445 }, 446 }, 447 }, 448 }, 449 { 450 desc: "valid config with three includes", 451 configFileURI: "pxelinux.cfg/default", 452 schemeFunc: func() urlfetch.Schemes { 453 s := make(urlfetch.Schemes) 454 fs := urlfetch.NewMockScheme("tftp") 455 fs.Add("1.2.3.4", "/foobar/pxelinux.0", "") 456 conf := `default mcnulty 457 458 include installer/txt.cfg 459 include installer/stdmenu.cfg 460 461 menu begin advanced 462 menu title Advanced Options 463 include installer/stdmenu.cfg 464 menu end 465 ` 466 467 txt := ` 468 label mcnulty 469 kernel ./pxefiles/copkernel 470 append earlyprintk=ttyS0 printk=ttyS0 471 ` 472 473 stdmenu := ` 474 label omar 475 kernel ./pxefiles/drugkernel 476 ` 477 fs.Add("1.2.3.4", "/foobar/pxelinux.cfg/default", conf) 478 fs.Add("1.2.3.4", "/foobar/installer/stdmenu.cfg", stdmenu) 479 fs.Add("1.2.3.4", "/foobar/installer/txt.cfg", txt) 480 fs.Add("1.2.3.4", "/foobar/pxefiles/copkernel", content1) 481 fs.Add("1.2.3.4", "/foobar/pxefiles/drugkernel", content2) 482 s.Register(fs.Scheme, fs) 483 return s 484 }, 485 wd: &url.URL{ 486 Scheme: "tftp", 487 Host: "1.2.3.4", 488 Path: "/foobar", 489 }, 490 want: &Config{ 491 DefaultEntry: "mcnulty", 492 Entries: map[string]*boot.LinuxImage{ 493 "mcnulty": { 494 Kernel: strings.NewReader(content1), 495 Cmdline: "earlyprintk=ttyS0 printk=ttyS0", 496 }, 497 "omar": { 498 Kernel: strings.NewReader(content2), 499 }, 500 }, 501 }, 502 }, 503 } { 504 t.Run(fmt.Sprintf("Test [%02d] %s", i, tt.desc), func(t *testing.T) { 505 s := tt.schemeFunc() 506 par := newParserWithSchemes(tt.wd, s) 507 508 if err := par.appendFile(tt.configFileURI); !reflect.DeepEqual(err, tt.err) { 509 t.Errorf("AppendFile() got %v, want %v", err, tt.err) 510 } else if err != nil { 511 return 512 } 513 c := par.config 514 515 if got, want := c.DefaultEntry, tt.want.DefaultEntry; got != want { 516 t.Errorf("DefaultEntry got %v, want %v", got, want) 517 } 518 519 for labelName, want := range tt.want.Entries { 520 t.Run(fmt.Sprintf("label %s", labelName), func(t *testing.T) { 521 got, ok := c.Entries[labelName] 522 if !ok { 523 t.Errorf("Config label %v does not exist", labelName) 524 return 525 } 526 527 // Same kernel? 528 if !uio.ReaderAtEqual(got.Kernel, want.Kernel) { 529 t.Errorf("got kernel %s, want %s", mustReadAll(got.Kernel), mustReadAll(want.Kernel)) 530 } 531 532 // Same initrd? 533 if !uio.ReaderAtEqual(got.Initrd, want.Initrd) { 534 t.Errorf("got initrd %s, want %s", mustReadAll(got.Initrd), mustReadAll(want.Initrd)) 535 } 536 537 // Same cmdline? 538 if got.Cmdline != want.Cmdline { 539 t.Errorf("got cmdline %s, want %s", got.Cmdline, want.Cmdline) 540 } 541 }) 542 } 543 544 // Check that the parser didn't make up Entries. 545 for labelName := range c.Entries { 546 if _, ok := tt.want.Entries[labelName]; !ok { 547 t.Errorf("config has extra label %s, but not wanted", labelName) 548 } 549 } 550 }) 551 } 552 } 553 554 func TestParseURL(t *testing.T) { 555 for i, tt := range []struct { 556 url string 557 wd *url.URL 558 err bool 559 want *url.URL 560 }{ 561 { 562 url: "default", 563 wd: &url.URL{ 564 Scheme: "tftp", 565 Host: "192.168.1.1", 566 Path: "/foobar/pxelinux.cfg", 567 }, 568 want: &url.URL{ 569 Scheme: "tftp", 570 Host: "192.168.1.1", 571 Path: "/foobar/pxelinux.cfg/default", 572 }, 573 }, 574 { 575 url: "http://192.168.2.1/configs/your-machine.cfg", 576 wd: &url.URL{ 577 Scheme: "tftp", 578 Host: "192.168.1.1", 579 Path: "/foobar/pxelinux.cfg", 580 }, 581 want: &url.URL{ 582 Scheme: "http", 583 Host: "192.168.2.1", 584 Path: "/configs/your-machine.cfg", 585 }, 586 }, 587 } { 588 t.Run(fmt.Sprintf("Test #%02d", i), func(t *testing.T) { 589 got, err := parseURL(tt.url, tt.wd) 590 if (err != nil) != tt.err { 591 t.Errorf("Wanted error (%v), but got %v", tt.err, err) 592 } 593 if !reflect.DeepEqual(got, tt.want) { 594 t.Errorf("parseURL() = %#v, want %#v", got, tt.want) 595 } 596 }) 597 } 598 }