github.com/rkt/rkt@v1.30.1-0.20200224141603-171c416fac02/tests/rkt_net_test.go (about)

     1  // Copyright 2015 The rkt Authors
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  // +build host coreos src kvm
    16  
    17  package main
    18  
    19  import (
    20  	"encoding/json"
    21  	"fmt"
    22  	"io/ioutil"
    23  	"os"
    24  	"path/filepath"
    25  	"regexp"
    26  	"strconv"
    27  	"strings"
    28  	"syscall"
    29  	"testing"
    30  	"time"
    31  
    32  	"github.com/rkt/rkt/networking/netinfo"
    33  	"github.com/rkt/rkt/pkg/fileutil"
    34  	"github.com/rkt/rkt/tests/testutils"
    35  	"github.com/rkt/rkt/tests/testutils/logger"
    36  	"github.com/vishvananda/netlink"
    37  )
    38  
    39  /*
    40   * Host network
    41   * ---
    42   * Container must have the same network namespace as the host
    43   */
    44  func NewNetHostTest() testutils.Test {
    45  	return testutils.TestFunc(func(t *testing.T) {
    46  		testImageArgs := []string{"--exec=/inspect --print-netns"}
    47  		testImage := patchTestACI("rkt-inspect-networking.aci", testImageArgs...)
    48  		defer os.Remove(testImage)
    49  
    50  		ctx := testutils.NewRktRunCtx()
    51  		defer ctx.Cleanup()
    52  
    53  		cmd := fmt.Sprintf("%s --net=host --debug --insecure-options=image run --mds-register=false %s", ctx.Cmd(), testImage)
    54  		child := spawnOrFail(t, cmd)
    55  		ctx.RegisterChild(child)
    56  		defer waitOrFail(t, child, 0)
    57  
    58  		expectedRegex := `NetNS: (net:\[\d+\])`
    59  		result, out, err := expectRegexWithOutput(child, expectedRegex)
    60  		if err != nil {
    61  			t.Fatalf("Error: %v\nOutput: %v", err, out)
    62  		}
    63  
    64  		ns, err := os.Readlink("/proc/self/ns/net")
    65  		if err != nil {
    66  			t.Fatalf("Cannot evaluate NetNS symlink: %v", err)
    67  		}
    68  
    69  		if nsChanged := ns != result[1]; nsChanged {
    70  			t.Fatalf("container left host netns")
    71  		}
    72  	})
    73  }
    74  
    75  /*
    76   * Host networking
    77   * ---
    78   * Container launches http server which must be reachable by the host via the
    79   * localhost address
    80   */
    81  func NewNetHostConnectivityTest() testutils.Test {
    82  	return testutils.TestFunc(func(t *testing.T) {
    83  		logger.SetLogger(t)
    84  
    85  		httpPort, err := testutils.GetNextFreePort4()
    86  		if err != nil {
    87  			t.Fatalf("%v", err)
    88  		}
    89  		httpServeAddr := fmt.Sprintf("0.0.0.0:%v", httpPort)
    90  		httpGetAddr := fmt.Sprintf("http://127.0.0.1:%v", httpPort)
    91  
    92  		testImageArgs := []string{"--exec=/inspect --serve-http=" + httpServeAddr}
    93  		testImage := patchTestACI("rkt-inspect-networking.aci", testImageArgs...)
    94  		defer os.Remove(testImage)
    95  
    96  		ctx := testutils.NewRktRunCtx()
    97  		defer ctx.Cleanup()
    98  
    99  		cmd := fmt.Sprintf("%s --net=host --debug --insecure-options=image run --mds-register=false %s", ctx.Cmd(), testImage)
   100  		child := spawnOrFail(t, cmd)
   101  		ctx.RegisterChild(child)
   102  
   103  		ga := testutils.NewGoroutineAssistant(t)
   104  		ga.Add(2)
   105  
   106  		// Child opens the server
   107  		go func() {
   108  			defer ga.Done()
   109  			ga.WaitOrFail(child)
   110  		}()
   111  
   112  		// Host connects to the child
   113  		go func() {
   114  			defer ga.Done()
   115  			expectedRegex := `serving on`
   116  			_, out, err := expectRegexWithOutput(child, expectedRegex)
   117  			if err != nil {
   118  				ga.Fatalf("Error: %v\nOutput: %v", err, out)
   119  			}
   120  			body, err := testutils.HTTPGet(httpGetAddr)
   121  			if err != nil {
   122  				ga.Fatalf("%v\n", err)
   123  			}
   124  			t.Logf("HTTP-Get received: %s", body)
   125  		}()
   126  
   127  		ga.Wait()
   128  	})
   129  }
   130  
   131  /*
   132   * None networking
   133   * ---
   134   * must be in an empty netns
   135   */
   136  func NewNetNoneTest() testutils.Test {
   137  	return testutils.TestFunc(func(t *testing.T) {
   138  		testImageArgs := []string{"--exec=/inspect --print-netns --print-iface-count"}
   139  		testImage := patchTestACI("rkt-inspect-networking.aci", testImageArgs...)
   140  		defer os.Remove(testImage)
   141  
   142  		ctx := testutils.NewRktRunCtx()
   143  		defer ctx.Cleanup()
   144  
   145  		cmd := fmt.Sprintf("%s --debug --insecure-options=image run --net=none --mds-register=false %s", ctx.Cmd(), testImage)
   146  
   147  		child := spawnOrFail(t, cmd)
   148  		defer waitOrFail(t, child, 0)
   149  		expectedRegex := `NetNS: (net:\[\d+\])`
   150  		result, out, err := expectRegexWithOutput(child, expectedRegex)
   151  		if err != nil {
   152  			t.Fatalf("Error: %v\nOutput: %v", err, out)
   153  		}
   154  
   155  		ns, err := os.Readlink("/proc/self/ns/net")
   156  		if err != nil {
   157  			t.Fatalf("Cannot evaluate NetNS symlink: %v", err)
   158  		}
   159  
   160  		if nsChanged := ns != result[1]; !nsChanged {
   161  			t.Fatalf("container did not leave host netns")
   162  		}
   163  
   164  		expectedRegex = `Interface count: (\d+)`
   165  		result, out, err = expectRegexWithOutput(child, expectedRegex)
   166  		if err != nil {
   167  			t.Fatalf("Error: %v\nOutput: %v", err, out)
   168  		}
   169  		ifaceCount, err := strconv.Atoi(result[1])
   170  		if err != nil {
   171  			t.Fatalf("Error parsing interface count: %v\nOutput: %v", err, out)
   172  		}
   173  		if ifaceCount != 1 {
   174  			t.Fatalf("Interface count must be 1 not %q", ifaceCount)
   175  		}
   176  	})
   177  }
   178  
   179  /*
   180   * Default net
   181   * ---
   182   * Container must be in a separate network namespace
   183   */
   184  func NewTestNetDefaultNetNS() testutils.Test {
   185  	return testutils.TestFunc(func(t *testing.T) {
   186  		testImageArgs := []string{"--exec=/inspect --print-netns"}
   187  		testImage := patchTestACI("rkt-inspect-networking.aci", testImageArgs...)
   188  		defer os.Remove(testImage)
   189  
   190  		ctx := testutils.NewRktRunCtx()
   191  		defer ctx.Cleanup()
   192  
   193  		f := func(argument string) {
   194  			cmd := fmt.Sprintf("%s --debug --insecure-options=image run %s --mds-register=false %s", ctx.Cmd(), argument, testImage)
   195  			child := spawnOrFail(t, cmd)
   196  			defer waitOrFail(t, child, 0)
   197  
   198  			expectedRegex := `NetNS: (net:\[\d+\])`
   199  			result, out, err := expectRegexWithOutput(child, expectedRegex)
   200  			if err != nil {
   201  				t.Fatalf("Error: %v\nOutput: %v", err, out)
   202  			}
   203  
   204  			ns, err := os.Readlink("/proc/self/ns/net")
   205  			if err != nil {
   206  				t.Fatalf("Cannot evaluate NetNS symlink: %v", err)
   207  			}
   208  
   209  			if nsChanged := ns != result[1]; !nsChanged {
   210  				t.Fatalf("container did not leave host netns")
   211  			}
   212  
   213  		}
   214  		f("--net=default")
   215  		f("")
   216  	})
   217  }
   218  
   219  /*
   220   * Default net
   221   * ---
   222   * Host launches http server on all interfaces in the host netns
   223   * Container must be able to connect via any IP address of the host in the
   224   * default network, which is NATed
   225   * TODO: test connection to host on an outside interface
   226   */
   227  func NewNetDefaultConnectivityTest() testutils.Test {
   228  	return testutils.TestFunc(func(t *testing.T) {
   229  		ctx := testutils.NewRktRunCtx()
   230  		defer ctx.Cleanup()
   231  
   232  		f := func(argument string) {
   233  			httpPort, err := testutils.GetNextFreePort4()
   234  			if err != nil {
   235  				t.Fatalf("%v", err)
   236  			}
   237  			httpServeAddr := fmt.Sprintf("0.0.0.0:%v", httpPort)
   238  			httpServeTimeout := 30
   239  
   240  			nonLoIPv4, err := testutils.GetNonLoIfaceIPv4()
   241  			if err != nil {
   242  				t.Fatalf("%v", err)
   243  			}
   244  			if nonLoIPv4 == "" {
   245  				t.Skipf("Can not find any NAT'able IPv4 on the host, skipping..")
   246  			}
   247  
   248  			httpGetAddr := fmt.Sprintf("http://%v:%v", nonLoIPv4, httpPort)
   249  			t.Log("Telling the child to connect via", httpGetAddr)
   250  
   251  			testImageArgs := []string{fmt.Sprintf("--exec=/inspect --get-http=%v", httpGetAddr)}
   252  			testImage := patchTestACI("rkt-inspect-networking.aci", testImageArgs...)
   253  			defer os.Remove(testImage)
   254  
   255  			hostname, err := os.Hostname()
   256  			if err != nil {
   257  				t.Fatalf("Error getting hostname: %v", err)
   258  			}
   259  
   260  			ga := testutils.NewGoroutineAssistant(t)
   261  			ga.Add(2)
   262  
   263  			// Host opens the server
   264  			go func() {
   265  				defer ga.Done()
   266  				err := testutils.HTTPServe(httpServeAddr, httpServeTimeout)
   267  				if err != nil {
   268  					ga.Fatalf("Error during HTTPServe: %v", err)
   269  				}
   270  			}()
   271  
   272  			// Child connects to host
   273  			go func() {
   274  				defer ga.Done()
   275  				cmd := fmt.Sprintf("%s --debug --insecure-options=image run %s --mds-register=false %s", ctx.Cmd(), argument, testImage)
   276  				child := ga.SpawnOrFail(cmd)
   277  				defer ga.WaitOrFail(child)
   278  
   279  				expectedRegex := `HTTP-Get received: (.*)\r`
   280  				result, out, err := expectRegexWithOutput(child, expectedRegex)
   281  				if err != nil {
   282  					ga.Fatalf("Error: %v\nOutput: %v", err, out)
   283  				}
   284  				if result[1] != hostname {
   285  					ga.Fatalf("Hostname received by client `%v` doesn't match `%v`", result[1], hostname)
   286  				}
   287  			}()
   288  
   289  			ga.Wait()
   290  		}
   291  		f("--net=default")
   292  		f("")
   293  	})
   294  }
   295  
   296  /*
   297   * Default-restricted net
   298   * ---
   299   * Container launches http server on all its interfaces
   300   * Host must be able to connects to container's http server via container's
   301   * eth0's IPv4
   302   * TODO: verify that the container isn't NATed
   303   */
   304  func NewTestNetDefaultRestrictedConnectivity() testutils.Test {
   305  	return testutils.TestFunc(func(t *testing.T) {
   306  		ctx := testutils.NewRktRunCtx()
   307  		defer ctx.Cleanup()
   308  
   309  		f := func(argument string) {
   310  			httpPort := "8080"
   311  			iface := "eth0"
   312  
   313  			testImageArgs := []string{fmt.Sprintf("--exec=/inspect --print-ipv4=%v --serve-http=0.0.0.0:%v", iface, httpPort)}
   314  			testImage := patchTestACI("rkt-inspect-networking.aci", testImageArgs...)
   315  			defer os.Remove(testImage)
   316  
   317  			cmd := fmt.Sprintf("%s --insecure-options=image run %s --mds-register=false %s", ctx.Cmd(), argument, testImage)
   318  			child := spawnOrFail(t, cmd)
   319  
   320  			// Wait for the container to print out the IP address
   321  			expectedRegex := `IPv4: (\d+\.\d+\.\d+\.\d+)`
   322  			result, out, err := expectRegexWithOutput(child, expectedRegex)
   323  			if err != nil {
   324  				t.Fatalf("Error: %v\nOutput: %v", err, out)
   325  			}
   326  			httpGetAddr := fmt.Sprintf("http://%v:%v", result[1], httpPort)
   327  
   328  			// Wait for the container to open the port
   329  			expectedRegex = `serving on`
   330  			_, out, err = expectRegexWithOutput(child, expectedRegex)
   331  			if err != nil {
   332  				t.Fatalf("Error: %v\nOutput: %v", err, out)
   333  			}
   334  			body, err := testutils.HTTPGet(httpGetAddr)
   335  			if err != nil {
   336  				t.Fatalf("%v\n", err)
   337  			}
   338  			t.Logf("HTTP-Get received: %s", body)
   339  			waitOrFail(t, child, 0)
   340  		}
   341  		f("--net=default-restricted")
   342  	})
   343  }
   344  
   345  type PortFwdCase struct {
   346  	HttpGetIP     string
   347  	HttpServePort int
   348  	ListenAddress string
   349  	RktArg        string
   350  	ShouldSucceed bool
   351  }
   352  
   353  var (
   354  	bannedPorts = make(map[int]struct{}, 0)
   355  
   356  	defaultSamePortFwdCase       = PortFwdCase{"172.16.28.1", 0, "", "--net=default", true}
   357  	defaultDiffPortFwdCase       = PortFwdCase{"172.16.28.1", 1024, "", "--net=default", true}
   358  	defaultSpecificIPFwdCase     = PortFwdCase{"172.16.28.1", 1024, "172.16.28.1:", "--net=default", true}
   359  	defaultSpecificIPFwdFailCase = PortFwdCase{"127.0.0.1", 1024, "172.16.28.1:", "--net=default", false}
   360  	defaultLoSamePortFwdCase     = PortFwdCase{"127.0.0.1", 0, "", "--net=default", true}
   361  	defaultLoDiffPortFwdCase     = PortFwdCase{"127.0.0.1", 1014, "", "--net=default", true}
   362  
   363  	portFwdBridge = networkTemplateT{
   364  		Name:      "bridge1",
   365  		Type:      "bridge",
   366  		Bridge:    "bridge1",
   367  		IpMasq:    true,
   368  		IsGateway: true,
   369  		Ipam: &ipamTemplateT{
   370  			Type:   "host-local",
   371  			Subnet: "11.11.5.0/24",
   372  			Routes: []map[string]string{
   373  				{"dst": "0.0.0.0/0"},
   374  			},
   375  		},
   376  	}
   377  	bridgeSamePortFwdCase   = PortFwdCase{"11.11.5.1", 0, "", "--net=" + portFwdBridge.Name, true}
   378  	bridgeDiffPortFwdCase   = PortFwdCase{"11.11.5.1", 1024, "", "--net=" + portFwdBridge.Name, true}
   379  	bridgeLoSamePortFwdCase = PortFwdCase{"127.0.0.1", 0, "", "--net=" + portFwdBridge.Name, true}
   380  	bridgeLoDiffPortFwdCase = PortFwdCase{"127.0.0.1", 1024, "", "--net=" + portFwdBridge.Name, true}
   381  )
   382  
   383  func (ct PortFwdCase) Execute(t *testing.T) {
   384  	ctx := testutils.NewRktRunCtx()
   385  	defer ctx.Cleanup()
   386  
   387  	prepareTestNet(t, ctx, portFwdBridge)
   388  
   389  	httpPort, err := testutils.GetNextFreePort4Banned(bannedPorts)
   390  	if err != nil {
   391  		t.Fatalf("%v", err)
   392  	}
   393  	bannedPorts[httpPort] = struct{}{}
   394  
   395  	httpServePort := ct.HttpServePort
   396  	if httpServePort == 0 {
   397  		httpServePort = httpPort
   398  	}
   399  
   400  	httpServeAddr := fmt.Sprintf("0.0.0.0:%d", httpServePort)
   401  	testImageArgs := []string{
   402  		fmt.Sprintf("--ports=http,protocol=tcp,port=%d", httpServePort),
   403  		fmt.Sprintf("--exec=/inspect --serve-http=%v", httpServeAddr),
   404  	}
   405  	t.Logf("testImageArgs: %v", testImageArgs)
   406  	testImage := patchTestACI("rkt-inspect-networking.aci", testImageArgs...)
   407  	defer os.Remove(testImage)
   408  
   409  	cmd := fmt.Sprintf(
   410  		"%s --debug --insecure-options=image run --port=http:%s%d %s --mds-register=false %s",
   411  		ctx.Cmd(), ct.ListenAddress, httpPort, ct.RktArg, testImage)
   412  	child := spawnOrFail(t, cmd)
   413  
   414  	httpGetAddr := fmt.Sprintf("http://%v:%v", ct.HttpGetIP, httpPort)
   415  
   416  	ga := testutils.NewGoroutineAssistant(t)
   417  	ga.Add(2)
   418  
   419  	// Child opens the server
   420  	go func() {
   421  		defer ga.Done()
   422  		ga.WaitOrFail(child)
   423  	}()
   424  
   425  	// Host connects to the child via the forward port on localhost
   426  	go func() {
   427  		defer ga.Done()
   428  		expectedRegex := `serving on`
   429  		_, out, err := expectRegexWithOutput(child, expectedRegex)
   430  		if err != nil {
   431  			ga.Fatalf("Error: %v\nOutput: %v", err, out)
   432  		}
   433  		body, err := testutils.HTTPGet(httpGetAddr)
   434  		switch {
   435  		case err != nil && ct.ShouldSucceed:
   436  			ga.Fatalf("%v\n", err)
   437  		case err == nil && !ct.ShouldSucceed:
   438  			ga.Fatalf("HTTP-Get to %q should have failed! But received %q", httpGetAddr, body)
   439  		case err != nil && !ct.ShouldSucceed:
   440  			t.Logf("HTTP-Get failed, as expected: %v", err)
   441  		default:
   442  			t.Logf("HTTP-Get received: %s", body)
   443  		}
   444  	}()
   445  
   446  	ga.Wait()
   447  }
   448  
   449  type portFwdTest []PortFwdCase
   450  
   451  func (ct portFwdTest) Execute(t *testing.T) {
   452  	for _, testCase := range ct {
   453  		testCase.Execute(t)
   454  	}
   455  }
   456  
   457  /*
   458   * Net port forwarding connectivity
   459   * ---
   460   * Container launches http server on all its interfaces
   461   * Host must be able to connect to container's http server on it's own interfaces
   462   */
   463  func NewNetPortFwdConnectivityTest(cases ...PortFwdCase) testutils.Test {
   464  	return portFwdTest(cases)
   465  }
   466  
   467  func writeNetwork(t *testing.T, net networkTemplateT, netd string) error {
   468  	var err error
   469  	path := filepath.Join(netd, net.Name+".conf")
   470  	file, err := os.Create(path)
   471  	if err != nil {
   472  		t.Errorf("%v", err)
   473  	}
   474  
   475  	b, err := json.Marshal(net)
   476  	if err != nil {
   477  		return err
   478  	}
   479  
   480  	fmt.Println("Writing", net.Name, "to", path)
   481  	_, err = file.Write(b)
   482  	if err != nil {
   483  		return err
   484  	}
   485  
   486  	return nil
   487  }
   488  
   489  // Compute what we should pass to the --net parameter at runtime
   490  func (nt *networkTemplateT) NetParameter() string {
   491  	out := nt.Name
   492  
   493  	if len(nt.Args) > 0 {
   494  		out += ":" + strings.Join(nt.Args, ";")
   495  	}
   496  
   497  	return out
   498  }
   499  
   500  type networkTemplateT struct {
   501  	Name       string
   502  	Type       string
   503  	SubnetFile string `json:"subnetFile,omitempty"`
   504  	Master     string `json:"master,omitempty"`
   505  	IpMasq     bool
   506  	IsGateway  bool
   507  	Bridge     string             `json:"bridge,omitempty"`
   508  	Ipam       *ipamTemplateT     `json:",omitempty"`
   509  	Delegate   *delegateTemplateT `json:",omitempty"`
   510  	Args       []string           `json:"args,omitempty"` // Arguments to the CNI plugin, array of "foo=bar" pairs
   511  }
   512  
   513  type ipamTemplateT struct {
   514  	Type   string              `json:",omitempty"`
   515  	Subnet string              `json:"subnet,omitempty"`
   516  	Routes []map[string]string `json:"routes,omitempty"`
   517  }
   518  
   519  type delegateTemplateT struct {
   520  	Bridge           string `json:"bridge,omitempty"`
   521  	IsDefaultGateway bool   `json:"isDefaultGateway"`
   522  }
   523  
   524  func TestNetTemplates(t *testing.T) {
   525  	net := networkTemplateT{
   526  		Name: "ptp0",
   527  		Type: "ptp",
   528  		Args: []string{"two=three", "black=white"},
   529  		Ipam: &ipamTemplateT{
   530  			Type:   "host-local",
   531  			Subnet: "11.11.3.0/24",
   532  			Routes: []map[string]string{{"dst": "0.0.0.0/0"}},
   533  		},
   534  	}
   535  
   536  	b, err := json.Marshal(net)
   537  	if err != nil {
   538  		t.Fatalf("%v", err)
   539  	}
   540  	expected := `{"Name":"ptp0","Type":"ptp","IpMasq":false,"IsGateway":false,"Ipam":{"Type":"host-local","subnet":"11.11.3.0/24","routes":[{"dst":"0.0.0.0/0"}]},"args":["two=three","black=white"]}`
   541  	if string(b) != expected {
   542  		t.Fatalf("Template extected:\n%v\ngot:\n%v\n", expected, string(b))
   543  	}
   544  }
   545  
   546  // The format of the logfile from cniproxy
   547  type cniProxyResult struct {
   548  	PluginPath string            `json:"pluginPath"`
   549  	Stdin      string            `json:"stdin"`
   550  	Stdout     string            `json:"stdout"`
   551  	Stderr     string            `json:"stderr"`
   552  	ExitCode   int               `json:"exitCode"`
   553  	Env        []string          `json:"env"`
   554  	EnvMap     map[string]string `json:"-"`
   555  }
   556  
   557  func parseCNIProxyLog(filepath string) (*cniProxyResult, error) {
   558  	fp, err := os.Open(filepath)
   559  	defer fp.Close()
   560  	if err != nil {
   561  		return nil, err
   562  	}
   563  	res := new(cniProxyResult)
   564  	err = json.NewDecoder(fp).Decode(res)
   565  	if err != nil {
   566  		return nil, err
   567  	}
   568  	res.EnvMap = make(map[string]string)
   569  
   570  	for _, envstr := range res.Env {
   571  		vals := strings.SplitN(envstr, "=", 2)
   572  		if len(vals) != 2 {
   573  			continue
   574  		}
   575  		res.EnvMap[vals[0]] = vals[1]
   576  	}
   577  
   578  	return res, nil
   579  }
   580  
   581  func prepareTestNet(t *testing.T, ctx *testutils.RktRunCtx, nt networkTemplateT) (netdir string) {
   582  	configdir := ctx.LocalDir()
   583  	netdir = filepath.Join(configdir, "net.d")
   584  	err := os.MkdirAll(netdir, 0644)
   585  	if err != nil {
   586  		t.Fatalf("Cannot create netdir: %v", err)
   587  	}
   588  	err = writeNetwork(t, nt, netdir)
   589  	if err != nil {
   590  		t.Fatalf("Cannot write network file: %v", err)
   591  	}
   592  
   593  	// If we're proxying the CNI call, then make sure it's in the netdir
   594  	if nt.Type == "cniproxy" {
   595  		dest := filepath.Join(netdir, "cniproxy")
   596  		err := fileutil.CopyRegularFile(testutils.GetValueFromEnvOrPanic("RKT_CNI_PROXY"), dest)
   597  		if err != nil {
   598  			t.Fatalf("Cannot copy cniproxy")
   599  		}
   600  		os.Chmod(dest, 0755)
   601  		if err != nil {
   602  			t.Fatalf("Cannot chmod cniproxy")
   603  		}
   604  	}
   605  	return netdir
   606  }
   607  
   608  /*
   609   * Two containers spawn in the same custom network.
   610   * ---
   611   * Container 1 opens the http server
   612   * Container 2 fires a HTTPGet on it
   613   * The body of the HTTPGet is Container 1's hostname, which must match
   614   */
   615  func testNetCustomDual(t *testing.T, nt networkTemplateT) {
   616  	httpPort, err := testutils.GetNextFreePort4()
   617  	if err != nil {
   618  		t.Fatalf("%v", err)
   619  	}
   620  
   621  	ctx := testutils.NewRktRunCtx()
   622  	defer ctx.Cleanup()
   623  
   624  	prepareTestNet(t, ctx, nt)
   625  
   626  	container1IPv4, container1Hostname := make(chan string), make(chan string)
   627  	ga := testutils.NewGoroutineAssistant(t)
   628  	ga.Add(2)
   629  
   630  	go func() {
   631  		defer ga.Done()
   632  		httpServeAddr := fmt.Sprintf("0.0.0.0:%v", httpPort)
   633  		testImageArgs := []string{"--exec=/inspect --print-ipv4=eth0 --serve-http=" + httpServeAddr}
   634  		testImage := patchTestACI("rkt-inspect-networking1.aci", testImageArgs...)
   635  		defer os.Remove(testImage)
   636  
   637  		cmd := fmt.Sprintf("%s --debug --insecure-options=image run --net=%v --mds-register=false %s", ctx.Cmd(), nt.Name, testImage)
   638  		child := ga.SpawnOrFail(cmd)
   639  		defer ga.WaitOrFail(child)
   640  
   641  		expectedRegex := `IPv4: (\d+\.\d+\.\d+\.\d+)`
   642  		result, out, err := expectRegexTimeoutWithOutput(child, expectedRegex, 30*time.Second)
   643  		if err != nil {
   644  			ga.Fatalf("Error: %v\nOutput: %v", err, out)
   645  		}
   646  		container1IPv4 <- result[1]
   647  		expectedRegex = ` ([a-zA-Z0-9\-]*): serving on`
   648  		result, out, err = expectRegexTimeoutWithOutput(child, expectedRegex, 30*time.Second)
   649  		if err != nil {
   650  			ga.Fatalf("Error: %v\nOutput: %v", err, out)
   651  		}
   652  		container1Hostname <- result[1]
   653  	}()
   654  
   655  	go func() {
   656  		defer ga.Done()
   657  
   658  		var httpGetAddr string
   659  		httpGetAddr = fmt.Sprintf("http://%v:%v", <-container1IPv4, httpPort)
   660  
   661  		testImageArgs := []string{"--exec=/inspect --get-http=" + httpGetAddr}
   662  		testImage := patchTestACI("rkt-inspect-networking2.aci", testImageArgs...)
   663  		defer os.Remove(testImage)
   664  
   665  		cmd := fmt.Sprintf("%s --debug --insecure-options=image run --net=%v --mds-register=false %s", ctx.Cmd(), nt.Name, testImage)
   666  		child := ga.SpawnOrFail(cmd)
   667  		defer ga.WaitOrFail(child)
   668  
   669  		expectedHostname := <-container1Hostname
   670  		expectedRegex := `HTTP-Get received: (.*?)\r`
   671  		result, out, err := expectRegexTimeoutWithOutput(child, expectedRegex, 20*time.Second)
   672  		if err != nil {
   673  			ga.Fatalf("Error: %v\nOutput: %v", err, out)
   674  		}
   675  		t.Logf("HTTP-Get received: %s", result[1])
   676  		receivedHostname := result[1]
   677  
   678  		if receivedHostname != expectedHostname {
   679  			ga.Fatalf("Received hostname `%v` doesn't match `%v`", receivedHostname, expectedHostname)
   680  		}
   681  	}()
   682  
   683  	ga.Wait()
   684  }
   685  
   686  /*
   687   * Host launches http server on all interfaces in the host netns
   688   * Container must be able to connect via any IP address of the host in the
   689   * macvlan network, which is NAT
   690   * TODO: test connection to host on an outside interface
   691   */
   692  func testNetCustomNatConnectivity(t *testing.T, nt networkTemplateT) {
   693  	ctx := testutils.NewRktRunCtx()
   694  	defer ctx.Cleanup()
   695  
   696  	prepareTestNet(t, ctx, nt)
   697  
   698  	httpPort, err := testutils.GetNextFreePort4()
   699  	if err != nil {
   700  		t.Fatalf("%v", err)
   701  	}
   702  	httpServeAddr := fmt.Sprintf("0.0.0.0:%v", httpPort)
   703  	httpServeTimeout := 30
   704  
   705  	nonLoIPv4, err := testutils.GetNonLoIfaceIPv4()
   706  	if err != nil {
   707  		t.Fatalf("%v", err)
   708  	}
   709  	if nonLoIPv4 == "" {
   710  		t.Skipf("Can not find any NAT'able IPv4 on the host, skipping..")
   711  	}
   712  
   713  	httpGetAddr := fmt.Sprintf("http://%v:%v", nonLoIPv4, httpPort)
   714  	t.Log("Telling the child to connect via", httpGetAddr)
   715  
   716  	ga := testutils.NewGoroutineAssistant(t)
   717  	ga.Add(2)
   718  
   719  	// Host opens the server
   720  	go func() {
   721  		defer ga.Done()
   722  		err := testutils.HTTPServe(httpServeAddr, httpServeTimeout)
   723  		if err != nil {
   724  			ga.Fatalf("Error during HTTPServe: %v", err)
   725  		}
   726  	}()
   727  
   728  	// Child connects to host
   729  	hostname, err := os.Hostname()
   730  	if err != nil {
   731  		panic(err)
   732  	}
   733  
   734  	go func() {
   735  		defer ga.Done()
   736  		testImageArgs := []string{fmt.Sprintf("--exec=/inspect --get-http=%v", httpGetAddr)}
   737  		testImage := patchTestACI("rkt-inspect-networking.aci", testImageArgs...)
   738  		defer os.Remove(testImage)
   739  
   740  		cmd := fmt.Sprintf("%s --debug --insecure-options=image run --net=%v --mds-register=false %s", ctx.Cmd(), nt.Name, testImage)
   741  		child := ga.SpawnOrFail(cmd)
   742  		defer ga.WaitOrFail(child)
   743  
   744  		expectedRegex := `HTTP-Get received: (.*?)\r`
   745  		result, out, err := expectRegexWithOutput(child, expectedRegex)
   746  		if err != nil {
   747  			ga.Fatalf("Error: %v\nOutput: %v", err, out)
   748  		}
   749  
   750  		if result[1] != hostname {
   751  			ga.Fatalf("Hostname received by client `%v` doesn't match `%v`", result[1], hostname)
   752  		}
   753  	}()
   754  
   755  	ga.Wait()
   756  }
   757  
   758  //Test that the CNI execution environment matches the spec
   759  func NewNetCNIEnvTest() testutils.Test {
   760  	return testutils.TestFunc(func(t *testing.T) {
   761  		ctx := testutils.NewRktRunCtx()
   762  		defer ctx.Cleanup()
   763  
   764  		iface, _, err := testutils.GetNonLoIfaceWithAddrs(netlink.FAMILY_V4)
   765  		if err != nil {
   766  			t.Fatalf("Error while getting non-lo host interface: %v\n", err)
   767  		}
   768  		if iface.Name == "" {
   769  			t.Skipf("Cannot run test without non-lo host interface")
   770  		}
   771  
   772  		// Declares a network of type cniproxy, which will record state and
   773  		// proxy through to $X_REAL_PLUGIN
   774  		nt := networkTemplateT{
   775  			Name:      "bridge0",
   776  			Type:      "cniproxy",
   777  			Args:      []string{"X_LOG=output.json", "X_REAL_PLUGIN=bridge"},
   778  			IpMasq:    true,
   779  			IsGateway: true,
   780  			Master:    iface.Name,
   781  			Ipam: &ipamTemplateT{
   782  				Type:   "host-local",
   783  				Subnet: "11.11.3.0/24",
   784  				Routes: []map[string]string{
   785  					{"dst": "0.0.0.0/0"},
   786  				},
   787  			},
   788  		}
   789  
   790  		// bring the networking up, copy the proxy
   791  		netdir := prepareTestNet(t, ctx, nt)
   792  
   793  		appCmd := "--exec=/inspect -- --print-defaultgwv4 "
   794  		cmd := fmt.Sprintf("%s --debug --insecure-options=image run --net=%v --mds-register=false %s %s",
   795  			ctx.Cmd(), nt.NetParameter(), getInspectImagePath(), appCmd)
   796  		child := spawnOrFail(t, cmd)
   797  
   798  		expectedRegex := "DefaultGWv4: 11.11.3.1"
   799  
   800  		_, out, err := expectRegexTimeoutWithOutput(child, expectedRegex, 30*time.Second)
   801  		if err != nil {
   802  			t.Fatalf("Error: %v\nOutput: %v", err, out)
   803  		}
   804  		waitOrFail(t, child, 0)
   805  
   806  		// Parse the log file
   807  		cniLogFilename := filepath.Join(netdir, "output.json")
   808  		proxyLog, err := parseCNIProxyLog(cniLogFilename)
   809  		if err != nil {
   810  			t.Fatal("Failed to read cniproxy ADD log", err)
   811  		}
   812  		os.Remove(cniLogFilename)
   813  
   814  		// Check that the stdin matches the network config file
   815  		expectedConfig, err := ioutil.ReadFile(filepath.Join(netdir, nt.Name+".conf"))
   816  		if err != nil {
   817  			t.Fatal("Failed to read network configuration", err)
   818  		}
   819  
   820  		if string(expectedConfig) != proxyLog.Stdin {
   821  			t.Fatalf("CNI plugin stdin incorrect, expected <<%v>>, actual <<%v>>", expectedConfig, proxyLog.Stdin)
   822  		}
   823  
   824  		// compare the CNI env against a set of regexes
   825  		checkEnv := func(step string, expectedEnv, actualEnv map[string]string) {
   826  			for k, v := range expectedEnv {
   827  				actual, exists := actualEnv[k]
   828  				if !exists {
   829  					t.Fatalf("Step %s, expected proxy CNI arg %s but not found", step, k)
   830  				}
   831  
   832  				re, err := regexp.Compile(v)
   833  				if err != nil {
   834  					t.Fatalf("Step %s, invalid CNI env regex for key %s %v", step, k, err)
   835  				}
   836  				found := re.FindString(actual)
   837  				if found == "" {
   838  					t.Fatalf("step %s cni environment %s was %s but expected pattern %s", step, k, actual, v)
   839  				}
   840  			}
   841  		}
   842  
   843  		expectedEnv := map[string]string{
   844  			"CNI_VERSION":     `^0\.1\.0$`,
   845  			"CNI_COMMAND":     `^ADD$`,
   846  			"CNI_IFNAME":      `^eth\d$`,
   847  			"CNI_PATH":        "^" + netdir + ":/usr/lib/rkt/plugins/net:stage1/rootfs/usr/lib/rkt/plugins/net$",
   848  			"CNI_NETNS":       `^/var/run/netns/cni-`,
   849  			"CNI_CONTAINERID": `^[a-fA-F0-9-]{36}$`, //UUID, close enough
   850  		}
   851  		checkEnv("add", expectedEnv, proxyLog.EnvMap)
   852  
   853  		/*
   854  			Run rkt GC, ensure the CNI invocation looks sane
   855  		*/
   856  		ctx.RunGC()
   857  		proxyLog, err = parseCNIProxyLog(cniLogFilename)
   858  		if err != nil {
   859  			t.Fatal("Failed to read cniproxy DEL log", err)
   860  		}
   861  		os.Remove(cniLogFilename)
   862  
   863  		expectedEnv["CNI_COMMAND"] = `^DEL$`
   864  		checkEnv("del", expectedEnv, proxyLog.EnvMap)
   865  
   866  	})
   867  }
   868  
   869  // Test that CNI invocations which return DNS information are carried through to /etc/resolv.conf
   870  func NewNetCNIDNSTest() testutils.Test {
   871  	return testutils.TestFunc(func(t *testing.T) {
   872  		ctx := testutils.NewRktRunCtx()
   873  		defer ctx.Cleanup()
   874  
   875  		iface, _, err := testutils.GetNonLoIfaceWithAddrs(netlink.FAMILY_V4)
   876  		if err != nil {
   877  			t.Fatalf("Error while getting non-lo host interface: %v\n", err)
   878  		}
   879  		if iface.Name == "" {
   880  			t.Skipf("Cannot run test without non-lo host interface")
   881  		}
   882  
   883  		nt := networkTemplateT{
   884  			Name:      "bridge0",
   885  			Type:      "cniproxy",
   886  			Args:      []string{"X_LOG=output.json", "X_REAL_PLUGIN=bridge", "X_ADD_DNS=1"},
   887  			IpMasq:    true,
   888  			IsGateway: true,
   889  			Master:    iface.Name,
   890  			Ipam: &ipamTemplateT{
   891  				Type:   "host-local",
   892  				Subnet: "11.11.3.0/24",
   893  				Routes: []map[string]string{
   894  					{"dst": "0.0.0.0/0"},
   895  				},
   896  			},
   897  		}
   898  
   899  		// bring the networking up, copy the proxy
   900  		prepareTestNet(t, ctx, nt)
   901  
   902  		ga := testutils.NewGoroutineAssistant(t)
   903  		ga.Add(1)
   904  
   905  		go func() {
   906  			defer ga.Done()
   907  
   908  			appCmd := "--exec=/inspect -- --read-file --file-name=/etc/resolv.conf"
   909  			cmd := fmt.Sprintf("%s --debug --insecure-options=image run --net=%v --mds-register=false %s %s",
   910  				ctx.Cmd(), nt.NetParameter(), getInspectImagePath(), appCmd)
   911  			child := ga.SpawnOrFail(cmd)
   912  			defer ga.WaitOrFail(child)
   913  
   914  			expectedRegex := "nameserver 1.2.3.4"
   915  
   916  			_, out, err := expectRegexTimeoutWithOutput(child, expectedRegex, 30*time.Second)
   917  			if err != nil {
   918  				ga.Fatalf("Error: %v\nOutput: %v", err, out)
   919  			}
   920  		}()
   921  
   922  		ga.Wait()
   923  	})
   924  }
   925  
   926  // Test that `rkt run --dns` overrides CNI DNS
   927  func NewNetCNIDNSArgTest() testutils.Test {
   928  	return testutils.TestFunc(func(t *testing.T) {
   929  		ctx := testutils.NewRktRunCtx()
   930  		defer ctx.Cleanup()
   931  
   932  		iface, _, err := testutils.GetNonLoIfaceWithAddrs(netlink.FAMILY_V4)
   933  		if err != nil {
   934  			t.Fatalf("Error while getting non-lo host interface: %v\n", err)
   935  		}
   936  		if iface.Name == "" {
   937  			t.Skipf("Cannot run test without non-lo host interface")
   938  		}
   939  
   940  		nt := networkTemplateT{
   941  			Name:      "bridge0",
   942  			Type:      "cniproxy",
   943  			Args:      []string{"X_LOG=output.json", "X_REAL_PLUGIN=bridge", "X_ADD_DNS=1"},
   944  			IpMasq:    true,
   945  			IsGateway: true,
   946  			Master:    iface.Name,
   947  			Ipam: &ipamTemplateT{
   948  				Type:   "host-local",
   949  				Subnet: "11.11.3.0/24",
   950  				Routes: []map[string]string{
   951  					{"dst": "0.0.0.0/0"},
   952  				},
   953  			},
   954  		}
   955  
   956  		// bring the networking up, copy the proxy
   957  		prepareTestNet(t, ctx, nt)
   958  
   959  		appCmd := "--exec=/inspect -- --read-file --file-name=/etc/resolv.conf"
   960  		cmd := fmt.Sprintf("%s --debug --insecure-options=image run --net=%v --mds-register=false --dns=244.244.244.244 %s %s",
   961  			ctx.Cmd(), nt.NetParameter(), getInspectImagePath(), appCmd)
   962  		child := spawnOrFail(t, cmd)
   963  		defer waitOrFail(t, child, 0)
   964  
   965  		expectedRegex := "nameserver 244.244.244.244"
   966  
   967  		_, out, err := expectRegexTimeoutWithOutput(child, expectedRegex, 30*time.Second)
   968  		if err != nil {
   969  			t.Fatalf("Error: %v\nOutput: %v", err, out)
   970  		}
   971  	})
   972  }
   973  
   974  // Test that `rkt run --dns=none` means no resolv.conf is created, even when
   975  // CNI returns DNS informationparseHostsEntries(flagHosts)
   976  func NewNetCNIDNSArgNoneTest() testutils.Test {
   977  	return testutils.TestFunc(func(t *testing.T) {
   978  		ctx := testutils.NewRktRunCtx()
   979  		defer ctx.Cleanup()
   980  
   981  		iface, _, err := testutils.GetNonLoIfaceWithAddrs(netlink.FAMILY_V4)
   982  		if err != nil {
   983  			t.Fatalf("Error while getting non-lo host interface: %v\n", err)
   984  		}
   985  		if iface.Name == "" {
   986  			t.Skipf("Cannot run test without non-lo host interface")
   987  		}
   988  
   989  		nt := networkTemplateT{
   990  			Name:      "bridge0",
   991  			Type:      "cniproxy",
   992  			Args:      []string{"X_LOG=output.json", "X_REAL_PLUGIN=bridge", "X_ADD_DNS=1"},
   993  			IpMasq:    true,
   994  			IsGateway: true,
   995  			Master:    iface.Name,
   996  			Ipam: &ipamTemplateT{
   997  				Type:   "host-local",
   998  				Subnet: "11.11.3.0/24",
   999  				Routes: []map[string]string{
  1000  					{"dst": "0.0.0.0/0"},
  1001  				},
  1002  			},
  1003  		}
  1004  
  1005  		// bring the networking up, copy the proxy
  1006  		prepareTestNet(t, ctx, nt)
  1007  
  1008  		appCmd := "--exec=/inspect -- --stat-file --file-name=/etc/resolv.conf"
  1009  		cmd := fmt.Sprintf("%s --debug --insecure-options=image run --net=%v --mds-register=false --dns=none %s %s",
  1010  			ctx.Cmd(), nt.NetParameter(), getInspectImagePath(), appCmd)
  1011  		child := spawnOrFail(t, cmd)
  1012  		ctx.RegisterChild(child)
  1013  		defer waitOrFail(t, child, 254)
  1014  
  1015  		expectedRegex := `Cannot stat file "/etc/resolv.conf": stat /etc/resolv.conf: no such file or directory`
  1016  
  1017  		_, out, err := expectRegexTimeoutWithOutput(child, expectedRegex, 30*time.Second)
  1018  		if err != nil {
  1019  			t.Fatalf("Error: %v\nOutput: %v", err, out)
  1020  		}
  1021  	})
  1022  }
  1023  
  1024  func NewNetCustomPtpTest(runCustomDual bool) testutils.Test {
  1025  	return testutils.TestFunc(func(t *testing.T) {
  1026  		nt := networkTemplateT{
  1027  			Name:   "ptp0",
  1028  			Type:   "ptp",
  1029  			IpMasq: true,
  1030  			Ipam: &ipamTemplateT{
  1031  				Type:   "host-local",
  1032  				Subnet: "11.11.1.0/24",
  1033  				Routes: []map[string]string{
  1034  					{"dst": "0.0.0.0/0"},
  1035  				},
  1036  			},
  1037  		}
  1038  		testNetCustomNatConnectivity(t, nt)
  1039  		if runCustomDual {
  1040  			testNetCustomDual(t, nt)
  1041  		}
  1042  	})
  1043  }
  1044  
  1045  func NewNetCustomMacvlanTest() testutils.Test {
  1046  	return testutils.TestFunc(func(t *testing.T) {
  1047  		iface, _, err := testutils.GetNonLoIfaceWithAddrs(netlink.FAMILY_V4)
  1048  		if err != nil {
  1049  			t.Fatalf("Error while getting non-lo host interface: %v\n", err)
  1050  		}
  1051  		if iface.Name == "" {
  1052  			t.Skipf("Cannot run test without non-lo host interface")
  1053  		}
  1054  
  1055  		nt := networkTemplateT{
  1056  			Name:   "macvlan0",
  1057  			Type:   "macvlan",
  1058  			Master: iface.Name,
  1059  			Ipam: &ipamTemplateT{
  1060  				Type:   "host-local",
  1061  				Subnet: "11.11.2.0/24",
  1062  			},
  1063  		}
  1064  		testNetCustomDual(t, nt)
  1065  	})
  1066  }
  1067  
  1068  func NewNetCustomBridgeTest() testutils.Test {
  1069  	return testutils.TestFunc(func(t *testing.T) {
  1070  		iface, _, err := testutils.GetNonLoIfaceWithAddrs(netlink.FAMILY_V4)
  1071  		if err != nil {
  1072  			t.Fatalf("Error while getting non-lo host interface: %v\n", err)
  1073  		}
  1074  		if iface.Name == "" {
  1075  			t.Skipf("Cannot run test without non-lo host interface")
  1076  		}
  1077  
  1078  		nt := networkTemplateT{
  1079  			Name:      "bridge0",
  1080  			Type:      "bridge",
  1081  			IpMasq:    true,
  1082  			IsGateway: true,
  1083  			Master:    iface.Name,
  1084  			Ipam: &ipamTemplateT{
  1085  				Type:   "host-local",
  1086  				Subnet: "11.11.3.0/24",
  1087  				Routes: []map[string]string{
  1088  					{"dst": "0.0.0.0/0"},
  1089  				},
  1090  			},
  1091  		}
  1092  		testNetCustomNatConnectivity(t, nt)
  1093  		testNetCustomDual(t, nt)
  1094  	})
  1095  }
  1096  
  1097  func NewNetOverrideTest() testutils.Test {
  1098  	return testutils.TestFunc(func(t *testing.T) {
  1099  		ctx := testutils.NewRktRunCtx()
  1100  		defer ctx.Cleanup()
  1101  
  1102  		iface, _, err := testutils.GetNonLoIfaceWithAddrs(netlink.FAMILY_V4)
  1103  		if err != nil {
  1104  			t.Fatalf("Error while getting non-lo host interface: %v\n", err)
  1105  		}
  1106  		if iface.Name == "" {
  1107  			t.Skipf("Cannot run test without non-lo host interface")
  1108  		}
  1109  
  1110  		nt := networkTemplateT{
  1111  			Name:   "overridemacvlan",
  1112  			Type:   "macvlan",
  1113  			Master: iface.Name,
  1114  			Ipam: &ipamTemplateT{
  1115  				Type:   "host-local",
  1116  				Subnet: "11.11.4.0/24",
  1117  			},
  1118  		}
  1119  
  1120  		prepareTestNet(t, ctx, nt)
  1121  
  1122  		testImageArgs := []string{"--exec=/inspect --print-ipv4=eth0"}
  1123  		testImage := patchTestACI("rkt-inspect-networking1.aci", testImageArgs...)
  1124  		defer os.Remove(testImage)
  1125  
  1126  		expectedIP := "11.11.4.244"
  1127  
  1128  		cmd := fmt.Sprintf("%s --debug --insecure-options=image run --net=\"%s:IP=%s\" --mds-register=false %s", ctx.Cmd(), nt.Name, expectedIP, testImage)
  1129  		child := spawnOrFail(t, cmd)
  1130  		defer waitOrFail(t, child, 0)
  1131  
  1132  		expectedRegex := `IPv4: (\d+\.\d+\.\d+\.\d+)`
  1133  		result, out, err := expectRegexTimeoutWithOutput(child, expectedRegex, 30*time.Second)
  1134  		if err != nil {
  1135  			t.Fatalf("Error: %v\nOutput: %v", err, out)
  1136  			return
  1137  		}
  1138  
  1139  		containerIP := result[1]
  1140  		if expectedIP != containerIP {
  1141  			t.Fatalf("overriding IP did not work: Got %q but expected %q", containerIP, expectedIP)
  1142  		}
  1143  	})
  1144  }
  1145  
  1146  /*
  1147   * Pass the IP arg to the default networks, ensure it works
  1148   */
  1149  func NewNetDefaultIPArgTest() testutils.Test {
  1150  	doTest := func(netArg, expectedIP string, t *testing.T) {
  1151  		ctx := testutils.NewRktRunCtx()
  1152  		defer ctx.Cleanup()
  1153  
  1154  		appCmd := "--exec=/inspect -- --print-ipv4=eth0"
  1155  		cmd := fmt.Sprintf("%s --debug --insecure-options=image run --net=\"%s\" --mds-register=false %s %s",
  1156  			ctx.Cmd(), netArg, getInspectImagePath(), appCmd)
  1157  		child := spawnOrFail(t, cmd)
  1158  		defer waitOrFail(t, child, 0)
  1159  
  1160  		expectedRegex := `IPv4: (\d+\.\d+\.\d+\.\d+)`
  1161  		result, out, err := expectRegexTimeoutWithOutput(child, expectedRegex, 30*time.Second)
  1162  		if err != nil {
  1163  			t.Fatalf("Error: %v\nOutput: %v", err, out)
  1164  			return
  1165  		}
  1166  
  1167  		containerIP := result[1]
  1168  		if expectedIP != containerIP {
  1169  			t.Fatalf("--net=%s setting IP failed: Got %q but expected %q", netArg, containerIP, expectedIP)
  1170  		}
  1171  	}
  1172  	return testutils.TestFunc(func(t *testing.T) {
  1173  		doTest("default:IP=172.16.28.123", "172.16.28.123", t)
  1174  		doTest("default-restricted:IP=172.31.42.42", "172.31.42.42", t)
  1175  	})
  1176  }
  1177  
  1178  /*
  1179   * Try and start two containers with the same IP, ensure
  1180   * the second invocation fails
  1181   */
  1182  func NewNetIPConflictTest() testutils.Test {
  1183  	return testutils.TestFunc(func(t *testing.T) {
  1184  		ctx := testutils.NewRktRunCtx()
  1185  		defer ctx.Cleanup()
  1186  
  1187  		// Launch one container and grab the IP it uses -- and have it idle
  1188  		appCmd := "--exec=/inspect -- --print-ipv4=eth0  --serve-http=0.0.0.0:80"
  1189  		cmd1 := fmt.Sprintf("%s --debug --insecure-options=image run --mds-register=false %s %s",
  1190  			ctx.Cmd(), getInspectImagePath(), appCmd)
  1191  
  1192  		child1 := spawnOrFail(t, cmd1)
  1193  
  1194  		expectedRegex := `IPv4: (\d+\.\d+\.\d+\.\d+)`
  1195  		result, out, err := expectRegexTimeoutWithOutput(child1, expectedRegex, 30*time.Second)
  1196  		if err != nil {
  1197  			t.Fatalf("Error: %v\nOutput: %v", err, out)
  1198  			return
  1199  		}
  1200  		ip := result[1]
  1201  
  1202  		// Launch a second container with the same IP
  1203  		cmd2 := fmt.Sprintf("%s --debug --insecure-options=image run --net=\"default:IP=%s\" %s --exec=/inspect -- --print-ipv4=eth0",
  1204  			ctx.Cmd(), ip, getInspectImagePath())
  1205  		child2 := spawnOrFail(t, cmd2)
  1206  
  1207  		expectedOutput := fmt.Sprintf(`requested IP address "%s" is not available in network: default`, ip)
  1208  
  1209  		_, out, err = expectRegexTimeoutWithOutput(child2, expectedOutput, 10*time.Second)
  1210  		if err != nil {
  1211  			t.Fatalf("Error: %v\nOutput: %v", err, out)
  1212  			return
  1213  		}
  1214  
  1215  		// Clean up
  1216  		waitOrFail(t, child2, 254)
  1217  		syscall.Kill(child1.Cmd.Process.Pid, syscall.SIGTERM)
  1218  		waitOrFail(t, child1, 0)
  1219  	})
  1220  }
  1221  
  1222  func NewTestNetLongName() testutils.Test {
  1223  	return testutils.TestFunc(func(t *testing.T) {
  1224  		nt := networkTemplateT{
  1225  			Name:   "thisnameiswaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaytoolong",
  1226  			Type:   "ptp",
  1227  			IpMasq: true,
  1228  			Ipam: &ipamTemplateT{
  1229  				Type:   "host-local",
  1230  				Subnet: "11.11.6.0/24",
  1231  				Routes: []map[string]string{
  1232  					{"dst": "0.0.0.0/0"},
  1233  				},
  1234  			},
  1235  		}
  1236  		testNetCustomNatConnectivity(t, nt)
  1237  	})
  1238  }
  1239  
  1240  /*
  1241   * mockFlannelNetwork creates fake flannel network status file and configuration pointing to this network.
  1242   * We won't have connectivity, but we could check if: netName was correct and if default gateway was set.
  1243   */
  1244  func mockFlannelNetwork(t *testing.T, ctx *testutils.RktRunCtx) (string, networkTemplateT, error) {
  1245  	// write fake flannel info
  1246  	subnetPath := filepath.Join(ctx.DataDir(), "subnet.env")
  1247  	file, err := os.Create(subnetPath)
  1248  	if err != nil {
  1249  		return "", networkTemplateT{}, err
  1250  	}
  1251  	mockedFlannel := strings.Join([]string{
  1252  		"FLANNEL_NETWORK=11.11.0.0/16",
  1253  		"FLANNEL_SUBNET=11.11.3.1/24",
  1254  		"FLANNEL_MTU=1472",
  1255  		"FLANNEL_IPMASQ=true",
  1256  	}, "\n")
  1257  	if _, err = file.WriteString(mockedFlannel); err != nil {
  1258  		return "", networkTemplateT{}, err
  1259  	}
  1260  
  1261  	file.Close()
  1262  
  1263  	// write net config for "flannel" based network
  1264  	ntFlannel := networkTemplateT{
  1265  		Name:       "rkt.kubernetes.io",
  1266  		Type:       "flannel",
  1267  		SubnetFile: subnetPath,
  1268  		Delegate: &delegateTemplateT{
  1269  			IsDefaultGateway: true,
  1270  		},
  1271  	}
  1272  
  1273  	netdir := prepareTestNet(t, ctx, ntFlannel)
  1274  
  1275  	return netdir, ntFlannel, nil
  1276  }
  1277  
  1278  /*
  1279   * NewNetPreserveNetNameTest checks if netName is set if network is configured via flannel
  1280   */
  1281  func NewNetPreserveNetNameTest() testutils.Test {
  1282  	return testutils.TestFunc(func(t *testing.T) {
  1283  		ctx := testutils.NewRktRunCtx()
  1284  		defer ctx.Cleanup()
  1285  
  1286  		_, ntFlannel, err := mockFlannelNetwork(t, ctx)
  1287  		if err != nil {
  1288  			t.Errorf("Can't mock flannel network: %v", err)
  1289  		}
  1290  
  1291  		defer os.Remove(ntFlannel.SubnetFile)
  1292  
  1293  		podUUIDFile := filepath.Join(ctx.DataDir(), "pod_uuid")
  1294  		defer os.Remove(podUUIDFile)
  1295  
  1296  		// start container with 'flannel' network
  1297  		testImageArgs := []string{"--exec=/inspect"}
  1298  		testImage := patchTestACI("rkt-inspect-networking.aci", testImageArgs...)
  1299  		defer os.Remove(testImage)
  1300  
  1301  		cmd := fmt.Sprintf("%s --debug --insecure-options=image run --uuid-file-save=%s --net=%s --mds-register=false %s", ctx.Cmd(), podUUIDFile, ntFlannel.Name, testImage)
  1302  		spawnAndWaitOrFail(t, cmd, 0)
  1303  
  1304  		podUUID, err := ioutil.ReadFile(podUUIDFile)
  1305  		if err != nil {
  1306  			t.Fatalf("Can't read pod UUID: %v", err)
  1307  		}
  1308  
  1309  		// read net-info.json created for pod
  1310  		podDir := filepath.Join(ctx.DataDir(), "pods", "run", string(podUUID))
  1311  		podDirfd, err := syscall.Open(podDir, syscall.O_RDONLY|syscall.O_DIRECTORY, 0)
  1312  		if err != nil {
  1313  			t.Fatalf("Can't open pod directory for reading! %v", err)
  1314  		}
  1315  
  1316  		info, err := netinfo.LoadAt(podDirfd)
  1317  		if err != nil {
  1318  			t.Fatalf("Can't open net-info.json for reading: %v", err)
  1319  		}
  1320  
  1321  		if len(info) != 1 {
  1322  			t.Fatalf("Incorrect number of networks: %v", len(info))
  1323  		}
  1324  
  1325  		if info[0].NetName != ntFlannel.Name {
  1326  			t.Fatalf("Network '%s' not found!\nnetInfo[0]: %v\nnetInfo[1]: %v", ntFlannel.Name, info[0], info[1])
  1327  		}
  1328  	})
  1329  }
  1330  
  1331  /*
  1332   * NewNetDefaultGWTest checks if default gateway is correct if only configured network is one provided by flannel.
  1333   */
  1334  func NewNetDefaultGWTest() testutils.Test {
  1335  	return testutils.TestFunc(func(t *testing.T) {
  1336  		ctx := testutils.NewRktRunCtx()
  1337  		defer ctx.Cleanup()
  1338  
  1339  		_, ntFlannel, err := mockFlannelNetwork(t, ctx)
  1340  		if err != nil {
  1341  			t.Errorf("Can't mock flannel network: %v", err)
  1342  		}
  1343  
  1344  		defer os.Remove(ntFlannel.SubnetFile)
  1345  
  1346  		testImageArgs := []string{"--exec=/inspect --print-defaultgwv4"}
  1347  		testImage := patchTestACI("rkt-inspect-networking.aci", testImageArgs...)
  1348  		defer os.Remove(testImage)
  1349  
  1350  		cmd := fmt.Sprintf("%s --debug --insecure-options=image run --net=%s --mds-register=false %s", ctx.Cmd(), ntFlannel.Name, testImage)
  1351  		child := spawnOrFail(t, cmd)
  1352  		defer waitOrFail(t, child, 0)
  1353  
  1354  		expectedRegex := `DefaultGWv4: (\d+\.\d+\.\d+\.\d+)`
  1355  		if _, out, err := expectRegexTimeoutWithOutput(child, expectedRegex, time.Minute); err != nil {
  1356  			t.Fatalf("No default gateway!\nError: %v\nOutput: %v", err, out)
  1357  		}
  1358  	})
  1359  }