Example #1
0
    def test_from_zip(self):
        # use the "evil" test case because it has multiple stages
        host_dir = 'testdata/executor/HOST_DIR'
        stages_dir = 'testdata/executor/evil'
        fake_file = six.BytesIO()
        executor.Stages(stages_dir).save_zip(fake_file)
        stages = executor.Stages.from_zip(fake_file, 'test_from_zip', host_dir)
        fake_file2 = six.BytesIO()
        executor.Stages(host_dir + '/test_from_zip').save_zip(fake_file2)
        shutil.rmtree(host_dir + '/test_from_zip')

        with zipfile.ZipFile(fake_file, 'r') as zf:
            with zipfile.ZipFile(fake_file2, 'r') as zf2:
                self.assertEqual(sorted(zf.namelist()), sorted(zf2.namelist()))
Example #2
0
 def test_hello_world_cpp(self):
     host_dir = 'testdata/executor/HOST_DIR/hello_world_cpp'
     stages_dir = 'testdata/executor/hello_world_cpp'
     code_path = 'testdata/executor/hello_world_cpp/code/hello_world.cpp'
     with EphemeralDir(host_dir):
         c = executor.DockerExecutor('test_hello_world_cpp', host_dir)
         c.init()
         output, errors = c.run_stages(code_path,
                                       executor.Stages(stages_dir))
         self.assertEqual(errors, [])
         self.assertEqual(output.strip(), '')
Example #3
0
def courses_x_assignments_x_edit(course_name, assignment_name):
  user = login.current_user
  instructs_course = user.instructs_course(course_name)
  if instructs_course:
    stages = executor.Stages(os.path.join(
      '../data/files/courses', course_name, 'assignments', assignment_name))
    form = flask.request.form
    for error in _edit_assignment(form, course_name, assignment_name, stages):
      flask.flash(error)
  return flask.redirect(
    u'/courses/{}/assignments/{}'.format(course_name, assignment_name),
    code=303)
Example #4
0
 def test_hello_world(self):
     host_dir = 'testdata/executor/HOST_DIR/hello_world'
     stages_dir = 'testdata/executor/hello_world'
     code_path = None
     with EphemeralDir(host_dir):
         c = executor.DockerExecutor('test_hello_world', host_dir)
         c.init()
         stages = executor.Stages(stages_dir)
         output, errors = c.run_stages(code_path, stages)
         self.assertEqual(errors, [])
         self.assertEqual(stages.stages['stage0'].output.stdout,
                          b'HELLO WORLD\n')
Example #5
0
 def test_hello_world_cpp(self):
     host_dir = 'testdata/executor/HOST_DIR/score'
     stages_dir = 'testdata/executor/score'
     code_path = None
     with EphemeralDir(host_dir):
         c = executor.DockerExecutor('test_score', host_dir)
         c.init()
         stages = executor.Stages(stages_dir)
         output, errors = c.run_stages(code_path, stages)
         self.assertEqual(errors, [])
         self.assertEqual(output.strip(), '')
         self.assertIn('score', stages.stages)
         self.assertEqual(stages.stages['score'].output.score, 12345)
Example #6
0
 def test_untrusted(self):
     host_dir = 'testdata/executor/HOST_DIR/untrusted'
     stages_dir = 'testdata/executor/untrusted'
     code_path = None
     score_map = {}
     with EphemeralDir(host_dir):
         c = executor.DockerExecutor('test_unptrusted', host_dir)
         c.init()
         output, errors = c.run_stages(
             code_path, executor.Stages(stages_dir), lambda stage: score_map
             .update({stage.name: stage.output.score}))
         self.assertEqual(errors, [])
         self.assertEqual(output.strip(), '')
         self.assertEqual(score_map, {'trusted': 123, 'untrusted': None})
