github.com/rumpl/bof@v23.0.0-rc.2+incompatible/libnetwork/etchosts/etchosts_test.go (about)

     1  package etchosts
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"os"
     7  	"testing"
     8  
     9  	"golang.org/x/sync/errgroup"
    10  )
    11  
    12  func TestBuildDefault(t *testing.T) {
    13  	file, err := os.CreateTemp("", "")
    14  	if err != nil {
    15  		t.Fatal(err)
    16  	}
    17  	defer os.Remove(file.Name())
    18  
    19  	// check that /etc/hosts has consistent ordering
    20  	for i := 0; i <= 5; i++ {
    21  		err = Build(file.Name(), "", "", "", nil)
    22  		if err != nil {
    23  			t.Fatal(err)
    24  		}
    25  
    26  		content, err := os.ReadFile(file.Name())
    27  		if err != nil {
    28  			t.Fatal(err)
    29  		}
    30  		expected := "127.0.0.1\tlocalhost\n::1\tlocalhost ip6-localhost ip6-loopback\nfe00::0\tip6-localnet\nff00::0\tip6-mcastprefix\nff02::1\tip6-allnodes\nff02::2\tip6-allrouters\n"
    31  
    32  		if expected != string(content) {
    33  			t.Fatalf("Expected to find '%s' got '%s'", expected, content)
    34  		}
    35  	}
    36  }
    37  
    38  func TestBuildHostnameDomainname(t *testing.T) {
    39  	file, err := os.CreateTemp("", "")
    40  	if err != nil {
    41  		t.Fatal(err)
    42  	}
    43  	defer os.Remove(file.Name())
    44  
    45  	err = Build(file.Name(), "10.11.12.13", "testhostname", "testdomainname", nil)
    46  	if err != nil {
    47  		t.Fatal(err)
    48  	}
    49  
    50  	content, err := os.ReadFile(file.Name())
    51  	if err != nil {
    52  		t.Fatal(err)
    53  	}
    54  
    55  	if expected := "10.11.12.13\ttesthostname.testdomainname testhostname\n"; !bytes.Contains(content, []byte(expected)) {
    56  		t.Fatalf("Expected to find '%s' got '%s'", expected, content)
    57  	}
    58  }
    59  
    60  func TestBuildHostname(t *testing.T) {
    61  	file, err := os.CreateTemp("", "")
    62  	if err != nil {
    63  		t.Fatal(err)
    64  	}
    65  	defer os.Remove(file.Name())
    66  
    67  	err = Build(file.Name(), "10.11.12.13", "testhostname", "", nil)
    68  	if err != nil {
    69  		t.Fatal(err)
    70  	}
    71  
    72  	content, err := os.ReadFile(file.Name())
    73  	if err != nil {
    74  		t.Fatal(err)
    75  	}
    76  
    77  	if expected := "10.11.12.13\ttesthostname\n"; !bytes.Contains(content, []byte(expected)) {
    78  		t.Fatalf("Expected to find '%s' got '%s'", expected, content)
    79  	}
    80  }
    81  
    82  func TestBuildHostnameFQDN(t *testing.T) {
    83  	file, err := os.CreateTemp("", "")
    84  	if err != nil {
    85  		t.Fatal(err)
    86  	}
    87  	defer os.Remove(file.Name())
    88  
    89  	err = Build(file.Name(), "10.11.12.13", "testhostname.testdomainname.com", "", nil)
    90  	if err != nil {
    91  		t.Fatal(err)
    92  	}
    93  
    94  	content, err := os.ReadFile(file.Name())
    95  	if err != nil {
    96  		t.Fatal(err)
    97  	}
    98  
    99  	if expected := "10.11.12.13\ttesthostname.testdomainname.com testhostname\n"; !bytes.Contains(content, []byte(expected)) {
   100  		t.Fatalf("Expected to find '%s' got '%s'", expected, content)
   101  	}
   102  }
   103  
   104  func TestBuildNoIP(t *testing.T) {
   105  	file, err := os.CreateTemp("", "")
   106  	if err != nil {
   107  		t.Fatal(err)
   108  	}
   109  	defer os.Remove(file.Name())
   110  
   111  	err = Build(file.Name(), "", "testhostname", "", nil)
   112  	if err != nil {
   113  		t.Fatal(err)
   114  	}
   115  
   116  	content, err := os.ReadFile(file.Name())
   117  	if err != nil {
   118  		t.Fatal(err)
   119  	}
   120  
   121  	if expected := ""; !bytes.Contains(content, []byte(expected)) {
   122  		t.Fatalf("Expected to find '%s' got '%s'", expected, content)
   123  	}
   124  }
   125  
   126  func TestUpdate(t *testing.T) {
   127  	file, err := os.CreateTemp("", "")
   128  	if err != nil {
   129  		t.Fatal(err)
   130  	}
   131  	defer os.Remove(file.Name())
   132  
   133  	if err := Build(file.Name(), "10.11.12.13", "testhostname", "testdomainname", nil); err != nil {
   134  		t.Fatal(err)
   135  	}
   136  
   137  	content, err := os.ReadFile(file.Name())
   138  	if err != nil {
   139  		t.Fatal(err)
   140  	}
   141  
   142  	if expected := "10.11.12.13\ttesthostname.testdomainname testhostname\n"; !bytes.Contains(content, []byte(expected)) {
   143  		t.Fatalf("Expected to find '%s' got '%s'", expected, content)
   144  	}
   145  
   146  	if err := Update(file.Name(), "1.1.1.1", "testhostname"); err != nil {
   147  		t.Fatal(err)
   148  	}
   149  
   150  	content, err = os.ReadFile(file.Name())
   151  	if err != nil {
   152  		t.Fatal(err)
   153  	}
   154  
   155  	if expected := "1.1.1.1\ttesthostname.testdomainname testhostname\n"; !bytes.Contains(content, []byte(expected)) {
   156  		t.Fatalf("Expected to find '%s' got '%s'", expected, content)
   157  	}
   158  }
   159  
   160  // This regression test ensures that when a host is given a new IP
   161  // via the Update function that other hosts which start with the
   162  // same name as the targeted host are not erroneously updated as well.
   163  // In the test example, if updating a host called "prefix", unrelated
   164  // hosts named "prefixAndMore" or "prefix2" or anything else starting
   165  // with "prefix" should not be changed. For more information see
   166  // GitHub issue #603.
   167  func TestUpdateIgnoresPrefixedHostname(t *testing.T) {
   168  	file, err := os.CreateTemp("", "")
   169  	if err != nil {
   170  		t.Fatal(err)
   171  	}
   172  	defer os.Remove(file.Name())
   173  
   174  	if err := Build(file.Name(), "10.11.12.13", "testhostname", "testdomainname", []Record{
   175  		{
   176  			Hosts: "prefix",
   177  			IP:    "2.2.2.2",
   178  		},
   179  		{
   180  			Hosts: "prefixAndMore",
   181  			IP:    "3.3.3.3",
   182  		},
   183  		{
   184  			Hosts: "unaffectedHost",
   185  			IP:    "4.4.4.4",
   186  		},
   187  	}); err != nil {
   188  		t.Fatal(err)
   189  	}
   190  
   191  	content, err := os.ReadFile(file.Name())
   192  	if err != nil {
   193  		t.Fatal(err)
   194  	}
   195  
   196  	if expected := "2.2.2.2\tprefix\n3.3.3.3\tprefixAndMore\n4.4.4.4\tunaffectedHost\n"; !bytes.Contains(content, []byte(expected)) {
   197  		t.Fatalf("Expected to find '%s' got '%s'", expected, content)
   198  	}
   199  
   200  	if err := Update(file.Name(), "5.5.5.5", "prefix"); err != nil {
   201  		t.Fatal(err)
   202  	}
   203  
   204  	content, err = os.ReadFile(file.Name())
   205  	if err != nil {
   206  		t.Fatal(err)
   207  	}
   208  
   209  	if expected := "5.5.5.5\tprefix\n3.3.3.3\tprefixAndMore\n4.4.4.4\tunaffectedHost\n"; !bytes.Contains(content, []byte(expected)) {
   210  		t.Fatalf("Expected to find '%s' got '%s'", expected, content)
   211  	}
   212  
   213  }
   214  
   215  // This regression test covers the host prefix issue for the
   216  // Delete function. In the test example, if deleting a host called
   217  // "prefix", an unrelated host called "prefixAndMore" should not
   218  // be deleted. For more information see GitHub issue #603.
   219  func TestDeleteIgnoresPrefixedHostname(t *testing.T) {
   220  	file, err := os.CreateTemp("", "")
   221  	if err != nil {
   222  		t.Fatal(err)
   223  	}
   224  	defer os.Remove(file.Name())
   225  
   226  	err = Build(file.Name(), "", "", "", nil)
   227  	if err != nil {
   228  		t.Fatal(err)
   229  	}
   230  
   231  	if err := Add(file.Name(), []Record{
   232  		{
   233  			Hosts: "prefix",
   234  			IP:    "1.1.1.1",
   235  		},
   236  		{
   237  			Hosts: "prefixAndMore",
   238  			IP:    "2.2.2.2",
   239  		},
   240  	}); err != nil {
   241  		t.Fatal(err)
   242  	}
   243  
   244  	if err := Delete(file.Name(), []Record{
   245  		{
   246  			Hosts: "prefix",
   247  			IP:    "1.1.1.1",
   248  		},
   249  	}); err != nil {
   250  		t.Fatal(err)
   251  	}
   252  
   253  	content, err := os.ReadFile(file.Name())
   254  	if err != nil {
   255  		t.Fatal(err)
   256  	}
   257  
   258  	if expected := "2.2.2.2\tprefixAndMore\n"; !bytes.Contains(content, []byte(expected)) {
   259  		t.Fatalf("Expected to find '%s' got '%s'", expected, content)
   260  	}
   261  
   262  	if expected := "1.1.1.1\tprefix\n"; bytes.Contains(content, []byte(expected)) {
   263  		t.Fatalf("Did not expect to find '%s' got '%s'", expected, content)
   264  	}
   265  }
   266  
   267  func TestAddEmpty(t *testing.T) {
   268  	file, err := os.CreateTemp("", "")
   269  	if err != nil {
   270  		t.Fatal(err)
   271  	}
   272  	defer os.Remove(file.Name())
   273  
   274  	err = Build(file.Name(), "", "", "", nil)
   275  	if err != nil {
   276  		t.Fatal(err)
   277  	}
   278  
   279  	if err := Add(file.Name(), []Record{}); err != nil {
   280  		t.Fatal(err)
   281  	}
   282  }
   283  
   284  func TestAdd(t *testing.T) {
   285  	file, err := os.CreateTemp("", "")
   286  	if err != nil {
   287  		t.Fatal(err)
   288  	}
   289  	defer os.Remove(file.Name())
   290  
   291  	err = Build(file.Name(), "", "", "", nil)
   292  	if err != nil {
   293  		t.Fatal(err)
   294  	}
   295  
   296  	if err := Add(file.Name(), []Record{
   297  		{
   298  			Hosts: "testhostname",
   299  			IP:    "2.2.2.2",
   300  		},
   301  	}); err != nil {
   302  		t.Fatal(err)
   303  	}
   304  
   305  	content, err := os.ReadFile(file.Name())
   306  	if err != nil {
   307  		t.Fatal(err)
   308  	}
   309  
   310  	if expected := "2.2.2.2\ttesthostname\n"; !bytes.Contains(content, []byte(expected)) {
   311  		t.Fatalf("Expected to find '%s' got '%s'", expected, content)
   312  	}
   313  }
   314  
   315  func TestDeleteEmpty(t *testing.T) {
   316  	file, err := os.CreateTemp("", "")
   317  	if err != nil {
   318  		t.Fatal(err)
   319  	}
   320  	defer os.Remove(file.Name())
   321  
   322  	err = Build(file.Name(), "", "", "", nil)
   323  	if err != nil {
   324  		t.Fatal(err)
   325  	}
   326  
   327  	if err := Delete(file.Name(), []Record{}); err != nil {
   328  		t.Fatal(err)
   329  	}
   330  }
   331  
   332  func TestDeleteNewline(t *testing.T) {
   333  	file, err := os.CreateTemp("", "")
   334  	if err != nil {
   335  		t.Fatal(err)
   336  	}
   337  	defer os.Remove(file.Name())
   338  
   339  	b := []byte("\n")
   340  	if _, err := file.Write(b); err != nil {
   341  		t.Fatal(err)
   342  	}
   343  
   344  	rec := []Record{
   345  		{
   346  			Hosts: "prefix",
   347  			IP:    "2.2.2.2",
   348  		},
   349  	}
   350  	if err := Delete(file.Name(), rec); err != nil {
   351  		t.Fatal(err)
   352  	}
   353  }
   354  
   355  func TestDelete(t *testing.T) {
   356  	file, err := os.CreateTemp("", "")
   357  	if err != nil {
   358  		t.Fatal(err)
   359  	}
   360  	defer os.Remove(file.Name())
   361  
   362  	err = Build(file.Name(), "", "", "", nil)
   363  	if err != nil {
   364  		t.Fatal(err)
   365  	}
   366  
   367  	if err := Add(file.Name(), []Record{
   368  		{
   369  			Hosts: "testhostname1",
   370  			IP:    "1.1.1.1",
   371  		},
   372  		{
   373  			Hosts: "testhostname2",
   374  			IP:    "2.2.2.2",
   375  		},
   376  		{
   377  			Hosts: "testhostname3",
   378  			IP:    "3.3.3.3",
   379  		},
   380  	}); err != nil {
   381  		t.Fatal(err)
   382  	}
   383  
   384  	if err := Delete(file.Name(), []Record{
   385  		{
   386  			Hosts: "testhostname1",
   387  			IP:    "1.1.1.1",
   388  		},
   389  		{
   390  			Hosts: "testhostname3",
   391  			IP:    "3.3.3.3",
   392  		},
   393  	}); err != nil {
   394  		t.Fatal(err)
   395  	}
   396  
   397  	content, err := os.ReadFile(file.Name())
   398  	if err != nil {
   399  		t.Fatal(err)
   400  	}
   401  
   402  	if expected := "2.2.2.2\ttesthostname2\n"; !bytes.Contains(content, []byte(expected)) {
   403  		t.Fatalf("Expected to find '%s' got '%s'", expected, content)
   404  	}
   405  
   406  	if expected := "1.1.1.1\ttesthostname1\n"; bytes.Contains(content, []byte(expected)) {
   407  		t.Fatalf("Did not expect to find '%s' got '%s'", expected, content)
   408  	}
   409  }
   410  
   411  func TestConcurrentWrites(t *testing.T) {
   412  	file, err := os.CreateTemp("", "")
   413  	if err != nil {
   414  		t.Fatal(err)
   415  	}
   416  	defer os.Remove(file.Name())
   417  
   418  	err = Build(file.Name(), "", "", "", nil)
   419  	if err != nil {
   420  		t.Fatal(err)
   421  	}
   422  
   423  	if err := Add(file.Name(), []Record{
   424  		{
   425  			Hosts: "inithostname",
   426  			IP:    "172.17.0.1",
   427  		},
   428  	}); err != nil {
   429  		t.Fatal(err)
   430  	}
   431  
   432  	group := new(errgroup.Group)
   433  	for i := 0; i < 10; i++ {
   434  		i := i
   435  		group.Go(func() error {
   436  			rec := []Record{
   437  				{
   438  					IP:    fmt.Sprintf("%d.%d.%d.%d", i, i, i, i),
   439  					Hosts: fmt.Sprintf("testhostname%d", i),
   440  				},
   441  			}
   442  
   443  			for j := 0; j < 25; j++ {
   444  				if err := Add(file.Name(), rec); err != nil {
   445  					return err
   446  				}
   447  
   448  				if err := Delete(file.Name(), rec); err != nil {
   449  					return err
   450  				}
   451  			}
   452  			return nil
   453  		})
   454  	}
   455  
   456  	if err := group.Wait(); err != nil {
   457  		t.Fatal(err)
   458  	}
   459  
   460  	content, err := os.ReadFile(file.Name())
   461  	if err != nil {
   462  		t.Fatal(err)
   463  	}
   464  
   465  	if expected := "172.17.0.1\tinithostname\n"; !bytes.Contains(content, []byte(expected)) {
   466  		t.Fatalf("Expected to find '%s' got '%s'", expected, content)
   467  	}
   468  }
   469  
   470  func benchDelete(b *testing.B) {
   471  	b.StopTimer()
   472  	file, err := os.CreateTemp("", "")
   473  	if err != nil {
   474  		b.Fatal(err)
   475  	}
   476  	defer func() {
   477  		b.StopTimer()
   478  		file.Close()
   479  		os.Remove(file.Name())
   480  		b.StartTimer()
   481  	}()
   482  
   483  	err = Build(file.Name(), "", "", "", nil)
   484  	if err != nil {
   485  		b.Fatal(err)
   486  	}
   487  
   488  	var records []Record
   489  	var toDelete []Record
   490  	for i := 0; i < 255; i++ {
   491  		record := Record{
   492  			Hosts: fmt.Sprintf("testhostname%d", i),
   493  			IP:    fmt.Sprintf("%d.%d.%d.%d", i, i, i, i),
   494  		}
   495  		records = append(records, record)
   496  		if i%2 == 0 {
   497  			toDelete = append(records, record)
   498  		}
   499  	}
   500  
   501  	if err := Add(file.Name(), records); err != nil {
   502  		b.Fatal(err)
   503  	}
   504  
   505  	b.StartTimer()
   506  	if err := Delete(file.Name(), toDelete); err != nil {
   507  		b.Fatal(err)
   508  	}
   509  }
   510  
   511  func BenchmarkDelete(b *testing.B) {
   512  	for i := 0; i < b.N; i++ {
   513  		benchDelete(b)
   514  	}
   515  }