Building a Development Environment for Mercury
----------------------------------------------
In this guide we will discuss how to setup a Mercury development environment on linux and osx. Microsoft Windows is not
supported in this guide and we suggest that windows users provision a Linux VM for Mercury development.
The Code
~~~~~~~~
Mercury sources are divided into three major repositories.
mercury
_______
This is the main repository for the mercury project. This documentation source (and much more) can be found in the main
repository's *docs* directory. The repo includes the core mercury packages: common, inventory, rpc, and log. It's super
important and you should clone it now.
.. code-block:: bash
$ git clone https://github.com/jr0d/mercury.git
mercury-api
___________
The mercury_api repository contains the mercury HTTP API and provides a convenient interface to the mercury inventory
and rpc ZeroMQ services. Clone this repository.
.. code-block:: bash
$ git clone https://github.com/jr0d/mercury-api.git
mercury-agent
_____________
The agent is designed to run on target devices running a Linux operating system. Regardless of your development
operating system, go ahead and clone it now. We'll circle back at the end of this guide.
.. code-block:: bash
$ git clone https://github.com/jr0d/mercury-agent.git
Install Python 3.6 and virtualenv
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
This process will very per your distribution. It is here for the uninitiated, if you already
have a working python3.6 development environment, you can skip to `Installing service dependencies`_
Enterprise Linux 7 / CentOS 7
_____________________________
.. code-block:: bash
yum install -y https://mirror.rackspace.com/ius/stable/CentOS/7/x86_64/ius-release-1.0-15.ius.centos7.noarch.rpm && \
yum install -y python36u python36u-pip && \
pip3.6 install virtualenv
Ubuntu 16.04
____________
For python 3.6 (preferred)
.. code-block:: bash
apt-get update && \
apt-get -y install software-properties-common && \
add-apt-repository -y ppa:jonathonf/python-3.6 && \
apt-get update && \
apt-get -y install python3.6 wget && \
wget https://bootstrap.pypa.io/get-pip.py -O - | python3.6 && \
pip3.6 install virtualenv
For python3.5, just install the python3.5-dev and python3.5-pip packages. Then:
.. code-block:: bash
pip3 install virtualenv
OSX
___
Use homebrew to install python
.. code-block:: bash
brew install python3
pip3 install virtualenv
Installing service dependencies
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Mercury utilizes mongodb for persistent storage and redis for distributed queuing. Install both of these
services using your distributions package management system or use the docker method (see next section)
before proceeding.
Using docker to run mongodb and redis
_____________________________________
On mac, the easiest way to get a development environment up and running is to launch mongo and redis in ephemeral
containers.
.. note::
Any data that is added to the services running within the container is lost when the container exits. This is
fine for mercury development, which does not require any table bootstrapping. If you would like to preserve
your data for more than one session, take a look at the docker
`volume `_ command
Docker hub provides first party mongo and redis library images. To run both services, use the following commands:
.. code-block:: bash
$ docker run -p 27017:27017 mongo
$ docker run -p 6379:6379 redis
This will launch both services in their own containers and forward their service port to your local environment.
To run the commands in the background, use the *-d* flag:
.. code-block:: bash
$ ~ : docker run -dp 27017:27017 mongo
b639809a68ff7525869ce799605f0001251169cb4e65407b56712471e8389cb8 <-- The container id
$ ~ : docker run -dp 6379:6379 redis
452e3997d6833df75dea1aad2cc966975605fa4d17a080e3e5f38710fa7a5433
You can see that they are running with the *ps* command
.. code-block:: bash
$ ~ : docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
452e3997d683 redis "docker-entrypoint..." 3 minutes ago Up 3 minutes 0.0.0.0:6379->6379/tcp pensive_poincare
b639809a68ff mongo "docker-entrypoint..." 4 minutes ago Up 4 minutes 0.0.0.0:27017->27017/tcp zen_albattani
When you are done with them, stop them with the kill command
.. code-block:: bash
$ ~ : docker kill 452e3997d683
452e3997d683
$ ~ : docker kill b639809a68ff
b639809a68ff
Mercury Services
~~~~~~~~~~~~~~~~~~~~~~~~~~~
Create a virtual environment
____________________________
.. code-block:: bash
$ mkdir ~/.virtualenvs
$ virtualenv -p`which python3.6` ~/.virtualenvs/mercury
Now activate the virtual environment.
.. code-block:: bash
$ source ~/.virtualenvs/mercury/bin/activate
.. note::
You will need to activate the virtual environment whenever you are running a mercury service.
To make virtualenv management easier, consider using
`virtualenvwrapper `_ or
`pyvenv `_.
Installing the services
_______________________
Mercury implements a micro-services architecture. This allows us to deploy and scale components
independently. Unfortunately, such an architecture slightly complicates the development process
when compared to a monolithic application. Instead of installing and running a single service
element, we must install and run several components.
The first component is the mercury-common package. This package, as the name implies, contains
common libraries used by two or more discrete components. Following common, are the mercury-inventory,
mercury-log, and mercury-rpc packages.
We'll install each package using pip -e. This is synonymous with using *setup.py devel*, but pip allows
us to use library wheelhouses for binary dependencies, such as ZeroMQ or pyYAML, when resolving requirements.
From the mercury repository root:
.. code-block:: bash
pushd src/mercury-common && \
pip install -r test-requirements.txt && \
pip install -e . && \
popd && \
pushd src/mercury-inventory && \
pip install -e . && \
popd && \
pushd src/mercury-rpc && \
pip install -e . && \
popd && \
pushd src/mercury-log && \
pip install -e . && \
popd
Install the HTTP API in the same manner
.. code-block:: bash
cd mercury-api && pip install -e .
Creating the Configuration Files
________________________________
All mercury services are configured using a YAML configuration file. Included with each source is a
sample file. The files are already ready for local development for the most part, so we only need
to copy them to a location mercury scans. By default, mercury scans the following directories:
* . (The current working directory)
* ~/.mercury
* /etc/mercury
.. note::
Once the **find_configuration()** function *finds* the configuration file it is looking for,
the loop breaks. If you happen to have a configuration file in your local directory and in /etc/mercury,
the configuration in /etc/mercury will be ignored.
For easy use, we will be populating our configuration files in our home directory, **~/.mercury**. Keep in mind,
Mercury is under heavy development, so watch for changes to the configuration file samples when pulling master; making
sure to update your local copies when necessary.
From the mercury repository root:
.. code-block:: bash
mkdir -p ~/.mercury && \
for _package in mercury-inventory mercury-rpc mercury-log; \
do cp src/${_package}/${_package}-sample.yaml ~/.mercury/${_package}.yaml; done
Running the Services
____________________
When installing mercury packages, the following servers launchers are created:
+--------------------+------------------------------+----------------+------------------------+
| *Program* | *Description* | *Default Port* | *Config file* |
+--------------------+------------------------------+----------------+------------------------+
| mercury-inventory | Starts the inventory service | 9000 | mercury-inventory.yaml |
+--------------------+------------------------------+----------------+------------------------+
| mercury-frontend | Starts the frontend service | 9001 | mercury-rpc.yaml |
+--------------------+------------------------------+----------------+------------------------+
| mercury-backend | Starts the backend service | 9002 | mercury-rpc.yaml |
+--------------------+------------------------------+----------------+------------------------+
| mercury-rpc-worker | Starts RPC manager process | N/A | mercury-rpc.yaml |
+--------------------+------------------------------+----------------+------------------------+
| mercury-log | Starts the log service | 9006 | mercury-log.yaml |
+--------------------+------------------------------+----------------+------------------------+
Each command line launcher can be configured using the configuration file,
command line arguments, or with environment variables. Running a command with
the --help switch will expose all available options for that service.
For development, starting the services from the command line may not be
desirable. Especially if you are working in an IDE and would like to do things
like attaching a debugger. For these cases, it is possible to launch the python
scripts directly.
* mercury-inventory
* Inventory Service | *python src/mercury-inventory/mercury/inventory/server.py*
* mercury-rpc
* Front End ZeroMQ service | **python src/mercury-rpc/mercury/rpc/frontend/frontend.py**
* Back End ZeroMQ service | **python src/mercury-rpc/mercury/rpc/backend/backend.py**
* Workers service | **python src/mercury-rpc/mercury/rpc/workers/worker.py**
* mercury-log
* Logging service | **python src/mercury-log/log_service/server.py**
Regardless of how you choose to start the services, make sure they are all
running before proceeding.
Starting the API
________________
The API service has not matured to the point where it has a dedicated service
launcher. To start the service, run the python file directly.
* mercury-api
* Bottle API service | *python mercury-api/mercury_api/frontend.py*
Running the Agent
~~~~~~~~~~~~~~~~~
Linux (Native)
______________
Following the same pattern as before, copy the agent configuration file to a place mercury will search
.. code-block:: bash
mkdir -p ~/.mercury
cp mercury-agent/mercury-agent-sample.yaml ~/.mercury/mercury-agent.yaml
Install the agent into the same virtual environment as the other services, see `Create a virtual environment`_.
.. code-block:: bash
cd mercury-agent ; pip install -e .
Now you can run the agent with:
.. code-block:: bash
$ mercury-agent
.. note::
You should probably run the mercury agent as a normal user for now. TODO: Create a link to the
press development integration documentation
Running the Agent in Docker on Mac
__________________________________
Running the agent natively on MacOS is not possible due to the agent's dependence on the Linux ABI. Docker for mac,
fortunately, use a linux VM to host containers, making it an excellent target for running the agent.
.. note::
Docker allows us to quickly develop on the RPC stack of mercury, without having to go through the process of spinning
up a dedicated VM. If you need to develop on hardware native components, protected ABI inspectors, or press
provisioning, follow this guide for setting up a development VM and network:
TODO: Provide link to Agent development guide.
To take advantage of this awesomeness, you need to install `Docker on your mac `_.
A docker file and configuration file (built specifically for local development on a mac) is provided with the
agent source. The docker file contains the following:
.. code-block:: Dockerfile
FROM python
WORKDIR /
ADD . /src/mercury/agent
ADD docker/mercury-agent-docker.yaml /etc/mercury/mercury-agent.yaml
RUN pip install -r /src/mercury/agent/requirements.txt
RUN pip install -e /src/mercury/agent
RUN apt-get -y update
RUN apt-get -y install pciutils
EXPOSE 9003
EXPOSE 9004
.. warning::
The docker build script installs the mercury-common package from pypi, and will not use any local copy. If you
are making changes to common that you want the agent to take advantage of, copy the provided docker file, and
modify it to look like this:
.. code-block:: Dockerfile
FROM python
WORKDIR /
ADD . /src/mercury/agent
ADD docker/mercury-agent-docker.yaml /etc/mercury/mercury-agent.yaml
ADD ### PATH TO LOCAL MERCURY COMMON SOURCE ### /src/mercury/common
RUN pip install -e /src/mercury/common
RUN pip install -e /src/mercury/agent
RUN apt-get -y update
RUN apt-get -y install pciutils
EXPOSE 9003
EXPOSE 9004
Then, run this command to build
.. code-block:: bash
$ docker build -f PATH_TO_DOCKERFILE -t mercury/agent .
Build the image with the following
.. code-block:: bash
$ cd mercury-agent
$ docker build -t mercury/agent .
Now, run the agent
.. code-block:: bash
$ docker run -p 9003:9003 -p 9004:9004 mercury/agent mercury-agent
If everything goes correctly you should see output similar to:
.. code-block:: default
INFO:mercury:Starting Agent
INFO:mercury.agent.agent:Running inspectors
INFO:mercury.agent.agent:Registering device inventory for MercuryID 00fc6ad81ffb792d04a7a4454a4c9af4579f9af982
INFO:mercury.agent.agent:Starting pong service
INFO:mercury.agent.agent:Registering device
INFO:mercury.agent.agent:Injecting MercuryID for remote logging
INFO:mercury.agent.agent:Injection completed
ERROR:mercury.agent.agent:Caught recoverable exception running async inspector: Could not find lldplite binary
INFO:mercury.agent.agent:Starting agent rpc service: tcp://0.0.0.0:9003
INFO:mercury.common.transport:Bound: tcp://0.0.0.0:9003
If you check the backend console you should also see the successful connection:
.. code-block:: default
2017-10-17 16:46:07,429 : INFO - mercury.rpc.active_asyncio - Adding record, 00fc6ad81ffb792d04a7a4454a4c9af4579f9af982, to active state
Testing out the API
~~~~~~~~~~~~~~~~~~~
Now that everything is up and running, we can begin using the HTTP API to explore the inventory and rpc systems. Try
pointing your browser here: http://localhost:9005/api/active/computers
You should see something like this:
.. code-block:: json
{
"total": 1,
"limit": 250,
"items": [
{
"_id": "59e518dd72bb0a572a05cf08",
"mercury_id": "00fc6ad81ffb792d04a7a4454a4c9af4579f9af982"
}
],
"direction": "ASCENDING"
}
That's your lonely little agent running all by it's lonesome. You can enumerate the agent's RPC capabilities by
hitting the active API with the mercury_id http://localhost:9005/api/active/computers/
To see it's full inventory record, hit the inventory endpoint http://localhost:9005/api/inventory/computers/
Now for the fun part, let's try scheduling a job!
.. code-block:: bash
curl -H 'Content-type: application/json' -d @- -XPOST http://localhost:9005/api/rpc/jobs << EOF
{
"query": {},
"instruction": {
"method": "echo",
"args": [
"Hello Mercury!"
]
}
}
EOF
Your consoles should light up and you should get a some JSON back, containing a job_id
.. code-block:: json
{"job_id": "314ac71b-d353-46e9-95c8-f2e72c3a4f77"}
Try the following urls to inspect the job
* http://localhost:9005/api/rpc/jobs/
* http://localhost:9005/api/rpc/jobs//status
* http://localhost:9005/api/rpc/jobs//tasks
Done!
~~~~~
Pat yourself on the back! You are now be ready to begin hacking on Mercury! For full API documentation, be sure to
check out the `API docs `_.
TODO: List mercury resources, slack, irc, mailing list, etc
References
~~~~~~~~~~
`Installing python on OSX `_