Example #7
0
 def test_save_zip(self):
     # use the "evil" test case because it has multiple stages
     stages_dir = 'testdata/executor/evil'
     stages = executor.Stages(stages_dir)
     fake_file = six.BytesIO()
     stages.save_zip(fake_file)
     expected_files = [
         'metadata.json', 'fork_bomb/', 'fork_bomb/fork_bomb.cpp',
         'fork_bomb/main', 'many_open_files/', 'many_open_files/main',
         'many_open_files/many_open_files.cpp', 'much_ram/',
         'much_ram/much_ram.cpp', 'much_ram/main'
     ]
     with zipfile.ZipFile(fake_file, 'r') as zf:
         self.assertEqual(sorted(zf.namelist()), sorted(expected_files))
Example #8
0
 def test_big_output(self):
     host_dir = 'testdata/executor/HOST_DIR/big_output'
     stages_dir = 'testdata/executor/big_output'
     code_path = None
     with EphemeralDir(host_dir):
         c = executor.DockerExecutor('test_big_output', host_dir)
         c.init()
         stages = executor.Stages(stages_dir)
         output, errors = c.run_stages(code_path, stages)
         self.assertEqual(errors, [])
         stage_output = stages.stages['make_output'].output.stdout
         self.assertGreaterEqual(len(stage_output),
                                 128 * 1024)  # >= than 128KB output
         self.assertLessEqual(len(stage_output),
                              132 * 1024)  # <= than 128KB + 4KB output
Example #9
0
def courses_x_assignments_x_download(course_name, assignment_name):
  user = login.current_user
  instructs_course = user.instructs_course(course_name)
  if instructs_course:
    stages = executor.Stages(os.path.join(
      '../data/files/courses', course_name, 'assignments', assignment_name))
    buf = six.BytesIO()
    stages.save_zip(buf)
    response = flask.make_response(buf.getvalue())
    response.headers['Content-Disposition'] = (
      'attachment; filename={}.zip'.format(assignment_name))
    return response
  else:
    return flask.redirect(
      u'/courses/{}/assignments/{}'.format(course_name, assignment_name),
      code=303)
Example #10
0
 def test_unicode_in_env(self):
     host_dir = 'testdata/executor/HOST_DIR/hello_world'
     stages_dir = 'testdata/executor/hello_world'
     code_path = None
     with EphemeralDir(host_dir):
         c = executor.DockerExecutor('test_unicode_in_env', host_dir)
         c.init()
         stages = executor.Stages(stages_dir)
         env = {
             'DECODED': u'┻━┻ ︵ ¯\(ツ)/¯ ︵ ┻━┻',
             'ENCODED': '┻━┻ ︵ ¯\(ツ)/¯ ︵ ┻━┻',
         }
         output, errors = c.run_stages(code_path, stages, env=env)
         self.assertEqual(errors, [])
         self.assertEqual(stages.stages['stage0'].output.stdout,
                          b'HELLO WORLD\n')
Example #11
0
def courses_x_assignments_x(course_name, assignment_name):
  user = login.current_user
  course = grade_oven.course(course_name)
  assignment = course.assignment(assignment_name)
  student_submission = assignment.student_submission(user.username)
  instructs_course = user.instructs_course(course.name)
  takes_course = user.takes_course(course.name)
  stages = executor.Stages(os.path.join(
    '../data/files/courses', course.name, 'assignments', assignment.name))
  if takes_course:
    due_date = assignment.due_date()
    if due_date is None:
      formatted_due_date = ''
    else:
      formatted_due_date = time.strftime('%Y-%m-%d %H:%M',
                                         time.localtime(due_date))
    submission_output = student_submission.output()
    submission_has_output_html = bool(student_submission.output_html())
    submission_errors = student_submission.errors().strip()
  else:
    formatted_due_date = ''
    submission_output, submission_errors = '', ''
    submission_has_output_html = False
  header_row, table = _make_grade_table(
    course, assignment, show_real_names=instructs_course)
  return flask.render_template(
    'courses_x_assignments_x.html',
    username=login.current_user.get_id(),
    instructs_course=instructs_course,
    takes_course=takes_course,
    course_name=course.name,
    assignment_name=assignment.name,
    formatted_due_date=formatted_due_date,
    stages=stages.stages.values(),
    submission_output=submission_output,
    submission_has_output_html=submission_has_output_html,
    submission_errors=submission_errors,
    stages_desc=stages.description,
    header_row=header_row,
    table=table)
