github.com/xyproto/u-root@v6.0.1-0.20200302025726-5528e0c77a3c+incompatible/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  	"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/curl"
    17  	"github.com/u-root/u-root/pkg/uio"
    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 TestIpxeConfig(t *testing.T) {
    40  	content1 := "1111"
    41  	content2 := "2222"
    42  
    43  	for i, tt := range []struct {
    44  		desc       string
    45  		schemeFunc func() curl.Schemes
    46  		curl       *url.URL
    47  		want       *boot.LinuxImage
    48  		err        error
    49  	}{
    50  		{
    51  			desc: "all files exist, simple config with no cmdline",
    52  			schemeFunc: func() curl.Schemes {
    53  				s := make(curl.Schemes)
    54  				fs := curl.NewMockScheme("http")
    55  				conf := `#!ipxe
    56  				kernel http://someplace.com/foobar/pxefiles/kernel
    57  				initrd http://someplace.com/foobar/pxefiles/initrd
    58  				boot`
    59  				fs.Add("someplace.com", "/foobar/pxefiles/ipxeconfig", conf)
    60  				fs.Add("someplace.com", "/foobar/pxefiles/kernel", content1)
    61  				fs.Add("someplace.com", "/foobar/pxefiles/initrd", content2)
    62  				s.Register(fs.Scheme, fs)
    63  				return s
    64  			},
    65  			curl: &url.URL{
    66  				Scheme: "http",
    67  				Host:   "someplace.com",
    68  				Path:   "/foobar/pxefiles/ipxeconfig",
    69  			},
    70  			want: &boot.LinuxImage{
    71  				Kernel: strings.NewReader(content1),
    72  				Initrd: strings.NewReader(content2),
    73  			},
    74  		},
    75  		{
    76  			desc: "all files exist, simple config, no initrd",
    77  			schemeFunc: func() curl.Schemes {
    78  				s := make(curl.Schemes)
    79  				fs := curl.NewMockScheme("http")
    80  				conf := `#!ipxe
    81  				kernel http://someplace.com/foobar/pxefiles/kernel
    82  				boot`
    83  				fs.Add("someplace.com", "/foobar/pxefiles/ipxeconfig", conf)
    84  				fs.Add("someplace.com", "/foobar/pxefiles/kernel", content1)
    85  				s.Register(fs.Scheme, fs)
    86  				return s
    87  			},
    88  			curl: &url.URL{
    89  				Scheme: "http",
    90  				Host:   "someplace.com",
    91  				Path:   "/foobar/pxefiles/ipxeconfig",
    92  			},
    93  			want: &boot.LinuxImage{
    94  				Kernel: strings.NewReader(content1),
    95  			},
    96  		},
    97  		{
    98  			desc: "comments and blank lines",
    99  			schemeFunc: func() curl.Schemes {
   100  				s := make(curl.Schemes)
   101  				fs := curl.NewMockScheme("http")
   102  				conf := `#!ipxe
   103  				# the next line is blank
   104  
   105  				kernel http://someplace.com/foobar/pxefiles/kernel
   106  				boot`
   107  				fs.Add("someplace.com", "/foobar/pxefiles/ipxeconfig", conf)
   108  				fs.Add("someplace.com", "/foobar/pxefiles/kernel", content1)
   109  				s.Register(fs.Scheme, fs)
   110  				return s
   111  			},
   112  			curl: &url.URL{
   113  				Scheme: "http",
   114  				Host:   "someplace.com",
   115  				Path:   "/foobar/pxefiles/ipxeconfig",
   116  			},
   117  			want: &boot.LinuxImage{
   118  				Kernel: strings.NewReader(content1),
   119  			},
   120  		},
   121  		{
   122  			desc: "kernel does not exist, simple config",
   123  			schemeFunc: func() curl.Schemes {
   124  				s := make(curl.Schemes)
   125  				fs := curl.NewMockScheme("http")
   126  				conf := `#!ipxe
   127  				kernel http://someplace.com/foobar/pxefiles/kernel
   128  				boot`
   129  				fs.Add("someplace.com", "/foobar/pxefiles/ipxeconfig", conf)
   130  				s.Register(fs.Scheme, fs)
   131  				return s
   132  			},
   133  			curl: &url.URL{
   134  				Scheme: "http",
   135  				Host:   "someplace.com",
   136  				Path:   "/foobar/pxefiles/ipxeconfig",
   137  			},
   138  			want: &boot.LinuxImage{
   139  				Kernel: errorReader{&curl.URLError{
   140  					URL: &url.URL{
   141  						Scheme: "http",
   142  						Host:   "someplace.com",
   143  						Path:   "/foobar/pxefiles/kernel",
   144  					},
   145  					Err: curl.ErrNoSuchFile,
   146  				}},
   147  				Initrd:  nil,
   148  				Cmdline: "",
   149  			},
   150  		},
   151  		{
   152  			desc: "config file does not exist",
   153  			schemeFunc: func() curl.Schemes {
   154  				s := make(curl.Schemes)
   155  				fs := curl.NewMockScheme("http")
   156  				s.Register(fs.Scheme, fs)
   157  				return s
   158  			},
   159  			curl: &url.URL{
   160  				Scheme: "http",
   161  				Host:   "someplace.com",
   162  				Path:   "/foobar/pxefiles/ipxeconfig",
   163  			},
   164  			err: &curl.URLError{
   165  				URL: &url.URL{
   166  					Scheme: "http",
   167  					Host:   "someplace.com",
   168  					Path:   "/foobar/pxefiles/ipxeconfig",
   169  				},
   170  				Err: curl.ErrNoSuchHost,
   171  			},
   172  		},
   173  		{
   174  			desc: "invalid config",
   175  			schemeFunc: func() curl.Schemes {
   176  				s := make(curl.Schemes)
   177  				fs := curl.NewMockScheme("http")
   178  				fs.Add("someplace.com", "/foobar/pxefiles/ipxeconfig", "")
   179  				s.Register(fs.Scheme, fs)
   180  				return s
   181  			},
   182  			curl: &url.URL{
   183  				Scheme: "http",
   184  				Host:   "someplace.com",
   185  				Path:   "/foobar/pxefiles/ipxeconfig",
   186  			},
   187  			err: ErrNotIpxeScript,
   188  		},
   189  		{
   190  			desc: "empty config",
   191  			schemeFunc: func() curl.Schemes {
   192  				s := make(curl.Schemes)
   193  				fs := curl.NewMockScheme("http")
   194  				conf := `#!ipxe`
   195  				fs.Add("someplace.com", "/foobar/pxefiles/ipxeconfig", conf)
   196  				s.Register(fs.Scheme, fs)
   197  				return s
   198  			},
   199  			curl: &url.URL{
   200  				Scheme: "http",
   201  				Host:   "someplace.com",
   202  				Path:   "/foobar/pxefiles/ipxeconfig",
   203  			},
   204  			want: &boot.LinuxImage{},
   205  		},
   206  		{
   207  			desc: "valid config with kernel cmdline args",
   208  			schemeFunc: func() curl.Schemes {
   209  				s := make(curl.Schemes)
   210  				fs := curl.NewMockScheme("http")
   211  				conf := `#!ipxe
   212  				kernel http://someplace.com/foobar/pxefiles/kernel earlyprintk=ttyS0 printk=ttyS0
   213  				boot`
   214  				fs.Add("someplace.com", "/foobar/pxefiles/ipxeconfig", conf)
   215  				fs.Add("someplace.com", "/foobar/pxefiles/kernel", content1)
   216  				s.Register(fs.Scheme, fs)
   217  				return s
   218  			},
   219  			curl: &url.URL{
   220  				Scheme: "http",
   221  				Host:   "someplace.com",
   222  				Path:   "/foobar/pxefiles/ipxeconfig",
   223  			},
   224  			want: &boot.LinuxImage{
   225  				Kernel:  strings.NewReader(content1),
   226  				Cmdline: "earlyprintk=ttyS0 printk=ttyS0",
   227  			},
   228  		},
   229  		{
   230  			desc: "multi-scheme valid config",
   231  			schemeFunc: func() curl.Schemes {
   232  				conf := `#!ipxe
   233  				kernel tftp://1.2.3.4/foobar/pxefiles/kernel
   234                                  initrd http://someplace.com/someinitrd.gz
   235  				boot`
   236  
   237  				tftp := curl.NewMockScheme("tftp")
   238  				tftp.Add("1.2.3.4", "/foobar/pxefiles/kernel", content1)
   239  
   240  				http := curl.NewMockScheme("http")
   241  				http.Add("someplace.com", "/foobar/pxefiles/ipxeconfig", conf)
   242  				http.Add("someplace.com", "/someinitrd.gz", content2)
   243  
   244  				s := make(curl.Schemes)
   245  				s.Register(tftp.Scheme, tftp)
   246  				s.Register(http.Scheme, http)
   247  				return s
   248  			},
   249  			curl: &url.URL{
   250  				Scheme: "http",
   251  				Host:   "someplace.com",
   252  				Path:   "/foobar/pxefiles/ipxeconfig",
   253  			},
   254  			want: &boot.LinuxImage{
   255  				Kernel: strings.NewReader(content1),
   256  				Initrd: strings.NewReader(content2),
   257  			},
   258  		},
   259  		{
   260  			desc: "valid config with unsupported cmds",
   261  			schemeFunc: func() curl.Schemes {
   262  				s := make(curl.Schemes)
   263  				fs := curl.NewMockScheme("http")
   264  				conf := `#!ipxe
   265  				kernel http://someplace.com/foobar/pxefiles/kernel
   266                                  initrd http://someplace.com/someinitrd.gz
   267                                  set ip 0.0.0.0
   268  				boot`
   269  				fs.Add("someplace.com", "/foobar/pxefiles/ipxeconfig", conf)
   270  				fs.Add("someplace.com", "/foobar/pxefiles/kernel", content1)
   271  				fs.Add("someplace.com", "/someinitrd.gz", content2)
   272  				s.Register(fs.Scheme, fs)
   273  				return s
   274  			},
   275  			curl: &url.URL{
   276  				Scheme: "http",
   277  				Host:   "someplace.com",
   278  				Path:   "/foobar/pxefiles/ipxeconfig",
   279  			},
   280  			want: &boot.LinuxImage{
   281  				Kernel: strings.NewReader(content1),
   282  				Initrd: strings.NewReader(content2),
   283  			},
   284  		},
   285  	} {
   286  		t.Run(fmt.Sprintf("Test [%02d] %s", i, tt.desc), func(t *testing.T) {
   287  			got, err := ParseConfigWithSchemes(tt.curl, tt.schemeFunc())
   288  			if !reflect.DeepEqual(err, tt.err) {
   289  				t.Errorf("NewConfigWithSchemes() got %v, want %v", err, tt.err)
   290  				return
   291  			} else if err != nil {
   292  				return
   293  			}
   294  			want := tt.want
   295  
   296  			// Same kernel?
   297  			if !uio.ReaderAtEqual(got.Kernel, want.Kernel) {
   298  				t.Errorf("got kernel %s, want %s", mustReadAll(got.Kernel), mustReadAll(want.Kernel))
   299  			}
   300  			// Same initrd?
   301  			if !uio.ReaderAtEqual(got.Initrd, want.Initrd) {
   302  				t.Errorf("got initrd %s, want %s", mustReadAll(got.Initrd), mustReadAll(want.Initrd))
   303  			}
   304  			// Same cmdline?
   305  			if got.Cmdline != want.Cmdline {
   306  				t.Errorf("got cmdline %s, want %s", got.Cmdline, want.Cmdline)
   307  			}
   308  		})
   309  	}
   310  }