libzypp 17.32.5
repomanagerwf.cc
Go to the documentation of this file.
1/*---------------------------------------------------------------------\
2| ____ _ __ __ ___ |
3| |__ / \ / / . \ . \ |
4| / / \ V /| _/ _/ |
5| / /__ | | | | | | |
6| /_____||_| |_| |_| |
7| |
8\---------------------------------------------------------------------*/
9#include "repomanagerwf.h"
10
12#include <utility>
13#include <zypp-core/zyppng/pipelines/MTry>
14#include <zypp-media/MediaException>
15#include <zypp-media/ng/Provide>
16#include <zypp-media/ng/ProvideSpec>
17
18#include <zypp/ng/Context>
22
23namespace zyppng {
24
25 using namespace zyppng::operators;
26
27 namespace {
28
29 template <class Executor, class OpType>
30 struct ProbeRepoLogic : public LogicBase<Executor, OpType>
31 {
32 protected:
34
35 public:
37 using ProvideType = typename remove_smart_ptr_t<ZyppContextRefType>::ProvideType;
38 using MediaHandle = typename ProvideType::MediaHandle;
39 using ProvideRes = typename ProvideType::Res;
40
41 ProbeRepoLogic(ZyppContextRefType zyppCtx, MediaHandle &&medium, zypp::Pathname &&path, std::optional<zypp::Pathname> &&targetPath )
42 : _zyppContext(std::move(zyppCtx))
43 , _medium(std::move(medium))
44 , _path(std::move(path))
45 , _targetPath(std::move(targetPath))
46 {}
47
49 const auto &url = _medium.baseUrl();
50 MIL << "going to probe the repo type at " << url << " (" << _path << ")" << std::endl;
51
52 if ( url.getScheme() == "dir" && ! zypp::PathInfo( url.getPathName()/_path ).isDir() ) {
53 // Handle non existing local directory in advance
54 MIL << "Probed type NONE (not exists) at " << url << " (" << _path << ")" << std::endl;
56 }
57
58 // prepare exception to be thrown if the type could not be determined
59 // due to a media exception. We can't throw right away, because of some
60 // problems with proxy servers returning an incorrect error
61 // on ftp file-not-found(bnc #335906). Instead we'll check another types
62 // before throwing.
63
64 std::shared_ptr<ProvideType> providerRef = _zyppContext->provider();
65
66 // TranslatorExplanation '%s' is an URL
67 _error = zypp::repo::RepoException (zypp::str::form( _("Error trying to read from '%s'"), url.asString().c_str() ));
68
69 // first try rpmmd
70 return providerRef->provide( _medium, _path/"repodata/repomd.xml", ProvideFileSpec().setCheckExistsOnly( !_targetPath.has_value() ) )
71 | and_then( maybeCopyResultToDest("repodata/repomd.xml") )
73 // try susetags if rpmmd fails and remember the error
74 | or_else( [this, providerRef]( std::exception_ptr err ) {
75 try {
76 std::rethrow_exception (err);
78 // do nothing
79 ;
80 } catch( const zypp::media::MediaException &e ) {
81 DBG << "problem checking for repodata/repomd.xml file" << std::endl;
82 _error.remember ( err );
83 _gotMediaError = true;
84 } catch( ... ) {
85 // any other error, we give up
86 return makeReadyResult( expected<zypp::repo::RepoType>::error( std::current_exception() ) );
87 }
88 return providerRef->provide( _medium, _path/"content", ProvideFileSpec().setCheckExistsOnly( !_targetPath.has_value() ) )
89 | and_then( maybeCopyResultToDest("content") )
91 })
92 // no rpmmd and no susetags!
93 | or_else( [this]( std::exception_ptr err ) {
94
95 try {
96 std::rethrow_exception (err);
98 // do nothing
99 ;
100 } catch( const zypp::media::MediaException &e ) {
101 DBG << "problem checking for content file" << std::endl;
102 _error.remember ( err );
103 _gotMediaError = true;
104 } catch( zypp::Exception &e ) {
106 // any other error, we give up
108 } catch(...) {
109 // any other error, we give up
110 return expected<zypp::repo::RepoType>::error( std::current_exception() );
111 }
112
113 const auto &url = _medium.baseUrl();
114
115 // if it is a non-downloading URL denoting a directory (bsc#1191286: and no plugin)
116 if ( ! ( url.schemeIsDownloading() || url.schemeIsPlugin() ) ) {
117
118 if ( _medium.localPath() && zypp::PathInfo(_medium.localPath().value()/_path).isDir() ) {
119 // allow empty dirs for now
120 MIL << "Probed type RPMPLAINDIR at " << url << " (" << _path << ")" << std::endl;
122 }
123 }
124
125 if( _gotMediaError )
127
128 MIL << "Probed type NONE at " << url << " (" << _path << ")" << std::endl;
130 })
131 ;
132 }
133
134 private:
139 auto maybeCopyResultToDest ( std::string &&subPath ) {
140 return [this, subPath = std::move(subPath)]( ProvideRes file ) -> MaybeAsyncRef<expected<void>> {
141 if ( _targetPath ) {
142 MIL << "Target path is set, copying " << file.file() << " to " << *_targetPath/subPath << std::endl;
143 return std::move(file)
144 | ProvideType::copyResultToDest( _zyppContext->provider(), *_targetPath/subPath)
145 | and_then([]( zypp::ManagedFile file ){ file.resetDispose(); return expected<void>::success(); } );
146 }
148 };
149 }
150
151 private:
152 ZyppContextRefType _zyppContext;
153 MediaHandle _medium;
155 std::optional<zypp::Pathname> _targetPath;
156
158 bool _gotMediaError = false;
159 };
160
161 }
162
164 {
165 return SimpleExecutor< ProbeRepoLogic, AsyncOp<expected<zypp::repo::RepoType>> >::run( std::move(ctx), std::move(medium), std::move(path), std::move(targetPath) );
166 }
167
169 {
170 return SimpleExecutor< ProbeRepoLogic, SyncOp<expected<zypp::repo::RepoType>> >::run( std::move(ctx), std::move(medium), std::move(path), std::move(targetPath) );
171 }
172
173
174
175 namespace {
176
177 template<typename Executor, class OpType>
178 struct CheckIfToRefreshMetadataLogic : public LogicBase<Executor, OpType> {
179
181 public:
182
183 using RefreshContextRefType = std::conditional_t<zyppng::detail::is_async_op_v<OpType>, repo::AsyncRefreshContextRef, repo::SyncRefreshContextRef>;
184 using ZyppContextRefType = typename RefreshContextRefType::element_type::ContextRefType;
185 using ZyppContextType = typename RefreshContextRefType::element_type::ContextType;
186 using ProvideType = typename ZyppContextType::ProvideType;
187 using MediaHandle = typename ProvideType::MediaHandle;
188 using ProvideRes = typename ProvideType::Res;
189
190 CheckIfToRefreshMetadataLogic( RefreshContextRefType refCtx, MediaHandle &&medium, ProgressObserverRef progressObserver )
191 : _refreshContext(std::move(refCtx))
192 , _progress(std::move( progressObserver ))
193 , _medium(std::move( medium ))
194 {}
195
197
198 MIL << "Going to CheckIfToRefreshMetadata" << std::endl;
199
200 return mtry( static_cast<void(*)(const zypp::RepoInfo &)>(zypp::assert_alias), _refreshContext->repoInfo() )
201 // | and_then( refreshGeoIPData( { url } ); ) << should this be even here? probably should be put where we attach the medium
202 | and_then( [this](){
203
204 const auto &info = _refreshContext->repoInfo();
205
206 MIL << "Check if to refresh repo " << _refreshContext->repoInfo().alias() << " at " << _medium.baseUrl() << " (" << info.type() << ")" << std::endl;
207
208 _mediarootpath = _refreshContext->rawCachePath();
209 zypp::filesystem::assert_dir(_mediarootpath );
210
211 // first check old (cached) metadata
213
214 if ( oldstatus.empty() ) {
215 MIL << "No cached metadata, going to refresh" << std::endl;
217 }
218
219 if ( _medium.baseUrl().schemeIsVolatile() ) {
220 MIL << "Never refresh CD/DVD" << std::endl;
222 }
223
224 if ( _refreshContext->policy() == repo::RefreshForced ) {
225 MIL << "Forced refresh!" << std::endl;
227 }
228
229 if ( _medium.baseUrl().schemeIsLocal() ) {
231 }
232
233 // Check whether repo.refresh.delay applies...
235 {
236 // bsc#1174016: Prerequisite to skipping the refresh is that metadata
237 // and solv cache status match. They will not, if the repos URL was
238 // changed e.g. due to changed repovars.
240
241 if ( oldstatus == cachestatus ) {
242 // difference in seconds
243 double diff = ::difftime( (zypp::Date::ValueType)zypp::Date::now(), (zypp::Date::ValueType)oldstatus.timestamp() ) / 60;
244 const auto refDelay = _refreshContext->zyppContext()->config().repo_refresh_delay();
245 if ( diff < refDelay ) {
246 if ( diff < 0 ) {
247 WAR << "Repository '" << info.alias() << "' was refreshed in the future!" << std::endl;
248 }
249 else {
250 MIL << "Repository '" << info.alias()
251 << "' has been refreshed less than repo.refresh.delay ("
252 << refDelay
253 << ") minutes ago. Advising to skip refresh" << std::endl;
255 }
256 }
257 }
258 else {
259 MIL << "Metadata and solv cache don't match. Check data on server..." << std::endl;
260 }
261 }
262
263 return info.type() | [this]( zypp::repo::RepoType repokind ) {
264 // if unknown: probe it
266 return RepoManagerWorkflow::probeRepoType( _refreshContext->zyppContext(), _medium, _refreshContext->repoInfo().path(), _refreshContext->targetDir() );
269
270 // make sure to remember the repo type
271 _refreshContext->repoInfo().setProbedType( repokind );
272
273 auto dlContext = std::make_shared<repo::DownloadContext<ZyppContextRefType>>( _refreshContext->zyppContext(), _refreshContext->repoInfo(), _refreshContext->targetDir() );
276 // check status
277 if ( oldstatus == newstatus ) {
278 MIL << "repo has not changed" << std::endl;
281 }
282 else { // includes newstatus.empty() if e.g. repo format changed
283 MIL << "repo has changed, going to refresh" << std::endl;
284 MIL << "Old status: " << oldstatus << " New Status: " << newstatus << std::endl;
286 }
287 });
288 });
289 });
290 }
291
292 protected:
293 RefreshContextRefType _refreshContext;
294 ProgressObserverRef _progress;
295 MediaHandle _medium;
297 };
298 }
299
304
309
310
311 namespace {
312
313 template<typename Executor, class OpType>
314 struct RefreshMetadataLogic : public LogicBase<Executor, OpType>{
315
317
318 public:
319
320 using RefreshContextRefType = std::conditional_t<zyppng::detail::is_async_op_v<OpType>, repo::AsyncRefreshContextRef, repo::SyncRefreshContextRef>;
321 using ZyppContextRefType = typename RefreshContextRefType::element_type::ContextRefType;
322 using ZyppContextType = typename RefreshContextRefType::element_type::ContextType;
323 using ProvideType = typename ZyppContextType::ProvideType;
324 using MediaHandle = typename ProvideType::MediaHandle;
325 using ProvideRes = typename ProvideType::Res;
326
327 using DlContextType = repo::DownloadContext<ZyppContextRefType>;
328 using DlContextRefType = std::shared_ptr<DlContextType>;
329
330 RefreshMetadataLogic( RefreshContextRefType refCtx, MediaHandle &&medium, ProgressObserverRef progressObserver )
331 : _refreshContext(std::move(refCtx))
332 , _progress ( std::move( progressObserver ) )
333 , _medium ( std::move( medium ) )
334 {
335 MIL << "Constructor called" << std::endl;
336 }
337
339
340 // manually resolv the overloaded func
341 constexpr auto assert_alias_cb = static_cast<void (*)( const zypp::RepoInfo &)>(zypp::assert_alias);
342
343 return mtry(assert_alias_cb, _refreshContext->repoInfo() )
344 | and_then( [this](){ return mtry(zypp::assert_urls, _refreshContext->repoInfo() ); })
345 | and_then( [this](){ return RepoManagerWorkflow::checkIfToRefreshMetadata ( _refreshContext, _medium, _progress ); })
346 | and_then( [this]( repo::RefreshCheckStatus status ){
347
348 MIL << "RefreshCheckStatus returned: " << status << std::endl;
349
350 // check whether to refresh metadata
351 // if the check fails for this url, it throws, so another url will be checked
352 if ( status != repo::REFRESH_NEEDED )
353 return makeReadyResult ( expected<RefreshContextRefType>::success( std::move(_refreshContext) ) );
354
355 MIL << "Going to refresh metadata from " << _medium.baseUrl() << std::endl;
356
357 // bsc#1048315: Always re-probe in case of repo format change.
358 // TODO: Would be sufficient to verify the type and re-probe
359 // if verification failed (or type is RepoType::NONE)
360 return RepoManagerWorkflow::probeRepoType ( _refreshContext->zyppContext(), _medium, _refreshContext->repoInfo().path(), _refreshContext->targetDir() )
362
363 auto &info = _refreshContext->repoInfo();
364
365 if ( info.type() != repokind ) {
366 _refreshContext->setProbedType( repokind );
367 // Adjust the probed type in RepoInfo
368 info.setProbedType( repokind ); // lazy init!
369 }
370
371 // no need to continue with an unknown type
374
375 const zypp::Pathname &mediarootpath = _refreshContext->rawCachePath();
377 auto exception = ZYPP_EXCPT_PTR (zypp::Exception(zypp::str::form( _("Can't create %s"), mediarootpath.c_str() )));
378 return makeReadyResult( expected<DlContextRefType>::error( std::move(exception) ));
379 }
380
381 auto dlContext = std::make_shared<DlContextType>( _refreshContext->zyppContext(), _refreshContext->repoInfo(), _refreshContext->targetDir() );
382 dlContext->setPluginRepoverification( _refreshContext->pluginRepoverification() );
383
384 return RepoDownloaderWorkflow::download ( dlContext, _medium, _progress );
385
386 })
387 | and_then([this]( DlContextRefType && ) {
388
389 // ok we have the metadata, now exchange
390 // the contents
391 _refreshContext->saveToRawCache();
392 // if ( ! isTmpRepo( info ) )
393 // reposManip(); // remember to trigger appdata refresh
394
395 // we are done.
396 return expected<RefreshContextRefType>::success( std::move(_refreshContext) );
397 })
398
399 ;
400 });
401
402
403
404
405#if 0
406 assert_alias(info);
407 assert_urls(info);
408
409 // make sure geoIP data is up 2 date
410 refreshGeoIPData( info.baseUrls() );
411
412 // we will throw this later if no URL checks out fine
413 RepoException rexception( info, PL_("Valid metadata not found at specified URL",
414 "Valid metadata not found at specified URLs",
415 info.baseUrlsSize() ) );
416
417 // Suppress (interactive) media::MediaChangeReport if we in have multiple basurls (>1)
418 media::ScopedDisableMediaChangeReport guard( info.baseUrlsSize() > 1 );
419 // try urls one by one
420 for ( RepoInfo::urls_const_iterator it = info.baseUrlsBegin(); it != info.baseUrlsEnd(); ++it )
421 {
422 try
423 {
424 Url url(*it);
425
426 // check whether to refresh metadata
427 // if the check fails for this url, it throws, so another url will be checked
428 if (checkIfToRefreshMetadata(info, url, policy)!=REFRESH_NEEDED)
429 return;
430
431 MIL << "Going to refresh metadata from " << url << endl;
432
433
434 repo::RepoType repokind = info.type();
435 {
436 repo::RepoType probed = probe( *it, info.path() );
437
438
439 }
440
441 aaaa
442
443 if ( ( repokind.toEnum() == RepoType::RPMMD_e ) ||
444 ( repokind.toEnum() == RepoType::YAST2_e ) )
445 {
446 MediaSetAccess media(url);
448
449 MIL << "Creating downloader for [ " << info.alias() << " ]" << endl;
450
451 if ( repokind.toEnum() == RepoType::RPMMD_e ) {
452 downloader_ptr.reset(new yum::Downloader(info, mediarootpath ));
453 if ( _pluginRepoverification.checkIfNeeded() )
454 downloader_ptr->setPluginRepoverification( _pluginRepoverification ); // susetags is dead so we apply just to yum
455 }
456 else
457 downloader_ptr.reset( new susetags::Downloader(info, mediarootpath) );
458
465 for_( it, repoBegin(), repoEnd() )
466 {
467 Pathname cachepath(rawcache_path_for_repoinfo( _options, *it ));
468 if ( PathInfo(cachepath).isExist() )
469 downloader_ptr->addCachePath(cachepath);
470
471 }
472
473 downloader_ptr->download( media, tmpdir.path() );
474 }
475 else if ( repokind.toEnum() == RepoType::RPMPLAINDIR_e )
476 {
477 // as substitute for real metadata remember the checksum of the directory we refreshed
478 MediaMounter media( url );
479 RepoStatus newstatus = RepoStatus( media.getPathName( info.path() ) ); // dir status
480
481 Pathname productpath( tmpdir.path() / info.path() );
483 newstatus.saveToCookieFile( productpath/"cookie" );
484 }
485 else
486 {
487 ZYPP_THROW(RepoUnknownTypeException( info ));
488 }
489
490 // ok we have the metadata, now exchange
491 // the contents
493 if ( ! isTmpRepo( info ) )
494 reposManip(); // remember to trigger appdata refresh
495
496 // we are done.
497 return;
498 }
499 catch ( const Exception &e )
500 {
501 ZYPP_CAUGHT(e);
502 ERR << "Trying another url..." << endl;
503
504 // remember the exception caught for the *first URL*
505 // if all other URLs fail, the rexception will be thrown with the
506 // cause of the problem of the first URL remembered
507 if (it == info.baseUrlsBegin())
508 rexception.remember(e);
509 else
510 rexception.addHistory( e.asUserString() );
511
512 }
513 } // for every url
514 ERR << "No more urls..." << endl;
516
517#endif
518
519
520 }
521
522 RefreshContextRefType _refreshContext;
523 ProgressObserverRef _progress;
524 MediaHandle _medium;
526
527 };
528 }
529
530 namespace RepoManagerWorkflow {
535
540 }
541}
Reference counted access to a Tp object calling a custom Dispose function when the last AutoDispose h...
Definition AutoDispose.h:95
void resetDispose()
Set no dispose function.
void reset()
Reset to default Ctor values.
time_t ValueType
Definition Date.h:38
static Date now()
Return the current time.
Definition Date.h:78
Base class for Exception.
Definition Exception.h:147
void remember(const Exception &old_r)
Store an other Exception as history.
Definition Exception.cc:124
Media access layer responsible for handling files distributed on a set of media with media change and...
What is known about a repository.
Definition RepoInfo.h:72
transform_iterator< repo::RepoVariablesUrlReplacer, url_set::const_iterator > urls_const_iterator
Definition RepoInfo.h:110
Track changing files or directories.
Definition RepoStatus.h:41
Url manipulation class.
Definition Url.h:92
Wrapper class for stat/lstat.
Definition PathInfo.h:222
Just inherits Exception to separate media exceptions.
Exception for repository handling.
thrown when it was impossible to determine this repo type.
A ProvideRes object is a reference counted ownership of a resource in the cache provided by a Provide...
Definition provideres.h:36
static expected success(ConsParams &&...params)
Definition expected.h:115
#define ZYPP_ENABLE_LOGIC_BASE(Executor, OpType)
Definition Arch.h:364
typename conditional< B, T, F >::type conditional_t
Definition TypeTraits.h:39
int assert_dir(const Pathname &path, unsigned mode)
Like 'mkdir -p'.
Definition PathInfo.cc:320
int exchange(const Pathname &lpath, const Pathname &rpath)
Exchanges two files or directories.
Definition PathInfo.cc:757
std::string form(const char *format,...) __attribute__((format(printf
Printf style construction of std::string.
Definition String.cc:37
Pathname rawcache_path_for_repoinfo(const RepoManagerOptions &opt, const RepoInfo &info)
Calculates the raw cache path for a repository, this is usually /var/cache/zypp/alias.
bool isTmpRepo(const RepoInfo &info_r)
Whether repo is not under RM control and provides its own methadata paths.
void assert_alias(const RepoInfo &info)
void assert_urls(const RepoInfo &info)
AsyncOpRef< expected< repo::AsyncDownloadContextRef > > download(repo::AsyncDownloadContextRef dl, ProvideMediaHandle mediaHandle, ProgressObserverRef progressObserver=nullptr)
AsyncOpRef< expected< zypp::RepoStatus > > repoStatus(repo::AsyncDownloadContextRef dl, ProvideMediaHandle mediaHandle)
AsyncOpRef< expected< zypp::repo::RepoType > > probeRepoType(ContextRef ctx, ProvideMediaHandle medium, zypp::Pathname path, std::optional< zypp::Pathname > targetPath={})
AsyncOpRef< expected< repo::AsyncRefreshContextRef > > refreshMetadata(repo::AsyncRefreshContextRef refCtx, ProvideMediaHandle medium, ProgressObserverRef progressObserver)
AsyncOpRef< expected< repo::RefreshCheckStatus > > checkIfToRefreshMetadata(repo::AsyncRefreshContextRef refCtx, ProvideMediaHandle medium, ProgressObserverRef progressObserver=nullptr)
RefreshCheckStatus
Possibly return state of checkIfRefreshMEtadata function.
Definition refresh.h:40
@ REFRESH_NEEDED
refresh is needed
Definition refresh.h:41
@ REPO_CHECK_DELAYED
refresh is delayed due to settings
Definition refresh.h:43
@ REPO_UP_TO_DATE
repository not changed
Definition refresh.h:42
@ RefreshForced
Definition refresh.h:33
@ RefreshIfNeededIgnoreDelay
Definition refresh.h:34
Exp mtry(F &&f, Args &&...args)
Definition mtry.h:28
std::conditional_t< isAsync, AsyncOpRef< T >, T > makeReadyResult(T &&result)
Definition asyncop.h:297
typename remove_smart_ptr< T >::type remove_smart_ptr_t
ResultType or_else(const expected< T, E > &exp, Function &&f)
Definition expected.h:407
ResultType and_then(const expected< T, E > &exp, Function &&f)
Definition expected.h:367
zypp::Pathname _path
ZyppContextRefType _zyppContext
std::optional< zypp::Pathname > _targetPath
RefreshContextRefType _refreshContext
MediaHandle _medium
zypp::repo::RepoException _error
ProgressObserverRef _progress
bool _gotMediaError
zypp::Pathname _mediarootpath
static void touchIndexFile(const RepoInfo &info, const RepoManagerOptions &options)
RepoStatus cacheStatus(const RepoInfo &info) const
static RepoStatus metadataStatus(const RepoInfo &info, const RepoManagerOptions &options)
Temporarily disable MediaChangeReport Sometimes helpful to suppress interactive messages connected to...
Repository type enumeration.
Definition RepoType.h:28
static const RepoType YAST2
Definition RepoType.h:30
static const RepoType RPMMD
Definition RepoType.h:29
static const RepoType NONE
Definition RepoType.h:32
static const RepoType RPMPLAINDIR
Definition RepoType.h:31
#define for_(IT, BEG, END)
Convenient for-loops using iterator.
Definition Easy.h:28
#define ZYPP_CAUGHT(EXCPT)
Drops a logline telling the Exception was caught (in order to handle it).
Definition Exception.h:437
#define ZYPP_EXCPT_PTR(EXCPT)
Drops a logline and returns Exception as a std::exception_ptr.
Definition Exception.h:433
#define ZYPP_THROW(EXCPT)
Drops a logline and throws the Exception.
Definition Exception.h:429
#define PL_(MSG1, MSG2, N)
Definition Gettext.h:40
#define _(MSG)
Definition Gettext.h:37
#define DBG
Definition Logger.h:95
#define MIL
Definition Logger.h:96
#define ERR
Definition Logger.h:98
#define WAR
Definition Logger.h:97