$darkmode
Elektra 0.11.0
|
In this tutorial you will learn how to interactively use the SpecElektra
specification language and kdb
to write a configuration specification for an example application.
kdb get
, kdb set
, kdb ls
)kdb
kdb
to generate a specification, instead of writing one by handkdb
to create and mount a specification for an example CRUD (Create, Read, Update, Delete) applicationFor this tutorial you will write a specification for a simple CRUD backend application. You need to configure a port
and a secure
property, that toggles SSL usage, for the REST server. An ip
and a SQL dialect
for the database server the app will connect to and finally a date
where all the data will be saved to a backup.
So the application will need the following configuration options:
Make sure you have Elektra
installed on your local machine:
Otherwise refer to the getting started guide to install it.
First you need to mount a specification file, in this case spec.ni
to the spec:/
namespace. You can define the path inside the spec:/
namespace as /tests/sw/org/app/#0/current
, refer to the documentation to find out more about constructing the name.
You will be using the profile current
, you can find out more about profiles in the documentation as well.
We will be writing values mostly to the user:/
namespace. If you want to learn more about namespaces in general, refer to the the documentation on namespaces
You also need to specify the plugin you will use for writing to the file in the correct format. In this case you can choose the ni
plugin to write to the specification file.
**_Attention_**: Mounting the specification by supplying an absolute path (like in the previous example with
`pwd`
) is only recommended for defining the specification in the first place. It is not recommended when mounting the final specification for usage with the application, especially not in production environments!Please read the section Using the specification at the end of this document for further information.
Using the command below you can get the location of the concrete file that is used by Elektra.
Next you can define, that this specification uses a specific mountpoint for a concrete application configuration. So you can say the concrete configuration should be written to app.ni
.
Your spec.ni
file should now look something like this:
This specification mount makes sure that the paths where the concrete configuration should be (app.ni
) are ready to fulfill our specification (spec.ni
). Be aware that different files get mounted for different namespaces. You've a specification file (spec.ni
) for the spec
-namespace and three files (app.ni
) on different locations for the dir
- user
- and system
-namespaces.
You can see the files by providing the namespace as prefix to the kdb file
command:
**_Note_**: The files only exist, when configuration values are stored there, i.e. they are created on the first
kdb set
and removed with the lastkdb rm
.
For more information about namespaces in Elektra please see here, a tutorial about the topic is available here.
The first key you will add to your specification is the port of the server. You add it by using the following command:
What you also specified in the command above is the type of the configuration value. Elektra uses the CORBA type system and will check if values conform to the type specified.
So after adding the initial key, your specification should look something like this:
So with your first key added, you of course want to specify more information for the port. There surely is more information to a port than just the type. What about a default
, or what about an example
for a usable port? Maybe a description
what the port really is for? Let's add that next!
Beautiful! Your specification is starting to look like something useful. But wait! Shouldn't a port just use values between 1
and 65535
?
Of course Elektra also has a plugin for that. You can just use the network checker plugin.
Now we define the plugins that we want to load. In our example we need the ni
-Plugin for reading and writing the configuration files, the type
-plugin for validating data types and the network
-plugin for validating port numbers.
Nice! You just have to do one more thing when using a new plugin. Elektra needs to remount the spec to use the new plugin. Use the command from before:
Your final specification after adding the port should now look something like this:
You can now try to read the value of the newly created configuration. Since you did not set the value to anything yet, you will get the default value back.
Try to set the port to 65536
now.
Did it work? I hope not. The validation plugins you specified will now correctly validate the port you enter and give you an error.
In this example, the network
plugin and the type
plugin are emitting warnings or errors, because the valid ranges for port
and unsigned_short
are the same.
Next up you will configure the secure
property of our server. This boolean key will toggle if your server encrypts the communication via SSL.
So we will add the key and some metadata for it:
By default the type
plugin will normalize boolean values when setting them, before storing them. This only works for the concrete config, so when setting the values for the spec you have to use the unnormalized values. In the case it uses 1
for boolean true
and 0
for boolean false
.
Since the key /sw/org/app/\#0/current/server/secure
has a default value of 1
, we are able to retrieve the default value from the key database:
You can read more about this in the documentation for the type plugin.
Next up you will add a key for the database ip
address. Like with the key before, you will add a type
, default
, example
and a description
so that the configuration will be easily usable.
Don't forget the most important rule of configurations: Always add sensible defaults!
Now let's try something different. What if you change the file manually? Will Elektra pick up on the changes? And save you from writing a lot of kdb
commands?
of course
So just open your file using good old vim
and add the following lines to specify configuration for the ip
address.
Alternatively you can of course use kdb
again to set the configuration values that way. Here are the commands to do that.
Next up you will add a key for the SQL dialect
the database will use. Since there are only a few databases your application will support, you can define the possible dialects via an enum type. This allows us to prohibit all other possible dialects that are not SQL.
First you define the size of the enum
type, and then you can add the different enum
values.
Afterwards you define all the other parameters, just as before.
After this meta-setting bonanza your specification file should look something like this:
The last key you will add to our specification is a date
key for the annual backup and restart (this should probably not be annually in a real application). Here you use the check/date plugin with the ISO8601
format. You also specify a check/date/format
. You can find all possible date formats on the plugin page. For this you can use the following commands:
Then just add examples, defaults and description as always.
Now we add the validation plugin for dates and remount the specification:
If we try to add a value that is not in the specified format, an error should get emitted.
To double-check if things are correct, we try to get the value from the user
-namespace and via cascading lookup.
As expected, no value was written to the user
-namespace and a cascading lookup returns the date that was given as default value in the specification. If we now use the correct format, the new date should be stored in the user
-namespace and retrieved with both kdb get
lookups.
If we explicitly query the system
-namespace, no key is found.
Your specification should be complete now! After adding all the keys that are necessary for our application, your specification should look something like this:
Now, after you've finished your specification and want to use it for daily business, some aspects have to be considered. A specification usually is written to cover the most common use cases and to provide sensible defaults. In some cases, the administrator may want to change the specification, e.g. for extending it or introducing further restrictions.
In such cases, directly changing the specification that was designed for and delivered with the application, is not the recommended approach.
The recommended way is making a copy of the default specification while installing the application and then saving changes to that copy. If misconfiguration occurs, you can easily look at the default specification or reapply it to start over.
If you mount the specification with an absolute path (e.g. by using `pwd`
in scripts), like it was done for defining the specification with kdb
, the file gets changed directly. If `pwd`
refers to the installation directory of the application, this would be totally fine, but if this is done during development and `pwd`
refers to the source directory, the source specification would be modified via kdb set spec:/...
calls.
First we have to unmount our original configuration file we just created:
The recommended way to apply the specification is:
Because we used a relative path (not starting with /
), Elektra will use a file within the spec
directory (/usr/share/elektra/specification
by default) for storing the specification.
The kdb import
command just tells Elektra to load the file ./spec.ni
with the ni
plugin and write it into spec:/tests/sw/org/app/\#0/current
. Using a separate kdb import
also means we could use a different storage plugin for mounting than what ./spec.ni
uses.
Another alternative is copying the file manually:
This works like the previous snippet, except that we directly modify the spec file without going through Elektra. For very big specifications this might be faster, but we get no validation that the file is actually readable and ./spec.ni
has to be readable by the storage plugin used with kdb mount
.
Finally, in some cases you may want to control where the mounted spec file is stored (e.g. if you are updating a legacy application that has an existing configuration directory). In those cases using an absolute path is fine:
or
Please note that also in these examples, the file referred to by the absolute path /etc/spec.ni
is a new file where the content of the file ./spec.ni
in the working directory gets imported or copied to.
If you want to remove the files that were created in the course of the tutorial, the following steps are necessary.
If you take a look at kdb mount
, you'll see that there are currently two mountpoints open.
Mountpoints are meant to mount (external) files into the key database structure of Elektra. This mechanism is similar to mount
on Linux: changes made to the key database will be written to the underlying mounted file. If you want to learn more on mounting and mountpoints in Eletra, refer to the documentation.
To round up this tutorial, we will kdb umount
these two mountpoints:
In case something went wrong and you want to reset the whole content of your kdb, please refer to the man page of kdb reset.
kdb mount
and kdb spec-mount
.kdb meta-set
.type string
, type boolean
or type unsigned_short
.enum types
, to restrict specific configuration settings to a defined set of possible values.example
, default
, description
.check/port
or check/date
.