Skip to content

Commit

Permalink
Merge pull request #33 from jaraco/feature/install-mongodb
Browse files Browse the repository at this point in the history
Download and install MongoDB if not already available
  • Loading branch information
jaraco authored Jul 29, 2023
2 parents 82a563e + 845f660 commit 8f11e29
Show file tree
Hide file tree
Showing 5 changed files with 118 additions and 4 deletions.
6 changes: 6 additions & 0 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,12 @@ jobs:
- name: Install tox
run: |
python -m pip install tox
# needed to run mongodb
# https://www.mongodb.com/docs/manual/tutorial/install-mongodb-on-ubuntu-tarball/
- uses: awalsh128/cache-apt-pkgs-action@latest
if: ${{ matrix.platform == 'ubuntu-latest' }}
with:
packages: libcurl4 libgssapi-krb5-2 libldap-2.5-0 libwrap0 libsasl2-2 libsasl2-modules libsasl2-modules-gssapi-mit snmp openssl liblzma5
- name: Run
run: tox

Expand Down
10 changes: 6 additions & 4 deletions jaraco/mongodb/fixtures.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,13 @@ def _ephemeral_instance(config):
params = shlex.split(params_raw)
try:
instance = service.MongoDBInstance()
instance.merge_mongod_args(params)
instance.start()
pymongo.MongoClient(instance.get_connect_hosts())
yield instance
with instance.ensure():
instance.merge_mongod_args(params)
instance.start()
pymongo.MongoClient(instance.get_connect_hosts())
yield instance
except Exception as err:
raise
pytest.skip(f"MongoDB not available ({err})")
instance.stop()

Expand Down
89 changes: 89 additions & 0 deletions jaraco/mongodb/install.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import json
import pathlib
import platform
import re
import tarfile
import urllib.request
import posixpath
import zipfile
import io

import autocommand
from more_itertools import one


def get_download_url():
source = 'https://www.mongodb.com/try/download/community'
with urllib.request.urlopen(source) as resp:
html = resp.read().decode('utf-8')
server_data = re.search(
r'<script id="server-data">\s*window\.__serverData=(.*?)\s*</script>',
html,
flags=re.DOTALL | re.MULTILINE,
).group(1)
data = json.loads(server_data)
versions = data['components'][2]['props']['embeddedComponents'][0]['props'][
'items'
][2]['embeddedComponents'][0]['props']['data'][0]['data'][0]
best_version = next(ver for ver in versions if versions[ver]['meta']['current'])
platforms = versions[best_version]['platforms']
lookup = {
('Darwin', 'arm64'): 'macOS ARM 64',
('Darwin', 'x86_64'): 'macOS x64',
('Linux', 'x86_64'): 'Ubuntu 22.04 x64',
('Linux', 'aarch64'): 'Ubuntu 22.04 ARM 64',
('Windows', 'x86_64'): 'Windows x64',
('Windows', 'ARM64'): 'Windows x64',
}
plat_name = lookup[(platform.system(), platform.machine())]
format = 'zip' if 'Windows' in plat_name else 'tgz'
return platforms[plat_name][format]


class RootFinder(set):
def __call__(self, info, path):
self.add(self.root(info.name))
return info

@staticmethod
def root(name):
root, _, _ = name.partition(posixpath.sep)
return root

@classmethod
def from_names(cls, names):
return cls(map(cls.root, names))


def _extract_all(resp, target):
desig = resp.headers['Content-Type'].lower().replace('/', '_').replace('+', '_')
func_name = f'_extract_{desig}'
return globals()[func_name](resp, target)


def _extract_application_zip(resp, target):
data = io.BytesIO(resp.read())
with zipfile.ZipFile(data) as obj:
roots = RootFinder.from_names(obj.namelist())
obj.extractall(
target,
)
return roots


def _extract_application_gzip(resp, target):
with tarfile.open(fileobj=resp, mode='r|*') as obj:
roots = RootFinder()
# python/typeshed#10514
obj.extractall(target, filter=roots) # type: ignore
return roots


def install(target: pathlib.Path = pathlib.Path()):
url = get_download_url()
with urllib.request.urlopen(url) as resp:
roots = _extract_all(resp, target.expanduser())
return target.joinpath(one(roots))


autocommand.autocommand(__name__)(install)
16 changes: 16 additions & 0 deletions jaraco/mongodb/service.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
import functools
import logging
import datetime
import pathlib
import contextlib

from typing import Dict, Any

Expand All @@ -18,6 +20,7 @@
from tempora import timing
from . import manage
from . import cli
from . import install


log = logging.getLogger(__name__)
Expand Down Expand Up @@ -55,6 +58,19 @@ class MongoDBFinder(paths.PathFinder):
def find_binary(cls):
return os.path.join(cls.find_root(), cls.exe)

@classmethod
@contextlib.contextmanager
def ensure(cls):
try:
yield cls.find_root()
except RuntimeError:
with tempfile.TemporaryDirectory() as tmp_dir:
tmp = pathlib.Path(tmp_dir)
root = install.install(target=tmp).joinpath('bin')
cls.candidate_paths.append(root)
yield root
cls.candidate_paths.remove(root)


class MongoDBService(MongoDBFinder, services.Subprocess, services.Service):
port = 27017
Expand Down
1 change: 1 addition & 0 deletions setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ install_requires =
pytimeparse
jaraco.collections>=2
importlib_metadata; python_version < "3.8"
autocommand

[options.packages.find]
exclude =
Expand Down

0 comments on commit 8f11e29

Please sign in to comment.