github.com/coreos/mantle@v0.13.0/kola/cluster/cluster.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 cluster 16 17 import ( 18 "bytes" 19 "fmt" 20 "os" 21 "path/filepath" 22 "strings" 23 24 "github.com/coreos/mantle/harness" 25 "github.com/coreos/mantle/platform" 26 ) 27 28 // TestCluster embedds a Cluster to provide platform independant helper 29 // methods. 30 type TestCluster struct { 31 *harness.H 32 platform.Cluster 33 NativeFuncs []string 34 35 // If set to true and a sub-test fails all future sub-tests will be skipped 36 FailFast bool 37 hasFailure bool 38 } 39 40 // Run runs f as a subtest and reports whether f succeeded. 41 func (t *TestCluster) Run(name string, f func(c TestCluster)) bool { 42 if t.FailFast && t.hasFailure { 43 return t.H.Run(name, func(h *harness.H) { 44 func(c TestCluster) { 45 c.Skip("A previous test has already failed") 46 }(TestCluster{H: h, Cluster: t.Cluster}) 47 }) 48 } 49 t.hasFailure = !t.H.Run(name, func(h *harness.H) { 50 f(TestCluster{H: h, Cluster: t.Cluster}) 51 }) 52 return !t.hasFailure 53 54 } 55 56 // RunNative runs a registered NativeFunc on a remote machine 57 func (t *TestCluster) RunNative(funcName string, m platform.Machine) bool { 58 command := fmt.Sprintf("./kolet run %q %q", t.H.Name(), funcName) 59 return t.Run(funcName, func(c TestCluster) { 60 client, err := m.SSHClient() 61 if err != nil { 62 c.Fatalf("kolet SSH client: %v", err) 63 } 64 defer client.Close() 65 66 session, err := client.NewSession() 67 if err != nil { 68 c.Fatalf("kolet SSH session: %v", err) 69 } 70 defer session.Close() 71 72 b, err := session.CombinedOutput(command) 73 b = bytes.TrimSpace(b) 74 if len(b) > 0 { 75 t.Logf("kolet:\n%s", b) 76 } 77 if err != nil { 78 c.Errorf("kolet: %v", err) 79 } 80 }) 81 } 82 83 // ListNativeFunctions returns a slice of function names that can be executed 84 // directly on machines in the cluster. 85 func (t *TestCluster) ListNativeFunctions() []string { 86 return t.NativeFuncs 87 } 88 89 // DropFile places file from localPath to ~/ on every machine in cluster 90 func (t *TestCluster) DropFile(localPath string) error { 91 in, err := os.Open(localPath) 92 if err != nil { 93 return err 94 } 95 defer in.Close() 96 97 for _, m := range t.Machines() { 98 if _, err := in.Seek(0, 0); err != nil { 99 return err 100 } 101 if err := platform.InstallFile(in, m, filepath.Base(localPath)); err != nil { 102 return err 103 } 104 } 105 return nil 106 } 107 108 // SSH runs a ssh command on the given machine in the cluster. It differs from 109 // Machine.SSH in that stderr is written to the test's output as a 'Log' line. 110 // This ensures the output will be correctly accumulated under the correct 111 // test. 112 func (t *TestCluster) SSH(m platform.Machine, cmd string) ([]byte, error) { 113 stdout, stderr, err := m.SSH(cmd) 114 115 if len(stderr) > 0 { 116 for _, line := range strings.Split(string(stderr), "\n") { 117 t.Log(line) 118 } 119 } 120 121 return stdout, err 122 } 123 124 // MustSSH runs a ssh command on the given machine in the cluster, writes 125 // its stderr to the test's output as a 'Log' line, fails the test if the 126 // command is unsuccessful, and returns the command's stdout. 127 func (t *TestCluster) MustSSH(m platform.Machine, cmd string) []byte { 128 out, err := t.SSH(m, cmd) 129 if err != nil { 130 t.Fatalf("%q failed: output %s, status %v", cmd, out, err) 131 } 132 return out 133 }