forked from square/gssh
/
gssh.go
149 lines (128 loc) · 3.52 KB
/
gssh.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
// Copyright (c) 2014 Square, Inc
package main
import (
"bufio"
"flag"
"fmt"
"log"
"os"
"strconv"
"github.com/square/gcmd"
"github.com/xaviershay/erg"
)
func main() {
// options
var maxflight, timeout int
var file, rangeexp string
var collapse bool
flag.IntVar(&maxflight, "m", 50,
"maximum number of parallel processes, default - 50")
flag.IntVar(&maxflight, "maxflight", 50,
"maximum number of parallel processes, default - 50")
flag.IntVar(&timeout, "t", 10, "timeout in seconds for initial conn, default - 10s")
flag.IntVar(&timeout, "timeout", 10,
"timeout in seconds for initial conn, default - 10s")
flag.StringVar(&file, "f", "",
"file to read hostnames from default - stdin")
flag.StringVar(&file, "file", "",
"file to read hostnames from default - stdin")
// TODO: should be able to use any grouping system
// TODO: perhaps use a cfg file to determine grouping system
flag.StringVar(&rangeexp, "r", "",
"rangeexp to read nodes from")
flag.BoolVar(&collapse, "c", false,
"collapse similar output - needs -r - be careful about memory usage")
flag.BoolVar(&collapse, "collapse", false,
"collapse similar output - needs -r - be careful about memory usage")
flag.Parse()
var nodes []string
var scanner *bufio.Scanner
var e *erg.Erg
// read list of nodes from grouping system
// or file/stdin
if rangeexp != "" || collapse {
host := "range"
port := 80
if envHost := os.Getenv("RANGE_HOST"); len(envHost) > 0 {
host = envHost
}
if envPort := os.Getenv("RANGE_PORT"); len(envPort) > 0 {
x, err := strconv.Atoi(envPort)
if err != nil {
log.Fatal("Invalid port in RANGE_PORT: ", envPort)
}
port = x
}
if envSSL := os.Getenv("RANGE_SSL"); len(envSSL) > 0 {
e = erg.NewWithSsl(host, port)
} else {
e = erg.New(host, port)
}
result, err := e.Expand(rangeexp)
nodes = result
if err != nil {
log.Fatal("Unable to expand: ", rangeexp, ":", err.Error())
}
} else {
if file == "" {
scanner = bufio.NewScanner(os.Stdin)
} else {
f, err := os.Open(file)
if err != nil {
log.Fatal("open:", file, err.Error())
}
scanner = bufio.NewScanner(f)
}
for scanner.Scan() {
nodes = append(nodes, scanner.Text())
}
}
timeout_arg := fmt.Sprintf("ConnectTimeout=%d", timeout)
args := []string{"__NODE__", "-n", "-o", timeout_arg} // marker
args = append(args, flag.Args()...)
g := gcmd.New(nodes, "ssh", args...)
g.Maxflight = maxflight
// collapse output if asked to
collapseStdout := map[string][]string{}
collapseStderr := map[string][]string{}
collapseExit := map[string][]string{}
if collapse {
g.StdoutHandler = func(node string, o string) {
_, ok := collapseStdout[o]
if !ok {
collapseStdout[o] = make([]string, 0)
}
collapseStdout[o] = append(collapseStdout[o], node)
}
g.StderrHandler = func(node string, o string) {
_, ok := collapseStderr[o]
if !ok {
collapseStderr[o] = make([]string, 0)
}
collapseStderr[o] = append(collapseStderr[o], node)
}
g.ExitHandler = func(node string, exit error) {
o := "success"
if exit != nil {
o = exit.Error()
}
_, ok := collapseExit[o]
if !ok {
collapseExit[o] = make([]string, 0)
}
collapseExit[o] = append(collapseExit[o], node)
}
}
g.Run()
if collapse {
for o, nodeArr := range collapseStdout {
fmt.Println(e.Compress(nodeArr), "STDOUT", o)
}
for o, nodeArr := range collapseStderr {
fmt.Println(e.Compress(nodeArr), "STDERR", o)
}
for o, nodeArr := range collapseExit {
fmt.Println(e.Compress(nodeArr), "STATUS", o)
}
}
}