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(®ister.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(®ister.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(®ister.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 }