예제 #1
0
    def _delete_task_from_queue(self, task_api):
        """Method to delete the task from the taskqueue.

        First, it tries to post the output back to speified url. On successful
        post, the task is deleted from taskqueue since the task has produced
        expected output. If the post was unsuccessful, the task is not deleted
        form the tskqueue since the expected output has yet not reached the
        application. In either case cleanup is performed on the task.

        Args:
            task_api: handle for taskqueue api collection.

        Returns:
            Delete status (True/False)
        """

        try:
            delete_request = task_api.tasks().delete(
                project=FLAGS.project_name,
                taskqueue=FLAGS.taskqueue_name,
                task=self.task_id)
            delete_request.execute()
        except HttpError, http_error:
            logger.error('Error deleting task %s from taskqueue.'
                         'Error details %s' % (self.task_id, str(http_error)))
예제 #2
0
    def _delete_task_from_queue(self, task_api):
        """Method to delete the task from the taskqueue.

        First, it tries to post the output back to speified url. On successful
        post, the task is deleted from taskqueue since the task has produced
        expected output. If the post was unsuccessful, the task is not deleted
        form the tskqueue since the expected output has yet not reached the
        application. In either case cleanup is performed on the task.

        Args:
            task_api: handle for taskqueue api collection.

        Returns:
            Delete status (True/False)
        """

        try:
            name = build_cloudtasks_task_name(FLAGS.project_name,
                                              FLAGS.project_location,
                                              FLAGS.taskqueue_name,
                                              task_id=self.task_id)
            body = {'scheduleTime': self.task_schedule_time}
            delete_request = task_api.projects().locations().queues().tasks(
            ).acknowledge(name=name, body=body)
            delete_request.execute()
        except HttpError, http_error:
            logger.error('Error deleting task %s from taskqueue.'
                         'Error details %s' % (self.task_id, str(http_error)))
예제 #3
0
    def _start_task_execution(self):
        """Method to spawn subprocess to execute the tasks.

        This method splits the commands/executable_binary to desired arguments
        format for Popen API. It appends input and output files to the
        arguments. It is assumed that commands/executable_binary expects input
        and output files as first and second positional parameters
        respectively.
        """
        # TODO: Add code to handle the cleanly shutdown when a process is killed
        # by Ctrl+C.
        try:
            cmdline = FLAGS.executable_binary.split(' ')
            cmdline.append(self._get_input_file())
            cmdline.append(self._get_output_file())
            self._process = subprocess.Popen(cmdline)
            self.task_start_time = time.time()
        except OSError:
            logger.error('Error creating subprocess %s. Error details %s' %
                         (self.task_id, str(OSError)))
            self._cleanup()
            raise ClientTaskInitError(self.task_id,
                                      'Error creating subprocess')
        except ValueError:
            logger.error('Invalid arguments while executing task ',
                         self.task_id)
            self._cleanup()
            raise ClientTaskInitError(
                self.task_id, 'Invalid arguments while executing task')
예제 #4
0
    def is_completed(self, task_api):
        """Method to check if task has finished executing.

        This is responsible for checking status of task execution. If the task
        has already finished executing, it deletes the task from the task
        queue. If the task has been running since long time then it assumes
        that there is high proabbility that it is dfunct and hence kills the
        corresponding subprocess. In this case, task had not completed
        successfully and hence we do not delete it form the taskqueue. In above
        two cases, task completion status is returned as true since there is
        nothing more to run in the task. In all other cases, task is still
        running and hence we return false as completion status.

        Args:
            task_api: handle for taskqueue api collection.

        Returns:
            Task completion status (True/False)
        """
        status = False
        try:
            task_status = self._process.poll()
            if task_status == 0:
                status = True
                if self._post_output():
                    self._delete_task_from_queue(task_api)
                self._cleanup()
            elif self._has_timedout():
                status = True
                self._kill_subprocess()
        except OSError:
            logger.error('Error during polling status of task %s, Error '
                         'details %s' % (self.task_id, str(OSError)))
        return status
예제 #5
0
 def __init__(self):
     if not FLAGS.project_name:
         raise app.UsageError('You must specify a project name'
                              ' using the "--project_name" flag.')
     discovery_uri = (
         FLAGS.api_host + 'discovery/v1/apis/{api}/{apiVersion}/rest')
     logger.info(discovery_uri)
     try:
         # If the Credentials don't exist or are invalid run through the
         # native clien flow. The Storage object will ensure that if
         # successful the good Credentials will get written back to a file.
         # Setting FLAGS.auth_local_webserver to false since we can run our
         # tool on Virtual Machines and we do not want to run the webserver
         # on VMs.
         FLAGS.auth_local_webserver = False
         storage = Storage(FLAGS.credentials_file)
         credentials = storage.get()
         if credentials is None or credentials.invalid == True:
             credentials = run(FLOW, storage)
         http = credentials.authorize(self._dump_request_wrapper(
                 httplib2.Http()))
         self.task_api = build('taskqueue',
                               FLAGS.service_version,
                               http=http,
                               discoveryServiceUrl=discovery_uri)
     except HttpError, http_error:
         logger.error('Error gettin task_api: %s' % http_error)
