Merge branch 'issue-2155' into calamares
This commit is contained in:
commit
98f26d9380
@ -16,6 +16,7 @@
|
|||||||
#include "compat/Variant.h"
|
#include "compat/Variant.h"
|
||||||
#include "locale/Global.h"
|
#include "locale/Global.h"
|
||||||
#include "utils/Logger.h"
|
#include "utils/Logger.h"
|
||||||
|
#include "utils/Runner.h"
|
||||||
#include "utils/StringExpander.h"
|
#include "utils/StringExpander.h"
|
||||||
#include "utils/System.h"
|
#include "utils/System.h"
|
||||||
#include "utils/Variant.h"
|
#include "utils/Variant.h"
|
||||||
@ -140,6 +141,11 @@ CommandLine::CommandLine( const QVariantMap& m )
|
|||||||
m_command = command;
|
m_command = command;
|
||||||
m_timeout = timeout >= 0 ? std::chrono::seconds( timeout ) : CommandLine::TimeoutNotSet();
|
m_timeout = timeout >= 0 ? std::chrono::seconds( timeout ) : CommandLine::TimeoutNotSet();
|
||||||
m_environment = Calamares::getStringList( m, "environment" );
|
m_environment = Calamares::getStringList( m, "environment" );
|
||||||
|
|
||||||
|
if ( m.contains( "verbose" ) )
|
||||||
|
{
|
||||||
|
m_verbose = Calamares::getBool( m, "verbose", false );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -159,7 +165,12 @@ CommandLine::expand( KMacroExpanderBase& expander ) const
|
|||||||
QStringList e = m_environment;
|
QStringList e = m_environment;
|
||||||
std::for_each( e.begin(), e.end(), [ &expander ]( QString& s ) { expander.expandMacrosShellQuote( s ); } );
|
std::for_each( e.begin(), e.end(), [ &expander ]( QString& s ) { expander.expandMacrosShellQuote( s ); } );
|
||||||
|
|
||||||
return { c, m_environment, m_timeout };
|
CommandLine l { c, m_environment, m_timeout };
|
||||||
|
if ( m_verbose.has_value() )
|
||||||
|
{
|
||||||
|
l.updateVerbose( m_verbose.value() );
|
||||||
|
}
|
||||||
|
return l;
|
||||||
}
|
}
|
||||||
|
|
||||||
Calamares::CommandLine
|
Calamares::CommandLine
|
||||||
@ -252,7 +263,16 @@ CommandList::run()
|
|||||||
shell_cmd << ( environmentSetting + processed_cmd );
|
shell_cmd << ( environmentSetting + processed_cmd );
|
||||||
|
|
||||||
std::chrono::seconds timeout = i->timeout() >= std::chrono::seconds::zero() ? i->timeout() : m_timeout;
|
std::chrono::seconds timeout = i->timeout() >= std::chrono::seconds::zero() ? i->timeout() : m_timeout;
|
||||||
ProcessResult r = System::runCommand( location, shell_cmd, QString(), QString(), timeout );
|
|
||||||
|
Calamares::Utils::Runner runner( shell_cmd );
|
||||||
|
runner.setLocation( location ).setTimeout( timeout ).setWorkingDirectory( QString() );
|
||||||
|
if ( i->isVerbose() )
|
||||||
|
{
|
||||||
|
runner.enableOutputProcessing();
|
||||||
|
QObject::connect(
|
||||||
|
&runner, &Calamares::Utils::Runner::output, []( QString output ) { cDebug() << output; } );
|
||||||
|
}
|
||||||
|
ProcessResult r = runner.run();
|
||||||
|
|
||||||
if ( r.getExitCode() != 0 )
|
if ( r.getExitCode() != 0 )
|
||||||
{
|
{
|
||||||
@ -289,4 +309,10 @@ CommandList::expand() const
|
|||||||
return expand( expander );
|
return expand( expander );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
CommandList::updateVerbose( bool verbose )
|
||||||
|
{
|
||||||
|
std::for_each( begin(), end(), [ verbose ]( CommandLine& command ) { command.updateVerbose( verbose ); } );
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Calamares
|
} // namespace Calamares
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
#include <QVariant>
|
#include <QVariant>
|
||||||
|
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
|
#include <optional>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
class KMacroExpanderBase;
|
class KMacroExpanderBase;
|
||||||
@ -63,6 +64,7 @@ public:
|
|||||||
QString command() const { return m_command; }
|
QString command() const { return m_command; }
|
||||||
[[nodiscard]] QStringList environment() const { return m_environment; }
|
[[nodiscard]] QStringList environment() const { return m_environment; }
|
||||||
std::chrono::seconds timeout() const { return m_timeout; }
|
std::chrono::seconds timeout() const { return m_timeout; }
|
||||||
|
bool isVerbose() const { return m_verbose.value_or( false ); }
|
||||||
|
|
||||||
bool isValid() const { return !m_command.isEmpty(); }
|
bool isValid() const { return !m_command.isEmpty(); }
|
||||||
|
|
||||||
@ -81,10 +83,20 @@ public:
|
|||||||
*/
|
*/
|
||||||
DLLEXPORT CommandLine expand() const;
|
DLLEXPORT CommandLine expand() const;
|
||||||
|
|
||||||
|
/** @brief If nothing has set verbosity yet, update to @p verbose */
|
||||||
|
void updateVerbose( bool verbose )
|
||||||
|
{
|
||||||
|
if ( !m_verbose.has_value() )
|
||||||
|
{
|
||||||
|
m_verbose = verbose;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QString m_command;
|
QString m_command;
|
||||||
QStringList m_environment;
|
QStringList m_environment;
|
||||||
std::chrono::seconds m_timeout = TimeoutNotSet();
|
std::chrono::seconds m_timeout = TimeoutNotSet();
|
||||||
|
std::optional< bool > m_verbose;
|
||||||
};
|
};
|
||||||
|
|
||||||
/** @brief Abbreviation, used internally. */
|
/** @brief Abbreviation, used internally. */
|
||||||
@ -103,6 +115,11 @@ class DLLEXPORT CommandList : protected CommandList_t
|
|||||||
public:
|
public:
|
||||||
/** @brief empty command-list with timeout to apply to entries. */
|
/** @brief empty command-list with timeout to apply to entries. */
|
||||||
CommandList( bool doChroot = true, std::chrono::seconds timeout = std::chrono::seconds( 10 ) );
|
CommandList( bool doChroot = true, std::chrono::seconds timeout = std::chrono::seconds( 10 ) );
|
||||||
|
/** @brief command-list constructed from script-entries in @p v
|
||||||
|
*
|
||||||
|
* The global settings @p doChroot and @p timeout can be overridden by
|
||||||
|
* the individual script-entries.
|
||||||
|
*/
|
||||||
CommandList( const QVariant& v, bool doChroot = true, std::chrono::seconds timeout = std::chrono::seconds( 10 ) );
|
CommandList( const QVariant& v, bool doChroot = true, std::chrono::seconds timeout = std::chrono::seconds( 10 ) );
|
||||||
CommandList( int ) = delete;
|
CommandList( int ) = delete;
|
||||||
CommandList( const QVariant&, int ) = delete;
|
CommandList( const QVariant&, int ) = delete;
|
||||||
@ -126,14 +143,17 @@ public:
|
|||||||
* Each command-line in the list is expanded with the given @p expander.
|
* Each command-line in the list is expanded with the given @p expander.
|
||||||
* @see CommandLine::expand() for details.
|
* @see CommandLine::expand() for details.
|
||||||
*/
|
*/
|
||||||
CommandList expand( KMacroExpanderBase& expander ) const;
|
DLLEXPORT CommandList expand( KMacroExpanderBase& expander ) const;
|
||||||
|
|
||||||
/** @brief As above, with a default macro-expander.
|
/** @brief As above, with a default macro-expander.
|
||||||
*
|
*
|
||||||
* Each command-line in the list is expanded with that default macro-expander.
|
* Each command-line in the list is expanded with that default macro-expander.
|
||||||
* @see CommandLine::expand() for details.
|
* @see CommandLine::expand() for details.
|
||||||
*/
|
*/
|
||||||
CommandList expand() const;
|
DLLEXPORT CommandList expand() const;
|
||||||
|
|
||||||
|
/** @brief Applies default-value @p verbose to each entry without an explicit setting. */
|
||||||
|
DLLEXPORT void updateVerbose( bool verbose );
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool m_doChroot;
|
bool m_doChroot;
|
||||||
|
@ -189,8 +189,9 @@ Calamares::Utils::Runner::run()
|
|||||||
? ( static_cast< int >( std::chrono::milliseconds( m_timeout ).count() ) )
|
? ( static_cast< int >( std::chrono::milliseconds( m_timeout ).count() ) )
|
||||||
: -1 ) )
|
: -1 ) )
|
||||||
{
|
{
|
||||||
cWarning() << "Process" << m_command.first() << "timed out after" << m_timeout.count() << "ms. Output so far:\n"
|
cWarning() << "Process" << m_command.first() << "timed out after" << m_timeout.count() << "ms."
|
||||||
<< Logger::NoQuote << process.readAllStandardOutput();
|
<< Logger::NoQuote << "Output so far:\n"
|
||||||
|
<< process.readAllStandardOutput();
|
||||||
return ProcessResult::Code::TimedOut;
|
return ProcessResult::Code::TimedOut;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -216,7 +217,7 @@ Calamares::Utils::Runner::run()
|
|||||||
|
|
||||||
if ( process.exitStatus() == QProcess::CrashExit )
|
if ( process.exitStatus() == QProcess::CrashExit )
|
||||||
{
|
{
|
||||||
cWarning() << "Process" << m_command.first() << "crashed. Output so far:\n" << Logger::NoQuote << output;
|
cWarning() << "Process" << m_command.first() << "crashed." << Logger::NoQuote << "Output so far:\n" << output;
|
||||||
return ProcessResult::Code::Crashed;
|
return ProcessResult::Code::Crashed;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -226,7 +227,7 @@ Calamares::Utils::Runner::run()
|
|||||||
{
|
{
|
||||||
if ( showDebug && !output.isEmpty() )
|
if ( showDebug && !output.isEmpty() )
|
||||||
{
|
{
|
||||||
cDebug() << Logger::SubEntry << "Finished. Exit code:" << r << "output:\n" << Logger::NoQuote << output;
|
cDebug() << Logger::SubEntry << "Finished. Exit code:" << r << Logger::NoQuote << "output:\n" << output;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else // if ( r != 0 )
|
else // if ( r != 0 )
|
||||||
@ -234,8 +235,8 @@ Calamares::Utils::Runner::run()
|
|||||||
if ( !output.isEmpty() )
|
if ( !output.isEmpty() )
|
||||||
{
|
{
|
||||||
cDebug() << Logger::SubEntry << "Target cmd:" << Logger::RedactedCommand( m_command ) << "Exit code:" << r
|
cDebug() << Logger::SubEntry << "Target cmd:" << Logger::RedactedCommand( m_command ) << "Exit code:" << r
|
||||||
<< "output:\n"
|
<< Logger::NoQuote << "output:\n"
|
||||||
<< Logger::NoQuote << output;
|
<< output;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -31,6 +31,8 @@
|
|||||||
|
|
||||||
#include <QtTest/QtTest>
|
#include <QtTest/QtTest>
|
||||||
|
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
@ -56,6 +58,8 @@ private Q_SLOTS:
|
|||||||
void testCommandConstructors();
|
void testCommandConstructors();
|
||||||
void testCommandConstructorsYAML();
|
void testCommandConstructorsYAML();
|
||||||
void testCommandRunning();
|
void testCommandRunning();
|
||||||
|
void testCommandTimeout();
|
||||||
|
void testCommandVerbose();
|
||||||
|
|
||||||
/** @section Test that all the UMask objects work correctly. */
|
/** @section Test that all the UMask objects work correctly. */
|
||||||
void testUmask();
|
void testUmask();
|
||||||
@ -454,6 +458,89 @@ LibCalamaresTests::testCommandRunning()
|
|||||||
tempRoot.setAutoRemove( true );
|
tempRoot.setAutoRemove( true );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
LibCalamaresTests::testCommandTimeout()
|
||||||
|
{
|
||||||
|
|
||||||
|
QTemporaryDir tempRoot( QDir::tempPath() + QStringLiteral( "/test-job-XXXXXX" ) );
|
||||||
|
tempRoot.setAutoRemove( false );
|
||||||
|
|
||||||
|
const QString testExecutable = tempRoot.filePath( "example.sh" );
|
||||||
|
|
||||||
|
cDebug() << "Creating example executable" << testExecutable;
|
||||||
|
|
||||||
|
{
|
||||||
|
QFile f( testExecutable );
|
||||||
|
QVERIFY( f.open( QIODevice::WriteOnly ) );
|
||||||
|
f.write( "#! /bin/sh\necho early\nsleep 3\necho late" );
|
||||||
|
f.close();
|
||||||
|
Calamares::Permissions::apply( testExecutable, 0755 );
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
Calamares::CommandList l( false ); // no chroot
|
||||||
|
Calamares::CommandLine c( testExecutable, {}, std::chrono::seconds( 2 ) );
|
||||||
|
l.push_back( c );
|
||||||
|
|
||||||
|
const auto r = l.run();
|
||||||
|
QVERIFY( !bool( r ) ); // Because it times out after 2 seconds
|
||||||
|
// The **command** timed out, but the job result is a generic "error"
|
||||||
|
// QCOMPARE( r.errorCode(), static_cast<std::underlying_type_t<Calamares::ProcessResult::Code>>(Calamares::ProcessResult::Code::TimedOut));
|
||||||
|
QCOMPARE( r.errorCode(), -1 );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
LibCalamaresTests::testCommandVerbose()
|
||||||
|
{
|
||||||
|
Logger::setupLogLevel( Logger::LOGDEBUG );
|
||||||
|
|
||||||
|
QTemporaryDir tempRoot( QDir::tempPath() + QStringLiteral( "/test-job-XXXXXX" ) );
|
||||||
|
tempRoot.setAutoRemove( false );
|
||||||
|
|
||||||
|
const QString testExecutable = tempRoot.filePath( "example.sh" );
|
||||||
|
|
||||||
|
cDebug() << "Creating example executable" << testExecutable;
|
||||||
|
{
|
||||||
|
QFile f( testExecutable );
|
||||||
|
QVERIFY( f.open( QIODevice::WriteOnly ) );
|
||||||
|
f.write( "#! /bin/sh\necho one\necho two\necho error 1>&2\nsleep 1; echo three\n" );
|
||||||
|
f.close();
|
||||||
|
Calamares::Permissions::apply( testExecutable, 0755 );
|
||||||
|
}
|
||||||
|
|
||||||
|
// Note that, because of the blocking way run() works,
|
||||||
|
// in this single-threaded test with no event loop,
|
||||||
|
// there's nothing for the verbose version to connect
|
||||||
|
// to for sending output.
|
||||||
|
|
||||||
|
cDebug() << "Running command non-verbose";
|
||||||
|
{
|
||||||
|
Calamares::CommandList l( false ); // no chroot
|
||||||
|
Calamares::CommandLine c( testExecutable, {}, std::chrono::seconds( 2 ) );
|
||||||
|
c.updateVerbose( false );
|
||||||
|
QVERIFY( !c.isVerbose() );
|
||||||
|
|
||||||
|
l.push_back( c );
|
||||||
|
|
||||||
|
const auto r = l.run();
|
||||||
|
QVERIFY( bool( r ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
cDebug() << "Running command verbosely";
|
||||||
|
{
|
||||||
|
Calamares::CommandList l( false ); // no chroot
|
||||||
|
Calamares::CommandLine c( testExecutable, {}, std::chrono::seconds( 2 ) );
|
||||||
|
c.updateVerbose( true );
|
||||||
|
QVERIFY( c.isVerbose() );
|
||||||
|
|
||||||
|
l.push_back( c );
|
||||||
|
|
||||||
|
const auto r = l.run();
|
||||||
|
QVERIFY( bool( r ) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
LibCalamaresTests::testUmask()
|
LibCalamaresTests::testUmask()
|
||||||
{
|
{
|
||||||
|
@ -60,6 +60,7 @@ ShellProcessJob::setConfigurationMap( const QVariantMap& configurationMap )
|
|||||||
{
|
{
|
||||||
timeout = 30;
|
timeout = 30;
|
||||||
}
|
}
|
||||||
|
bool verbose = Calamares::getBool( configurationMap, "verbose", false );
|
||||||
|
|
||||||
if ( configurationMap.contains( "script" ) )
|
if ( configurationMap.contains( "script" ) )
|
||||||
{
|
{
|
||||||
@ -69,6 +70,7 @@ ShellProcessJob::setConfigurationMap( const QVariantMap& configurationMap )
|
|||||||
{
|
{
|
||||||
cDebug() << "ShellProcessJob: \"script\" contains no commands for" << moduleInstanceKey();
|
cDebug() << "ShellProcessJob: \"script\" contains no commands for" << moduleInstanceKey();
|
||||||
}
|
}
|
||||||
|
m_commands->updateVerbose( verbose );
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -84,8 +84,20 @@
|
|||||||
---
|
---
|
||||||
# Set to true to run in host, rather than target system
|
# Set to true to run in host, rather than target system
|
||||||
dontChroot: false
|
dontChroot: false
|
||||||
# Tune this for the commands you're actually running
|
|
||||||
# timeout: 10
|
# Tune this for the commands you're actually running, or
|
||||||
|
# use the list-of-items form of commands to tune the timeout
|
||||||
|
# for each command individually.
|
||||||
|
timeout: 10
|
||||||
|
|
||||||
|
# This will copy the output from the command into the Calamares
|
||||||
|
# log file. No processing is done beyond log-each-line-separately,
|
||||||
|
# so this can introduce weirdness in the log if the script
|
||||||
|
# outputs e.g. escape codes.
|
||||||
|
#
|
||||||
|
# The default is `false`. This can also be set for each
|
||||||
|
# command individually.
|
||||||
|
verbose: false
|
||||||
|
|
||||||
# Script may be a single string (because false returns an error exit
|
# Script may be a single string (because false returns an error exit
|
||||||
# code, this will trigger a failure in the installation):
|
# code, this will trigger a failure in the installation):
|
||||||
@ -109,6 +121,8 @@ script:
|
|||||||
- "/usr/bin/true"
|
- "/usr/bin/true"
|
||||||
- command: "/usr/local/bin/slowloris"
|
- command: "/usr/local/bin/slowloris"
|
||||||
timeout: 3600
|
timeout: 3600
|
||||||
|
- command: "echo -e '\e[33;2mred\e[33;0m'"
|
||||||
|
verbose: true
|
||||||
|
|
||||||
# You can change the description of the job (as it is displayed in the
|
# You can change the description of the job (as it is displayed in the
|
||||||
# progress bar during installation) by defining an *i18n* key, which
|
# progress bar during installation) by defining an *i18n* key, which
|
||||||
|
@ -19,7 +19,10 @@ definitions:
|
|||||||
timeout:
|
timeout:
|
||||||
type: number
|
type: number
|
||||||
description: the (optional) timeout for this specific command (differently
|
description: the (optional) timeout for this specific command (differently
|
||||||
from the global setting)
|
from the global setting).
|
||||||
|
verbose:
|
||||||
|
type: boolean
|
||||||
|
description: when true, log output from the command to the Calamares log.
|
||||||
required:
|
required:
|
||||||
- command
|
- command
|
||||||
type: object
|
type: object
|
||||||
@ -34,6 +37,9 @@ properties:
|
|||||||
type: number
|
type: number
|
||||||
description: The (global) timeout for the command list in seconds. If unset, defaults
|
description: The (global) timeout for the command list in seconds. If unset, defaults
|
||||||
to 30 seconds.
|
to 30 seconds.
|
||||||
|
verbose:
|
||||||
|
type: boolean
|
||||||
|
description: when true, log output from the command to the Calamares log.
|
||||||
script:
|
script:
|
||||||
anyOf:
|
anyOf:
|
||||||
- $ref: '#definitions/command'
|
- $ref: '#definitions/command'
|
||||||
@ -55,3 +61,5 @@ properties:
|
|||||||
type: string
|
type: string
|
||||||
required:
|
required:
|
||||||
- name
|
- name
|
||||||
|
required:
|
||||||
|
- script
|
||||||
|
Loading…
Reference in New Issue
Block a user