github.com/coreos/mantle@v0.13.0/kola/tests/misc/network.go (about)

     1  // Copyright 2016 CoreOS, Inc.
     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  package misc
    16  
    17  import (
    18  	"fmt"
    19  	"regexp"
    20  	"strings"
    21  	"time"
    22  
    23  	"github.com/coreos/go-semver/semver"
    24  
    25  	"github.com/coreos/mantle/kola/cluster"
    26  	"github.com/coreos/mantle/kola/register"
    27  	"github.com/coreos/mantle/platform/conf"
    28  	"github.com/coreos/mantle/util"
    29  )
    30  
    31  func init() {
    32  	register.Register(&register.Test{
    33  		Run:         NetworkListeners,
    34  		ClusterSize: 1,
    35  		Name:        "cl.network.listeners",
    36  		Distros:     []string{"cl"},
    37  		// be sure to notice listeners in the docker stack
    38  		UserData: conf.ContainerLinuxConfig(`systemd:
    39    units:
    40      - name: docker.service
    41        enabled: true`),
    42  		MinVersion:       semver.Version{Major: 1967},
    43  		ExcludePlatforms: []string{"qemu-unpriv"},
    44  	})
    45  	register.Register(&register.Test{
    46  		Run:              NetworkListeners,
    47  		ClusterSize:      1,
    48  		Name:             "cl.network.listeners.legacy",
    49  		Distros:          []string{"cl"},
    50  		EndVersion:       semver.Version{Major: 1967},
    51  		ExcludePlatforms: []string{"qemu-unpriv"},
    52  	})
    53  	register.Register(&register.Test{
    54  		Run:              NetworkInitramfsSecondBoot,
    55  		ClusterSize:      1,
    56  		Name:             "cl.network.initramfs.second-boot",
    57  		ExcludePlatforms: []string{"do"},
    58  		Distros:          []string{"cl"},
    59  	})
    60  }
    61  
    62  type listener struct {
    63  	// udp or tcp; note each v4 variant will also match 'v6'
    64  	protocol string
    65  	port     string
    66  	process  string
    67  }
    68  
    69  func checkListeners(c cluster.TestCluster, expectedListeners []listener) error {
    70  	m := c.Machines()[0]
    71  
    72  	output := c.MustSSH(m, "sudo netstat -plutn")
    73  
    74  	processes := strings.Split(string(output), "\n")
    75  	// verify header is as expected
    76  	if len(processes) < 2 {
    77  		c.Fatalf("expected at least two lines of nestat output: %q", output)
    78  	}
    79  	if processes[0] != "Active Internet connections (only servers)" {
    80  		c.Fatalf("netstat output has changed format: %q", output)
    81  	}
    82  	if !regexp.MustCompile(`Proto\s+Recv-Q\s+Send-Q\s+Local Address\s+Foreign Address\s+State\s+PID/Program name`).MatchString(processes[1]) {
    83  		c.Fatalf("netstat output has changed format: %q", output)
    84  	}
    85  	// skip header
    86  	processes = processes[2:]
    87  
    88  NextProcess:
    89  	for _, line := range processes {
    90  		parts := strings.Fields(line)
    91  		// One gotcha: udp's 'state' field is optional, so it's possible to have 6
    92  		// or 7 parts depending on that.
    93  		if len(parts) != 6 && len(parts) != 7 {
    94  			c.Fatalf("unexpected number of parts on line: %q in output %q", line, output)
    95  		}
    96  		proto := parts[0]
    97  		portdata := strings.Split(parts[3], ":")
    98  		port := portdata[len(portdata)-1]
    99  		pidProgramParts := strings.SplitN(parts[len(parts)-1], "/", 2)
   100  		if len(pidProgramParts) != 2 {
   101  			c.Errorf("%v did not contain pid and program parts; full output: %q", parts[6], output)
   102  			continue
   103  		}
   104  		pid, process := pidProgramParts[0], pidProgramParts[1]
   105  
   106  		for _, expected := range expectedListeners {
   107  			if strings.HasPrefix(proto, expected.protocol) && // allow expected tcp to match tcp6
   108  				expected.port == port &&
   109  				expected.process == process {
   110  				// matches expected process
   111  				continue NextProcess
   112  			}
   113  		}
   114  
   115  		if process[0] == '(' {
   116  			c.Logf("Ignoring %q listener process: %q (pid %s) on %q", proto, process, pid, port)
   117  			continue
   118  		}
   119  
   120  		c.Logf("full netstat output: %q", output)
   121  		return fmt.Errorf("Unexpected listener process: %q", line)
   122  	}
   123  
   124  	return nil
   125  }
   126  
   127  func NetworkListeners(c cluster.TestCluster) {
   128  	expectedListeners := []listener{
   129  		{"tcp", "22", "systemd"},          // ssh
   130  		{"udp", "68", "systemd-network"},  // dhcp6-client
   131  		{"udp", "546", "systemd-network"}, // bootpc
   132  	}
   133  	checkList := func() error {
   134  		return checkListeners(c, expectedListeners)
   135  	}
   136  	if err := util.Retry(3, 5*time.Second, checkList); err != nil {
   137  		c.Errorf(err.Error())
   138  	}
   139  }
   140  
   141  // Verify that networking is not started in the initramfs on the second boot.
   142  // https://github.com/coreos/bugs/issues/1768
   143  func NetworkInitramfsSecondBoot(c cluster.TestCluster) {
   144  	m := c.Machines()[0]
   145  
   146  	m.Reboot()
   147  
   148  	// get journal lines from the current boot
   149  	output := c.MustSSH(m, "journalctl -b 0 -o cat -u initrd-switch-root.target -u systemd-networkd.service")
   150  	lines := strings.Split(string(output), "\n")
   151  
   152  	// verify that the network service was started
   153  	found := false
   154  	for _, line := range lines {
   155  		if line == "Started Network Service." {
   156  			found = true
   157  			break
   158  		}
   159  	}
   160  	if !found {
   161  		c.Fatal("couldn't find log entry for networkd startup")
   162  	}
   163  
   164  	// check that we exited the initramfs first
   165  	if lines[0] != "Reached target Switch Root." {
   166  		c.Fatal("networkd started in initramfs")
   167  	}
   168  }