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

Update installation docs to include installation for isolated envs #715

Merged
merged 10 commits into from
Sep 29, 2023
126 changes: 126 additions & 0 deletions docs/source/getting_started/Installing.rst
Original file line number Diff line number Diff line change
Expand Up @@ -237,3 +237,129 @@ 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 utilize msticpy in an isolated or semi-isolated environment. It is recommended to utilize a virtual environment and the scripts provided within the steps. Also ensure you have the correct python version installed on your host machine as well as the isolated environment. In order to find the correct python version, you can run the following:
ccianelli22 marked this conversation as resolved.
Show resolved Hide resolved
ccianelli22 marked this conversation as resolved.
Show resolved Hide resolved

ccianelli22 marked this conversation as resolved.
Show resolved Hide resolved
.. code-block:: powershell

python --version

You can also run:

.. code-block:: python
import sys
print(sys.version)

Below are some examples on how to do this for certain platforms:

Windows -> Isolated Windows Environment
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

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

.. code-block:: powershell

pip download msticpy --no-deps --dest \path\to\destination
ccianelli22 marked this conversation as resolved.
Show resolved Hide resolved

- 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:

.. code-block:: powershell

pip wheel {file.tar.gz} -w \path\to\destination

or use the tool here
ccianelli22 marked this conversation as resolved.
Show resolved Hide resolved
`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.

- Zip and/or copy the directory folder to the isolated environment.

- From the Isolated environment, unzip if needed and then run the following:

.. code-block:: powershell

pip install "\path\to\destination\{msticpy.whl}"

.. note:: If you have an issue installing msticpy, you may have to install the dependencies first. You can also utilize
ccianelli22 marked this conversation as resolved.
Show resolved Hide resolved
`install_all_whl_files.py
<https://github.com/microsoft/msticpy/blob/main/tools/install_all_whl_files.py>`__
to help.


- Test the installation by running msticpy that suites your needs.
ccianelli22 marked this conversation as resolved.
Show resolved Hide resolved



Linux -> Isolated Linux Environment
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Similar to Windows to Isolated Windows Environment instructions above


Windows -> Isolated Linux Environment including Jupyter Notebooks
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

- On your primary Windows machine with internet access download dockers for windows.
ccianelli22 marked this conversation as resolved.
Show resolved Hide resolved
- Run the script
`download_python_package.py
<https://github.com/microsoft/msticpy/blob/main/tools/download_python_package.py>`__

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

- Zip and/or copy the directory folder to the isolated environment.

- From the Isolated environment, unzip if needed and then run the following:

.. code-block:: powershell

pip install "\path\to\destination\{msticpy.whl}"

.. note:: If you have an issue installing msticpy, you may have to install the dependencies first. You can also utilize
`install_all_whl_files.py
<https://github.com/microsoft/msticpy/blob/main/tools/install_all_whl_files.py>`__
to help.

- Test the installation by running msticpy that suites your needs.

- 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 need to unzip the zip file, 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"
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}
40 changes: 40 additions & 0 deletions tools/build_wheel_from_targz.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import os
import sys
import argparse

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"pip wheel {file} -w {directory}")
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()

download_python_package(args.directory)
ccianelli22 marked this conversation as resolved.
Show resolved Hide resolved
125 changes: 125 additions & 0 deletions tools/download_python_package.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
import os
import subprocess
import time
import shutil
import sys
import argparse

VERSION = "1.0.0"

__version__ = VERSION
__author__ = "Chris Cianelli"


def download_python_package(
python_version: str, module_name: str, module_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"
module_name: str
ccianelli22 marked this conversation as resolved.
Show resolved Hide resolved
Name of the module to download. Ex: "msticpy"
module_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 = module_name.split("[")[0]

# 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 MODULE_NAME="{module_name}"
ENV MODULE_VERSION="{module_version}"
ccianelli22 marked this conversation as resolved.
Show resolved Hide resolved

RUN pip download "${{MODULE_NAME}}==${{MODULE_VERSION}}" -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}_$MODULE_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") 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)

# 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(
["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}"])

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

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


if __name__ == "__main__":
parser = argparse.ArgumentParser(
description="Download Python modules for local use"
)
parser.add_argument("-v", "--python-version", help="Python version", required=True)
parser.add_argument("-m", "--module-name", help="Module name", required=True)
parser.add_argument("-mv", "--module-version", help="Module version", required=True)
parser.add_argument(
"-d", "--directory", help="Directory for saved zip file", required=True
)

args = parser.parse_args()

download_python_package(
args.python_version, args.module_name, args.module_version, args.directory
)
42 changes: 42 additions & 0 deletions tools/install_all_whl_files.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import os
import sys
import argparse

VERSION = "1.0.0"

__version__ = VERSION
__author__ = "Chris Cianelli"


def install_all_whl_files(directory: str):
"""
Install all wheel files in a directory

Parameters
----------
directory: str
Directory containing wheel files
"""
files = [
os.path.join(directory, filename)
for filename in os.listdir(directory)
if filename.endswith(".whl")
]
for file in files:
os.system(
f"pip install --quiet --no-index --no-deps --find-links . {file} --user"
)
print(f"Installed {os.path.split(file)[-1]}")


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()

install_all_whl_files(args.directory)