GitHub PullRequestReviewCommentEvent at 2014-11-07 21:26:50
Repository: autotest-docker
Actor: cevich
Payload: {"action":"created","comment":{"url":"https://api.github.com/repos/autotest/autotest-docker/pulls/comments/20038643","id":20038643,"diff_hunk":"@@ -0,0 +1,548 @@\n+\"\"\"\n+Summary\n+-------\n+\n+This test uses the ``--ipc`` (System V shared memory segment) sharing feature\n+of the ``docker``. It creates segment and changes the value from multiple\n+places simultaneously on variuos setups.\n+\n+Prerequisites\n+-------------\n+\n+Host and containers have to have ``libc`` and ``librt`` libraries installed\n+or you might drop those libraries in $SRCDIR/src/{libc.so.x86_64,libc.so.i686,\n+librt.so.x86_64, ./librt.so.i686} and they'll get shared between host/guest.\n+\n+Operational Summary\n+-------------------\n+\n+1. Execute multiple workers from host and container according to setup\n+2. Wait until they finish/fail and analyze the output\n+3. Cleanup\n+\n+\"\"\"\n+import os\n+import random\n+import shutil\n+import time\n+\n+from autotest.client.shared import utils\n+from dockertest import subtest, xceptions, config\n+from dockertest.containers import DockerContainers\n+from dockertest.dockercmd import AsyncDockerCmd, DockerCmd\n+from dockertest.images import DockerImage\n+from dockertest.output import OutputGood\n+\n+\n+class InteractiveAsyncDockerCmd(AsyncDockerCmd):\n+\n+    \"\"\"\n+    Execute docker command as asynchronous background process on ``execute()``\n+    with PIPE as stdin and allows use of stdin(data) to interact with process.\n+    \"\"\"\n+\n+    def __init__(self, subbase, subcmd, subargs=None, timeout=None,\n+                 verbose=True):\n+        super(InteractiveAsyncDockerCmd, self).__init__(subbase, subcmd,\n+                                                        subargs, timeout,\n+                                                        verbose)\n+        self._stdin = None\n+        self._stdout_idx = 0\n+\n+    def execute(self, stdin=None):\n+        \"\"\"\n+        Start execution of asynchronous docker command\n+        \"\"\"\n+        ps_stdin, self._stdin = os.pipe()\n+        ret = super(InteractiveAsyncDockerCmd, self).execute(ps_stdin)\n+        os.close(ps_stdin)\n+        if stdin:\n+            for line in stdin.splitlines(True):\n+                self.stdin(line)\n+        return ret\n+\n+    def stdin(self, data):\n+        \"\"\"\n+        Sends data to stdin (partial send is possible!)\n+        :param data: Data to be send\n+        :return: Number of written data\n+        \"\"\"\n+        return os.write(self._stdin, data)\n+\n+    def close(self):\n+        \"\"\"\n+        Close the pipes (when opened)\n+        \"\"\"\n+        if self._stdin:\n+            os.close(self._stdin)\n+            self._stdin = None\n+\n+    def __del__(self):\n+        \"\"\" In case someone forget to run self.close()... \"\"\"\n+        self.close()\n+\n+\n+class Worker(object):\n+\n+    \"\"\"\n+    Abstraction to work with DockerCmd and BgJob the same way with some\n+    additional features like expected results handling and wait_for_start\n+    \"\"\"\n+\n+    def __init__(self, name, cmd, err=None, cname=None):\n+        \"\"\"\n+        :param name: User friendly name of the worker\n+        :param cmd: Command\n+        :param err: Expected error (string or bool)\n+        :param cname: Container name (only for DockerCmd to map name2cname)\n+        \"\"\"\n+        self.name = name\n+        self.cmd = cmd\n+        self.err = err\n+        self.cname = cname\n+        self.is_bgjob = isinstance(self.cmd, utils.BgJob)\n+\n+    def __str__(self):\n+        \"\"\" Workaround BgJob outdated results \"\"\"\n+        if self.is_bgjob:\n+            cmd = (\"Command %s\\nStdout:\\n%s\\nStderr:\\n%s\\nExit code: %s\"\n+                   % (self.cmd.command, self.cmd.get_stdout(),\n+                      self.cmd.get_stderr(), self.cmd.sp.poll()))\n+        else:\n+            cmd = self.cmd\n+        return \"%s:\\nExpected error: %s\\n%s\" % (self.name, self.err, cmd)\n+\n+    def output(self):\n+        \"\"\" Get output + stderr \"\"\"\n+        if self.is_bgjob:\n+            return self.cmd.get_stdout() + self.cmd.get_stderr()\n+        else:\n+            return self.cmd.stdout + self.cmd.stderr\n+\n+    def done(self):\n+        \"\"\" Check if process finished \"\"\"\n+        if self.is_bgjob:\n+            return self.cmd.sp.poll() is not None\n+        else:\n+            if isinstance(self.cmd, InteractiveAsyncDockerCmd):\n+                try:    # Workaround unclosed pipe docker bug...\n+                    self.cmd.stdin(\"\\n\")\n+                except OSError:     # Already closed...\n+                    pass\n+            return self.cmd.done\n+\n+    @staticmethod\n+    def failif(condition, reason):\n+        \"\"\" Raise DockerTestFail if $condition is nonzero \"\"\"\n+        if condition:\n+            raise xceptions.DockerTestFail(reason)\n+\n+    def verify_started(self, timeout=10):\n+        \"\"\"\n+        Wait until special message occurs.\n+        A) Silent 0th iteration for non-error cases\n+        B) Exit or err string in the output\n+        \"\"\"\n+        end_time = time.time() + timeout\n+        while time.time() < end_time:\n+            if self.err:    # Looking for error\n+                if self.done():\n+                    break\n+                elif (isinstance(self.err, basestring)\n+                      and self.err in self.output()):\n+                    break\n+            else:       # Looking for 0th iteration\n+                done = self.done()\n+                if \"Silent 0th iteration\" in self.output():\n+                    break\n+                self.failif(done, \"Worker finished before 'Silent 0th \"\n+                            \"iteration'message occurred in output\\n%s\" % self)\n+        else:\n+            self.failif(True, \"Worker %s didn't started in %ss\\n%s\"\n+                        % (self.name, timeout, self))\n+\n+    def wait_check(self, timeout):\n+        \"\"\" Wait for finish and check the output for errors/expected errors \"\"\"\n+        if not self.err:\n+            self._wait_check_good(timeout)\n+        else:\n+            self._wait_check_bad(timeout)\n+\n+    def _wait_check_good(self, timeout):\n+        \"\"\"\n+        Check 'All iterations passed' string is in the output and no errors\n+        occurred.\n+        \"\"\"\n+        res = self.wait(timeout)\n+        if not self.is_bgjob:     # DockerCmd, check for other failures\n+            OutputGood(res, skip='error_check')\n+        self.failif(res.exit_status, \"Worker %s failed\\n%s\" % (self.name, res))\n+        self.failif(\"All iterations passed\" not in res.stdout, \"Worker %s \"\n+                    \"passed but 'All iterations passed' not in the output:\\n\"\n+                    \"%s\" % (self.name, res))\n+        return res\n+\n+    def _wait_check_bad(self, timeout):\n+        \"\"\"\n+        Check the 'All iterations passed' message is not present in the output\n+        and if self.err is string check it's there. Othervise just expect\n+        exit_status != 0.\n+        \"\"\"\n+        complete = lambda: self.done() or self.err in self.output()\n+        utils.wait_for(complete, timeout)\n+        if self.done():   # Process finished, it's safe to wait_for\n+            res = self.wait(0)\n+        elif self.is_bgjob:\n+            # BgJob updates results only on wait_for but the process is still\n+            # running.\n+            res = self.cmd.result\n+            res.stdout = self.output()\n+        else:   # DockerCmd.cmdresult is updated automatically\n+            res = self.cmd.cmdresult\n+        output = res.stdout + res.stderr\n+        if isinstance(self.err, basestring) and self.err not in output:\n+            msg = (\"Worker %s doesn't have '%s' message in the output:\\n%s\"\n+                   % (self.name, self.err, res))\n+            raise xceptions.DockerTestFail(msg)\n+        elif res.exit_status == 0:\n+            msg = (\"Worker %s was suppose to fail but passed instead:\\n%s\"\n+                   % (self.name, res))\n+            raise xceptions.DockerTestFail(msg)\n+        self.failif('All iterations passed' in output, \"Worker %s contains \"\n+                    \"'All iterations passed' message in output although it \"\n+                    \"should have failed...:\\n%s\" % (self.name, res))\n+        if not self.done():\n+            self.wait(0)    # Kill the bastard in case it's still running\n+\n+    def wait(self, timeout):\n+        \"\"\" Wait for the command to finish or kill it if timeout expires \"\"\"\n+        if self.is_bgjob:\n+            return self.cmd.wait_for(timeout)\n+        else:\n+            return self.cmd.wait(timeout)\n+\n+\n+class run_ipc(subtest.SubSubtestCallerSimultaneous):\n+\n+    \"\"\" SubSubtest caller \"\"\"\n+\n+    def _copy_lib(self, library, paths):\n+        \"\"\"\n+        Try to find at least one matching library and copy it into srcdir\n+        :param library: list of matching library names (last one shortest)\n+        :param paths: LD_LIBRARY_PATH as list\n+        :raise DockerTestNAError: When library not found (at least one math)\n+        \"\"\"\n+        def find_and_copy(name, path, dst, srcdir):\n+            \"\"\" If not already copied and file exists, copy it \"\"\"\n+            if dst is not None:\n+                src = os.path.join(path, name)\n+                if os.path.exists(src):\n+                    shutil.copy(src, os.path.join(srcdir, dst))\n+                    return  # Handled, remove dst\n+            return dst  # Not handled, return dst\n+\n+        dst32 = \"%s.i686\" % library[-1]\n+        dst64 = \"%s.x86_64\" % library[-1]\n+        for name in library:\n+            for path in paths:\n+                if path.rstrip('/').endswith('64'):\n+                    dst64 = find_and_copy(name, path, dst64, self.srcdir)\n+                else:\n+                    dst32 = find_and_copy(name, path, dst32, self.srcdir)\n+                if not dst32 and not dst64:\n+                    return  # We are done\n+        if dst32:\n+            self.logwarning(\"32bit %s not found in %s, using stock one (\"\n+                            \"possibly incompatible with your system)\"\n+                            % (library, paths))\n+            dst32 = find_and_copy(dst32, os.path.join(self.bindir, 'src'),\n+                                  dst32, self.srcdir)\n+        if dst64:\n+            self.logwarning(\"64bit %s not found in %s, using stock one (\"\n+                            \"possibly incompatible with your system)\"\n+                            % (library, paths))\n+            dst64 = find_and_copy(dst64, os.path.join(self.bindir, 'src'),\n+                                  dst64, self.srcdir)\n+        if dst32 and dst64:\n+            raise xceptions.DockerTestNAError(\"Library %s not found in %s\"\n+                                              % (library, paths))\n+\n+    def setup(self):\n+        \"\"\"\n+        Copy files which will be shared as volume in containers\n+        1) Find libs libraries and copy them into self.srcdir\n+        2) Copy shm_ping_pong testing script also to self.srcdir\n+        \"\"\"\n+        # symlink libraries used in volumes\n+        libs = ((\"libc.so.6\", \"libc.so\"), (\"librt.so.1\", \"librt.so\"))\n+        paths = os.environ.get('LD_LIBRARY_PATH', [])\n+        if paths:\n+            paths = paths.split(':')\n+        paths.extend((\"/lib\", \"/usr/lib\", \"/usr/local/lib\", \"/lib64\",\n+                      \"/usr/lib64\", \"/usr/local/lib64\"))\n+        paths = []\n+        for lib in libs:\n+            self._copy_lib(lib, paths)\n+        # copy shm test program\n+        shutil.copy(os.path.join(self.bindir, 'src', 'shm_ping_pong.py'),\n+                    self.srcdir)\n+\n+\n+class ipc_base(subtest.SubSubtest):\n+\n+    \"\"\" Base class \"\"\"\n+\n+    def initialize(self):\n+        \"\"\"\n+        Runs one container\n+        \"\"\"\n+        super(ipc_base, self).initialize()\n+        # Substuff\n+        config.none_if_empty(self.config)\n+        self.sub_stuff['dc'] = DockerContainers(self)\n+        self.sub_stuff['containers'] = []\n+        self.sub_stuff['cmds'] = []\n+        self.sub_stuff['shms'] = []\n+        default_image = DockerImage.full_name_from_defaults(self.config)\n+        self.sub_stuff['default_image'] = default_image\n+\n+    @staticmethod\n+    def _generate_string(min_length=0, max_length=1023):\n+        \"\"\"\n+        Generate random string in range of <min-max> length. For safeness\n+        prepend unique char to prevent partial match while shm is being\n+        written. (max length of shm_ping_pong segment is 1024)\n+        \"\"\"\n+        return utils.generate_random_string(random.randint(min_length,\n+                                                           max_length))\n+\n+    @staticmethod\n+    def _find_hosts_free_ipc(start_key):\n+        \"\"\" Try to find first usable shm in 1024 iterations \"\"\"\n+        for key in xrange(start_key, start_key + 1024):\n+            if 'not found' in utils.run(\"LC_ALL=C ipcs -m -i %s\" % key,\n+                                        10, verbose=False).stderr:\n+                return key\n+        raise xceptions.DockerTestError(\"Unable to find free shmmid in 1024 \"\n+                                        \"steps (starting from %s)\" % start_key)\n+\n+    def _exec_container(self, name, ipc, args, err=None, timeout=10):\n+        \"\"\"\n+        Starts container\n+        :param name: User-friendly name used as reference\n+        :param ipc: --ipc $ipc argument\n+        :param args: shm_ping_pong.py arguments in form of:\n+                     \"$shmid $no_iter $set_str $wait_for_str $cleanup[yn]\"\n+        :param err: Expected error (bool or string)\n+        :param timeout: How long to wait until it's started\n+        \"\"\"\n+        subargs = self.config.get('run_options_csv')\n+        if subargs is None:\n+            subargs = []\n+        else:\n+            subargs = subargs.split(',')\n+        subargs.append(\"--volume %s:/opt/ipc:rz\" % self.parent_subtest.srcdir)\n+        if ipc:\n+            subargs.append(\"--ipc %s\" % ipc)\n+        cname = self.sub_stuff['dc'].get_unique_name(name)\n+        self.sub_stuff['containers'].append(cname)\n+        subargs.append(\"--name %s\" % cname)\n+        subargs.append(self.sub_stuff['default_image'])\n+        if self.config.get('workaround_first_access'):\n+            # Warning: This might influence negative tests!\n+            subargs.append(\"sh -c 'cd /opt/ipc; python shm_ping_pong.py %s || \"\n+                           \"(ipcrm -M %s && python shm_ping_pong.py %s); \"\n+                           \"exit $?'\"\n+                           % (args, args.split(' ')[0], args))\n+        else:\n+            subargs.append(\"sh -c 'cd /opt/ipc; python shm_ping_pong.py %s; \"\n+                           \"exit $?'\" % args)\n+        dkrcmd = AsyncDockerCmd(self, 'run', subargs, verbose=False)\n+        worker = Worker(name, dkrcmd, err, cname)\n+        self.sub_stuff['cmds'].append(worker)\n+        dkrcmd.execute()\n+        worker.verify_started(timeout)\n+        return cname\n+\n+    '''\n+    def _exec_container_stdin(self, name, ipc, args, err=None, timeout=10):\n+        \"\"\"\n+        Starts container\n+        :param ipc: --ipc $ipc argument\n+        :param args: shm_ping_pong.py arguments in form of:\n+                     \"$shmid $no_iter $set_str $wait_for_str $cleanup[yn]\"\n+        \"\"\"\n+        subargs = self.config.get('run_options_csv')\n+        if subargs is None:\n+            subargs = []\n+        else:\n+            subargs = subargs.split(',')\n+        subargs.append(\"--volume %s:/opt/ipc:rz\" % self.parent_subtest.srcdir)\n+        if ipc:\n+            subargs.append(\"--ipc %s\" % ipc)\n+        cname = self.sub_stuff['dc'].get_unique_name(name)\n+        self.sub_stuff['containers'].append(cname)\n+        subargs.append(\"--name %s\" % cname)\n+        subargs.append(self.sub_stuff['default_image'])\n+        subargs.append(\"sh\")\n+        dkrcmd = InteractiveAsyncDockerCmd(self, 'run', subargs, verbose=False)\n+        worker = Worker(name, dkrcmd, err)\n+        self.sub_stuff['cmds'].append(worker)\n+        dkrcmd.execute(\"cd /opt/ipc; python shm_ping_pong.py %s || \"\n+                       \"(ipcrm -M %s && python shm_ping_pong.py %s); exit $?\\n\"\n+                       % (args, args.split(' ')[0], args))\n+        worker.verify_started(timeout)\n+        return cname\n+    '''     # Custom scenarios will utilize this one pylint: disable=W0105\n+\n+    def _exec_host(self, name, args, err=None, timeout=10):\n+        \"\"\" Execute shm_ping_pong.py $args on host\n+        :param args: shm_ping_pong.py arguments in form of:\n+                     \"$shmid $no_iter $set_str $wait_for_str $cleanup[yn]\"\n+        \"\"\"\n+        cmdline = \"./shm_ping_pong.py %s\" % args\n+        os.chdir(self.parent_subtest.srcdir)\n+        self.logdebug(\"Async-execute: Command: %s\" % cmdline)\n+        cmd = utils.AsyncJob(cmdline, verbose=False)\n+        worker = Worker(name, cmd, err)\n+        self.sub_stuff['cmds'].append(worker)\n+        worker.verify_started(timeout)\n+\n+    def _cleanup_containers(self):\n+        \"\"\" Cleanup the container \"\"\"\n+        for name in set(self.sub_stuff.get('containers', [])):\n+            conts = self.sub_stuff['dc'].list_containers_with_name(name)\n+            if conts == []:\n+                return  # Docker was created, but apparently doesn't exist\n+            elif len(conts) > 1:\n+                msg = (\"Multiple containers matches name %s, not removing any \"\n+                       \"of them...\", name)\n+                raise xceptions.DockerTestError(msg)\n+            DockerCmd(self, 'rm', ['--force', '--volumes', name],\n+                      verbose=False).execute()\n+\n+    def _cleanup_shm(self, key):\n+        \"\"\" Remove shmkey from host \"\"\"\n+        cmd = \"ipcrm -M %s\" % key\n+        self.logdebug(\"Running: Command: %s\" % cmd)\n+        utils.run(cmd, ignore_status=True, verbose=False)\n+        self.sub_stuff['cmds'].pop(-1).wait(60)\n+\n+    def _cleanup_shms(self):\n+        \"\"\" Unregister and remove the shm segment (using host) \"\"\"\n+        for key in set(self.sub_stuff.get('shms', [])):\n+            self._cleanup_shm(key)\n+\n+    def cleanup(self):\n+        \"\"\"\n+        Log uncleanded cmds (useful for debuging) and cleanup all containers,\n+        shms and workers.\n+        \"\"\"\n+        super(ipc_base, self).cleanup()\n+        # Log uncleaned workers/shms (to get better overview on error)\n+        if self.sub_stuff.get('cmds'):\n+            self.logwarning(\"Uncleaned workers:\")\n+            for cmd in self.sub_stuff['cmds']:\n+                self.logwarning(cmd)\n+        if self.sub_stuff.get('shms'):\n+            self.logerror('Uncleaned IPCs: %s' % self.sub_stuff.get('shms'))\n+        # Cleanup\n+        self._cleanup_containers()\n+        self._cleanup_shms()","path":"subtests/docker_cli/run_ipc/run_ipc.py","position":452,"original_position":452,"commit_id":"b944fc25ca893e36b1eb4e254c09831278b00ddf","original_commit_id":"b944fc25ca893e36b1eb4e254c09831278b00ddf","user":{"login":"cevich","id":1183438,"avatar_url":"https://avatars.githubusercontent.com/u/1183438?v=3","gravatar_id":"","url":"https://api.github.com/users/cevich","html_url":"https://github.com/cevich","followers_url":"https://api.github.com/users/cevich/followers","following_url":"https://api.github.com/users/cevich/following{/other_user}","gists_url":"https://api.github.com/users/cevich/gists{/gist_id}","starred_url":"https://api.github.com/users/cevich/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/cevich/subscriptions","organizations_url":"https://api.github.com/users/cevich/orgs","repos_url":"https://api.github.com/users/cevich/repos","events_url":"https://api.github.com/users/cevich/events{/privacy}","received_events_url":"https://api.github.com/users/cevich/received_events","type":"User","site_admin":false},"body":"These seem to have the biggest potential to cause harm if leaked, could they be cleaned first, then the other cleanup happen?  Or maybe some nested try...finally...'s could help?","created_at":"2014-11-07T21:26:50Z","updated_at":"2014-11-07T21:26:50Z","html_url":"https://github.com/autotest/autotest-docker/pull/401#discussion_r20038643","pull_request_url":"https://api.github.com/repos/autotest/autotest-docker/pulls/401","_links":{"self":{"href":"https://api.github.com/repos/autotest/autotest-docker/pulls/comments/20038643"},"html":{"href":"https://github.com/autotest/autotest-docker/pull/401#discussion_r20038643"},"pull_request":{"href":"https://api.github.com/repos/autotest/autotest-docker/pulls/401"}}},"pull_request":{"url":"https://api.github.com/repos/autotest/autotest-docker/pulls/401","id":24051866,"html_url":"https://github.com/autotest/autotest-docker/pull/401","diff_url":"https://github.com/autotest/autotest-docker/pull/401.diff","patch_url":"https://github.com/autotest/autotest-docker/pull/401.patch","issue_url":"https://api.github.com/repos/autotest/autotest-docker/issues/401","number":401,"state":"open","locked":false,"title":"subtests.docker_cli: Add run_ipc test","user":{"login":"ldoktor","id":1074021,"avatar_url":"https://avatars.githubusercontent.com/u/1074021?v=3","gravatar_id":"","url":"https://api.github.com/users/ldoktor","html_url":"https://github.com/ldoktor","followers_url":"https://api.github.com/users/ldoktor/followers","following_url":"https://api.github.com/users/ldoktor/following{/other_user}","gists_url":"https://api.github.com/users/ldoktor/gists{/gist_id}","starred_url":"https://api.github.com/users/ldoktor/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ldoktor/subscriptions","organizations_url":"https://api.github.com/users/ldoktor/orgs","repos_url":"https://api.github.com/users/ldoktor/repos","events_url":"https://api.github.com/users/ldoktor/events{/privacy}","received_events_url":"https://api.github.com/users/ldoktor/received_events","type":"User","site_admin":false},"body":"ISSUE: https://github.com/autotest/autotest-docker/issues/371#issuecomment-61664433\r\nBUGS: _TBD (There are couple of bugs I haven't yet filled in BZ, please reveiw it and in the mean time I'll add the links); due of one bug the `host_hcont_c1cont` test fails, other tests should pass._\r\n\r\nThis test utilizes the '--ipc' feature of docker and uses SysV shm\r\nto share data between various setups (host and guests).\r\n\r\nSigned-off-by: Lukáš Doktor <ldoktor@redhat.com>\r\n\r\n__NOTE__: I set the `no_iteration` to quite high number, you might need to decrease it on some older HW as the tests runs in parallel...\r\n__NOTE2__: I'll implement the remaining `TODO:` tests as special tests later, this is only the basic functionality...","created_at":"2014-11-07T13:13:05Z","updated_at":"2014-11-07T21:26:50Z","closed_at":null,"merged_at":null,"merge_commit_sha":"1976ddc516b9e41e860b4fa787d77659ac0dae35","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/autotest/autotest-docker/pulls/401/commits","review_comments_url":"https://api.github.com/repos/autotest/autotest-docker/pulls/401/comments","review_comment_url":"https://api.github.com/repos/autotest/autotest-docker/pulls/comments/{number}","comments_url":"https://api.github.com/repos/autotest/autotest-docker/issues/401/comments","statuses_url":"https://api.github.com/repos/autotest/autotest-docker/statuses/b944fc25ca893e36b1eb4e254c09831278b00ddf","head":{"label":"ldoktor:ipc","ref":"ipc","sha":"b944fc25ca893e36b1eb4e254c09831278b00ddf","user":{"login":"ldoktor","id":1074021,"avatar_url":"https://avatars.githubusercontent.com/u/1074021?v=3","gravatar_id":"","url":"https://api.github.com/users/ldoktor","html_url":"https://github.com/ldoktor","followers_url":"https://api.github.com/users/ldoktor/followers","following_url":"https://api.github.com/users/ldoktor/following{/other_user}","gists_url":"https://api.github.com/users/ldoktor/gists{/gist_id}","starred_url":"https://api.github.com/users/ldoktor/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ldoktor/subscriptions","organizations_url":"https://api.github.com/users/ldoktor/orgs","repos_url":"https://api.github.com/users/ldoktor/repos","events_url":"https://api.github.com/users/ldoktor/events{/privacy}","received_events_url":"https://api.github.com/users/ldoktor/received_events","type":"User","site_admin":false},"repo":{"id":17896906,"name":"autotest-docker","full_name":"ldoktor/autotest-docker","owner":{"login":"ldoktor","id":1074021,"avatar_url":"https://avatars.githubusercontent.com/u/1074021?v=3","gravatar_id":"","url":"https://api.github.com/users/ldoktor","html_url":"https://github.com/ldoktor","followers_url":"https://api.github.com/users/ldoktor/followers","following_url":"https://api.github.com/users/ldoktor/following{/other_user}","gists_url":"https://api.github.com/users/ldoktor/gists{/gist_id}","starred_url":"https://api.github.com/users/ldoktor/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ldoktor/subscriptions","organizations_url":"https://api.github.com/users/ldoktor/orgs","repos_url":"https://api.github.com/users/ldoktor/repos","events_url":"https://api.github.com/users/ldoktor/events{/privacy}","received_events_url":"https://api.github.com/users/ldoktor/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/ldoktor/autotest-docker","description":"Autotest client test containing functional & integration subtests for the Docker project","fork":true,"url":"https://api.github.com/repos/ldoktor/autotest-docker","forks_url":"https://api.github.com/repos/ldoktor/autotest-docker/forks","keys_url":"https://api.github.com/repos/ldoktor/autotest-docker/keys{/key_id}","collaborators_url":"https://api.github.com/repos/ldoktor/autotest-docker/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/ldoktor/autotest-docker/teams","hooks_url":"https://api.github.com/repos/ldoktor/autotest-docker/hooks","issue_events_url":"https://api.github.com/repos/ldoktor/autotest-docker/issues/events{/number}","events_url":"https://api.github.com/repos/ldoktor/autotest-docker/events","assignees_url":"https://api.github.com/repos/ldoktor/autotest-docker/assignees{/user}","branches_url":"https://api.github.com/repos/ldoktor/autotest-docker/branches{/branch}","tags_url":"https://api.github.com/repos/ldoktor/autotest-docker/tags","blobs_url":"https://api.github.com/repos/ldoktor/autotest-docker/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/ldoktor/autotest-docker/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/ldoktor/autotest-docker/git/refs{/sha}","trees_url":"https://api.github.com/repos/ldoktor/autotest-docker/git/trees{/sha}","statuses_url":"https://api.github.com/repos/ldoktor/autotest-docker/statuses/{sha}","languages_url":"https://api.github.com/repos/ldoktor/autotest-docker/languages","stargazers_url":"https://api.github.com/repos/ldoktor/autotest-docker/stargazers","contributors_url":"https://api.github.com/repos/ldoktor/autotest-docker/contributors","subscribers_url":"https://api.github.com/repos/ldoktor/autotest-docker/subscribers","subscription_url":"https://api.github.com/repos/ldoktor/autotest-docker/subscription","commits_url":"https://api.github.com/repos/ldoktor/autotest-docker/commits{/sha}","git_commits_url":"https://api.github.com/repos/ldoktor/autotest-docker/git/commits{/sha}","comments_url":"https://api.github.com/repos/ldoktor/autotest-docker/comments{/number}","issue_comment_url":"https://api.github.com/repos/ldoktor/autotest-docker/issues/comments/{number}","contents_url":"https://api.github.com/repos/ldoktor/autotest-docker/contents/{+path}","compare_url":"https://api.github.com/repos/ldoktor/autotest-docker/compare/{base}...{head}","merges_url":"https://api.github.com/repos/ldoktor/autotest-docker/merges","archive_url":"https://api.github.com/repos/ldoktor/autotest-docker/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/ldoktor/autotest-docker/downloads","issues_url":"https://api.github.com/repos/ldoktor/autotest-docker/issues{/number}","pulls_url":"https://api.github.com/repos/ldoktor/autotest-docker/pulls{/number}","milestones_url":"https://api.github.com/repos/ldoktor/autotest-docker/milestones{/number}","notifications_url":"https://api.github.com/repos/ldoktor/autotest-docker/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/ldoktor/autotest-docker/labels{/name}","releases_url":"https://api.github.com/repos/ldoktor/autotest-docker/releases{/id}","created_at":"2014-03-19T08:31:12Z","updated_at":"2014-07-07T06:48:22Z","pushed_at":"2014-11-07T13:28:17Z","git_url":"git://github.com/ldoktor/autotest-docker.git","ssh_url":"git@github.com:ldoktor/autotest-docker.git","clone_url":"https://github.com/ldoktor/autotest-docker.git","svn_url":"https://github.com/ldoktor/autotest-docker","homepage":null,"size":3517,"stargazers_count":0,"watchers_count":0,"language":"Python","has_issues":false,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":1,"forks":0,"open_issues":1,"watchers":0,"default_branch":"master"}},"base":{"label":"autotest:master","ref":"master","sha":"9fd372d5a6e9b520bd8b7f2f3546bce31052b629","user":{"login":"autotest","id":472856,"avatar_url":"https://avatars.githubusercontent.com/u/472856?v=3","gravatar_id":"","url":"https://api.github.com/users/autotest","html_url":"https://github.com/autotest","followers_url":"https://api.github.com/users/autotest/followers","following_url":"https://api.github.com/users/autotest/following{/other_user}","gists_url":"https://api.github.com/users/autotest/gists{/gist_id}","starred_url":"https://api.github.com/users/autotest/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/autotest/subscriptions","organizations_url":"https://api.github.com/users/autotest/orgs","repos_url":"https://api.github.com/users/autotest/repos","events_url":"https://api.github.com/users/autotest/events{/privacy}","received_events_url":"https://api.github.com/users/autotest/received_events","type":"Organization","site_admin":false},"repo":{"id":17869790,"name":"autotest-docker","full_name":"autotest/autotest-docker","owner":{"login":"autotest","id":472856,"avatar_url":"https://avatars.githubusercontent.com/u/472856?v=3","gravatar_id":"","url":"https://api.github.com/users/autotest","html_url":"https://github.com/autotest","followers_url":"https://api.github.com/users/autotest/followers","following_url":"https://api.github.com/users/autotest/following{/other_user}","gists_url":"https://api.github.com/users/autotest/gists{/gist_id}","starred_url":"https://api.github.com/users/autotest/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/autotest/subscriptions","organizations_url":"https://api.github.com/users/autotest/orgs","repos_url":"https://api.github.com/users/autotest/repos","events_url":"https://api.github.com/users/autotest/events{/privacy}","received_events_url":"https://api.github.com/users/autotest/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/autotest/autotest-docker","description":"Autotest client test containing functional & integration subtests for the Docker project","fork":false,"url":"https://api.github.com/repos/autotest/autotest-docker","forks_url":"https://api.github.com/repos/autotest/autotest-docker/forks","keys_url":"https://api.github.com/repos/autotest/autotest-docker/keys{/key_id}","collaborators_url":"https://api.github.com/repos/autotest/autotest-docker/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/autotest/autotest-docker/teams","hooks_url":"https://api.github.com/repos/autotest/autotest-docker/hooks","issue_events_url":"https://api.github.com/repos/autotest/autotest-docker/issues/events{/number}","events_url":"https://api.github.com/repos/autotest/autotest-docker/events","assignees_url":"https://api.github.com/repos/autotest/autotest-docker/assignees{/user}","branches_url":"https://api.github.com/repos/autotest/autotest-docker/branches{/branch}","tags_url":"https://api.github.com/repos/autotest/autotest-docker/tags","blobs_url":"https://api.github.com/repos/autotest/autotest-docker/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/autotest/autotest-docker/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/autotest/autotest-docker/git/refs{/sha}","trees_url":"https://api.github.com/repos/autotest/autotest-docker/git/trees{/sha}","statuses_url":"https://api.github.com/repos/autotest/autotest-docker/statuses/{sha}","languages_url":"https://api.github.com/repos/autotest/autotest-docker/languages","stargazers_url":"https://api.github.com/repos/autotest/autotest-docker/stargazers","contributors_url":"https://api.github.com/repos/autotest/autotest-docker/contributors","subscribers_url":"https://api.github.com/repos/autotest/autotest-docker/subscribers","subscription_url":"https://api.github.com/repos/autotest/autotest-docker/subscription","commits_url":"https://api.github.com/repos/autotest/autotest-docker/commits{/sha}","git_commits_url":"https://api.github.com/repos/autotest/autotest-docker/git/commits{/sha}","comments_url":"https://api.github.com/repos/autotest/autotest-docker/comments{/number}","issue_comment_url":"https://api.github.com/repos/autotest/autotest-docker/issues/comments/{number}","contents_url":"https://api.github.com/repos/autotest/autotest-docker/contents/{+path}","compare_url":"https://api.github.com/repos/autotest/autotest-docker/compare/{base}...{head}","merges_url":"https://api.github.com/repos/autotest/autotest-docker/merges","archive_url":"https://api.github.com/repos/autotest/autotest-docker/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/autotest/autotest-docker/downloads","issues_url":"https://api.github.com/repos/autotest/autotest-docker/issues{/number}","pulls_url":"https://api.github.com/repos/autotest/autotest-docker/pulls{/number}","milestones_url":"https://api.github.com/repos/autotest/autotest-docker/milestones{/number}","notifications_url":"https://api.github.com/repos/autotest/autotest-docker/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/autotest/autotest-docker/labels{/name}","releases_url":"https://api.github.com/repos/autotest/autotest-docker/releases{/id}","created_at":"2014-03-18T14:53:27Z","updated_at":"2014-10-01T17:46:48Z","pushed_at":"2014-11-06T20:23:19Z","git_url":"git://github.com/autotest/autotest-docker.git","ssh_url":"git@github.com:autotest/autotest-docker.git","clone_url":"https://github.com/autotest/autotest-docker.git","svn_url":"https://github.com/autotest/autotest-docker","homepage":null,"size":6179,"stargazers_count":3,"watchers_count":3,"language":"Python","has_issues":true,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":11,"mirror_url":null,"open_issues_count":67,"forks":11,"open_issues":67,"watchers":3,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/autotest/autotest-docker/pulls/401"},"html":{"href":"https://github.com/autotest/autotest-docker/pull/401"},"issue":{"href":"https://api.github.com/repos/autotest/autotest-docker/issues/401"},"comments":{"href":"https://api.github.com/repos/autotest/autotest-docker/issues/401/comments"},"review_comments":{"href":"https://api.github.com/repos/autotest/autotest-docker/pulls/401/comments"},"review_comment":{"href":"https://api.github.com/repos/autotest/autotest-docker/pulls/comments/{number}"},"commits":{"href":"https://api.github.com/repos/autotest/autotest-docker/pulls/401/commits"},"statuses":{"href":"https://api.github.com/repos/autotest/autotest-docker/statuses/b944fc25ca893e36b1eb4e254c09831278b00ddf"}}}}