-
Notifications
You must be signed in to change notification settings - Fork 223
/
socorro-cmd
executable file
·204 lines (159 loc) · 5.55 KB
/
socorro-cmd
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
#!/usr/bin/env python
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
"""
Script that helps discover and run the various runnable services and scripts in
Socorro.
This command should be run in the Docker-based local development environment in
one of the Docker containers.
Run "socorro-cmd --help" for help.
"""
import argparse
import importlib
import inspect
import sys
import textwrap
import types
import click
sys.path.insert(0, "bin")
sys.path.insert(0, "webapp")
def wrapped(text, width=80):
"""Wraps a block of text"""
return "\n".join(textwrap.wrap(text, width=width))
def showcommands_cmd(argv):
"""Sub command to show location of all command runners"""
parser = argparse.ArgumentParser(
description="Shows socorro-cmd subcommands and runner paths."
)
parser.parse_args(argv)
print("Available commands and runners for socorro-cmd:")
print("")
for group in COMMANDS:
print(f"{group.name}:")
for cmd, runner in group:
if not isinstance(runner, str):
runner = f"{runner.__name__} in {inspect.getfile(runner)}"
print(f" {cmd:<24} {runner}")
print("")
def django_cmd(cmd):
"""Run a Django command."""
def _django_cmd(argv):
argv = ["socorro-cmd", cmd] + argv
import manage
manage.main(argv)
return _django_cmd
def import_path(python_path):
"""Import a name from a module."""
module_path, name = python_path.rsplit(".", 1)
module = importlib.import_module(module_path)
return getattr(module, name)
class Group:
"""Defines a grouping of commands"""
def __init__(self, name, cmd_to_run_map):
self.name = name
self.cmd_to_run_map = cmd_to_run_map
def __iter__(self):
return iter(sorted(self.cmd_to_run_map.items()))
def lookup(self, cmd):
return self.cmd_to_run_map.get(cmd)
COMMANDS = [
Group(
"Crash processing utilities",
{
"fetch_crashids": import_path("socorro.scripts.fetch_crashids.main"),
"fetch_crash_data": import_path("socorro.scripts.fetch_crash_data.main"),
"reprocess": import_path("socorro.scripts.reprocess.main"),
"fetch_missing": import_path("socorro.scripts.fetch_missing.main"),
},
),
Group(
"Local development environment maintenance",
{
"upload_telemetry_schema": import_path("upload_telemetry_schema.main"),
"db": import_path("socorro.scripts.db.db_group"),
"es": import_path("es_cli.main"),
"legacy_es": import_path("legacy_es_cli.main"),
"pubsub": import_path("pubsub_cli.main"),
"gcs": import_path("gcs_cli.main"),
},
),
Group(
"Services",
{
"cronrun": django_cmd("cronrun"),
},
),
Group(
"Miscellaneous",
{
"showcommands": showcommands_cmd,
"signature": import_path("socorro.signature.cmd_signature.main"),
"signature-doc": import_path("socorro.signature.cmd_doc.main"),
},
),
]
def import_and_run(app):
"""Takes a runner and runs it."""
# If the app is a main function, we run it as is
if isinstance(app, types.FunctionType):
sys.exit(app(sys.argv[1:]))
# If the app is a click app, then run it; click apps exit on their
# own
if isinstance(app, click.core.Group):
app()
print("ProgrammerError: Unknown runner type")
sys.exit(1)
def build_epilog():
"""Builds the epilog containing the groups and commands"""
output = []
output.append("Available commands:")
output.append("\n")
for group in COMMANDS:
output.append(f"{group.name}:")
for cmd, _ in group:
output.append(f" {cmd}")
output.append("\n")
output.append("")
output.append('Type "socorro-cmd CMD --help" for help on any command.')
output.append("")
output.append(
wrapped(
"All commands need to be run in a Docker container. Service commands are run in "
'the related container. For example, the "processor" command should run in the '
'"processor" container. All other commands should be run in the processor '
"container."
)
)
output.append("")
output.append(
wrapped("For more documentation, see <https://socorro.readthedocs.io/>.")
)
return "\n".join(output)
def get_runner(cmd):
"""Given a cmd, returns the runner"""
for group in COMMANDS:
runner = group.lookup(cmd)
if runner is not None:
return runner
def cmd_main():
# Build a basic parser so we can take advantage of --help
parser = argparse.ArgumentParser(
prog="socorro-cmd",
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog=build_epilog(),
)
parser.add_argument("cmd", nargs="?", help="command to run")
parser.add_argument("arg", nargs="*", help="command arguments")
if not sys.argv[1:] or sys.argv[1] in ("-h", "--help"):
parser.print_help()
return 0
cmd = sys.argv[1]
runner = get_runner(cmd)
if runner is None:
parser.error(f"{cmd!r} is not a valid command; see --help for command list")
# Rewrite sys.argv so anything that parses sys.argv has the right one
sys.argv = [sys.argv[0] + " " + cmd] + sys.argv[2:]
import_and_run(runner)
if __name__ == "__main__":
cmd_main()