146 lines
4.8 KiB
Python
146 lines
4.8 KiB
Python
# ***** BEGIN GPL LICENSE BLOCK *****
|
|
#
|
|
# This program 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 2
|
|
# of the License, or (at your option) any later version.
|
|
#
|
|
# This program 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 this program; if not, write to the Free Software Foundation,
|
|
# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
#
|
|
# ***** END GPL LICENCE BLOCK *****
|
|
#
|
|
# (c) 2019, Blender Foundation - Sybren A. Stüvel
|
|
"""Shaman Client interface.
|
|
|
|
Note that this supports the Shaman API of Flamenco Manager 2.x. Support for
|
|
Flamenco 3.x will be implemented in a new Flamenco Blender add-on, and not in
|
|
BAT itself.
|
|
"""
|
|
import logging
|
|
import os
|
|
import pathlib
|
|
import typing
|
|
import urllib.parse
|
|
|
|
import requests
|
|
|
|
import blender_asset_tracer.pack as bat_pack
|
|
import blender_asset_tracer.pack.transfer as bat_transfer
|
|
|
|
from .transfer import ShamanTransferrer
|
|
from .client import ShamanClient
|
|
|
|
log = logging.getLogger(__name__)
|
|
|
|
|
|
class ShamanPacker(bat_pack.Packer):
|
|
"""Creates BAT Packs on a Shaman server."""
|
|
|
|
def __init__(
|
|
self,
|
|
bfile: pathlib.Path,
|
|
project: pathlib.Path,
|
|
target: str,
|
|
endpoint: str,
|
|
checkout_id: str,
|
|
**kwargs
|
|
) -> None:
|
|
"""Constructor
|
|
|
|
:param target: mock target '/' to construct project-relative paths.
|
|
:param endpoint: URL of the Shaman endpoint.
|
|
"""
|
|
super().__init__(bfile, project, target, **kwargs)
|
|
self.checkout_id = checkout_id
|
|
self.shaman_endpoint = endpoint
|
|
self._checkout_location = ""
|
|
|
|
def _get_auth_token(self) -> str:
|
|
# TODO: get a token from the Flamenco Server.
|
|
token_from_env = os.environ.get("SHAMAN_JWT_TOKEN")
|
|
if token_from_env:
|
|
return token_from_env
|
|
|
|
log.warning(
|
|
"Using temporary hack to get auth token from Shaman, "
|
|
"set SHAMAN_JTW_TOKEN to prevent"
|
|
)
|
|
unauth_shaman = ShamanClient("", self.shaman_endpoint)
|
|
resp = unauth_shaman.get("get-token", timeout=10)
|
|
resp.raise_for_status()
|
|
return resp.text
|
|
|
|
def _create_file_transferer(self) -> bat_transfer.FileTransferer:
|
|
# TODO: pass self._get_auth_token itself, so that the Transferer will be able to
|
|
# decide when to get this token (and how many times).
|
|
auth_token = self._get_auth_token()
|
|
return ShamanTransferrer(
|
|
auth_token, self.project, self.shaman_endpoint, self.checkout_id
|
|
)
|
|
|
|
def _make_target_path(self, target: str) -> pathlib.PurePath:
|
|
return pathlib.PurePosixPath("/")
|
|
|
|
def _on_file_transfer_finished(self, *, file_transfer_completed: bool):
|
|
super()._on_file_transfer_finished(
|
|
file_transfer_completed=file_transfer_completed
|
|
)
|
|
|
|
assert isinstance(self._file_transferer, ShamanTransferrer)
|
|
self._checkout_location = self._file_transferer.checkout_location
|
|
|
|
@property
|
|
def checkout_location(self) -> str:
|
|
"""Return the checkout location of the packed blend file.
|
|
|
|
:return: the checkout location, or '' if no checkout was made.
|
|
"""
|
|
return self._checkout_location
|
|
|
|
@property
|
|
def output_path(self) -> pathlib.PurePath:
|
|
"""The path of the packed blend file in the target directory."""
|
|
assert self._output_path is not None
|
|
|
|
checkout_location = pathlib.PurePosixPath(self._checkout_location)
|
|
rel_output = self._output_path.relative_to(self._target_path)
|
|
return checkout_location / rel_output
|
|
|
|
def execute(self):
|
|
try:
|
|
super().execute()
|
|
except requests.exceptions.ConnectionError as ex:
|
|
log.exception("Error communicating with Shaman")
|
|
self.abort(str(ex))
|
|
self._check_aborted()
|
|
|
|
|
|
def parse_endpoint(shaman_url: str) -> typing.Tuple[str, str]:
|
|
"""Convert shaman://hostname/path#checkoutID into endpoint URL + checkout ID."""
|
|
|
|
urlparts = urllib.parse.urlparse(str(shaman_url))
|
|
|
|
if urlparts.scheme in {"shaman", "shaman+https"}:
|
|
scheme = "https"
|
|
elif urlparts.scheme == "shaman+http":
|
|
scheme = "http"
|
|
else:
|
|
raise ValueError(
|
|
"Invalid scheme %r, choose shaman:// or shaman+http://", urlparts.scheme
|
|
)
|
|
|
|
checkout_id = urllib.parse.unquote(urlparts.fragment)
|
|
|
|
path = urlparts.path or "/"
|
|
new_urlparts = (scheme, urlparts.netloc, path, *urlparts[3:-1], "")
|
|
endpoint = urllib.parse.urlunparse(new_urlparts)
|
|
|
|
return endpoint, checkout_id
|