Skip to content
Snippets Groups Projects
Commit 146b0db0 authored by Daniel Lyons's avatar Daniel Lyons
Browse files

Two changes: improve output and add delivery.json file with more information

parent 30fc7cb9
No related branches found
No related tags found
1 merge request!180Delivery results propagation
Pipeline #1226 passed
Showing with 101 additions and 33 deletions
import json
import pathlib
from typing import Dict
from .context import DeliveryContext
from .finder import HeuristicProductFinder, ProductFinder
......@@ -23,7 +25,7 @@ class Delivery:
else:
return HeuristicProductFinder(source)
def deliver(self, context: DeliveryContext):
def deliver(self, context: DeliveryContext) -> Dict:
"""
Primary entrypoint to delivery process
......@@ -36,17 +38,26 @@ class Delivery:
# loop over the products and deliver them
with context.create_destination() as destination:
for product in finder.find_products():
print(f"Delivering {product} to {destination}")
product.deliver_to(destination)
# the last thing we should do is return the URL to the delivered stuff.
# for a local delivery, this will be a file URL; otherwise it will be an http URL.
return destination.result_url()
return destination.results()
def main(args=None):
"""CLI entry point"""
# parse the arguments
context = DeliveryContext.parse_commandline(args)
print(Delivery().deliver(context))
# perform the delivery
delivery = Delivery().deliver(context)
# write the results to a file
with open("delivery.json", "w") as result_file:
# indent=2 causes the file to be pretty-printed for humans
json.dump(delivery, result_file, indent=2)
if __name__ == "__main__":
......
......@@ -57,3 +57,6 @@ class ChecksumDecorator(DestinationDecorator):
# now proceed
super().close()
def __str__(self):
return str(self.underlying) + " with SHA1SUM"
......@@ -22,9 +22,8 @@ class FetchFileGenerator(DestinationDecorator):
super().add_file(file, relative_path)
# add a line to the fetch script for this file
self.fetch_script.file().writelines(
[f"wget {self.underlying.result_url()}/{relative_path}\n".encode("utf8")]
)
url = self.underlying.results()["url"]
self.fetch_script.file().writelines([f"wget {url}/{relative_path}\n".encode("utf8")])
def close(self):
# first close the script
......@@ -32,3 +31,6 @@ class FetchFileGenerator(DestinationDecorator):
# proceed
super().close()
def __str__(self):
return str(self.underlying) + " with fetch-all.sh"
import pathlib
from abc import ABC, abstractmethod
from typing import BinaryIO
from typing import BinaryIO, Dict
class DeliveryContextIF(ABC):
......@@ -105,10 +105,16 @@ class Destination(ABC):
pass
@abstractmethod
def result_url(self) -> str:
def results(self) -> Dict:
"""
Returns a URL to the results.
:return: URL pointing to the results
Returns some result information, to be returned to the caller. Expected keys include:
``delivered_to``
A filesystem location where the delivery placed files
``url``
A URL to the delivery location, if it can be accessed via a browser
:return: result information
"""
pass
......@@ -171,9 +177,12 @@ class DestinationDecorator(Destination):
"""
self.underlying.close()
def result_url(self) -> str:
def results(self) -> Dict:
"""
In most cases you should leave this alone unless you want to modify the URL that gets
returned to the console at the end of delivery.
"""
return self.underlying.result_url()
return self.underlying.results()
def __str__(self):
return str(self.underlying)
import os
import shutil
import tempfile
from typing import BinaryIO
from typing import BinaryIO, Dict
from .interfaces import Destination, DeliveryContextIF, DestinationTempFile
import pathlib
......@@ -50,11 +50,17 @@ class LocalDestination(Destination):
# hand off to the LocalDestinationTempFile, which handles the rest
return LocalDestinationTempFile(self, relative_path, rawfile)
def result_url(self) -> str:
def results(self) -> Dict:
"""
Return a file:/// URL for this location
"""
return pathlib.Path(self.path.absolute()).as_uri()
# we could also supply a file:/// URL, which would be constructed like so:
# "url": pathlib.Path(self.path.absolute()).as_uri()
return {"delivered_to": str(self.path)}
def __str__(self):
return f"local destination {self.path}"
class LocalDestinationTempFile(DestinationTempFile):
......
import pathlib
from typing import Dict
from .interfaces import DestinationDecorator, DeliveryContextIF
from .local import LocalDestination
......@@ -22,16 +23,19 @@ class SharedWebDestination(DestinationDecorator):
username = "anonymous"
# determine the destination directory
destination_dir = pathlib.Path(capo.nraoDownloadDirectory) / username / context.token()
self.destination_dir = pathlib.Path(capo.nraoDownloadDirectory) / username / context.token()
# LocalDestination won't create a directory, so we'll do that here
destination_dir.mkdir(parents=True)
self.destination_dir.mkdir(parents=True)
# we're ready to build the local destination and thread it through our super constructor
super().__init__(LocalDestination(context, destination_dir))
super().__init__(LocalDestination(context, self.destination_dir))
# generate the download URL for later usage
self.download_url = f"{capo.nraoDownloadUrl}/{username}/{context.token()}"
def result_url(self) -> str:
return self.download_url
def results(self) -> Dict:
return {"delivered_to": str(self.destination_dir), "url": self.download_url}
def __str__(self):
return f"shared web root at {self.destination_dir}"
......@@ -55,3 +55,6 @@ class TarArchiver(DestinationDecorator):
# now we can proceed
self.underlying.close()
def __str__(self):
return f"tar archive in " + str(self.underlying)
......@@ -45,10 +45,16 @@ class ExecutionBlock(SpooledProduct):
eventually.
"""
@property
def eb_name(self):
return self.path.absolute().name
def deliver_to(self, destination: Destination):
eb_name = self.path.absolute().name
# let's use our directory name as the relative path
destination.add_directory(self.path, eb_name)
destination.add_directory(self.path, self.eb_name)
def __str__(self):
return f"execution block {self.eb_name}"
# Future types of product that might be needed:
......
......@@ -60,6 +60,11 @@ def test_local_delivery_add_file(tmpdir, file_to_deliver: pathlib.Path, dest_dir
# see if the content of the file is intact
assert (dest_dir / file_to_deliver.name).read_text(encoding=None) == "content"
# see if the results are alright
results = local_dest.results()
assert len(results.keys()) == 1
assert results["delivered_to"] == str(dest_dir)
def test_local_delivery_add_directory(
tmpdir: pathlib.Path, file_to_deliver: pathlib.Path, dest_dir: pathlib.Path
......@@ -71,9 +76,13 @@ def test_local_delivery_add_directory(
local_dest.add_directory(file_to_deliver.parent, file_to_deliver.parent.name)
compare_dirs = filecmp.dircmp(file_to_deliver.parent, dest_dir / "prior")
# see if the destination got all the files from source
assert (
len(compare_dirs.left_only) == 0
and len(compare_dirs.right_only) == 0
and len(compare_dirs.funny_files) == 0
)
assert len(compare_dirs.left_only) == 0
assert len(compare_dirs.right_only) == 0
assert len(compare_dirs.funny_files) == 0
# see if the results are alright
results = local_dest.results()
assert len(results.keys()) == 1
assert results["delivered_to"] == str(dest_dir)
# Testing the CLI
import filecmp
import json
import pathlib
import shutil
import tarfile
......@@ -64,9 +65,16 @@ def test_local_rawdata_no_tar(tmpdir_factory):
test_data_path = "../../../../shared/workspaces/test/test_data/spool/724126739/"
eb_name = "17A-109.sb33151331.eb33786546.57892.65940042824"
main(["-r", "-l", temp_directory, test_data_path])
# compare the source and destination
assert_directories_are_same(temp_directory + "/" + eb_name, (test_data_path + eb_name))
# ensure that we actually got a delivery file with the proper contents
with open("delivery.json", "r") as delivery_results_file:
results = json.load(delivery_results_file)
assert len(results.keys()) == 1
assert results["delivered_to"] == temp_directory
def test_local_rawdata_with_tar(tmpdir_factory):
"""
......@@ -86,6 +94,11 @@ def test_local_rawdata_with_tar(tmpdir_factory):
verify_extracted_directory(eb_name, tar_path, temp_directory, test_data_path)
with open("delivery.json", "r") as delivery_results_file:
results = json.load(delivery_results_file)
assert len(results.keys()) == 1
assert results["delivered_to"] == str(temp_directory)
# @pytest.mark.skip(reason="Test needs more dev time")
def test_web_rawdata_no_tar(tmpdir_factory):
......@@ -100,10 +113,11 @@ def test_web_rawdata_no_tar(tmpdir_factory):
mocked_capo_settings.return_value.nraoDownloadDirectory = str(temp_directory)
mocked_capo_settings.return_value.nraoDownloadUrl = "http://testing"
assert str(temp_directory) == mocked_capo_settings().nraoDownloadDirectory
destination_url = Delivery().deliver(test_context)
results = Delivery().deliver(test_context)
# determine the destination by looking at the URL
actual_delivery_dir = temp_directory / destination_url.lstrip("http://testing")
# check the relationship between the delivery root and the URL
assert str(temp_directory / results["url"].lstrip("http://testing")) == results["delivered_to"]
actual_delivery_dir = pathlib.Path(results["delivered_to"])
# compare the source and destination
assert_directories_are_same(actual_delivery_dir / eb_name, f"{test_data_path}{eb_name}")
......@@ -121,11 +135,12 @@ def test_web_rawdata_with_tar(tmpdir_factory):
mocked_capo_settings.return_value.nraoDownloadDirectory = temp_directory
mocked_capo_settings.return_value.nraoDownloadUrl = "http://testing"
assert temp_directory == mocked_capo_settings().nraoDownloadDirectory
destination_url = Delivery().deliver(test_context)
results = Delivery().deliver(test_context)
eb_name = "17A-109.sb33151331.eb33786546.57892.65940042824"
# determine the destination by looking at the URL
actual_delivery_dir = temp_directory / destination_url.lstrip("http://testing")
# check the relationship between the delivery root and the URL
assert str(temp_directory / results["url"].lstrip("http://testing")) == results["delivered_to"]
actual_delivery_dir = pathlib.Path(results["delivered_to"])
# does a tar exist where we think
tar_path = actual_delivery_dir / (eb_name + ".tar")
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment