Skip to content

Commit 73485e9

Browse files
author
Vladimir Kotal
committed
add command handling to mirror.py
- refactor disabled command handling - add tests fixes #2973
1 parent 45c5af7 commit 73485e9

File tree

6 files changed

+144
-24
lines changed

6 files changed

+144
-24
lines changed

opengrok-tools/setup.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@ def readme():
4747
'pytest',
4848
'GitPython',
4949
'pytest-xdist',
50+
'mockito',
51+
'pytest-mockito',
5052
],
5153
entry_points={
5254
'console_scripts': [

opengrok-tools/src/main/python/opengrok_tools/utils/commandsequence.py

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
SUCCESS_EXITVAL
3030
)
3131
from .restful import call_rest_api
32+
from .patterns import PROJECT_SUBST, COMMAND_PROPERTY
3233
import re
3334

3435

@@ -72,7 +73,6 @@ def fill(self, retcodes, outputs, failed):
7273

7374

7475
class CommandSequence(CommandSequenceBase):
75-
PROJECT_SUBST = '%PROJECT%'
7676

7777
re_program = re.compile('ERROR[:]*\\s+')
7878

@@ -87,11 +87,11 @@ def run_command(self, command):
8787
"""
8888
Execute a command and return its return code.
8989
"""
90-
command_args = command.get("command")
90+
command_args = command.get(COMMAND_PROPERTY)
9191
cmd = Command(command_args,
9292
env_vars=command.get("env"),
9393
resource_limits=command.get("limits"),
94-
args_subst={self.PROJECT_SUBST: self.name},
94+
args_subst={PROJECT_SUBST: self.name},
9595
args_append=[self.name], excl_subst=True)
9696
cmd.execute()
9797
self.retcodes[str(cmd)] = cmd.getretcode()
@@ -116,8 +116,8 @@ def run(self):
116116
"""
117117

118118
for command in self.commands:
119-
if is_web_uri(command.get("command")[0]):
120-
call_rest_api(command, self.PROJECT_SUBST, self.name)
119+
if is_web_uri(command.get(COMMAND_PROPERTY)[0]):
120+
call_rest_api(command, PROJECT_SUBST, self.name)
121121
else:
122122
retcode = self.run_command(command)
123123

@@ -155,14 +155,14 @@ def run_cleanup(self):
155155
return
156156

157157
for cleanup_cmd in self.cleanup:
158-
if is_web_uri(cleanup_cmd.get("command")[0]):
159-
call_rest_api(cleanup_cmd, self.PROJECT_SUBST, self.name)
158+
if is_web_uri(cleanup_cmd.get(COMMAND_PROPERTY)[0]):
159+
call_rest_api(cleanup_cmd, PROJECT_SUBST, self.name)
160160
else:
161-
command_args = cleanup_cmd.get("command")
161+
command_args = cleanup_cmd.get(COMMAND_PROPERTY)
162162
self.logger.debug("Running cleanup command '{}'".
163163
format(command_args))
164164
cmd = Command(command_args,
165-
args_subst={self.PROJECT_SUBST: self.name},
165+
args_subst={PROJECT_SUBST: self.name},
166166
args_append=[self.name], excl_subst=True)
167167
cmd.execute()
168168
if cmd.getretcode() != SUCCESS_EXITVAL:

opengrok-tools/src/main/python/opengrok_tools/utils/mirror.py

Lines changed: 50 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,13 @@
3535
CONTINUE_EXITVAL,
3636
SUCCESS_EXITVAL
3737
)
38-
from .utils import is_exe, check_create_dir, get_int
38+
from .patterns import PROJECT_SUBST, COMMAND_PROPERTY
39+
from .utils import is_exe, check_create_dir, get_int, is_web_uri
3940
from .webutil import get
4041
from .opengrok import get_repos, get_repo_type, get_uri
4142
from .hook import run_hook
43+
from .command import Command
44+
from .restful import call_rest_api
4245

4346
from ..scm.repofactory import get_repository
4447
from ..scm.repository import RepositoryException
@@ -55,6 +58,7 @@
5558
HOOKS_PROPERTY = 'hooks'
5659
LOGDIR_PROPERTY = 'logdir'
5760
PROJECTS_PROPERTY = 'projects'
61+
DISABLED_CMD_PROPERTY = 'disabled_command'
5862

5963

6064
def get_repos_for_project(project_name, ignored_repos, uri, source_root,
@@ -277,6 +281,41 @@ def process_changes(repos, project_name, uri):
277281
return SUCCESS_EXITVAL
278282

279283

284+
def run_command(cmd, project_name):
285+
cmd.execute()
286+
if cmd.getretcode() != 0:
287+
logger = logging.getLogger(__name__)
288+
289+
logger.error("Command for disabled project '{}' failed "
290+
"with error code {}: {}".
291+
format(project_name, cmd.getretcode(),
292+
cmd.getoutputstr()))
293+
294+
295+
def handle_disabled_project(config, project_name):
296+
disabled_command = config.get(DISABLED_CMD_PROPERTY)
297+
if disabled_command:
298+
logger = logging.getLogger(__name__)
299+
300+
logger.debug("Calling disabled command: {}".format(disabled_command))
301+
command_args = disabled_command.get(COMMAND_PROPERTY)
302+
if is_web_uri(command_args[0]):
303+
r = call_rest_api(disabled_command, PROJECT_SUBST, project_name)
304+
try:
305+
r.raise_for_status()
306+
except HTTPError:
307+
logger.error("API call failed for disabled command of "
308+
"project '{}': {}".
309+
format(project_name, r))
310+
else:
311+
cmd = Command(command_args,
312+
env_vars=disabled_command.get("env"),
313+
resource_limits=disabled_command.get("limits"),
314+
args_subst={PROJECT_SUBST: project_name},
315+
args_append=[project_name], excl_subst=True)
316+
run_command(cmd, project_name)
317+
318+
280319
def mirror_project(config, project_name, check_changes, uri,
281320
source_root):
282321
"""
@@ -296,7 +335,8 @@ def mirror_project(config, project_name, check_changes, uri,
296335

297336
project_config = get_project_config(config, project_name)
298337
prehook, posthook, hook_timeout, command_timeout, use_proxy, \
299-
ignored_repos = get_project_properties(project_config, project_name,
338+
ignored_repos = get_project_properties(project_config,
339+
project_name,
300340
config.get(HOOKDIR_PROPERTY))
301341

302342
proxy = None
@@ -306,7 +346,8 @@ def mirror_project(config, project_name, check_changes, uri,
306346
# We want this to be logged to the log file (if any).
307347
if project_config:
308348
if project_config.get(DISABLED_PROPERTY):
309-
logger.info("Project {} disabled, exiting".
349+
handle_disabled_project(config, project_name)
350+
logger.info("Project '{}' disabled, exiting".
310351
format(project_name))
311352
return CONTINUE_EXITVAL
312353

@@ -450,7 +491,8 @@ def check_configuration(config):
450491

451492
global_tunables = [HOOKDIR_PROPERTY, PROXY_PROPERTY, LOGDIR_PROPERTY,
452493
COMMANDS_PROPERTY, PROJECTS_PROPERTY,
453-
HOOK_TIMEOUT_PROPERTY, CMD_TIMEOUT_PROPERTY]
494+
HOOK_TIMEOUT_PROPERTY, CMD_TIMEOUT_PROPERTY,
495+
DISABLED_CMD_PROPERTY]
454496
diff = set(config.keys()).difference(global_tunables)
455497
if diff:
456498
logger.error("unknown global configuration option(s): '{}'"
@@ -462,6 +504,10 @@ def check_configuration(config):
462504
if logdir:
463505
check_create_dir(logger, logdir)
464506

507+
disabled_command = config.get(DISABLED_CMD_PROPERTY)
508+
if disabled_command:
509+
logger.debug("Disabled command: {}".format(disabled_command))
510+
465511
if not check_project_configuration(config.get(PROJECTS_PROPERTY),
466512
config.get(HOOKDIR_PROPERTY),
467513
config.get(PROXY_PROPERTY)):
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
#
2+
# CDDL HEADER START
3+
#
4+
# The contents of this file are subject to the terms of the
5+
# Common Development and Distribution License (the "License").
6+
# You may not use this file except in compliance with the License.
7+
#
8+
# See LICENSE.txt included in this distribution for the specific
9+
# language governing permissions and limitations under the License.
10+
#
11+
# When distributing Covered Code, include this CDDL HEADER in each
12+
# file and include the License file at LICENSE.txt.
13+
# If applicable, add the following below this CDDL HEADER, with the
14+
# fields enclosed by brackets "[]" replaced with your own identifying
15+
# information: Portions Copyright [yyyy] [name of copyright owner]
16+
#
17+
# CDDL HEADER END
18+
#
19+
20+
#
21+
# Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
22+
#
23+
24+
PROJECT_SUBST = '%PROJECT%'
25+
COMMAND_PROPERTY = "command"

opengrok-tools/src/test/python/test_command_sequence.py

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131

3232
from opengrok_tools.utils.commandsequence import CommandSequence, \
3333
CommandSequenceBase
34+
from opengrok_tools.utils.patterns import PROJECT_SUBST
3435

3536

3637
def test_str():
@@ -46,9 +47,9 @@ def test_str():
4647
def test_run_retcodes():
4748
cmd_list = [{"command": ["/bin/echo"]},
4849
{"command": ["/bin/sh", "-c",
49-
"echo " + CommandSequence.PROJECT_SUBST + "; exit 0"]},
50+
"echo " + PROJECT_SUBST + "; exit 0"]},
5051
{"command": ["/bin/sh", "-c",
51-
"echo " + CommandSequence.PROJECT_SUBST + "; exit 1"]}]
52+
"echo " + PROJECT_SUBST + "; exit 1"]}]
5253
cmds = CommandSequence(CommandSequenceBase("opengrok-master", cmd_list))
5354
cmds.run()
5455
assert cmds.retcodes == {
@@ -63,7 +64,7 @@ def test_run_retcodes():
6364
reason="requires Unix")
6465
def test_terminate_after_non_zero_code():
6566
cmd_list = [{"command": ["/bin/sh", "-c",
66-
"echo " + CommandSequence.PROJECT_SUBST + "; exit 255"]},
67+
"echo " + PROJECT_SUBST + "; exit 255"]},
6768
{"command": ["/bin/echo"]}]
6869
cmds = CommandSequence(CommandSequenceBase("opengrok-master", cmd_list))
6970
cmds.run()
@@ -77,7 +78,7 @@ def test_terminate_after_non_zero_code():
7778
reason="requires Unix")
7879
def test_exit_2_handling():
7980
cmd_list = [{"command": ["/bin/sh", "-c",
80-
"echo " + CommandSequence.PROJECT_SUBST + "; exit 2"]},
81+
"echo " + PROJECT_SUBST + "; exit 2"]},
8182
{"command": ["/bin/echo"]}]
8283
cmds = CommandSequence(CommandSequenceBase("opengrok-master", cmd_list))
8384
cmds.run()
@@ -92,13 +93,13 @@ def test_exit_2_handling():
9293
reason="requires Unix")
9394
def test_driveon_flag():
9495
cmd_list = [{"command": ["/bin/sh", "-c",
95-
"echo " + CommandSequence.PROJECT_SUBST + "; exit 2"]},
96+
"echo " + PROJECT_SUBST + "; exit 2"]},
9697
{"command": ["/bin/echo"]},
9798
{"command": ["/bin/sh", "-c",
98-
"echo " + CommandSequence.PROJECT_SUBST +
99+
"echo " + PROJECT_SUBST +
99100
"; exit 1"]},
100101
{"command": ["/bin/sh", "-c",
101-
"echo " + CommandSequence.PROJECT_SUBST]}]
102+
"echo " + PROJECT_SUBST]}]
102103
cmds = CommandSequence(CommandSequenceBase("opengrok-master",
103104
cmd_list, driveon=True))
104105
cmds.run()
@@ -113,7 +114,7 @@ def test_driveon_flag():
113114
@pytest.mark.skipif(not os.path.exists('/bin/echo'),
114115
reason="requires Unix")
115116
def test_project_subst():
116-
cmd_list = [{"command": ["/bin/echo", CommandSequence.PROJECT_SUBST]}]
117+
cmd_list = [{"command": ["/bin/echo", PROJECT_SUBST]}]
117118
cmds = CommandSequence(CommandSequenceBase("test-subst", cmd_list))
118119
cmds.run()
119120

