<trclass="field-odd field"><thclass="field-name">Authors:</th><tdclass="field-body">Brian C. Lane <<aclass="reference external"href="mailto:bcl%40redhat.com">bcl<span>@</span>redhat<span>.</span>com</a>></td>
</tr>
</tbody>
</table>
<p><codeclass="docutils literal notranslate"><spanclass="pre">lorax-composer</span></code> is an API server that allows you to build disk images using
<aclass="reference internal"href="#blueprints">Blueprints</a> to describe the package versions to be installed into the image.
It is compatible with the Weldr project’s bdcs-api REST protocol. More
information on Weldr can be found <aclass="reference external"href="http://www.weldr.io">on the Weldr blog</a>.</p>
<p>Behind the scenes it uses <aclass="reference external"href="livemedia-creator.html">livemedia-creator</a> and
<aclass="reference external"href="https://anaconda-installer.readthedocs.io/en/latest/">Anaconda</a> to handle the
installation and configuration of the images.</p>
<divclass="section"id="installation">
<h2>Installation<aclass="headerlink"href="#installation"title="Permalink to this headline">¶</a></h2>
<p>The best way to install <codeclass="docutils literal notranslate"><spanclass="pre">lorax-composer</span></code> is to use <codeclass="docutils literal notranslate"><spanclass="pre">sudo</span><spanclass="pre">dnf</span><spanclass="pre">install</span>
<spanclass="pre">lorax-composer</span><spanclass="pre">composer-cli</span></code>, this will setup the weldr user and install the
systemd socket activation service. You will then need to enable it with <codeclass="docutils literal notranslate"><spanclass="pre">sudo</span>
<spanclass="pre">systemctl</span><spanclass="pre">start</span><spanclass="pre">lorax-composer.socket</span></code>. This will leave the server off until
the first request is made. Systemd will then launch the server and it will
remain running until the system is rebooted.</p>
</div>
<divclass="section"id="quickstart">
<h2>Quickstart<aclass="headerlink"href="#quickstart"title="Permalink to this headline">¶</a></h2>
<olclass="arabic simple">
<li>Create a <codeclass="docutils literal notranslate"><spanclass="pre">weldr</span></code> user and group by running <codeclass="docutils literal notranslate"><spanclass="pre">useradd</span><spanclass="pre">weldr</span></code></li>
<li>Remove any pre-existing socket directory with <codeclass="docutils literal notranslate"><spanclass="pre">rm</span><spanclass="pre">-rf</span><spanclass="pre">/run/weldr/</span></code>
A new directory with correct permissions will be created the first time the server runs.</li>
<li>Enable the socket activation with <codeclass="docutils literal notranslate"><spanclass="pre">systemctl</span><spanclass="pre">enable</span><spanclass="pre">lorax-composer.socket</span>
run it directly with <codeclass="docutils literal notranslate"><spanclass="pre">lorax-composer</span><spanclass="pre">/path/to/blueprints/</span></code></li>
</ol>
<p>The <codeclass="docutils literal notranslate"><spanclass="pre">/path/to/blueprints/</span></code> directory is where the blueprints’ git repo will
be created, and all the blueprints created with the <codeclass="docutils literal notranslate"><spanclass="pre">/api/v0/blueprints/new</span></code>
route will be stored. If there are blueprint <codeclass="docutils literal notranslate"><spanclass="pre">.toml</span></code> files in the top level
of the directory they will be imported into the blueprint git storage when
<h2>Logs<aclass="headerlink"href="#logs"title="Permalink to this headline">¶</a></h2>
<p>Logs are stored under <codeclass="docutils literal notranslate"><spanclass="pre">/var/log/lorax-composer/</span></code> and include all console
messages as well as extra debugging info and API requests.</p>
</div>
<divclass="section"id="security">
<h2>Security<aclass="headerlink"href="#security"title="Permalink to this headline">¶</a></h2>
<p>Some security related issues that you should be aware of before running <codeclass="docutils literal notranslate"><spanclass="pre">lorax-composer</span></code>:</p>
<ulclass="simple">
<li>One of the API server threads needs to retain root privileges in order to run Anaconda.</li>
<li>SELinux must be set to Permissive or disabled to allow <codeclass="docutils literal notranslate"><spanclass="pre">livemedia-creator</span></code> to run Anaconda.</li>
<li>Only allow authorized users access to the <codeclass="docutils literal notranslate"><spanclass="pre">weldr</span></code> group and socket.</li>
</ul>
<p>Since Anaconda kickstarts are used there is the possibility that a user could
inject commands into a blueprint that would result in the kickstart executing
arbitrary code on the host. Only authorized users should be allowed to build
images using <codeclass="docutils literal notranslate"><spanclass="pre">lorax-composer</span></code>.</p>
<td>Set proxy for DNF, overrides configuration file setting.</td></tr>
</tbody>
</table>
</div>
</div>
<divclass="section"id="how-it-works">
<h2>How it Works<aclass="headerlink"href="#how-it-works"title="Permalink to this headline">¶</a></h2>
<p>The server runs as root, and as <codeclass="docutils literal notranslate"><spanclass="pre">weldr</span></code>. Communication with it is via a unix
domain socket (<codeclass="docutils literal notranslate"><spanclass="pre">/run/weldr/api.socket</span></code> by default). The directory and socket
are owned by <codeclass="docutils literal notranslate"><spanclass="pre">root:weldr</span></code> so that any user in the <codeclass="docutils literal notranslate"><spanclass="pre">weldr</span></code> group can use the API
to control <codeclass="docutils literal notranslate"><spanclass="pre">lorax-composer</span></code>.</p>
<p>At startup the server will check for the correct permissions and
ownership of a pre-existing directory, or it will create a new one if it
doesn’t exist. The socket path and group owner’s name can be changed from the
cmdline by passing it the <codeclass="docutils literal notranslate"><spanclass="pre">--socket</span></code> and <codeclass="docutils literal notranslate"><spanclass="pre">--group</span></code> arguments.</p>
<p>It will then drop root privileges for the API thread and run as the <codeclass="docutils literal notranslate"><spanclass="pre">weldr</span></code>
user. The queue and compose thread still runs as root because it needs to be
able to mount/umount files and run Anaconda.</p>
</div>
<divclass="section"id="composing-images">
<h2>Composing Images<aclass="headerlink"href="#composing-images"title="Permalink to this headline">¶</a></h2>
<p>The <aclass="reference external"href="https://github.com/weldr/welder-web/">welder-web</a> GUI project can be used to construct
blueprints and create composes using a web browser.</p>
<p>Or use the command line with <aclass="reference external"href="composer-cli.html">composer-cli</a>.</p>
</div>
<divclass="section"id="blueprints">
<h2>Blueprints<aclass="headerlink"href="#blueprints"title="Permalink to this headline">¶</a></h2>
<p>Blueprints are simple text files in <aclass="reference external"href="https://github.com/toml-lang/toml">TOML</a> format that describe
which packages, and what versions, to install into the image. They can also define a limited set
of customizations to make to the final image.</p>
<p>Example blueprints can be found in the <codeclass="docutils literal notranslate"><spanclass="pre">lorax-composer</span></code><aclass="reference external"href="https://github.com/weldr/lorax/tree/master/tests/pylorax/blueprints/">test suite</a>, with a simple one
<p>The <codeclass="docutils literal notranslate"><spanclass="pre">name</span></code> field is the name of the blueprint. It can contain spaces, but they will be converted to <codeclass="docutils literal notranslate"><spanclass="pre">-</span></code>
when it is written to disk. It should be short and descriptive.</p>
<p><codeclass="docutils literal notranslate"><spanclass="pre">description</span></code> can be a longer description of the blueprint, it is only used for display purposes.</p>
<p><codeclass="docutils literal notranslate"><spanclass="pre">version</span></code> is a <aclass="reference external"href="https://semver.org/">semver compatible</a> version number. If
a new blueprint is uploaded with the same <codeclass="docutils literal notranslate"><spanclass="pre">version</span></code> the server will
automatically bump the PATCH level of the <codeclass="docutils literal notranslate"><spanclass="pre">version</span></code>. If the <codeclass="docutils literal notranslate"><spanclass="pre">version</span></code>
doesn’t match it will be used as is. eg. Uploading a blueprint with <codeclass="docutils literal notranslate"><spanclass="pre">version</span></code> set to <codeclass="docutils literal notranslate"><spanclass="pre">0.0.1</span></code> when the existing blueprint <codeclass="docutils literal notranslate"><spanclass="pre">version</span></code> is <codeclass="docutils literal notranslate"><spanclass="pre">0.0.1</span></code> will result in the new blueprint being stored as <codeclass="docutils literal notranslate"><spanclass="pre">version</span><spanclass="pre">0.0.1</span></code>.</p>
<divclass="section"id="packages-and-modules">
<h3>[[packages]] and [[modules]]<aclass="headerlink"href="#packages-and-modules"title="Permalink to this headline">¶</a></h3>
<p>These entries describe the package names and matching version glob to be installed into the image.</p>
<p>The names must match the names exactly, and the versions can be an exact match
or a filesystem-like glob of the version using <codeclass="docutils literal notranslate"><spanclass="pre">*</span></code> wildcards and <codeclass="docutils literal notranslate"><spanclass="pre">?</span></code>
character matching.</p>
<p>NOTE: As of lorax-composer-29.2-1 the versions are not used for depsolving,
that is planned for a future release. And currently there are no differences
between <codeclass="docutils literal notranslate"><spanclass="pre">packages</span></code> and <codeclass="docutils literal notranslate"><spanclass="pre">modules</span></code> in <codeclass="docutils literal notranslate"><spanclass="pre">lorax-composer</span></code>.</p>
</div>
<divclass="section"id="customizations">
<h3>Customizations<aclass="headerlink"href="#customizations"title="Permalink to this headline">¶</a></h3>
<p>The <codeclass="docutils literal notranslate"><spanclass="pre">[[customizations]]</span></code> section can be used to configure the hostname of the final image. eg.:</p>
<p>The key will be added to the user’s authorized_keys file.</p>
</div>
<divclass="section"id="customizations-user">
<h4>[[customizations.user]]<aclass="headerlink"href="#customizations-user"title="Permalink to this headline">¶</a></h4>
<p>Add a user to the image, and/or set their ssh key.
All fields for this section are optional except for the <codeclass="docutils literal notranslate"><spanclass="pre">name</span></code>, here is a complete example:</p>
<p>If the password starts with <codeclass="docutils literal notranslate"><spanclass="pre">$6$</span></code>, <codeclass="docutils literal notranslate"><spanclass="pre">$5$</span></code>, or <codeclass="docutils literal notranslate"><spanclass="pre">$2b$</span></code> it will be stored as
an encrypted password. Otherwise it will be treated as a plain text password.</p>
</div>
<divclass="section"id="customizations-group">
<h4>[[customizations.group]]<aclass="headerlink"href="#customizations-group"title="Permalink to this headline">¶</a></h4>
<p>Add a group to the image. <codeclass="docutils literal notranslate"><spanclass="pre">name</span></code> is required and <codeclass="docutils literal notranslate"><spanclass="pre">gid</span></code> is optional:</p>
<h2>Adding Output Types<aclass="headerlink"href="#adding-output-types"title="Permalink to this headline">¶</a></h2>
<p><codeclass="docutils literal notranslate"><spanclass="pre">livemedia-creator</span></code> supports a large number of output types, and only some of
these are currently available via <codeclass="docutils literal notranslate"><spanclass="pre">lorax-composer</span></code>. To add a new output type to
lorax-composer a kickstart file needs to be added to <codeclass="docutils literal notranslate"><spanclass="pre">./share/composer/</span></code>. The
name of the kickstart is what will be used by the <codeclass="docutils literal notranslate"><spanclass="pre">/compose/types</span></code> route, and the
<codeclass="docutils literal notranslate"><spanclass="pre">compose_type</span></code> field of the POST to start a compose. It also needs to have
code added to the <aclass="reference internal"href="pylorax.api.html#pylorax.api.compose.compose_args"title="pylorax.api.compose.compose_args"><codeclass="xref py py-func docutils literal notranslate"><spanclass="pre">pylorax.api.compose.compose_args()</span></code></a> function. The
<codeclass="docutils literal notranslate"><spanclass="pre">_MAP</span></code> entry in this function defines what lorax-composer will pass to
<aclass="reference internal"href="pylorax.html#pylorax.installer.novirt_install"title="pylorax.installer.novirt_install"><codeclass="xref py py-func docutils literal notranslate"><spanclass="pre">pylorax.installer.novirt_install()</span></code></a> when it runs the compose. When the
compose is finished the output files need to be copied out of the build
<aclass="reference internal"href="pylorax.api.html#pylorax.api.compose.move_compose_results"title="pylorax.api.compose.move_compose_results"><codeclass="xref py py-func docutils literal notranslate"><spanclass="pre">pylorax.api.compose.move_compose_results()</span></code></a> handles this for each type.
You should move them instead of copying to save space.</p>
<p>If the new output type does not have support in livemedia-creator it should be
added there first. This will make the output available to the widest number of
<h3>Example: Add partitioned disk support<aclass="headerlink"href="#example-add-partitioned-disk-support"title="Permalink to this headline">¶</a></h3>
<p>Partitioned disk support is something that livemedia-creator already supports
via the <codeclass="docutils literal notranslate"><spanclass="pre">--make-disk</span></code> cmdline argument. To add this to lorax-composer it
needs 3 things:</p>
<ulclass="simple">
<li>A <codeclass="docutils literal notranslate"><spanclass="pre">partitioned-disk.ks</span></code> file in <codeclass="docutils literal notranslate"><spanclass="pre">./share/composer/</span></code></li>
<li>A new entry in the _MAP in <aclass="reference internal"href="pylorax.api.html#pylorax.api.compose.compose_args"title="pylorax.api.compose.compose_args"><codeclass="xref py py-func docutils literal notranslate"><spanclass="pre">pylorax.api.compose.compose_args()</span></code></a></li>
<li>Add a bit of code to <aclass="reference internal"href="pylorax.api.html#pylorax.api.compose.move_compose_results"title="pylorax.api.compose.move_compose_results"><codeclass="xref py py-func docutils literal notranslate"><spanclass="pre">pylorax.api.compose.move_compose_results()</span></code></a> to move the disk image from
the compose directory to the results directory.</li>
</ul>
<p>The <codeclass="docutils literal notranslate"><spanclass="pre">partitioned-disk.ks</span></code> is pretty similar to the example minimal kickstart
in <codeclass="docutils literal notranslate"><spanclass="pre">./docs/fedora-minimal.ks</span></code>. You should remove the <codeclass="docutils literal notranslate"><spanclass="pre">url</span></code> and <codeclass="docutils literal notranslate"><spanclass="pre">repo</span></code>
commands, they will be added by the compose process. Make sure the bootloader
packages are included in the <codeclass="docutils literal notranslate"><spanclass="pre">%packages</span></code> section at the end of the kickstart,
and you will want to leave off the <codeclass="docutils literal notranslate"><spanclass="pre">%end</span></code> so that the compose can append the
list of packages from the blueprint.</p>
<p>The new <codeclass="docutils literal notranslate"><spanclass="pre">_MAP</span></code> entry should be a copy of one of the existing entries, but with <codeclass="docutils literal notranslate"><spanclass="pre">make_disk</span></code> set
to <codeclass="docutils literal notranslate"><spanclass="pre">True</span></code>. Make sure that none of the other <codeclass="docutils literal notranslate"><spanclass="pre">make_*</span></code> options are <codeclass="docutils literal notranslate"><spanclass="pre">True</span></code>. The <codeclass="docutils literal notranslate"><spanclass="pre">image_name</span></code> is
what the name of the final image will be.</p>
<p><codeclass="docutils literal notranslate"><spanclass="pre">move_compose_results()</span></code> can be as simple as moving the output file into
the results directory, or it could do some post-processing on it. The end of
the function should always clean up the <codeclass="docutils literal notranslate"><spanclass="pre">./compose/</span></code> directory, removing any
unneeded extra files. This is especially true for the <codeclass="docutils literal notranslate"><spanclass="pre">live-iso</span></code> since it produces
the contents of the iso as well as the boot.iso itself.</p>
Built with <ahref="http://sphinx-doc.org/">Sphinx</a> using a <ahref="https://github.com/rtfd/sphinx_rtd_theme">theme</a> provided by <ahref="https://readthedocs.org">Read the Docs</a>.