github.com/insomniacslk/u-root@v0.0.0-20200717035308-96b791510d76/pkg/boot/netboot/ipxe/ipxe_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 ipxe 6 7 import ( 8 "context" 9 "fmt" 10 "io" 11 "net/url" 12 "reflect" 13 "strings" 14 "testing" 15 16 "github.com/u-root/u-root/pkg/boot" 17 "github.com/u-root/u-root/pkg/curl" 18 "github.com/u-root/u-root/pkg/uio" 19 "github.com/u-root/u-root/pkg/ulog/ulogtest" 20 ) 21 22 func mustReadAll(r io.ReaderAt) string { 23 if r == nil { 24 return "" 25 } 26 b, err := uio.ReadAll(r) 27 if err != nil { 28 return fmt.Sprintf("read error: %s", err) 29 } 30 return string(b) 31 } 32 33 type errorReader struct { 34 err error 35 } 36 37 func (e errorReader) ReadAt(p []byte, n int64) (int, error) { 38 return 0, e.err 39 } 40 41 func mustParseURL(s string) *url.URL { 42 u, err := url.Parse(s) 43 if err != nil { 44 panic(fmt.Sprintf("parsing %q failed: %v", s, err)) 45 } 46 return u 47 } 48 49 func TestParseURL(t *testing.T) { 50 for _, tt := range []struct { 51 filename string 52 wd *url.URL 53 want *url.URL 54 }{ 55 { 56 filename: "foobar", 57 wd: mustParseURL("http://[2001::1]:18282/files/more"), 58 want: mustParseURL("http://[2001::1]:18282/files/more/foobar"), 59 }, 60 { 61 filename: "/foobar", 62 wd: mustParseURL("http://[2001::1]:18282/files/more"), 63 want: mustParseURL("http://[2001::1]:18282/foobar"), 64 }, 65 { 66 filename: "http://[2002::2]/blabla", 67 wd: mustParseURL("http://[2001::1]:18282/files/more"), 68 want: mustParseURL("http://[2002::2]/blabla"), 69 }, 70 { 71 filename: "http://[2002::2]/blabla", 72 wd: nil, 73 want: mustParseURL("http://[2002::2]/blabla"), 74 }, 75 } { 76 got, err := parseURL(tt.filename, tt.wd) 77 if err != nil { 78 t.Errorf("parseURL(%q, %s) = %v, want %v", tt.filename, tt.wd, err, nil) 79 } 80 81 if !reflect.DeepEqual(got, tt.want) { 82 t.Errorf("parseURL(%q, %s) = %v, want %v", tt.filename, tt.wd, got, tt.want) 83 } 84 } 85 } 86 87 func TestIpxeConfig(t *testing.T) { 88 content1 := "1111" 89 content2 := "2222" 90 91 for i, tt := range []struct { 92 desc string 93 schemeFunc func() curl.Schemes 94 curl *url.URL 95 want *boot.LinuxImage 96 err error 97 }{ 98 { 99 desc: "all files exist, simple config with no cmdline, one relative file path", 100 schemeFunc: func() curl.Schemes { 101 s := make(curl.Schemes) 102 fs := curl.NewMockScheme("http") 103 conf := `#!ipxe 104 kernel http://someplace.com/foobar/pxefiles/kernel 105 initrd initrd-file 106 boot` 107 fs.Add("someplace.com", "/foobar/pxefiles/ipxeconfig", conf) 108 fs.Add("someplace.com", "/foobar/pxefiles/kernel", content1) 109 fs.Add("someplace.com", "/foobar/pxefiles/initrd-file", content2) 110 s.Register(fs.Scheme, fs) 111 return s 112 }, 113 curl: &url.URL{ 114 Scheme: "http", 115 Host: "someplace.com", 116 Path: "/foobar/pxefiles/ipxeconfig", 117 }, 118 want: &boot.LinuxImage{ 119 Kernel: strings.NewReader(content1), 120 Initrd: strings.NewReader(content2), 121 }, 122 }, 123 { 124 desc: "all files exist, simple config, no initrd", 125 schemeFunc: func() curl.Schemes { 126 s := make(curl.Schemes) 127 fs := curl.NewMockScheme("http") 128 conf := `#!ipxe 129 kernel http://someplace.com/foobar/pxefiles/kernel 130 boot` 131 fs.Add("someplace.com", "/foobar/pxefiles/ipxeconfig", conf) 132 fs.Add("someplace.com", "/foobar/pxefiles/kernel", content1) 133 s.Register(fs.Scheme, fs) 134 return s 135 }, 136 curl: &url.URL{ 137 Scheme: "http", 138 Host: "someplace.com", 139 Path: "/foobar/pxefiles/ipxeconfig", 140 }, 141 want: &boot.LinuxImage{ 142 Kernel: strings.NewReader(content1), 143 }, 144 }, 145 { 146 desc: "comments and blank lines", 147 schemeFunc: func() curl.Schemes { 148 s := make(curl.Schemes) 149 fs := curl.NewMockScheme("http") 150 conf := `#!ipxe 151 # the next line is blank 152 153 kernel http://someplace.com/foobar/pxefiles/kernel 154 boot` 155 fs.Add("someplace.com", "/foobar/pxefiles/ipxeconfig", conf) 156 fs.Add("someplace.com", "/foobar/pxefiles/kernel", content1) 157 s.Register(fs.Scheme, fs) 158 return s 159 }, 160 curl: &url.URL{ 161 Scheme: "http", 162 Host: "someplace.com", 163 Path: "/foobar/pxefiles/ipxeconfig", 164 }, 165 want: &boot.LinuxImage{ 166 Kernel: strings.NewReader(content1), 167 }, 168 }, 169 { 170 desc: "kernel does not exist, simple config", 171 schemeFunc: func() curl.Schemes { 172 s := make(curl.Schemes) 173 fs := curl.NewMockScheme("http") 174 conf := `#!ipxe 175 kernel http://someplace.com/foobar/pxefiles/kernel 176 boot` 177 fs.Add("someplace.com", "/foobar/pxefiles/ipxeconfig", conf) 178 s.Register(fs.Scheme, fs) 179 return s 180 }, 181 curl: &url.URL{ 182 Scheme: "http", 183 Host: "someplace.com", 184 Path: "/foobar/pxefiles/ipxeconfig", 185 }, 186 want: &boot.LinuxImage{ 187 Kernel: errorReader{&curl.URLError{ 188 URL: &url.URL{ 189 Scheme: "http", 190 Host: "someplace.com", 191 Path: "/foobar/pxefiles/kernel", 192 }, 193 Err: curl.ErrNoSuchFile, 194 }}, 195 Initrd: nil, 196 Cmdline: "", 197 }, 198 }, 199 { 200 desc: "config file does not exist", 201 schemeFunc: func() curl.Schemes { 202 s := make(curl.Schemes) 203 fs := curl.NewMockScheme("http") 204 s.Register(fs.Scheme, fs) 205 return s 206 }, 207 curl: &url.URL{ 208 Scheme: "http", 209 Host: "someplace.com", 210 Path: "/foobar/pxefiles/ipxeconfig", 211 }, 212 err: &curl.URLError{ 213 URL: &url.URL{ 214 Scheme: "http", 215 Host: "someplace.com", 216 Path: "/foobar/pxefiles/ipxeconfig", 217 }, 218 Err: curl.ErrNoSuchHost, 219 }, 220 }, 221 { 222 desc: "invalid config", 223 schemeFunc: func() curl.Schemes { 224 s := make(curl.Schemes) 225 fs := curl.NewMockScheme("http") 226 fs.Add("someplace.com", "/foobar/pxefiles/ipxeconfig", "") 227 s.Register(fs.Scheme, fs) 228 return s 229 }, 230 curl: &url.URL{ 231 Scheme: "http", 232 Host: "someplace.com", 233 Path: "/foobar/pxefiles/ipxeconfig", 234 }, 235 err: ErrNotIpxeScript, 236 }, 237 { 238 desc: "empty config", 239 schemeFunc: func() curl.Schemes { 240 s := make(curl.Schemes) 241 fs := curl.NewMockScheme("http") 242 conf := `#!ipxe` 243 fs.Add("someplace.com", "/foobar/pxefiles/ipxeconfig", conf) 244 s.Register(fs.Scheme, fs) 245 return s 246 }, 247 curl: &url.URL{ 248 Scheme: "http", 249 Host: "someplace.com", 250 Path: "/foobar/pxefiles/ipxeconfig", 251 }, 252 want: &boot.LinuxImage{}, 253 }, 254 { 255 desc: "valid config with kernel cmdline args", 256 schemeFunc: func() curl.Schemes { 257 s := make(curl.Schemes) 258 fs := curl.NewMockScheme("http") 259 conf := `#!ipxe 260 kernel http://someplace.com/foobar/pxefiles/kernel earlyprintk=ttyS0 printk=ttyS0 261 boot` 262 fs.Add("someplace.com", "/foobar/pxefiles/ipxeconfig", conf) 263 fs.Add("someplace.com", "/foobar/pxefiles/kernel", content1) 264 s.Register(fs.Scheme, fs) 265 return s 266 }, 267 curl: &url.URL{ 268 Scheme: "http", 269 Host: "someplace.com", 270 Path: "/foobar/pxefiles/ipxeconfig", 271 }, 272 want: &boot.LinuxImage{ 273 Kernel: strings.NewReader(content1), 274 Cmdline: "earlyprintk=ttyS0 printk=ttyS0", 275 }, 276 }, 277 { 278 desc: "multi-scheme valid config", 279 schemeFunc: func() curl.Schemes { 280 conf := `#!ipxe 281 kernel tftp://1.2.3.4/foobar/pxefiles/kernel 282 initrd http://someplace.com/someinitrd.gz 283 boot` 284 285 tftp := curl.NewMockScheme("tftp") 286 tftp.Add("1.2.3.4", "/foobar/pxefiles/kernel", content1) 287 288 http := curl.NewMockScheme("http") 289 http.Add("someplace.com", "/foobar/pxefiles/ipxeconfig", conf) 290 http.Add("someplace.com", "/someinitrd.gz", content2) 291 292 s := make(curl.Schemes) 293 s.Register(tftp.Scheme, tftp) 294 s.Register(http.Scheme, http) 295 return s 296 }, 297 curl: &url.URL{ 298 Scheme: "http", 299 Host: "someplace.com", 300 Path: "/foobar/pxefiles/ipxeconfig", 301 }, 302 want: &boot.LinuxImage{ 303 Kernel: strings.NewReader(content1), 304 Initrd: strings.NewReader(content2), 305 }, 306 }, 307 { 308 desc: "valid config with unsupported cmds", 309 schemeFunc: func() curl.Schemes { 310 s := make(curl.Schemes) 311 fs := curl.NewMockScheme("http") 312 conf := `#!ipxe 313 kernel http://someplace.com/foobar/pxefiles/kernel 314 initrd http://someplace.com/someinitrd.gz 315 set ip 0.0.0.0 316 boot` 317 fs.Add("someplace.com", "/foobar/pxefiles/ipxeconfig", conf) 318 fs.Add("someplace.com", "/foobar/pxefiles/kernel", content1) 319 fs.Add("someplace.com", "/someinitrd.gz", content2) 320 s.Register(fs.Scheme, fs) 321 return s 322 }, 323 curl: &url.URL{ 324 Scheme: "http", 325 Host: "someplace.com", 326 Path: "/foobar/pxefiles/ipxeconfig", 327 }, 328 want: &boot.LinuxImage{ 329 Kernel: strings.NewReader(content1), 330 Initrd: strings.NewReader(content2), 331 }, 332 }, 333 } { 334 t.Run(fmt.Sprintf("Test [%02d] %s", i, tt.desc), func(t *testing.T) { 335 got, err := ParseConfig(context.Background(), ulogtest.Logger{t}, tt.curl, tt.schemeFunc()) 336 if !reflect.DeepEqual(err, tt.err) { 337 t.Errorf("ParseConfig() got %v, want %v", err, tt.err) 338 return 339 } else if err != nil { 340 return 341 } 342 want := tt.want 343 344 // Same kernel? 345 if !uio.ReaderAtEqual(got.Kernel, want.Kernel) { 346 t.Errorf("got kernel %s, want %s", mustReadAll(got.Kernel), mustReadAll(want.Kernel)) 347 } 348 // Same initrd? 349 if !uio.ReaderAtEqual(got.Initrd, want.Initrd) { 350 t.Errorf("got initrd %s, want %s", mustReadAll(got.Initrd), mustReadAll(want.Initrd)) 351 } 352 // Same cmdline? 353 if got.Cmdline != want.Cmdline { 354 t.Errorf("got cmdline %s, want %s", got.Cmdline, want.Cmdline) 355 } 356 }) 357 } 358 }