github.com/containerd/nerdctl@v1.7.7/pkg/resolvconf/resolvconf_linux_test.go (about)

     1  /*
     2     Copyright The containerd Authors.
     3  
     4     Licensed under the Apache License, Version 2.0 (the "License");
     5     you may not use this file except in compliance with the License.
     6     You may obtain a copy of the License at
     7  
     8         http://www.apache.org/licenses/LICENSE-2.0
     9  
    10     Unless required by applicable law or agreed to in writing, software
    11     distributed under the License is distributed on an "AS IS" BASIS,
    12     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13     See the License for the specific language governing permissions and
    14     limitations under the License.
    15  */
    16  
    17  // originally from https://github.com/moby/moby/blob/6014c1e29dc34dffa77fb5749cc3281c1b4854ac/libnetwork/resolvconf/resolvconf_linux_test.go
    18  package resolvconf
    19  
    20  import (
    21  	"bytes"
    22  	"os"
    23  	"testing"
    24  )
    25  
    26  func TestGet(t *testing.T) {
    27  	resolvConfUtils, err := Get()
    28  	if err != nil {
    29  		t.Fatal(err)
    30  	}
    31  	resolvConfSystem, err := os.ReadFile("/run/systemd/resolve/resolv.conf")
    32  	if err != nil {
    33  		t.Fatal(err)
    34  	}
    35  	if string(resolvConfUtils.Content) != string(resolvConfSystem) {
    36  		t.Fatalf("/etc/resolv.conf and GetResolvConf have different content.")
    37  	}
    38  	hashSystem, err := hashData(bytes.NewReader(resolvConfSystem))
    39  	if err != nil {
    40  		t.Fatal(err)
    41  	}
    42  	if resolvConfUtils.Hash != hashSystem {
    43  		t.Fatalf("/etc/resolv.conf and GetResolvConf have different hashes.")
    44  	}
    45  }
    46  
    47  func TestGetNameservers(t *testing.T) {
    48  	for resolv, result := range map[string][]string{`
    49  nameserver 1.2.3.4
    50  nameserver 40.3.200.10
    51  search example.com`: {"1.2.3.4", "40.3.200.10"},
    52  		`search example.com`: {},
    53  		`nameserver 1.2.3.4
    54  search example.com
    55  nameserver 4.30.20.100`: {"1.2.3.4", "4.30.20.100"},
    56  		``:                        {},
    57  		`  nameserver 1.2.3.4   `: {"1.2.3.4"},
    58  		`search example.com
    59  nameserver 1.2.3.4
    60  #nameserver 4.3.2.1`: {"1.2.3.4"},
    61  		`search example.com
    62  nameserver 1.2.3.4 # not 4.3.2.1`: {"1.2.3.4"},
    63  	} {
    64  		test := GetNameservers([]byte(resolv), IP)
    65  		if !strSlicesEqual(test, result) {
    66  			t.Fatalf("Wrong nameserver string {%s} should be %v. Input: %s", test, result, resolv)
    67  		}
    68  	}
    69  }
    70  
    71  func TestGetNameserversAsCIDR(t *testing.T) {
    72  	for resolv, result := range map[string][]string{`
    73  nameserver 1.2.3.4
    74  nameserver 40.3.200.10
    75  search example.com`: {"1.2.3.4/32", "40.3.200.10/32"},
    76  		`search example.com`: {},
    77  		`nameserver 1.2.3.4
    78  search example.com
    79  nameserver 4.30.20.100`: {"1.2.3.4/32", "4.30.20.100/32"},
    80  		``:                        {},
    81  		`  nameserver 1.2.3.4   `: {"1.2.3.4/32"},
    82  		`search example.com
    83  nameserver 1.2.3.4
    84  #nameserver 4.3.2.1`: {"1.2.3.4/32"},
    85  		`search example.com
    86  nameserver 1.2.3.4 # not 4.3.2.1`: {"1.2.3.4/32"},
    87  	} {
    88  		test := GetNameserversAsCIDR([]byte(resolv))
    89  		if !strSlicesEqual(test, result) {
    90  			t.Fatalf("Wrong nameserver string {%s} should be %v. Input: %s", test, result, resolv)
    91  		}
    92  	}
    93  }
    94  
    95  func TestGetSearchDomains(t *testing.T) {
    96  	for resolv, result := range map[string][]string{
    97  		`search example.com`:                                   {"example.com"},
    98  		`search example.com # ignored`:                         {"example.com"},
    99  		`	  search	 example.com	  `:                            {"example.com"},
   100  		`	  search	 example.com	  # ignored`:                   {"example.com"},
   101  		`search foo.example.com example.com`:                   {"foo.example.com", "example.com"},
   102  		`	   search	   foo.example.com	 example.com	`:          {"foo.example.com", "example.com"},
   103  		`	   search	   foo.example.com	 example.com	# ignored`: {"foo.example.com", "example.com"},
   104  		``:          {},
   105  		`# ignored`: {},
   106  		`nameserver 1.2.3.4
   107  search foo.example.com example.com`: {"foo.example.com", "example.com"},
   108  		`nameserver 1.2.3.4
   109  search dup1.example.com dup2.example.com
   110  search foo.example.com example.com`: {"foo.example.com", "example.com"},
   111  		`nameserver 1.2.3.4
   112  search foo.example.com example.com
   113  nameserver 4.30.20.100`: {"foo.example.com", "example.com"},
   114  	} {
   115  		test := GetSearchDomains([]byte(resolv))
   116  		if !strSlicesEqual(test, result) {
   117  			t.Fatalf("Wrong search domain string {%s} should be %v. Input: %s", test, result, resolv)
   118  		}
   119  	}
   120  }
   121  
   122  func TestGetOptions(t *testing.T) {
   123  	for resolv, result := range map[string][]string{
   124  		`options opt1`:                            {"opt1"},
   125  		`options opt1 # ignored`:                  {"opt1"},
   126  		`	  options	 opt1	  `:                     {"opt1"},
   127  		`	  options	 opt1	  # ignored`:            {"opt1"},
   128  		`options opt1 opt2 opt3`:                  {"opt1", "opt2", "opt3"},
   129  		`options opt1 opt2 opt3 # ignored`:        {"opt1", "opt2", "opt3"},
   130  		`	   options	 opt1	 opt2	 opt3	`:          {"opt1", "opt2", "opt3"},
   131  		`	   options	 opt1	 opt2	 opt3	# ignored`: {"opt1", "opt2", "opt3"},
   132  		``:                   {},
   133  		`# ignored`:          {},
   134  		`nameserver 1.2.3.4`: {},
   135  		`nameserver 1.2.3.4
   136  options opt1 opt2 opt3`: {"opt1", "opt2", "opt3"},
   137  		`nameserver 1.2.3.4
   138  options opt1 opt2
   139  options opt3 opt4`: {"opt3", "opt4"},
   140  	} {
   141  		test := GetOptions([]byte(resolv))
   142  		if !strSlicesEqual(test, result) {
   143  			t.Fatalf("Wrong options string {%s} should be %v. Input: %s", test, result, resolv)
   144  		}
   145  	}
   146  }
   147  
   148  func strSlicesEqual(a, b []string) bool {
   149  	if len(a) != len(b) {
   150  		return false
   151  	}
   152  
   153  	for i, v := range a {
   154  		if v != b[i] {
   155  			return false
   156  		}
   157  	}
   158  
   159  	return true
   160  }
   161  
   162  func TestBuild(t *testing.T) {
   163  	file, err := os.CreateTemp("", "")
   164  	if err != nil {
   165  		t.Fatal(err)
   166  	}
   167  	defer os.Remove(file.Name())
   168  
   169  	_, err = Build(file.Name(), []string{"ns1", "ns2", "ns3"}, []string{"search1"}, []string{"opt1"})
   170  	if err != nil {
   171  		t.Fatal(err)
   172  	}
   173  
   174  	content, err := os.ReadFile(file.Name())
   175  	if err != nil {
   176  		t.Fatal(err)
   177  	}
   178  
   179  	if expected := "search search1\nnameserver ns1\nnameserver ns2\nnameserver ns3\noptions opt1\n"; !bytes.Contains(content, []byte(expected)) {
   180  		t.Fatalf("Expected to find '%s' got '%s'", expected, content)
   181  	}
   182  }
   183  
   184  func TestBuildWithZeroLengthDomainSearch(t *testing.T) {
   185  	file, err := os.CreateTemp("", "")
   186  	if err != nil {
   187  		t.Fatal(err)
   188  	}
   189  	defer os.Remove(file.Name())
   190  
   191  	_, err = Build(file.Name(), []string{"ns1", "ns2", "ns3"}, []string{"."}, []string{"opt1"})
   192  	if err != nil {
   193  		t.Fatal(err)
   194  	}
   195  
   196  	content, err := os.ReadFile(file.Name())
   197  	if err != nil {
   198  		t.Fatal(err)
   199  	}
   200  
   201  	if expected := "nameserver ns1\nnameserver ns2\nnameserver ns3\noptions opt1\n"; !bytes.Contains(content, []byte(expected)) {
   202  		t.Fatalf("Expected to find '%s' got '%s'", expected, content)
   203  	}
   204  	if notExpected := "search ."; bytes.Contains(content, []byte(notExpected)) {
   205  		t.Fatalf("Expected to not find '%s' got '%s'", notExpected, content)
   206  	}
   207  }
   208  
   209  func TestBuildWithNoOptions(t *testing.T) {
   210  	file, err := os.CreateTemp("", "")
   211  	if err != nil {
   212  		t.Fatal(err)
   213  	}
   214  	defer os.Remove(file.Name())
   215  
   216  	_, err = Build(file.Name(), []string{"ns1", "ns2", "ns3"}, []string{"search1"}, []string{})
   217  	if err != nil {
   218  		t.Fatal(err)
   219  	}
   220  
   221  	content, err := os.ReadFile(file.Name())
   222  	if err != nil {
   223  		t.Fatal(err)
   224  	}
   225  
   226  	if expected := "search search1\nnameserver ns1\nnameserver ns2\nnameserver ns3\n"; !bytes.Contains(content, []byte(expected)) {
   227  		t.Fatalf("Expected to find '%s' got '%s'", expected, content)
   228  	}
   229  	if notExpected := "search ."; bytes.Contains(content, []byte(notExpected)) {
   230  		t.Fatalf("Expected to not find '%s' got '%s'", notExpected, content)
   231  	}
   232  }
   233  
   234  func TestFilterResolvDns(t *testing.T) {
   235  	ns0 := "nameserver 10.16.60.14\nnameserver 10.16.60.21\n"
   236  
   237  	if result, _ := FilterResolvDNS([]byte(ns0), false); result != nil {
   238  		if ns0 != string(result.Content) {
   239  			t.Fatalf("Failed No Localhost: expected \n<%s> got \n<%s>", ns0, string(result.Content))
   240  		}
   241  	}
   242  
   243  	ns1 := "nameserver 10.16.60.14\nnameserver 10.16.60.21\nnameserver 127.0.0.1\n"
   244  	if result, _ := FilterResolvDNS([]byte(ns1), false); result != nil {
   245  		if ns0 != string(result.Content) {
   246  			t.Fatalf("Failed Localhost: expected \n<%s> got \n<%s>", ns0, string(result.Content))
   247  		}
   248  	}
   249  
   250  	ns1 = "nameserver 10.16.60.14\nnameserver 127.0.0.1\nnameserver 10.16.60.21\n"
   251  	if result, _ := FilterResolvDNS([]byte(ns1), false); result != nil {
   252  		if ns0 != string(result.Content) {
   253  			t.Fatalf("Failed Localhost: expected \n<%s> got \n<%s>", ns0, string(result.Content))
   254  		}
   255  	}
   256  
   257  	ns1 = "nameserver 127.0.1.1\nnameserver 10.16.60.14\nnameserver 10.16.60.21\n"
   258  	if result, _ := FilterResolvDNS([]byte(ns1), false); result != nil {
   259  		if ns0 != string(result.Content) {
   260  			t.Fatalf("Failed Localhost: expected \n<%s> got \n<%s>", ns0, string(result.Content))
   261  		}
   262  	}
   263  
   264  	ns1 = "nameserver ::1\nnameserver 10.16.60.14\nnameserver 127.0.2.1\nnameserver 10.16.60.21\n"
   265  	if result, _ := FilterResolvDNS([]byte(ns1), false); result != nil {
   266  		if ns0 != string(result.Content) {
   267  			t.Fatalf("Failed Localhost: expected \n<%s> got \n<%s>", ns0, string(result.Content))
   268  		}
   269  	}
   270  
   271  	ns1 = "nameserver 10.16.60.14\nnameserver ::1\nnameserver 10.16.60.21\nnameserver ::1"
   272  	if result, _ := FilterResolvDNS([]byte(ns1), false); result != nil {
   273  		if ns0 != string(result.Content) {
   274  			t.Fatalf("Failed Localhost: expected \n<%s> got \n<%s>", ns0, string(result.Content))
   275  		}
   276  	}
   277  
   278  	// with IPv6 disabled (false param), the IPv6 nameserver should be removed
   279  	ns1 = "nameserver 10.16.60.14\nnameserver 2002:dead:beef::1\nnameserver 10.16.60.21\nnameserver ::1"
   280  	if result, _ := FilterResolvDNS([]byte(ns1), false); result != nil {
   281  		if ns0 != string(result.Content) {
   282  			t.Fatalf("Failed Localhost+IPv6 off: expected \n<%s> got \n<%s>", ns0, string(result.Content))
   283  		}
   284  	}
   285  
   286  	// with IPv6 disabled (false param), the IPv6 link-local nameserver with zone ID should be removed
   287  	ns1 = "nameserver 10.16.60.14\nnameserver FE80::BB1%1\nnameserver FE80::BB1%eth0\nnameserver 10.16.60.21\n"
   288  	if result, _ := FilterResolvDNS([]byte(ns1), false); result != nil {
   289  		if ns0 != string(result.Content) {
   290  			t.Fatalf("Failed Localhost+IPv6 off: expected \n<%s> got \n<%s>", ns0, string(result.Content))
   291  		}
   292  	}
   293  
   294  	// with IPv6 enabled, the IPv6 nameserver should be preserved
   295  	ns0 = "nameserver 10.16.60.14\nnameserver 2002:dead:beef::1\nnameserver 10.16.60.21\n"
   296  	ns1 = "nameserver 10.16.60.14\nnameserver 2002:dead:beef::1\nnameserver 10.16.60.21\nnameserver ::1"
   297  	if result, _ := FilterResolvDNS([]byte(ns1), true); result != nil {
   298  		if ns0 != string(result.Content) {
   299  			t.Fatalf("Failed Localhost+IPv6 on: expected \n<%s> got \n<%s>", ns0, string(result.Content))
   300  		}
   301  	}
   302  
   303  	// with IPv6 enabled, and no non-localhost servers, Google defaults (both IPv4+IPv6) should be added
   304  	ns0 = "\nnameserver 8.8.8.8\nnameserver 8.8.4.4\nnameserver 2001:4860:4860::8888\nnameserver 2001:4860:4860::8844"
   305  	ns1 = "nameserver 127.0.0.1\nnameserver ::1\nnameserver 127.0.2.1"
   306  	if result, _ := FilterResolvDNS([]byte(ns1), true); result != nil {
   307  		if ns0 != string(result.Content) {
   308  			t.Fatalf("Failed no Localhost+IPv6 enabled: expected \n<%s> got \n<%s>", ns0, string(result.Content))
   309  		}
   310  	}
   311  
   312  	// with IPv6 disabled, and no non-localhost servers, Google defaults (only IPv4) should be added
   313  	ns0 = "\nnameserver 8.8.8.8\nnameserver 8.8.4.4"
   314  	ns1 = "nameserver 127.0.0.1\nnameserver ::1\nnameserver 127.0.2.1"
   315  	if result, _ := FilterResolvDNS([]byte(ns1), false); result != nil {
   316  		if ns0 != string(result.Content) {
   317  			t.Fatalf("Failed no Localhost+IPv6 enabled: expected \n<%s> got \n<%s>", ns0, string(result.Content))
   318  		}
   319  	}
   320  }