Code snippets
Some code examples to ease the life of module developers
Hostname FQDN
Retrieve the fully qualified domain name of the server by using the agent Python library. In case of exception the function outputs the default FQDN myserver.example.org.
Python example code:
import agent
fqdn = agent.get_hostname()
print(fqdn)
output example:
foo.domain.com
Port validation
Check if the TCP port is already used by a service. Exit with an error if the port is used.
import sys
import agent
port=80
if agent.tcp_port_in_use(port):
sys.exit(1)
Web route validation
Check if HTTP routes are handled by Traefik. The function issues an HTTP request to 127.0.0.1 port 80, setting the Host header in the HTTP request and/or the URL path. Any HTTP non-404 response assumes the HTTP route exists.
Test if a domain-based route is used (domain.com)
import sys
import agent
hostname = 'foo.com'
if agent.http_route_in_use(domain=hostname):
sys.exit(2)
Test if a path-based route is used (/path)
import sys
import agent
path ='/path' # path fragment of the URL to check
if agent.http_route_in_use(domain=none, path=path):
sys.exit(2)
Atomic file write
Write a file so that readers never see partial contents, even if the
process crashes or the system loses power during the write. The
agent.safe_open() context manager writes to a temporary file in the
same directory as the target, flushes it to disk, then atomically
replaces the target and syncs the directory.
import agent
with agent.safe_open("myapp.conf", "w") as fo:
fo.write("key=value\n")
- Only write modes, like
worwb, are supported; read and append modes raiseValueError. Theencodingargument applies to text modes. - If the target file exists, its permission bits are copied to the new
file. Otherwise the new file is created with mode
0666minus the process umask. - If the
withblock raises an exception, the target file is left untouched and the temporary file is removed.
The agent library itself uses safe_open() to write the environment
file in agent.set_env() and agent.write_envfile(). The core uses it
also to generate the HAProxy configuration of the rclone backup gateway
(rclonegwctl).
safe_open() does not serialize concurrent writers: the last one wins.
For read-modify-write cycles, wrap the whole cycle with the
agent.exclusive_file_lock() context manager, as agent.set_env()
does:
import agent
with agent.exclusive_file_lock("myapp.conf"):
with open("myapp.conf") as fi:
data = fi.read()
with agent.safe_open("myapp.conf", "w") as fo:
fo.write(data + "other=value\n")
The lock is an advisory flock() on a hidden .<filename>.lock file in
the same directory, so it is effective only among processes that use
the same function.
Extra python libraries
If the module requires extra Python libraries, you can install them inside the create-module action.
Here’s an example of a create-module action step named 20initialize:
#!/bin/bash
pip install bcrypt==4.1.2
You can then use the imported bcrypt library in all your action steps. A similar script can be added under update-module.d/.