[netinstall] Improve loader queue API a bit

- use load() to start loading
- the FetchNextUnless class is useful in more spots in
  the loading process
- set status explicitly on success (otherwise, a failure in a
  previous URL would leave a failure message lying around even
  when the module shows something useful)
This commit is contained in:
Adriaan de Groot 2021-03-19 12:30:09 +01:00
parent 03d086a233
commit fdfe52efe2
4 changed files with 69 additions and 35 deletions

View File

@ -54,9 +54,11 @@ Config::status() const
case Status::FailedBadData: case Status::FailedBadData:
return tr( "Network Installation. (Disabled: Received invalid groups data)" ); return tr( "Network Installation. (Disabled: Received invalid groups data)" );
case Status::FailedInternalError: case Status::FailedInternalError:
return tr( "Network Installation. (Disabled: internal error)" ); return tr( "Network Installation. (Disabled: Internal error)" );
case Status::FailedNetworkError: case Status::FailedNetworkError:
return tr( "Network Installation. (Disabled: Unable to fetch package lists, check your network connection)" ); return tr( "Network Installation. (Disabled: Unable to fetch package lists, check your network connection)" );
case Status::FailedNoData:
return tr( "Network Installation. (Disabled: No package list)" );
} }
__builtin_unreachable(); __builtin_unreachable();
} }
@ -89,6 +91,11 @@ Config::loadGroupList( const QVariantList& groupData )
if ( m_model->rowCount() < 1 ) if ( m_model->rowCount() < 1 )
{ {
cWarning() << "NetInstall groups data was empty."; cWarning() << "NetInstall groups data was empty.";
setStatus( Status::FailedNoData );
}
else
{
setStatus( Status::Ok );
} }
emit statusReady(); emit statusReady();
} }
@ -134,7 +141,7 @@ Config::setConfigurationMap( const QVariantMap& configurationMap )
m_queue = new LoaderQueue( this ); m_queue = new LoaderQueue( this );
m_queue->append( SourceItem::makeSourceItem( groupsUrlVariant.toString(), configurationMap ) ); m_queue->append( SourceItem::makeSourceItem( groupsUrlVariant.toString(), configurationMap ) );
} }
else if ( groupsUrlVariant.type() == QVariant::StringList ) else if ( groupsUrlVariant.type() == QVariant::List )
{ {
m_queue = new LoaderQueue( this ); m_queue = new LoaderQueue( this );
for ( const auto& s : groupsUrlVariant.toStringList() ) for ( const auto& s : groupsUrlVariant.toStringList() )
@ -142,10 +149,11 @@ Config::setConfigurationMap( const QVariantMap& configurationMap )
m_queue->append( SourceItem::makeSourceItem( s, configurationMap ) ); m_queue->append( SourceItem::makeSourceItem( s, configurationMap ) );
} }
} }
if ( m_queue ) if ( m_queue && m_queue->count() > 0 )
{ {
cDebug() << "Loading netinstall from" << m_queue->count() << "alternate sources.";
connect( m_queue, &LoaderQueue::done, this, &Config::loadingDone ); connect( m_queue, &LoaderQueue::done, this, &Config::loadingDone );
QMetaObject::invokeMethod( m_queue, "fetchNext", Qt::QueuedConnection ); m_queue->load();
} }
} }

View File

@ -47,7 +47,9 @@ public:
FailedBadConfiguration, FailedBadConfiguration,
FailedInternalError, FailedInternalError,
FailedNetworkError, FailedNetworkError,
FailedBadData FailedBadData,
FailedNoData
}; };
QString status() const; QString status() const;

View File

