sculpt: sanitize deployment, diagnostic feedback

This patch suppresses the start of components that cannot run because
obvious runtime dependencies (used servers) are missing in the runtime.
In this situation, the sculpt manager gives diagnostic feedback to the
user in the runtime dialog.
This commit is contained in:
Norman Feske 2018-06-06 18:10:45 +02:00 committed by Christian Helmuth
parent bf1428be18
commit a3999c93f4
5 changed files with 168 additions and 4 deletions

View File

@ -43,6 +43,16 @@ class Depot_deploy::Child : public List_model<Child>::Element
Reconstructible<Buffered_xml> _start_xml; /* from config */
Constructible<Buffered_xml> _pkg_xml { }; /* from blueprint */
/*
* State of the condition check for generating the start node of
* the child. I.e., if the child is complete and configured but
* a used server component is missing, we need to suppress the start
* node until the condition is satisfied.
*/
enum Condition { UNCHECKED, SATISFIED, UNSATISFIED };
Condition _condition { UNCHECKED };
Name const _name;
Archive::Path _config_pkg_path() const
@ -158,6 +168,24 @@ class Depot_deploy::Child : public List_model<Child>::Element
_pkg_xml.construct(_alloc, pkg);
}
template <typename COND_FN>
void apply_condition(COND_FN const &fn)
{
/* don't check the condition twice */
if (_condition == SATISFIED)
return;
if (_start_xml.constructed())
_condition = fn(_start_xml->xml()) ? SATISFIED : UNSATISFIED;
}
template <typename FN>
void apply_if_unsatisfied(FN const &fn) const
{
if (_condition == UNSATISFIED && _start_xml.constructed())
fn(_start_xml->xml());
}
void mark_as_incomplete(Xml_node missing)
{
/* print error message only once */
@ -225,7 +253,7 @@ class Depot_deploy::Child : public List_model<Child>::Element
void Depot_deploy::Child::gen_start_node(Xml_generator &xml, Xml_node common,
Depot_rom_server const &depot_rom) const
{
if (!_configured())
if (!_configured() || _condition == UNSATISFIED)
return;
if (!_pkg_xml->xml().has_sub_node("runtime")) {

View File

@ -78,6 +78,24 @@ class Depot_deploy::Children
child.mark_as_incomplete(missing); }); });
}
template <typename COND_FN>
void apply_condition(COND_FN const &fn)
{
_children.for_each([&] (Child &child) {
child.apply_condition(fn); });
}
/**
* Call 'fn' with start 'Xml_node' of each child that has an
* unsatisfied start condition.
*/
template <typename FN>
void for_each_unsatisfied_child(FN const &fn) const
{
_children.for_each([&] (Child const &child) {
child.apply_if_unsatisfied(fn); });
}
void reset_incomplete()
{
_children.for_each([&] (Child &child) {

View File

@ -15,6 +15,68 @@
#include <deploy.h>
bool Sculpt::Deploy::update_child_conditions()
{
/* track whether any condition changed for the better */
bool result = false;
_children.apply_condition([&] (Xml_node start) {
/* the child cannot be started as long as any dependency is missing */
bool condition = true;
_for_each_missing_server(start, [&] (Start_name const &) {
condition = false; });
result |= condition;
return condition;
});
return result;
}
void Sculpt::Deploy::_gen_missing_dependencies(Xml_generator &xml,
Xml_node start, int &count) const
{
Start_name const child = start.attribute_value("name", Start_name());
_for_each_missing_server(start, [&] (Start_name const &server) {
gen_named_node(xml, "hbox", String<20>(count++), [&] () {
gen_named_node(xml, "float", "left", [&] () {
xml.attribute("west", "yes");
xml.node("label", [&] () {
xml.attribute("text", String<64>(child, " requires ", server));
xml.attribute("font", "annotation/regular");
});
});
});
});
}
void Sculpt::Deploy::gen_child_diagnostics(Xml_generator &xml) const
{
bool all_children_ok = true;
_children.for_each_unsatisfied_child([&] (Xml_node) {
all_children_ok = false; });
if (all_children_ok)
return;
int count = 0;
gen_named_node(xml, "frame", "diagnostics", [&] () {
xml.node("vbox", [&] () {
xml.node("label", [&] () {
xml.attribute("text", "Diagnostics"); });
xml.node("float", [&] () {
xml.node("vbox", [&] () {
_children.for_each_unsatisfied_child([&] (Xml_node start) {
_gen_missing_dependencies(xml, start, count); }); }); });
});
});
}
void Sculpt::Deploy::handle_deploy()
{
Xml_node const manual_deploy = _manual_deploy_rom.xml();
@ -60,6 +122,10 @@ void Sculpt::Deploy::handle_deploy()
xml.attribute("arch", _arch);
_children.gen_installation_entries(xml); });
/* apply runtime condition checks */
update_child_conditions();
_dialog_generator.generate_dialog();
_runtime_config_generator.generate_runtime_config();
}

View File

@ -25,6 +25,7 @@
#include <types.h>
#include <runtime.h>
#include <managed_config.h>
#include <view/dialog.h>
namespace Sculpt { struct Deploy; }
@ -35,6 +36,10 @@ struct Sculpt::Deploy
Allocator &_alloc;
Runtime_info const &_runtime_info;
Dialog::Generator &_dialog_generator;
Runtime_config_generator &_runtime_config_generator;
typedef String<16> Arch;
@ -85,6 +90,44 @@ struct Sculpt::Deploy
handle_deploy();
}
/**
* Call 'fn' for each unsatisfied dependency of the child's 'start' node
*/
template <typename FN>
void _for_each_missing_server(Xml_node start, FN const &fn) const
{
start.for_each_sub_node("route", [&] (Xml_node route) {
route.for_each_sub_node("service", [&] (Xml_node service) {
service.for_each_sub_node("child", [&] (Xml_node child) {
Start_name const name = child.attribute_value("name", Start_name());
/*
* The dependency to the default-fs alias is always
* satisfied during the deploy phase. But it does not
* appear in the runtime-state report.
*/
if (name == "default_fs_rw")
return;
if (!_runtime_info.present_in_runtime(name))
fn(name);
});
});
});
}
/**
* Re-evaluate child dependencies
*
* \return true if any condition has changed and new children may have
* become able to start
*/
bool update_child_conditions();
void _gen_missing_dependencies(Xml_generator &, Xml_node, int &) const;
void gen_child_diagnostics(Xml_generator &xml) const;
void gen_runtime_start_nodes(Xml_generator &) const;
Signal_handler<Deploy> _manual_deploy_handler {
@ -107,10 +150,12 @@ struct Sculpt::Deploy
handle_deploy();
}
Deploy(Env &env, Allocator &alloc,
Deploy(Env &env, Allocator &alloc, Runtime_info const &runtime_info,
Dialog::Generator &dialog_generator,
Runtime_config_generator &runtime_config_generator)
:
_env(env), _alloc(alloc),
_env(env), _alloc(alloc), _runtime_info(runtime_info),
_dialog_generator(dialog_generator),
_runtime_config_generator(runtime_config_generator)
{
_manual_deploy_rom.sigh(_manual_deploy_handler);

View File

@ -158,7 +158,7 @@ struct Sculpt::Main : Input_event_handler,
&& _network.ready()
&& _deploy.update_needed(); };
Deploy _deploy { _env, _heap, *this };
Deploy _deploy { _env, _heap, *this, *this, *this };
@ -214,6 +214,8 @@ struct Sculpt::Main : Input_event_handler,
xml.attribute("font", "title/regular");
});
_deploy.gen_child_diagnostics(xml);
Xml_node const state = _update_state_rom.xml();
if (_update_running() && state.has_sub_node("archive"))
gen_download_status(xml, state);
@ -618,6 +620,11 @@ void Sculpt::Main::_handle_runtime_state()
*/
_network.reattempt_nic_router_config();
if (_deploy.update_child_conditions()) {
reconfigure_runtime = true;
generate_dialog();
}
if (reconfigure_runtime)
generate_runtime_config();
}