Changeset View
Standalone View
swh/loader/core/loader.py
Show First 20 Lines • Show All 74 Lines • ▼ Show 20 Lines | class BaseLoader: | |||||||||||||||||||||||||||||||||||||||||||||||||
Args: | Args: | |||||||||||||||||||||||||||||||||||||||||||||||||
lister_name: Name of the lister which triggered this load. | lister_name: Name of the lister which triggered this load. | |||||||||||||||||||||||||||||||||||||||||||||||||
If provided, the loader will try to use the forge's API to retrieve extrinsic | If provided, the loader will try to use the forge's API to retrieve extrinsic | |||||||||||||||||||||||||||||||||||||||||||||||||
metadata | metadata | |||||||||||||||||||||||||||||||||||||||||||||||||
lister_instance_name: Name of the lister instance which triggered this load. | lister_instance_name: Name of the lister instance which triggered this load. | |||||||||||||||||||||||||||||||||||||||||||||||||
Must be None iff lister_name is, but it may be the empty string for listers | Must be None iff lister_name is, but it may be the empty string for listers | |||||||||||||||||||||||||||||||||||||||||||||||||
with a single instance. | with a single instance. | |||||||||||||||||||||||||||||||||||||||||||||||||
""" | """ | |||||||||||||||||||||||||||||||||||||||||||||||||
visit_type: str | visit_type: str | |||||||||||||||||||||||||||||||||||||||||||||||||
origin: Origin | origin: Origin | |||||||||||||||||||||||||||||||||||||||||||||||||
loaded_snapshot_id: Optional[Sha1Git] | loaded_snapshot_id: Optional[Sha1Git] | |||||||||||||||||||||||||||||||||||||||||||||||||
parent_origins: Optional[List[Origin]] | parent_origins: Optional[List[Origin]] | |||||||||||||||||||||||||||||||||||||||||||||||||
"""If the given origin is a "forge fork" (ie. created with the "Fork" button | """If the given origin is a "forge fork" (ie. created with the "Fork" button | |||||||||||||||||||||||||||||||||||||||||||||||||
of GitHub-like forges), :meth:`build_extrinsic_origin_metadata` sets this to | of GitHub-like forges), :meth:`build_extrinsic_origin_metadata` sets this to | |||||||||||||||||||||||||||||||||||||||||||||||||
a list of origins it was forked from; closest parent first.""" | a list of origins it was forked from; closest parent first.""" | |||||||||||||||||||||||||||||||||||||||||||||||||
def __init__( | def __init__( | |||||||||||||||||||||||||||||||||||||||||||||||||
self, | self, | |||||||||||||||||||||||||||||||||||||||||||||||||
storage: StorageInterface, | storage: StorageInterface, | |||||||||||||||||||||||||||||||||||||||||||||||||
origin_url: str, | origin_url: str, | |||||||||||||||||||||||||||||||||||||||||||||||||
logging_class: Optional[str] = None, | logging_class: Optional[str] = None, | |||||||||||||||||||||||||||||||||||||||||||||||||
save_data_path: Optional[str] = None, | save_data_path: Optional[str] = None, | |||||||||||||||||||||||||||||||||||||||||||||||||
max_content_size: Optional[int] = None, | max_content_size: Optional[int] = None, | |||||||||||||||||||||||||||||||||||||||||||||||||
lister_name: Optional[str] = None, | lister_name: Optional[str] = None, | |||||||||||||||||||||||||||||||||||||||||||||||||
lister_instance_name: Optional[str] = None, | lister_instance_name: Optional[str] = None, | |||||||||||||||||||||||||||||||||||||||||||||||||
metadata_fetcher_credentials: CredentialsType = None, | metadata_fetcher_credentials: CredentialsType = None, | |||||||||||||||||||||||||||||||||||||||||||||||||
create_partial_snapshot: bool = False, | ||||||||||||||||||||||||||||||||||||||||||||||||||
): | ): | |||||||||||||||||||||||||||||||||||||||||||||||||
if lister_name == "": | if lister_name == "": | |||||||||||||||||||||||||||||||||||||||||||||||||
raise ValueError("lister_name must not be the empty string") | raise ValueError("lister_name must not be the empty string") | |||||||||||||||||||||||||||||||||||||||||||||||||
if lister_name is None and lister_instance_name is not None: | if lister_name is None and lister_instance_name is not None: | |||||||||||||||||||||||||||||||||||||||||||||||||
raise ValueError( | raise ValueError( | |||||||||||||||||||||||||||||||||||||||||||||||||
f"lister_name is None but lister_instance_name is {lister_instance_name!r}" | f"lister_name is None but lister_instance_name is {lister_instance_name!r}" | |||||||||||||||||||||||||||||||||||||||||||||||||
) | ) | |||||||||||||||||||||||||||||||||||||||||||||||||
if lister_name is not None and lister_instance_name is None: | if lister_name is not None and lister_instance_name is None: | |||||||||||||||||||||||||||||||||||||||||||||||||
raise ValueError( | raise ValueError( | |||||||||||||||||||||||||||||||||||||||||||||||||
f"lister_instance_name is None but lister_name is {lister_name!r}" | f"lister_instance_name is None but lister_name is {lister_name!r}" | |||||||||||||||||||||||||||||||||||||||||||||||||
) | ) | |||||||||||||||||||||||||||||||||||||||||||||||||
self.storage = storage | self.storage = storage | |||||||||||||||||||||||||||||||||||||||||||||||||
self.origin = Origin(url=origin_url) | self.origin = Origin(url=origin_url) | |||||||||||||||||||||||||||||||||||||||||||||||||
self.max_content_size = int(max_content_size) if max_content_size else None | self.max_content_size = int(max_content_size) if max_content_size else None | |||||||||||||||||||||||||||||||||||||||||||||||||
self.lister_name = lister_name | self.lister_name = lister_name | |||||||||||||||||||||||||||||||||||||||||||||||||
self.lister_instance_name = lister_instance_name | self.lister_instance_name = lister_instance_name | |||||||||||||||||||||||||||||||||||||||||||||||||
self.metadata_fetcher_credentials = metadata_fetcher_credentials or {} | self.metadata_fetcher_credentials = metadata_fetcher_credentials or {} | |||||||||||||||||||||||||||||||||||||||||||||||||
self.create_partial_snapshot = create_partial_snapshot | ||||||||||||||||||||||||||||||||||||||||||||||||||
if logging_class is None: | if logging_class is None: | |||||||||||||||||||||||||||||||||||||||||||||||||
logging_class = "%s.%s" % ( | logging_class = "%s.%s" % ( | |||||||||||||||||||||||||||||||||||||||||||||||||
self.__class__.__module__, | self.__class__.__module__, | |||||||||||||||||||||||||||||||||||||||||||||||||
self.__class__.__name__, | self.__class__.__name__, | |||||||||||||||||||||||||||||||||||||||||||||||||
) | ) | |||||||||||||||||||||||||||||||||||||||||||||||||
self.log = logging.getLogger(logging_class) | self.log = logging.getLogger(logging_class) | |||||||||||||||||||||||||||||||||||||||||||||||||
▲ Show 20 Lines • Show All 149 Lines • ▼ Show 20 Lines | def process_data(self) -> bool: | |||||||||||||||||||||||||||||||||||||||||||||||||
Returns: | Returns: | |||||||||||||||||||||||||||||||||||||||||||||||||
a value that is interpreted as a boolean. If True, fetch_data needs | a value that is interpreted as a boolean. If True, fetch_data needs | |||||||||||||||||||||||||||||||||||||||||||||||||
to be called again to complete loading. | to be called again to complete loading. | |||||||||||||||||||||||||||||||||||||||||||||||||
Ignored if ``fetch_data`` already returned :const:`False`. | Ignored if ``fetch_data`` already returned :const:`False`. | |||||||||||||||||||||||||||||||||||||||||||||||||
""" | """ | |||||||||||||||||||||||||||||||||||||||||||||||||
return True | return True | |||||||||||||||||||||||||||||||||||||||||||||||||
def store_data(self) -> None: | def store_data(self, create_partial_snapshot: bool = False): | |||||||||||||||||||||||||||||||||||||||||||||||||
"""Store fetched data in the database. | """Store fetched data in the database. | |||||||||||||||||||||||||||||||||||||||||||||||||
Should call the :func:`maybe_load_xyz` methods, which handle the | Use create_partial_snapshot boolean to to create partial snapshot at the end of | |||||||||||||||||||||||||||||||||||||||||||||||||
bundles sent to storage, rather than send directly. | each call of this method. This could help when loading large repositories is | |||||||||||||||||||||||||||||||||||||||||||||||||
happening and a crash happens for some reasons (running out of disk, memory, | ||||||||||||||||||||||||||||||||||||||||||||||||||
etc...). | ||||||||||||||||||||||||||||||||||||||||||||||||||
""" | """ | |||||||||||||||||||||||||||||||||||||||||||||||||
raise NotImplementedError | raise NotImplementedError | |||||||||||||||||||||||||||||||||||||||||||||||||
def load_status(self) -> Dict[str, str]: | def load_status(self) -> Dict[str, str]: | |||||||||||||||||||||||||||||||||||||||||||||||||
"""Detailed loading status. | """Detailed loading status. | |||||||||||||||||||||||||||||||||||||||||||||||||
Defaults to logging an eventful load. | Defaults to logging an eventful load. | |||||||||||||||||||||||||||||||||||||||||||||||||
▲ Show 20 Lines • Show All 106 Lines • ▼ Show 20 Lines | def load(self) -> Dict[str, str]: | |||||||||||||||||||||||||||||||||||||||||||||||||
more_data_to_fetch = self.fetch_data() | more_data_to_fetch = self.fetch_data() | |||||||||||||||||||||||||||||||||||||||||||||||||
t2 = time.monotonic() | t2 = time.monotonic() | |||||||||||||||||||||||||||||||||||||||||||||||||
total_time_fetch_data += t2 - t1 | total_time_fetch_data += t2 - t1 | |||||||||||||||||||||||||||||||||||||||||||||||||
more_data_to_fetch = self.process_data() and more_data_to_fetch | more_data_to_fetch = self.process_data() and more_data_to_fetch | |||||||||||||||||||||||||||||||||||||||||||||||||
t3 = time.monotonic() | t3 = time.monotonic() | |||||||||||||||||||||||||||||||||||||||||||||||||
total_time_process_data += t3 - t2 | total_time_process_data += t3 - t2 | |||||||||||||||||||||||||||||||||||||||||||||||||
self.store_data() | more_data_to_fetch = self.store_data( | |||||||||||||||||||||||||||||||||||||||||||||||||
self.create_partial_snapshot and more_data_to_fetch | ||||||||||||||||||||||||||||||||||||||||||||||||||
) | ||||||||||||||||||||||||||||||||||||||||||||||||||
vlorentz: This should use a keyword argument, for readability (make it clear what the argument means) | ||||||||||||||||||||||||||||||||||||||||||||||||||
t4 = time.monotonic() | t4 = time.monotonic() | |||||||||||||||||||||||||||||||||||||||||||||||||
total_time_store_data += t4 - t3 | total_time_store_data += t4 - t3 | |||||||||||||||||||||||||||||||||||||||||||||||||
if not more_data_to_fetch: | if not more_data_to_fetch: | |||||||||||||||||||||||||||||||||||||||||||||||||
break | break | |||||||||||||||||||||||||||||||||||||||||||||||||
self.statsd_timing("fetch_data", total_time_fetch_data * 1000.0) | self.statsd_timing("fetch_data", total_time_fetch_data * 1000.0) | |||||||||||||||||||||||||||||||||||||||||||||||||
self.statsd_timing("process_data", total_time_process_data * 1000.0) | self.statsd_timing("process_data", total_time_process_data * 1000.0) | |||||||||||||||||||||||||||||||||||||||||||||||||
self.statsd_timing("store_data", total_time_store_data * 1000.0) | self.statsd_timing("store_data", total_time_store_data * 1000.0) | |||||||||||||||||||||||||||||||||||||||||||||||||
status = self.visit_status() | status = self.visit_status() | |||||||||||||||||||||||||||||||||||||||||||||||||
visit_status = OriginVisitStatus( | visit_status = OriginVisitStatus( | |||||||||||||||||||||||||||||||||||||||||||||||||
origin=self.origin.url, | origin=self.origin.url, | |||||||||||||||||||||||||||||||||||||||||||||||||
visit=self.visit.visit, | visit=self.visit.visit, | |||||||||||||||||||||||||||||||||||||||||||||||||
type=self.visit_type, | type=self.visit_type, | |||||||||||||||||||||||||||||||||||||||||||||||||
date=now(), | date=now(), | |||||||||||||||||||||||||||||||||||||||||||||||||
status=status, | status=status, | |||||||||||||||||||||||||||||||||||||||||||||||||
snapshot=self.loaded_snapshot_id, | snapshot=self.loaded_snapshot_id, | |||||||||||||||||||||||||||||||||||||||||||||||||
) | ) | |||||||||||||||||||||||||||||||||||||||||||||||||
self.storage.origin_visit_status_add([visit_status]) | self.storage.origin_visit_status_add([visit_status]) | |||||||||||||||||||||||||||||||||||||||||||||||||
Done Inline Actions
The current code uses slightly ambiguous "create_partial_snapshot" parameter to store_data, which makes it create an OVS in some cases; and create the OVS here in other cases. Instead, you can create the OVS here, simply by moving it inside the loop. This keeps store_data as it was before the diff (doesn't need to concern itself with creating objects) and avoid duplicating OVS creation. Just because Phabricator mangles the diff, this is what the new code should look like: while True: t1 = time.monotonic() more_data_to_fetch = self.fetch_data() t2 = time.monotonic() total_time_fetch_data += t2 - t1 more_data_to_fetch = self.process_data() and more_data_to_fetch t3 = time.monotonic() total_time_process_data += t3 - t2 status = "partial" if more_data_to_fetch else self.visit_status() self.store_data() visit_status = OriginVisitStatus( origin=self.origin.url, visit=self.visit.visit, type=self.visit_type, date=now(), status=status, snapshot=self.loaded_snapshot_id, ) self.storage.origin_visit_status_add([visit_status]) t4 = time.monotonic() total_time_store_data += t4 - t3 if not more_data_to_fetch: break self.statsd_timing("fetch_data", total_time_fetch_data * 1000.0) self.statsd_timing("process_data", total_time_process_data * 1000.0) self.statsd_timing("store_data", total_time_store_data * 1000.0) vlorentz: The current code uses slightly ambiguous "create_partial_snapshot" parameter to store_data… | ||||||||||||||||||||||||||||||||||||||||||||||||||
Done Inline Actionsoops, you should move the status = "partial" if more_data_to_fetch else self.visit_status() line after the call to store_data in both my snippets vlorentz: oops, you should move the `status = "partial" if more_data_to_fetch else self.visit_status()`… | ||||||||||||||||||||||||||||||||||||||||||||||||||
success = True | success = True | |||||||||||||||||||||||||||||||||||||||||||||||||
with self.statsd_timed( | with self.statsd_timed( | |||||||||||||||||||||||||||||||||||||||||||||||||
"post_load", tags={"success": success, "status": status} | "post_load", tags={"success": success, "status": status} | |||||||||||||||||||||||||||||||||||||||||||||||||
): | ): | |||||||||||||||||||||||||||||||||||||||||||||||||
self.post_load() | self.post_load() | |||||||||||||||||||||||||||||||||||||||||||||||||
except BaseException as e: | except BaseException as e: | |||||||||||||||||||||||||||||||||||||||||||||||||
success = False | success = False | |||||||||||||||||||||||||||||||||||||||||||||||||
if isinstance(e, NotFound): | if isinstance(e, NotFound): | |||||||||||||||||||||||||||||||||||||||||||||||||
▲ Show 20 Lines • Show All 178 Lines • ▼ Show 20 Lines | class DVCSLoader(BaseLoader): | |||||||||||||||||||||||||||||||||||||||||||||||||
def get_snapshot(self) -> Snapshot: | def get_snapshot(self) -> Snapshot: | |||||||||||||||||||||||||||||||||||||||||||||||||
"""Get the snapshot that needs to be loaded""" | """Get the snapshot that needs to be loaded""" | |||||||||||||||||||||||||||||||||||||||||||||||||
raise NotImplementedError | raise NotImplementedError | |||||||||||||||||||||||||||||||||||||||||||||||||
def eventful(self) -> bool: | def eventful(self) -> bool: | |||||||||||||||||||||||||||||||||||||||||||||||||
"""Whether the load was eventful""" | """Whether the load was eventful""" | |||||||||||||||||||||||||||||||||||||||||||||||||
raise NotImplementedError | raise NotImplementedError | |||||||||||||||||||||||||||||||||||||||||||||||||
def store_data(self) -> None: | def store_data(self, create_partial_snapshot: bool = False) -> None: | |||||||||||||||||||||||||||||||||||||||||||||||||
assert self.origin | assert self.origin | |||||||||||||||||||||||||||||||||||||||||||||||||
if self.save_data_path: | if self.save_data_path: | |||||||||||||||||||||||||||||||||||||||||||||||||
self.save_data() | self.save_data() | |||||||||||||||||||||||||||||||||||||||||||||||||
if self.has_contents(): | if self.has_contents(): | |||||||||||||||||||||||||||||||||||||||||||||||||
for obj in self.get_contents(): | for obj in self.get_contents(): | |||||||||||||||||||||||||||||||||||||||||||||||||
if isinstance(obj, Content): | if isinstance(obj, Content): | |||||||||||||||||||||||||||||||||||||||||||||||||
self.storage.content_add([obj]) | self.storage.content_add([obj]) | |||||||||||||||||||||||||||||||||||||||||||||||||
elif isinstance(obj, SkippedContent): | elif isinstance(obj, SkippedContent): | |||||||||||||||||||||||||||||||||||||||||||||||||
self.storage.skipped_content_add([obj]) | self.storage.skipped_content_add([obj]) | |||||||||||||||||||||||||||||||||||||||||||||||||
else: | else: | |||||||||||||||||||||||||||||||||||||||||||||||||
raise TypeError(f"Unexpected content type: {obj}") | raise TypeError(f"Unexpected content type: {obj}") | |||||||||||||||||||||||||||||||||||||||||||||||||
if self.has_directories(): | if self.has_directories(): | |||||||||||||||||||||||||||||||||||||||||||||||||
for directory in self.get_directories(): | for directory in self.get_directories(): | |||||||||||||||||||||||||||||||||||||||||||||||||
self.storage.directory_add([directory]) | self.storage.directory_add([directory]) | |||||||||||||||||||||||||||||||||||||||||||||||||
if self.has_revisions(): | if self.has_revisions(): | |||||||||||||||||||||||||||||||||||||||||||||||||
for revision in self.get_revisions(): | for revision in self.get_revisions(): | |||||||||||||||||||||||||||||||||||||||||||||||||
self.storage.revision_add([revision]) | self.storage.revision_add([revision]) | |||||||||||||||||||||||||||||||||||||||||||||||||
if self.has_releases(): | if self.has_releases(): | |||||||||||||||||||||||||||||||||||||||||||||||||
for release in self.get_releases(): | for release in self.get_releases(): | |||||||||||||||||||||||||||||||||||||||||||||||||
self.storage.release_add([release]) | self.storage.release_add([release]) | |||||||||||||||||||||||||||||||||||||||||||||||||
snapshot = self.get_snapshot() | snapshot = self.get_snapshot() | |||||||||||||||||||||||||||||||||||||||||||||||||
self.storage.snapshot_add([snapshot]) | self.storage.snapshot_add([snapshot]) | |||||||||||||||||||||||||||||||||||||||||||||||||
# More work to do, we make a partial visit targeting the snapshot though. That | ||||||||||||||||||||||||||||||||||||||||||||||||||
# should ease further visit if we somehow can't make it through the ingestion. | ||||||||||||||||||||||||||||||||||||||||||||||||||
if create_partial_snapshot: | ||||||||||||||||||||||||||||||||||||||||||||||||||
Done Inline Actions
maybe? vlorentz: maybe? | ||||||||||||||||||||||||||||||||||||||||||||||||||
Done Inline ActionsI recall that's what i initially did and mypy was not happy about it. ardumont: I recall that's what i initially did and mypy was not happy about it. | ||||||||||||||||||||||||||||||||||||||||||||||||||
assert self.visit.visit is not None | ||||||||||||||||||||||||||||||||||||||||||||||||||
visit_status = OriginVisitStatus( | ||||||||||||||||||||||||||||||||||||||||||||||||||
origin=self.origin.url, | ||||||||||||||||||||||||||||||||||||||||||||||||||
visit=self.visit.visit, | ||||||||||||||||||||||||||||||||||||||||||||||||||
type=self.visit_type, | ||||||||||||||||||||||||||||||||||||||||||||||||||
date=now(), | ||||||||||||||||||||||||||||||||||||||||||||||||||
status="partial", | ||||||||||||||||||||||||||||||||||||||||||||||||||
snapshot=snapshot.id, | ||||||||||||||||||||||||||||||||||||||||||||||||||
) | ||||||||||||||||||||||||||||||||||||||||||||||||||
self.storage.origin_visit_status_add([visit_status]) | ||||||||||||||||||||||||||||||||||||||||||||||||||
self.flush() | self.flush() | |||||||||||||||||||||||||||||||||||||||||||||||||
self.loaded_snapshot_id = snapshot.id | self.loaded_snapshot_id = snapshot.id | |||||||||||||||||||||||||||||||||||||||||||||||||
class NodeLoader(BaseLoader): | class NodeLoader(BaseLoader): | |||||||||||||||||||||||||||||||||||||||||||||||||
"""Common class for :class:`ContentLoader` and :class:`Directoryloader`. | """Common class for :class:`ContentLoader` and :class:`Directoryloader`. | |||||||||||||||||||||||||||||||||||||||||||||||||
The "checksums" field is a dictionary of hex hashes on the object retrieved (content | The "checksums" field is a dictionary of hex hashes on the object retrieved (content | |||||||||||||||||||||||||||||||||||||||||||||||||
or directory). When "checksums_computation" is "standard", that means the checksums | or directory). When "checksums_computation" is "standard", that means the checksums | |||||||||||||||||||||||||||||||||||||||||||||||||
are computed on the content of the remote file to retrieve itself (as unix cli | are computed on the content of the remote file to retrieve itself (as unix cli | |||||||||||||||||||||||||||||||||||||||||||||||||
allows, "sha1sum", "sha256sum", ...). When "checksums_computation" is "nar", the | allows, "sha1sum", "sha256sum", ...). When "checksums_computation" is "nar", the | |||||||||||||||||||||||||||||||||||||||||||||||||
checks is delegated to the `nix-store --dump` command, it's actually checksums on | checks is delegated to the `nix-store --dump` command, it's actually checksums on | |||||||||||||||||||||||||||||||||||||||||||||||||
the content of the remote artifact retrieved. Other "checksums_computation" will | the content of the remote artifact retrieved. Other "checksums_computation" will | |||||||||||||||||||||||||||||||||||||||||||||||||
raise UnsupportedChecksumComputation | raise UnsupportedChecksumComputation | |||||||||||||||||||||||||||||||||||||||||||||||||
The multiple "fallback" urls received are mirror urls only used to fetch the object | The multiple "fallback" urls received are mirror urls only used to fetch the object | |||||||||||||||||||||||||||||||||||||||||||||||||
if the main origin is no longer available. Those are not stored. | if the main origin is no longer available. Those are not stored. | |||||||||||||||||||||||||||||||||||||||||||||||||
Ingestion is considered eventful on the first ingestion. Subsequent load of the same | Ingestion is considered eventful on the first ingestion. Subsequent load of the same | |||||||||||||||||||||||||||||||||||||||||||||||||
object should end up being an uneventful visit (matching snapshot). | object should end up being an uneventful visit (matching snapshot). | |||||||||||||||||||||||||||||||||||||||||||||||||
""" | """ | |||||||||||||||||||||||||||||||||||||||||||||||||
def __init__( | def __init__( | |||||||||||||||||||||||||||||||||||||||||||||||||
self, | self, | |||||||||||||||||||||||||||||||||||||||||||||||||
storage: StorageInterface, | storage: StorageInterface, | |||||||||||||||||||||||||||||||||||||||||||||||||
url: str, | url: str, | |||||||||||||||||||||||||||||||||||||||||||||||||
checksums: Dict[str, str], | checksums: Dict[str, str], | |||||||||||||||||||||||||||||||||||||||||||||||||
checksums_computation: str = "standard", | checksums_computation: str = "standard", | |||||||||||||||||||||||||||||||||||||||||||||||||
fallback_urls: List[str] = None, | fallback_urls: List[str] = None, | |||||||||||||||||||||||||||||||||||||||||||||||||
**kwargs, | **kwargs, | |||||||||||||||||||||||||||||||||||||||||||||||||
): | ): | |||||||||||||||||||||||||||||||||||||||||||||||||
super().__init__(storage, url, **kwargs) | super().__init__(storage, url, **kwargs) | |||||||||||||||||||||||||||||||||||||||||||||||||
self.snapshot: Optional[Snapshot] = None | self.snapshot: Optional[Snapshot] = None | |||||||||||||||||||||||||||||||||||||||||||||||||
self.checksums = checksums | self.checksums = checksums | |||||||||||||||||||||||||||||||||||||||||||||||||
self.checksums_computation = checksums_computation | self.checksums_computation = checksums_computation | |||||||||||||||||||||||||||||||||||||||||||||||||
if self.checksums_computation not in ("nar", "standard"): | if self.checksums_computation not in ("nar", "standard"): | |||||||||||||||||||||||||||||||||||||||||||||||||
raise UnsupportedChecksumComputation( | raise UnsupportedChecksumComputation( | |||||||||||||||||||||||||||||||||||||||||||||||||
"Unsupported checksums computations: %s", | "Unsupported checksums computations: %s", | |||||||||||||||||||||||||||||||||||||||||||||||||
self.checksums_computation, | self.checksums_computation, | |||||||||||||||||||||||||||||||||||||||||||||||||
) | ) | |||||||||||||||||||||||||||||||||||||||||||||||||
fallback_urls_ = fallback_urls or [] | fallback_urls_ = fallback_urls or [] | |||||||||||||||||||||||||||||||||||||||||||||||||
self.mirror_urls: List[str] = [self.origin.url, *fallback_urls_] | self.mirror_urls: List[str] = [self.origin.url, *fallback_urls_] | |||||||||||||||||||||||||||||||||||||||||||||||||
# Ensure content received matched the "standard" checksums received, this | # Ensure content received matched the "standard" checksums received, this | |||||||||||||||||||||||||||||||||||||||||||||||||
# contains the checksums when checksum_computations is "standard", it's empty | # contains the checksums when checksum_computations is "standard", it's empty | |||||||||||||||||||||||||||||||||||||||||||||||||
# otherwise | # otherwise | |||||||||||||||||||||||||||||||||||||||||||||||||
self.standard_hashes = ( | self.standard_hashes = ( | |||||||||||||||||||||||||||||||||||||||||||||||||
self.checksums if self.checksums_computation == "standard" else {} | self.checksums if self.checksums_computation == "standard" else {} | |||||||||||||||||||||||||||||||||||||||||||||||||
) | ) | |||||||||||||||||||||||||||||||||||||||||||||||||
self.log.debug("Loader checksums computation: %s", self.checksums_computation) | self.log.debug("Loader checksums computation: %s", self.checksums_computation) | |||||||||||||||||||||||||||||||||||||||||||||||||
def prepare(self) -> None: | def prepare(self) -> None: | |||||||||||||||||||||||||||||||||||||||||||||||||
self.last_snapshot = snapshot_get_latest(self.storage, self.origin.url) | self.last_snapshot = snapshot_get_latest(self.storage, self.origin.url) | |||||||||||||||||||||||||||||||||||||||||||||||||
def load_status(self) -> Dict[str, Any]: | def load_status(self) -> Dict[str, Any]: | |||||||||||||||||||||||||||||||||||||||||||||||||
return { | return { | |||||||||||||||||||||||||||||||||||||||||||||||||
"status": "uneventful" | "status": "uneventful" | |||||||||||||||||||||||||||||||||||||||||||||||||
if self.last_snapshot == self.snapshot | if self.last_snapshot == self.snapshot | |||||||||||||||||||||||||||||||||||||||||||||||||
else "eventful" | else "eventful" | |||||||||||||||||||||||||||||||||||||||||||||||||
} | } | |||||||||||||||||||||||||||||||||||||||||||||||||
def cleanup(self) -> None: | def cleanup(self) -> None: | |||||||||||||||||||||||||||||||||||||||||||||||||
self.log.debug("cleanup") | self.log.debug("cleanup") | |||||||||||||||||||||||||||||||||||||||||||||||||
class ContentLoader(NodeLoader): | class ContentLoader(NodeLoader): | |||||||||||||||||||||||||||||||||||||||||||||||||
"""Basic loader for edge case content ingestion. | """Basic loader for edge case content ingestion. | |||||||||||||||||||||||||||||||||||||||||||||||||
The output snapshot is of the form: | The output snapshot is of the form: | |||||||||||||||||||||||||||||||||||||||||||||||||
.. code:: | .. code:: | |||||||||||||||||||||||||||||||||||||||||||||||||
id: <bytes> | id: <bytes> | |||||||||||||||||||||||||||||||||||||||||||||||||
branches: | branches: | |||||||||||||||||||||||||||||||||||||||||||||||||
HEAD: | HEAD: | |||||||||||||||||||||||||||||||||||||||||||||||||
target_type: content | target_type: content | |||||||||||||||||||||||||||||||||||||||||||||||||
target: <content-id> | target: <content-id> | |||||||||||||||||||||||||||||||||||||||||||||||||
""" | """ | |||||||||||||||||||||||||||||||||||||||||||||||||
visit_type = "content" | visit_type = "content" | |||||||||||||||||||||||||||||||||||||||||||||||||
def __init__(self, *args, **kwargs): | def __init__(self, *args, **kwargs): | |||||||||||||||||||||||||||||||||||||||||||||||||
super().__init__(*args, **kwargs) | super().__init__(*args, **kwargs) | |||||||||||||||||||||||||||||||||||||||||||||||||
self.content: Optional[Content] = None | self.content: Optional[Content] = None | |||||||||||||||||||||||||||||||||||||||||||||||||
def fetch_data(self) -> bool: | def fetch_data(self) -> bool: | |||||||||||||||||||||||||||||||||||||||||||||||||
"""Retrieve the content file as a Content Object""" | """Retrieve the content file as a Content Object""" | |||||||||||||||||||||||||||||||||||||||||||||||||
errors = [] | errors = [] | |||||||||||||||||||||||||||||||||||||||||||||||||
for url in self.mirror_urls: | for url in self.mirror_urls: | |||||||||||||||||||||||||||||||||||||||||||||||||
url_ = urlparse(url) | url_ = urlparse(url) | |||||||||||||||||||||||||||||||||||||||||||||||||
self.log.debug( | self.log.debug( | |||||||||||||||||||||||||||||||||||||||||||||||||
"prepare; origin_url=%s fallback=%s scheme=%s path=%s", | "prepare; origin_url=%s fallback=%s scheme=%s path=%s", | |||||||||||||||||||||||||||||||||||||||||||||||||
self.origin.url, | self.origin.url, | |||||||||||||||||||||||||||||||||||||||||||||||||
url, | url, | |||||||||||||||||||||||||||||||||||||||||||||||||
url_.scheme, | url_.scheme, | |||||||||||||||||||||||||||||||||||||||||||||||||
url_.path, | url_.path, | |||||||||||||||||||||||||||||||||||||||||||||||||
) | ) | |||||||||||||||||||||||||||||||||||||||||||||||||
try: | try: | |||||||||||||||||||||||||||||||||||||||||||||||||
# FIXME: Ensure no "nar" computations is required for file | # FIXME: Ensure no "nar" computations is required for file | |||||||||||||||||||||||||||||||||||||||||||||||||
with tempfile.TemporaryDirectory() as tmpdir: | with tempfile.TemporaryDirectory() as tmpdir: | |||||||||||||||||||||||||||||||||||||||||||||||||
file_path, _ = download( | file_path, _ = download( | |||||||||||||||||||||||||||||||||||||||||||||||||
url, dest=tmpdir, hashes=self.standard_hashes | url, dest=tmpdir, hashes=self.standard_hashes | |||||||||||||||||||||||||||||||||||||||||||||||||
) | ) | |||||||||||||||||||||||||||||||||||||||||||||||||
if self.checksums_computation == "nar": | if self.checksums_computation == "nar": | |||||||||||||||||||||||||||||||||||||||||||||||||
# hashes are not "standard", so we need an extra check to happen | # hashes are not "standard", so we need an extra check to happen | |||||||||||||||||||||||||||||||||||||||||||||||||
self.log.debug("Content to check nar hashes: %s", file_path) | self.log.debug("Content to check nar hashes: %s", file_path) | |||||||||||||||||||||||||||||||||||||||||||||||||
actual_checksums = nix_hashes( | actual_checksums = nix_hashes( | |||||||||||||||||||||||||||||||||||||||||||||||||
Path(file_path), self.checksums.keys() | Path(file_path), self.checksums.keys() | |||||||||||||||||||||||||||||||||||||||||||||||||
).hexdigest() | ).hexdigest() | |||||||||||||||||||||||||||||||||||||||||||||||||
if actual_checksums != self.checksums: | if actual_checksums != self.checksums: | |||||||||||||||||||||||||||||||||||||||||||||||||
errors.append( | errors.append( | |||||||||||||||||||||||||||||||||||||||||||||||||
ValueError( | ValueError( | |||||||||||||||||||||||||||||||||||||||||||||||||
f"Checksum mismatched on <{url}>: " | f"Checksum mismatched on <{url}>: " | |||||||||||||||||||||||||||||||||||||||||||||||||
f"{actual_checksums} != {self.checksums}" | f"{actual_checksums} != {self.checksums}" | |||||||||||||||||||||||||||||||||||||||||||||||||
) | ) | |||||||||||||||||||||||||||||||||||||||||||||||||
) | ) | |||||||||||||||||||||||||||||||||||||||||||||||||
self.log.debug( | self.log.debug( | |||||||||||||||||||||||||||||||||||||||||||||||||
"Mismatched checksums <%s>: continue on next mirror " | "Mismatched checksums <%s>: continue on next mirror " | |||||||||||||||||||||||||||||||||||||||||||||||||
"url if any", | "url if any", | |||||||||||||||||||||||||||||||||||||||||||||||||
url, | url, | |||||||||||||||||||||||||||||||||||||||||||||||||
) | ) | |||||||||||||||||||||||||||||||||||||||||||||||||
continue | continue | |||||||||||||||||||||||||||||||||||||||||||||||||
with open(file_path, "rb") as file: | with open(file_path, "rb") as file: | |||||||||||||||||||||||||||||||||||||||||||||||||
self.content = Content.from_data(file.read()) | self.content = Content.from_data(file.read()) | |||||||||||||||||||||||||||||||||||||||||||||||||
except ValueError as e: | except ValueError as e: | |||||||||||||||||||||||||||||||||||||||||||||||||
errors.append(e) | errors.append(e) | |||||||||||||||||||||||||||||||||||||||||||||||||
self.log.debug( | self.log.debug( | |||||||||||||||||||||||||||||||||||||||||||||||||
"Mismatched checksums <%s>: continue on next mirror url if any", | "Mismatched checksums <%s>: continue on next mirror url if any", | |||||||||||||||||||||||||||||||||||||||||||||||||
url, | url, | |||||||||||||||||||||||||||||||||||||||||||||||||
) | ) | |||||||||||||||||||||||||||||||||||||||||||||||||
continue | continue | |||||||||||||||||||||||||||||||||||||||||||||||||
except HTTPError as http_error: | except HTTPError as http_error: | |||||||||||||||||||||||||||||||||||||||||||||||||
if http_error.response.status_code == 404: | if http_error.response.status_code == 404: | |||||||||||||||||||||||||||||||||||||||||||||||||
self.log.debug( | self.log.debug( | |||||||||||||||||||||||||||||||||||||||||||||||||
"Not found '%s', continue on next mirror url if any", url | "Not found '%s', continue on next mirror url if any", url | |||||||||||||||||||||||||||||||||||||||||||||||||
) | ) | |||||||||||||||||||||||||||||||||||||||||||||||||
continue | continue | |||||||||||||||||||||||||||||||||||||||||||||||||
else: | else: | |||||||||||||||||||||||||||||||||||||||||||||||||
return False # no more data to fetch | return False # no more data to fetch | |||||||||||||||||||||||||||||||||||||||||||||||||
if errors: | if errors: | |||||||||||||||||||||||||||||||||||||||||||||||||
raise errors[0] | raise errors[0] | |||||||||||||||||||||||||||||||||||||||||||||||||
# If we reach this point, we did not find any proper content, consider the | # If we reach this point, we did not find any proper content, consider the | |||||||||||||||||||||||||||||||||||||||||||||||||
# origin not found | # origin not found | |||||||||||||||||||||||||||||||||||||||||||||||||
raise NotFound(f"Unknown origin {self.origin.url}.") | raise NotFound(f"Unknown origin {self.origin.url}.") | |||||||||||||||||||||||||||||||||||||||||||||||||
def process_data(self) -> bool: | def process_data(self) -> bool: | |||||||||||||||||||||||||||||||||||||||||||||||||
"""Build the snapshot out of the Content retrieved.""" | """Build the snapshot out of the Content retrieved.""" | |||||||||||||||||||||||||||||||||||||||||||||||||
assert self.content is not None | assert self.content is not None | |||||||||||||||||||||||||||||||||||||||||||||||||
self.snapshot = Snapshot( | self.snapshot = Snapshot( | |||||||||||||||||||||||||||||||||||||||||||||||||
branches={ | branches={ | |||||||||||||||||||||||||||||||||||||||||||||||||
b"HEAD": SnapshotBranch( | b"HEAD": SnapshotBranch( | |||||||||||||||||||||||||||||||||||||||||||||||||
target=self.content.sha1_git, | target=self.content.sha1_git, | |||||||||||||||||||||||||||||||||||||||||||||||||
target_type=TargetType.CONTENT, | target_type=TargetType.CONTENT, | |||||||||||||||||||||||||||||||||||||||||||||||||
), | ), | |||||||||||||||||||||||||||||||||||||||||||||||||
} | } | |||||||||||||||||||||||||||||||||||||||||||||||||
) | ) | |||||||||||||||||||||||||||||||||||||||||||||||||
return False # no more data to process | return False # no more data to process | |||||||||||||||||||||||||||||||||||||||||||||||||
def store_data(self) -> None: | def store_data(self, create_partial_snapshot: bool = False) -> None: | |||||||||||||||||||||||||||||||||||||||||||||||||
"""Store newly retrieved Content and Snapshot.""" | """Store newly retrieved Content and Snapshot.""" | |||||||||||||||||||||||||||||||||||||||||||||||||
assert self.content is not None | assert self.content is not None | |||||||||||||||||||||||||||||||||||||||||||||||||
self.storage.content_add([self.content]) | self.storage.content_add([self.content]) | |||||||||||||||||||||||||||||||||||||||||||||||||
assert self.snapshot is not None | assert self.snapshot is not None | |||||||||||||||||||||||||||||||||||||||||||||||||
self.storage.snapshot_add([self.snapshot]) | self.storage.snapshot_add([self.snapshot]) | |||||||||||||||||||||||||||||||||||||||||||||||||
self.loaded_snapshot_id = self.snapshot.id | self.loaded_snapshot_id = self.snapshot.id | |||||||||||||||||||||||||||||||||||||||||||||||||
def visit_status(self): | def visit_status(self): | |||||||||||||||||||||||||||||||||||||||||||||||||
return "full" if self.content and self.snapshot is not None else "partial" | return "full" if self.content and self.snapshot is not None else "partial" | |||||||||||||||||||||||||||||||||||||||||||||||||
class DirectoryLoader(NodeLoader): | class DirectoryLoader(NodeLoader): | |||||||||||||||||||||||||||||||||||||||||||||||||
"""Basic loader for edge case directory ingestion (through one tarball). | """Basic loader for edge case directory ingestion (through one tarball). | |||||||||||||||||||||||||||||||||||||||||||||||||
The output snapshot is of the form: | The output snapshot is of the form: | |||||||||||||||||||||||||||||||||||||||||||||||||
.. code:: | .. code:: | |||||||||||||||||||||||||||||||||||||||||||||||||
id: <bytes> | id: <bytes> | |||||||||||||||||||||||||||||||||||||||||||||||||
branches: | branches: | |||||||||||||||||||||||||||||||||||||||||||||||||
HEAD: | HEAD: | |||||||||||||||||||||||||||||||||||||||||||||||||
target_type: directory | target_type: directory | |||||||||||||||||||||||||||||||||||||||||||||||||
target: <directory-id> | target: <directory-id> | |||||||||||||||||||||||||||||||||||||||||||||||||
""" | """ | |||||||||||||||||||||||||||||||||||||||||||||||||
visit_type = "directory" | visit_type = "directory" | |||||||||||||||||||||||||||||||||||||||||||||||||
def __init__(self, *args, **kwargs): | def __init__(self, *args, **kwargs): | |||||||||||||||||||||||||||||||||||||||||||||||||
super().__init__(*args, **kwargs) | super().__init__(*args, **kwargs) | |||||||||||||||||||||||||||||||||||||||||||||||||
self.directory: Optional[from_disk.Directory] = None | self.directory: Optional[from_disk.Directory] = None | |||||||||||||||||||||||||||||||||||||||||||||||||
self.cnts: List[Content] = None | self.cnts: List[Content] = None | |||||||||||||||||||||||||||||||||||||||||||||||||
self.skipped_cnts: List[SkippedContent] = None | self.skipped_cnts: List[SkippedContent] = None | |||||||||||||||||||||||||||||||||||||||||||||||||
self.dirs: List[Directory] = None | self.dirs: List[Directory] = None | |||||||||||||||||||||||||||||||||||||||||||||||||
def fetch_data(self) -> bool: | def fetch_data(self) -> bool: | |||||||||||||||||||||||||||||||||||||||||||||||||
"""Fetch directory as a tarball amongst the self.mirror_urls. | """Fetch directory as a tarball amongst the self.mirror_urls. | |||||||||||||||||||||||||||||||||||||||||||||||||
Raises NotFound if no tarball is found | Raises NotFound if no tarball is found | |||||||||||||||||||||||||||||||||||||||||||||||||
""" | """ | |||||||||||||||||||||||||||||||||||||||||||||||||
errors = [] | errors = [] | |||||||||||||||||||||||||||||||||||||||||||||||||
for url in self.mirror_urls: | for url in self.mirror_urls: | |||||||||||||||||||||||||||||||||||||||||||||||||
url_ = urlparse(url) | url_ = urlparse(url) | |||||||||||||||||||||||||||||||||||||||||||||||||
self.log.debug( | self.log.debug( | |||||||||||||||||||||||||||||||||||||||||||||||||
"prepare; origin_url=%s fallback=%s scheme=%s path=%s", | "prepare; origin_url=%s fallback=%s scheme=%s path=%s", | |||||||||||||||||||||||||||||||||||||||||||||||||
self.origin.url, | self.origin.url, | |||||||||||||||||||||||||||||||||||||||||||||||||
url, | url, | |||||||||||||||||||||||||||||||||||||||||||||||||
url_.scheme, | url_.scheme, | |||||||||||||||||||||||||||||||||||||||||||||||||
url_.path, | url_.path, | |||||||||||||||||||||||||||||||||||||||||||||||||
) | ) | |||||||||||||||||||||||||||||||||||||||||||||||||
with tempfile.TemporaryDirectory() as tmpdir: | with tempfile.TemporaryDirectory() as tmpdir: | |||||||||||||||||||||||||||||||||||||||||||||||||
try: | try: | |||||||||||||||||||||||||||||||||||||||||||||||||
tarball_path, extrinsic_metadata = download( | tarball_path, extrinsic_metadata = download( | |||||||||||||||||||||||||||||||||||||||||||||||||
url, | url, | |||||||||||||||||||||||||||||||||||||||||||||||||
tmpdir, | tmpdir, | |||||||||||||||||||||||||||||||||||||||||||||||||
hashes=self.standard_hashes, | hashes=self.standard_hashes, | |||||||||||||||||||||||||||||||||||||||||||||||||
extra_request_headers={"Accept-Encoding": "identity"}, | extra_request_headers={"Accept-Encoding": "identity"}, | |||||||||||||||||||||||||||||||||||||||||||||||||
) | ) | |||||||||||||||||||||||||||||||||||||||||||||||||
except ValueError as e: | except ValueError as e: | |||||||||||||||||||||||||||||||||||||||||||||||||
errors.append(e) | errors.append(e) | |||||||||||||||||||||||||||||||||||||||||||||||||
self.log.debug( | self.log.debug( | |||||||||||||||||||||||||||||||||||||||||||||||||
"Mismatched checksums <%s>: continue on next mirror url if any", | "Mismatched checksums <%s>: continue on next mirror url if any", | |||||||||||||||||||||||||||||||||||||||||||||||||
url, | url, | |||||||||||||||||||||||||||||||||||||||||||||||||
) | ) | |||||||||||||||||||||||||||||||||||||||||||||||||
continue | continue | |||||||||||||||||||||||||||||||||||||||||||||||||
except HTTPError as http_error: | except HTTPError as http_error: | |||||||||||||||||||||||||||||||||||||||||||||||||
if http_error.response.status_code == 404: | if http_error.response.status_code == 404: | |||||||||||||||||||||||||||||||||||||||||||||||||
self.log.debug( | self.log.debug( | |||||||||||||||||||||||||||||||||||||||||||||||||
"Not found <%s>: continue on next mirror url if any", url | "Not found <%s>: continue on next mirror url if any", url | |||||||||||||||||||||||||||||||||||||||||||||||||
) | ) | |||||||||||||||||||||||||||||||||||||||||||||||||
continue | continue | |||||||||||||||||||||||||||||||||||||||||||||||||
directory_path = Path(tmpdir) / "src" | directory_path = Path(tmpdir) / "src" | |||||||||||||||||||||||||||||||||||||||||||||||||
directory_path.mkdir(parents=True, exist_ok=True) | directory_path.mkdir(parents=True, exist_ok=True) | |||||||||||||||||||||||||||||||||||||||||||||||||
uncompress(tarball_path, dest=str(directory_path)) | uncompress(tarball_path, dest=str(directory_path)) | |||||||||||||||||||||||||||||||||||||||||||||||||
self.log.debug("uncompressed path to directory: %s", directory_path) | self.log.debug("uncompressed path to directory: %s", directory_path) | |||||||||||||||||||||||||||||||||||||||||||||||||
if self.checksums_computation == "nar": | if self.checksums_computation == "nar": | |||||||||||||||||||||||||||||||||||||||||||||||||
# hashes are not "standard", so we need an extra check to happen | # hashes are not "standard", so we need an extra check to happen | |||||||||||||||||||||||||||||||||||||||||||||||||
# on the uncompressed tarball | # on the uncompressed tarball | |||||||||||||||||||||||||||||||||||||||||||||||||
dir_to_check = next(directory_path.iterdir()) | dir_to_check = next(directory_path.iterdir()) | |||||||||||||||||||||||||||||||||||||||||||||||||
self.log.debug("Directory to check nar hashes: %s", dir_to_check) | self.log.debug("Directory to check nar hashes: %s", dir_to_check) | |||||||||||||||||||||||||||||||||||||||||||||||||
actual_checksums = nix_hashes( | actual_checksums = nix_hashes( | |||||||||||||||||||||||||||||||||||||||||||||||||
dir_to_check, self.checksums.keys() | dir_to_check, self.checksums.keys() | |||||||||||||||||||||||||||||||||||||||||||||||||
).hexdigest() | ).hexdigest() | |||||||||||||||||||||||||||||||||||||||||||||||||
if actual_checksums != self.checksums: | if actual_checksums != self.checksums: | |||||||||||||||||||||||||||||||||||||||||||||||||
errors.append( | errors.append( | |||||||||||||||||||||||||||||||||||||||||||||||||
ValueError( | ValueError( | |||||||||||||||||||||||||||||||||||||||||||||||||
f"Checksum mismatched on <{url}>: " | f"Checksum mismatched on <{url}>: " | |||||||||||||||||||||||||||||||||||||||||||||||||
f"{actual_checksums} != {self.checksums}" | f"{actual_checksums} != {self.checksums}" | |||||||||||||||||||||||||||||||||||||||||||||||||
) | ) | |||||||||||||||||||||||||||||||||||||||||||||||||
) | ) | |||||||||||||||||||||||||||||||||||||||||||||||||
self.log.debug( | self.log.debug( | |||||||||||||||||||||||||||||||||||||||||||||||||
"Mismatched checksums <%s>: continue on next mirror url if any", | "Mismatched checksums <%s>: continue on next mirror url if any", | |||||||||||||||||||||||||||||||||||||||||||||||||
url, | url, | |||||||||||||||||||||||||||||||||||||||||||||||||
) | ) | |||||||||||||||||||||||||||||||||||||||||||||||||
continue | continue | |||||||||||||||||||||||||||||||||||||||||||||||||
self.directory = from_disk.Directory.from_disk( | self.directory = from_disk.Directory.from_disk( | |||||||||||||||||||||||||||||||||||||||||||||||||
path=bytes(directory_path), | path=bytes(directory_path), | |||||||||||||||||||||||||||||||||||||||||||||||||
max_content_length=self.max_content_size, | max_content_length=self.max_content_size, | |||||||||||||||||||||||||||||||||||||||||||||||||
) | ) | |||||||||||||||||||||||||||||||||||||||||||||||||
# Compute the merkle dag from the top-level directory | # Compute the merkle dag from the top-level directory | |||||||||||||||||||||||||||||||||||||||||||||||||
self.cnts, self.skipped_cnts, self.dirs = from_disk.iter_directory( | self.cnts, self.skipped_cnts, self.dirs = from_disk.iter_directory( | |||||||||||||||||||||||||||||||||||||||||||||||||
self.directory | self.directory | |||||||||||||||||||||||||||||||||||||||||||||||||
) | ) | |||||||||||||||||||||||||||||||||||||||||||||||||
if self.directory is not None: | if self.directory is not None: | |||||||||||||||||||||||||||||||||||||||||||||||||
return False # no more data to fetch | return False # no more data to fetch | |||||||||||||||||||||||||||||||||||||||||||||||||
if errors: | if errors: | |||||||||||||||||||||||||||||||||||||||||||||||||
raise errors[0] | raise errors[0] | |||||||||||||||||||||||||||||||||||||||||||||||||
# if we reach here, we did not find any proper tarball, so consider the origin | # if we reach here, we did not find any proper tarball, so consider the origin | |||||||||||||||||||||||||||||||||||||||||||||||||
# not found | # not found | |||||||||||||||||||||||||||||||||||||||||||||||||
raise NotFound(f"Unknown origin {self.origin.url}.") | raise NotFound(f"Unknown origin {self.origin.url}.") | |||||||||||||||||||||||||||||||||||||||||||||||||
def process_data(self) -> bool: | def process_data(self) -> bool: | |||||||||||||||||||||||||||||||||||||||||||||||||
"""Build the snapshot out of the Directory retrieved.""" | """Build the snapshot out of the Directory retrieved.""" | |||||||||||||||||||||||||||||||||||||||||||||||||
assert self.directory is not None | assert self.directory is not None | |||||||||||||||||||||||||||||||||||||||||||||||||
# Build the snapshot | # Build the snapshot | |||||||||||||||||||||||||||||||||||||||||||||||||
self.snapshot = Snapshot( | self.snapshot = Snapshot( | |||||||||||||||||||||||||||||||||||||||||||||||||
branches={ | branches={ | |||||||||||||||||||||||||||||||||||||||||||||||||
b"HEAD": SnapshotBranch( | b"HEAD": SnapshotBranch( | |||||||||||||||||||||||||||||||||||||||||||||||||
target=self.directory.hash, | target=self.directory.hash, | |||||||||||||||||||||||||||||||||||||||||||||||||
target_type=TargetType.DIRECTORY, | target_type=TargetType.DIRECTORY, | |||||||||||||||||||||||||||||||||||||||||||||||||
), | ), | |||||||||||||||||||||||||||||||||||||||||||||||||
} | } | |||||||||||||||||||||||||||||||||||||||||||||||||
) | ) | |||||||||||||||||||||||||||||||||||||||||||||||||
return False # no more data to process | return False # no more data to process | |||||||||||||||||||||||||||||||||||||||||||||||||
def store_data(self) -> None: | def store_data(self, create_partial_snapshot: bool = False) -> None: | |||||||||||||||||||||||||||||||||||||||||||||||||
"""Store newly retrieved Content and Snapshot.""" | """Store newly retrieved Content and Snapshot.""" | |||||||||||||||||||||||||||||||||||||||||||||||||
self.log.debug("Number of skipped contents: %s", len(self.skipped_cnts)) | self.log.debug("Number of skipped contents: %s", len(self.skipped_cnts)) | |||||||||||||||||||||||||||||||||||||||||||||||||
self.storage.skipped_content_add(self.skipped_cnts) | self.storage.skipped_content_add(self.skipped_cnts) | |||||||||||||||||||||||||||||||||||||||||||||||||
self.log.debug("Number of contents: %s", len(self.cnts)) | self.log.debug("Number of contents: %s", len(self.cnts)) | |||||||||||||||||||||||||||||||||||||||||||||||||
self.storage.content_add(self.cnts) | self.storage.content_add(self.cnts) | |||||||||||||||||||||||||||||||||||||||||||||||||
self.log.debug("Number of directories: %s", len(self.dirs)) | self.log.debug("Number of directories: %s", len(self.dirs)) | |||||||||||||||||||||||||||||||||||||||||||||||||
self.storage.directory_add(self.dirs) | self.storage.directory_add(self.dirs) | |||||||||||||||||||||||||||||||||||||||||||||||||
assert self.snapshot is not None | assert self.snapshot is not None | |||||||||||||||||||||||||||||||||||||||||||||||||
self.storage.snapshot_add([self.snapshot]) | self.storage.snapshot_add([self.snapshot]) | |||||||||||||||||||||||||||||||||||||||||||||||||
self.loaded_snapshot_id = self.snapshot.id | self.loaded_snapshot_id = self.snapshot.id | |||||||||||||||||||||||||||||||||||||||||||||||||
def visit_status(self): | def visit_status(self): | |||||||||||||||||||||||||||||||||||||||||||||||||
return "full" if self.directory and self.snapshot is not None else "partial" | return "full" if self.directory and self.snapshot is not None else "partial" | |||||||||||||||||||||||||||||||||||||||||||||||||
Done Inline Actionsshould be in the if. We don't really want the flush to happen each time the loop occurs. ardumont: should be in the if. We don't really want the flush to happen each time the loop occurs.
Plus… | ||||||||||||||||||||||||||||||||||||||||||||||||||
Done Inline Actionsthat comment was for a previous implem, it's now irrelevant. ardumont: that comment was for a previous implem, it's now irrelevant. |
This should use a keyword argument, for readability (make it clear what the argument means)