예제 #6
0
 def _kill_subprocess(self):
     """Kills the process after cleaning up the task."""
     self._cleanup()
     try:
         self._process.kill()
         logger.info('Trying to kill task %s, since it has been running '
                     'for long' % self.task_id)
     except OSError:
         logger.error('Error killing task %s. Error details %s' %
                      (self.task_id, str(OSError)))
예제 #7
0
 def _cleanup(self):
     """Cleans up temporary input/output files used in task execution."""
     try:
         if os.path.exists(self._get_input_file()):
             os.remove(self._get_input_file())
         if os.path.exists(self._get_output_file()):
             os.remove(self._get_output_file())
     except OSError:
         logger.error('Error during file cleanup for task %s. Error'
                      'details %s' % (self.task_id, str(OSError)))
예제 #8
0
 def _dump_payload_to_file(self):
     """Method to write input extracted from payload to a temporary file."""
     try:
         (fd, fname) = tempfile.mkstemp()
         f = os.fdopen(fd, 'w')
         f.write(self._payload)
         f.close()
         return fname
     except OSError:
         logger.error('Error dumping payload %s. Error details %s' %
                      (self.task_id, str(OSError)))
         raise ClientTaskInitError(self.task_id, 'Error dumping payload')
예제 #9
0
 def _decode_base64_payload(self, encoded_str):
     """Method to decode payload encoded in base64."""
     try:
         # If the payload is empty, do not try to decode it. Payload usually
         # not expected to be empty and hence log a warning and then
         # continue.
         if encoded_str:
             decoded_str = base64.urlsafe_b64decode(
                 encoded_str.encode('utf-8'))
             return decoded_str
         else:
             logger.warn('Empty paylaod for task %s' % self.task_id)
             return ''
     except base64.binascii.Error, berror:
         logger.error(
             'Error decoding payload for task %s. Error details %s' %
             (self.task_id, str(berror)))
         raise ClientTaskInitError(self.task_id, 'Error decoding payload')
예제 #10
0
    def _post_output(self):
        """Posts the outback back to specified url in the form of a byte
        array.

        It reads the output generated by the task as a byte-array. It posts the
        response to specified url appended with the taskId. The  application
        using the taskqueue must have a handler to handle the data being posted
        from puller. Format of body of response object is byte-array to make
        the it genric for any kind of output generated.

        Returns:
            True/False based on post status.
        """
        if FLAGS.output_url:
            try:
                f = open(self._get_output_file(), 'rb')
                body = f.read()
                f.close()
                url = FLAGS.output_url + self.task_id
                logger.debug('Posting data to url %s' % url)
                headers = {'Content-Type': 'byte-array'}
                # Add an access token to the headers if specified.
                # This enables the output_url to be authenticated and not open.
                access_token = ClientTask.get_access_token()
                if access_token:
                    consumer = oauth.Consumer('anonymous', 'anonymous')
                    oauth_req = oauth.Request.from_consumer_and_token(
                        consumer, token=access_token, http_url=url)
                    headers.update(oauth_req.to_header())
                # TODO: Use httplib instead of urllib for consistency.
                req = urllib2.Request(url, body, headers)
                urllib2.urlopen(req)
            except ValueError:
                logger.error('Error posting data back %s. Error details %s' %
                             (self.task_id, str(ValueError)))
                return False
            except Exception:
                logger.error('Exception while posting data back %s. Error'
                             'details %s' % (self.task_id, str(Exception)))
                return False
        return True
예제 #11
0
    def init(self):
        """Extracts information from task object and intializes processing.

        Extracts id and payload from task object, decodes the payload and puts
        it in input file. After this, it spawns a subprocess to execute the
        task.

        Returns:
            True if everything till task execution starts fine.
            False if anything goes wrong in initialization of task execution.
        """
        try:
            self.task_id = self._task.get('id')
            self._payload = self._decode_base64_payload(
                self._task.get('payloadBase64'))
            self._payload_file = self._dump_payload_to_file()
            self._start_task_execution()
            return True
        except ClientTaskInitError, ctie:
            logger.error(str(ctie))
            return False
 def __init__(self):
     if not FLAGS.project_name:
         raise app.UsageError('You must specify a project name'
                              ' using the "--project_name" flag.')
     discovery_uri = (FLAGS.api_host +
                      'discovery/v1/apis/{api}/{apiVersion}/rest')
     logger.info(discovery_uri)
     try:
         # Load the credentials from the service account credentials json file
         if not FLAGS.service_account_file:
             FLAGS.service_account_file = get_env_variable(
                 'GOOGLE_APPLICATION_CREDENTIALS')
         credentials = ServiceAccountCredentials.from_json_keyfile_name(
             FLAGS.service_account_file, scopes=SCOPES)
         http = credentials.authorize(
             self._dump_request_wrapper(httplib2.Http()))
         self.task_api = build('cloudtasks',
                               FLAGS.service_version,
                               http=http,
                               discoveryServiceUrl=discovery_uri)
     except HttpError, http_error:
         logger.error('Error gettin task_api: %s' % http_error)