******
GRAINS
******
**System information discovery and asset tracking**

INSTALLATION
============
::

    pip install grainsv2

INSTALLATION FOR DEVELOPMENT
============================

Clone the `grains` repo and install with pip::

    git clone https://gitlab.com/saltstack/pop/grains.git
    pip install -e grains

EXECUTION
=========
After installation the `grains` command should now be available.

The command will have no output until it is vertically extended (details below).

TESTING
=======
install `requirements-test.txt` with pip and run pytest::

    pip install -r grains/requirements-test.txt
    pytest grains/tests

VERTICAL APP-MERGING
====================
Instructions for extending grains

Install pop::

    pip install --upgrade pop

Create a new directory for the project::

    mkdir grains_{project_name}
    cd grains_{project_name}


Use `pop-seed` to generate the structure of a project that extends `grains`:

    pop-seed -t v grains_{project_name} -d grains

* "-t v" specifies that this is a vertically app-merged project
*  "-d grains" says that we want to implement the dynamic name of "grains"

Notice that some structure has been created for you.
We especially care about the new directory in `grains_{project_name}/grains`

Add "grains" to the requirements.txt::

    echo "grains" >> requirements.txt

CREATING GRAINS
===============
 - Create a new file in "grains_{project_name}/grains"
    - The directory should already have been created by `pop-seed`.
    - The file name is arbitrary.
    - Every file in this directory, then it's subdirectories,  will be parsed "simultaneously" (as far as that makes sense for asyncio).
    - Grains that are dependant on each other should be assigned in the same function.
    - Do NOT rely on the collector's recursion strategy for grains to depend on each other.
    - Use `hub.grains.init.wait_for("grains")` to wait for grains to be generated by another sub

 - Add your new project to the python path
    - alternatively, run "pip install -e ." from your project's root directory
        - you only need to do this once

 - Create an async function in this file with a descriptive yet arbitrary name
    - By convention it should start with "load"
    - Make it async unless you have a really really really good reason.
    - Grains that depend on each other already belong in the same function, don't depend on synchronous programming for determinism
    - Use `hub.grains.init.wait_for("specific_grain")` to explicitly wait for an external grain to become available before moving on
    - Grains can be accessed and assigned like a dictionary, but our convention is to use the namespace
    - Use `hub.grains.GRAINS._dict()` or `copy.copy(hub.grains.GRAINS)` to retrieve a JSON serializable version of grains.

Example::

    async def load_my_grain(hub):
        hub.grains.GRAINS.new_grain = "Hello World!"


And that's it! Now verify that your grain is collected from the command line::

    grains new_grain

Output::

    new_grain:
        Hello World!
