Scripting¶
All deployments have the ability to execute scripts once a node becomes available.
Before starting¶
A couple of things to keep in mind:
- All scripts are executed in order based on the filenames. It is recommended to create scripts with a numbered prefix, for example:
- There is a special reserved filename
teardown
, if this file exists it will only be executed during a removal of a node. This is useful for any cleanup actions that may need to be run, such as removing test users, un-enrolling from a service, etc.
Writing scripts¶
Scripts can be written in any language, it is up to you to configure the nodes so that any runtimes and library dependencies are met on the target node for your script to execute in.
One way to accomplish this is to create 01-setup-env
bash script:
#!/bin/bash
echo "Installing python3 on ubuntu"
sudo apt-get update
sudo apt-get install -qyf python3
sudo pip install sh
Then in subsequent scripts, using python3 is available. For example, in file 02-run-cmd-in-python
:
Templating¶
OGC provides some additional capabilities through templating. Under the hood python-mako is used for the parsing.
With templating, you have the ability to query the underlying database to gather node information, a couple of modules are already exposed in the templates context:
Var | Description |
---|---|
nodes | Nodes available |
node | Current deployed node metadata |
env | Environment variables are made available through this key, env['USER'] |
#!/bin/bash
<%! from ogc.templatetags import run, header, hr %>
<%namespace name="utils" file="/functions.mako"/>
${header('Connection information')}
echo "id: ${node.instance_id}"
echo "name: ${node.instance_name}"
echo "connection: ${node.layout.username}@${node.public_ip}"
echo "provider ${node.layout.provider}"
${hr()}
${run('ls', '/', l=True, h=True)}
${header('All nodes')}
% for obj in nodes:
echo "id: ${obj.instance_id}"
echo "name: ${obj.instance_name}"
echo "connection: ${obj.layout.username}@${obj.public_ip}"
echo "provider ${obj.layout.provider}"
% endfor
${header('All nodes finished')}
${run('mkdir', node.layout.remote_path + "/output", p=True)} && \
${run('touch', node.layout.remote_path + "/output/test.xml")}
The runtime environment is also available within the template context.
About Environment Variables
Any environment variables exported within OGC will be exposed in the templates.
Reusable helpers¶
In the above example we reference a file called /functions.mako
this is just another template file that sits just outside of our defined scripts
, for example, if our scripts
is defined to be in scripts/my_ubuntu_deploy
then this functions.mako
will live at scripts/functions.mako
.
Good Practice
This is good practice as you may have multiple layouts with different script directories for each and would like to store common functionality in a single place.
Defining helper functions is straight forward, lets look at functions.mako
for an example:
## Helper template functions downloading/extracting files
<%def name="setup_env()">
if ! test -f "/usr/local/bin/pacapt"; then
wget -O /usr/local/bin/pacapt https://github.com/icy/pacapt/raw/ng/pacapt
chmod 755 /usr/local/bin/pacapt
ln -sv /usr/local/bin/pacapt /usr/local/bin/pacman || true
fi
</%def>
<%def name="install_pkgs(pkgs)">
% for pkg in pkgs:
pacapt install --noconfirm ${pkg}
% endfor
</%def>
<%def name="download(url, src_file)">
wget -O ${src_file} ${url}
</%def>
<%def name="extract(src, dst=None)">
% if dst:
mkdir -p ${dst}
tar -xvf ${src} -C ${dst}
% else:
tar -xvf ${src}
% endif
</%def>
Each %def
section defines a function block that when called with any necessary arguments will output that data into the scripts with all necessary translations handled.
You can see the usage of these functions in the previous example for installing elastic-agent.
It is worth the time to visit Mako's website and learn about its feature set, particularly namespaces and defs and blocks.