def command_update_resource_limits(args): "Implements the update_resource_limits subcommand" oauth2_instance = oauth2.build_oauth2(args) auth = oauth2_instance.build_authorizer() s = requests.Session() s.auth = auth course_branch_id = (args.course.replace("~", "!~") if "authoringBranch~" in args.course else args.course) course_branch_item = '%s~%s' % (course_branch_id, args.item) params = 'id=%s&partId=%s' % (course_branch_item, args.part) body = { "reservedCpu": (int(args.grader_cpu) * 1024 if args.grader_cpu is not None else None), "reservedMemory": (int(args.grader_memory_limit) if args.grader_memory_limit is not None else None), "wallClockTimeout": (int(args.grader_timeout) if args.grader_timeout is not None else None) } result = s.post(args.updateGraderResourceLimits_endpoint, params=params, json=body) if result.status_code == 404: logging.error( '\nUnable to find the part or grader with ' + 'part id %s in item %s in course %s.\n' 'Status Code: 404 \n' 'URL: %s \n' 'Response: %s\n', args.part, args.item, args.course, result.url, result.text) return 1 elif result.status_code != 200: logging.error( '\nUnable to update grader resources.\n' 'CourseId: %s\n' 'ItemId: %s\n' 'PartId: %s\n' 'Status Code: %d\n' 'URL: %s\n' 'Response: %s\n', args.course, args.item, args.part, result.status_code, result.url, result.text) return 1 print('\nUpdated resource Limits for grader with ' + 'part id %s in item %s in course %s:\n' 'New Reserved CPU (vCPUs): %s\n' 'New Reserved Memory (MiB): %s\n' 'New Wall Clock Timeout (s): %s\n' % (args.part, args.item, args.course, (int(result.json()['reservedCpu']) / 1024 if 'reservedCpu' in result.json() else 'Cpu limit not set - default is 1 vCPU'), (result.json()['reservedMemory'] if 'reservedMemory' in result.json() else 'Memory limit not set - default is 4096 MiB'), (result.json()['wallClockTimeout'] if 'wallClockTimeout' in result.json() else 'Timeout not set - default is 1200 seconds'))) return 0
def test_compute_cache_filname_expanded_path(): args = argparse.Namespace() args.client_id = 'client_id' args.client_secret = 'fake-secret' args.scopes = 'fake scopes' cfg = configparser.ConfigParser() cfg.add_section('oauth2') cfg.set('oauth2', 'token_cache', '~/.coursera/oauth2_cache.pickle') computed = oauth2.build_oauth2(args, cfg).token_cache_file assert '~' not in computed, 'Computed contained "~": %s' % computed
def test_compute_cache_filename(): args = argparse.Namespace() args.client_id = 'client_id' args.client_secret = 'fake-secret' args.scopes = 'fake scopes' cfg = configparser.ConfigParser() cfg.add_section('oauth2') cfg.set('oauth2', 'token_cache', '/tmp/configured_cache') assert oauth2.build_oauth2(args, cfg).token_cache_file == \ '/tmp/configured_cache'
def check_auth(args): """ Checks coursera_autograder's connectivity to the coursera.org API servers """ oauth2_instance = oauth2.build_oauth2(args) auth = oauth2_instance.build_authorizer() my_profile_url = ( 'https://api.coursera.org/api/externalBasicProfiles.v1?' 'q=me&fields=name' ) r = requests.get(my_profile_url, auth=auth) if r.status_code != 200: logging.error('Received response code %s from the basic profile API.', r.status_code) logging.debug('Response body:\n%s', r.text) sys.exit(1) try: external_id = r.json()['elements'][0]['id'] except: logging.error( 'Could not parse the external id out of the response body %s', r.text) external_id = None try: name = r.json()['elements'][0]['name'] except: logging.error( 'Could not parse the name out of the response body %s', r.text) name = None if not args.quiet or args.quiet == 0: print('Name: %s' % name) print('External ID: %s' % external_id) if name is None or external_id is None: sys.exit(1)
def command_get_status(args): "Implements the get_status subcommand" oauth2_instance = oauth2.build_oauth2(args) auth = oauth2_instance.build_authorizer() s = requests.Session() s.auth = auth course_branch_id = (args.course.replace("~", "!~") if "authoringBranch~" in args.course else args.course) course_grader_id = '%s~%s' % (course_branch_id, args.graderId) result = s.get('%s%s' % (args.getGraderStatus_endpoint, course_grader_id)) if result.status_code == 404: logging.error( '\nUnable to find grader with id %s in course %s.\n' 'Status Code: 404 \n' 'URL: %s \n' 'Response: %s\n', args.graderId, args.course, result.url, result.text) return 1 elif result.status_code != 200: logging.error( '\nUnable to get grader status.\n' 'CourseId: %s\n' 'GraderId: %s\n' 'Status Code: %d \n' 'URL: %s \n' 'Response: %s\n', args.course, args.graderId, result.status_code, result.url, result.text) return 1 status = result.json()['elements'][0]['status'] print('\nGrader status: %s\n' % (status)) return 0
def display_auth_cache(args): ''' Writes to the screen the state of the authentication cache. (For debugging authentication issues.) BEWARE: DO NOT email the output of this command!!! You must keep the tokens secure. Treat them as passwords. ''' oauth2_instance = oauth2.build_oauth2(args) if not args.quiet or args.quiet == 0: token = oauth2_instance.token_cache['token'] if not args.no_truncate and token is not None: token = token[:10] + '...' print("Auth token: %s" % token) expires_time = oauth2_instance.token_cache['expires'] expires_in = int((expires_time - time.time()) * 10) / 10.0 print("Auth token expires in: %s seconds." % expires_in) if 'refresh' in oauth2_instance.token_cache: refresh = oauth2_instance.token_cache['refresh'] if not args.no_truncate and refresh is not None: refresh = refresh[:10] + '...' print("Refresh token: %s" % refresh) else: print("No refresh token found.")
def command_list_graders(args): "Implements the list subcommand" oauth2_instance = oauth2.build_oauth2(args) auth = oauth2_instance.build_authorizer() s = requests.Session() s.auth = auth result = s.get('%s%s' % (args.listGrader_endpoint, args.course)) if result.status_code == 404: logging.error( '\nUnable to locate course with id %s.\n' 'Status Code: 404 \n' 'URL: %s \n' 'Response: %s\n', args.course, result.url, result.text) return 1 elif result.status_code != 200: logging.error( '\nUnable to list graders.\n' 'CourseId: %s\n' 'Status Code: %d \n' 'URL: %s \n' 'Response: %s\n', args.course, result.status_code, result.url, result.text) return 1 elements = result.json()['elements'] print('Graders associated with course id %s:\n' % args.course) for element in elements: course_grader_id = element['id'] grader = course_grader_id.split('~')[-1] filename = element['filename'] print('Filename: %s\nGraderId: %s\n' % (filename, grader)) return 0
def command_upload(args): "Implements the upload subcommand" validate_memory_based_on_cpu(args.grader_cpu, args.grader_memory_limit) d = utils.docker_client(args) image = (args.imageZipFile, os.path.basename(args.imageZipFile)) oauth2_instance = oauth2.build_oauth2(args) auth = oauth2_instance.build_authorizer() # TODO: use transloadit's signatures for upload signing. # authorization = authorize_upload(args, auth) # Generate a random uuid for upload. upload_id = uuid.uuid4().hex transloadit_host = idle_transloadit_server(args) upload_url = 'https://%(host)s/assemblies/%(id)s' % { 'host': transloadit_host, 'id': upload_id, } if args.upload_to_requestbin is not None: upload_url = 'http://requestb.in/%s' % args.upload_to_requestbin if not args.quiet or args.quiet == 0: sys.stdout.write( 'About to upload to server:\n\t%(transloadit_host)s\n' 'with upload id:\n\t%(upload_id)s\nStatus API:\n' '\t%(upload_url)s\nUploading...' % { 'transloadit_host': transloadit_host, 'upload_id': upload_id, 'upload_url': upload_url, }) sys.stdout.flush() p = multiprocessing.Process(target=upload, args=(args, upload_url, image)) p.daemon = True # Auto-kill when the main process exits. p.start() time.sleep(20) # Yield control to the child process to kick off upload. upload_information = None while p.is_alive(): upload_information = poll_transloadit(args, upload_url) if upload_information is not None: logging.warn( 'Upload information retrieved before upload completed??! %s', upload_information) break time.sleep(10) # 10 seconds p.join(1) # Join to clean up zombie. # TODO: make time waiting for transloadit to finish processing configurable for i in range(300): upload_information = poll_transloadit(args, upload_url) if upload_information is not None: break time.sleep(5) if upload_information is None: logging.error( 'Upload did not complete within expected time limits. Upload ' 'URL: %s', upload_url) return 1 # Register the grader with Coursera to initiate the image cleaning process logging.debug('Grader upload info is: %s', upload_information) # Rebuild an authorizer to ensure it's fresh and not expired auth = oauth2_instance.build_authorizer() grader_id = register_grader(auth, args, bucket=upload_information[0], key=upload_information[1]) return update_assignments(auth, grader_id, args)