Example #12
0
 def test_evil_cpp(self):
     host_dir = 'testdata/executor/HOST_DIR/evil'
     stages_dir = 'testdata/executor/evil'
     code_path = None
     with EphemeralDir(host_dir):
         c = executor.DockerExecutor('test_evil', host_dir)
         c.init()
         c.timeout_seconds = 5
         c.max_num_files = 100
         c.max_mem_bytes = 64 * 1024**2
         stages = executor.Stages(stages_dir)
         output, errors = c.run_stages(code_path, stages)
         self.assertEqual(stages.stages['fork_bomb'].output.errors, [
             'Command "/grade_oven/fork_bomb/main" did not finish in '
             '5 seconds and timed out.'
         ])
         self.assertTrue(b'many_open_files: 80 files open' in
                         stages.stages['many_open_files'].output.stdout)
         self.assertFalse(b'many_open_files: 120 files open' in
                          stages.stages['many_open_files'].output.stdout)
         self.assertTrue(b'much_ram: Allocated 48MB.' in
                         stages.stages['much_ram'].output.stdout)
         self.assertFalse(b'much_ram: Allocated 64MB.' in
                          stages.stages['much_ram'].output.stdout)
Example #13
0
def _enqueue_student_submission(course_name, assignment_name, username, files):
  user = grade_oven.user(username)
  course = grade_oven.course(course_name)
  assignment = course.assignment(assignment_name)
  student_submission = assignment.student_submission(username)
  # If this is a resubmission, but there's no original submission, skip it.
  # if student_submission.num_submissions() == 0 and not files:
  #   return
  monitor_variables['assignment_attempts'] += 1
  logging.info(u'Student "%s" is attempting assignment "%s/%s".',
               username, course_name, assignment_name)
  submission_dir = os.path.join(
    '../data/files/courses', course_name, 'assignments', assignment_name,
    'submissions', username)
  desc = u'{}_{}_{}'.format(course_name, assignment_name, username)
  # TODO: Fix the quick hack below.  It is only in place to avoid "escaped"
  # names that are not safe docker container names.
  container_id = str(abs(hash(desc)))[:32]
  num_submissions = student_submission.num_submissions()
  submit_time = student_submission.submit_time() or 0
  cur_time = time.time()
  min_seconds_since_last_submission = min(num_submissions**3, 5.0)
  priority = (num_submissions, submit_time)
  stages = executor.Stages(os.path.join(
    '../data/files/courses', course_name, 'assignments', assignment_name))
  submission = GradeOvenSubmission(
    priority, username, desc, submission_dir, container_id, stages,
    student_submission)
  if submission in executor_queue:
    logging.warning(
      u'Student "%s" submited assignment "%s/%s" while still in the queue.',
      username, course_name, assignment_name)
    flask.flash(
      u'{} cannot submit assignment {} for {} while in the queue.'.format(
        username, assignment_name, course_name))
  elif cur_time < submit_time + min_seconds_since_last_submission:
    seconds_left = min_seconds_since_last_submission - (cur_time - submit_time)
    formatted_time = time.strftime(
      '%Y-%m-%d %H:%M:%S',
      time.localtime(submit_time + min_seconds_since_last_submission))
    logging.info(
      u'Student "%s" submitted assignment "%s/%s" '
      'but needs to wait until %s (%s seconds).',
      username, course_name, assignment_name, formatted_time, seconds_left)
    flask.flash(
      u'Please wait until {} ({:.0f} seconds) to submit {} again.'.format(
        formatted_time, seconds_left, assignment_name))
  else:
    if files:
      try:
        shutil.rmtree(submission_dir)
      except OSError as e:
        if e.errno != errno.ENOENT:
          raise e
      save_files_in_dir(files, submission_dir)
      # If there are no files being uploaded, then this must be a resubmission.
      student_submission.set_submit_time()
      student_submission.set_num_submissions(
        student_submission.num_submissions() + 1)
    student_submission.set_status('queued')
    executor_queue.enqueue(submission)