-
Notifications
You must be signed in to change notification settings - Fork 27
Expand file tree
/
Copy pathserviced-tests.py
More file actions
executable file
·257 lines (194 loc) · 8.27 KB
/
serviced-tests.py
File metadata and controls
executable file
·257 lines (194 loc) · 8.27 KB
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
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
#!/usr/bin/env python
import sys
import tempfile
import time
import os
import subprocess
import argparse
import logging
from contextlib import contextmanager
import uuid
log = logging.getLogger("serviced-tests")
SERVICED_ROOT = os.path.dirname(os.path.abspath(__file__))
DEVNULL = open(os.devnull, 'w')
def fail(msg):
log.critical(msg)
sys.exit(1)
def which(prog):
return subprocess.check_output("which %s" % prog, shell=True).strip()
@contextmanager
def elastic_server(port):
try:
log.info("Starting elastic on port %d " % port)
container_name = str(uuid.uuid4())
# TODO: handle already started
# TODO: Get image name from serviced binary or isvcs.go
# TODO: Wait for start more betterly
master_nodes = node_name = "elasticsearch-serviced"
cluster_name = str(uuid.uuid4())
cmd = ["docker", "run", "-d", "--name", container_name, "--user", "1001:1001",
"--env", "ES_JAVA_HOME=/opt/elasticsearch-serviced/jdk",
"-p", "%d:9200" % port, "zenoss/serviced-isvcs:v75-dev",
"sh", "-c", "/opt/elasticsearch-serviced/bin/elasticsearch",
"-E", "cluster.initial_master_nodes=%s" % master_nodes,
"-E", "node.name=%s" % node_name,
"-E", "cluster.name=%s" % cluster_name]
subprocess.call(cmd)
time.sleep(10)
yield
finally:
log.info("Stopping elastic")
subprocess.call(["docker", "stop", container_name])
@contextmanager
def dummy(*args, **kwargs):
yield
def ensure_tool(executable, importpath):
try:
return which(executable)
except subprocess.CalledProcessError:
log.info("Installing %s tool" % executable)
subprocess.call(["go", "install", importpath])
return which(executable)
def has_dm_deferred_remove():
"""
Test whether libdevmapper.h is new enough to support deferred remove
functionality by compiling a file to see if the function is defined.
"""
cmd = """
command -v gcc && ! (
cat <<EOF | gcc -ldevmapper -xc -
#include <libdevmapper.h>
int main() { dm_task_deferred_remove(NULL); }
EOF
)
"""
try:
subprocess.check_call(cmd, shell=True, stdout=DEVNULL, stderr=subprocess.STDOUT)
return False
except subprocess.CalledProcessError:
return True
def args():
"""
--all (some subset that is useful for someone)
--packages (maybe positional?)
"""
parser = argparse.ArgumentParser("serviced-tests")
parser.add_argument("-v", "--verbose", action="store_true", help="verbose logging")
types = parser.add_argument_group("Test Type")
types.add_argument("--unit", action="store_true", help="pass the 'unit' build tag")
types.add_argument("--integration", action="store_true", help="pass the 'integration' build tag")
options = parser.add_argument_group("Test Options")
options.add_argument("--quick", action="store_true", help="don't run tests with the '!quick' build constraint")
options.add_argument("--root", action="store_true", help="run the tests as the root user")
options.add_argument("--race", action="store_true", help="run tests with race detection")
options.add_argument("--cover", action="store_true", help="run tests with coverage")
options.add_argument("--tag", action="append", help="optional extra build tag (may be specified multiple times)")
options.add_argument("--include_vendor", action="store_true", dest="include_vendor", help="run tests against the vendor directory")
coverage = parser.add_argument_group("Coverage Options")
coverage.add_argument("--cover-html", required=False, help="output file for HTML coverage report")
coverage.add_argument("--cover-xml", required=False, help="output file for Cobertura coverage report")
fixtures = parser.add_argument_group("Fixture Options")
fixtures.add_argument("--elastic", action="store_true", help="start an elastic server before the test run")
fixtures.add_argument("--elastic-port", type=int, help="elastic server port", default=9202)
parser.add_argument("--packages", nargs="*", help="serviced packages to test, relative to the serviced root (defaults to ./...)")
parser.add_argument("arguments", nargs=argparse.REMAINDER, help="optional arguments to be passed through to the test runner")
return parser.parse_args()
def build_tags(options):
tags = options.tag or []
# We always need the daemon tag
tags.append("daemon")
if not has_dm_deferred_remove():
tags.append("libdm_no_deferred_remove")
if options.unit:
tags.append("unit")
if options.integration:
tags.append('integration')
if options.quick:
tags.append('quick')
if options.root:
tags.append('root')
log.debug("Using build tags: %s" % tags)
return tags
def main(options):
logging.basicConfig(level=logging.DEBUG if options.verbose else logging.INFO)
if not any((options.unit, options.integration)):
fail("No tests were specified to run. Please pass at least one of --unit or --integration.")
log.debug("Running tests under serviced in %s" % SERVICED_ROOT)
env = os.environ
env["SERVICED_HOME"] = SERVICED_ROOT
#set environment variable for serviced log directory
env["SERVICED_LOG_PATH"] = os.path.join(SERVICED_ROOT, "var_log_serviced")
# Unset EDITOR so CLI tests won't fail
env.pop("EDITOR", None)
tags = build_tags(options)
args = {}
if options.cover:
if options.race:
fail("--race and --cover are mutually exclusive.")
runner = ensure_tool("gocov", "github.com/axw/gocov/gocov@latest")
log.debug("Using gocov executable %s" % runner)
if options.cover_html:
ensure_tool("gocov-html", "github.com/matm/gocov-html/cmd/gocov-html@latest")
if options.cover_xml:
ensure_tool("gocov-xml", "github.com/AlekSi/gocov-xml@latest")
stdout = tempfile.NamedTemporaryFile()
log.debug("Writing temporary coverage output to %s" % stdout.name)
args["stdout"] = stdout
else:
runner = which("go")
log.debug("Using go executable %s" % runner)
# TODO: Get a sudo session set up with an interactive proc
cmd = ["sudo", "-E", "PATH=%s" % env["PATH"]] if options.root else []
cmd.extend([runner, "test", "-v", "-timeout", "360s", "-tags", " ".join(tags)])
usep1 = False
if options.integration:
if options.cover:
env["GOMAXPROCS"] = "1"
else:
usep1 = True
if options.race:
log.debug("Running with race detection")
env["GORACE"] = "history_size=7 halt_on_error=1"
cmd.append("-race")
usep1 = True
if usep1:
cmd.extend(['-p', '1'])
packages = options.packages
if not packages:
if options.include_vendor:
packages = "./..."
else:
packages = subprocess.check_output("go list ./... | grep -v vendor", cwd=SERVICED_ROOT, shell=True).splitlines()
cmd.extend(packages)
passthru = options.arguments
if passthru and passthru[0] == "--":
passthru = passthru[1:]
cmd.extend(passthru)
log.debug("Running command: %s" % cmd)
log.debug("Running in directory: %s" % SERVICED_ROOT)
fixture = elastic_server if options.elastic else dummy
with fixture(options.elastic_port):
try:
subprocess.check_call(
cmd,
env=env,
cwd=SERVICED_ROOT,
**args
)
except (subprocess.CalledProcessError, KeyboardInterrupt):
sys.exit(1)
if options.cover_html:
log.debug("Converting coverage to HTML")
with open(options.cover_html, 'w') as output:
subprocess.call(["gocov-html", stdout.name], stdout=output)
log.info("HTML output written to %s" % options.cover_html)
if options.cover_xml:
log.debug("Converting coverage to Cobertura XML")
with open(options.cover_xml, 'w') as output:
proc = subprocess.Popen(["gocov-xml", stdout.name], stdout=output, stdin=subprocess.PIPE)
stdout.seek(0)
proc.communicate(stdout.read())
log.info("Cobertura output written to %s" % options.cover_xml)
if __name__ == "__main__":
options = args()
main(options)