-
Charlotte Hausman authoredCharlotte Hausman authored
context.py 6.47 KiB
#
# Copyright (C) 2021 Associated Universities, Inc. Washington DC, USA.
#
# This file is part of NRAO Workspaces.
#
# Workspaces is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Workspaces is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Workspaces. If not, see <https://www.gnu.org/licenses/>.
# -------------------------------------------------------------------------
#
# D E L I V E R Y C O N T E X T
#
# -------------------------------------------------------------------------
import argparse
import pathlib
import secrets
from .deliverer import DeliveryContextIF, Destination, DestinationBuilder
from .finder import ProductFinder, JsonProductFinder
class DeliveryContext(DeliveryContextIF):
"""
The delivery context provides access to some environmental functions that
are not really the responsibility of any particular component, but which
many components need to share information about, including:
- Creating and removing temporary files
- Creating and retaining tokens
- Settings which change how delivery is performed
"""
def __init__(
self,
source: pathlib.Path,
tar=False,
local_destination=None,
use_piperesults=True,
rawdata=False,
prefix="",
username="anonymous",
):
self.source = source
self.tar = tar
self.local_destination = local_destination
self.use_piperesults = use_piperesults
self.rawdata = rawdata
self.prefix = prefix
self._username = username
# the token is not something that gets created at construction time
self._token = None
def __exit__(self, exc_type, exc_val, exc_tb):
# TODO: possibility: remove all the generated tempfiles here
pass
def __repr__(self):
return f"<DeliveryContext {self.__dict__}>"
def token(self) -> str:
"""
If a delivery only requires one token, just use this property
to get it. It will be created once and remain the same throughout
the lifetime of this object.
:return: the current token
"""
if self._token is None:
self._token = self.generate_token()
return self._token
@property
def username(self) -> str:
return self._username
@staticmethod
def generate_token() -> str:
"""
Generates a random token suitable for use in paths and URLs.
:return: a random token
"""
return secrets.token_hex(16)
def create_finder(self) -> ProductFinder:
# Only one type of product finder for now; may change in the future
return JsonProductFinder(self.source)
def create_destination(self, finder: ProductFinder) -> Destination:
builder = DestinationBuilder(self)
# so the layer-cake has to be built in kind of reverse order because it's a stack
# at the bottom we want the physical destination, which is either local or web delivery
# first handle the local destination argument
if self.local_destination:
builder.local(self.local_destination, self.prefix)
else:
builder.web(self.prefix)
# always do a checksum at the end
builder.checksums()
# make a CURL file before that, but only if we're doing a web delivery
if not self.local_destination:
builder.fetchfile()
# tar goes here, so that we generate a checksum for the tar file
# and not for all the files in the tar file
if self.tar:
builder.tar(finder.projects)
return builder.build()
@classmethod
def parse_commandline(cls, args=None) -> "DeliveryContext":
parser = argparse.ArgumentParser()
parser.add_argument(
"--prefix",
dest="prefix",
default="",
action="store",
help="Prefix for the destination (a request ID perhaps)",
)
# piperesults options
group = parser.add_mutually_exclusive_group()
group.add_argument(
"-p",
"--use-piperesults",
dest="use_piperesults",
default=True,
action="store_true",
help="Use the CASA piperesults file, if present",
)
group.add_argument(
"-P",
"--ignore-piperesults",
dest="use_piperesults",
action="store_false",
help="Ignore the CASA piperesults file",
)
# destination options
group = parser.add_argument_group("Destination options")
group.add_argument(
"-l",
"--local-destination",
type=str,
default=None,
help="Deliver to this local directory instead of the appropriate web root",
)
group.add_argument(
"-t",
"--tar",
action="store_true",
default=False,
help="Archive the delivered items as a tar file",
)
# filtering options
group = parser.add_argument_group("Product filtering options")
group.add_argument(
"-r",
"--rawdata",
default=False,
action="store_true",
help="Deliver the rawdata instead of the products",
)
parser.add_argument(
"source",
type=pathlib.Path,
metavar="SOURCE_DIRECTORY",
help="The directory where the products to be delivered are located",
)
parser.add_argument(
"-u",
"--username",
default="anonymous",
action="store",
help="User on whose behalf we are delivering (defaults to anonymous)",
)
ns = parser.parse_args(args)
return DeliveryContext(
source=ns.source,
tar=ns.tar,
local_destination=pathlib.Path(ns.local_destination) if ns.local_destination else None,
use_piperesults=ns.use_piperesults,
rawdata=ns.rawdata,
prefix=ns.prefix,
username=ns.username,
)