Feature/load new lifecycle config#208
Conversation
License Check Results🚀 The license check job ran with the Bazel command: bazel run --lockfile_mode=error //:license-checkStatus: Click to expand output |
|
The created documentation from the pull request is available at: docu-html |
| module_name = "score_baselibs", | ||
| commit = "498a4b256c9073602140243d30c33b106e279f75", | ||
| remote = "https://github.com/eclipse-score/baselibs.git", | ||
| ) |
There was a problem hiding this comment.
@paulquiring Any idea when a new baselibs release will be available containing the new flatbuffer functionality?
There was a problem hiding this comment.
Next release of baselibs: planed on 21.05 or 22.05. See https://github.com/orgs/eclipse-score/discussions/2390?sort=new#discussioncomment-16987333
| } | ||
|
|
||
| private: | ||
| friend class Builder; |
There was a problem hiding this comment.
A comment would have been helpful to mention that this saves the need to write getters in the builder class.
There was a problem hiding this comment.
Discussed in meeting. The friend is needed to access the private constructor of the Config class.
This is in line with typical builder pattern implementation
There was a problem hiding this comment.
Moving the Builder class to the outer scope so that it is not a nested class anymore.
Nested class is not required and may be discouraged by some SCA rules.
Done in f70d64b
| { | ||
| if (ev != nullptr && ev->key() != nullptr) | ||
| { | ||
| result.emplace(ev->key()->str(), safeString(ev->value())); |
There was a problem hiding this comment.
I guess the key cannot be nullptr?
There was a problem hiding this comment.
In the flatbuffer schema I have defined it as
// A key-value pair representing an environment variable.
table EnvironmentalVariable {
key:string (required);
value:string (required);
}
So I think if an environment variable is there, the key and value must be available.
There was a problem hiding this comment.
Consider adding asserts for values that are never expected to be nullptr.
safestring can be removed for value() because it is required in the schema
|
|
||
| SwitchRunTargetAction convertRequiredSwitchRunTargetAction(const fb::SwitchRunTargetAction* sa) | ||
| { | ||
| return convertSwitchRunTargetAction(sa).value(); |
There was a problem hiding this comment.
It should never happen because the SwitchRunTargetRecoveryAction is defined as required in this context:
// Configuration for a Run Target representing an operational mode of the system.
table RunTarget {
....
// Recovery action when a component in this Run Target fails.
recovery_action:SwitchRunTargetAction (required);
}
However to be defensive against potential future changes on the schema, we could:
- Add a try/catch within parseFlatbuffer and return
score::cpp::make_unexpected(IConfigLoader::Error::InvalidFormat);in case there is any exception. - And/or put in an assertion
auto opt=convertSwitchRunTargetAction(sa)
assert(opt.has_value() && "SwitchRunTargetAction is null although it is expected to be a mandatory configuration");
return opt.value();
What do you think?
There was a problem hiding this comment.
Discussed: We shall add asserts
Done in 99cdb9c
| module_name = "score_baselibs", | ||
| commit = "498a4b256c9073602140243d30c33b106e279f75", | ||
| remote = "https://github.com/eclipse-score/baselibs.git", | ||
| ) |
There was a problem hiding this comment.
Next release of baselibs: planed on 21.05 or 22.05. See https://github.com/orgs/eclipse-score/discussions/2390?sort=new#discussioncomment-16987333
| constexpr int32_t kExpectedSchemaVersion = 1; | ||
| constexpr double kSecondsToMilliseconds = 1000.0; | ||
|
|
||
| uint32_t secondsToMs(double seconds) |
There was a problem hiding this comment.
#include <iostream>
#include <cstdint>
#include <string_view>
#include <cassert>
constexpr double kSecondsToMilliseconds = 1000.0;
uint32_t secondsToMs(double seconds)
{
return static_cast<uint32_t>(seconds * kSecondsToMilliseconds);
}
int main () {
assert(secondsToMs(0.000500) == 0);// holds is this a problem ?
assert(secondsToMs(0.001000) == 1);
}if this is a problem we might need to change the fbs
There was a problem hiding this comment.
I guess this is not an issue.
The user is configuring all time values in seconds
Though we should reconsider whether the want to switch the configuration to ms.
There was a problem hiding this comment.
How to safeguard that the user does not configure anything < 1ms?
There was a problem hiding this comment.
Adding an assertion for now, to be moved to schema validation later
There was a problem hiding this comment.
Assertion added in a1f2860
Assertion fails if configured seconds > 0 but is rounded down to 0ms.
|
review from my side is finished. |
|
|
||
| uint32_t secondsToMs(double seconds) | ||
| { | ||
| return static_cast<uint32_t>(seconds * kSecondsToMilliseconds); |
There was a problem hiding this comment.
Could this result in UB if seconds < 0 or > UINT32_MAX?
Schema validator should take care about negative input, but static analysis or compiler warnings may not know about this. However we don't have cap on max timeout in schema.
Maybe we can log warning if value is too small or too big and apply some boundary values
| Config::Builder builder; | ||
|
|
||
| // initial_run_target is a required field, guaranteed non-null by the schema and verifier. | ||
| builder.setInitialRunTarget(config->initial_run_target()->str()); |
There was a problem hiding this comment.
I have mixed feelings about defensive coding, but if somebody prepares crazy config (bypassing verifier), we will crash instead of printing error message and exiting.
However if somebody is doing this, then maybe, they should not expect much support from launch manager.
There was a problem hiding this comment.
I had similar discussion with Paul. For fields that are marked as required in the flatbuffer schema, flatbuffer will ensure that this is not nullptr.
Nevertheless, we decided for adding additional asserts to check that those are not nullptr.
Adding further nullptr checks seemed redundant and would make covering this code via tests almost impossible.
Is that approach fine for you?
|
|
||
| struct Sandbox | ||
| { | ||
| uint32_t uid{}; |
There was a problem hiding this comment.
uid_t
| uint32_t uid{}; | |
| uid_t uid{}; |
| struct Sandbox | ||
| { | ||
| uint32_t uid{}; | ||
| uint32_t gid{}; |
There was a problem hiding this comment.
| uint32_t gid{}; | |
| gid_t gid{}; |
| { | ||
| uint32_t ready_timeout_ms{}; | ||
| uint32_t shutdown_timeout_ms{}; | ||
| std::unordered_map<std::string, std::string> environmental_variables; |
There was a problem hiding this comment.
from: https://linux.die.net/man/3/execve
we need a format like this:
char *env[] = { "HOME=/usr/home", "LOGNAME=home", (char *)0 };this will do the job
std::vector<const char*> environmental_variables = {"HOME=/usr/home", "LOGNAME=home" , nullptr };execve(... , const_cast<char* const*>(environmental_variables .data()));There was a problem hiding this comment.
With std::vector<const char*> I think there is the question where do the strings live in case they are not constant char sequences. We would require a combination of
std::vector<const char*> pointers_;
std::vector<std::string> strings_;There was a problem hiding this comment.
Please tell me if this to too overcomplicated. But I see the use case that we might want to add an environment variable before starting the process that was not configured by the user. We had this use case already in the past.
This could be achieved rather cleanly with a dedicated class, that stores both std::vector<const char*> and std::vector<std::string>
class EnvironmentVariable
{
public:
EnvironmentVariable(std::string_view key, std::string_view value);
std::string_view key() const;
std::string_view value() const;
const char* c_str() const;
private:
std::string entry_; // internal storage in "key=value" format.
std::size_t key_length_;
};
class Environment
{
public:
using const_iterator = std::vector<EnvironmentVariable>::const_iterator;
Environment() = default;
Environment(const Environment&) = delete;
Environment& operator=(const Environment&) = delete;
Environment(Environment&& other) noexcept;
Environment& operator=(Environment&& other) noexcept;
~Environment() = default;
void reserve(std::size_t count);
void add(std::string_view key, std::string_view value);
const_iterator begin() const;
const_iterator end() const;
std::size_t size() const;
// Access format expected by execve systemcall
const std::vector<const char*>& envp() const;
private:
void rebuildPointers();
std::vector<EnvironmentVariable> entries_;
std::vector<const char*> pointers_;
};The alternative that I see is to keep doing what we do know:
char* envp_[static_cast<std::size_t>(score::lcm::internal::kEnvArraySize)];
However, than we have fixed size and need to deal with the scenario this size may be exceeded by the configuration. Still there is the problem of string ownership for which I believe we will need to wrap it in a dedicated class.
| { | ||
| uint32_t uid{}; | ||
| uint32_t gid{}; | ||
| std::vector<uint32_t> supplementary_group_ids; |
There was a problem hiding this comment.
| std::vector<uint32_t> supplementary_group_ids; | |
| std::vector<gid_t > supplementary_group_ids; |
| gid_t gid{}; | ||
| std::vector<gid_t> supplementary_group_ids; | ||
| std::optional<std::string> security_policy; | ||
| std::string scheduling_policy; |
There was a problem hiding this comment.
Do we want to translate scheduling_policy already to an integer?
I think for logging it might be nice to have the string available, so you can log "SCHED_OTHER" instead of "scheduling policy: 1".
Though I don't have a string opinion. Storing the same string over and over again is also not nice.
Probably makes sense to already translate it to int

Note: The code is not actually in use yet. The python script which does the translation from the user-facing json configuration to the flatbuffer json format is not part of the PR and will be added in a follow up PR.
Furthermore, the launch manager code needs to be adapted to use the new structure.
Background:
Relates To: #209