Skip to content

Commit

Permalink
Update installation docs to include installation for isolated envs (#715
Browse files Browse the repository at this point in the history
)

* Update installation docs to include installation for isolated envs

Add several scripts to assist with downloading and installing in an isolated/semi-isolated environment.

* Update documentation, add docstring to scripts

* Updates

Add unzipping example in installing documentation
Update naming of zip when using docker script

* update installing documentation

* update documentation, change var names in download_python_package

* update documentation

* Some re-wording of docs - Installing.rst
Adding python -m to the pip commands
Added couple of auto-generated docs for previous PRs (missed earlier)

* Small typo in Installing.rst

---------

Co-authored-by: Christopher Cianelli <ccianelli@microsoft.com>
Co-authored-by: Ian Hellen <ianhelle@microsoft.com>
  • Loading branch information
3 people committed Sep 29, 2023
1 parent b74c9c9 commit da400e1
Show file tree
Hide file tree
Showing 7 changed files with 493 additions and 86 deletions.
7 changes: 7 additions & 0 deletions docs/source/api/msticpy.auth.cloud_mappings_offline.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
msticpy.auth.cloud\_mappings\_offline module
============================================

.. automodule:: msticpy.auth.cloud_mappings_offline
:members:
:undoc-members:
:show-inheritance:
1 change: 1 addition & 0 deletions docs/source/api/msticpy.auth.rst
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ Submodules
msticpy.auth.azure_auth
msticpy.auth.azure_auth_core
msticpy.auth.cloud_mappings
msticpy.auth.cloud_mappings_offline
msticpy.auth.cred_wrapper
msticpy.auth.keyring_client
msticpy.auth.keyvault_client
Expand Down
185 changes: 102 additions & 83 deletions docs/source/data_acquisition/DataQueries.rst

Large diffs are not rendered by default.

151 changes: 148 additions & 3 deletions docs/source/getting_started/Installing.rst
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ created and activated in the prompt.
Installation
------------

Run the following command to install the base configuation of *MSTICPy*.
Run the following command to install the base configuration of *MSTICPy*.


``pip install msticpy``
Expand All @@ -71,7 +71,7 @@ known as extras. The syntax for this is:

As of version 0.9.0 *MSTICPy* has its dependencies split into
extras. This allows you to install only the packages that you
need and avoid the overhead of time and diskspace of dependencies
need and avoid the overhead of time and disk space of dependencies
that you do not need.

.. note:: extras do not affect the which code from *MSTICPy* is
Expand Down Expand Up @@ -191,7 +191,7 @@ exception message:

.. code:: bash
pip install msticpy[ml]
python -m pip install msticpy[ml]
.. note:: In some cases you many not get an informative error. We've
tried to trap all of the cases but if
Expand Down Expand Up @@ -237,3 +237,148 @@ se, and choose the conda file saved earlier with the Spark session configuration
- numpy
- pip:
- msticpy[azure]>=2.3.1
Installing for isolated or semi-isolated environments
-----------------------------------------------------

There are instances in which you may want to use msticpy in an isolated
or semi-isolated environment (e.g. an environment that does not have internet
PyPI access to install packages and dependencies).

To do this you need to build a transportable archive of MSTICPy and its
dependencies and use that as the source to install from in your target environment.

We have included a set of scripts to simplify some of this process. These
are available in the `tools folder <https://github.com/microsoft/msticpy/tree/main/tools>`__
of the MSTICPy repo.

- ``build_wheel_from_targz.py`` - builds wheel files from source tar.gz files
- ``install_all_whl_files.py`` - installs all .whl files in a directory to the target environment
- ``download_python_package.py`` - downloads a python package and its dependencies to a directory.
This script uses docker to perform the download and allows you to build an install
package for a Linux environment from a Windows environment.

In the instructions below we give both the manual steps and the script-based steps.
The latter are recommended to avoid repetitious typing and to avoid missing files.

For either manual or script-based installation it is **essential** that you
use the same Python version for both source and target environments, since
pip will download the correct version of the package for the Python version
that it is executing in. We recommend creating a virtual Python or Conda
environment for this purpose (this isn't required for the docker-based
script).

In order to find the correct python version, you can run the following:

