Skip to content

Commit

Permalink
Specify SSH key for private git repo (#981)
Browse files Browse the repository at this point in the history
* Updating readme with docker swarm/compose example

* Adding openssh-client to dockerfile, adding key_path as a skill parameter

* test_install_specific_remote_module required None type on git_clone call

* test attempt

* reworking

* adding None type to test_instalL-default_remote_module

* running black

* Update aiohttp from 3.6.0 to 3.6.1 (#1066)

* Update webexteamssdk from 1.1.1 to 1.2 (#1067)

* Add flake8 to the precommit hooks (#1073)

* Make load and start_connectors async (#1072)

* Make load async

* Add missing docstring

* Undo wrapping config path in string

* Test git clone ssh rsa path
  • Loading branch information
jseiser authored and jacobtomlinson committed Sep 24, 2019
1 parent a4faa3f commit c2cc9d3
Show file tree
Hide file tree
Showing 3 changed files with 44 additions and 9 deletions.
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ COPY requirements.txt requirements.txt
COPY README.md README.md
COPY MANIFEST.in MANIFEST.in

RUN apk update && apk add git
RUN apk update && apk add --no-cache git openssh-client
RUN pip3 install --upgrade pip
RUN pip3 install --no-cache-dir --no-use-pep517 .
RUN apk del gcc musl-dev
Expand Down
15 changes: 12 additions & 3 deletions opsdroid/loader.py
Original file line number Diff line number Diff line change
Expand Up @@ -177,20 +177,28 @@ def build_module_install_path(self, config):
return os.path.join(self.modules_directory, config["type"], config["name"])

@staticmethod
def git_clone(git_url, install_path, branch):
def git_clone(git_url, install_path, branch, key_path=None):
"""Clone a git repo to a location and wait for finish.
Args:
git_url: The url to the git repository
install_path: Location where the git repository will be cloned
branch: The branch to be cloned
key_path: SSH Key for git repository
"""
git_env = os.environ.copy()
if key_path:
git_env[
"GIT_SSH_COMMAND"
] = f"ssh -i {key_path} -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no"

process = subprocess.Popen(
["git", "clone", "-b", branch, git_url, install_path],
shell=False,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
env=git_env,
)
Loader._communicate_process(process)

Expand Down Expand Up @@ -607,11 +615,12 @@ def _install_git_module(self, config):
else:
git_url = DEFAULT_GIT_URL + config["type"] + "-" + config["name"] + ".git"

if any(prefix in git_url for prefix in ["http", "https", "ssh"]):
if any(prefix in git_url for prefix in ["http", "https", "ssh", "git@"]):
# TODO Test if url or ssh path exists
# TODO Handle github authentication
_LOGGER.info(_("Cloning %s from remote repository"), config["name"])
self.git_clone(git_url, config["install_path"], config["branch"])
key_path = config.get("key_path", None)
self.git_clone(git_url, config["install_path"], config["branch"], key_path)
else:
if os.path.isdir(git_url):
_LOGGER.debug(_("Cloning %s from local repository"), config["name"])
Expand Down
36 changes: 31 additions & 5 deletions tests/test_loader.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import sys
import os
import shutil
import subprocess
Expand Down Expand Up @@ -65,7 +64,7 @@ def test_load_config_valid_without_db_and_parsers(self):
def test_load_config_broken_without_connectors(self):
opsdroid, loader = self.setup()
with self.assertRaises(SystemExit) as cm:
config = loader.load_config_file(
_ = loader.load_config_file(
[os.path.abspath("tests/configs/broken_without_connectors.yaml")]
)
self.assertEqual(cm.exception.code, 1)
Expand All @@ -81,7 +80,7 @@ def test_load_config_broken(self):
opsdroid, loader = self.setup()

with self.assertRaises(SystemExit) as cm:
config = loader.load_config_file(
_ = loader.load_config_file(
[os.path.abspath("tests/configs/full_broken.yaml")]
)
self.assertEqual(cm.exception.code, 1)
Expand All @@ -105,7 +104,7 @@ def test_load_exploit(self):
"""
opsdroid, loader = self.setup()
with self.assertRaises(SystemExit):
config = loader.load_config_file(
_ = loader.load_config_file(
[os.path.abspath("tests/configs/include_exploit.yaml")]
)
self.assertLogs("_LOGGER", "critical")
Expand Down Expand Up @@ -182,12 +181,16 @@ def test_load_broken_config_file(self):
def test_git_clone(self):
with mock.patch.object(subprocess, "Popen") as mock_subproc_popen:
opsdroid, loader = self.setup()
myrsa = "/path/to/my/id_rsa"
loader.git_clone(
"https://github.com/rmccue/test-repository.git",
os.path.join(self._tmp_dir, "/test"),
"master",
myrsa,
)
self.assertTrue(mock_subproc_popen.called)
_, mock_subproc_popen_kwargs = mock_subproc_popen.call_args
assert myrsa in mock_subproc_popen_kwargs["env"]["GIT_SSH_COMMAND"]

def test_git_pull(self):
with mock.patch.object(subprocess, "Popen") as mock_subproc_popen:
Expand Down Expand Up @@ -511,7 +514,29 @@ def test_install_specific_remote_module(self):
) as mockclone:
loader._install_module(config)
mockclone.assert_called_with(
config["repo"], config["install_path"], config["branch"]
config["repo"], config["install_path"], config["branch"], None
)

def test_install_specific_remote_module_ssl(self):
opsdroid, loader = self.setup()
config = {
"name": "testmodule",
"install_path": os.path.join(self._tmp_dir, "test_specific_remote_module"),
"repo": "https://github.com/rmccue/test-repository.git",
"branch": "master",
"key_path": os.path.join(
self._tmp_dir, os.path.normpath("install/from/here.key")
),
}
with mock.patch("opsdroid.loader._LOGGER.debug"), mock.patch.object(
loader, "git_clone"
) as mockclone:
loader._install_module(config)
mockclone.assert_called_with(
config["repo"],
config["install_path"],
config["branch"],
config["key_path"],
)

def test_install_specific_local_git_module(self):
Expand Down Expand Up @@ -600,6 +625,7 @@ def test_install_default_remote_module(self):
+ ".git",
config["install_path"],
config["branch"],
None,
)
shutil.rmtree(config["install_path"], onerror=del_rw)

Expand Down

0 comments on commit c2cc9d3

Please sign in to comment.