212 lines
6.4 KiB
Markdown
212 lines
6.4 KiB
Markdown
+++
|
|
title = "Specification file format"
|
|
|
|
paginate_by = 5
|
|
+++
|
|
|
|
Configuration should be done with a simple file format.
|
|
Here is the file format `BaguetteOS` uses for its tools: `spec`.
|
|
|
|
<a name="top"></a>
|
|
|
|
- [Overview](#overview)
|
|
- [Real-life examples](#examples)
|
|
- [In packaging](#example-packaging)
|
|
- [In service](#example-service)
|
|
- [Rationale](#rationale)
|
|
|
|
<a name="overview"></a>
|
|
# Overview of the `spec` file format
|
|
|
|
```yaml
|
|
# This is a comment
|
|
|
|
# This is a simple variable instanciation
|
|
my-variable: value
|
|
|
|
# This is an inlined list
|
|
my-list: a, b, c
|
|
|
|
# This is a multiline list
|
|
my-list:
|
|
- a
|
|
- b
|
|
- c
|
|
|
|
# Interpolation is performed with %{variable}
|
|
url: https://example.com/
|
|
software-url: %{url}/my-software-v0.2.tar.gz
|
|
|
|
|
|
# "@an-example-of-code-block" is a code block.
|
|
@an-example-of-code-block
|
|
# Code blocks allow inserting simple shell scripting in the configuration file.
|
|
curl %{url} -o- | \
|
|
sed "s/_my-software.tar.gz//" | \
|
|
tail -1
|
|
# The scope is the indentation.
|
|
|
|
# Not the same indentation: not in the block anymore.
|
|
|
|
# "%an-example-of-section" is a section, allowing to add metadata to a "target".
|
|
# "the-target" is the target, receiving metadata.
|
|
%an-example-of-section the-target
|
|
# Within a section, variables are declared in the same way as outside the block.
|
|
name: The name of the target.
|
|
list-of-random-info-about-the-target:
|
|
- a
|
|
- b
|
|
- c
|
|
```
|
|
|
|
<a name="examples"></a>
|
|
# Real-life examples
|
|
|
|
<a name="example-packaging"></a>
|
|
## In `packaging`
|
|
|
|
Most of the `recipes` used to build packages with [packaging][packaging] looks like this:
|
|
|
|
```YAML
|
|
name: my-program
|
|
description: "my program is a tool to do blah"
|
|
version: 0.5.2
|
|
sources: https://example.com/software-v%{version}.tar.gz
|
|
```
|
|
|
|
|
|
From time to time, an application requires non-standard operation in its build-system, so a `code block` is used.
|
|
`@configure` code blocks allow to change the execution of the `configure` script in a package build.
|
|
Without it, `packaging` simply performs a `./configure` with options provided in the recipe.
|
|
In the next example, a file (`some-file`) needs to be changed before using the `configure` script.
|
|
|
|
```YAML
|
|
# "@configure" is a code block in the recipes of `packaging`.
|
|
@configure
|
|
sed -i "s/a/b/" some-file
|
|
./configure
|
|
```
|
|
In this example, `sed` is called before `./configure`, and this could have been as complex as required by the build system of the application.
|
|
Having this code block allows to package applications with non-standard build system, while keeping recipes as simple as possible when applications do use standard build systems.
|
|
|
|
<a name="example-service"></a>
|
|
## In `service`
|
|
|
|
A service description can be as simple as providing the name of the service, the command to run it and the list of tokens the service provides or consumes.
|
|
For example, the `pubsubd` service:
|
|
|
|
```YAML
|
|
name: pubsubd
|
|
environment-variables:
|
|
- LD_LIBRARY_PATH=/usr/local/lib
|
|
command: pubsubd -s /srv/${ENVIRONMENT}/pubsub
|
|
provides: pubsub
|
|
```
|
|
|
|
Additionaly, before running the service, the service may require the creation of directories (or files).
|
|
In this case, sections are used to describe the file to create.
|
|
In the next example,
|
|
|
|
```YAML
|
|
# "%configuration" is a section and its "target" ("nginx.conf" in this example ) is
|
|
# the name of both the template to use and the generated file.
|
|
%configuration nginx.conf
|
|
# Within a section, variables are declared in the same way as outside the block.
|
|
name: This is the configuration file for Nginx.
|
|
permissions: 0700
|
|
```
|
|
|
|
Another example is the `%directory` section.
|
|
|
|
```YAML
|
|
# "%directory" is a section, the target is the name of the directory.
|
|
%directory %{SERVICE_ROOT}/data
|
|
name: Storage directory for pubsubd.
|
|
# A command may be provided to know how to create the directory and its content.
|
|
command: install -d -m6777 %{SERVICE_ROOT}/data
|
|
```
|
|
|
|
|
|
<a name="rationale"></a>
|
|
# Rationale
|
|
|
|
## `spec` is *declarative* <side-note>*describe what is, not how*</side-note>
|
|
|
|
Configuration should be about <u>describing what something is</u>.
|
|
For example, a web server should ask the domains it should serve.
|
|
|
|
## `spec` can be *imperative* too <side-note>*for complex cases, where it is not possible to do differently*</side-note>
|
|
|
|
<u>How</u> things are done is an **implementation detail**.
|
|
However, in complex cases the user may provide <u>optional</u> informations on *how* to do things.
|
|
|
|
Imperative configuration is useful for example in [packaging][packaging]: some applications require specific non-generic operations to be packaged.
|
|
In those cases, it is not a reasonable solution to try to implement every possible - and changing - way of packaging an application directly in `packaging`.
|
|
Instead, each application with a non-standard build system has a code block in its recipe where every non-standard operation is performed.
|
|
|
|
## `spec` is concise and simple <side-note>variables, lists, code blocks and sections</side-note>
|
|
|
|
Variables and lists look like YAML (and markdown), which is one of the simpliest configuration possible.
|
|
It is humanly readable, unlike XML and its bloated-beyond-repair syntax, and it is simpler to read than JSON.
|
|
|
|
XML <side-note>(XML Schema could be used too, to create simpler lists *after the definition of the schema*, but seriously… if documentation is needed to create a simple fucking list, this is just fucked up)</side-note>
|
|
```XML
|
|
<the-property>
|
|
the value
|
|
</the-property>
|
|
<a-list>
|
|
<element>
|
|
value
|
|
</element>
|
|
<element>
|
|
value
|
|
</element>
|
|
<element>
|
|
value
|
|
</element>
|
|
</a-list>
|
|
```
|
|
|
|
JSON
|
|
```JSON
|
|
{
|
|
"the-property": "the value",
|
|
"a-list": [ "value", "value", "value" ]
|
|
}
|
|
```
|
|
|
|
YAML
|
|
```YAML
|
|
the-property: the value
|
|
a-list: value, value, value
|
|
```
|
|
|
|
`code blocks` allow to insert `shell scripting`, because sometimes we <u>do</u> want to tell instructions and it is not available in YAML or JSON.
|
|
|
|
Finally, `sections` allow a more concise configuration by factoring simple patterns.
|
|
|
|
```YAML
|
|
# Without sections
|
|
list-of-directories:
|
|
this-directory:
|
|
name: Storage directory.
|
|
path: /path/to/dir
|
|
|
|
# With sections
|
|
%directory /path/to/dir
|
|
name: Storage directory.
|
|
```
|
|
|
|
|
|
## `spec` is simple for developers too
|
|
|
|
The `spec` file format can be read line-by-line, which is very simple to implement.
|
|
|
|
The specification file format is used in practice in [packaging][packaging] and [service][service].
|
|
|
|
[Back to top](#top)
|
|
|
|
[service]: https://git.baguette.netlib.re/Baguette/service
|
|
[package]: https://git.baguette.netlib.re/Baguette/package
|
|
[packaging]: https://git.baguette.netlib.re/Baguette/packaging
|