.. code-block:: powershell
python --version
Windows Source to Isolated Windows Environment
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

1. On your primary Windows machine with internet access create a virtual environment
for the python version you want to use in the target environment.
2. Download msticpy by running the following:

.. code-block:: powershell
python -m pip download msticpy --dest \path\to\destination
Within ``\path\to\destination`` you should see a .whl file for msticpy and the other dependencies.
Some dependencies may not be .whl files, but tar.gz files.
These files will need to be built into .whl files. To do this, run the following
for each tar.gz file:

.. code-block:: powershell
python -m pip wheel {file.tar.gz} -w \path\to\destination
or use the script from MSTICPy repo "tools" folder to process all files
`build_wheel_from_targz.py <https://github.com/microsoft/msticpy/blob/main/tools/build_wheel_from_targz.py>`__
to build all the tar.gz files in a directory.

3. Zip and copy the directory folder to your target environment.

4. From the Isolated environment, unzip if needed and then run the following for each .whl file:

.. code-block:: powershell
python -m pip install "\path\to\destination\{whl_file.whl}"
.. note:: If you have an issue installing any of the packages you can use the script from
the MSTICPy repo "tools" folder `install_all_whl_files.py <https://github.com/microsoft/msticpy/blob/main/tools/install_all_whl_files.py>`__
to help.

5. Test the installation by running msticpy that suits your needs.


Linux Source to Isolated Linux Environment
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Follow the *Windows Source to Isolated Windows Environment* instructions above.


Windows Source to Isolated Linux Environment
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

1. On your source Windows machine with internet access, download
`Docker for Windows <https://docs.docker.com/desktop/install/windows-install/>`__.
We are using docker to ensure that the wheels that we are downloading are meant for the Linux architecture.

2. Run the `download_python_package.py
<https://github.com/microsoft/msticpy/blob/main/tools/download_python_package.py>`__ script.

Example:
.. code-block:: powershell
python \path\to\python\file --python-version "3.8.5" --module-name "msticpy[sentinel]" --module-version "2.7.0" --directory \path\to\destination
3. Zip and copy the directory folder to the isolated environment.

4. From the isolated environment, unzip if needed and then you will need to run the following for each .whl file:

.. code-block:: powershell
python -m pip install "\path\to\destination\{whl_file.whl}"
.. note:: If you have an issue installing any of the packages you can use the script
from MSTICPy repo "tools" folder
`install_all_whl_files.py <https://github.com/microsoft/msticpy/blob/main/tools/install_all_whl_files.py>`__
to help.

5. Test the installation by running some MSTICPy operations in a Jupyter notebook.

If you are installing within a Jupyter Notebooks, you will need to upload your zip file/directory
containing all of the whl files.

If you zipped your transfer archive and need to unzip source files, run the following:

.. code-block:: python
import zipfile
import os
import shutil
file_path = "./{zip_file_name}"
file_name = os.path.split(file_path)[-1]
file_name_without_ext = os.path.splitext(file_name)[0]
with zipfile.ZipFile(file_path, 'r') as zip_ref:
zip_ref.extractall(os.path.join(os.getcwd(), file_name_without_ext))
- To install the whl files, run the following in a cell:

.. code-block:: python
import os
directory = "/path/to/whl/files/directory" # edit this to match your directory
files = [
os.path.join(directory, filename)
for filename in os.listdir(directory)
if filename.endswith(".whl")
]
for file in files:
filename = os.path.split(file)[-1]
print(f"\nAttempting to install {filename}")
%pip install --quiet --no-index --no-deps --find-links . {file}
47 changes: 47 additions & 0 deletions tools/build_wheel_from_targz.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# -------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See License.txt in the project root for
# license information.
# --------------------------------------------------------------------------
"""Script to create PIP Wheels from tar.gz files."""

import argparse
import os

VERSION = "1.0.0"

__version__ = VERSION
__author__ = "Chris Cianelli"


def build_wheel_from_targz(directory: str):
"""
Build wheel files from tar.gz files in a directory.
Parameters
----------
directory: str
Directory containing tar.gz files
"""
files = [
os.path.join(directory, filename)
for filename in os.listdir(directory)
if filename.endswith(".tar.gz")
]
for file in files:
os.system(f"python -m pip wheel {file} -w {directory}") # nosec
os.remove(file)


