Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Download and install MongoDB if not already available #33

Merged
merged 7 commits into from
Jul 29, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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