k8s.io/kubernetes@v1.31.0-alpha.0.0.20240520171757-56147500dadc/test/e2e_node/remote/run_remote_suite.go (about) 1 /* 2 Copyright 2016 The Kubernetes 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 package remote 18 19 import ( 20 "flag" 21 "fmt" 22 "log" 23 "os" 24 "os/exec" 25 "os/signal" 26 "strings" 27 "sync" 28 29 "k8s.io/klog/v2" 30 ) 31 32 var mode = flag.String("mode", "gce", "Mode to operate in. One of gce|ssh. Defaults to gce") 33 var testArgs = flag.String("test_args", "", "Space-separated list of arguments to pass to Ginkgo test runner.") 34 var instanceNamePrefix = flag.String("instance-name-prefix", "", "prefix for instance names") 35 var imageConfigFile = flag.String("image-config-file", "", "yaml file describing images to run") 36 var imageConfigDir = flag.String("image-config-dir", "", "(optional) path to image config files") 37 var images = flag.String("images", "", "images to test") 38 var hosts = flag.String("hosts", "", "hosts to test") 39 var cleanup = flag.Bool("cleanup", true, "If true remove files from remote hosts and delete temporary instances") 40 var deleteInstances = flag.Bool("delete-instances", true, "If true, delete any instances created") 41 var buildOnly = flag.Bool("build-only", false, "If true, build e2e_node_test.tar.gz and exit.") 42 var gubernator = flag.Bool("gubernator", false, "If true, output Gubernator link to view logs") 43 var ginkgoFlags = flag.String("ginkgo-flags", "", "Passed to ginkgo to specify additional flags such as --skip=.") 44 var ( 45 arc Archive 46 ) 47 48 // Archive contains path info in the archive. 49 type Archive struct { 50 sync.Once 51 path string 52 err error 53 } 54 55 func getFlag(name string) string { 56 lookup := flag.Lookup(name) 57 if lookup == nil { 58 return "" 59 } 60 return lookup.Value.String() 61 } 62 63 func RunRemoteTestSuite(testSuite TestSuite) { 64 // Listen for SIGINT and ignore the first one. In case SIGINT is sent to this 65 // process and all its children, we ignore it here, while our children ssh connections 66 // are stopped. This allows us to gather artifacts and print out test state before 67 // being killed. 68 c := make(chan os.Signal, 2) 69 signal.Notify(c, os.Interrupt) 70 go func() { 71 <-c 72 fmt.Printf("Received SIGINT. Will exit on next SIGINT.\n") 73 <-c 74 fmt.Printf("Received another SIGINT. Will exit.\n") 75 os.Exit(1) 76 }() 77 78 if *buildOnly { 79 // Build the archive and exit 80 CreateTestArchive(testSuite, 81 getFlag("system-spec-name"), 82 getFlag("kubelet-config-file")) 83 return 84 } 85 86 // Append some default ginkgo flags. We use similar defaults here as hack/ginkgo-e2e.sh 87 allGinkgoFlags := fmt.Sprintf("%s --no-color -v", *ginkgoFlags) 88 fmt.Printf("Will use ginkgo flags as: %s", allGinkgoFlags) 89 90 var runner Runner 91 cfg := Config{ 92 InstanceNamePrefix: *instanceNamePrefix, 93 ImageConfigFile: *imageConfigFile, 94 ImageConfigDir: *imageConfigDir, 95 Images: splitCommaList(*images), 96 Hosts: parseHostsList(*hosts), 97 GinkgoFlags: allGinkgoFlags, 98 DeleteInstances: *deleteInstances, 99 Cleanup: *cleanup, 100 TestArgs: *testArgs, 101 ExtraEnvs: getFlag("extra-envs"), 102 RuntimeConfig: getFlag("runtime-config"), 103 SystemSpecName: getFlag("system-spec-name"), 104 } 105 106 var sshRunner Runner 107 108 if *mode == "ssh" { 109 runner = NewSSHRunner(cfg) 110 } else { 111 getRunner, err := GetRunner(*mode) 112 if err != nil { 113 klog.Fatalf("getting runner mode %q : %v", *mode, err) 114 } 115 runner = getRunner(cfg) 116 sshRunner = NewSSHRunner(cfg) 117 } 118 119 if err := runner.Validate(); err != nil { 120 klog.Fatalf("validating remote config, %s", err) 121 } 122 123 // Setup coloring 124 stat, _ := os.Stdout.Stat() 125 useColor := (stat.Mode() & os.ModeCharDevice) != 0 126 blue := "" 127 noColour := "" 128 if useColor { 129 blue = "\033[0;34m" 130 noColour = "\033[0m" 131 } 132 133 results := make(chan *TestResult) 134 135 path, err := arc.getArchive(testSuite) 136 if err != nil { 137 log.Fatalf("unable to create test archive: %s", err) 138 } 139 defer arc.deleteArchive() 140 141 running := runner.StartTests(testSuite, path, results) 142 // You can potentially run SSH based tests while running image based test as well. The GCE provider does this, see 143 // test-e2e-node.sh. 144 if sshRunner != nil && len(cfg.Hosts) > 0 { 145 running += sshRunner.StartTests(testSuite, path, results) 146 } 147 148 // Wait for all tests to complete and emit the results 149 errCount := 0 150 exitOk := true 151 for i := 0; i < running; i++ { 152 tr := <-results 153 host := tr.Host 154 fmt.Println() // Print an empty line 155 fmt.Printf("%s>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>%s\n", blue, noColour) 156 fmt.Printf("%s> START TEST >%s\n", blue, noColour) 157 fmt.Printf("%s>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>%s\n", blue, noColour) 158 fmt.Printf("Start Test Suite on Host %s\n", host) 159 fmt.Printf("%s\n", tr.Output) 160 if tr.Err != nil { 161 errCount++ 162 fmt.Printf("Failure Finished Test Suite on Host %s. Refer to artifacts directory for ginkgo log for this host.\n%v\n", host, tr.Err) 163 } else { 164 fmt.Printf("Success Finished Test Suite on Host %s. Refer to artifacts directory for ginkgo log for this host.\n", host) 165 } 166 exitOk = exitOk && tr.ExitOK 167 fmt.Printf("%s<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<%s\n", blue, noColour) 168 fmt.Printf("%s< FINISH TEST <%s\n", blue, noColour) 169 fmt.Printf("%s<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<%s\n", blue, noColour) 170 fmt.Println() // Print an empty line 171 } 172 // Set the exit code if there were failures 173 if !exitOk { 174 fmt.Printf("Failure: %d errors encountered.\n", errCount) 175 callGubernator(*gubernator) 176 arc.deleteArchive() 177 os.Exit(1) 178 } 179 callGubernator(*gubernator) 180 } 181 182 func splitCommaList(s string) []string { 183 if len(s) == 0 { 184 return nil 185 } 186 return strings.Split(s, ",") 187 } 188 189 func callGubernator(gubernator bool) { 190 if gubernator { 191 fmt.Println("Running gubernator.sh") 192 output, err := exec.Command("./test/e2e_node/gubernator.sh", "y").Output() 193 194 if err != nil { 195 fmt.Println("gubernator.sh Failed") 196 fmt.Println(err) 197 return 198 } 199 fmt.Printf("%s", output) 200 } 201 return 202 } 203 204 func (a *Archive) getArchive(suite TestSuite) (string, error) { 205 a.Do(func() { 206 a.path, a.err = CreateTestArchive(suite, 207 getFlag("system-spec-name"), 208 getFlag("kubelet-config-file")) 209 }) 210 return a.path, a.err 211 } 212 213 func (a *Archive) deleteArchive() { 214 path, err := a.getArchive(nil) 215 if err != nil { 216 return 217 } 218 os.Remove(path) 219 } 220 221 // parseHostsList splits a host list of the form a=1.2.3.4,b=5.6.7.8 into the list of hosts [a,b] while registering the 222 // given addresses 223 func parseHostsList(hostList string) []string { 224 if len(hostList) == 0 { 225 return nil 226 } 227 hosts := strings.Split(hostList, ",") 228 var hostsOnly []string 229 for _, host := range hosts { 230 segs := strings.Split(host, "=") 231 if len(segs) == 2 { 232 AddHostnameIP(segs[0], segs[1]) 233 } else if len(segs) > 2 { 234 klog.Fatalf("invalid format of host %q", hostList) 235 } 236 hostsOnly = append(hostsOnly, segs[0]) 237 } 238 return hostsOnly 239 }