.. _tutorial_part1: Scenario: working with micro-services ===================================== In this scenerio we'll see how Dodo Commands can be used to work with two micro-services. It's probably over-kill to use Dodo Commmands in this simple scenario, but as the project grows bigger, it will start to be worth it. To keep it simple the services are not Dockerized. .. tip:: In the tutorials we'll assume that Bash is used. In some cases we will source the output of a Dodo command using ``$(dodo )``. In case you are curious what is being sourced, you can run the command without ``$()`` and source it manually on the command line. If you are using the Fish shell, then you can use ``dodo | source`` instead of ``$(dodo )``. In that case, be sure to also run `dodo global-config setting.shell fish` to tell Dodo Commands that you are using the fish shell. .. tip:: Parts of the tutorial that give technical details or explain more advanced features are kept separate from the main text. To not be overwhelmed, it's probably better to skip them on a first read. Two simple micro-services ------------------------- The first micro-service writes the time to a file in the ``/tmp`` directory, whereas the second micro-service runs a ``tail`` command that tracks the contents of this file. The source code for these services is found in ``part1/before`` of the dodo-commands-tutorial `_ repository. We will go ahead and clone the code for this part of the tutorial: .. tabs:: .. tab:: Step 1: Clone the tutorial files .. code-block:: bash cd /tmp git clone https://github.com/mnieber/dodo-commands-tutorial.git # Copy part 1 of the tutorial so that we can work with a short path cp -rf ./dodo-commands-tutorial/part1/before ./tutorial .. tab:: Step 2: Run the example servers Let's try out the services: .. code-block:: bash cd /tmp/tutorial/writer # let this run for a few seconds... (or run it in a separate tab and keep it running) make runserver cd /tmp/tutorial/reader # this will print some timestamps that were generated by the writer service make runserver Setting up an environment ------------------------- The next step is to create a Dodo Commands environment for working with our project: .. tabs:: .. tab:: Step 1: Create the environment .. code-block:: bash cd /tmp/tutorial $(dodo env --init tutorial) # Check that we are in the "tutorial" environment dodo which tutorial .. tab:: Step 2: Inspect the environment .. code-block:: bash # The project dir is where your project lives. # In this case, it's the directory where we called 'dodo env --init'. dodo which --project-dir /tmp/tutorial # The configuration directory is where the Dodo Commands configuration files # for the environment are stored. dodo which --config-dir /tmp/tutorial/.dodo_commands # The environment directory is where Dodo Commands stores all other information # about your environment. Usually, you don't need to work with this directory directly. dodo which --env-dir ~/.dodo_commands/envs/tutorial # The (optional) python_env directory contains the virtual Python environment for your project. # In this case, we don't have any dodo which --python-env-dir (nothing here) .. tab:: Troubleshooting .. tip:: If something goes wrong during the creation of the Dodo Commands environment then you can delete the /tmp/tutorial directory and try again. In this case, you should also run `dodo env --forget tutorial` and run the clean up steps that it prints out (for reasons of safety Dodo Commands does not run these cleanup steps automatically). Working with the configuration ------------------------------ Each environment contains a set of configuration files: .. tabs:: .. tab:: Step 1: Inspect the configuration files .. code-block:: bash # The main configuration file is called config.yaml dodo which --config /tmp/tutorial/.dodo_commands/config.yaml # Let's take a look at the configuration file: cat $(dodo which --config) ROOT: command_path: - ~/.dodo_commands/default_project/commands/* version: 1.0.0 .. tab:: (Details) Inspect the run-time configuration values When we print the contents of the configuration, we see that some extra values were added automatically. These values do not appear in the configuration file but they are available at run-time. .. code-block:: bash dodo print-config ROOT: env_name: tutorial command_path: - ~/.dodo_commands/default_project/commands/* - /some/path/to/dodo_commands/dodo_system_commands project_dir: /tmp/tutorial/part1 config_dir: /tmp/tutorial/part1/.dodo_commands version: 1.0.0 Extending the configuration ------------------------------ You can extend the configuration with new keys: .. tabs:: .. tab:: Step 1: Add a new key .. code-block:: yaml # (bottom of) /tmp/tutorial/.dodo_commands/config.yaml MAKE: cwd: ${/ROOT/project_dir}/writer .. tab:: Step 2: Inspect Now, when we print the contents of the ``MAKE`` section, we get: .. code-block:: bash dodo print-config MAKE cwd: /tmp/tutorial/writer We see that we can interpolate values. In this case ``${/ROOT/project_dir}/writer`` was interpolated to ``/tmp/tutorial/writer``. .. tab:: (Details) Using `dodo edit-config` .. tip:: Run the ``dodo edit-config`` command to open all files in the configuration directory in an editor. Set the ``config_editor`` field in the global configuration file (``~/.dodo_commands/config``) to the editor you wish to use (we recommend using gedit with the Side Panel enabled). .. note:: From here on, we will use the notation ${/FOO/bar} to refer to the ``bar`` key in the ``FOO`` section of the configuration file. Adding an alias to run the writer service ----------------------------------------- We'll now create a ``mk.py`` script that can be used as an alias for running the writer service. This alias will serve as a shortcut to running `make` in the directory of the writer service. .. tabs:: .. tab:: Step 1: Add the mk.py script .. code-block:: bash cd /tmp/tutorial mkdir ./commands touch ./commands/mk.py Add the following code to ``mk.py``: .. code-block:: python from dodo_commands import Dodo Dodo.parser.add_argument("what") Dodo.run(["make", Dodo.args.what], cwd=Dodo.get("/MAKE/cwd")) .. tab:: Step 2: Extend the command path Open ``/tmp/tutorial/.dodo_commands/config.yaml`` and edit ``${/ROOT/command_path}`` so it looks like this: .. code-block:: yaml ROOT: command_path: - ~/.dodo_commands/default_project/commands/* - ${/ROOT/project_dir}/commands .. tab:: Step 3: Inspect Now when we run ``dodo`` (without passing any arguments) we get a list of all available commands, and ``mk`` should be somewhere in that list. To run the command, let's use the ``--confirm`` flag so we can check that everything is looking good: .. code-block:: bash dodo mk runserver --confirm (/tmp/tutorial/writer) make runserver confirm? [Y/n] We see that the command will run ``make runserver`` in the ``/tmp/tutorial/writer`` directory, great! Using layers to run the reader and writer service ------------------------------------------------- At the moment, the `mk` command operates on the writer service. What if we instead want to run the Makefile of the reader service? .. tabs:: .. tab:: Step 1: Add the mk.py script As a first step to generalize our `mk` command we will move the ``${/MAKE}`` section to a new configuration file: ``server.writer.yaml``. This file should therefore look like this: .. code-block:: yaml # /tmp/tutorial/.dodo_commands/server.writer.yaml MAKE: cwd: ${/ROOT/project_dir}/writer Then we add a similar file for the reader: .. code-block:: yaml # /tmp/tutorial/.dodo_commands/server.reader.yaml MAKE: cwd: ${/ROOT/project_dir}/reader .. tip:: Don't forget to remove the `MAKE` section from the main Dodo configuration file. To edit this file, you can run (substituting your favourite editor) `nano $(dodo which --config)`. .. tab:: Step 2: Add a LAYERS_GROUP Next, we will add a ``LAYERS_GROUP`` in the main configuration file: .. code-block:: yaml # (bottom of) /tmp/tutorial/.dodo_commands/config.yaml LAYER_GROUPS: server: - writer - reader .. tab:: Step 3: Inspect Now when we call ``dodo writer.mk runserver`` then Dodo Commands will look for a layer that has the name ``writer``. It will find this layer in the ``server`` group and load the ``server.writer.yaml`` layer: .. code-block:: bash dodo writer.mk runserver --confirm (/tmp/tutorial/writer) make runserver confirm? [Y/n] Of course, to run the reader, we can use ``dodo reader.mk runserver``. Detail sections --------------- .. tabs:: .. tab:: Details Open the adjacent tabs for more detail sections .. tab:: The --trace option We saw above the Dodo Commands applies some magic to find out what command you want to run based on the prefixes that you use before the name of the command. To find out what is going on below the surface, use the ``--trace`` option to print the result of this translation process (without running any commands). For example: .. code-block:: bash dodo reader.mk runserver --trace ['/usr/local/bin/dodo', 'mk', 'runserver', '--layer=server.reader.yaml'] This tells us that we are actually invoking the command ``dodo mk runserver --layer=server.reader.yaml``. .. tab:: Running the services in tmux We can group commands in a menu so we can easily run them in a tmux session. First, make sure that tmux is installed on your system. Then, add a ``MENU`` section to the configuration file like this: .. code-block:: yaml # (bottom of) /tmp/tutorial/.dodo_commands/config.yaml MENU: commands: server: - dodo writer.mk runserver - dodo reader.mk runserver When we run ``dodo menu --tmux`` we'll open a tmux session that show the menu: .. code-block:: bash dodo menu --tmux 1 [server] - dodo writer.mk runserver 2 [server] - dodo reader.mk runserver Select one or more commands (e.g. 1,3-4) or type 0 to exit: Type ``1,2`` to run both commands. They will open in separate windows inside the tmux screen.