@@ -125,7 +126,7 @@ def test_cleanup_exception():
125126
If cleanup is not a list, Exception should be thrown when initializing
126127
the CommandSequence object.
127128
"""
128-
cleanup = {"cleanup": ["foo", CommandSequence.PROJECT_SUBST]}
129+
cleanup = {"cleanup": ["foo", PROJECT_SUBST]}
129130
with pytest.raises(Exception):
130131
CommandSequence(CommandSequenceBase("test-cleanup-list", None,
131132
cleanup=cleanup))

opengrok-tools/src/test/python/test_mirror.py

Lines changed: 48 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,16 +30,20 @@
3030
from git import Repo
3131
import pytest
3232
import sys
33+
from mockito import verify, patch, spy2, mock, ANY
34+
import requests
3335

3436
from opengrok_tools.scm.repofactory import get_repository
3537
from opengrok_tools.utils.mirror import check_project_configuration, \
36-
check_configuration, \
38+
check_configuration, mirror_project, run_command, \
3739
HOOKS_PROPERTY, PROXY_PROPERTY, IGNORED_REPOS_PROPERTY, \
38-
PROJECTS_PROPERTY
40+
PROJECTS_PROPERTY, DISABLED_CMD_PROPERTY, DISABLED_PROPERTY
3941
import opengrok_tools.mirror
4042
from opengrok_tools.utils.exitvals import (
4143
CONTINUE_EXITVAL,
4244
)
45+
from opengrok_tools.utils.patterns import COMMAND_PROPERTY, PROJECT_SUBST
46+
from opengrok_tools.utils.command import Command
4347

4448

4549
def test_empty_project_configuration():
@@ -173,3 +177,45 @@ def mock_get(*args, **kwargs):
173177

174178
def test_empty_project_config():
175179
assert check_project_configuration({'foo': None})
180+
181+
182+
def test_disabled_command_api():
183+
with patch(opengrok_tools.utils.mirror.call_rest_api,
184+
lambda a, b, c: mock(spec=requests.Response)):
185+
project_name = "foo"
186+
config = {DISABLED_CMD_PROPERTY:
187+
{COMMAND_PROPERTY:
188+
["http://localhost:8080/source/api/v1/foo",
189+
"POST", "data"]},
190+
PROJECTS_PROPERTY: {project_name: {DISABLED_PROPERTY: True}}}
191+
192+
assert mirror_project(config, project_name, False,
193+
None, None) == CONTINUE_EXITVAL
194+
verify(opengrok_tools.utils.mirror). \
195+
call_rest_api(config.get(DISABLED_CMD_PROPERTY),
196+
PROJECT_SUBST, project_name)
197+
198+
199+
def test_disabled_command_run():
200+
"""
201+
Make sure that mirror_project() results in calling run_command().
202+
"""
203+
spy2(opengrok_tools.utils.mirror.run_command)
204+
project_name = "foo"
205+
config = {DISABLED_CMD_PROPERTY:
206+
{COMMAND_PROPERTY: ["cat"]},
207+
PROJECTS_PROPERTY: {project_name: {DISABLED_PROPERTY: True}}}
208+
209+
assert mirror_project(config, project_name, False,
210+
None, None) == CONTINUE_EXITVAL
211+
verify(opengrok_tools.utils.mirror).run_command(ANY, project_name)
212+
213+
214+
def test_disabled_command_run_args():
215+
"""
216+
Make sure that run_command() calls Command.execute().
217+
"""
218+
cmd = mock(spec=Command)
219+
project_name = "foo"
220+
run_command(cmd, project_name)
221+
verify(cmd).execute()

0 commit comments

Comments
 (0)