@ -20,6 +20,32 @@
#include <QNetworkReply> #include <QNetworkReply>
#include <QTimer> #include <QTimer>
/** @brief Call fetchNext() on the queue if it can
*
* On destruction, a new call to fetchNext() is queued, so that
* the queue continues loading. Calling release() before the
* destructor skips the fetchNext(), ending the queue-loading.
*/
class FetchNextUnless
{
public:
FetchNextUnless( LoaderQueue* q )
: m_q( q )
{
}
~FetchNextUnless()
{
if ( m_q )
{
QMetaObject::invokeMethod( m_q, "fetchNext", Qt::QueuedConnection );
}
}
void release() { m_q = nullptr; }
private:
LoaderQueue* m_q = nullptr;
};
SourceItem SourceItem
SourceItem::makeSourceItem( const QString& groupsUrl, const QVariantMap& configurationMap ) SourceItem::makeSourceItem( const QString& groupsUrl, const QVariantMap& configurationMap )
{ {
@ -45,6 +71,13 @@ LoaderQueue::append( SourceItem&& i )
m_queue.append( std::move( i ) ); m_queue.append( std::move( i ) );
} }
void
LoaderQueue::load()
{
QMetaObject::invokeMethod( this, "fetchNext", Qt::QueuedConnection );
}
void void
LoaderQueue::fetchNext() LoaderQueue::fetchNext()
{ {
@ -67,13 +100,16 @@ LoaderQueue::fetchNext()
} }
} }
void void
LoaderQueue::fetch( const QUrl& url ) LoaderQueue::fetch( const QUrl& url )
{ {
FetchNextUnless next( this );
if ( !url.isValid() ) if ( !url.isValid() )
{ {
m_config->setStatus( Config::Status::FailedBadConfiguration ); m_config->setStatus( Config::Status::FailedBadConfiguration );
cDebug() << "Invalid URL" << url;
return;
} }
using namespace CalamaresUtils::Network; using namespace CalamaresUtils::Network;
@ -85,42 +121,20 @@ LoaderQueue::fetch( const QUrl& url )
if ( !reply ) if ( !reply )
{ {
cDebug() << Logger::Continuation << "request failed immediately."; cDebug() << Logger::SubEntry << "Request failed immediately.";
// If nobody sets a different status, this will remain
m_config->setStatus( Config::Status::FailedBadConfiguration ); m_config->setStatus( Config::Status::FailedBadConfiguration );
} }
else else
{ {
// When the network request is done, **then** we might
// do the next item from the queue, so don't call fetchNext() now.
next.release();
m_reply = reply; m_reply = reply;
connect( reply, &QNetworkReply::finished, this, &LoaderQueue::dataArrived ); connect( reply, &QNetworkReply::finished, this, &LoaderQueue::dataArrived );
} }
} }
/** @brief Call fetchNext() on the queue if it can
*
* On destruction, a new call to fetchNext() is queued, so that
* the queue continues loading. Calling release() before the
* destructor skips the fetchNext(), ending the queue-loading.
*/
class FetchNextUnless
{
public:
FetchNextUnless( LoaderQueue* q )
: m_q( q )
{
}
~FetchNextUnless()
{
if ( m_q )
{
QMetaObject::invokeMethod( m_q, "fetchNext", Qt::QueuedConnection );
}
}
void release() { m_q = nullptr; }
private:
LoaderQueue* m_q = nullptr;
};
void void
LoaderQueue::dataArrived() LoaderQueue::dataArrived()
{ {

View File

@ -41,7 +41,14 @@ struct SourceItem
static SourceItem makeSourceItem( const QString& groupsUrl, const QVariantMap& configurationMap ); static SourceItem makeSourceItem( const QString& groupsUrl, const QVariantMap& configurationMap );
}; };
/** @brief Queue of source items to load
*
* Queue things up by calling append() and then kick things off
* by calling load(). This will try to load the items, in order;
* the first one that succeeds will end the loading process.
*
* Signal done() is emitted when done (also when all of the items fail).
*/
class LoaderQueue : public QObject class LoaderQueue : public QObject
{ {
Q_OBJECT Q_OBJECT
@ -49,9 +56,12 @@ public:
LoaderQueue( Config* parent ); LoaderQueue( Config* parent );
void append( SourceItem&& i ); void append( SourceItem&& i );
void fetchNext(); int count() const { return m_queue.count(); }
public Q_SLOTS: public Q_SLOTS:
void load();
void fetchNext();
void fetch( const QUrl& url ); void fetch( const QUrl& url );
void dataArrived(); void dataArrived();