Skip to content
This repository has been archived by the owner on Oct 15, 2024. It is now read-only.

Codegen: Ignore missing required keys in help mode #2944

Merged
merged 9 commits into from
Sep 16, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions doc/decisions/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ section here.
- [Elektra Web Structure](elektra_web.md)
- [Elektra Web Recursive Structure](elektra_web_recursive.md)
- [Cryptographic Key Handling](cryptograhic_key_handling.md)
- [High-level API Help Message](highlevel_help_message.md)

## Decided

Expand Down
54 changes: 54 additions & 0 deletions doc/decisions/highlevel_help_message.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# High-level API Help Message

This decision _does not_ assume code-generation is used. For the case of code-generation see the [Notes](#notes) section.

## Problem

We want to allow to print the help message no matter what errors happened in `kdbOpen` or `kdbGet`.

## Constraints

- `elektraOpen` should not return a broken `Elektra` instance.
- The help message can only be printed, if `elektraOpen` returns an `Elektra` instance and no `ElektraError`.

## Assumptions

- We assume that the application in question was correctly installed.
- We assume `gopts` was mounted.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this already default?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

gopts is not mounted by default no. However, the file generated by the highlevel template, include a kdbEnsure contract, that mounts it, if it is missing.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please copy this sentence into the decision.

- We assume the application was called in _help mode_, i.e. with `-h` or `--help`.
Otherwise printing the help message is not possible, anyway.

## Considered Alternatives

- Ignore all errors (in help mode):
Not a feasible solution, because there may have been problems when reading the storage file and therefore,
the help message may be broken or incomplete.
- Ignore all errors (in help mode), which occurred after the `gopts` plugin ran:
Complicated to implement (we need to know about plugin order, etc.).
Not actually necessary (see [Rationale](#rationale)).

## Decision

Ignore missing `require`d keys (in help mode), but fail for every other error.

## Rationale

Required keys **must** be provided by the user/admin and cannot come from another source (Elektra, app developer, etc.).
Therefore they will be missing until the user makes changes to the KDB. Before that, no other error should occur (we
assumed a correct installation). If a user runs `app` for the first time and receives an error about a missing required
key, they will:

- know what to do and add the key, thereby fixing the problem.
- try `app -h`/`app --help` to find out more. The help message may or may not contain useful information. If not they may try 3.
- read some other documentation to find out more. Ideally this leads them to 1.

In any case after this the user definitely know how to interact with the KDB. Since we assumed that there won't be any
errors before the KDB was changed, we can assume that the user caused other errors by changing the KDB.

## Notes

If code-generation is used, the situation is a little different. If the parameter `embedHelpFallback` is set to `1`, a
fallback help message will be created from the specification originally passed to the code-generator and embedded into
the application. The parameter also changes, how help mode is detected and ultimately allows the help message function
(`printHelpMessage` by default) to always print a help message. Although it may not reflect changes the user made to the
specification.
6 changes: 5 additions & 1 deletion doc/help/kdb-gen-highlevel.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,10 @@ For detailed information about the contents of the header file see [elektra-high
Changes the name of the function that checks for "specload mode" (default: `exitForSpecload`)
- `tagPrefix`:
Changes the prefix of the generated tags (default: `ELEKTRA_TAG_`)
- `embedHelpFallback`:
Switches whether a fallback help message should be embedded; allowed values: `1` (default), `0`
If enabled (`1`), a help message will be generated from the specification passed to the code-generator and embedded
into the application. This message will be used, if the normal help message could not be generated at runtime.
- `enumConv`:
Switches how enum conversion should be done; allowed values: `default` (default), `switch`, `strcmp`
- `strcmp`: uses a simple series of `if (strcmp(*, *) == 0)` to convert strings into enums
Expand All @@ -69,7 +73,7 @@ For detailed information about the contents of the header file see [elektra-high
Comma-separated (`,`) list of additional header files to include. For each of the listed headers we will generate an `#include "*"`
statement
- `genSetters`:
Switches whether setters should be generated at all; allowed values: `true` (default), `false`
Switches whether setters should be generated at all; allowed values: `1` (default), `0`
- `embeddedSpec`:
Changes how much of the specification is embedded into the application; allowed values: `full` (default), `defaults`, `none`.
see [elektra-highlevel-gen(7)](elektra-highlevel-gen.md)
Expand Down
5 changes: 4 additions & 1 deletion doc/man/man1/kdb-gen-highlevel.1
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,9 @@ For detailed information about the contents of the header file see elektra\-high
\fBtagPrefix\fR: Changes the prefix of the generated tags (default: \fBELEKTRA_TAG_\fR)
.
.IP "\(bu" 4
\fBembedHelpFallback\fR: Switches whether a fallback help message should be embedded; allowed values: \fB1\fR (default), \fB0\fR If enabled (\fB1\fR), a help message will be generated from the specification passed to the code\-generator and embedded into the application\. This message will be used, if the normal help message could not be generated at runtime\.
.
.IP "\(bu" 4
.
.IP "\(bu" 4
\fBstrcmp\fR: uses a simple series of \fBif (strcmp(*, *) == 0)\fR to convert strings into enums
Expand All @@ -97,7 +100,7 @@ For detailed information about the contents of the header file see elektra\-high
\fBheaders\fR: Comma\-separated (\fB,\fR) list of additional header files to include\. For each of the listed headers we will generate an \fB#include "*"\fR statement
.
.IP "\(bu" 4
\fBgenSetters\fR: Switches whether setters should be generated at all; allowed values: \fBtrue\fR (default), \fBfalse\fR
\fBgenSetters\fR: Switches whether setters should be generated at all; allowed values: \fB1\fR (default), \fB0\fR
.
.IP "\(bu" 4
\fBembeddedSpec\fR: Changes how much of the specification is embedded into the application; allowed values: \fBfull\fR (default), \fBdefaults\fR, \fBnone\fR\. see elektra\-highlevel\-gen(7) \fIelektra\-highlevel\-gen\.md\fR
Expand Down
9 changes: 3 additions & 6 deletions doc/man/man7/elektra-highlevel-gen.7
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,15 @@ This document focuses on the advanced features of the high\-level API code\-gene
The parameters that are relevant to the concepts described here are (for the rest see `kdb\-gen\-highlevel(1) \fIkdb\-gen\-highlevel\.md\fR):
.
.IP "\(bu" 4
\fBenumConv\fR: allowed values: \fBstrcmp\fR, \fBswitch\fR, \fBauto\fR (default)
.
.IP "\(bu" 4
\fBembeddedSpec\fR: allowed values: \fBfull\fR (default), \fBdefaults\fR, \fBnone\fR
.
.IP "\(bu" 4
\fBspecValidation\fR: allowed values: \fBnone\fR (default), \fBminimal\fR
.
.IP "" 0
.IP "\(bu" 4
\fBenumConv\fR: allowed values: \fBstrcmp\fR, \fBswitch\fR, \fBauto\fR (default)
.
.P
The \fBenumConv\fR option is described \fIbelow\fR\.
.IP "" 0
.
.P
Using \fBembeddedSpec\fR you can configure how much of the specification is embedded into your application\. By default we use \fBfull\fR\. This means the full specification is embedded into your application\'s binary\. Since this can drastically increase the size of the binary, you can also choose \fBdefaults\fR or \fBnone\fR\. The \fBdefaults\fR setting embeds a reduced version of the specification, which only contains the metadata required by \fBelektraOpen\fR\. By setting \fBembeddedSpec=none\fR you can also remove this reduced specification\.
Expand Down
6 changes: 5 additions & 1 deletion doc/news/_preparation_next_release.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ If you specifically want to use it with the High-Level API take a look at [this

We also created a new CMake function that will be available, if you include Elektra via CMake's
`find_package`. The function is called `elektra_kdb_gen` and can be used to tell CMake about files
that are generated with `kdb gen`. _(Klemens Böswirth)_
that are generated via `kdb gen`. _(Klemens Böswirth)_

### <<HIGHLIGHT2>>

Expand Down Expand Up @@ -80,6 +80,10 @@ The following section lists news about the [modules](https://www.libelektra.org/

- We now treat relative paths as relative to `KDB_DB_SPEC` instead of the current working directory. _(Klemens Böswirth)_

### Spec

- There is now the config key `missing/log` that allows logging of all missing `require`d keys. _(Klemens Böswirth)_
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't we fail if a required key is missing? Why do we need the log as this should already be part of the error message?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. spec can be configured to ignore some or all errors. I don't really know why, but it's always been the case.
  2. This is needed for the case where a program using the codegen API is called in "help mode", i.e. app -h or app --help. In such a case, we want to ignore missing required keys and simply print the help message.
    How the missing/log array helps detect this case, is documented in the relevant code:
    Key * helpKey = ksLookupByName (config, "proc/elektra/gopts/help", 0);
    const Key * missingKeys = keyGetMeta (parentKey, "logs/spec/missing");
    if (ignoreRequireInHelpMode == 1 && helpKey != NULL && missingKeys != NULL)
    {
    // proc/elektra/gopts/help was set -> we are in help mode
    // logs/spec/missing exists on parentKey -> spec detected missing keys
    // we ensured that spec uses conflict/get = ERROR -> the error in kdbGet must be from spec
    // --> we are in the error case that should be ignored
    // BUT: anything other than helpKey may be incorrect
    // and only helpKey should be used anyway
    // so create a new config KeySet
    Key * helpKeyDup = keyDup (helpKey);
    ksClear (config);
    ksAppendKey (config, helpKeyDup);
    }

    There could technically be other problems from the spec plugin as well, but since gopts runs first and was successful, there can't be any errors that would break the help message.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

spec can be configured to ignore some or all errors. I don't really know why, but it's always been the case.

This is one of spec's anti-features. Thank you for documenting it but I would be in favor of removing these anti-features. If validation fails, the plugin should return an error.

This is needed for the case where a program using the codegen API is called in "help mode"

Ok. But wouldn't it be better to directly ask for a meta-key which says that we are in the help mode? We want the help to be printed no matter what errors happened in kdbOpen or kdbGet.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If validation fails, the plugin should return an error.

IMO the option to switch to warnings instead might be useful.

  1. it allows spec to report more then one problem at once
  2. sometimes ignoring a problem is fine or even intended (e.g. when fixing the problem)

I agree, however, that ignoring all problems by default is bad. I think it was done, so that problems can still be fixed via kdb, which would fail, if there are warnings or errors.

But wouldn't it be better to directly ask for a meta-key which says that we are in the help mode?

We are doing that. gopts creates the key proc/elektra/gopts/help, if we are in help mode. It is the key that was passed to elektraGetOpts, which contains data for the help message.

We want the help to be printed no matter what errors happened in kdbOpen or kdbGet.

I thought about that, but I am not sure this is possible. Ignoring all errors from spec should be fine, since gopts already ran at that point. But ignoring all errors (no matter from where) could be problematic. Ignoring only missing required keys was the safest solution.

Required keys must be provided by the user/admin and cannot come from another source (Elektra, app developer, etc.). Therefore they will be missing until the user makes changes to the KDB. Before that, no other error should occur (°). If a user runs app for the first time and receives an error about a missing required key, will:

  1. Know what to do and add the key, thereby fixing the problem.
  2. Try app -h/app --help to find out more. The help message may or may not contain useful information. If not they may try 3.
  3. Read some other documentation to find out more. Ideally this leads them to 1.

In any case after this the user definitely know how to interact with the KDB. Since we assumed that there won't be any errors before the KDB was changed, we can assume that the user caused other errors by changing the KDB.

Of course it would be nice to always show the help message, but since it depends on the specification being readable, that is somewhat impossible. One way around that (at least in applications using codegen) is to generate a fallback help message that is used, if we couldn't read the spec. This fallback help message would be based on the spec provided to the code generator. It would therefore not reflect any changes to the spec (e.g. description or opt/help) that the user made.


(°) assuming a correct installation. But broken installations cannot be ignored anyway, since they may interfere with the help message that is generated from the spec.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it allows spec to report more then one problem at once

This would also be possible if the first error is as error and the others as warning.

sometimes ignoring a problem is fine or even intended (e.g. when fixing the problem)

We usually only add warnings during kdbGet() and errors only in kdbSet(). I would also expect that behavior in the spec plugin. We should document this behavior in a decision.

I thought about that,

Very nice write-up. Can you make a decision of what you wrote here?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We usually only add warnings during kdbGet()

There are many cases, where kdbGet() should return an error. The best example would be a broken storage file. The storage plugin should return an error and kdbGet() should abort.

For the spec plugin, it probably makes sense to have to option of switching between warnings and errors. In kdb (and other configuration tools) warnings would be preferred, as a kdbGet() is required before a kdbSet() could fix the problems. In applications on the other hand errors are the better solution. They are easier to detect, since kdbGet() uses a different return code for errors. The handling of actual errors is also easier in the high-level API (see #2952).

Can you make a decision of what you wrote here?

Will do.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are many cases, where kdbGet() should return an error.

I agree. I made a different proposal: #2955

For the spec plugin, it probably makes sense to have to option of switching between warnings and errors.

I hope this will not be needed once we have #2955 implemented

Will do.

💖


## Libraries

The text below summarizes updates to the [C (and C++)-based libraries](https://www.libelektra.org/libraries/readme) of Elektra.
Expand Down
11 changes: 11 additions & 0 deletions src/include/kdbopts.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,18 @@

#include <kdb.h>

#ifdef __cplusplus
namespace ckdb
{
extern "C" {
#endif

int elektraGetOpts (KeySet * ks, int argc, const char ** argv, const char ** envp, Key * parentKey);
char * elektraGetOptsHelpMessage (Key * errorKey, const char * usage, const char * prefix);

#ifdef __cplusplus
}
}
#endif

#endif // ELEKTRA_KDBOPTS_H
2 changes: 2 additions & 0 deletions src/libs/ease/symbols.map
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@ libelektra_0.8 {
elektraKeyGetRelativeName;
elektraKsFilter;
elektraResolveReference;
};

libelektra_0.9 {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why this change?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It should have been part of #2916. The functions were not part of any 0.8.x release, so they should not be versioned as libelektra_0.8.

If we won't have another 0.9.x release and go straight to 1.0.0, they should actually be in libelektra_1.0.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We will definitely have a 0.9 release before 1.0.

## ToString;
elektraBooleanToString;
elektraCharToString;
Expand Down
36 changes: 34 additions & 2 deletions src/libs/highlevel/elektra.c
Original file line number Diff line number Diff line change
Expand Up @@ -68,16 +68,24 @@ Elektra * elektraOpen (const char * application, KeySet * defaults, KeySet * con
if (kdb == NULL)
{
*error = elektraErrorFromKey (parentKey);
keyDel (parentKey);
return NULL;
}

int ignoreRequireInHelpMode = 0;

if (contract != NULL)
{
Key * contractCut = keyNew ("system/elektra/highlevel", KEY_END);
KeySet * highlevelContract = ksCut (contract, contractCut);

if (ksGetSize (highlevelContract) > 0)
{
if (ksLookupByName (highlevelContract, "system/elektra/highlevel/helpmode/ignore/require", 0) != NULL)
{
ignoreRequireInHelpMode = 1;
}

if (!checkHighlevelContract (application, highlevelContract, error))
{
keyDel (contractCut);
Expand All @@ -99,6 +107,7 @@ Elektra * elektraOpen (const char * application, KeySet * defaults, KeySet * con
keyNew ("system/elektra/ensure/plugins/global/spec/config/conflict/get", KEY_VALUE, "ERROR", KEY_END));
ksAppendKey (contract,
keyNew ("system/elektra/ensure/plugins/global/spec/config/conflict/set", KEY_VALUE, "ERROR", KEY_END));
ksAppendKey (contract, keyNew ("system/elektra/ensure/plugins/global/spec/config/missing/log", KEY_VALUE, "1", KEY_END));

const int kdbEnsureResult = kdbEnsure (kdb, contract, parentKey);

Expand Down Expand Up @@ -131,8 +140,31 @@ Elektra * elektraOpen (const char * application, KeySet * defaults, KeySet * con

if (kdbGetResult == -1)
{
*error = elektraErrorFromKey (parentKey);
return NULL;
Key * helpKey = ksLookupByName (config, "proc/elektra/gopts/help", 0);
const Key * missingKeys = keyGetMeta (parentKey, "logs/spec/missing");
if (ignoreRequireInHelpMode == 1 && helpKey != NULL && missingKeys != NULL)
{
// proc/elektra/gopts/help was set -> we are in help mode
// logs/spec/missing exists on parentKey -> spec detected missing keys
// we ensured that spec uses conflict/get = ERROR -> the error in kdbGet must be from spec
// --> we are in the error case that should be ignored

// BUT: anything other than helpKey may be incorrect
// and only helpKey should be used anyway
// so create a new config KeySet
Key * helpKeyDup = keyDup (helpKey);
ksClear (config);
ksAppendKey (config, helpKeyDup);
}
else
{
*error = elektraErrorFromKey (parentKey);

ksDel (config);
kdbClose (kdb, parentKey);
keyDel (parentKey);
return NULL;
}
}

Elektra * const elektra = elektraCalloc (sizeof (struct _Elektra));
Expand Down
30 changes: 30 additions & 0 deletions src/plugins/gopts/gopts.c
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,35 @@ static void cleanupEnvp (char ** envp);
#error "No implementation available"
#endif

/**
* Detects whether we are in help mode or not.
* DOES NOT set 'proc/elektra/gopts/help' for use with elektraGetOptsHelpMessage().
*
* @retval 1 if the -h or --help is part of argv
* @retval 0 otherwise
* @retval -1 on error (could not load argv)
*/
int elektraGOptsIsHelpMode (void)
{
char ** argv = NULL;
int argc = loadArgs (&argv);

if (argv == NULL)
{
return -1;
}

for (int i = 0; i < argc; ++i)
{
if (strcmp (argv[i], "-h") == 0 || strcmp (argv[i], "--help") == 0)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do not like that this is hardcoded here. -H is very common (and also used in the kdb tool). -h has man other meanings.

Furthermore this is not properly parsed, e.g. -vh would not be recognized.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do not like that this is hardcoded here. -H is very common (and also used in the kdb tool). -h has man other meanings.

Using -h/--help for help mode is already hard-coded in elektraGetOpts (there it is properly parsed).

This function is not actually intended to accurately detect help mode (for that one should still check for the presence of proc/elektra/gopts/help. It is only meant in case kdbGet() failed.

Apart from kdb tool, I have never seen -H used in favour of -h. The kdb tool also takes a very unusual approach, since it opens the man-page instead of printing to stdout/stderr.
In fact most tools I know, use only --help (if they support long options).

Furthermore this is not properly parsed, e.g. -vh would not be recognized.

See above #2944 (comment).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Apart from kdb tool, I have never seen -H used in favour of -h.

Because of how the cmdline options in Elektra are implemented every option needs to have a short option. -H was the most natural one as -h is already "human readable".

The kdb tool also takes a very unusual approach, since it opens the man-page instead of printing to stdout/stderr.

It makes a lot of sense to not have a --help output and a separated man page. E.g. git also does it this way.

In fact most tools I know, use only --help (if they support long options).

I would prefer if we only hardcode --help and drop support for -h.

{
return 1;
}
}

return 0;
}


int elektraGOptsGet (Plugin * handle ELEKTRA_UNUSED, KeySet * returned, Key * parentKey)
{
Expand All @@ -43,6 +72,7 @@ int elektraGOptsGet (Plugin * handle ELEKTRA_UNUSED, KeySet * returned, Key * pa
ksNew (30, keyNew ("system/elektra/modules/gopts", KEY_VALUE, "gopts plugin waits for your orders", KEY_END),
keyNew ("system/elektra/modules/gopts/exports", KEY_END),
keyNew ("system/elektra/modules/gopts/exports/get", KEY_FUNC, elektraGOptsGet, KEY_END),
keyNew ("system/elektra/modules/gopts/exports/ishelpmode", KEY_FUNC, elektraGOptsIsHelpMode, KEY_END),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

isinhelpmode or simply helpmode?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since you already merged this PR: Do you still want the change?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It was only a question. If you like ishelpmode best, we can keep it.

#include ELEKTRA_README
keyNew ("system/elektra/modules/gopts/infos/version", KEY_VALUE, PLUGINVERSION, KEY_END), KS_END);
ksAppend (returned, contract);
Expand Down
4 changes: 4 additions & 0 deletions src/plugins/spec/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,10 @@ The allowed values for the conflict keys are always the same:
`logs/spec/info` is an array, each element is one conflict.
- any other value ignores the conflict, this includes if the conflict key is not given (i.e. the default)

There is also the special key `missing/log`. If it is set to `1`, the plugin will create the meta array `logs/spec/missing/#`.
This array will contain a list of the missing keys. The key `missing/log` can only be part of the main plugin configuration,
not individual keys' metakeys. It also applies to `kdbGet` and `kdbSet` calls.

## Examples

Ini files can be found in [/examples/spec](/examples/spec) which should be PWD
Expand Down
12 changes: 11 additions & 1 deletion src/plugins/spec/spec.c
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ typedef struct
OnConflict conflict;
OnConflict range;
OnConflict missing;
int logMissing;
} ConflictHandling;

static void copyMeta (Key * dest, Key * src);
Expand Down Expand Up @@ -128,6 +129,9 @@ static void parseConfig (KeySet * config, ConflictHandling * ch, const char base
strcpy (nameBufferEnd, "/missing");
key = ksLookupByName (config, nameBuffer, 0);
ch->missing = key == NULL ? base : parseOnConflictKey (key);

key = ksLookupByName (config, "/missing/log", 0);
ch->logMissing = key != NULL && strcmp (keyString (key), "1") == 0;
}

static void parseLocalConfig (Key * specKey, ConflictHandling * ch, bool isKdbGet)
Expand Down Expand Up @@ -801,13 +805,19 @@ static int processSpecKey (Key * specKey, Key * parentKey, KeySet * ks, const Co
{
if (require)
{
char * msg = elektraFormat ("Required key %s is missing.", strchr (keyName (specKey), '/'));
const char * missing = strchr (keyName (specKey), '/');
char * msg = elektraFormat ("Required key %s is missing.", missing);
handleConflict (parentKey, msg, ch->missing);
elektraFree (msg);
if (ch->missing != IGNORE)
{
ret = -1;
}

if (ch->logMissing)
{
elektraMetaArrayAdd (parentKey, "logs/spec/missing", missing);
}
}

if (isKdbGet)
Expand Down
40 changes: 40 additions & 0 deletions src/plugins/spec/testmod_spec.c
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,45 @@ static void test_require (void)
ksDel (_conf);
}

static void test_logMissing (void)
{
printf ("test logMissing\n");

KeySet * _conf = ksNew (2, keyNew ("user/conflict/get", KEY_VALUE, "ERROR", KEY_END),
keyNew ("user/missing/log", KEY_VALUE, "1", KEY_END), KS_END);

TEST_BEGIN
{
KeySet * ks = ksNew (10, keyNew ("spec" PARENT_KEY "/a", KEY_META, "require", "", KEY_END), KS_END);

TEST_CHECK (plugin->kdbGet (plugin, ks, parentKey) == ELEKTRA_PLUGIN_STATUS_ERROR, "kdbGet shouldn't succeed");

succeed_if (keyGetMeta (parentKey, "logs/spec/missing") != NULL, "missing key should have been logged");
succeed_if_same_string (keyString (keyGetMeta (parentKey, "logs/spec/missing")), "#0");

succeed_if (keyGetMeta (parentKey, "logs/spec/missing/#0") != NULL, "missing key should have been logged");
succeed_if_same_string (keyString (keyGetMeta (parentKey, "logs/spec/missing/#0")), PARENT_KEY "/a");

ksDel (ks);
}
TEST_END

TEST_BEGIN
{
KeySet * ks = ksNew (10, keyNew ("spec" PARENT_KEY "/a", KEY_META, "require", "", KEY_END),
keyNew ("user" PARENT_KEY "/a", KEY_END), KS_END);

TEST_CHECK (plugin->kdbGet (plugin, ks, parentKey) == ELEKTRA_PLUGIN_STATUS_SUCCESS, "kdbGet failed");
TEST_ON_FAIL (output_error (parentKey));

succeed_if (keyGetMeta (parentKey, "logs/spec/missing") == NULL, "missing key should not have been logged");

ksDel (ks);
}
TEST_END
ksDel (_conf);
}

static void test_array (void)
{
printf ("test array\n");
Expand Down Expand Up @@ -573,6 +612,7 @@ int main (int argc, char ** argv)
test_assign_condition ();
test_wildcard ();
test_require ();
test_logMissing ();
test_array ();
test_require_array ();
test_array_member ();
Expand Down
1 change: 1 addition & 0 deletions src/tools/kdb/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ if (BUILD_SHARED)
elektra-core
elektra-kdb
elektratools
elektra-opts
elektra-merge)

install (TARGETS kdb DESTINATION bin)
Expand Down
Loading