github.com/tonistiigi/docker@v0.10.1-0.20240229224939-974013b0dc6a/libnetwork/resolvconf/resolvconf_unix_test.go (about)

     1  //go:build !windows
     2  
     3  package resolvconf
     4  
     5  import (
     6  	"bytes"
     7  	"os"
     8  	"strings"
     9  	"testing"
    10  
    11  	"github.com/opencontainers/go-digest"
    12  	"gotest.tools/v3/assert"
    13  	is "gotest.tools/v3/assert/cmp"
    14  )
    15  
    16  func TestGet(t *testing.T) {
    17  	actual, err := Get()
    18  	if err != nil {
    19  		t.Fatal(err)
    20  	}
    21  	expected, err := os.ReadFile(Path())
    22  	if err != nil {
    23  		t.Fatal(err)
    24  	}
    25  	if !bytes.Equal(actual.Content, expected) {
    26  		t.Errorf("%s and GetResolvConf have different content.", Path())
    27  	}
    28  	hash := digest.FromBytes(expected)
    29  	if !bytes.Equal(actual.Hash, []byte(hash)) {
    30  		t.Errorf("%s and GetResolvConf have different hashes.", Path())
    31  	}
    32  }
    33  
    34  func TestGetNameservers(t *testing.T) {
    35  	for _, tc := range []struct {
    36  		input  string
    37  		result []string
    38  	}{
    39  		{
    40  			input: ``,
    41  		},
    42  		{
    43  			input: `search example.com`,
    44  		},
    45  		{
    46  			input:  `  nameserver 1.2.3.4   `,
    47  			result: []string{"1.2.3.4"},
    48  		},
    49  		{
    50  			input: `
    51  nameserver 1.2.3.4
    52  nameserver 40.3.200.10
    53  search example.com`,
    54  			result: []string{"1.2.3.4", "40.3.200.10"},
    55  		},
    56  		{
    57  			input: `nameserver 1.2.3.4
    58  search example.com
    59  nameserver 4.30.20.100`,
    60  			result: []string{"1.2.3.4", "4.30.20.100"},
    61  		},
    62  		{
    63  			input: `search example.com
    64  nameserver 1.2.3.4
    65  #nameserver 4.3.2.1`,
    66  			result: []string{"1.2.3.4"},
    67  		},
    68  		{
    69  			input: `search example.com
    70  nameserver 1.2.3.4 # not 4.3.2.1`,
    71  			result: []string{"1.2.3.4"},
    72  		},
    73  	} {
    74  		test := GetNameservers([]byte(tc.input), IP)
    75  		if !strSlicesEqual(test, tc.result) {
    76  			t.Errorf("Wrong nameserver string {%s} should be %v. Input: %s", test, tc.result, tc.input)
    77  		}
    78  	}
    79  }
    80  
    81  func TestGetNameserversAsCIDR(t *testing.T) {
    82  	for _, tc := range []struct {
    83  		input  string
    84  		result []string
    85  	}{
    86  		{
    87  			input: ``,
    88  		},
    89  		{
    90  			input: `search example.com`,
    91  		},
    92  		{
    93  			input:  `  nameserver 1.2.3.4   `,
    94  			result: []string{"1.2.3.4/32"},
    95  		},
    96  		{
    97  			input: `
    98  nameserver 1.2.3.4
    99  nameserver 40.3.200.10
   100  search example.com`,
   101  			result: []string{"1.2.3.4/32", "40.3.200.10/32"},
   102  		},
   103  		{
   104  			input: `nameserver 1.2.3.4
   105  search example.com
   106  nameserver 4.30.20.100`,
   107  			result: []string{"1.2.3.4/32", "4.30.20.100/32"},
   108  		},
   109  		{
   110  			input: `search example.com
   111  nameserver 1.2.3.4
   112  #nameserver 4.3.2.1`,
   113  			result: []string{"1.2.3.4/32"},
   114  		},
   115  		{
   116  			input: `search example.com
   117  nameserver 1.2.3.4 # not 4.3.2.1`,
   118  			result: []string{"1.2.3.4/32"},
   119  		},
   120  		{
   121  			input:  `nameserver fd6f:c490:ec68::1`,
   122  			result: []string{"fd6f:c490:ec68::1/128"},
   123  		},
   124  		{
   125  			input:  `nameserver fe80::1234%eth0`,
   126  			result: []string{"fe80::1234/128"},
   127  		},
   128  	} {
   129  		test := GetNameserversAsCIDR([]byte(tc.input))
   130  		if !strSlicesEqual(test, tc.result) {
   131  			t.Errorf("Wrong nameserver string {%s} should be %v. Input: %s", test, tc.result, tc.input)
   132  		}
   133  	}
   134  }
   135  
   136  func TestGetSearchDomains(t *testing.T) {
   137  	for _, tc := range []struct {
   138  		input  string
   139  		result []string
   140  	}{
   141  		{
   142  			input: ``,
   143  		},
   144  		{
   145  			input: `# ignored`,
   146  		},
   147  		{
   148  			input:  `search example.com`,
   149  			result: []string{"example.com"},
   150  		},
   151  		{
   152  			input:  `search example.com # ignored`,
   153  			result: []string{"example.com"},
   154  		},
   155  		{
   156  			input:  `	  search	 example.com	  `,
   157  			result: []string{"example.com"},
   158  		},
   159  		{
   160  			input:  `	  search	 example.com	  # ignored`,
   161  			result: []string{"example.com"},
   162  		},
   163  		{
   164  			input:  `search foo.example.com example.com`,
   165  			result: []string{"foo.example.com", "example.com"},
   166  		},
   167  		{
   168  			input:  `	   search	   foo.example.com	 example.com	`,
   169  			result: []string{"foo.example.com", "example.com"},
   170  		},
   171  		{
   172  			input:  `	   search	   foo.example.com	 example.com	# ignored`,
   173  			result: []string{"foo.example.com", "example.com"},
   174  		},
   175  		{
   176  			input: `nameserver 1.2.3.4
   177  search foo.example.com example.com`,
   178  			result: []string{"foo.example.com", "example.com"},
   179  		},
   180  		{
   181  			input: `nameserver 1.2.3.4
   182  search dup1.example.com dup2.example.com
   183  search foo.example.com example.com`,
   184  			result: []string{"foo.example.com", "example.com"},
   185  		},
   186  		{
   187  			input: `nameserver 1.2.3.4
   188  search foo.example.com example.com
   189  nameserver 4.30.20.100`,
   190  			result: []string{"foo.example.com", "example.com"},
   191  		},
   192  		{
   193  			input:  `domain an.example`,
   194  			result: []string{"an.example"},
   195  		},
   196  	} {
   197  		test := GetSearchDomains([]byte(tc.input))
   198  		if !strSlicesEqual(test, tc.result) {
   199  			t.Errorf("Wrong search domain string {%s} should be %v. Input: %s", test, tc.result, tc.input)
   200  		}
   201  	}
   202  }
   203  
   204  func TestGetOptions(t *testing.T) {
   205  	for _, tc := range []struct {
   206  		input  string
   207  		result []string
   208  	}{
   209  		{
   210  			input: ``,
   211  		},
   212  		{
   213  			input: `# ignored`,
   214  		},
   215  		{
   216  			input: `nameserver 1.2.3.4`,
   217  		},
   218  		{
   219  			input:  `options opt1`,
   220  			result: []string{"opt1"},
   221  		},
   222  		{
   223  			input:  `options opt1 # ignored`,
   224  			result: []string{"opt1"},
   225  		},
   226  		{
   227  			input:  `	  options	 opt1	  `,
   228  			result: []string{"opt1"},
   229  		},
   230  		{
   231  			input:  `	  options	 opt1	  # ignored`,
   232  			result: []string{"opt1"},
   233  		},
   234  		{
   235  			input:  `options opt1 opt2 opt3`,
   236  			result: []string{"opt1", "opt2", "opt3"},
   237  		},
   238  		{
   239  			input:  `options opt1 opt2 opt3 # ignored`,
   240  			result: []string{"opt1", "opt2", "opt3"},
   241  		},
   242  		{
   243  			input:  `	   options	 opt1	 opt2	 opt3	`,
   244  			result: []string{"opt1", "opt2", "opt3"},
   245  		},
   246  		{
   247  			input:  `	   options	 opt1	 opt2	 opt3	# ignored`,
   248  			result: []string{"opt1", "opt2", "opt3"},
   249  		},
   250  		{
   251  			input: `nameserver 1.2.3.4
   252  options opt1 opt2 opt3`,
   253  			result: []string{"opt1", "opt2", "opt3"},
   254  		},
   255  		{
   256  			input: `nameserver 1.2.3.4
   257  options opt1 opt2
   258  options opt3 opt4`,
   259  			result: []string{"opt3", "opt4"},
   260  		},
   261  	} {
   262  		test := GetOptions([]byte(tc.input))
   263  		if !strSlicesEqual(test, tc.result) {
   264  			t.Errorf("Wrong options string {%s} should be %v. Input: %s", test, tc.result, tc.input)
   265  		}
   266  	}
   267  }
   268  
   269  func strSlicesEqual(a, b []string) bool {
   270  	if len(a) != len(b) {
   271  		return false
   272  	}
   273  
   274  	for i, v := range a {
   275  		if v != b[i] {
   276  			return false
   277  		}
   278  	}
   279  
   280  	return true
   281  }
   282  
   283  func TestBuild(t *testing.T) {
   284  	tmpDir := t.TempDir()
   285  	file, err := os.CreateTemp(tmpDir, "")
   286  	if err != nil {
   287  		t.Fatal(err)
   288  	}
   289  
   290  	f, err := Build(file.Name(), []string{"ns1", "ns2", "ns3"}, []string{"search1"}, []string{"opt1"})
   291  	if err != nil {
   292  		t.Fatal(err)
   293  	}
   294  
   295  	const expected = "search search1\nnameserver ns1\nnameserver ns2\nnameserver ns3\noptions opt1\n"
   296  	if !bytes.Equal(f.Content, []byte(expected)) {
   297  		t.Errorf("Expected to find '%s' got '%s'", expected, f.Content)
   298  	}
   299  	content, err := os.ReadFile(file.Name())
   300  	if err != nil {
   301  		t.Fatal(err)
   302  	}
   303  	if !bytes.Equal(content, []byte(expected)) {
   304  		t.Errorf("Expected to find '%s' got '%s'", expected, content)
   305  	}
   306  }
   307  
   308  func TestBuildWithZeroLengthDomainSearch(t *testing.T) {
   309  	tmpDir := t.TempDir()
   310  	file, err := os.CreateTemp(tmpDir, "")
   311  	if err != nil {
   312  		t.Fatal(err)
   313  	}
   314  
   315  	f, err := Build(file.Name(), []string{"ns1", "ns2", "ns3"}, []string{"."}, []string{"opt1"})
   316  	if err != nil {
   317  		t.Fatal(err)
   318  	}
   319  
   320  	const expected = "nameserver ns1\nnameserver ns2\nnameserver ns3\noptions opt1\n"
   321  	if !bytes.Equal(f.Content, []byte(expected)) {
   322  		t.Errorf("Expected to find '%s' got '%s'", expected, f.Content)
   323  	}
   324  	content, err := os.ReadFile(file.Name())
   325  	if err != nil {
   326  		t.Fatal(err)
   327  	}
   328  	if !bytes.Equal(content, []byte(expected)) {
   329  		t.Errorf("Expected to find '%s' got '%s'", expected, content)
   330  	}
   331  }
   332  
   333  func TestBuildWithNoOptions(t *testing.T) {
   334  	tmpDir := t.TempDir()
   335  	file, err := os.CreateTemp(tmpDir, "")
   336  	if err != nil {
   337  		t.Fatal(err)
   338  	}
   339  
   340  	f, err := Build(file.Name(), []string{"ns1", "ns2", "ns3"}, []string{"search1"}, []string{})
   341  	if err != nil {
   342  		t.Fatal(err)
   343  	}
   344  
   345  	const expected = "search search1\nnameserver ns1\nnameserver ns2\nnameserver ns3\n"
   346  	if !bytes.Equal(f.Content, []byte(expected)) {
   347  		t.Errorf("Expected to find '%s' got '%s'", expected, f.Content)
   348  	}
   349  	content, err := os.ReadFile(file.Name())
   350  	if err != nil {
   351  		t.Fatal(err)
   352  	}
   353  	if !bytes.Equal(content, []byte(expected)) {
   354  		t.Errorf("Expected to find '%s' got '%s'", expected, content)
   355  	}
   356  }
   357  
   358  func TestFilterResolvDNS(t *testing.T) {
   359  	testcases := []struct {
   360  		name        string
   361  		input       string
   362  		ipv6Enabled bool
   363  		expOut      string
   364  	}{
   365  		{
   366  			name:   "No localhost",
   367  			input:  "nameserver 10.16.60.14\nnameserver 10.16.60.21\n",
   368  			expOut: "nameserver 10.16.60.14\nnameserver 10.16.60.21",
   369  		},
   370  		{
   371  			name:   "Localhost last",
   372  			input:  "nameserver 10.16.60.14\nnameserver 10.16.60.21\nnameserver 127.0.0.1\n",
   373  			expOut: "nameserver 10.16.60.14\nnameserver 10.16.60.21",
   374  		},
   375  		{
   376  			name:   "Localhost middle",
   377  			input:  "nameserver 10.16.60.14\nnameserver 127.0.0.1\nnameserver 10.16.60.21\n",
   378  			expOut: "nameserver 10.16.60.14\nnameserver 10.16.60.21",
   379  		},
   380  		{
   381  			name:   "Localhost first",
   382  			input:  "nameserver 127.0.1.1\nnameserver 10.16.60.14\nnameserver 10.16.60.21\n",
   383  			expOut: "nameserver 10.16.60.14\nnameserver 10.16.60.21",
   384  		},
   385  		{
   386  			name:   "IPv6 Localhost",
   387  			input:  "nameserver ::1\nnameserver 10.16.60.14\nnameserver 127.0.2.1\nnameserver 10.16.60.21\n",
   388  			expOut: "nameserver 10.16.60.14\nnameserver 10.16.60.21",
   389  		},
   390  		{
   391  			name:   "Two IPv6 Localhosts",
   392  			input:  "nameserver 10.16.60.14\nnameserver ::1\nnameserver 10.16.60.21\nnameserver ::1",
   393  			expOut: "nameserver 10.16.60.14\nnameserver 10.16.60.21",
   394  		},
   395  		{
   396  			name:   "IPv6 disabled",
   397  			input:  "nameserver 10.16.60.14\nnameserver 2002:dead:beef::1\nnameserver 10.16.60.21\nnameserver ::1",
   398  			expOut: "nameserver 10.16.60.14\nnameserver 10.16.60.21",
   399  		},
   400  		{
   401  			name:   "IPv6 link-local disabled",
   402  			input:  "nameserver 10.16.60.14\nnameserver FE80::BB1%1\nnameserver FE80::BB1%eth0\nnameserver 10.16.60.21",
   403  			expOut: "nameserver 10.16.60.14\nnameserver 10.16.60.21",
   404  		},
   405  		{
   406  			name:        "IPv6 enabled",
   407  			input:       "nameserver 10.16.60.14\nnameserver 2002:dead:beef::1\nnameserver 10.16.60.21\nnameserver ::1\n",
   408  			ipv6Enabled: true,
   409  			expOut:      "nameserver 10.16.60.14\nnameserver 2002:dead:beef::1\nnameserver 10.16.60.21",
   410  		},
   411  		{
   412  			// with IPv6 enabled, and no non-localhost servers, Google defaults (both IPv4+IPv6) should be added
   413  			name:        "localhost only IPv6",
   414  			input:       "nameserver 127.0.0.1\nnameserver ::1\nnameserver 127.0.2.1",
   415  			ipv6Enabled: true,
   416  			expOut:      "nameserver 8.8.8.8\nnameserver 8.8.4.4\nnameserver 2001:4860:4860::8888\nnameserver 2001:4860:4860::8844",
   417  		},
   418  		{
   419  			// with IPv6 disabled, and no non-localhost servers, Google defaults (only IPv4) should be added
   420  			name:   "localhost only no IPv6",
   421  			input:  "nameserver 127.0.0.1\nnameserver ::1\nnameserver 127.0.2.1",
   422  			expOut: "nameserver 8.8.8.8\nnameserver 8.8.4.4",
   423  		},
   424  	}
   425  
   426  	for _, tc := range testcases {
   427  		t.Run(tc.name, func(t *testing.T) {
   428  			f, err := FilterResolvDNS([]byte(tc.input), tc.ipv6Enabled)
   429  			assert.Check(t, is.Nil(err))
   430  			out := strings.TrimSpace(string(f.Content))
   431  			assert.Check(t, is.Equal(out, tc.expOut))
   432  		})
   433  	}
   434  }