{"action":"created","comment":{"url":"https://api.github.com/repos/autolab/Tango/pulls/comments/26831511","id":26831511,"diff_hunk":"@@ -0,0 +1,323 @@\n+#\n+# dockerSSH.py - Implements the Tango VMMS interface to run Tango jobs in \n+#                docker containers. In this context, VMs are docker containers.\n+#\n+import random, subprocess, re, time, logging, threading, os, sys\n+import config\n+from tangoObjects import TangoMachine\n+\n+def timeout(command, time_out=1):\n+    \"\"\" timeout - Run a unix command with a timeout. Return -1 on\n+    timeout, otherwise return the return value from the command, which\n+    is typically 0 for success, 1-255 for failure.\n+    \"\"\" \n+\n+    # Launch the command\n+    p = subprocess.Popen(command,\n+                        stdout=open(\"/dev/null\", 'w'),\n+                        stderr=subprocess.STDOUT)\n+\n+    # Wait for the command to complete\n+    t = 0.0\n+    while t < time_out and p.poll() is None:\n+        time.sleep(config.Config.TIMER_POLL_INTERVAL)\n+        t += config.Config.TIMER_POLL_INTERVAL\n+\n+    # Determine why the while loop terminated\n+    if p.poll() is None:\n+        subprocess.call([\"/bin/kill\", \"-9\", str(p.pid)])\n+        returncode = -1\n+    else:\n+        returncode = p.poll()\n+    return returncode\n+\n+def timeoutWithReturnStatus(command, time_out, returnValue = 0):\n+    \"\"\" timeoutWithReturnStatus - Run a Unix command with a timeout,\n+    until the expected value is returned by the command; On timeout,\n+    return last error code obtained from the command.\n+    \"\"\"\n+    p = subprocess.Popen(command, \n+                        stdout=open(\"/dev/null\", 'w'), \n+                        stderr=subprocess.STDOUT)\n+    t = 0.0\n+    while (t < time_out):\n+        ret = p.poll()\n+        if ret is None:\n+            time.sleep(config.Config.TIMER_POLL_INTERVAL)\n+            t += config.Config.TIMER_POLL_INTERVAL\n+        elif ret == returnValue:\n+            return ret\n+        else:\n+            p = subprocess.Popen(command,\n+                            stdout=open(\"/dev/null\", 'w'),\n+                            stderr=subprocess.STDOUT)\n+    return ret\n+\n+def dockerExec(container, cmd, time_out=1):\n+    \"\"\" docerExec - Executes `docker exec container cmd` and\n+        returns output. Container is the name of the docker\n+        container and cmd is a list of commands to run.\n+    \"\"\"\n+    command = ['docker', 'exec', container, 'sh', '-c'] + cmd\n+    return timeout(command, time_out)\n+\n+#\n+# User defined exceptions\n+#\n+\n+class DockerSSH:\n+    _OS_X = 'darwin'\n+    LOCALHOST = '127.0.0.1'\n+\n+    def __init__(self):\n+        \"\"\"\n+\t\t\tChecks if the machine is ready to run docker containers.\n+            Initialize boot2docker if running on OS X.\n+        \"\"\"\n+        try:\n+            self.log = logging.getLogger(\"DockerSSH\")\n+            # If running on OS X, create a boot2docker VM\n+            if sys.platform == self._OS_X:\n+                # self.boot2dockerVM()\n+                # boot2docker initialization will be part of initial\n+                # set up with Tango.\n+                self.docker_host_ip = subprocess.check_output(['boot2docker', 'ip']).strip('\\n')\n+            else:\n+                self.docker_host_ip = self.LOCALHOST\n+\n+            self.log.info(\"Docker host IP is %s\" % self.docker_host_ip)\n+\n+        except Exception as e:\n+            self.log.error(str(e))\n+            exit(1)\n+\n+    def boot2dockerVM(self):\n+        \"\"\" boot2dockerVM - Initializes and starts a boot2docker \n+            VM and sets its environment variables. If boot2docker\n+            VM has already been set up on the machine, these steps\n+            will simply exit gracefully.\n+        \"\"\"\n+        init_ret = -1\n+        start_ret = -1\n+        env_ret = -1\n+        image_ret = -1\n+\n+        self.log.debug(\"Initializing boot2docker VM.\")\n+        init_ret = timeout(['boot2docker', 'init'], \n+                            config.Config.BOOT2DOCKER_INIT_TIMEOUT)\n+        \n+        self.log.debug(\"Starting boot2docker VM.\")\n+        if init_ret == 0:\n+            start_ret = timeout(['boot2docker', 'start'], \n+                        config.Config.BOOT2DOCKER_START_TIMEOUT)\n+        \n+        self.log.debug(\"Setting environment variables for boot2docker VM.\")\n+        if start_ret == 0:\n+            env_ret = timeout(['boot2docker', 'shellinit'],\n+                        config.Config.BOOT2DOCKER_ENV_TIMEOUT)\n+        \n+        self.log.debug(\"Pulling the autolab docker image from docker hub.\")\n+        if env_ret == 0:\n+            image_ret = timeout(['docker', 'build', '-t', \n+                        self.config.Config.DOCKER_IMAGE, '.'],\n+                        config.Config.DOCKER_IMAGE_BUILD_TIMEOUT)\n+\n+        if init_ret != 0:\n+            raise Exception('Could not initialize boot2docker.')\n+        if start_ret != 0:\n+            raise Exception('Could not start boot2docker VM.')\n+        if env_ret != 0:\n+            raise Exception('Could not set environment variables \\\n+                of boot2docker VM.')\n+        if image_ret != 0:\n+            raise Exception('Could not pull autolab docker image \\\n+                from docker hub.')\n+\n+    def instanceName(self, id, name):\n+        \"\"\" instanceName - Constructs a VM instance name. Always use\n+        this function when you need a VM instance name. Never generate\n+        instance names manually.\n+        \"\"\"\n+        return \"%s-%d-%s\" % (config.Config.PREFIX, id, name)\n+\n+    def domainName(self, vm):\n+        \"\"\" Returns the domain name that is stored in the vm\n+        instance.\n+        \"\"\"\n+        return vm.domain_name\n+\n+    #\n+    # VMMS API functions\n+    #\n+    def initializeVM(self, vm):\n+        \"\"\" initializeVM -  Start dockerized autograding container by \n+        running a trivially long-running process so that the container\n+        continues to run. Otherwise, the container will stop running \n+        once the program has come to completion.\n+        \"\"\"\n+        instanceName = self.instanceName(vm.id, vm.name)\n+        args = ['docker', 'run', '-d']\n+        args.append('--name')\n+        args.append(instanceName)\n+        args.append(config.Config.DOCKER_IMAGE)\n+        args.append('/bin/bash')\n+        args.append('-c')\n+        args.append('while true; do sleep 1; done')\n+        ret = timeout(args, config.Config.INITIALIZEVM_TIMEOUT)","path":"vmms/dockerSSH.py","position":166,"original_position":166,"commit_id":"2fbc989cb231af9972525852f3ed2fe8cb5e54de","original_commit_id":"2fbc989cb231af9972525852f3ed2fe8cb5e54de","user":{"login":"jez","id":5544532,"avatar_url":"https://avatars.githubusercontent.com/u/5544532?v=3","gravatar_id":"","url":"https://api.github.com/users/jez","html_url":"https://github.com/jez","followers_url":"https://api.github.com/users/jez/followers","following_url":"https://api.github.com/users/jez/following{/other_user}","gists_url":"https://api.github.com/users/jez/gists{/gist_id}","starred_url":"https://api.github.com/users/jez/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jez/subscriptions","organizations_url":"https://api.github.com/users/jez/orgs","repos_url":"https://api.github.com/users/jez/repos","events_url":"https://api.github.com/users/jez/events{/privacy}","received_events_url":"https://api.github.com/users/jez/received_events","type":"User","site_admin":false},"body":"Once the image signature exists locally, it will be stored. Each step of\nthe dockfile build is cached, so even if you run docker build many times it\nwill only build it once.\n\nWe don't need our image on docker hub for this reason. We probably don't\nwant it there anyways, because it's not the kind of thing that is normally\nmade public. We might consider running our own docker registry later, so\nthat instructors could upload their docker files there.\n\nEven though docker will cache build steps, it doesn't make semantic sense\nto docker build the image once every time we initialize VMs. It doesn't\nlook like that's what you're doing here, but since Ilter brought it up I\nthought I'd give you the opportunity to confirm this.\nOn Fri, Mar 20, 2015 at 3:55 AM Ilter <notifications@github.com> wrote:\n\n> In vmms/dockerSSH.py\n> <https://github.com/autolab/Tango/pull/45#discussion_r26823357>:\n>\n> > +    #\n> > +    def initializeVM(self, vm):\n> > +        \"\"\" initializeVM -  Start dockerized autograding container by\n> > +        running a trivially long-running process so that the container\n> > +        continues to run. Otherwise, the container will stop running\n> > +        once the program has come to completion.\n> > +        \"\"\"\n> > +        instanceName = self.instanceName(vm.id, vm.name)\n> > +        args = ['docker', 'run', '-d']\n> > +        args.append('--name')\n> > +        args.append(instanceName)\n> > +        args.append(config.Config.DOCKER_IMAGE)\n> > +        args.append('/bin/bash')\n> > +        args.append('-c')\n> > +        args.append('while true; do sleep 1; done')\n> > +        ret = timeout(args, config.Config.INITIALIZEVM_TIMEOUT)\n>\n> So do we create the container from the Dockerfile everytime we need a new\n> VM? I feel like it would take way too long to install all the dev binary\n> etc and setup that Docker. Isn't it possible to create a base docker, push\n> it to registry and build from that image in the registry?\n>\n> —\n> Reply to this email directly or view it on GitHub\n> <https://github.com/autolab/Tango/pull/45/files#r26823357>.\n>\n","created_at":"2015-03-20T10:49:46Z","updated_at":"2015-03-20T10:49:46Z","html_url":"https://github.com/autolab/Tango/pull/45#discussion_r26831511","pull_request_url":"https://api.github.com/repos/autolab/Tango/pulls/45","_links":{"self":{"href":"https://api.github.com/repos/autolab/Tango/pulls/comments/26831511"},"html":{"href":"https://github.com/autolab/Tango/pull/45#discussion_r26831511"},"pull_request":{"href":"https://api.github.com/repos/autolab/Tango/pulls/45"}}},"pull_request":{"url":"https://api.github.com/repos/autolab/Tango/pulls/45","id":31421939,"html_url":"https://github.com/autolab/Tango/pull/45","diff_url":"https://github.com/autolab/Tango/pull/45.diff","patch_url":"https://github.com/autolab/Tango/pull/45.patch","issue_url":"https://api.github.com/repos/autolab/Tango/issues/45","number":45,"state":"open","locked":false,"title":"Implementation of Docker vmms","user":{"login":"icanberk","id":803072,"avatar_url":"https://avatars.githubusercontent.com/u/803072?v=3","gravatar_id":"","url":"https://api.github.com/users/icanberk","html_url":"https://github.com/icanberk","followers_url":"https://api.github.com/users/icanberk/followers","following_url":"https://api.github.com/users/icanberk/following{/other_user}","gists_url":"https://api.github.com/users/icanberk/gists{/gist_id}","starred_url":"https://api.github.com/users/icanberk/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/icanberk/subscriptions","organizations_url":"https://api.github.com/users/icanberk/orgs","repos_url":"https://api.github.com/users/icanberk/repos","events_url":"https://api.github.com/users/icanberk/events{/privacy}","received_events_url":"https://api.github.com/users/icanberk/received_events","type":"User","site_admin":false},"body":"","created_at":"2015-03-18T11:56:17Z","updated_at":"2015-03-20T10:49:46Z","closed_at":null,"merged_at":null,"merge_commit_sha":"00f3a9909e17fa2aa6568a27a3623faf9c7cf659","assignee":{"login":"yrkumar","id":2357784,"avatar_url":"https://avatars.githubusercontent.com/u/2357784?v=3","gravatar_id":"","url":"https://api.github.com/users/yrkumar","html_url":"https://github.com/yrkumar","followers_url":"https://api.github.com/users/yrkumar/followers","following_url":"https://api.github.com/users/yrkumar/following{/other_user}","gists_url":"https://api.github.com/users/yrkumar/gists{/gist_id}","starred_url":"https://api.github.com/users/yrkumar/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/yrkumar/subscriptions","organizations_url":"https://api.github.com/users/yrkumar/orgs","repos_url":"https://api.github.com/users/yrkumar/repos","events_url":"https://api.github.com/users/yrkumar/events{/privacy}","received_events_url":"https://api.github.com/users/yrkumar/received_events","type":"User","site_admin":false},"milestone":null,"commits_url":"https://api.github.com/repos/autolab/Tango/pulls/45/commits","review_comments_url":"https://api.github.com/repos/autolab/Tango/pulls/45/comments","review_comment_url":"https://api.github.com/repos/autolab/Tango/pulls/comments{/number}","comments_url":"https://api.github.com/repos/autolab/Tango/issues/45/comments","statuses_url":"https://api.github.com/repos/autolab/Tango/statuses/2fbc989cb231af9972525852f3ed2fe8cb5e54de","head":{"label":"autolab:docker_vmms","ref":"docker_vmms","sha":"2fbc989cb231af9972525852f3ed2fe8cb5e54de","user":{"login":"autolab","id":2337016,"avatar_url":"https://avatars.githubusercontent.com/u/2337016?v=3","gravatar_id":"","url":"https://api.github.com/users/autolab","html_url":"https://github.com/autolab","followers_url":"https://api.github.com/users/autolab/followers","following_url":"https://api.github.com/users/autolab/following{/other_user}","gists_url":"https://api.github.com/users/autolab/gists{/gist_id}","starred_url":"https://api.github.com/users/autolab/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/autolab/subscriptions","organizations_url":"https://api.github.com/users/autolab/orgs","repos_url":"https://api.github.com/users/autolab/repos","events_url":"https://api.github.com/users/autolab/events{/privacy}","received_events_url":"https://api.github.com/users/autolab/received_events","type":"Organization","site_admin":false},"repo":{"id":25789674,"name":"Tango","full_name":"autolab/Tango","owner":{"login":"autolab","id":2337016,"avatar_url":"https://avatars.githubusercontent.com/u/2337016?v=3","gravatar_id":"","url":"https://api.github.com/users/autolab","html_url":"https://github.com/autolab","followers_url":"https://api.github.com/users/autolab/followers","following_url":"https://api.github.com/users/autolab/following{/other_user}","gists_url":"https://api.github.com/users/autolab/gists{/gist_id}","starred_url":"https://api.github.com/users/autolab/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/autolab/subscriptions","organizations_url":"https://api.github.com/users/autolab/orgs","repos_url":"https://api.github.com/users/autolab/repos","events_url":"https://api.github.com/users/autolab/events{/privacy}","received_events_url":"https://api.github.com/users/autolab/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/autolab/Tango","description":"External standalone, RESTful job service","fork":false,"url":"https://api.github.com/repos/autolab/Tango","forks_url":"https://api.github.com/repos/autolab/Tango/forks","keys_url":"https://api.github.com/repos/autolab/Tango/keys{/key_id}","collaborators_url":"https://api.github.com/repos/autolab/Tango/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/autolab/Tango/teams","hooks_url":"https://api.github.com/repos/autolab/Tango/hooks","issue_events_url":"https://api.github.com/repos/autolab/Tango/issues/events{/number}","events_url":"https://api.github.com/repos/autolab/Tango/events","assignees_url":"https://api.github.com/repos/autolab/Tango/assignees{/user}","branches_url":"https://api.github.com/repos/autolab/Tango/branches{/branch}","tags_url":"https://api.github.com/repos/autolab/Tango/tags","blobs_url":"https://api.github.com/repos/autolab/Tango/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/autolab/Tango/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/autolab/Tango/git/refs{/sha}","trees_url":"https://api.github.com/repos/autolab/Tango/git/trees{/sha}","statuses_url":"https://api.github.com/repos/autolab/Tango/statuses/{sha}","languages_url":"https://api.github.com/repos/autolab/Tango/languages","stargazers_url":"https://api.github.com/repos/autolab/Tango/stargazers","contributors_url":"https://api.github.com/repos/autolab/Tango/contributors","subscribers_url":"https://api.github.com/repos/autolab/Tango/subscribers","subscription_url":"https://api.github.com/repos/autolab/Tango/subscription","commits_url":"https://api.github.com/repos/autolab/Tango/commits{/sha}","git_commits_url":"https://api.github.com/repos/autolab/Tango/git/commits{/sha}","comments_url":"https://api.github.com/repos/autolab/Tango/comments{/number}","issue_comment_url":"https://api.github.com/repos/autolab/Tango/issues/comments{/number}","contents_url":"https://api.github.com/repos/autolab/Tango/contents/{+path}","compare_url":"https://api.github.com/repos/autolab/Tango/compare/{base}...{head}","merges_url":"https://api.github.com/repos/autolab/Tango/merges","archive_url":"https://api.github.com/repos/autolab/Tango/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/autolab/Tango/downloads","issues_url":"https://api.github.com/repos/autolab/Tango/issues{/number}","pulls_url":"https://api.github.com/repos/autolab/Tango/pulls{/number}","milestones_url":"https://api.github.com/repos/autolab/Tango/milestones{/number}","notifications_url":"https://api.github.com/repos/autolab/Tango/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/autolab/Tango/labels{/name}","releases_url":"https://api.github.com/repos/autolab/Tango/releases{/id}","created_at":"2014-10-26T20:29:15Z","updated_at":"2015-03-19T16:40:36Z","pushed_at":"2015-03-19T16:40:40Z","git_url":"git://github.com/autolab/Tango.git","ssh_url":"git@github.com:autolab/Tango.git","clone_url":"https://github.com/autolab/Tango.git","svn_url":"https://github.com/autolab/Tango","homepage":"http://www.autolabproject.com/","size":3673,"stargazers_count":4,"watchers_count":4,"language":"Python","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":2,"mirror_url":null,"open_issues_count":14,"forks":2,"open_issues":14,"watchers":4,"default_branch":"master"}},"base":{"label":"autolab:master","ref":"master","sha":"c3f61db0de0c682c9b337e0f38a36e910718a23c","user":{"login":"autolab","id":2337016,"avatar_url":"https://avatars.githubusercontent.com/u/2337016?v=3","gravatar_id":"","url":"https://api.github.com/users/autolab","html_url":"https://github.com/autolab","followers_url":"https://api.github.com/users/autolab/followers","following_url":"https://api.github.com/users/autolab/following{/other_user}","gists_url":"https://api.github.com/users/autolab/gists{/gist_id}","starred_url":"https://api.github.com/users/autolab/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/autolab/subscriptions","organizations_url":"https://api.github.com/users/autolab/orgs","repos_url":"https://api.github.com/users/autolab/repos","events_url":"https://api.github.com/users/autolab/events{/privacy}","received_events_url":"https://api.github.com/users/autolab/received_events","type":"Organization","site_admin":false},"repo":{"id":25789674,"name":"Tango","full_name":"autolab/Tango","owner":{"login":"autolab","id":2337016,"avatar_url":"https://avatars.githubusercontent.com/u/2337016?v=3","gravatar_id":"","url":"https://api.github.com/users/autolab","html_url":"https://github.com/autolab","followers_url":"https://api.github.com/users/autolab/followers","following_url":"https://api.github.com/users/autolab/following{/other_user}","gists_url":"https://api.github.com/users/autolab/gists{/gist_id}","starred_url":"https://api.github.com/users/autolab/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/autolab/subscriptions","organizations_url":"https://api.github.com/users/autolab/orgs","repos_url":"https://api.github.com/users/autolab/repos","events_url":"https://api.github.com/users/autolab/events{/privacy}","received_events_url":"https://api.github.com/users/autolab/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/autolab/Tango","description":"External standalone, RESTful job service","fork":false,"url":"https://api.github.com/repos/autolab/Tango","forks_url":"https://api.github.com/repos/autolab/Tango/forks","keys_url":"https://api.github.com/repos/autolab/Tango/keys{/key_id}","collaborators_url":"https://api.github.com/repos/autolab/Tango/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/autolab/Tango/teams","hooks_url":"https://api.github.com/repos/autolab/Tango/hooks","issue_events_url":"https://api.github.com/repos/autolab/Tango/issues/events{/number}","events_url":"https://api.github.com/repos/autolab/Tango/events","assignees_url":"https://api.github.com/repos/autolab/Tango/assignees{/user}","branches_url":"https://api.github.com/repos/autolab/Tango/branches{/branch}","tags_url":"https://api.github.com/repos/autolab/Tango/tags","blobs_url":"https://api.github.com/repos/autolab/Tango/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/autolab/Tango/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/autolab/Tango/git/refs{/sha}","trees_url":"https://api.github.com/repos/autolab/Tango/git/trees{/sha}","statuses_url":"https://api.github.com/repos/autolab/Tango/statuses/{sha}","languages_url":"https://api.github.com/repos/autolab/Tango/languages","stargazers_url":"https://api.github.com/repos/autolab/Tango/stargazers","contributors_url":"https://api.github.com/repos/autolab/Tango/contributors","subscribers_url":"https://api.github.com/repos/autolab/Tango/subscribers","subscription_url":"https://api.github.com/repos/autolab/Tango/subscription","commits_url":"https://api.github.com/repos/autolab/Tango/commits{/sha}","git_commits_url":"https://api.github.com/repos/autolab/Tango/git/commits{/sha}","comments_url":"https://api.github.com/repos/autolab/Tango/comments{/number}","issue_comment_url":"https://api.github.com/repos/autolab/Tango/issues/comments{/number}","contents_url":"https://api.github.com/repos/autolab/Tango/contents/{+path}","compare_url":"https://api.github.com/repos/autolab/Tango/compare/{base}...{head}","merges_url":"https://api.github.com/repos/autolab/Tango/merges","archive_url":"https://api.github.com/repos/autolab/Tango/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/autolab/Tango/downloads","issues_url":"https://api.github.com/repos/autolab/Tango/issues{/number}","pulls_url":"https://api.github.com/repos/autolab/Tango/pulls{/number}","milestones_url":"https://api.github.com/repos/autolab/Tango/milestones{/number}","notifications_url":"https://api.github.com/repos/autolab/Tango/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/autolab/Tango/labels{/name}","releases_url":"https://api.github.com/repos/autolab/Tango/releases{/id}","created_at":"2014-10-26T20:29:15Z","updated_at":"2015-03-19T16:40:36Z","pushed_at":"2015-03-19T16:40:40Z","git_url":"git://github.com/autolab/Tango.git","ssh_url":"git@github.com:autolab/Tango.git","clone_url":"https://github.com/autolab/Tango.git","svn_url":"https://github.com/autolab/Tango","homepage":"http://www.autolabproject.com/","size":3673,"stargazers_count":4,"watchers_count":4,"language":"Python","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":2,"mirror_url":null,"open_issues_count":14,"forks":2,"open_issues":14,"watchers":4,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/autolab/Tango/pulls/45"},"html":{"href":"https://github.com/autolab/Tango/pull/45"},"issue":{"href":"https://api.github.com/repos/autolab/Tango/issues/45"},"comments":{"href":"https://api.github.com/repos/autolab/Tango/issues/45/comments"},"review_comments":{"href":"https://api.github.com/repos/autolab/Tango/pulls/45/comments"},"review_comment":{"href":"https://api.github.com/repos/autolab/Tango/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/autolab/Tango/pulls/45/commits"},"statuses":{"href":"https://api.github.com/repos/autolab/Tango/statuses/2fbc989cb231af9972525852f3ed2fe8cb5e54de"}}}}