Add lightweight integration tests for Celery/Redis basics in CI#2369
Open
berendt wants to merge 3 commits into
Open
Add lightweight integration tests for Celery/Redis basics in CI#2369berendt wants to merge 3 commits into
berendt wants to merge 3 commits into
Conversation
1b3d448 to
cb9d08b
Compare
There was a problem hiding this comment.
Hey - I've found 2 security issues, and 1 other issue
Security issues:
- Detected subprocess function 'Popen' without a static string. If this data can be controlled by a malicious actor, it may be an instance of command injection. Audit the use of this call to ensure it is not controllable by an external resource. You may consider using 'shlex.escape()'. (link)
- Detected subprocess function 'Popen' without a static string. If this data can be controlled by a malicious actor, it may be an instance of command injection. Audit the use of this call to ensure it is not controllable by an external resource. You may consider using 'shlex.escape()'. (link)
Prompt for AI Agents
Please address the comments from this code review:
## Individual Comments
### Comment 1
<location path="osism/commands/service.py" line_range="63" />
<code_context>
elif service == "flower":
p = subprocess.Popen(
- "celery --broker=redis://redis flower",
+ f"celery --broker={Config.broker_url} flower",
shell=True,
)
</code_context>
<issue_to_address>
**issue (bug_risk):** Handle special characters in broker URL for the flower invocation.
Because this uses `shell=True`, any `&`, `?`, or other shell metacharacters in `Config.broker_url` can be split or reinterpreted by the shell. Please use a list-based `Popen` without `shell=True`, e.g. `Popen(["celery", "--broker", Config.broker_url, "flower"])`, or otherwise ensure the URL is safely quoted/escaped.
</issue_to_address>
### Comment 2
<location path="osism/commands/service.py" line_range="51-54" />
<code_context>
subprocess.Popen(
f"celery -A {t} --broker={Config.broker_url} beat -s /tmp/celerybeat-schedule-{t}.db",
shell=True,
)
</code_context>
<issue_to_address>
**security (python.lang.security.audit.dangerous-subprocess-use-audit):** Detected subprocess function 'Popen' without a static string. If this data can be controlled by a malicious actor, it may be an instance of command injection. Audit the use of this call to ensure it is not controllable by an external resource. You may consider using 'shlex.escape()'.
*Source: opengrep*
</issue_to_address>
### Comment 3
<location path="osism/commands/service.py" line_range="62-65" />
<code_context>
p = subprocess.Popen(
f"celery --broker={Config.broker_url} flower",
shell=True,
)
</code_context>
<issue_to_address>
**security (python.lang.security.audit.dangerous-subprocess-use-audit):** Detected subprocess function 'Popen' without a static string. If this data can be controlled by a malicious actor, it may be an instance of command injection. Audit the use of this call to ensure it is not controllable by an external resource. You may consider using 'shlex.escape()'.
*Source: opengrep*
</issue_to_address>Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
The Celery Config hardcoded broker_url and result_backend to redis://redis, with the same literals duplicated in the beat and flower service commands. Derive the URLs from REDIS_HOST / REDIS_PORT / REDIS_DB -- the same settings osism.utils._init_redis already honors -- and allow an override via CELERY_BROKER_URL / CELERY_RESULT_BACKEND. The default of REDIS_HOST is redis, so this stays backwards compatible while letting the integration tests point Celery at a local Redis with REDIS_HOST=localhost. Also fix task_track_started, which was the tuple (True,) instead of the intended bool; it only worked because a non-empty tuple is truthy. Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Christian Berendt <berendt@osism.tech>
Add tests/integration/ covering the task-processing core against a live Redis and a Celery worker started from the same virtualenv: - Celery round-trip: ansible.noop.delay() returns True, exercising broker, osism-ansible queue routing, worker and result backend. - Worker visibility: app.control.inspect().ping() sees the worker, the mechanism behind `osism get status workers`. - Redis streams: push/finish/fetch_task_output round-trip, the path `osism apply` uses to stream task logs. - Locking: create_redlock acquire/release and set/is/remove_task_lock. The worker runs only the ansible Celery app, which has no import-time dependency on NetBox/OpenStack/ansible-core. GATHER_FACTS_SCHEDULE=0 keeps the periodic gather_facts task from registering. The tests carry an `integration` marker (registered for the --strict-markers run) and are skipped when Redis is not reachable, so the default `pytest tests/unit` run is unaffected. Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Christian Berendt <berendt@osism.tech>
Add the python-osism-integration-tests job to the check and periodic-daily pipelines. It reuses playbooks/pre.yml (which already sets up Docker via ensure-docker), starts a redis:7-alpine container, installs the package with pipenv like the unit-test job, and runs `pytest tests/integration` with REDIS_HOST=localhost. Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Christian Berendt <berendt@osism.tech>
cb9d08b to
81ec7d2
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Implements #2368.
Adds a lightweight integration-test job that covers the Celery/Redis
task-processing core (broker, queue routing, worker, result backend, Redis
streams for task output, distributed locks) end-to-end in CI, plus the
prerequisite that makes the broker URL configurable.
Change set (in commit order)
1. Derive Celery broker URL from settings and environment
osism/tasks/__init__.py,osism/commands/service.pyConfig.broker_url/Config.result_backendwere hardcoded toredis://redis, with the same literals duplicated in thebeatandflowerservice commands. They now derive fromREDIS_HOST/REDIS_PORT/REDIS_DB(the same settingsosism.utils._init_redisalready honors), overridable via
CELERY_BROKER_URL/CELERY_RESULT_BACKEND. The default ofREDIS_HOSTisredis, so thisis backwards compatible.
service.pynow referencesConfig.broker_url(single source of truth)for
beat/flower, removing the duplicated literals.task_track_started, which was the tuple(True,)instead of theintended bool (it only worked because a non-empty tuple is truthy).
This is what lets the tests point Celery at a local Redis via
REDIS_HOST=localhost.2. Add Celery/Redis integration tests
tests/integration/,setup.cfg,README.mdtest_celery.py— round-trip:ansible.noop.delay()→result.get(timeout=60) is True(broker +osism-ansiblerouting +worker + result backend); worker visibility: a fresh
Celeryclient configured from
Configsees the worker viainspect().ping()(the mechanism behindosism get status workers).test_redis_streams.py—push_task_output/finish_task_output/fetch_task_outputround-trip (the pathosism applyuses to streamlogs).
test_locking.py—create_redlockacquire/release andset_task_lock/is_task_locked/remove_task_lock.conftest.pystarts the worker as a session-scoped fixture from thesame venv, running only the
ansibleCelery app (no import-timedependency on NetBox/OpenStack/ansible-core) with
GATHER_FACTS_SCHEDULE=0so the periodicgather_factstask is notregistered. It polls
inspect().ping()until the worker is ready andterminates it on teardown.
integrationmarker (registered insetup.cfgfor the--strict-markersrun) and are skipped when Redis is not reachable,so a local
pytestis unaffected.3. Add Zuul integration-test job
.zuul.yaml,playbooks/test-integration.ymlpython-osism-integration-testsjob added to thecheckandperiodic-dailypipelines. It reusesplaybooks/pre.yml(which alreadysets up Docker via
ensure-docker), starts aredis:7-alpinecontainer, installs the package with pipenv exactly like the unit-test
job, and runs
pytest tests/integrationwithREDIS_HOST=localhost.Notes / deviations
tests/unitviatestpaths = tests/unitinsetup.cfg, so a barepytestnevercollects
tests/integration— the issue's first concern is alreadysatisfied. The
integrationmarker + Redis-reachability skip are stilladded (the marker is required by
--strict-markers, and the skip keepspytest tests/integrationgreen locally without Redis).CHANGELOG.mdentry: this repo maintains the changelog in batched,post-release commits that map merged PR numbers to release versions, so
per-PR entries are out of convention here.
coverage) are intentionally out of scope.
Local verification
black --check— clean (6 files)flake8— cleanpytest tests/integration— not run locally (no Redis); exercised bythe new CI job.
🤖 Generated with Claude Code