<p>In this document, we explain how modules are written and integrated into MIG.</p>
<p>The reception of a command by an agent triggers the execution of modules. A module is a Go package that is imported into the agent at compilation, and that performs a very specific set of tasks. For example, the <cite>filechecker</cite> module provides a way to scan a file system for files that contain regexes, match a checksum, ... Another module is called <cite>connected</cite>, and looks for IP addresses currently connected to an endpoint. <cite>user</cite> is a module to manages users, etc...</p>
<p>Module are somewhat autonomous. They can be developped outside of the MIG code base, and only imported during compilation of the agent. Go does not provide a way to load external libraries, so modules are shipped within the agent's static binary, and not as separate files.</p>
<sectionid="module-logic">
<h2>1Module logic</h2>
<p>A module registers itself at runtime via the init() function, which calls <cite>mig.RegisterModule</cite> with a module name and an instance of the <cite>Runner</cite> variable. The agent uses the list populated by <cite>mig.RegisterModule</cite> to keep track of the available modules. When a command is received from the scheduler by the agent, the agent goes through the list of operations, and looks for an available module to execute each operation.</p>
<pre><codeclass="go"><spanclass="c1">// in src/mig/agent/agent.go
<p>If a module is available to run an operation, the agent executes a fork of itself to run the module. This is done by calling the agent binary with the flag <strong>-m</strong>, followed by the name of the module, and the module parameters provided by the command.</p>
<p>This can easily be done on the command line directly:</p>
<pre><codeclass="bash"><spanclass="nv">$ </span>/sbin/mig-agent -m example <spanclass="s1">'{"gethostname": true, "getaddresses": true, "lookuphost": "www.google.com"}'</span>
<p>When the agent is invoked with a <strong>-m</strong> flag that is not set to <cite>agent</cite>, it will attempt to run a module instead of running in agent mode. The snippet of code below is then executed:</p>
<pre><codeclass="go"><spanclass="c1">// runModuleDirectly executes a module and displays the results on stdout
<p>The code above shows how the agent find the right module to run. A module implements the <cite>mig.Moduler</cite> interface, which implements a function named <cite>Run()</cite>. The agent simply invokes the <cite>Run()</cite> function of the module using the information provided during the registration.</p>
</section>
<sectionid="the-example-module">
<h2>2The <cite>Example</cite> module</h2>
<p>An example module that can be used as a template is available in <ahref="../src/mig/modules/example/example.go">src/mig/modules/example/</a>. We will study its structure to understand how modules are written and executed.</p>
<p>The main function of a module is called <cite>Run()</cite>. It takes one argument: an array of bytes that unmarshals into a JSON struct of parameters. The module takes care of unmarshalling into the proper struct, and validates the parameters using a function called <cite>ValidateParameters()</cite>.</p>
<p>The agent has no idea what parameters format a module expects. And different modules have different parameters. From the point of view of the agent, module parameters are treated as an <cite>interface{}</cite>, such that the content of the interface doesn't matter to the agent, as long as it is valid JSON (this requirement is enforced by the database).</p>
<p>For more details on the <cite>action</cite> and <cite>command</cite> formats used by MIG, read <ahref="concepts.rst">Concepts & Internal Components</a>.</p>
<p>The JSON sample below show an action that calls the <cite>example</cite> module. The</p>
<p>The content of the <cite>parameters</cite> field is passed <cite>Run()</cite> as an array of bytes. Inside the module, <cite>Run()</cite> unmarshals and validates the parameters into its internal format.</p>
<pre><codeclass="go"><spanclass="c1">// Runner gives access to the exported functions and structs of the module
<li>a module must implement a <strong>Runner</strong> type and register a new instance of it as part of the init process. The name (here <cite>example</cite>) used in the call to RegisterModule must be unique. Two modules cannot share the same name, otherwise the agent will panic at runtime.</li>
<li><cite>Runner</cite> must implement two functions: <strong>Run()</strong> and <strong>ValidateParameters()</strong>.</li>
<li><cite>Run()</cite> takes a single argument: a <strong>[]byte</strong> of the encoded JSON Parameters, and returns a single string, typically a marshalled JSON string.</li>
<li>a module must have a registration name that is unique</li>
</ul>
</section>
<sectionid="use-a-module">
<h2>4Use a module</h2>
<p>To use a module, you only need to anonymously import it into the configuration of the agent. The example agent configuration at <ahref="../conf/mig-agent-conf.go.inc">conf/mig-agent-conf.go.inc</a> shows how modules need to be imported using the underscore character:</p>
<p>Additionally, the MIG console may need to import the modules as well in order to use the <cite>HasResultsPrinter</cite> interface. To do so, add the same imports into the <cite>import()</cite> section of <cite>src/mig/clients/console/console.go</cite>.</p>
</section>
<sectionid="optional-module-interfaces">
<h2>5Optional module interfaces</h2>
<sectionid="hasresultsprinter">
<h3>5.1HasResultsPrinter</h3>
<p><cite>HasResultsPrinter</cite> is an interface used to allow a module <cite>Runner</cite> to implement the <strong>PrintResults()</strong> function. <cite>PrintResults()</cite> can be used to return the results of a module as an array of string, for pretty display in the MIG Console.</p>
<p><cite>HasParamsCreator</cite> can be implemented by a module to provide interactive parameters creation in the MIG Console. It doesn't accept any input value, but prompts the user for the correct parameters, and returns a Parameters structure back to the caller. It can be implemented in various ways, as long as it prompt the user in the terminal using something like <cite>fmt.Scanln()</cite>.</p>