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  }