Preview: proactive_log_migration.py
Size: 3.21 KB
/opt/imunify360/venv/lib/python3.11/site-packages/defence360agent/feature_management/plugins/proactive_log_migration.py
"""Promote stale `proactive: na` perms to `log` when the deployment toggle
``FEATURE_MANAGEMENT.proactive_disable_target == "log"`` is set.
DEF-42523. Without this, customers (notably Cloudways) flipping the toggle
post-upgrade would only see the new behavior on users whose add-on state
changes; pre-existing disabled users would linger in NA / mode=DISABLED
indefinitely. Re-firing the proactive hook with LOG writes
``mode=LOG`` to user_config and updates the perm row.
Idempotent: once promoted, no NA proactive perms remain, so subsequent
boots no-op.
"""
import asyncio
from logging import getLogger
from defence360agent.contracts.config import ConfigFile
from defence360agent.contracts.plugins import MessageSink, thisguy
from defence360agent.feature_management.constants import LOG, NA, PROACTIVE
from defence360agent.feature_management.model import FeatureManagementPerms
from defence360agent.feature_management.utils import set_feature
from defence360agent.utils import Scope, create_task_and_log_exceptions
logger = getLogger(__name__)
@thisguy
class ProactiveLogMigration(MessageSink):
# Proactive Defence is an Imunify360-only feature. ImunifyAV-only
# installs have no proactive permissions to migrate and no
# FEATURE_MANAGEMENT.proactive_disable_target schema key.
SCOPE = Scope.IM360
async def create_sink(self, loop: asyncio.AbstractEventLoop):
# Spawn the migration as a background task so the agent's other
# MessageSinks (and serving traffic) come up immediately. The
# migration is idempotent on retry, so cancellation at shutdown
# is safe. create_task_and_log_exceptions surfaces failures
# to the agent's exception handler instead of silently dropping
# them (the bare loop.create_task would).
self._migration_task = create_task_and_log_exceptions(
loop, self._migrate
)
async def shutdown(self):
task = getattr(self, "_migration_task", None)
if task is None or task.done():
return
task.cancel()
try:
await task
except asyncio.CancelledError:
pass
@staticmethod
async def _migrate():
target = ConfigFile().get(
"FEATURE_MANAGEMENT", "proactive_disable_target"
)
if target != "log":
return
users = [
row.user
for row in FeatureManagementPerms.select().where(
(FeatureManagementPerms.proactive == NA)
& (
FeatureManagementPerms.user
!= FeatureManagementPerms.DEFAULT
)
)
]
if not users:
return
logger.info(
"Promoting %d proactive=na users to log (DEF-42523)",
len(users),
)
promoted = 0
for user in users:
try:
if await set_feature(user, PROACTIVE, LOG):
promoted += 1
except Exception:
logger.exception(
"Failed to promote proactive=na user %s to log", user
)
logger.info(
"Promoted %d/%d users to proactive=log",
promoted,
len(users),
)
Directory Contents
Dirs: 1 × Files: 3