if __name__ == "__main__":
parser = argparse.ArgumentParser(
description="Build wheel files from tar.gz files in a directory"
)
parser.add_argument(
"-d", "--directory", help="Directory for saved zip file", required=True
)

args = parser.parse_args()

build_wheel_from_targz(args.directory)
139 changes: 139 additions & 0 deletions tools/download_python_package.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
# -------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See License.txt in the project root for
# license information.
# --------------------------------------------------------------------------
"""Create MSTICPy install archive using docker."""
import argparse
import os
import subprocess
import time

VERSION = "1.0.0"

__version__ = VERSION
__author__ = "Chris Cianelli"


# pylint: disable=subprocess-run-check


def download_python_package(
python_version: str, package_name: str, package_version: str, host_directory: str
):
"""
Download a Python package and its dependencies for local use.
Parameters
----------
python_version: str
Python version to use. Ex: "3.8.5" or "3.9"
package_name: str
Name of the module to download. Ex: "msticpy"
package_version: str
Version of the module to download. Ex: "1.0.0"
host_directory: str
Directory containing tar.gz files
"""
os.makedirs(host_directory, exist_ok=True)
try:
# Generate a unique tag based on the current timestamp
image_tag = f"{python_version}:{int(time.time())}"

# get base name if module name includes additional dependencies
module_base_name = package_name.split("[")[0]

pipstring = (
f"{package_name}=={package_version}" if package_version else package_name
)

# Define Dockerfile content
dockerfile_content = f"""
FROM python:{python_version}
WORKDIR /{python_version}
RUN apt-get update && \\
apt-get install -y zip && \\
rm -rf /var/lib/apt/lists/*
ENV PACKAGE_NAME="{package_name}"
ENV PIP_STRING="{pipstring}"
RUN pip download "$PIP_STRING" -d /{python_version}
RUN for file in *.tar.gz; do \\
if [ -f "$file" ]; then \\
pip wheel "$file" -w /{python_version}; \\
rm -f "$file"; \\
fi; \\
done
RUN zip -j /{python_version}/py{python_version}_$PACKAGE_NAME.zip /{python_version}/*.whl
# Remove the wheel files
RUN rm -f /{python_version}/*.whl
RUN rm -f /{python_version}/*.tar.gz
ENTRYPOINT ["echo", "Docker tasks completed."]
"""

# Write Dockerfile content to a file
with open("Dockerfile", "w", encoding="utf-8") as dockerfile:
dockerfile.write(dockerfile_content)

# Build Docker image with a unique tag
docker_build_cmd = ["docker", "build", "-t", image_tag, "."]
subprocess.run(docker_build_cmd, check=True) # nosec

# Run Docker container, copy files to temporary directory, and remove it after it's done
docker_run_cmd = [
"docker",
"run",
"-v",
f"./{python_version}:/{python_version}", # Bind-mount the temporary directory
"--name",
f"{module_base_name}",
image_tag,
]
subprocess.run(docker_run_cmd, check=True)

print("copying files")

subprocess.run( # nosec
["docker", "cp", f"{module_base_name}:/{python_version}", host_directory],
check=True,
)

print("removing container")

finally:
# Delete the Docker volume
subprocess.run(["docker", "rm", f"{module_base_name}"]) # nosec

subprocess.run(["docker", "volume", "rm", f"{python_version}"]) # nosec

# Delete the Docker image
subprocess.run(["docker", "rmi", image_tag]) # nosec


if __name__ == "__main__":
parser = argparse.ArgumentParser(
description="Download Python package for local use"
)
parser.add_argument("-v", "--python-version", help="Python version", required=True)
parser.add_argument(
"-d", "--directory", help="Directory for saved zip file", required=True
)
parser.add_argument("-p", "--package-name", help="Package name", required=True)
parser.add_argument(
"-pv", "--package-version", help="Package version", required=False
)

args = parser.parse_args()

download_python_package(
args.python_version, args.package_name, args.package_version, args.directory
)

0 comments on commit da400e1

Please sign in to comment.