{"action":"created","comment":{"url":"https://api.github.com/repos/pulp/pulp/pulls/comments/79236646","id":79236646,"diff_hunk":"@@ -0,0 +1,474 @@\n+from datetime import datetime\n+from gettext import gettext as _\n+import logging\n+import random\n+import signal\n+import time\n+import traceback\n+import uuid\n+\n+from celery import task, Task as CeleryTask, current_task\n+from celery.app import control, defaults\n+from celery.result import AsyncResult\n+from django.db.models import Count\n+\n+from pulp.common.constants import SCHEDULER_WORKER_NAME, RESOURCE_MANAGER_WORKER_NAME\n+from pulp.common import constants, dateutils, tags\n+from pulp.platform.models import Worker, ReservedResource, Task as TaskStatus\n+from pulp.server.exceptions import PulpException, MissingResource, \\\n+    PulpCodedException\n+from pulp.server.db.model import ResourceManagerLock\n+from pulp.server.managers.repo import _common as common_utils\n+from pulp.tasking.celery_instance import celery, RESOURCE_MANAGER_QUEUE, \\\n+    DEDICATED_QUEUE_EXCHANGE\n+\n+\n+celery_controller = control.Control(app=celery)\n+_logger = logging.getLogger(__name__)\n+\n+\n+class PulpTask(CeleryTask):\n+    \"\"\"\n+    The ancestor of all Celery tasks in Pulp. Use the 'base' argument to specify this task as its\n+    parent. For example:\n+\n+        >>> from celery import task\n+        >>> @task(base=PulpTask, acks_late=True)\n+        >>> def sum(a, b):\n+        >>>     return a + b\n+\n+    This object provides a centralized place to put behavioral changes which should affect all\n+    tasks.\n+    \"\"\"\n+    pass\n+\n+\n+@task(base=PulpTask, acks_late=True)\n+def _queue_reserved_task(name, task_id, resource_id, inner_args, inner_kwargs):\n+    \"\"\"\n+    A task that encapsulates another task to be dispatched later. This task being encapsulated is\n+    called the \"inner\" task, and a task name, UUID, and accepts a list of positional args\n+    and keyword args for the inner task. These arguments are named inner_args and inner_kwargs.\n+    inner_args is a list, and inner_kwargs is a dictionary passed to the inner task as positional\n+    and keyword arguments using the * and ** operators.\n+\n+    The inner task is dispatched into a dedicated queue for a worker that is decided at dispatch\n+    time. The logic deciding which queue receives a task is controlled through the\n+    find_worker function.\n+\n+    :param name:          The name of the task to be called\n+    :type name:           basestring\n+    :param inner_task_id: The UUID to be set on the task being called. By providing\n+                          the UUID, the caller can have an asynchronous reference to the inner task\n+                          that will be dispatched.\n+    :type inner_task_id:  basestring\n+    :param resource_id:   The name of the resource you wish to reserve for your task. The system\n+                          will ensure that no other tasks that want that same reservation will run\n+                          concurrently with yours.\n+    :type  resource_id:   basestring\n+\n+    :return: None\n+    \"\"\"\n+    while True:\n+        # Find a worker who already has this reservation, it is safe to send this work to them\n+        try:\n+            worker = ReservedResource.objects.get(resource=resource_id).worker\n+        except ReservedResource.DoesNotExist:\n+            pass\n+        else:\n+            break\n+\n+        try:\n+            worker = _get_unreserved_worker()\n+        except Worker.DoesNotExist:\n+            pass\n+        else:\n+            break\n+\n+        # No worker is ready for this work, so we need to wait\n+        time.sleep(0.25)\n+\n+    task_status = TaskStatus.objects.get(id=task_id)\n+    ReservedResource(task=task_status, worker=worker, resource=resource_id).save()\n+\n+    inner_kwargs['routing_key'] = worker.name\n+    inner_kwargs['exchange'] = DEDICATED_QUEUE_EXCHANGE\n+    inner_kwargs['task_id'] = task_id\n+\n+    try:\n+        celery.tasks[name].apply_async(*inner_args, **inner_kwargs)\n+    finally:\n+        _release_resource.apply_async((task_id, ), routing_key=worker.name,\n+                                      exchange=DEDICATED_QUEUE_EXCHANGE)\n+\n+\n+def _is_worker(worker_name):\n+    \"\"\"\n+    Strip out workers that should never be assigned work. We need to check\n+    via \"startswith()\" since we do not know which host the worker is running on.\n+    \"\"\"\n+\n+    if worker_name.startswith(SCHEDULER_WORKER_NAME) or \\\n+       worker_name.startswith(RESOURCE_MANAGER_QUEUE):\n+        return False\n+    return True\n+\n+\n+def _get_unreserved_worker():\n+    \"\"\"\n+    Find an unreserved Worker\n+\n+    Return the Worker instance that has no ReservedResource associated with it. If all workers have\n+    ReservedResource relationships a Worker.DoesNotExist pulp.platform.model.Worker exception is\n+    raised.\n+\n+    This function also provides randomization for worker selection.\n+\n+    :raises Worker.DoesNotExist: If all workers have ReservedResource entries associated with them.\n+\n+    :returns:          The Worker instance that has no reserved_resource\n+                       entries associated with it.\n+    :rtype:            pulp.platform.model.Worker\n+    \"\"\"\n+    free_workers = [Worker.objects.annotate(Count('reservations')).filter(reservations__count=0)]\n+    if len(free_workers) == 0:\n+        raise Worker.DoesNotExist()\n+    random.shuffle(free_workers)\n+    return free_workers[0]\n+\n+\n+def _delete_worker(name, normal_shutdown=False):\n+    \"\"\"\n+    Delete the Worker with name from the database, cancel any associated tasks and reservations\n+\n+    If the worker shutdown normally, no message is logged, otherwise an error level message is\n+    logged. Default is to assume the worker did not shut down normally.\n+\n+    Any resource reservations associated with this worker are cleaned up by this function.\n+\n+    Any tasks associated with this worker are explicitly canceled.\n+\n+    :param name:            The name of the worker you wish to delete.\n+    :type  name:            basestring\n+    :param normal_shutdown: True if the worker shutdown normally, False otherwise. Defaults to\n+                            False.\n+    :type normal_shutdown:  bool\n+    \"\"\"\n+    if not normal_shutdown:\n+        msg = _('The worker named %(name)s is missing. Canceling the tasks in its queue.')\n+        msg = msg % {'name': name}\n+        _logger.error(msg)\n+\n+    # Cancel all of the tasks that were assigned to this worker's queue\n+    for task_status in Worker.objects.tasks.filter(state__in=constants.CALL_INCOMPLETE_STATES):\n+        cancel(task_status.id)\n+\n+    # Delete the worker document\n+    Worker.objects.get(name=name).delete()\n+\n+    # If the worker is a resource manager, we also need to delete the associated lock\n+    if name.startswith(RESOURCE_MANAGER_WORKER_NAME):\n+        ResourceManagerLock.objects.filter(name=name).delete()\n+\n+\n+@task(base=PulpTask)\n+def _release_resource(task_id):\n+    \"\"\"\n+    Do not queue this task yourself. It will be used automatically when your task is dispatched by\n+    the _queue_reserved_task task.\n+\n+    When a resource-reserving task is complete, this method releases the resource by removing the\n+    ReservedResource object by UUID.\n+\n+    :param task_id: The UUID of the task that requested the reservation\n+    :type  task_id: basestring\n+    \"\"\"\n+    try:\n+        TaskStatus.objects.get(id=task_id, state=constants.CALL_RUNNING_STATE)\n+    except TaskStatus.DoesNotExist:\n+        pass\n+    else:\n+        new_task = Task()\n+        msg = _('The task status %(task_id)s exited immediately for some reason. Marking as '\n+                'errored. Check the logs for more details')\n+        runtime_exception = RuntimeError(msg % {'task_id': task_id})\n+\n+        class MyEinfo(object):\n+            traceback = None\n+\n+        new_task.on_failure(runtime_exception, task_id, (), {}, MyEinfo)\n+\n+    ReservedResource.objects.get(task__id=task_id).delete()\n+\n+\n+class UserFacingPulpTask(PulpTask):","path":"platform/pulp/tasking/system.py","position":204,"original_position":204,"commit_id":"2ec604f72a3df05a4b45619b531164615d848fa3","original_commit_id":"2ec604f72a3df05a4b45619b531164615d848fa3","user":{"login":"bmbouter","id":180319,"avatar_url":"https://avatars.githubusercontent.com/u/180319?v=3","gravatar_id":"","url":"https://api.github.com/users/bmbouter","html_url":"https://github.com/bmbouter","followers_url":"https://api.github.com/users/bmbouter/followers","following_url":"https://api.github.com/users/bmbouter/following{/other_user}","gists_url":"https://api.github.com/users/bmbouter/gists{/gist_id}","starred_url":"https://api.github.com/users/bmbouter/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/bmbouter/subscriptions","organizations_url":"https://api.github.com/users/bmbouter/orgs","repos_url":"https://api.github.com/users/bmbouter/repos","events_url":"https://api.github.com/users/bmbouter/events{/privacy}","received_events_url":"https://api.github.com/users/bmbouter/received_events","type":"User","site_admin":false},"body":"I agree, my next version will use `UserFacingTask`.","created_at":"2016-09-16T19:34:16Z","updated_at":"2016-09-16T19:34:16Z","html_url":"https://github.com/pulp/pulp/pull/2752#discussion_r79236646","pull_request_url":"https://api.github.com/repos/pulp/pulp/pulls/2752","_links":{"self":{"href":"https://api.github.com/repos/pulp/pulp/pulls/comments/79236646"},"html":{"href":"https://github.com/pulp/pulp/pull/2752#discussion_r79236646"},"pull_request":{"href":"https://api.github.com/repos/pulp/pulp/pulls/2752"}}},"pull_request":{"url":"https://api.github.com/repos/pulp/pulp/pulls/2752","id":84920342,"html_url":"https://github.com/pulp/pulp/pull/2752","diff_url":"https://github.com/pulp/pulp/pull/2752.diff","patch_url":"https://github.com/pulp/pulp/pull/2752.patch","issue_url":"https://api.github.com/repos/pulp/pulp/issues/2752","number":2752,"state":"open","locked":false,"title":"Converts tasking system to use Django models","user":{"login":"bmbouter","id":180319,"avatar_url":"https://avatars.githubusercontent.com/u/180319?v=3","gravatar_id":"","url":"https://api.github.com/users/bmbouter","html_url":"https://github.com/bmbouter","followers_url":"https://api.github.com/users/bmbouter/followers","following_url":"https://api.github.com/users/bmbouter/following{/other_user}","gists_url":"https://api.github.com/users/bmbouter/gists{/gist_id}","starred_url":"https://api.github.com/users/bmbouter/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/bmbouter/subscriptions","organizations_url":"https://api.github.com/users/bmbouter/orgs","repos_url":"https://api.github.com/users/bmbouter/repos","events_url":"https://api.github.com/users/bmbouter/events{/privacy}","received_events_url":"https://api.github.com/users/bmbouter/received_events","type":"User","site_admin":false},"body":"Moves all tasking code to pulp.platform.tasks and updates\r\nexisting usage to use the new location. Also the code\r\nitself is converted to use the new Django models.\r\n\r\nhttps://pulp.plan.io/issues/2154\r\ncloses #2154","created_at":"2016-09-12T18:28:13Z","updated_at":"2016-09-16T19:34:16Z","closed_at":null,"merged_at":null,"merge_commit_sha":"3502e6dfd73e6e1c7be141c25f7fd8eee54e1427","assignee":{"login":"bmbouter","id":180319,"avatar_url":"https://avatars.githubusercontent.com/u/180319?v=3","gravatar_id":"","url":"https://api.github.com/users/bmbouter","html_url":"https://github.com/bmbouter","followers_url":"https://api.github.com/users/bmbouter/followers","following_url":"https://api.github.com/users/bmbouter/following{/other_user}","gists_url":"https://api.github.com/users/bmbouter/gists{/gist_id}","starred_url":"https://api.github.com/users/bmbouter/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/bmbouter/subscriptions","organizations_url":"https://api.github.com/users/bmbouter/orgs","repos_url":"https://api.github.com/users/bmbouter/repos","events_url":"https://api.github.com/users/bmbouter/events{/privacy}","received_events_url":"https://api.github.com/users/bmbouter/received_events","type":"User","site_admin":false},"assignees":[{"login":"bmbouter","id":180319,"avatar_url":"https://avatars.githubusercontent.com/u/180319?v=3","gravatar_id":"","url":"https://api.github.com/users/bmbouter","html_url":"https://github.com/bmbouter","followers_url":"https://api.github.com/users/bmbouter/followers","following_url":"https://api.github.com/users/bmbouter/following{/other_user}","gists_url":"https://api.github.com/users/bmbouter/gists{/gist_id}","starred_url":"https://api.github.com/users/bmbouter/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/bmbouter/subscriptions","organizations_url":"https://api.github.com/users/bmbouter/orgs","repos_url":"https://api.github.com/users/bmbouter/repos","events_url":"https://api.github.com/users/bmbouter/events{/privacy}","received_events_url":"https://api.github.com/users/bmbouter/received_events","type":"User","site_admin":false}],"milestone":null,"commits_url":"https://api.github.com/repos/pulp/pulp/pulls/2752/commits","review_comments_url":"https://api.github.com/repos/pulp/pulp/pulls/2752/comments","review_comment_url":"https://api.github.com/repos/pulp/pulp/pulls/comments{/number}","comments_url":"https://api.github.com/repos/pulp/pulp/issues/2752/comments","statuses_url":"https://api.github.com/repos/pulp/pulp/statuses/2ec604f72a3df05a4b45619b531164615d848fa3","head":{"label":"bmbouter:2154-Belgian-Dubbel","ref":"2154-Belgian-Dubbel","sha":"2ec604f72a3df05a4b45619b531164615d848fa3","user":{"login":"bmbouter","id":180319,"avatar_url":"https://avatars.githubusercontent.com/u/180319?v=3","gravatar_id":"","url":"https://api.github.com/users/bmbouter","html_url":"https://github.com/bmbouter","followers_url":"https://api.github.com/users/bmbouter/followers","following_url":"https://api.github.com/users/bmbouter/following{/other_user}","gists_url":"https://api.github.com/users/bmbouter/gists{/gist_id}","starred_url":"https://api.github.com/users/bmbouter/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/bmbouter/subscriptions","organizations_url":"https://api.github.com/users/bmbouter/orgs","repos_url":"https://api.github.com/users/bmbouter/repos","events_url":"https://api.github.com/users/bmbouter/events{/privacy}","received_events_url":"https://api.github.com/users/bmbouter/received_events","type":"User","site_admin":false},"repo":{"id":24460180,"name":"pulp","full_name":"bmbouter/pulp","owner":{"login":"bmbouter","id":180319,"avatar_url":"https://avatars.githubusercontent.com/u/180319?v=3","gravatar_id":"","url":"https://api.github.com/users/bmbouter","html_url":"https://github.com/bmbouter","followers_url":"https://api.github.com/users/bmbouter/followers","following_url":"https://api.github.com/users/bmbouter/following{/other_user}","gists_url":"https://api.github.com/users/bmbouter/gists{/gist_id}","starred_url":"https://api.github.com/users/bmbouter/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/bmbouter/subscriptions","organizations_url":"https://api.github.com/users/bmbouter/orgs","repos_url":"https://api.github.com/users/bmbouter/repos","events_url":"https://api.github.com/users/bmbouter/events{/privacy}","received_events_url":"https://api.github.com/users/bmbouter/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/bmbouter/pulp","description":"Repository for Pulp platform code, including the server and base admin and consumer clients","fork":true,"url":"https://api.github.com/repos/bmbouter/pulp","forks_url":"https://api.github.com/repos/bmbouter/pulp/forks","keys_url":"https://api.github.com/repos/bmbouter/pulp/keys{/key_id}","collaborators_url":"https://api.github.com/repos/bmbouter/pulp/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/bmbouter/pulp/teams","hooks_url":"https://api.github.com/repos/bmbouter/pulp/hooks","issue_events_url":"https://api.github.com/repos/bmbouter/pulp/issues/events{/number}","events_url":"https://api.github.com/repos/bmbouter/pulp/events","assignees_url":"https://api.github.com/repos/bmbouter/pulp/assignees{/user}","branches_url":"https://api.github.com/repos/bmbouter/pulp/branches{/branch}","tags_url":"https://api.github.com/repos/bmbouter/pulp/tags","blobs_url":"https://api.github.com/repos/bmbouter/pulp/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/bmbouter/pulp/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/bmbouter/pulp/git/refs{/sha}","trees_url":"https://api.github.com/repos/bmbouter/pulp/git/trees{/sha}","statuses_url":"https://api.github.com/repos/bmbouter/pulp/statuses/{sha}","languages_url":"https://api.github.com/repos/bmbouter/pulp/languages","stargazers_url":"https://api.github.com/repos/bmbouter/pulp/stargazers","contributors_url":"https://api.github.com/repos/bmbouter/pulp/contributors","subscribers_url":"https://api.github.com/repos/bmbouter/pulp/subscribers","subscription_url":"https://api.github.com/repos/bmbouter/pulp/subscription","commits_url":"https://api.github.com/repos/bmbouter/pulp/commits{/sha}","git_commits_url":"https://api.github.com/repos/bmbouter/pulp/git/commits{/sha}","comments_url":"https://api.github.com/repos/bmbouter/pulp/comments{/number}","issue_comment_url":"https://api.github.com/repos/bmbouter/pulp/issues/comments{/number}","contents_url":"https://api.github.com/repos/bmbouter/pulp/contents/{+path}","compare_url":"https://api.github.com/repos/bmbouter/pulp/compare/{base}...{head}","merges_url":"https://api.github.com/repos/bmbouter/pulp/merges","archive_url":"https://api.github.com/repos/bmbouter/pulp/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/bmbouter/pulp/downloads","issues_url":"https://api.github.com/repos/bmbouter/pulp/issues{/number}","pulls_url":"https://api.github.com/repos/bmbouter/pulp/pulls{/number}","milestones_url":"https://api.github.com/repos/bmbouter/pulp/milestones{/number}","notifications_url":"https://api.github.com/repos/bmbouter/pulp/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/bmbouter/pulp/labels{/name}","releases_url":"https://api.github.com/repos/bmbouter/pulp/releases{/id}","deployments_url":"https://api.github.com/repos/bmbouter/pulp/deployments","created_at":"2014-09-25T13:58:32Z","updated_at":"2016-07-14T16:48:25Z","pushed_at":"2016-09-16T15:38:51Z","git_url":"git://github.com/bmbouter/pulp.git","ssh_url":"git@github.com:bmbouter/pulp.git","clone_url":"https://github.com/bmbouter/pulp.git","svn_url":"https://github.com/bmbouter/pulp","homepage":"","size":91924,"stargazers_count":1,"watchers_count":1,"language":"Python","has_issues":false,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":0,"forks":0,"open_issues":0,"watchers":1,"default_branch":"master"}},"base":{"label":"pulp:3.0-dev","ref":"3.0-dev","sha":"d92374fbc4cf36e85b4e942fcfdb3efbade2124d","user":{"login":"pulp","id":2103606,"avatar_url":"https://avatars.githubusercontent.com/u/2103606?v=3","gravatar_id":"","url":"https://api.github.com/users/pulp","html_url":"https://github.com/pulp","followers_url":"https://api.github.com/users/pulp/followers","following_url":"https://api.github.com/users/pulp/following{/other_user}","gists_url":"https://api.github.com/users/pulp/gists{/gist_id}","starred_url":"https://api.github.com/users/pulp/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/pulp/subscriptions","organizations_url":"https://api.github.com/users/pulp/orgs","repos_url":"https://api.github.com/users/pulp/repos","events_url":"https://api.github.com/users/pulp/events{/privacy}","received_events_url":"https://api.github.com/users/pulp/received_events","type":"Organization","site_admin":false},"repo":{"id":5314652,"name":"pulp","full_name":"pulp/pulp","owner":{"login":"pulp","id":2103606,"avatar_url":"https://avatars.githubusercontent.com/u/2103606?v=3","gravatar_id":"","url":"https://api.github.com/users/pulp","html_url":"https://github.com/pulp","followers_url":"https://api.github.com/users/pulp/followers","following_url":"https://api.github.com/users/pulp/following{/other_user}","gists_url":"https://api.github.com/users/pulp/gists{/gist_id}","starred_url":"https://api.github.com/users/pulp/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/pulp/subscriptions","organizations_url":"https://api.github.com/users/pulp/orgs","repos_url":"https://api.github.com/users/pulp/repos","events_url":"https://api.github.com/users/pulp/events{/privacy}","received_events_url":"https://api.github.com/users/pulp/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/pulp/pulp","description":"Repository for Pulp platform code, including the server and base admin and consumer clients","fork":false,"url":"https://api.github.com/repos/pulp/pulp","forks_url":"https://api.github.com/repos/pulp/pulp/forks","keys_url":"https://api.github.com/repos/pulp/pulp/keys{/key_id}","collaborators_url":"https://api.github.com/repos/pulp/pulp/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/pulp/pulp/teams","hooks_url":"https://api.github.com/repos/pulp/pulp/hooks","issue_events_url":"https://api.github.com/repos/pulp/pulp/issues/events{/number}","events_url":"https://api.github.com/repos/pulp/pulp/events","assignees_url":"https://api.github.com/repos/pulp/pulp/assignees{/user}","branches_url":"https://api.github.com/repos/pulp/pulp/branches{/branch}","tags_url":"https://api.github.com/repos/pulp/pulp/tags","blobs_url":"https://api.github.com/repos/pulp/pulp/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/pulp/pulp/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/pulp/pulp/git/refs{/sha}","trees_url":"https://api.github.com/repos/pulp/pulp/git/trees{/sha}","statuses_url":"https://api.github.com/repos/pulp/pulp/statuses/{sha}","languages_url":"https://api.github.com/repos/pulp/pulp/languages","stargazers_url":"https://api.github.com/repos/pulp/pulp/stargazers","contributors_url":"https://api.github.com/repos/pulp/pulp/contributors","subscribers_url":"https://api.github.com/repos/pulp/pulp/subscribers","subscription_url":"https://api.github.com/repos/pulp/pulp/subscription","commits_url":"https://api.github.com/repos/pulp/pulp/commits{/sha}","git_commits_url":"https://api.github.com/repos/pulp/pulp/git/commits{/sha}","comments_url":"https://api.github.com/repos/pulp/pulp/comments{/number}","issue_comment_url":"https://api.github.com/repos/pulp/pulp/issues/comments{/number}","contents_url":"https://api.github.com/repos/pulp/pulp/contents/{+path}","compare_url":"https://api.github.com/repos/pulp/pulp/compare/{base}...{head}","merges_url":"https://api.github.com/repos/pulp/pulp/merges","archive_url":"https://api.github.com/repos/pulp/pulp/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/pulp/pulp/downloads","issues_url":"https://api.github.com/repos/pulp/pulp/issues{/number}","pulls_url":"https://api.github.com/repos/pulp/pulp/pulls{/number}","milestones_url":"https://api.github.com/repos/pulp/pulp/milestones{/number}","notifications_url":"https://api.github.com/repos/pulp/pulp/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/pulp/pulp/labels{/name}","releases_url":"https://api.github.com/repos/pulp/pulp/releases{/id}","deployments_url":"https://api.github.com/repos/pulp/pulp/deployments","created_at":"2012-08-06T13:49:20Z","updated_at":"2016-09-15T22:07:21Z","pushed_at":"2016-09-16T19:18:13Z","git_url":"git://github.com/pulp/pulp.git","ssh_url":"git@github.com:pulp/pulp.git","clone_url":"https://github.com/pulp/pulp.git","svn_url":"https://github.com/pulp/pulp","homepage":"","size":102996,"stargazers_count":280,"watchers_count":280,"language":"Python","has_issues":false,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":112,"mirror_url":null,"open_issues_count":18,"forks":112,"open_issues":18,"watchers":280,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/pulp/pulp/pulls/2752"},"html":{"href":"https://github.com/pulp/pulp/pull/2752"},"issue":{"href":"https://api.github.com/repos/pulp/pulp/issues/2752"},"comments":{"href":"https://api.github.com/repos/pulp/pulp/issues/2752/comments"},"review_comments":{"href":"https://api.github.com/repos/pulp/pulp/pulls/2752/comments"},"review_comment":{"href":"https://api.github.com/repos/pulp/pulp/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/pulp/pulp/pulls/2752/commits"},"statuses":{"href":"https://api.github.com/repos/pulp/pulp/statuses/2ec604f72a3df05a4b45619b531164615d848fa3"}}}}
{"actor":{"display_login":"bmbouter"}}