Define TrackedCommand for use as base class for tracking management commands. Use in create_user command.
This commit is contained in:
@@ -4,8 +4,10 @@ from student.models import CourseEnrollment, Registration
|
||||
from student.views import _do_create_account
|
||||
from django.contrib.auth.models import User
|
||||
|
||||
from track.management.tracked_command import TrackedCommand
|
||||
|
||||
class Command(BaseCommand):
|
||||
|
||||
class Command(TrackedCommand):
|
||||
help = """
|
||||
This command creates and registers a user in a given course
|
||||
as "audit", "verified" or "honor".
|
||||
|
||||
0
common/djangoapps/track/management/__init__.py
Normal file
0
common/djangoapps/track/management/__init__.py
Normal file
@@ -0,0 +1,47 @@
|
||||
import json
|
||||
from StringIO import StringIO
|
||||
from django.test import TestCase
|
||||
|
||||
from eventtracking import tracker as eventtracker
|
||||
|
||||
from track.command import TrackedCommand
|
||||
|
||||
|
||||
class DummyCommand(TrackedCommand):
|
||||
"""A locally-defined command, for testing, that returns the current context as a JSON string."""
|
||||
def handle(self, *args, **options):
|
||||
return json.dumps(eventtracker.get_tracker().resolve_context())
|
||||
|
||||
|
||||
class CommandsTestBase(TestCase):
|
||||
|
||||
def _run_dummy_command(self, *args, **kwargs):
|
||||
"""Runs the test command's execute method directly, and outputs a dict of the current context."""
|
||||
out = StringIO()
|
||||
DummyCommand().execute(*args, stdout=out, **kwargs)
|
||||
out.seek(0)
|
||||
return json.loads(out.read())
|
||||
|
||||
def test_command(self):
|
||||
args = ['whee']
|
||||
kwargs = {'key1': 'default', 'key2': True}
|
||||
json_out = self._run_dummy_command(*args, **kwargs)
|
||||
self.assertEquals(json_out['command'], 'unknown')
|
||||
self.assertEquals(json_out['command_args'], args)
|
||||
self.assertEquals(json_out['command_options'], kwargs)
|
||||
|
||||
def test_password_in_command(self):
|
||||
args = []
|
||||
kwargs = {'password': 'default'}
|
||||
json_out = self._run_dummy_command(*args, **kwargs)
|
||||
self.assertEquals(json_out['command'], 'unknown')
|
||||
self.assertEquals(json_out['command_args'], args)
|
||||
self.assertEquals(json_out['command_options'], {'password': '********'})
|
||||
|
||||
def test_removed_args_in_command(self):
|
||||
args = []
|
||||
kwargs = {'settings': 'dummy', 'pythonpath': 'whee'}
|
||||
json_out = self._run_dummy_command(*args, **kwargs)
|
||||
self.assertEquals(json_out['command'], 'unknown')
|
||||
self.assertEquals(json_out['command_args'], args)
|
||||
self.assertEquals(json_out['command_options'], {})
|
||||
94
common/djangoapps/track/management/tracked_command.py
Normal file
94
common/djangoapps/track/management/tracked_command.py
Normal file
@@ -0,0 +1,94 @@
|
||||
"""Provides management command calling info to tracking context."""
|
||||
|
||||
from django.core.management.base import BaseCommand
|
||||
|
||||
from eventtracking import tracker
|
||||
|
||||
|
||||
class TrackedCommand(BaseCommand):
|
||||
"""
|
||||
Provides management command calling info to tracking context.
|
||||
|
||||
Information provided to context includes three values:
|
||||
|
||||
'command': the program name and the subcommand used to run a management command.
|
||||
'command_args': the argument list passed to the command.
|
||||
'command_options': the option dict passed to the command. This includes options
|
||||
that were not explicitly specified, and receive default values.
|
||||
|
||||
Special treatment are provided for several options, including obfuscation and filtering.
|
||||
|
||||
The values for the following options are filtered entirely:
|
||||
'settings', 'pythonpath', 'verbosity', 'traceback', 'stdout', 'stderr'.
|
||||
The values for the following options are replaced with eight asterisks:
|
||||
'password'.
|
||||
|
||||
An example tracking log entry resulting from running the 'create_user' management command:
|
||||
|
||||
{
|
||||
"username": "anonymous",
|
||||
"host": "",
|
||||
"event_source": "server",
|
||||
"event_type": "edx.course.enrollment.activated",
|
||||
"context": {
|
||||
"course_id": "edX/Open_DemoX/edx_demo_course",
|
||||
"org_id": "edX",
|
||||
"command_options": {
|
||||
"username": null,
|
||||
"name": null,
|
||||
"course": "edX/Open_DemoX/edx_demo_course",
|
||||
"mode": "verified",
|
||||
"password": "********",
|
||||
"email": "rando9c@example.com",
|
||||
"staff": false
|
||||
},
|
||||
"command": "./manage.py create_user",
|
||||
"command_args": []
|
||||
},
|
||||
"time": "2014-01-06T15:59:49.599522+00:00",
|
||||
"ip": "",
|
||||
"event": {
|
||||
"course_id": "edX/Open_DemoX/edx_demo_course",
|
||||
"user_id": 29,
|
||||
"mode": "verified"
|
||||
},
|
||||
"agent": "",
|
||||
"page": null
|
||||
}
|
||||
|
||||
The name of the context used to add (and remove) these values is "edx.mgmt.command".
|
||||
The context name is used to allow the context additions to be scoped, but doesn't
|
||||
appear in the context itself.
|
||||
"""
|
||||
prog_name = 'unknown'
|
||||
|
||||
def create_parser(self, prog_name, subcommand):
|
||||
"""Wraps create_parser to snag command line info."""
|
||||
self.prog_name = "{} {}".format(prog_name, subcommand)
|
||||
return super(TrackedCommand, self).create_parser(prog_name, subcommand)
|
||||
|
||||
def execute(self, *args, **options):
|
||||
"""Wraps base execute() to add command line to tracking context."""
|
||||
# Make a copy of options, and obfuscate or filter particular values.
|
||||
options_dict = dict(options)
|
||||
|
||||
# Stuff to obfuscate:
|
||||
censored_opts = ['password']
|
||||
for opt in censored_opts:
|
||||
if opt in options_dict:
|
||||
options_dict[opt] = '*' * 8
|
||||
|
||||
# Stuff to filter:
|
||||
removed_opts = ['settings', 'pythonpath', 'verbosity', 'traceback', 'stdout', 'stderr']
|
||||
for opt in removed_opts:
|
||||
if opt in options_dict:
|
||||
del options_dict[opt]
|
||||
|
||||
context = {
|
||||
'command': self.prog_name,
|
||||
'command_args': args,
|
||||
'command_options': options_dict,
|
||||
}
|
||||
COMMAND_CONTEXT_NAME = 'edx.mgmt.command'
|
||||
with tracker.get_tracker().context(COMMAND_CONTEXT_NAME, context):
|
||||
super(TrackedCommand, self).execute(*args, **options)
|
||||
Reference in New Issue
Block a user