WIP: convert GORM to sqlc, for jobs/tasks #104304
13
CHANGELOG.md
13
CHANGELOG.md
@ -4,14 +4,23 @@ This file contains the history of changes to Flamenco. Only changes that might
|
|||||||
be interesting for users are listed here, such as new features and fixes for
|
be interesting for users are listed here, such as new features and fixes for
|
||||||
bugs in actually-released versions.
|
bugs in actually-released versions.
|
||||||
|
|
||||||
## 3.5 - in development
|
## 3.6 - in development
|
||||||
|
|
||||||
- Add MQTT support. Flamenco Manager can now send internal events to an MQTT broker.
|
## 3.5 - released 2024-04-16
|
||||||
|
|
||||||
|
- Add MQTT support ([docs](https://flamenco.blender.org/usage/manager-configuration/mqtt/)). Flamenco Manager can now send internal events to an MQTT broker.
|
||||||
- Simplify the preview video filename when a complex set of frames rendered ([#104285](https://projects.blender.org/studio/flamenco/issues/104285)). Instead of `video-1, 4, 10.mp4` it is now simply `video-1-10.mp4`.
|
- Simplify the preview video filename when a complex set of frames rendered ([#104285](https://projects.blender.org/studio/flamenco/issues/104285)). Instead of `video-1, 4, 10.mp4` it is now simply `video-1-10.mp4`.
|
||||||
- Make the `blendfile` parameter of a `blender-render` command optional. This makes it possible to pass, for example, a Python file that loads/constructs the blend file, instead of loading one straight from disk.
|
- Make the `blendfile` parameter of a `blender-render` command optional. This makes it possible to pass, for example, a Python file that loads/constructs the blend file, instead of loading one straight from disk.
|
||||||
- Show the farm status in the web frontend. This shows whether the farm is actively working on a job, idle, asleep (all workers are sleeping and no work is queued), waiting (all workers are sleeping, and work is queued), or inoperable (no workers, or all workers are offline). This status is also broadcast as event via the event bus, and thus available via SocketIO and MQTT.
|
- Show the farm status in the web frontend. This shows whether the farm is actively working on a job, idle, asleep (all workers are sleeping and no work is queued), waiting (all workers are sleeping, and work is queued), or inoperable (no workers, or all workers are offline). This status is also broadcast as event via the event bus, and thus available via SocketIO and MQTT.
|
||||||
- Fix an issue where the columns in the web interface wouldn't correctly resize when the shown information changed.
|
- Fix an issue where the columns in the web interface wouldn't correctly resize when the shown information changed.
|
||||||
- Add-on: replace the different 'refresh' buttons (for Manager info & storage location, job types, and worker tags) with a single button that just refreshes everything in one go. The information obtained from Flamenco Manager is now stored in a JSON file on disk, making it independent from Blender auto-saving the user preferences.
|
- Add-on: replace the different 'refresh' buttons (for Manager info & storage location, job types, and worker tags) with a single button that just refreshes everything in one go. The information obtained from Flamenco Manager is now stored in a JSON file on disk, making it independent from Blender auto-saving the user preferences.
|
||||||
|
- Ensure the web frontend connects to the backend correctly when served over HTTPS ([#104296](https://projects.blender.org/studio/flamenco/pulls/104296)).
|
||||||
|
- For Workers running on Linux, it is now possible to configure the "OOM score adjustment" for sub-processes. This makes it possible for the out-of-memory killer to target Blender, and not Flamenco Worker itself.
|
||||||
|
- Security updates of some dependencies:
|
||||||
|
- [Incorrect forwarding of sensitive headers and cookies on HTTP redirect in net/http](https://pkg.go.dev/vuln/GO-2024-2600)
|
||||||
|
- [Memory exhaustion in multipart form parsing in net/textproto and net/http](https://pkg.go.dev/vuln/GO-2024-2599)
|
||||||
|
- [Verify panics on certificates with an unknown public key algorithm in crypto/x509](https://pkg.go.dev/vuln/GO-2024-2600)
|
||||||
|
- [HTTP/2 CONTINUATION flood in net/http](https://pkg.go.dev/vuln/GO-2024-2687)
|
||||||
|
|
||||||
## 3.4 - released 2024-01-12
|
## 3.4 - released 2024-01-12
|
||||||
|
|
||||||
|
2
Makefile
2
Makefile
@ -4,7 +4,7 @@ PKG := projects.blender.org/studio/flamenco
|
|||||||
|
|
||||||
# To update the version number in all the relevant places, update the VERSION
|
# To update the version number in all the relevant places, update the VERSION
|
||||||
# variable below and run `make update-version`.
|
# variable below and run `make update-version`.
|
||||||
VERSION := 3.5-alpha1
|
VERSION := 3.6-alpha0
|
||||||
# "alpha", "beta", or "release".
|
# "alpha", "beta", or "release".
|
||||||
RELEASE_CYCLE := alpha
|
RELEASE_CYCLE := alpha
|
||||||
|
|
||||||
|
@ -5,14 +5,14 @@
|
|||||||
bl_info = {
|
bl_info = {
|
||||||
"name": "Flamenco 3",
|
"name": "Flamenco 3",
|
||||||
"author": "Sybren A. Stüvel",
|
"author": "Sybren A. Stüvel",
|
||||||
"version": (3, 5),
|
"version": (3, 6),
|
||||||
"blender": (3, 1, 0),
|
"blender": (3, 1, 0),
|
||||||
"description": "Flamenco client for Blender.",
|
"description": "Flamenco client for Blender.",
|
||||||
"location": "Output Properties > Flamenco",
|
"location": "Output Properties > Flamenco",
|
||||||
"doc_url": "https://flamenco.blender.org/",
|
"doc_url": "https://flamenco.blender.org/",
|
||||||
"category": "System",
|
"category": "System",
|
||||||
"support": "COMMUNITY",
|
"support": "COMMUNITY",
|
||||||
"warning": "This is version 3.5-alpha1 of the add-on, which is not a stable release",
|
"warning": "This is version 3.6-alpha0 of the add-on, which is not a stable release",
|
||||||
}
|
}
|
||||||
|
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
@ -286,16 +286,16 @@ class Transferrer(submodules.transfer.FileTransferer): # type: ignore
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
self.log.debug(" %s: %s", file_spec.status, file_spec.path)
|
self.log.debug(" %s: %s", file_spec.status, file_spec.path)
|
||||||
match file_spec.status.value:
|
status = file_spec.status.value
|
||||||
case "unknown":
|
if status == "unknown":
|
||||||
to_upload.appendleft(file_spec)
|
to_upload.appendleft(file_spec)
|
||||||
case "uploading":
|
elif status == "uploading":
|
||||||
to_upload.append(file_spec)
|
to_upload.append(file_spec)
|
||||||
case _:
|
else:
|
||||||
msg = "Unknown status in response from Shaman: %r" % file_spec
|
msg = "Unknown status in response from Shaman: %r" % file_spec
|
||||||
self.log.error(msg)
|
self.log.error(msg)
|
||||||
self.error_set(msg)
|
self.error_set(msg)
|
||||||
return None
|
return None
|
||||||
return to_upload
|
return to_upload
|
||||||
|
|
||||||
def _upload_files(
|
def _upload_files(
|
||||||
@ -375,25 +375,26 @@ class Transferrer(submodules.transfer.FileTransferer): # type: ignore
|
|||||||
x_shaman_original_filename=file_spec.path,
|
x_shaman_original_filename=file_spec.path,
|
||||||
)
|
)
|
||||||
except ApiException as ex:
|
except ApiException as ex:
|
||||||
match ex.status:
|
if ex.status == 425:
|
||||||
case 425: # Too Early, i.e. defer uploading this file.
|
# Too Early, i.e. defer uploading this file.
|
||||||
self.log.info(
|
self.log.info(
|
||||||
" %s: someone else is uploading this file, deferring",
|
" %s: someone else is uploading this file, deferring",
|
||||||
file_spec.path,
|
file_spec.path,
|
||||||
)
|
)
|
||||||
defer(file_spec)
|
defer(file_spec)
|
||||||
continue
|
continue
|
||||||
case 417: # Expectation Failed; mismatch of checksum or file size.
|
elif ex.status == 417:
|
||||||
msg = "Error from Shaman uploading %s, code %d: %s" % (
|
# Expectation Failed; mismatch of checksum or file size.
|
||||||
file_spec.path,
|
msg = "Error from Shaman uploading %s, code %d: %s" % (
|
||||||
ex.status,
|
file_spec.path,
|
||||||
ex.body,
|
ex.status,
|
||||||
)
|
ex.body,
|
||||||
case _: # Unknown error
|
)
|
||||||
msg = "API exception\nHeaders: %s\nBody: %s\n" % (
|
else: # Unknown error
|
||||||
ex.headers,
|
msg = "API exception\nHeaders: %s\nBody: %s\n" % (
|
||||||
ex.body,
|
ex.headers,
|
||||||
)
|
ex.body,
|
||||||
|
)
|
||||||
|
|
||||||
self.log.error(msg)
|
self.log.error(msg)
|
||||||
self.error_set(msg)
|
self.error_set(msg)
|
||||||
@ -453,19 +454,15 @@ class Transferrer(submodules.transfer.FileTransferer): # type: ignore
|
|||||||
checkoutRequest
|
checkoutRequest
|
||||||
)
|
)
|
||||||
except ApiException as ex:
|
except ApiException as ex:
|
||||||
match ex.status:
|
if ex.status == 424: # Files were missing
|
||||||
case 424: # Files were missing
|
msg = "We did not upload some files, checkout aborted"
|
||||||
msg = "We did not upload some files, checkout aborted"
|
elif ex.status == 409: # Checkout already exists
|
||||||
case 409: # Checkout already exists
|
msg = "There is already an existing checkout at %s" % self.checkout_path
|
||||||
msg = (
|
else: # Unknown error
|
||||||
"There is already an existing checkout at %s"
|
msg = "API exception\nHeaders: %s\nBody: %s\n" % (
|
||||||
% self.checkout_path
|
ex.headers,
|
||||||
)
|
ex.body,
|
||||||
case _: # Unknown error
|
)
|
||||||
msg = "API exception\nHeaders: %s\nBody: %s\n" % (
|
|
||||||
ex.headers,
|
|
||||||
ex.body,
|
|
||||||
)
|
|
||||||
self.log.error(msg)
|
self.log.error(msg)
|
||||||
self.error_set(msg)
|
self.error_set(msg)
|
||||||
return None
|
return None
|
||||||
|
@ -116,7 +116,8 @@ def _store_available_job_types(available_job_types: _AvailableJobTypes) -> None:
|
|||||||
else:
|
else:
|
||||||
# Convert from API response type to list suitable for an EnumProperty.
|
# Convert from API response type to list suitable for an EnumProperty.
|
||||||
_job_type_enum_items = [
|
_job_type_enum_items = [
|
||||||
(job_type.name, job_type.label, "") for job_type in job_types
|
(job_type.name, job_type.label, getattr(job_type, "description", ""))
|
||||||
|
for job_type in job_types
|
||||||
]
|
]
|
||||||
_job_type_enum_items.insert(0, _JOB_TYPE_NOT_SELECTED_ENUM_ITEM)
|
_job_type_enum_items.insert(0, _JOB_TYPE_NOT_SELECTED_ENUM_ITEM)
|
||||||
|
|
||||||
|
2
addon/flamenco/manager/__init__.py
generated
2
addon/flamenco/manager/__init__.py
generated
@ -10,7 +10,7 @@
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
__version__ = "3.5-alpha1"
|
__version__ = "3.6-alpha0"
|
||||||
|
|
||||||
# import ApiClient
|
# import ApiClient
|
||||||
from flamenco.manager.api_client import ApiClient
|
from flamenco.manager.api_client import ApiClient
|
||||||
|
2
addon/flamenco/manager/api_client.py
generated
2
addon/flamenco/manager/api_client.py
generated
@ -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/3.5-alpha1 (Blender add-on)'
|
self.user_agent = 'Flamenco/3.6-alpha0 (Blender add-on)'
|
||||||
|
|
||||||
def __enter__(self):
|
def __enter__(self):
|
||||||
return self
|
return self
|
||||||
|
2
addon/flamenco/manager/configuration.py
generated
2
addon/flamenco/manager/configuration.py
generated
@ -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: 3.5-alpha1".\
|
"SDK Package Version: 3.6-alpha0".\
|
||||||
format(env=sys.platform, pyversion=sys.version)
|
format(env=sys.platform, pyversion=sys.version)
|
||||||
|
|
||||||
def get_host_settings(self):
|
def get_host_settings(self):
|
||||||
|
1
addon/flamenco/manager/docs/AvailableJobType.md
generated
1
addon/flamenco/manager/docs/AvailableJobType.md
generated
@ -9,6 +9,7 @@ Name | Type | Description | Notes
|
|||||||
**label** | **str** | |
|
**label** | **str** | |
|
||||||
**settings** | [**[AvailableJobSetting]**](AvailableJobSetting.md) | |
|
**settings** | [**[AvailableJobSetting]**](AvailableJobSetting.md) | |
|
||||||
**etag** | **str** | Hash of the job type. If the job settings or the label change, this etag will change. This is used on job submission to ensure that the submitted job settings are up to date. |
|
**etag** | **str** | Hash of the job type. If the job settings or the label change, this etag will change. This is used on job submission to ensure that the submitted job settings are up to date. |
|
||||||
|
**description** | **str** | The description/tooltip shown in the user interface. | [optional]
|
||||||
**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]
|
**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)
|
[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)
|
||||||
|
@ -91,6 +91,7 @@ class AvailableJobType(ModelNormal):
|
|||||||
'label': (str,), # noqa: E501
|
'label': (str,), # noqa: E501
|
||||||
'settings': ([AvailableJobSetting],), # noqa: E501
|
'settings': ([AvailableJobSetting],), # noqa: E501
|
||||||
'etag': (str,), # noqa: E501
|
'etag': (str,), # noqa: E501
|
||||||
|
'description': (str,), # noqa: E501
|
||||||
}
|
}
|
||||||
|
|
||||||
@cached_property
|
@cached_property
|
||||||
@ -103,6 +104,7 @@ class AvailableJobType(ModelNormal):
|
|||||||
'label': 'label', # noqa: E501
|
'label': 'label', # noqa: E501
|
||||||
'settings': 'settings', # noqa: E501
|
'settings': 'settings', # noqa: E501
|
||||||
'etag': 'etag', # noqa: E501
|
'etag': 'etag', # noqa: E501
|
||||||
|
'description': 'description', # noqa: E501
|
||||||
}
|
}
|
||||||
|
|
||||||
read_only_vars = {
|
read_only_vars = {
|
||||||
@ -152,6 +154,7 @@ class AvailableJobType(ModelNormal):
|
|||||||
Animal class but this time we won't travel
|
Animal class but this time we won't travel
|
||||||
through its discriminator because we passed in
|
through its discriminator because we passed in
|
||||||
_visited_composed_classes = (Animal,)
|
_visited_composed_classes = (Animal,)
|
||||||
|
description (str): The description/tooltip shown in the user interface.. [optional] # noqa: E501
|
||||||
"""
|
"""
|
||||||
|
|
||||||
_check_type = kwargs.pop('_check_type', True)
|
_check_type = kwargs.pop('_check_type', True)
|
||||||
@ -243,6 +246,7 @@ class AvailableJobType(ModelNormal):
|
|||||||
Animal class but this time we won't travel
|
Animal class but this time we won't travel
|
||||||
through its discriminator because we passed in
|
through its discriminator because we passed in
|
||||||
_visited_composed_classes = (Animal,)
|
_visited_composed_classes = (Animal,)
|
||||||
|
description (str): The description/tooltip shown in the user interface.. [optional] # noqa: E501
|
||||||
"""
|
"""
|
||||||
|
|
||||||
_check_type = kwargs.pop('_check_type', True)
|
_check_type = kwargs.pop('_check_type', True)
|
||||||
|
2
addon/flamenco/manager_README.md
generated
2
addon/flamenco/manager_README.md
generated
@ -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: 3.5-alpha1
|
- Package version: 3.6-alpha0
|
||||||
- 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/)
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@ import dataclasses
|
|||||||
import json
|
import json
|
||||||
import platform
|
import platform
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import TYPE_CHECKING, Optional
|
from typing import TYPE_CHECKING, Optional, Union
|
||||||
|
|
||||||
from urllib3.exceptions import HTTPError, MaxRetryError
|
from urllib3.exceptions import HTTPError, MaxRetryError
|
||||||
|
|
||||||
@ -133,7 +133,7 @@ def _to_json(info: ManagerInfo) -> str:
|
|||||||
return json.dumps(info, indent=" ", cls=Encoder)
|
return json.dumps(info, indent=" ", cls=Encoder)
|
||||||
|
|
||||||
|
|
||||||
def _from_json(contents: str | bytes) -> ManagerInfo:
|
def _from_json(contents: Union[str, bytes]) -> ManagerInfo:
|
||||||
# Do a late import, so that the API is only imported when actually used.
|
# Do a late import, so that the API is only imported when actually used.
|
||||||
from flamenco.manager.configuration import Configuration
|
from flamenco.manager.configuration import Configuration
|
||||||
from flamenco.manager.model_utils import validate_and_convert_types
|
from flamenco.manager.model_utils import validate_and_convert_types
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
# <pep8 compliant>
|
# <pep8 compliant>
|
||||||
|
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Callable, TypeAlias
|
from typing import Callable
|
||||||
import dataclasses
|
import dataclasses
|
||||||
|
|
||||||
from .bat.submodules import bpathlib
|
from .bat.submodules import bpathlib
|
||||||
@ -64,7 +64,7 @@ def _search_path_marker(blendfile: Path, marker_path: str) -> Path:
|
|||||||
return blendfile_dir
|
return blendfile_dir
|
||||||
|
|
||||||
|
|
||||||
Finder: TypeAlias = Callable[[Path], Path]
|
Finder = Callable[[Path], Path]
|
||||||
|
|
||||||
|
|
||||||
@dataclasses.dataclass
|
@dataclasses.dataclass
|
||||||
|
@ -23,6 +23,7 @@ import (
|
|||||||
"projects.blender.org/studio/flamenco/internal/appinfo"
|
"projects.blender.org/studio/flamenco/internal/appinfo"
|
||||||
"projects.blender.org/studio/flamenco/internal/worker"
|
"projects.blender.org/studio/flamenco/internal/worker"
|
||||||
"projects.blender.org/studio/flamenco/internal/worker/cli_runner"
|
"projects.blender.org/studio/flamenco/internal/worker/cli_runner"
|
||||||
|
"projects.blender.org/studio/flamenco/pkg/oomscore"
|
||||||
"projects.blender.org/studio/flamenco/pkg/sysinfo"
|
"projects.blender.org/studio/flamenco/pkg/sysinfo"
|
||||||
"projects.blender.org/studio/flamenco/pkg/website"
|
"projects.blender.org/studio/flamenco/pkg/website"
|
||||||
)
|
)
|
||||||
@ -114,6 +115,10 @@ func main() {
|
|||||||
findBlender()
|
findBlender()
|
||||||
findFFmpeg()
|
findFFmpeg()
|
||||||
|
|
||||||
|
// Create the CLI runner before the auto-discovery, to make any configuration
|
||||||
|
// problems clear before waiting for the Manager to respond.
|
||||||
|
cliRunner := createCLIRunner(&configWrangler)
|
||||||
|
|
||||||
// Give the auto-discovery some time to find a Manager.
|
// Give the auto-discovery some time to find a Manager.
|
||||||
discoverTimeout := 10 * time.Minute
|
discoverTimeout := 10 * time.Minute
|
||||||
discoverCtx, discoverCancel := context.WithTimeout(context.Background(), discoverTimeout)
|
discoverCtx, discoverCancel := context.WithTimeout(context.Background(), discoverTimeout)
|
||||||
@ -149,7 +154,6 @@ func main() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
cliRunner := cli_runner.NewCLIRunner()
|
|
||||||
listener = worker.NewListener(client, buffer)
|
listener = worker.NewListener(client, buffer)
|
||||||
cmdRunner := worker.NewCommandExecutor(cliRunner, listener, timeService)
|
cmdRunner := worker.NewCommandExecutor(cliRunner, listener, timeService)
|
||||||
taskRunner := worker.NewTaskExecutor(cmdRunner, listener)
|
taskRunner := worker.NewTaskExecutor(cmdRunner, listener)
|
||||||
@ -304,3 +308,27 @@ func logFatalManagerDiscoveryError(err error, discoverTimeout time.Duration) {
|
|||||||
Msgf("auto-discovery error, see %s", website.CannotFindManagerHelpURL)
|
Msgf("auto-discovery error, see %s", website.CannotFindManagerHelpURL)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func createCLIRunner(configWrangler *worker.FileConfigWrangler) *cli_runner.CLIRunner {
|
||||||
|
config, err := configWrangler.WorkerConfig()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal().Err(err).Msg("error loading worker configuration")
|
||||||
|
}
|
||||||
|
|
||||||
|
if config.LinuxOOMScoreAdjust == nil {
|
||||||
|
log.Debug().Msg("executables will be run without OOM score adjustment")
|
||||||
|
return cli_runner.NewCLIRunner()
|
||||||
|
}
|
||||||
|
|
||||||
|
if !oomscore.Available() {
|
||||||
|
log.Warn().
|
||||||
|
Msgf("config: oom_score_adjust configured, but that is only available on Linux, not this platform. See %s for more information.",
|
||||||
|
website.OOMScoreAdjURL)
|
||||||
|
return cli_runner.NewCLIRunner()
|
||||||
|
}
|
||||||
|
|
||||||
|
adjustment := *config.LinuxOOMScoreAdjust
|
||||||
|
log.Info().Int("oom_score_adjust", adjustment).Msg("executables will be run with OOM score adjustment")
|
||||||
|
|
||||||
|
return cli_runner.NewCLIRunnerWithOOMScoreAdjuster(adjustment)
|
||||||
|
}
|
||||||
|
8
go.mod
8
go.mod
@ -1,6 +1,6 @@
|
|||||||
module projects.blender.org/studio/flamenco
|
module projects.blender.org/studio/flamenco
|
||||||
|
|
||||||
go 1.22
|
go 1.22.2
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/adrg/xdg v0.4.0
|
github.com/adrg/xdg v0.4.0
|
||||||
@ -28,10 +28,10 @@ require (
|
|||||||
github.com/stretchr/testify v1.8.4
|
github.com/stretchr/testify v1.8.4
|
||||||
github.com/zcalusic/sysinfo v1.0.1
|
github.com/zcalusic/sysinfo v1.0.1
|
||||||
github.com/ziflex/lecho/v3 v3.1.0
|
github.com/ziflex/lecho/v3 v3.1.0
|
||||||
golang.org/x/crypto v0.16.0
|
golang.org/x/crypto v0.21.0
|
||||||
golang.org/x/image v0.10.0
|
golang.org/x/image v0.10.0
|
||||||
golang.org/x/net v0.19.0
|
golang.org/x/net v0.23.0
|
||||||
golang.org/x/sys v0.15.0
|
golang.org/x/sys v0.18.0
|
||||||
gopkg.in/yaml.v2 v2.4.0
|
gopkg.in/yaml.v2 v2.4.0
|
||||||
gorm.io/gorm v1.25.5
|
gorm.io/gorm v1.25.5
|
||||||
modernc.org/sqlite v1.28.0
|
modernc.org/sqlite v1.28.0
|
||||||
|
6
go.sum
6
go.sum
@ -201,6 +201,8 @@ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5y
|
|||||||
golang.org/x/crypto v0.0.0-20211215165025-cf75a172585e/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
|
golang.org/x/crypto v0.0.0-20211215165025-cf75a172585e/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
|
||||||
golang.org/x/crypto v0.16.0 h1:mMMrFzRSCF0GvB7Ne27XVtVAaXLrPmgPC7/v0tkwHaY=
|
golang.org/x/crypto v0.16.0 h1:mMMrFzRSCF0GvB7Ne27XVtVAaXLrPmgPC7/v0tkwHaY=
|
||||||
golang.org/x/crypto v0.16.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
|
golang.org/x/crypto v0.16.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
|
||||||
|
golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA=
|
||||||
|
golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
|
||||||
golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||||
golang.org/x/image v0.10.0 h1:gXjUUtwtx5yOE0VKWq1CH4IJAClq4UGgUA3i+rpON9M=
|
golang.org/x/image v0.10.0 h1:gXjUUtwtx5yOE0VKWq1CH4IJAClq4UGgUA3i+rpON9M=
|
||||||
golang.org/x/image v0.10.0/go.mod h1:jtrku+n79PfroUbvDdeUWMAI+heR786BofxrbiSF+J0=
|
golang.org/x/image v0.10.0/go.mod h1:jtrku+n79PfroUbvDdeUWMAI+heR786BofxrbiSF+J0=
|
||||||
@ -223,6 +225,8 @@ golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug
|
|||||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||||
golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c=
|
golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c=
|
||||||
golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U=
|
golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U=
|
||||||
|
golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs=
|
||||||
|
golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
@ -262,6 +266,8 @@ golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|||||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
|
golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
|
||||||
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
|
golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=
|
||||||
|
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||||
|
@ -442,6 +442,32 @@ func TestGetJobTypeHappy(t *testing.T) {
|
|||||||
assertResponseJSON(t, echoCtx, http.StatusOK, jt)
|
assertResponseJSON(t, echoCtx, http.StatusOK, jt)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestGetJobTypeWithDescriptionHappy(t *testing.T) {
|
||||||
|
mockCtrl := gomock.NewController(t)
|
||||||
|
defer mockCtrl.Finish()
|
||||||
|
mf := newMockedFlamenco(mockCtrl)
|
||||||
|
|
||||||
|
// Get an existing job type with a description.
|
||||||
|
description := "This is a test job type"
|
||||||
|
jt := api.AvailableJobType{
|
||||||
|
Description: &description,
|
||||||
|
Etag: "some etag",
|
||||||
|
Name: "test-job-type",
|
||||||
|
Label: "Test Job Type",
|
||||||
|
Settings: []api.AvailableJobSetting{
|
||||||
|
{Key: "setting", Type: api.AvailableJobSettingTypeString},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
mf.jobCompiler.EXPECT().GetJobType("test-job-type").
|
||||||
|
Return(jt, nil)
|
||||||
|
|
||||||
|
echoCtx := mf.prepareMockedRequest(nil)
|
||||||
|
err := mf.flamenco.GetJobType(echoCtx, "test-job-type")
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
assertResponseJSON(t, echoCtx, http.StatusOK, jt)
|
||||||
|
}
|
||||||
|
|
||||||
func TestGetJobTypeUnknown(t *testing.T) {
|
func TestGetJobTypeUnknown(t *testing.T) {
|
||||||
mockCtrl := gomock.NewController(t)
|
mockCtrl := gomock.NewController(t)
|
||||||
defer mockCtrl.Finish()
|
defer mockCtrl.Finish()
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
const JOB_TYPE = {
|
const JOB_TYPE = {
|
||||||
label: "Simple Blender Render",
|
label: "Simple Blender Render",
|
||||||
|
description: "Render a sequence of frames, and create a preview video file",
|
||||||
settings: [
|
settings: [
|
||||||
// Settings for artists to determine:
|
// Settings for artists to determine:
|
||||||
{ key: "frames", type: "string", required: true, eval: "f'{C.scene.frame_start}-{C.scene.frame_end}'",
|
{ key: "frames", type: "string", required: true, eval: "f'{C.scene.frame_start}-{C.scene.frame_end}'",
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
const JOB_TYPE = {
|
const JOB_TYPE = {
|
||||||
label: "Simple Blender Render",
|
label: "Simple Blender Render",
|
||||||
|
description: "Render a sequence of frames, and create a preview video file",
|
||||||
settings: [
|
settings: [
|
||||||
// Settings for artists to determine:
|
// Settings for artists to determine:
|
||||||
{ key: "frames", type: "string", required: true,
|
{ key: "frames", type: "string", required: true,
|
||||||
|
@ -62,6 +62,15 @@ func (db *DB) SaveWorkerTag(ctx context.Context, tag *WorkerTag) error {
|
|||||||
|
|
||||||
// DeleteWorkerTag deletes the given tag, after unassigning all workers from it.
|
// DeleteWorkerTag deletes the given tag, after unassigning all workers from it.
|
||||||
func (db *DB) DeleteWorkerTag(ctx context.Context, uuid string) error {
|
func (db *DB) DeleteWorkerTag(ctx context.Context, uuid string) error {
|
||||||
|
// As a safety measure, refuse to delete unless foreign key constraints are active.
|
||||||
|
fkEnabled, err := db.areForeignKeysEnabled()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("checking whether foreign keys are enabled: %w", err)
|
||||||
|
}
|
||||||
|
if !fkEnabled {
|
||||||
|
return ErrDeletingWithoutFK
|
||||||
|
}
|
||||||
|
|
||||||
tx := db.gormDB.WithContext(ctx).
|
tx := db.gormDB.WithContext(ctx).
|
||||||
Where("uuid = ?", uuid).
|
Where("uuid = ?", uuid).
|
||||||
Delete(&WorkerTag{})
|
Delete(&WorkerTag{})
|
||||||
|
@ -3,6 +3,7 @@ package persistence
|
|||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"slices"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@ -50,17 +51,7 @@ func TestFetchDeleteTags(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
require.NoError(t, f.db.CreateWorkerTag(f.ctx, &secondTag))
|
require.NoError(t, f.db.CreateWorkerTag(f.ctx, &secondTag))
|
||||||
|
assertTagsMatch(t, f, f.tag.UUID, secondTag.UUID)
|
||||||
allTags, err := f.db.FetchWorkerTags(f.ctx)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
require.Len(t, allTags, 2)
|
|
||||||
var allTagIDs [2]string
|
|
||||||
for idx := range allTags {
|
|
||||||
allTagIDs[idx] = allTags[idx].UUID
|
|
||||||
}
|
|
||||||
assert.Contains(t, allTagIDs, f.tag.UUID)
|
|
||||||
assert.Contains(t, allTagIDs, secondTag.UUID)
|
|
||||||
|
|
||||||
has, err = f.db.HasWorkerTags(f.ctx)
|
has, err = f.db.HasWorkerTags(f.ctx)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
@ -68,11 +59,7 @@ func TestFetchDeleteTags(t *testing.T) {
|
|||||||
|
|
||||||
// Test deleting the 2nd tag.
|
// Test deleting the 2nd tag.
|
||||||
require.NoError(t, f.db.DeleteWorkerTag(f.ctx, secondTag.UUID))
|
require.NoError(t, f.db.DeleteWorkerTag(f.ctx, secondTag.UUID))
|
||||||
|
assertTagsMatch(t, f, f.tag.UUID)
|
||||||
allTags, err = f.db.FetchWorkerTags(f.ctx)
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.Len(t, allTags, 1)
|
|
||||||
assert.Equal(t, f.tag.UUID, allTags[0].UUID)
|
|
||||||
|
|
||||||
// Test deleting the 1st tag.
|
// Test deleting the 1st tag.
|
||||||
require.NoError(t, f.db.DeleteWorkerTag(f.ctx, f.tag.UUID))
|
require.NoError(t, f.db.DeleteWorkerTag(f.ctx, f.tag.UUID))
|
||||||
@ -81,6 +68,31 @@ func TestFetchDeleteTags(t *testing.T) {
|
|||||||
assert.False(t, has, "expecting HasWorkerTags to return false")
|
assert.False(t, has, "expecting HasWorkerTags to return false")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestDeleteTagsWithoutFK(t *testing.T) {
|
||||||
|
f := workerTestFixtures(t, 1*time.Second)
|
||||||
|
defer f.done()
|
||||||
|
|
||||||
|
// Single tag was created by fixture.
|
||||||
|
has, err := f.db.HasWorkerTags(f.ctx)
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.True(t, has, "expecting HasWorkerTags to return true")
|
||||||
|
|
||||||
|
secondTag := WorkerTag{
|
||||||
|
UUID: uuid.New(),
|
||||||
|
Name: "arbeiderskaartje",
|
||||||
|
Description: "Worker tag in Dutch",
|
||||||
|
}
|
||||||
|
require.NoError(t, f.db.CreateWorkerTag(f.ctx, &secondTag))
|
||||||
|
|
||||||
|
// Try deleting with foreign key constraints disabled.
|
||||||
|
require.NoError(t, f.db.pragmaForeignKeys(false))
|
||||||
|
err = f.db.DeleteWorkerTag(f.ctx, f.tag.UUID)
|
||||||
|
require.ErrorIs(t, err, ErrDeletingWithoutFK)
|
||||||
|
|
||||||
|
// Test the deletion did not happen.
|
||||||
|
assertTagsMatch(t, f, f.tag.UUID, secondTag.UUID)
|
||||||
|
}
|
||||||
|
|
||||||
func TestAssignUnassignWorkerTags(t *testing.T) {
|
func TestAssignUnassignWorkerTags(t *testing.T) {
|
||||||
f := workerTestFixtures(t, 1*time.Second)
|
f := workerTestFixtures(t, 1*time.Second)
|
||||||
defer f.done()
|
defer f.done()
|
||||||
@ -163,3 +175,19 @@ func TestDeleteWorkerTagWithWorkersAssigned(t *testing.T) {
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.Empty(t, w.Tags)
|
assert.Empty(t, w.Tags)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func assertTagsMatch(t *testing.T, f WorkerTestFixture, expectUUIDs ...string) {
|
||||||
|
allTags, err := f.db.FetchWorkerTags(f.ctx)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
require.Len(t, allTags, len(expectUUIDs))
|
||||||
|
var actualUUIDs []string
|
||||||
|
for idx := range allTags {
|
||||||
|
actualUUIDs = append(actualUUIDs, allTags[idx].UUID)
|
||||||
|
}
|
||||||
|
|
||||||
|
slices.Sort(expectUUIDs)
|
||||||
|
slices.Sort(actualUUIDs)
|
||||||
|
|
||||||
|
assert.Equal(t, actualUUIDs, expectUUIDs)
|
||||||
|
}
|
||||||
|
@ -101,6 +101,15 @@ func (db *DB) FetchWorker(ctx context.Context, uuid string) (*Worker, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (db *DB) DeleteWorker(ctx context.Context, uuid string) error {
|
func (db *DB) DeleteWorker(ctx context.Context, uuid string) error {
|
||||||
|
// As a safety measure, refuse to delete unless foreign key constraints are active.
|
||||||
|
fkEnabled, err := db.areForeignKeysEnabled()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("checking whether foreign keys are enabled: %w", err)
|
||||||
|
}
|
||||||
|
if !fkEnabled {
|
||||||
|
return ErrDeletingWithoutFK
|
||||||
|
}
|
||||||
|
|
||||||
tx := db.gormDB.WithContext(ctx).
|
tx := db.gormDB.WithContext(ctx).
|
||||||
Where("uuid = ?", uuid).
|
Where("uuid = ?", uuid).
|
||||||
Delete(&Worker{})
|
Delete(&Worker{})
|
||||||
|
@ -314,6 +314,30 @@ func TestDeleteWorker(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestDeleteWorkerNoForeignKeys(t *testing.T) {
|
||||||
|
ctx, cancel, db := persistenceTestFixtures(t, 1*time.Second)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
// Create a Worker to delete.
|
||||||
|
w1 := Worker{
|
||||||
|
UUID: "fd97a35b-a5bd-44b4-ac2b-64c193ca877d",
|
||||||
|
Name: "Worker 1",
|
||||||
|
Status: api.WorkerStatusAwake,
|
||||||
|
}
|
||||||
|
require.NoError(t, db.CreateWorker(ctx, &w1))
|
||||||
|
|
||||||
|
// Try deleting with foreign key constraints disabled.
|
||||||
|
require.NoError(t, db.pragmaForeignKeys(false))
|
||||||
|
require.ErrorIs(t, ErrDeletingWithoutFK, db.DeleteWorker(ctx, w1.UUID))
|
||||||
|
|
||||||
|
// The worker should still exist.
|
||||||
|
{
|
||||||
|
fetchedWorker, err := db.FetchWorker(ctx, w1.UUID)
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Equal(t, w1.UUID, fetchedWorker.UUID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestDeleteWorkerWithTagAssigned(t *testing.T) {
|
func TestDeleteWorkerWithTagAssigned(t *testing.T) {
|
||||||
f := workerTestFixtures(t, 1*time.Second)
|
f := workerTestFixtures(t, 1*time.Second)
|
||||||
defer f.done()
|
defer f.done()
|
||||||
|
@ -11,6 +11,7 @@ import (
|
|||||||
|
|
||||||
"github.com/alessio/shellescape"
|
"github.com/alessio/shellescape"
|
||||||
"github.com/rs/zerolog"
|
"github.com/rs/zerolog"
|
||||||
|
"projects.blender.org/studio/flamenco/pkg/oomscore"
|
||||||
)
|
)
|
||||||
|
|
||||||
// The buffer size used to read stdout/stderr output from subprocesses, in
|
// The buffer size used to read stdout/stderr output from subprocesses, in
|
||||||
@ -20,11 +21,19 @@ const StdoutBufferSize = 40 * 1024
|
|||||||
|
|
||||||
// CLIRunner is a wrapper around exec.CommandContext() to allow mocking.
|
// CLIRunner is a wrapper around exec.CommandContext() to allow mocking.
|
||||||
type CLIRunner struct {
|
type CLIRunner struct {
|
||||||
|
oomScoreAdjust int
|
||||||
|
useOOMScoreAdjust bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewCLIRunner() *CLIRunner {
|
func NewCLIRunner() *CLIRunner {
|
||||||
return &CLIRunner{}
|
return &CLIRunner{}
|
||||||
}
|
}
|
||||||
|
func NewCLIRunnerWithOOMScoreAdjuster(oomScoreAdjust int) *CLIRunner {
|
||||||
|
return &CLIRunner{
|
||||||
|
oomScoreAdjust: oomScoreAdjust,
|
||||||
|
useOOMScoreAdjust: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (cli *CLIRunner) CommandContext(ctx context.Context, name string, arg ...string) *exec.Cmd {
|
func (cli *CLIRunner) CommandContext(ctx context.Context, name string, arg ...string) *exec.Cmd {
|
||||||
return exec.CommandContext(ctx, name, arg...)
|
return exec.CommandContext(ctx, name, arg...)
|
||||||
@ -55,7 +64,7 @@ func (cli *CLIRunner) RunWithTextOutput(
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := execCmd.Start(); err != nil {
|
if err := cli.startWithOOMAdjust(execCmd); err != nil {
|
||||||
logger.Error().Err(err).Msg("error starting CLI execution")
|
logger.Error().Err(err).Msg("error starting CLI execution")
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -171,3 +180,13 @@ func (cli *CLIRunner) logCmd(
|
|||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// startWithOOMAdjust runs the command with its OOM score adjusted.
|
||||||
|
func (cli *CLIRunner) startWithOOMAdjust(execCmd *exec.Cmd) error {
|
||||||
|
if cli.useOOMScoreAdjust {
|
||||||
|
oomScoreRestore := oomscore.Adjust(cli.oomScoreAdjust)
|
||||||
|
defer oomScoreRestore()
|
||||||
|
}
|
||||||
|
|
||||||
|
return execCmd.Start()
|
||||||
|
}
|
||||||
|
@ -58,6 +58,18 @@ type WorkerConfig struct {
|
|||||||
|
|
||||||
TaskTypes []string `yaml:"task_types"`
|
TaskTypes []string `yaml:"task_types"`
|
||||||
RestartExitCode int `yaml:"restart_exit_code"`
|
RestartExitCode int `yaml:"restart_exit_code"`
|
||||||
|
|
||||||
|
// LinuxOOMScoreAdjust controls the Linux out-of-memory killer. Is used when
|
||||||
|
// spawning a sub-process, to adjust the likelyness that that subprocess is
|
||||||
|
// killed rather than Flamenco Worker itself. That way Flamenco Worker can
|
||||||
|
// report the failure to the Manager.
|
||||||
|
//
|
||||||
|
// If the Worker itself would be OOM-killed, it would just be restarted and
|
||||||
|
// get the task it was already working on, causing an infinite OOM-loop.
|
||||||
|
//
|
||||||
|
// If this value is not specified in the configuration file, Flamenco Worker
|
||||||
|
// will not attempt to adjust its OOM score.
|
||||||
|
LinuxOOMScoreAdjust *int `yaml:"oom_score_adjust"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type WorkerCredentials struct {
|
type WorkerCredentials struct {
|
||||||
|
@ -52,8 +52,8 @@ func (ou *OutputUploader) OutputProduced(taskID, filename string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (ou *OutputUploader) Run(ctx context.Context) {
|
func (ou *OutputUploader) Run(ctx context.Context) {
|
||||||
log.Info().Msg("output uploader: running")
|
log.Debug().Msg("output uploader: running")
|
||||||
defer log.Info().Msg("output uploader: shutting down")
|
defer log.Debug().Msg("output uploader: shutting down")
|
||||||
|
|
||||||
wg := sync.WaitGroup{}
|
wg := sync.WaitGroup{}
|
||||||
wg.Add(1)
|
wg.Add(1)
|
||||||
|
@ -133,7 +133,7 @@ func (ub *UpstreamBufferDB) Close() error {
|
|||||||
ub.wg.Wait()
|
ub.wg.Wait()
|
||||||
|
|
||||||
// Attempt one final flush, if it's fast enough:
|
// Attempt one final flush, if it's fast enough:
|
||||||
log.Info().Msg("upstream buffer shutting down, doing one final flush")
|
log.Debug().Msg("upstream buffer shutting down, doing one final flush")
|
||||||
flushCtx, ctxCancel := context.WithTimeout(context.Background(), flushOnShutdownTimeout)
|
flushCtx, ctxCancel := context.WithTimeout(context.Background(), flushOnShutdownTimeout)
|
||||||
defer ctxCancel()
|
defer ctxCancel()
|
||||||
if err := ub.Flush(flushCtx); err != nil {
|
if err := ub.Flush(flushCtx); err != nil {
|
||||||
|
@ -1719,6 +1719,9 @@ components:
|
|||||||
properties:
|
properties:
|
||||||
"name": { type: string }
|
"name": { type: string }
|
||||||
"label": { type: string }
|
"label": { type: string }
|
||||||
|
"description":
|
||||||
|
type: string
|
||||||
|
description: The description/tooltip shown in the user interface.
|
||||||
"settings":
|
"settings":
|
||||||
type: array
|
type: array
|
||||||
items: { $ref: "#/components/schemas/AvailableJobSetting" }
|
items: { $ref: "#/components/schemas/AvailableJobSetting" }
|
||||||
|
404
pkg/api/openapi_spec.gen.go
generated
404
pkg/api/openapi_spec.gen.go
generated
@ -44,208 +44,208 @@ var swaggerSpec = []string{
|
|||||||
"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",
|
||||||
"AQA=",
|
"Qwu3RhEBAA==",
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetSwagger returns the content of the embedded swagger specification file
|
// GetSwagger returns the content of the embedded swagger specification file
|
||||||
|
3
pkg/api/openapi_types.gen.go
generated
3
pkg/api/openapi_types.gen.go
generated
@ -242,6 +242,9 @@ type AvailableJobSettingVisibility string
|
|||||||
|
|
||||||
// Job type supported by this Manager, and its parameters.
|
// Job type supported by this Manager, and its parameters.
|
||||||
type AvailableJobType struct {
|
type AvailableJobType struct {
|
||||||
|
// The description/tooltip shown in the user interface.
|
||||||
|
Description *string `json:"description,omitempty"`
|
||||||
|
|
||||||
// Hash of the job type. If the job settings or the label change, this etag will change. This is used on job submission to ensure that the submitted job settings are up to date.
|
// Hash of the job type. If the job settings or the label change, this etag will change. This is used on job submission to ensure that the submitted job settings are up to date.
|
||||||
Etag string `json:"etag"`
|
Etag string `json:"etag"`
|
||||||
Label string `json:"label"`
|
Label string `json:"label"`
|
||||||
|
86
pkg/oomscore/oomscore.go
Normal file
86
pkg/oomscore/oomscore.go
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
// package oomscore provides some functions to adjust the Linux
|
||||||
|
// out-of-memory (OOM) score, i.e. the number that determines how likely it is
|
||||||
|
// that a process is killed in an out-of-memory situation.
|
||||||
|
//
|
||||||
|
// It is available only on Linux. On other platforms ErrNotImplemented will be returned.
|
||||||
|
package oomscore
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
|
||||||
|
"github.com/rs/zerolog/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
var ErrNotImplemented = errors.New("OOM score functionality not implemented on this platform")
|
||||||
|
|
||||||
|
// Available returns whether the functionality in this package is available for
|
||||||
|
// the current platform.
|
||||||
|
func Available() bool {
|
||||||
|
return available
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetOOMScore returns the current process' OOM score.
|
||||||
|
func GetOOMScore() (int, error) {
|
||||||
|
return getOOMScore()
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetOOMScoreAdj returns the current process' OOM score adjustment.
|
||||||
|
func GetOOMScoreAdj() (int, error) {
|
||||||
|
return getOOMScoreAdj()
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetOOMScoreAdj sets the current process' OOM score adjustment.
|
||||||
|
func SetOOMScoreAdj(score int) error {
|
||||||
|
return setOOMScoreAdj(score)
|
||||||
|
}
|
||||||
|
|
||||||
|
type ScoreRestoreFunc func()
|
||||||
|
|
||||||
|
var emptyRestoreFunc ScoreRestoreFunc = func() {}
|
||||||
|
|
||||||
|
// Adjust temporarily sets the OOM score adjustment.
|
||||||
|
// The returned function MUST be called to restore the original value.
|
||||||
|
// Any problems changing the score are logged, but not otherwise returned.
|
||||||
|
func Adjust(score int) (restoreFunc ScoreRestoreFunc) {
|
||||||
|
restoreFunc = emptyRestoreFunc
|
||||||
|
|
||||||
|
if !Available() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
origScore, err := getOOMScoreAdj()
|
||||||
|
if err != nil {
|
||||||
|
log.Error().
|
||||||
|
AnErr("cause", err).
|
||||||
|
Msg("could not get the current process' oom_score_adj value")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Trace().
|
||||||
|
Int("oom_score_adj", score).
|
||||||
|
Msg("setting oom_score_adj")
|
||||||
|
|
||||||
|
err = setOOMScoreAdj(score)
|
||||||
|
if err != nil {
|
||||||
|
log.Error().
|
||||||
|
Int("oom_score_adj", score).
|
||||||
|
AnErr("cause", err).
|
||||||
|
Msg("could not set the current process' oom_score_adj value")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return func() {
|
||||||
|
log.Trace().
|
||||||
|
Int("oom_score_adj", origScore).
|
||||||
|
Msg("restoring oom_score_adj")
|
||||||
|
|
||||||
|
err = setOOMScoreAdj(origScore)
|
||||||
|
if err != nil {
|
||||||
|
log.Error().
|
||||||
|
Int("oom_score_adj", origScore).
|
||||||
|
AnErr("cause", err).
|
||||||
|
Msg("could not restore the current process' oom_score_adj value")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
66
pkg/oomscore/oomscore_linux.go
Normal file
66
pkg/oomscore/oomscore_linux.go
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
//go:build linux
|
||||||
|
|
||||||
|
package oomscore
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
available = true
|
||||||
|
)
|
||||||
|
|
||||||
|
// getOOMScore returns the current process' OOM score.
|
||||||
|
func getOOMScore() (int, error) {
|
||||||
|
return readInt("oom_score")
|
||||||
|
}
|
||||||
|
|
||||||
|
// getOOMScoreAdj returns the current process' OOM score adjustment.
|
||||||
|
func getOOMScoreAdj() (int, error) {
|
||||||
|
return readInt("oom_score_adj")
|
||||||
|
}
|
||||||
|
|
||||||
|
// setOOMScoreAdj sets the current process' OOM score adjustment.
|
||||||
|
func setOOMScoreAdj(newScore int) error {
|
||||||
|
return writeInt(newScore, "oom_score_adj")
|
||||||
|
}
|
||||||
|
|
||||||
|
// readInt reads an integer from /proc/{pid}/{filename}
|
||||||
|
func readInt(filename string) (int, error) {
|
||||||
|
fullPath := procPidPath(filename)
|
||||||
|
|
||||||
|
file, err := os.Open(fullPath)
|
||||||
|
if err != nil {
|
||||||
|
return 0, fmt.Errorf("opening %s: %w", fullPath, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var valueInFile int
|
||||||
|
n, err := fmt.Fscan(file, &valueInFile)
|
||||||
|
if err != nil {
|
||||||
|
return 0, fmt.Errorf("reading %s: %w", fullPath, err)
|
||||||
|
}
|
||||||
|
if n < 1 {
|
||||||
|
return 0, fmt.Errorf("reading %s: did not find a number", fullPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
return valueInFile, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// writeInt writes an integer to /proc/{pid}/{filename}
|
||||||
|
func writeInt(value int, filename string) error {
|
||||||
|
fullPath := procPidPath(filename)
|
||||||
|
contents := fmt.Sprint(value)
|
||||||
|
err := os.WriteFile(fullPath, []byte(contents), os.ModePerm)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("writing %s: %w", fullPath, err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// procPidPath returns "/proc/{pid}/{filename}".
|
||||||
|
func procPidPath(filename string) string {
|
||||||
|
pid := os.Getpid()
|
||||||
|
return filepath.Join("/proc", fmt.Sprint(pid), filename)
|
||||||
|
}
|
19
pkg/oomscore/oomscore_nonlinux.go
Normal file
19
pkg/oomscore/oomscore_nonlinux.go
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
//go:build !linux
|
||||||
|
|
||||||
|
package oomscore
|
||||||
|
|
||||||
|
const (
|
||||||
|
available = false
|
||||||
|
)
|
||||||
|
|
||||||
|
func getOOMScore() (int, error) {
|
||||||
|
return 0, ErrNotImplemented
|
||||||
|
}
|
||||||
|
|
||||||
|
func getOOMScoreAdj() (int, error) {
|
||||||
|
return 0, ErrNotImplemented
|
||||||
|
}
|
||||||
|
|
||||||
|
func setOOMScoreAdj(int) error {
|
||||||
|
return ErrNotImplemented
|
||||||
|
}
|
@ -9,4 +9,5 @@ const (
|
|||||||
BugReportURL = "https://flamenco.blender.org/get-involved"
|
BugReportURL = "https://flamenco.blender.org/get-involved"
|
||||||
ShamanRequirementsURL = "https://flamenco.blender.org/usage/shared-storage/shaman/#requirements"
|
ShamanRequirementsURL = "https://flamenco.blender.org/usage/shared-storage/shaman/#requirements"
|
||||||
WorkerConfigURL = "https://flamenco.blender.org/usage/worker-configuration/"
|
WorkerConfigURL = "https://flamenco.blender.org/usage/worker-configuration/"
|
||||||
|
OOMScoreAdjURL = WorkerConfigURL
|
||||||
)
|
)
|
||||||
|
@ -12,7 +12,7 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite --port 8081 --base /app/",
|
"dev": "vite --port 8081 --base /app/ --mode development",
|
||||||
"build": "vite build",
|
"build": "vite build",
|
||||||
"preview": "vite preview --port 5050",
|
"preview": "vite preview --port 5050",
|
||||||
"lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs --fix --ignore-path .gitignore"
|
"lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs --fix --ignore-path .gitignore"
|
||||||
|
2
web/app/src/manager-api/ApiClient.js
generated
2
web/app/src/manager-api/ApiClient.js
generated
@ -55,7 +55,7 @@ class ApiClient {
|
|||||||
* @default {}
|
* @default {}
|
||||||
*/
|
*/
|
||||||
this.defaultHeaders = {
|
this.defaultHeaders = {
|
||||||
'User-Agent': 'Flamenco/3.5-alpha1 / webbrowser'
|
'User-Agent': 'Flamenco/3.6-alpha0 / webbrowser'
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -63,6 +63,9 @@ class AvailableJobType {
|
|||||||
if (data.hasOwnProperty('label')) {
|
if (data.hasOwnProperty('label')) {
|
||||||
obj['label'] = ApiClient.convertToType(data['label'], 'String');
|
obj['label'] = ApiClient.convertToType(data['label'], 'String');
|
||||||
}
|
}
|
||||||
|
if (data.hasOwnProperty('description')) {
|
||||||
|
obj['description'] = ApiClient.convertToType(data['description'], 'String');
|
||||||
|
}
|
||||||
if (data.hasOwnProperty('settings')) {
|
if (data.hasOwnProperty('settings')) {
|
||||||
obj['settings'] = ApiClient.convertToType(data['settings'], [AvailableJobSetting]);
|
obj['settings'] = ApiClient.convertToType(data['settings'], [AvailableJobSetting]);
|
||||||
}
|
}
|
||||||
@ -86,6 +89,12 @@ AvailableJobType.prototype['name'] = undefined;
|
|||||||
*/
|
*/
|
||||||
AvailableJobType.prototype['label'] = undefined;
|
AvailableJobType.prototype['label'] = undefined;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The description/tooltip shown in the user interface.
|
||||||
|
* @member {String} description
|
||||||
|
*/
|
||||||
|
AvailableJobType.prototype['description'] = undefined;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @member {Array.<module:model/AvailableJobSetting>} settings
|
* @member {Array.<module:model/AvailableJobSetting>} settings
|
||||||
*/
|
*/
|
||||||
|
@ -1,18 +1,15 @@
|
|||||||
let url = new URL(window.location.href);
|
let url = new URL(window.location.href);
|
||||||
// Uncomment this when the web interface is running on a different port than the
|
|
||||||
// API, for example when using the Vite devserver. Set the API port here.
|
// When the web interface is running on a different port than the API, for
|
||||||
if (url.port == '8081') {
|
// example when using the Vite devserver, setting the Vite --mode flag to
|
||||||
|
// "development" will force port 8080 for the API.
|
||||||
|
if (import.meta.env.MODE == 'development') {
|
||||||
url.port = '8080';
|
url.port = '8080';
|
||||||
}
|
}
|
||||||
url.pathname = '/';
|
|
||||||
const flamencoAPIURL = url.href;
|
|
||||||
|
|
||||||
url.protocol = 'ws:';
|
|
||||||
const websocketURL = url.href;
|
|
||||||
|
|
||||||
const URLs = {
|
const URLs = {
|
||||||
api: flamencoAPIURL,
|
api: `${url.protocol}//${url.hostname}:${url.port}/`,
|
||||||
ws: websocketURL,
|
ws: `${url.protocol.replace('http', 'ws')}//${url.hostname}:${url.port}/`,
|
||||||
};
|
};
|
||||||
|
|
||||||
// console.log("Flamenco API:", URLs.api);
|
// console.log("Flamenco API:", URLs.api);
|
||||||
|
@ -74,11 +74,3 @@ Flamenco v3 is in active development at Blender Studio. Join
|
|||||||
[the chat](https://blender.chat/channel/flamenco) to see what's happening!
|
[the chat](https://blender.chat/channel/flamenco) to see what's happening!
|
||||||
|
|
||||||
{{< /columns >}}
|
{{< /columns >}}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
-------------------
|
|
||||||
|
|
||||||
Looking for the old [Flamenco v2 documentation][F2]?
|
|
||||||
|
|
||||||
[F2]: /v2/
|
|
||||||
|
@ -68,8 +68,8 @@ start afresh with the following steps:
|
|||||||
|
|
||||||
The macOS "Silicon" build does not ship with FFmpeg, because a trusted build for
|
The macOS "Silicon" build does not ship with FFmpeg, because a trusted build for
|
||||||
this architecture is not provided by the FFmpeg project. This is why Flamenco v3
|
this architecture is not provided by the FFmpeg project. This is why Flamenco v3
|
||||||
did not ship macOS/ARM64 builds. As of v3.3 this architecture will be included
|
did not ship macOS/ARM64 builds. As of v3.3 this architecture is included in the
|
||||||
in the official Flamenco builds, but still without FFmpeg binary.
|
official Flamenco builds, but still without FFmpeg binary.
|
||||||
|
|
||||||
You can install FFmpeg using [the ffmpeg Homebrew formula][brew] or any other
|
You can install FFmpeg using [the ffmpeg Homebrew formula][brew] or any other
|
||||||
method. Once installed Flamenco Worker should find it automatically. If not,
|
method. Once installed Flamenco Worker should find it automatically. If not,
|
||||||
|
@ -3,9 +3,10 @@ title: Compositor Nodes
|
|||||||
weight: 10
|
weight: 10
|
||||||
---
|
---
|
||||||
|
|
||||||
*Job type documented and maintained by: [Dylan Blanqué][author].*
|
*Job type documented and maintained by: [Dylan Blanqué][author]. Please report any issues at [the script's Github project][github].*
|
||||||
|
|
||||||
[author]: https://projects.blender.org/Dylan-Blanque
|
[author]: https://projects.blender.org/Dylan-Blanque
|
||||||
|
[github]: https://github.com/dblanque/flamenco-compositor-script/issues
|
||||||
|
|
||||||
{{< hint >}}
|
{{< hint >}}
|
||||||
|
|
||||||
|
@ -19,6 +19,9 @@ This is an example of such a configuration file:
|
|||||||
manager_url: http://flamenco.local:8080/
|
manager_url: http://flamenco.local:8080/
|
||||||
task_types: [blender, ffmpeg, file-management, misc]
|
task_types: [blender, ffmpeg, file-management, misc]
|
||||||
restart_exit_code: 47
|
restart_exit_code: 47
|
||||||
|
|
||||||
|
# Optional advanced option, available on Linux only:
|
||||||
|
oom_score_adjust: 500
|
||||||
```
|
```
|
||||||
|
|
||||||
- `manager_url`: The URL of the Manager to connect to. If the setting is blank
|
- `manager_url`: The URL of the Manager to connect to. If the setting is blank
|
||||||
@ -31,10 +34,18 @@ restart_exit_code: 47
|
|||||||
- `restart_exit_code`: Having this set to a non-zero value will mark this Worker
|
- `restart_exit_code`: Having this set to a non-zero value will mark this Worker
|
||||||
as 'restartable'. See [Shut Down & Restart Actions][restarting] for more
|
as 'restartable'. See [Shut Down & Restart Actions][restarting] for more
|
||||||
information.
|
information.
|
||||||
|
- `oom_score_adjust`: an optional value between 0 and 1000, only available on
|
||||||
|
Linux. It configures the Out Of Memory behaviour of the Linux kernel. This is
|
||||||
|
the `oom_score_adj` value for all sub-processes started by the Worker. Set
|
||||||
|
this to a high value, so that when the machine runs out of memory when
|
||||||
|
rendering, it is Blender that gets killed, and not Flamenco Worker itself. For
|
||||||
|
more information, see [Linux Kernel: Per-Process Parameters][per-process-proc].
|
||||||
|
*This option was introduced in Flamenco v3.5.*
|
||||||
|
|
||||||
[scripts]: {{< ref "usage/job-types" >}}
|
[scripts]: {{< ref "usage/job-types" >}}
|
||||||
[task-types]: {{< ref "usage/job-types" >}}#task-types
|
[task-types]: {{< ref "usage/job-types" >}}#task-types
|
||||||
[restarting]: {{< ref "usage/worker-actions" >}}#shut-down--restart-actions
|
[restarting]: {{< ref "usage/worker-actions" >}}#shut-down--restart-actions
|
||||||
|
[per-process-proc]: https://docs.kernel.org/filesystems/proc.html#chapter-3-per-process-parameters
|
||||||
|
|
||||||
## Worker-Specific Files
|
## Worker-Specific Files
|
||||||
|
|
||||||
|
@ -1,2 +1,2 @@
|
|||||||
latestVersion: "3.4"
|
latestVersion: "3.5"
|
||||||
latestExperimentalVersion: "3.5-alpha0"
|
latestExperimentalVersion: "3.6-alpha0"
|
||||||
|
Loading…
Reference in New Issue
Block a user