{"action":"created","comment":{"url":"https://api.github.com/repos/yegor256/rultor/pulls/comments/59463589","id":59463589,"diff_hunk":"@@ -0,0 +1,148 @@\n+/**\n+ * Copyright (c) 2009-2016, rultor.com\n+ * All rights reserved.\n+ *\n+ * Redistribution and use in source and binary forms, with or without\n+ * modification, are permitted provided that the following conditions\n+ * are met: 1) Redistributions of source code must retain the above\n+ * copyright notice, this list of conditions and the following\n+ * disclaimer. 2) Redistributions in binary form must reproduce the above\n+ * copyright notice, this list of conditions and the following\n+ * disclaimer in the documentation and/or other materials provided\n+ * with the distribution. 3) Neither the name of the rultor.com nor\n+ * the names of its contributors may be used to endorse or promote\n+ * products derived from this software without specific prior written\n+ * permission.\n+ *\n+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n+ * \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT\n+ * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND\n+ * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL\n+ * THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,\n+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\n+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,\n+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED\n+ * OF THE POSSIBILITY OF SUCH DAMAGE.\n+ */\n+package com.rultor.agents.docker;\n+\n+import com.github.dockerjava.api.DockerClient;\n+import com.github.dockerjava.api.command.CreateContainerResponse;\n+import com.github.dockerjava.api.model.ExposedPort;\n+import com.github.dockerjava.api.model.Ports;\n+import com.github.dockerjava.core.DockerClientBuilder;\n+import com.github.dockerjava.core.command.BuildImageResultCallback;\n+import com.rultor.agents.shells.PfShell;\n+import com.rultor.spi.Profile;\n+import java.io.File;\n+import java.io.IOException;\n+import java.io.StringWriter;\n+import java.util.Collection;\n+import java.util.Collections;\n+import java.util.concurrent.ConcurrentHashMap;\n+import org.codehaus.plexus.util.IOUtil;\n+\n+/**\n+ * Starts a Docker Container containing a Docker daemon and SSHD.\n+ * @author Armin Braun (me@obrown.io)\n+ * @version $Id$\n+ * @since 1.63\n+ */\n+public final class StartsDockerDaemon implements AutoCloseable {\n+\n+    /**\n+     * Profile used.\n+     */\n+    private final transient Profile profile;\n+\n+    /**\n+     * Docker client.\n+     */\n+    private final transient DockerClient client;\n+\n+    /**\n+     * Docker Containers created.\n+     */\n+    private final transient Collection<CreateContainerResponse> containers;\n+\n+    /**\n+     * Ctor.\n+     * @param prof Current Profile\n+     */\n+    public StartsDockerDaemon(final Profile prof) {\n+        this.profile = prof;\n+        this.client = DockerClientBuilder.getInstance().build();\n+        this.containers = Collections.newSetFromMap(\n+            // @checkstyle MagicNumber (1 line)\n+            new ConcurrentHashMap<CreateContainerResponse, Boolean>(1, 0.9f, 1)\n+        );\n+    }\n+\n+    /**\n+     * Sets up a PfShell in which Rultor can function.\n+     * @return PfShell loaded with credentials for new Rultor runner container\n+     * @throws IOException on failure\n+     */\n+    public PfShell shell() throws IOException {\n+        final ExposedPort ssh = ExposedPort.tcp(22);\n+        final Ports ports = new Ports();\n+        ports.bind(ssh, Ports.Binding(null));\n+        final CreateContainerResponse container = this.client\n+            .createContainerCmd(this.build()).withExposedPorts(\n+                ssh\n+            ).withPortBindings(ports).exec();\n+        this.containers.add(container);\n+        this.client.startContainerCmd(container.getId()).exec();\n+        return new PfShell(\n+            this.profile,\n+            this.client.infoCmd().exec().getName(),\n+            this.client.inspectContainerCmd(container.getId())\n+                .exec().getNetworkSettings().getPorts().getBindings().values()\n+                .iterator().next()[0].getHostPort(),\n+            \"root\",\n+            this.key(container)\n+        );\n+    }\n+\n+    @Override\n+    public void close() throws IOException {\n+        for (final CreateContainerResponse container : this.containers) {\n+            this.client.killContainerCmd(container.getId()).exec();\n+            this.client.removeContainerCmd(container.getId()).exec();\n+        }\n+        this.client.close();\n+    }\n+\n+    /**\n+     * Retrieves SSH private key needed to connect to a given container.\n+     * @param container Container from which to get the SSH key\n+     * @return SSH private key\n+     * @throws IOException on failure\n+     */\n+    private String key(final CreateContainerResponse container)\n+        throws IOException {\n+        final StringWriter writer = new StringWriter();\n+        IOUtil.copy(","path":"src/main/java/com/rultor/agents/docker/StartsDockerDaemon.java","position":128,"original_position":128,"commit_id":"c82a067bded230bf30f2b9521b0f68ebc2751b0f","original_commit_id":"c82a067bded230bf30f2b9521b0f68ebc2751b0f","user":{"login":"mkordas","id":5467276,"avatar_url":"https://avatars.githubusercontent.com/u/5467276?v=3","gravatar_id":"","url":"https://api.github.com/users/mkordas","html_url":"https://github.com/mkordas","followers_url":"https://api.github.com/users/mkordas/followers","following_url":"https://api.github.com/users/mkordas/following{/other_user}","gists_url":"https://api.github.com/users/mkordas/gists{/gist_id}","starred_url":"https://api.github.com/users/mkordas/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mkordas/subscriptions","organizations_url":"https://api.github.com/users/mkordas/orgs","repos_url":"https://api.github.com/users/mkordas/repos","events_url":"https://api.github.com/users/mkordas/events{/privacy}","received_events_url":"https://api.github.com/users/mkordas/received_events","type":"User","site_admin":false},"body":"@original-brownbear does it need to be `IOUtil` from plexus?","created_at":"2016-04-12T22:14:29Z","updated_at":"2016-04-12T22:14:29Z","html_url":"https://github.com/yegor256/rultor/pull/1092#discussion_r59463589","pull_request_url":"https://api.github.com/repos/yegor256/rultor/pulls/1092","_links":{"self":{"href":"https://api.github.com/repos/yegor256/rultor/pulls/comments/59463589"},"html":{"href":"https://github.com/yegor256/rultor/pull/1092#discussion_r59463589"},"pull_request":{"href":"https://api.github.com/repos/yegor256/rultor/pulls/1092"}}},"pull_request":{"url":"https://api.github.com/repos/yegor256/rultor/pulls/1092","id":65926067,"html_url":"https://github.com/yegor256/rultor/pull/1092","diff_url":"https://github.com/yegor256/rultor/pull/1092.diff","patch_url":"https://github.com/yegor256/rultor/pull/1092.patch","issue_url":"https://api.github.com/repos/yegor256/rultor/issues/1092","number":1092,"state":"open","locked":false,"title":" #1082 Moved shell tests to a proper Docker container","user":{"login":"original-brownbear","id":6490959,"avatar_url":"https://avatars.githubusercontent.com/u/6490959?v=3","gravatar_id":"","url":"https://api.github.com/users/original-brownbear","html_url":"https://github.com/original-brownbear","followers_url":"https://api.github.com/users/original-brownbear/followers","following_url":"https://api.github.com/users/original-brownbear/following{/other_user}","gists_url":"https://api.github.com/users/original-brownbear/gists{/gist_id}","starred_url":"https://api.github.com/users/original-brownbear/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/original-brownbear/subscriptions","organizations_url":"https://api.github.com/users/original-brownbear/orgs","repos_url":"https://api.github.com/users/original-brownbear/repos","events_url":"https://api.github.com/users/original-brownbear/events{/privacy}","received_events_url":"https://api.github.com/users/original-brownbear/received_events","type":"User","site_admin":false},"body":"#1082 should be resolved by this ...\r\n\r\nThis is what I did:\r\n* Updated Travis configuration to work Docker based tests\r\n * Requiring a Docker daemon loads a different Travis environment, hence the explicit specification of Maven and JVM version now\r\n* Added explanation about the newly available integration tests to the Readme\r\n* Added `docker-java` dependency so we can finally have a clean environment to tests SSH things in\r\n* Using this instead of bash now to setup the Docker related things, I tried getting it to work with Mac + Bash, but to no avail. This is much more portable, plus this code can be used in the on demand ECS provisioning implementation.\r\n * Also removed the need for a hard committed SSH keypair by that ( also nice to be reused when we move building to dynamically provisioned containers, much more secure to to run things this way imo.)\r\n* Created `StartsDockerDaemon` to setup a Docker SSH daemon for the tests, added to production code, since we can continue with the ECS run image there.\r\n* Made `PfShell` public so it could be returned from  `StartsDockerDaemon`\r\n* Improved test run Docker image, to include `bsdmainutils` and properly set up locale (Rultor tests need both)\r\n* Moved SSH based tests to run on Docker ( and only Docker ).\r\n * I get we're minimally losing coverage now on Systems that don't have Docker. **But**, the tests run fine on Travis and they only worked on Linux in the first place. Installing Docker on Linux is trivial => this choice. Plus ... the old tests were kind of crazy ;), they actually ran commands on the machine executing Maven and were absolutely non-portable ...","created_at":"2016-04-10T22:54:22Z","updated_at":"2016-04-12T22:14:29Z","closed_at":null,"merged_at":null,"merge_commit_sha":"f2a7d8a256a99cf1ed3d47823c1add18901ed493","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/yegor256/rultor/pulls/1092/commits","review_comments_url":"https://api.github.com/repos/yegor256/rultor/pulls/1092/comments","review_comment_url":"https://api.github.com/repos/yegor256/rultor/pulls/comments{/number}","comments_url":"https://api.github.com/repos/yegor256/rultor/issues/1092/comments","statuses_url":"https://api.github.com/repos/yegor256/rultor/statuses/c82a067bded230bf30f2b9521b0f68ebc2751b0f","head":{"label":"original-brownbear:1082","ref":"1082","sha":"c82a067bded230bf30f2b9521b0f68ebc2751b0f","user":{"login":"original-brownbear","id":6490959,"avatar_url":"https://avatars.githubusercontent.com/u/6490959?v=3","gravatar_id":"","url":"https://api.github.com/users/original-brownbear","html_url":"https://github.com/original-brownbear","followers_url":"https://api.github.com/users/original-brownbear/followers","following_url":"https://api.github.com/users/original-brownbear/following{/other_user}","gists_url":"https://api.github.com/users/original-brownbear/gists{/gist_id}","starred_url":"https://api.github.com/users/original-brownbear/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/original-brownbear/subscriptions","organizations_url":"https://api.github.com/users/original-brownbear/orgs","repos_url":"https://api.github.com/users/original-brownbear/repos","events_url":"https://api.github.com/users/original-brownbear/events{/privacy}","received_events_url":"https://api.github.com/users/original-brownbear/received_events","type":"User","site_admin":false},"repo":{"id":51169829,"name":"rultor","full_name":"original-brownbear/rultor","owner":{"login":"original-brownbear","id":6490959,"avatar_url":"https://avatars.githubusercontent.com/u/6490959?v=3","gravatar_id":"","url":"https://api.github.com/users/original-brownbear","html_url":"https://github.com/original-brownbear","followers_url":"https://api.github.com/users/original-brownbear/followers","following_url":"https://api.github.com/users/original-brownbear/following{/other_user}","gists_url":"https://api.github.com/users/original-brownbear/gists{/gist_id}","starred_url":"https://api.github.com/users/original-brownbear/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/original-brownbear/subscriptions","organizations_url":"https://api.github.com/users/original-brownbear/orgs","repos_url":"https://api.github.com/users/original-brownbear/repos","events_url":"https://api.github.com/users/original-brownbear/events{/privacy}","received_events_url":"https://api.github.com/users/original-brownbear/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/original-brownbear/rultor","description":"DevOps Team Assistant","fork":true,"url":"https://api.github.com/repos/original-brownbear/rultor","forks_url":"https://api.github.com/repos/original-brownbear/rultor/forks","keys_url":"https://api.github.com/repos/original-brownbear/rultor/keys{/key_id}","collaborators_url":"https://api.github.com/repos/original-brownbear/rultor/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/original-brownbear/rultor/teams","hooks_url":"https://api.github.com/repos/original-brownbear/rultor/hooks","issue_events_url":"https://api.github.com/repos/original-brownbear/rultor/issues/events{/number}","events_url":"https://api.github.com/repos/original-brownbear/rultor/events","assignees_url":"https://api.github.com/repos/original-brownbear/rultor/assignees{/user}","branches_url":"https://api.github.com/repos/original-brownbear/rultor/branches{/branch}","tags_url":"https://api.github.com/repos/original-brownbear/rultor/tags","blobs_url":"https://api.github.com/repos/original-brownbear/rultor/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/original-brownbear/rultor/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/original-brownbear/rultor/git/refs{/sha}","trees_url":"https://api.github.com/repos/original-brownbear/rultor/git/trees{/sha}","statuses_url":"https://api.github.com/repos/original-brownbear/rultor/statuses/{sha}","languages_url":"https://api.github.com/repos/original-brownbear/rultor/languages","stargazers_url":"https://api.github.com/repos/original-brownbear/rultor/stargazers","contributors_url":"https://api.github.com/repos/original-brownbear/rultor/contributors","subscribers_url":"https://api.github.com/repos/original-brownbear/rultor/subscribers","subscription_url":"https://api.github.com/repos/original-brownbear/rultor/subscription","commits_url":"https://api.github.com/repos/original-brownbear/rultor/commits{/sha}","git_commits_url":"https://api.github.com/repos/original-brownbear/rultor/git/commits{/sha}","comments_url":"https://api.github.com/repos/original-brownbear/rultor/comments{/number}","issue_comment_url":"https://api.github.com/repos/original-brownbear/rultor/issues/comments{/number}","contents_url":"https://api.github.com/repos/original-brownbear/rultor/contents/{+path}","compare_url":"https://api.github.com/repos/original-brownbear/rultor/compare/{base}...{head}","merges_url":"https://api.github.com/repos/original-brownbear/rultor/merges","archive_url":"https://api.github.com/repos/original-brownbear/rultor/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/original-brownbear/rultor/downloads","issues_url":"https://api.github.com/repos/original-brownbear/rultor/issues{/number}","pulls_url":"https://api.github.com/repos/original-brownbear/rultor/pulls{/number}","milestones_url":"https://api.github.com/repos/original-brownbear/rultor/milestones{/number}","notifications_url":"https://api.github.com/repos/original-brownbear/rultor/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/original-brownbear/rultor/labels{/name}","releases_url":"https://api.github.com/repos/original-brownbear/rultor/releases{/id}","deployments_url":"https://api.github.com/repos/original-brownbear/rultor/deployments","created_at":"2016-02-05T19:43:10Z","updated_at":"2016-02-10T22:33:44Z","pushed_at":"2016-04-12T22:07:14Z","git_url":"git://github.com/original-brownbear/rultor.git","ssh_url":"git@github.com:original-brownbear/rultor.git","clone_url":"https://github.com/original-brownbear/rultor.git","svn_url":"https://github.com/original-brownbear/rultor","homepage":"www.rultor.com","size":83960,"stargazers_count":1,"watchers_count":1,"language":"Java","has_issues":false,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":0,"forks":0,"open_issues":0,"watchers":1,"default_branch":"develop"}},"base":{"label":"yegor256:master","ref":"master","sha":"125621b748e5df1889769d718836359758cbdcad","user":{"login":"yegor256","id":526301,"avatar_url":"https://avatars.githubusercontent.com/u/526301?v=3","gravatar_id":"","url":"https://api.github.com/users/yegor256","html_url":"https://github.com/yegor256","followers_url":"https://api.github.com/users/yegor256/followers","following_url":"https://api.github.com/users/yegor256/following{/other_user}","gists_url":"https://api.github.com/users/yegor256/gists{/gist_id}","starred_url":"https://api.github.com/users/yegor256/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/yegor256/subscriptions","organizations_url":"https://api.github.com/users/yegor256/orgs","repos_url":"https://api.github.com/users/yegor256/repos","events_url":"https://api.github.com/users/yegor256/events{/privacy}","received_events_url":"https://api.github.com/users/yegor256/received_events","type":"User","site_admin":false},"repo":{"id":8271291,"name":"rultor","full_name":"yegor256/rultor","owner":{"login":"yegor256","id":526301,"avatar_url":"https://avatars.githubusercontent.com/u/526301?v=3","gravatar_id":"","url":"https://api.github.com/users/yegor256","html_url":"https://github.com/yegor256","followers_url":"https://api.github.com/users/yegor256/followers","following_url":"https://api.github.com/users/yegor256/following{/other_user}","gists_url":"https://api.github.com/users/yegor256/gists{/gist_id}","starred_url":"https://api.github.com/users/yegor256/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/yegor256/subscriptions","organizations_url":"https://api.github.com/users/yegor256/orgs","repos_url":"https://api.github.com/users/yegor256/repos","events_url":"https://api.github.com/users/yegor256/events{/privacy}","received_events_url":"https://api.github.com/users/yegor256/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/yegor256/rultor","description":"DevOps Team Assistant","fork":false,"url":"https://api.github.com/repos/yegor256/rultor","forks_url":"https://api.github.com/repos/yegor256/rultor/forks","keys_url":"https://api.github.com/repos/yegor256/rultor/keys{/key_id}","collaborators_url":"https://api.github.com/repos/yegor256/rultor/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/yegor256/rultor/teams","hooks_url":"https://api.github.com/repos/yegor256/rultor/hooks","issue_events_url":"https://api.github.com/repos/yegor256/rultor/issues/events{/number}","events_url":"https://api.github.com/repos/yegor256/rultor/events","assignees_url":"https://api.github.com/repos/yegor256/rultor/assignees{/user}","branches_url":"https://api.github.com/repos/yegor256/rultor/branches{/branch}","tags_url":"https://api.github.com/repos/yegor256/rultor/tags","blobs_url":"https://api.github.com/repos/yegor256/rultor/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/yegor256/rultor/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/yegor256/rultor/git/refs{/sha}","trees_url":"https://api.github.com/repos/yegor256/rultor/git/trees{/sha}","statuses_url":"https://api.github.com/repos/yegor256/rultor/statuses/{sha}","languages_url":"https://api.github.com/repos/yegor256/rultor/languages","stargazers_url":"https://api.github.com/repos/yegor256/rultor/stargazers","contributors_url":"https://api.github.com/repos/yegor256/rultor/contributors","subscribers_url":"https://api.github.com/repos/yegor256/rultor/subscribers","subscription_url":"https://api.github.com/repos/yegor256/rultor/subscription","commits_url":"https://api.github.com/repos/yegor256/rultor/commits{/sha}","git_commits_url":"https://api.github.com/repos/yegor256/rultor/git/commits{/sha}","comments_url":"https://api.github.com/repos/yegor256/rultor/comments{/number}","issue_comment_url":"https://api.github.com/repos/yegor256/rultor/issues/comments{/number}","contents_url":"https://api.github.com/repos/yegor256/rultor/contents/{+path}","compare_url":"https://api.github.com/repos/yegor256/rultor/compare/{base}...{head}","merges_url":"https://api.github.com/repos/yegor256/rultor/merges","archive_url":"https://api.github.com/repos/yegor256/rultor/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/yegor256/rultor/downloads","issues_url":"https://api.github.com/repos/yegor256/rultor/issues{/number}","pulls_url":"https://api.github.com/repos/yegor256/rultor/pulls{/number}","milestones_url":"https://api.github.com/repos/yegor256/rultor/milestones{/number}","notifications_url":"https://api.github.com/repos/yegor256/rultor/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/yegor256/rultor/labels{/name}","releases_url":"https://api.github.com/repos/yegor256/rultor/releases{/id}","deployments_url":"https://api.github.com/repos/yegor256/rultor/deployments","created_at":"2013-02-18T15:39:50Z","updated_at":"2016-04-11T19:55:23Z","pushed_at":"2016-04-12T22:07:15Z","git_url":"git://github.com/yegor256/rultor.git","ssh_url":"git@github.com:yegor256/rultor.git","clone_url":"https://github.com/yegor256/rultor.git","svn_url":"https://github.com/yegor256/rultor","homepage":"www.rultor.com","size":88510,"stargazers_count":191,"watchers_count":191,"language":"Java","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":true,"forks_count":67,"mirror_url":null,"open_issues_count":62,"forks":67,"open_issues":62,"watchers":191,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/yegor256/rultor/pulls/1092"},"html":{"href":"https://github.com/yegor256/rultor/pull/1092"},"issue":{"href":"https://api.github.com/repos/yegor256/rultor/issues/1092"},"comments":{"href":"https://api.github.com/repos/yegor256/rultor/issues/1092/comments"},"review_comments":{"href":"https://api.github.com/repos/yegor256/rultor/pulls/1092/comments"},"review_comment":{"href":"https://api.github.com/repos/yegor256/rultor/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/yegor256/rultor/pulls/1092/commits"},"statuses":{"href":"https://api.github.com/repos/yegor256/rultor/statuses/c82a067bded230bf30f2b9521b0f68ebc2751b0f"}}}}