More Shaman integration work

- Addon switches between filesystem-packing and Shaman-packing
  automatically, depending on whether the Manager has Shaman enabled.
- Actually using BAT for Shaman packing.

It doesn't work though, some error occurs when receiving Shaman response
from the Manager in the Addon.
This commit is contained in:
Sybren A. Stüvel 2022-03-24 18:56:00 +01:00
parent f9b7510c42
commit 21227c8046
23 changed files with 848 additions and 127 deletions

View File

@ -197,7 +197,7 @@ def copy( # type: ignore
*, *,
relative_only: bool, relative_only: bool,
packer_class=pack.Packer, packer_class=pack.Packer,
**packer_args: dict[Any, Any], packer_kwargs: Optional[dict[Any, Any]] = None,
) -> PackThread: ) -> PackThread:
"""Use BAT to copy the given file and dependencies to the target location. """Use BAT to copy the given file and dependencies to the target location.
@ -210,13 +210,18 @@ def copy( # type: ignore
if _running_packthread is not None: if _running_packthread is not None:
raise RuntimeError("other packing operation already in progress") raise RuntimeError("other packing operation already in progress")
print(f"packer_class: {packer_class}")
if packer_kwargs is None:
packer_kwargs = {}
packer_kwargs.setdefault("compress", True)
packer_kwargs.setdefault("relative_only", relative_only)
print(f"packer_kwargs: {packer_kwargs}")
packer = packer_class( packer = packer_class(
base_blendfile, base_blendfile,
project, project,
target, target,
compress=True, **packer_kwargs,
relative_only=relative_only,
**packer_args,
) )
if exclusion_filter: if exclusion_filter:
filter_parts = exclusion_filter.strip().split(" ") filter_parts = exclusion_filter.strip().split(" ")

View File

@ -44,6 +44,7 @@ class Packer(bat_pack.Packer): # type: ignore
blendfile: Path, blendfile: Path,
project_root: Path, project_root: Path,
target: str, target: str,
*,
api_client: _ApiClient, api_client: _ApiClient,
checkout_path: str, checkout_path: str,
**kwargs: dict[Any, Any], **kwargs: dict[Any, Any],
@ -201,7 +202,7 @@ class Transferrer(bat_transfer.FileTransferer): # type: ignore
try: try:
checksum = cache.compute_cached_checksum(src) checksum = cache.compute_cached_checksum(src)
filesize = src.stat().st_size filesize = src.stat().st_size
relpath: str = bat_bpathlib.strip_root(dst) relpath = str(bat_bpathlib.strip_root(dst))
filespec = ShamanFileSpec( filespec = ShamanFileSpec(
sha=checksum, sha=checksum,
@ -253,6 +254,7 @@ class Transferrer(bat_transfer.FileTransferer): # type: ignore
to_upload: deque[_ShamanFileSpec] = deque() to_upload: deque[_ShamanFileSpec] = deque()
for file_spec in resp.files: for file_spec in resp.files:
print(file_spec)
if file_spec.path not in self._rel_to_local_path: if file_spec.path not in self._rel_to_local_path:
msg = ( msg = (
"Shaman requested path we did not intend to upload: %r" % file_spec "Shaman requested path we did not intend to upload: %r" % file_spec
@ -261,7 +263,7 @@ class Transferrer(bat_transfer.FileTransferer): # type: ignore
self.error_set(msg) self.error_set(msg)
return None return None
self.log.debug(" %s: %s", file_spec.status, file_spec.path) self.log.debug(" %s: %s", file_spec.status.value, file_spec.path)
match file_spec.status.value: match file_spec.status.value:
case "unknown": case "unknown":
to_upload.appendleft(file_spec) to_upload.appendleft(file_spec)

View File

@ -3,8 +3,8 @@
from typing import Optional, TYPE_CHECKING from typing import Optional, TYPE_CHECKING
from flamenco import job_submission from . import preferences
from flamenco.job_types_propgroup import JobTypePropertyGroup from .job_types_propgroup import JobTypePropertyGroup
import bpy import bpy
@ -31,6 +31,8 @@ class FLAMENCO_PT_job_submission(bpy.types.Panel):
def draw(self, context: bpy.types.Context) -> None: def draw(self, context: bpy.types.Context) -> None:
from . import job_types from . import job_types
prefs = preferences.get(context)
layout = self.layout layout = self.layout
layout.use_property_decorate = False layout.use_property_decorate = False
layout.use_property_split = True layout.use_property_split = True
@ -39,7 +41,14 @@ class FLAMENCO_PT_job_submission(bpy.types.Panel):
col = layout.column(align=True) col = layout.column(align=True)
col.prop(context.scene, "flamenco_job_name", text="Job Name") col.prop(context.scene, "flamenco_job_name", text="Job Name")
row = col.row(align=True)
job_storage_col = col.column(align=True)
job_storage_col.enabled = not prefs.is_shaman_enabled
if prefs.is_shaman_enabled:
job_storage_col.label(
text="Shaman API will be used for job submission, so job storage location is ignored:"
)
row = job_storage_col.row(align=True)
row.prop(context.scene, "flamenco_job_storage", text="Job Storage") row.prop(context.scene, "flamenco_job_storage", text="Job Storage")
prop = row.operator("flamenco3.explore_file_path", text="", icon="WINDOW") prop = row.operator("flamenco3.explore_file_path", text="", icon="WINDOW")
prop.path = context.scene.flamenco_job_storage prop.path = context.scene.flamenco_job_storage

View File

@ -1,5 +1,5 @@
# SPDX-License-Identifier: GPL-3.0-or-later # SPDX-License-Identifier: GPL-3.0-or-later
from pathlib import Path from pathlib import Path, PurePosixPath
from typing import TYPE_CHECKING, Optional, Union from typing import TYPE_CHECKING, Optional, Union
import bpy import bpy
@ -47,7 +47,9 @@ def job_for_scene(scene: bpy.types.Scene) -> Optional[_SubmittedJob]:
def set_blend_file( def set_blend_file(
job_type: _AvailableJobType, job: _SubmittedJob, blendfile: Union[str, Path] job_type: _AvailableJobType,
job: _SubmittedJob,
blendfile: Union[str, Path, PurePosixPath],
) -> None: ) -> None:
"""Update the job's 'blendfile' setting, if available. """Update the job's 'blendfile' setting, if available.

View File

@ -10,7 +10,7 @@
""" """
__version__ = "63793f42-dirty" __version__ = "8a43c69f-dirty"
# import ApiClient # import ApiClient
from flamenco.manager.api_client import ApiClient from flamenco.manager.api_client import ApiClient

View File

@ -22,6 +22,7 @@ from flamenco.manager.model_utils import ( # noqa: F401
validate_and_convert_types validate_and_convert_types
) )
from flamenco.manager.model.flamenco_version import FlamencoVersion from flamenco.manager.model.flamenco_version import FlamencoVersion
from flamenco.manager.model.manager_configuration import ManagerConfiguration
class MetaApi(object): class MetaApi(object):
@ -35,6 +36,48 @@ class MetaApi(object):
if api_client is None: if api_client is None:
api_client = ApiClient() api_client = ApiClient()
self.api_client = api_client self.api_client = api_client
self.get_configuration_endpoint = _Endpoint(
settings={
'response_type': (ManagerConfiguration,),
'auth': [],
'endpoint_path': '/api/configuration',
'operation_id': 'get_configuration',
'http_method': 'GET',
'servers': None,
},
params_map={
'all': [
],
'required': [],
'nullable': [
],
'enum': [
],
'validation': [
]
},
root_map={
'validations': {
},
'allowed_values': {
},
'openapi_types': {
},
'attribute_map': {
},
'location_map': {
},
'collection_format_map': {
}
},
headers_map={
'accept': [
'application/json'
],
'content_type': [],
},
api_client=api_client
)
self.get_version_endpoint = _Endpoint( self.get_version_endpoint = _Endpoint(
settings={ settings={
'response_type': (FlamencoVersion,), 'response_type': (FlamencoVersion,),
@ -78,6 +121,78 @@ class MetaApi(object):
api_client=api_client api_client=api_client
) )
def get_configuration(
self,
**kwargs
):
"""Get the configuration of this Manager. # noqa: E501
This method makes a synchronous HTTP request by default. To make an
asynchronous HTTP request, please pass async_req=True
>>> thread = api.get_configuration(async_req=True)
>>> result = thread.get()
Keyword Args:
_return_http_data_only (bool): response data without head status
code and headers. Default is True.
_preload_content (bool): if False, the urllib3.HTTPResponse object
will be returned without reading/decoding response data.
Default is True.
_request_timeout (int/float/tuple): timeout setting for this request. If
one number provided, it will be total request timeout. It can also
be a pair (tuple) of (connection, read) timeouts.
Default is None.
_check_input_type (bool): specifies if type checking
should be done one the data sent to the server.
Default is True.
_check_return_type (bool): specifies if type checking
should be done one the data received from the server.
Default is True.
_spec_property_naming (bool): True if the variable names in the input data
are serialized names, as specified in the OpenAPI document.
False if the variable names in the input data
are pythonic names, e.g. snake case (default)
_content_type (str/None): force body content-type.
Default is None and content-type will be predicted by allowed
content-types and body.
_host_index (int/None): specifies the index of the server
that we want to use.
Default is read from the configuration.
async_req (bool): execute request asynchronously
Returns:
ManagerConfiguration
If the method is called asynchronously, returns the request
thread.
"""
kwargs['async_req'] = kwargs.get(
'async_req', False
)
kwargs['_return_http_data_only'] = kwargs.get(
'_return_http_data_only', True
)
kwargs['_preload_content'] = kwargs.get(
'_preload_content', True
)
kwargs['_request_timeout'] = kwargs.get(
'_request_timeout', None
)
kwargs['_check_input_type'] = kwargs.get(
'_check_input_type', True
)
kwargs['_check_return_type'] = kwargs.get(
'_check_return_type', True
)
kwargs['_spec_property_naming'] = kwargs.get(
'_spec_property_naming', False
)
kwargs['_content_type'] = kwargs.get(
'_content_type')
kwargs['_host_index'] = kwargs.get('_host_index')
return self.get_configuration_endpoint.call_with_http_info(**kwargs)
def get_version( def get_version(
self, self,
**kwargs **kwargs

View File

@ -76,7 +76,7 @@ class ApiClient(object):
self.default_headers[header_name] = header_value self.default_headers[header_name] = header_value
self.cookie = cookie self.cookie = cookie
# Set default User-Agent. # Set default User-Agent.
self.user_agent = 'Flamenco/63793f42-dirty (Blender add-on)' self.user_agent = 'Flamenco/8a43c69f-dirty (Blender add-on)'
def __enter__(self): def __enter__(self):
return self return self

View File

@ -404,7 +404,7 @@ conf = flamenco.manager.Configuration(
"OS: {env}\n"\ "OS: {env}\n"\
"Python Version: {pyversion}\n"\ "Python Version: {pyversion}\n"\
"Version of the API: 1.0.0\n"\ "Version of the API: 1.0.0\n"\
"SDK Package Version: 63793f42-dirty".\ "SDK Package Version: 8a43c69f-dirty".\
format(env=sys.platform, pyversion=sys.version) format(env=sys.platform, pyversion=sys.version)
def get_host_settings(self): def get_host_settings(self):

View File

@ -0,0 +1,12 @@
# ManagerConfiguration
## Properties
Name | Type | Description | Notes
------------ | ------------- | ------------- | -------------
**shaman_enabled** | **bool** | Whether the Shaman file transfer API is available. |
**any string name** | **bool, date, datetime, dict, float, int, list, str, none_type** | any string name can be used but the value must be the correct type | [optional]
[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)

View File

@ -4,9 +4,71 @@ All URIs are relative to *http://localhost*
Method | HTTP request | Description Method | HTTP request | Description
------------- | ------------- | ------------- ------------- | ------------- | -------------
[**get_configuration**](MetaApi.md#get_configuration) | **GET** /api/configuration | Get the configuration of this Manager.
[**get_version**](MetaApi.md#get_version) | **GET** /api/version | Get the Flamenco version of this Manager [**get_version**](MetaApi.md#get_version) | **GET** /api/version | Get the Flamenco version of this Manager
# **get_configuration**
> ManagerConfiguration get_configuration()
Get the configuration of this Manager.
### Example
```python
import time
import flamenco.manager
from flamenco.manager.api import meta_api
from flamenco.manager.model.manager_configuration import ManagerConfiguration
from pprint import pprint
# Defining the host is optional and defaults to http://localhost
# See configuration.py for a list of all supported configuration parameters.
configuration = flamenco.manager.Configuration(
host = "http://localhost"
)
# Enter a context with an instance of the API client
with flamenco.manager.ApiClient() as api_client:
# Create an instance of the API class
api_instance = meta_api.MetaApi(api_client)
# example, this endpoint has no required or optional parameters
try:
# Get the configuration of this Manager.
api_response = api_instance.get_configuration()
pprint(api_response)
except flamenco.manager.ApiException as e:
print("Exception when calling MetaApi->get_configuration: %s\n" % e)
```
### Parameters
This endpoint does not need any parameter.
### Return type
[**ManagerConfiguration**](ManagerConfiguration.md)
### Authorization
No authorization required
### HTTP request headers
- **Content-Type**: Not defined
- **Accept**: application/json
### HTTP response details
| Status code | Description | Response headers |
|-------------|-------------|------------------|
**200** | normal response | - |
[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)
# **get_version** # **get_version**
> FlamencoVersion get_version() > FlamencoVersion get_version()

View File

@ -0,0 +1,261 @@
"""
Flamenco manager
Render Farm manager API # noqa: E501
The version of the OpenAPI document: 1.0.0
Generated by: https://openapi-generator.tech
"""
import re # noqa: F401
import sys # noqa: F401
from flamenco.manager.model_utils import ( # noqa: F401
ApiTypeError,
ModelComposed,
ModelNormal,
ModelSimple,
cached_property,
change_keys_js_to_python,
convert_js_args_to_python_args,
date,
datetime,
file_type,
none_type,
validate_get_composed_info,
OpenApiModel
)
from flamenco.manager.exceptions import ApiAttributeError
class ManagerConfiguration(ModelNormal):
"""NOTE: This class is auto generated by OpenAPI Generator.
Ref: https://openapi-generator.tech
Do not edit the class manually.
Attributes:
allowed_values (dict): The key is the tuple path to the attribute
and the for var_name this is (var_name,). The value is a dict
with a capitalized key describing the allowed value and an allowed
value. These dicts store the allowed enum values.
attribute_map (dict): The key is attribute name
and the value is json key in definition.
discriminator_value_class_map (dict): A dict to go from the discriminator
variable value to the discriminator class name.
validations (dict): The key is the tuple path to the attribute
and the for var_name this is (var_name,). The value is a dict
that stores validations for max_length, min_length, max_items,
min_items, exclusive_maximum, inclusive_maximum, exclusive_minimum,
inclusive_minimum, and regex.
additional_properties_type (tuple): A tuple of classes accepted
as additional properties values.
"""
allowed_values = {
}
validations = {
}
@cached_property
def additional_properties_type():
"""
This must be a method because a model may have properties that are
of type self, this must run after the class is loaded
"""
return (bool, date, datetime, dict, float, int, list, str, none_type,) # noqa: E501
_nullable = False
@cached_property
def openapi_types():
"""
This must be a method because a model may have properties that are
of type self, this must run after the class is loaded
Returns
openapi_types (dict): The key is attribute name
and the value is attribute type.
"""
return {
'shaman_enabled': (bool,), # noqa: E501
}
@cached_property
def discriminator():
return None
attribute_map = {
'shaman_enabled': 'shamanEnabled', # noqa: E501
}
read_only_vars = {
}
_composed_schemas = {}
@classmethod
@convert_js_args_to_python_args
def _from_openapi_data(cls, shaman_enabled, *args, **kwargs): # noqa: E501
"""ManagerConfiguration - a model defined in OpenAPI
Args:
shaman_enabled (bool): Whether the Shaman file transfer API is available.
Keyword Args:
_check_type (bool): if True, values for parameters in openapi_types
will be type checked and a TypeError will be
raised if the wrong type is input.
Defaults to True
_path_to_item (tuple/list): This is a list of keys or values to
drill down to the model in received_data
when deserializing a response
_spec_property_naming (bool): True if the variable names in the input data
are serialized names, as specified in the OpenAPI document.
False if the variable names in the input data
are pythonic names, e.g. snake case (default)
_configuration (Configuration): the instance to use when
deserializing a file_type parameter.
If passed, type conversion is attempted
If omitted no type conversion is done.
_visited_composed_classes (tuple): This stores a tuple of
classes that we have traveled through so that
if we see that class again we will not use its
discriminator again.
When traveling through a discriminator, the
composed schema that is
is traveled through is added to this set.
For example if Animal has a discriminator
petType and we pass in "Dog", and the class Dog
allOf includes Animal, we move through Animal
once using the discriminator, and pick Dog.
Then in Dog, we will make an instance of the
Animal class but this time we won't travel
through its discriminator because we passed in
_visited_composed_classes = (Animal,)
"""
_check_type = kwargs.pop('_check_type', True)
_spec_property_naming = kwargs.pop('_spec_property_naming', False)
_path_to_item = kwargs.pop('_path_to_item', ())
_configuration = kwargs.pop('_configuration', None)
_visited_composed_classes = kwargs.pop('_visited_composed_classes', ())
self = super(OpenApiModel, cls).__new__(cls)
if args:
raise ApiTypeError(
"Invalid positional arguments=%s passed to %s. Remove those invalid positional arguments." % (
args,
self.__class__.__name__,
),
path_to_item=_path_to_item,
valid_classes=(self.__class__,),
)
self._data_store = {}
self._check_type = _check_type
self._spec_property_naming = _spec_property_naming
self._path_to_item = _path_to_item
self._configuration = _configuration
self._visited_composed_classes = _visited_composed_classes + (self.__class__,)
self.shaman_enabled = shaman_enabled
for var_name, var_value in kwargs.items():
if var_name not in self.attribute_map and \
self._configuration is not None and \
self._configuration.discard_unknown_keys and \
self.additional_properties_type is None:
# discard variable.
continue
setattr(self, var_name, var_value)
return self
required_properties = set([
'_data_store',
'_check_type',
'_spec_property_naming',
'_path_to_item',
'_configuration',
'_visited_composed_classes',
])
@convert_js_args_to_python_args
def __init__(self, shaman_enabled, *args, **kwargs): # noqa: E501
"""ManagerConfiguration - a model defined in OpenAPI
Args:
shaman_enabled (bool): Whether the Shaman file transfer API is available.
Keyword Args:
_check_type (bool): if True, values for parameters in openapi_types
will be type checked and a TypeError will be
raised if the wrong type is input.
Defaults to True
_path_to_item (tuple/list): This is a list of keys or values to
drill down to the model in received_data
when deserializing a response
_spec_property_naming (bool): True if the variable names in the input data
are serialized names, as specified in the OpenAPI document.
False if the variable names in the input data
are pythonic names, e.g. snake case (default)
_configuration (Configuration): the instance to use when
deserializing a file_type parameter.
If passed, type conversion is attempted
If omitted no type conversion is done.
_visited_composed_classes (tuple): This stores a tuple of
classes that we have traveled through so that
if we see that class again we will not use its
discriminator again.
When traveling through a discriminator, the
composed schema that is
is traveled through is added to this set.
For example if Animal has a discriminator
petType and we pass in "Dog", and the class Dog
allOf includes Animal, we move through Animal
once using the discriminator, and pick Dog.
Then in Dog, we will make an instance of the
Animal class but this time we won't travel
through its discriminator because we passed in
_visited_composed_classes = (Animal,)
"""
_check_type = kwargs.pop('_check_type', True)
_spec_property_naming = kwargs.pop('_spec_property_naming', False)
_path_to_item = kwargs.pop('_path_to_item', ())
_configuration = kwargs.pop('_configuration', None)
_visited_composed_classes = kwargs.pop('_visited_composed_classes', ())
if args:
raise ApiTypeError(
"Invalid positional arguments=%s passed to %s. Remove those invalid positional arguments." % (
args,
self.__class__.__name__,
),
path_to_item=_path_to_item,
valid_classes=(self.__class__,),
)
self._data_store = {}
self._check_type = _check_type
self._spec_property_naming = _spec_property_naming
self._path_to_item = _path_to_item
self._configuration = _configuration
self._visited_composed_classes = _visited_composed_classes + (self.__class__,)
self.shaman_enabled = shaman_enabled
for var_name, var_value in kwargs.items():
if var_name not in self.attribute_map and \
self._configuration is not None and \
self._configuration.discard_unknown_keys and \
self.additional_properties_type is None:
# discard variable.
continue
setattr(self, var_name, var_value)
if var_name in self.read_only_vars:
raise ApiAttributeError(f"`{var_name}` is a read-only attribute. Use `from_openapi_data` to instantiate "
f"class with read only attributes.")

View File

@ -23,6 +23,7 @@ from flamenco.manager.model.job_all_of import JobAllOf
from flamenco.manager.model.job_metadata import JobMetadata from flamenco.manager.model.job_metadata import JobMetadata
from flamenco.manager.model.job_settings import JobSettings from flamenco.manager.model.job_settings import JobSettings
from flamenco.manager.model.job_status import JobStatus from flamenco.manager.model.job_status import JobStatus
from flamenco.manager.model.manager_configuration import ManagerConfiguration
from flamenco.manager.model.registered_worker import RegisteredWorker from flamenco.manager.model.registered_worker import RegisteredWorker
from flamenco.manager.model.security_error import SecurityError from flamenco.manager.model.security_error import SecurityError
from flamenco.manager.model.shaman_checkout import ShamanCheckout from flamenco.manager.model.shaman_checkout import ShamanCheckout

View File

@ -4,7 +4,7 @@ Render Farm manager API
The `flamenco.manager` package is automatically generated by the [OpenAPI Generator](https://openapi-generator.tech) project: The `flamenco.manager` package is automatically generated by the [OpenAPI Generator](https://openapi-generator.tech) project:
- API version: 1.0.0 - API version: 1.0.0
- Package version: 63793f42-dirty - Package version: 8a43c69f-dirty
- Build package: org.openapitools.codegen.languages.PythonClientCodegen - Build package: org.openapitools.codegen.languages.PythonClientCodegen
For more information, please visit [https://flamenco.io/](https://flamenco.io/) For more information, please visit [https://flamenco.io/](https://flamenco.io/)
@ -67,6 +67,7 @@ Class | Method | HTTP request | Description
*JobsApi* | [**fetch_job**](flamenco/manager/docs/JobsApi.md#fetch_job) | **GET** /api/jobs/{job_id} | Fetch info about the job. *JobsApi* | [**fetch_job**](flamenco/manager/docs/JobsApi.md#fetch_job) | **GET** /api/jobs/{job_id} | Fetch info about the job.
*JobsApi* | [**get_job_types**](flamenco/manager/docs/JobsApi.md#get_job_types) | **GET** /api/jobs/types | Get list of job types and their parameters. *JobsApi* | [**get_job_types**](flamenco/manager/docs/JobsApi.md#get_job_types) | **GET** /api/jobs/types | Get list of job types and their parameters.
*JobsApi* | [**submit_job**](flamenco/manager/docs/JobsApi.md#submit_job) | **POST** /api/jobs | Submit a new job for Flamenco Manager to execute. *JobsApi* | [**submit_job**](flamenco/manager/docs/JobsApi.md#submit_job) | **POST** /api/jobs | Submit a new job for Flamenco Manager to execute.
*MetaApi* | [**get_configuration**](flamenco/manager/docs/MetaApi.md#get_configuration) | **GET** /api/configuration | Get the configuration of this Manager.
*MetaApi* | [**get_version**](flamenco/manager/docs/MetaApi.md#get_version) | **GET** /api/version | Get the Flamenco version of this Manager *MetaApi* | [**get_version**](flamenco/manager/docs/MetaApi.md#get_version) | **GET** /api/version | Get the Flamenco version of this Manager
*ShamanApi* | [**shaman_checkout**](flamenco/manager/docs/ShamanApi.md#shaman_checkout) | **POST** /shaman/checkout/create | Create a directory, and symlink the required files into it. The files must all have been uploaded to Shaman before calling this endpoint. *ShamanApi* | [**shaman_checkout**](flamenco/manager/docs/ShamanApi.md#shaman_checkout) | **POST** /shaman/checkout/create | Create a directory, and symlink the required files into it. The files must all have been uploaded to Shaman before calling this endpoint.
*ShamanApi* | [**shaman_checkout_requirements**](flamenco/manager/docs/ShamanApi.md#shaman_checkout_requirements) | **POST** /shaman/checkout/requirements | Checks a Shaman Requirements file, and reports which files are unknown. *ShamanApi* | [**shaman_checkout_requirements**](flamenco/manager/docs/ShamanApi.md#shaman_checkout_requirements) | **POST** /shaman/checkout/requirements | Checks a Shaman Requirements file, and reports which files are unknown.
@ -97,6 +98,7 @@ Class | Method | HTTP request | Description
- [JobMetadata](flamenco/manager/docs/JobMetadata.md) - [JobMetadata](flamenco/manager/docs/JobMetadata.md)
- [JobSettings](flamenco/manager/docs/JobSettings.md) - [JobSettings](flamenco/manager/docs/JobSettings.md)
- [JobStatus](flamenco/manager/docs/JobStatus.md) - [JobStatus](flamenco/manager/docs/JobStatus.md)
- [ManagerConfiguration](flamenco/manager/docs/ManagerConfiguration.md)
- [RegisteredWorker](flamenco/manager/docs/RegisteredWorker.md) - [RegisteredWorker](flamenco/manager/docs/RegisteredWorker.md)
- [SecurityError](flamenco/manager/docs/SecurityError.md) - [SecurityError](flamenco/manager/docs/SecurityError.md)
- [ShamanCheckout](flamenco/manager/docs/ShamanCheckout.md) - [ShamanCheckout](flamenco/manager/docs/ShamanCheckout.md)

View File

@ -2,18 +2,19 @@
# <pep8 compliant> # <pep8 compliant>
import datetime import datetime
import functools
import logging import logging
from pathlib import Path from pathlib import Path, PurePosixPath
from typing import Optional, TYPE_CHECKING from typing import Optional, TYPE_CHECKING
from urllib3.exceptions import HTTPError, MaxRetryError from urllib3.exceptions import HTTPError, MaxRetryError
import bpy import bpy
from . import job_types, job_submission from . import job_types, job_submission, preferences
from .job_types_propgroup import JobTypePropertyGroup from .job_types_propgroup import JobTypePropertyGroup
if TYPE_CHECKING: if TYPE_CHECKING:
from .bat_interface import ( from .bat.interface import (
PackThread as _PackThread, PackThread as _PackThread,
Message as _Message, Message as _Message,
) )
@ -50,7 +51,6 @@ class FLAMENCO_OT_fetch_job_types(FlamencoOpMixin, bpy.types.Operator):
api_client = self.get_api_client(context) api_client = self.get_api_client(context)
from flamenco.manager import ApiException from flamenco.manager import ApiException
from . import job_types
scene = context.scene scene = context.scene
old_job_type_name = getattr(scene, "flamenco_job_type", "") old_job_type_name = getattr(scene, "flamenco_job_type", "")
@ -85,13 +85,14 @@ class FLAMENCO_OT_ping_manager(FlamencoOpMixin, bpy.types.Operator):
from flamenco.manager import ApiException from flamenco.manager import ApiException
from flamenco.manager.apis import MetaApi from flamenco.manager.apis import MetaApi
from flamenco.manager.models import FlamencoVersion from flamenco.manager.models import FlamencoVersion, ManagerConfiguration
context.window_manager.flamenco_status_ping = "..." context.window_manager.flamenco_status_ping = "..."
meta_api = MetaApi(api_client) meta_api = MetaApi(api_client)
try: try:
response: FlamencoVersion = meta_api.get_version() version: FlamencoVersion = meta_api.get_version()
config: ManagerConfiguration = meta_api.get_configuration()
except ApiException as ex: except ApiException as ex:
report = "Manager cannot be reached: %s" % ex report = "Manager cannot be reached: %s" % ex
level = "ERROR" level = "ERROR"
@ -104,9 +105,13 @@ class FLAMENCO_OT_ping_manager(FlamencoOpMixin, bpy.types.Operator):
report = "Manager cannot be reached: %s" % ex report = "Manager cannot be reached: %s" % ex
level = "ERROR" level = "ERROR"
else: else:
report = "%s version %s found" % (response.name, response.version) report = "%s version %s found" % (version.name, version.version)
level = "INFO" level = "INFO"
# Store whether this Manager supports the Shaman API.
prefs = preferences.get(context)
prefs.is_shaman_enabled = config.shaman_enabled
self.report({level}, report) self.report({level}, report)
context.window_manager.flamenco_status_ping = report context.window_manager.flamenco_status_ping = report
return {"FINISHED"} return {"FINISHED"}
@ -138,7 +143,7 @@ class FLAMENCO_OT_submit_job(FlamencoOpMixin, bpy.types.Operator):
bl_description = "Pack the current blend file and send it to Flamenco" bl_description = "Pack the current blend file and send it to Flamenco"
bl_options = {"REGISTER"} # No UNDO. bl_options = {"REGISTER"} # No UNDO.
blendfile_on_farm: Optional[Path] = None blendfile_on_farm: Optional[PurePosixPath] = None
job_name: bpy.props.StringProperty(name="Job Name") # type: ignore job_name: bpy.props.StringProperty(name="Job Name") # type: ignore
job: Optional[_SubmittedJob] = None job: Optional[_SubmittedJob] = None
@ -211,13 +216,36 @@ class FLAMENCO_OT_submit_job(FlamencoOpMixin, bpy.types.Operator):
return filepath return filepath
def _bat_pack(self, context: bpy.types.Context, blendfile: Path) -> set[str]: def _bat_pack(self, context: bpy.types.Context, blendfile: Path) -> set[str]:
from . import bat_interface from .bat import interface as bat_interface
if bat_interface.is_packing(): if bat_interface.is_packing():
self.report({"ERROR"}, "Another packing operation is running") self.report({"ERROR"}, "Another packing operation is running")
self._quit(context) self._quit(context)
return {"CANCELLED"} return {"CANCELLED"}
prefs = preferences.get(context)
if prefs.is_shaman_enabled:
blendfile_on_farm = self._bat_pack_shaman(context, blendfile)
else:
blendfile_on_farm = self._bat_pack_filesystem(context, blendfile)
self.blendfile_on_farm = blendfile_on_farm
context.window_manager.modal_handler_add(self)
wm = context.window_manager
self.timer = wm.event_timer_add(0.25, window=context.window)
return {"RUNNING_MODAL"}
def _bat_pack_filesystem(
self, context: bpy.types.Context, blendfile: Path
) -> PurePosixPath:
"""Use BAT to store the pack on the filesystem.
:return: the path of the blend file, for use in the job definition.
"""
from .bat import interface as bat_interface
# TODO: get project path from addon preferences / project definition on Manager. # TODO: get project path from addon preferences / project definition on Manager.
project_path = blendfile.parent project_path = blendfile.parent
try: try:
@ -225,7 +253,7 @@ class FLAMENCO_OT_submit_job(FlamencoOpMixin, bpy.types.Operator):
except FileNotFoundError: except FileNotFoundError:
# Path.resolve() will raise a FileNotFoundError if the project path doesn't exist. # Path.resolve() will raise a FileNotFoundError if the project path doesn't exist.
self.report({"ERROR"}, "Project path %s does not exist" % project_path) self.report({"ERROR"}, "Project path %s does not exist" % project_path)
return {"CANCELLED"} raise # TODO: handle this properly.
# Determine where the blend file will be stored. # Determine where the blend file will be stored.
unique_dir = "%s-%s" % ( unique_dir = "%s-%s" % (
@ -237,7 +265,6 @@ class FLAMENCO_OT_submit_job(FlamencoOpMixin, bpy.types.Operator):
# TODO: this should take the blendfile location relative to the project path into account. # TODO: this should take the blendfile location relative to the project path into account.
pack_target_file = pack_target_dir / blendfile.name pack_target_file = pack_target_dir / blendfile.name
self.log.info("Will store blend file at %s", pack_target_file) self.log.info("Will store blend file at %s", pack_target_file)
self.blendfile_on_farm = pack_target_file
self.packthread = bat_interface.copy( self.packthread = bat_interface.copy(
base_blendfile=blendfile, base_blendfile=blendfile,
@ -247,14 +274,44 @@ class FLAMENCO_OT_submit_job(FlamencoOpMixin, bpy.types.Operator):
relative_only=True, # TODO: get from GUI. relative_only=True, # TODO: get from GUI.
) )
context.window_manager.modal_handler_add(self) return PurePosixPath(pack_target_file.as_posix())
wm = context.window_manager
self.timer = wm.event_timer_add(0.25, window=context.window)
return {"RUNNING_MODAL"} def _bat_pack_shaman(
self, context: bpy.types.Context, blendfile: Path
) -> PurePosixPath:
"""Use the Manager's Shaman API to submit the BAT pack.
:return: the filesystem path of the blend file, for in the render job definition.
"""
from .bat import (
interface as bat_interface,
shaman as bat_shaman,
)
assert self.job is not None
self.log.info("Sending BAT pack to Shaman")
# TODO: get project name from preferences/GUI.
# TODO: update Shaman API to ensure this is a unique location.
checkout_root = PurePosixPath(f"project/{self.job.name}")
self.packthread = bat_interface.copy(
base_blendfile=blendfile,
project=blendfile.parent, # TODO: get from preferences/GUI.
target="/", # Target directory irrelevant for Shaman transfers.
exclusion_filter="", # TODO: get from GUI.
relative_only=True, # TODO: get from GUI.
packer_class=functools.partial(
bat_shaman.Packer,
api_client=self.get_api_client(context),
checkout_path=checkout_root,
),
)
return checkout_root / blendfile.name # TODO: get relative to the checkout dir.
def _on_bat_pack_msg(self, context: bpy.types.Context, msg: _Message) -> set[str]: def _on_bat_pack_msg(self, context: bpy.types.Context, msg: _Message) -> set[str]:
from . import bat_interface from .bat import interface as bat_interface
if isinstance(msg, bat_interface.MsgDone): if isinstance(msg, bat_interface.MsgDone):
self._submit_job(context) self._submit_job(context)

View File

@ -9,6 +9,7 @@ def discard_flamenco_client(prefs, context):
from . import comms from . import comms
comms.discard_flamenco_data() comms.discard_flamenco_data()
context.window_manager.flamenco_status_ping = ""
def _update_default_job_storage( def _update_default_job_storage(
@ -28,6 +29,12 @@ class FlamencoPreferences(bpy.types.AddonPreferences):
update=discard_flamenco_client, update=discard_flamenco_client,
) )
is_shaman_enabled: bpy.props.BoolProperty( # type: ignore
name="Shaman Enabled",
description="Whether this Manager has the Shaman protocol enabled",
default=False,
)
job_storage: bpy.props.StringProperty( # type: ignore job_storage: bpy.props.StringProperty( # type: ignore
name="Job Storage Directory", name="Job Storage Directory",
subtype="DIR_PATH", subtype="DIR_PATH",
@ -48,6 +55,12 @@ class FlamencoPreferences(bpy.types.AddonPreferences):
if context.window_manager.flamenco_status_ping: if context.window_manager.flamenco_status_ping:
col.label(text=context.window_manager.flamenco_status_ping) col.label(text=context.window_manager.flamenco_status_ping)
col = layout.column(align=True)
col.enabled = not self.is_shaman_enabled
if self.is_shaman_enabled:
col.label(
text="This Manager supports the Shaman API, so this setting will be ignored:"
)
col.prop(self, "job_storage") col.prop(self, "job_storage")
@ -88,6 +101,7 @@ _register, _unregister = bpy.utils.register_classes_factory(classes)
def register(): def register():
_register() _register()
_register_rna_props(get(bpy.context)) _register_rna_props(get(bpy.context))
bpy.context.window_manager.flamenco_status_ping = ""
def unregister(): def unregister():

View File

@ -7,9 +7,7 @@ import (
"context" "context"
"fmt" "fmt"
"io" "io"
"net/http"
"git.blender.org/flamenco/internal/appinfo"
"git.blender.org/flamenco/internal/manager/job_compilers" "git.blender.org/flamenco/internal/manager/job_compilers"
"git.blender.org/flamenco/internal/manager/persistence" "git.blender.org/flamenco/internal/manager/persistence"
"git.blender.org/flamenco/internal/manager/task_state_machine" "git.blender.org/flamenco/internal/manager/task_state_machine"
@ -136,10 +134,3 @@ func sendAPIError(e echo.Context, code int, message string, args ...interface{})
} }
return e.JSON(code, petErr) return e.JSON(code, petErr)
} }
func (f *Flamenco) GetVersion(e echo.Context) error {
return e.JSON(http.StatusOK, api.FlamencoVersion{
Version: appinfo.ApplicationVersion,
Name: appinfo.ApplicationName,
})
}

View File

@ -0,0 +1,25 @@
// Package api_impl implements the OpenAPI API from pkg/api/flamenco-manager.yaml.
package api_impl
// SPDX-License-Identifier: GPL-3.0-or-later
import (
"net/http"
"git.blender.org/flamenco/internal/appinfo"
"git.blender.org/flamenco/pkg/api"
"github.com/labstack/echo/v4"
)
func (f *Flamenco) GetVersion(e echo.Context) error {
return e.JSON(http.StatusOK, api.FlamencoVersion{
Version: appinfo.ApplicationVersion,
Name: appinfo.ApplicationName,
})
}
func (f *Flamenco) GetConfiguration(e echo.Context) error {
return e.JSON(http.StatusOK, api.ManagerConfiguration{
ShamanEnabled: f.isShamanEnabled(),
})
}

View File

@ -56,6 +56,26 @@ func (mr *MockFlamencoClientMockRecorder) FetchJobWithResponse(arg0, arg1 interf
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FetchJobWithResponse", reflect.TypeOf((*MockFlamencoClient)(nil).FetchJobWithResponse), varargs...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FetchJobWithResponse", reflect.TypeOf((*MockFlamencoClient)(nil).FetchJobWithResponse), varargs...)
} }
// GetConfigurationWithResponse mocks base method.
func (m *MockFlamencoClient) GetConfigurationWithResponse(arg0 context.Context, arg1 ...api.RequestEditorFn) (*api.GetConfigurationResponse, error) {
m.ctrl.T.Helper()
varargs := []interface{}{arg0}
for _, a := range arg1 {
varargs = append(varargs, a)
}
ret := m.ctrl.Call(m, "GetConfigurationWithResponse", varargs...)
ret0, _ := ret[0].(*api.GetConfigurationResponse)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// GetConfigurationWithResponse indicates an expected call of GetConfigurationWithResponse.
func (mr *MockFlamencoClientMockRecorder) GetConfigurationWithResponse(arg0 interface{}, arg1 ...interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
varargs := append([]interface{}{arg0}, arg1...)
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetConfigurationWithResponse", reflect.TypeOf((*MockFlamencoClient)(nil).GetConfigurationWithResponse), varargs...)
}
// GetJobTypesWithResponse mocks base method. // GetJobTypesWithResponse mocks base method.
func (m *MockFlamencoClient) GetJobTypesWithResponse(arg0 context.Context, arg1 ...api.RequestEditorFn) (*api.GetJobTypesResponse, error) { func (m *MockFlamencoClient) GetJobTypesWithResponse(arg0 context.Context, arg1 ...api.RequestEditorFn) (*api.GetJobTypesResponse, error) {
m.ctrl.T.Helper() m.ctrl.T.Helper()

View File

@ -13,7 +13,7 @@ servers:
- url: / - url: /
paths: paths:
/api/version: /api/version:
summary: Workers can use this to check this is actually a Flamenco server. summary: Clients can use this to check this is actually a Flamenco server.
get: get:
summary: Get the Flamenco version of this Manager summary: Get the Flamenco version of this Manager
operationId: getVersion operationId: getVersion
@ -26,6 +26,19 @@ paths:
schema: schema:
$ref: "#/components/schemas/FlamencoVersion" $ref: "#/components/schemas/FlamencoVersion"
/api/configuration:
summary: Endpoint for getting configuration of Flamenco Manager.
get:
summary: Get the configuration of this Manager.
operationId: getConfiguration
tags: [meta]
responses:
"200":
description: normal response
content:
application/json:
schema: {$ref: "#/components/schemas/ManagerConfiguration"}
/api/worker/register-worker: /api/worker/register-worker:
summary: Registration of new workers summary: Registration of new workers
post: post:
@ -437,6 +450,14 @@ components:
version: { type: string } version: { type: string }
name: { type: string } name: { type: string }
ManagerConfiguration:
type: object
properties:
"shamanEnabled":
description: Whether the Shaman file transfer API is available.
type: boolean
required: [shamanEnabled]
WorkerRegistration: WorkerRegistration:
type: object type: object
required: [secret, platform, supported_task_types, nickname] required: [secret, platform, supported_task_types, nickname]

View File

@ -90,6 +90,9 @@ func WithRequestEditorFn(fn RequestEditorFn) ClientOption {
// The interface specification for the client above. // The interface specification for the client above.
type ClientInterface interface { type ClientInterface interface {
// GetConfiguration request
GetConfiguration(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error)
// SubmitJob request with any body // SubmitJob request with any body
SubmitJobWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) SubmitJobWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error)
@ -150,6 +153,18 @@ type ClientInterface interface {
ShamanFileStoreWithBody(ctx context.Context, checksum string, filesize int, params *ShamanFileStoreParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) ShamanFileStoreWithBody(ctx context.Context, checksum string, filesize int, params *ShamanFileStoreParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error)
} }
func (c *Client) GetConfiguration(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error) {
req, err := NewGetConfigurationRequest(c.Server)
if err != nil {
return nil, err
}
req = req.WithContext(ctx)
if err := c.applyEditors(ctx, req, reqEditors); err != nil {
return nil, err
}
return c.Client.Do(req)
}
func (c *Client) SubmitJobWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { func (c *Client) SubmitJobWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) {
req, err := NewSubmitJobRequestWithBody(c.Server, contentType, body) req, err := NewSubmitJobRequestWithBody(c.Server, contentType, body)
if err != nil { if err != nil {
@ -414,6 +429,33 @@ func (c *Client) ShamanFileStoreWithBody(ctx context.Context, checksum string, f
return c.Client.Do(req) return c.Client.Do(req)
} }
// NewGetConfigurationRequest generates requests for GetConfiguration
func NewGetConfigurationRequest(server string) (*http.Request, error) {
var err error
serverURL, err := url.Parse(server)
if err != nil {
return nil, err
}
operationPath := fmt.Sprintf("/api/configuration")
if operationPath[0] == '/' {
operationPath = "." + operationPath
}
queryURL, err := serverURL.Parse(operationPath)
if err != nil {
return nil, err
}
req, err := http.NewRequest("GET", queryURL.String(), nil)
if err != nil {
return nil, err
}
return req, nil
}
// NewSubmitJobRequest calls the generic SubmitJob builder with application/json body // NewSubmitJobRequest calls the generic SubmitJob builder with application/json body
func NewSubmitJobRequest(server string, body SubmitJobJSONRequestBody) (*http.Request, error) { func NewSubmitJobRequest(server string, body SubmitJobJSONRequestBody) (*http.Request, error) {
var bodyReader io.Reader var bodyReader io.Reader
@ -1019,6 +1061,9 @@ func WithBaseURL(baseURL string) ClientOption {
// ClientWithResponsesInterface is the interface specification for the client with responses above. // ClientWithResponsesInterface is the interface specification for the client with responses above.
type ClientWithResponsesInterface interface { type ClientWithResponsesInterface interface {
// GetConfiguration request
GetConfigurationWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*GetConfigurationResponse, error)
// SubmitJob request with any body // SubmitJob request with any body
SubmitJobWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*SubmitJobResponse, error) SubmitJobWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*SubmitJobResponse, error)
@ -1079,6 +1124,28 @@ type ClientWithResponsesInterface interface {
ShamanFileStoreWithBodyWithResponse(ctx context.Context, checksum string, filesize int, params *ShamanFileStoreParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*ShamanFileStoreResponse, error) ShamanFileStoreWithBodyWithResponse(ctx context.Context, checksum string, filesize int, params *ShamanFileStoreParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*ShamanFileStoreResponse, error)
} }
type GetConfigurationResponse struct {
Body []byte
HTTPResponse *http.Response
JSON200 *ManagerConfiguration
}
// Status returns HTTPResponse.Status
func (r GetConfigurationResponse) Status() string {
if r.HTTPResponse != nil {
return r.HTTPResponse.Status
}
return http.StatusText(0)
}
// StatusCode returns HTTPResponse.StatusCode
func (r GetConfigurationResponse) StatusCode() int {
if r.HTTPResponse != nil {
return r.HTTPResponse.StatusCode
}
return 0
}
type SubmitJobResponse struct { type SubmitJobResponse struct {
Body []byte Body []byte
HTTPResponse *http.Response HTTPResponse *http.Response
@ -1419,6 +1486,15 @@ func (r ShamanFileStoreResponse) StatusCode() int {
return 0 return 0
} }
// GetConfigurationWithResponse request returning *GetConfigurationResponse
func (c *ClientWithResponses) GetConfigurationWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*GetConfigurationResponse, error) {
rsp, err := c.GetConfiguration(ctx, reqEditors...)
if err != nil {
return nil, err
}
return ParseGetConfigurationResponse(rsp)
}
// SubmitJobWithBodyWithResponse request with arbitrary body returning *SubmitJobResponse // SubmitJobWithBodyWithResponse request with arbitrary body returning *SubmitJobResponse
func (c *ClientWithResponses) SubmitJobWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*SubmitJobResponse, error) { func (c *ClientWithResponses) SubmitJobWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*SubmitJobResponse, error) {
rsp, err := c.SubmitJobWithBody(ctx, contentType, body, reqEditors...) rsp, err := c.SubmitJobWithBody(ctx, contentType, body, reqEditors...)
@ -1610,6 +1686,32 @@ func (c *ClientWithResponses) ShamanFileStoreWithBodyWithResponse(ctx context.Co
return ParseShamanFileStoreResponse(rsp) return ParseShamanFileStoreResponse(rsp)
} }
// ParseGetConfigurationResponse parses an HTTP response from a GetConfigurationWithResponse call
func ParseGetConfigurationResponse(rsp *http.Response) (*GetConfigurationResponse, error) {
bodyBytes, err := ioutil.ReadAll(rsp.Body)
defer func() { _ = rsp.Body.Close() }()
if err != nil {
return nil, err
}
response := &GetConfigurationResponse{
Body: bodyBytes,
HTTPResponse: rsp,
}
switch {
case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200:
var dest ManagerConfiguration
if err := json.Unmarshal(bodyBytes, &dest); err != nil {
return nil, err
}
response.JSON200 = &dest
}
return response, nil
}
// ParseSubmitJobResponse parses an HTTP response from a SubmitJobWithResponse call // ParseSubmitJobResponse parses an HTTP response from a SubmitJobWithResponse call
func ParseSubmitJobResponse(rsp *http.Response) (*SubmitJobResponse, error) { func ParseSubmitJobResponse(rsp *http.Response) (*SubmitJobResponse, error) {
bodyBytes, err := ioutil.ReadAll(rsp.Body) bodyBytes, err := ioutil.ReadAll(rsp.Body)

View File

@ -13,6 +13,9 @@ import (
// ServerInterface represents all server handlers. // ServerInterface represents all server handlers.
type ServerInterface interface { type ServerInterface interface {
// Get the configuration of this Manager.
// (GET /api/configuration)
GetConfiguration(ctx echo.Context) error
// Submit a new job for Flamenco Manager to execute. // Submit a new job for Flamenco Manager to execute.
// (POST /api/jobs) // (POST /api/jobs)
SubmitJob(ctx echo.Context) error SubmitJob(ctx echo.Context) error
@ -66,6 +69,15 @@ type ServerInterfaceWrapper struct {
Handler ServerInterface Handler ServerInterface
} }
// GetConfiguration converts echo context to params.
func (w *ServerInterfaceWrapper) GetConfiguration(ctx echo.Context) error {
var err error
// Invoke the callback with all the unmarshalled arguments
err = w.Handler.GetConfiguration(ctx)
return err
}
// SubmitJob converts echo context to params. // SubmitJob converts echo context to params.
func (w *ServerInterfaceWrapper) SubmitJob(ctx echo.Context) error { func (w *ServerInterfaceWrapper) SubmitJob(ctx echo.Context) error {
var err error var err error
@ -320,6 +332,7 @@ func RegisterHandlersWithBaseURL(router EchoRouter, si ServerInterface, baseURL
Handler: si, Handler: si,
} }
router.GET(baseURL+"/api/configuration", wrapper.GetConfiguration)
router.POST(baseURL+"/api/jobs", wrapper.SubmitJob) router.POST(baseURL+"/api/jobs", wrapper.SubmitJob)
router.GET(baseURL+"/api/jobs/types", wrapper.GetJobTypes) router.GET(baseURL+"/api/jobs/types", wrapper.GetJobTypes)
router.GET(baseURL+"/api/jobs/:job_id", wrapper.FetchJob) router.GET(baseURL+"/api/jobs/:job_id", wrapper.FetchJob)

View File

@ -18,92 +18,93 @@ import (
// Base64 encoded, gzipped, json marshaled Swagger object // Base64 encoded, gzipped, json marshaled Swagger object
var swaggerSpec = []string{ var swaggerSpec = []string{
"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",
"3by5+Z8AAAD//+uKmtshVAAA", "Jp09c8M2Q9P7UqZN39QVcH6pQTHQo2DUbrQ2uTTpzXfpCNCjk+P+sF/YNpNlWQt/g8Oa9OGsaAve158i",
"vh7pd3RyPHIbOZHrmO8P5Eos9u9LmbaJrA7ge37dvL35vwAAAP//hZ9HEDhWAAA=",
} }
// GetSwagger returns the content of the embedded swagger specification file // GetSwagger returns the content of the embedded swagger specification file

View File

@ -225,6 +225,12 @@ type JobSettings struct {
// JobStatus defines model for JobStatus. // JobStatus defines model for JobStatus.
type JobStatus string type JobStatus string
// ManagerConfiguration defines model for ManagerConfiguration.
type ManagerConfiguration struct {
// Whether the Shaman file transfer API is available.
ShamanEnabled bool `json:"shamanEnabled"`
}
// RegisteredWorker defines model for RegisteredWorker. // RegisteredWorker defines model for RegisteredWorker.
type RegisteredWorker struct { type RegisteredWorker struct {
Address string `json:"address"` Address string `json:"address"`