diff --git a/addon/flamenco/manager/api/meta_api.py b/addon/flamenco/manager/api/meta_api.py index 9627d223..af9b5c84 100644 --- a/addon/flamenco/manager/api/meta_api.py +++ b/addon/flamenco/manager/api/meta_api.py @@ -511,6 +511,56 @@ class MetaApi(object): }, api_client=api_client ) + self.update_configuration_file_endpoint = _Endpoint( + settings={ + 'response_type': None, + 'auth': [], + 'endpoint_path': '/api/v3/configuration/file', + 'operation_id': 'update_configuration_file', + 'http_method': 'PUT', + 'servers': None, + }, + params_map={ + 'all': [ + 'body', + ], + 'required': [ + 'body', + ], + 'nullable': [ + ], + 'enum': [ + ], + 'validation': [ + ] + }, + root_map={ + 'validations': { + }, + 'allowed_values': { + }, + 'openapi_types': { + 'body': + ({str: (bool, date, datetime, dict, float, int, list, str, none_type)},), + }, + 'attribute_map': { + }, + 'location_map': { + 'body': 'body', + }, + 'collection_format_map': { + } + }, + headers_map={ + 'accept': [ + 'application/json' + ], + 'content_type': [ + 'application/json' + ] + }, + api_client=api_client + ) def check_blender_exe_path( self, @@ -1253,3 +1303,80 @@ class MetaApi(object): kwargs['_host_index'] = kwargs.get('_host_index') return self.save_setup_assistant_config_endpoint.call_with_http_info(**kwargs) + def update_configuration_file( + self, + body, + **kwargs + ): + """Overwrites the configuration file. It does not actively reload the new configuration. # noqa: E501 + + This method makes a synchronous HTTP request by default. To make an + asynchronous HTTP request, please pass async_req=True + + >>> thread = api.update_configuration_file(body, async_req=True) + >>> result = thread.get() + + Args: + body ({str: (bool, date, datetime, dict, float, int, list, str, none_type)}): The configuration values as a JSON + + 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: + None + 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') + kwargs['body'] = \ + body + return self.update_configuration_file_endpoint.call_with_http_info(**kwargs) + diff --git a/addon/flamenco/manager/docs/MetaApi.md b/addon/flamenco/manager/docs/MetaApi.md index b4d7b5bb..eccc8981 100644 --- a/addon/flamenco/manager/docs/MetaApi.md +++ b/addon/flamenco/manager/docs/MetaApi.md @@ -14,6 +14,7 @@ Method | HTTP request | Description [**get_variables**](MetaApi.md#get_variables) | **GET** /api/v3/configuration/variables/{audience}/{platform} | Get the variables of this Manager. Used by the Blender add-on to recognise two-way variables, and for the web interface to do variable replacement based on the browser's platform. [**get_version**](MetaApi.md#get_version) | **GET** /api/v3/version | Get the Flamenco version of this Manager [**save_setup_assistant_config**](MetaApi.md#save_setup_assistant_config) | **POST** /api/v3/configuration/setup-assistant | Update the Manager's configuration, and restart it in fully functional mode. +[**update_configuration_file**](MetaApi.md#update_configuration_file) | **PUT** /api/v3/configuration/file | Overwrites the configuration file. It does not actively reload the new configuration. # **check_blender_exe_path** @@ -676,3 +677,68 @@ No authorization required [[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) +# **update_configuration_file** +> update_configuration_file(body) + +Overwrites the configuration file. It does not actively reload the new configuration. + +### Example + + +```python +import time +import flamenco.manager +from flamenco.manager.api import meta_api +from flamenco.manager.model.error import Error +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) + body = {} # {str: (bool, date, datetime, dict, float, int, list, str, none_type)} | The configuration values as a JSON + + # example passing only required values which don't have defaults set + try: + # Overwrites the configuration file. It does not actively reload the new configuration. + api_instance.update_configuration_file(body) + except flamenco.manager.ApiException as e: + print("Exception when calling MetaApi->update_configuration_file: %s\n" % e) +``` + + +### Parameters + +Name | Type | Description | Notes +------------- | ------------- | ------------- | ------------- + **body** | **{str: (bool, date, datetime, dict, float, int, list, str, none_type)}**| The configuration values as a JSON | + +### Return type + +void (empty response body) + +### Authorization + +No authorization required + +### HTTP request headers + + - **Content-Type**: application/json + - **Accept**: application/json + + +### HTTP response details + +| Status code | Description | Response headers | +|-------------|-------------|------------------| +**204** | The configuration was successfully stored to flamenco-manager.yaml | - | +**0** | Unable to save the configuration file | - | + +[[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) + diff --git a/addon/flamenco/manager_README.md b/addon/flamenco/manager_README.md index e462d5c4..28bf0ba6 100644 --- a/addon/flamenco/manager_README.md +++ b/addon/flamenco/manager_README.md @@ -105,6 +105,7 @@ Class | Method | HTTP request | Description *MetaApi* | [**get_variables**](flamenco/manager/docs/MetaApi.md#get_variables) | **GET** /api/v3/configuration/variables/{audience}/{platform} | Get the variables of this Manager. Used by the Blender add-on to recognise two-way variables, and for the web interface to do variable replacement based on the browser's platform. *MetaApi* | [**get_version**](flamenco/manager/docs/MetaApi.md#get_version) | **GET** /api/v3/version | Get the Flamenco version of this Manager *MetaApi* | [**save_setup_assistant_config**](flamenco/manager/docs/MetaApi.md#save_setup_assistant_config) | **POST** /api/v3/configuration/setup-assistant | Update the Manager's configuration, and restart it in fully functional mode. +*MetaApi* | [**update_configuration_file**](flamenco/manager/docs/MetaApi.md#update_configuration_file) | **PUT** /api/v3/configuration/file | Overwrites the configuration file. It does not actively reload the new configuration. *ShamanApi* | [**shaman_checkout**](flamenco/manager/docs/ShamanApi.md#shaman_checkout) | **POST** /api/v3/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** /api/v3/shaman/checkout/requirements | Checks a Shaman Requirements file, and reports which files are unknown. *ShamanApi* | [**shaman_file_store**](flamenco/manager/docs/ShamanApi.md#shaman_file_store) | **POST** /api/v3/shaman/files/{checksum}/{filesize} | Store a new file on the Shaman server. Note that the Shaman server can forcibly close the HTTP connection when another client finishes uploading the exact same file, to prevent double uploads. The file's contents should be sent in the request body. diff --git a/internal/manager/api_impl/interfaces.go b/internal/manager/api_impl/interfaces.go index 08da47a6..db613b48 100644 --- a/internal/manager/api_impl/interfaces.go +++ b/internal/manager/api_impl/interfaces.go @@ -193,6 +193,10 @@ type ConfigService interface { // Save writes the in-memory configuration to the config file. Save() error + + // Replace attempts to overwrite the configuration file on disk with the given Conf. + // If successful, it discards the in-memory configuration, and replaces it with the given Conf. + Replace(config.Conf) error } type Shaman interface { diff --git a/internal/manager/api_impl/meta.go b/internal/manager/api_impl/meta.go index b9e03a24..53e8edb1 100644 --- a/internal/manager/api_impl/meta.go +++ b/internal/manager/api_impl/meta.go @@ -53,6 +53,24 @@ func (f *Flamenco) GetConfiguration(e echo.Context) error { }) } +func (f *Flamenco) UpdateConfigurationFile(e echo.Context) error { + logger := requestLogger(e) + + var newConf config.Conf + if err := e.Bind(&newConf); err != nil { + logger.Warn().Err(err).Msg("bad request received") + return sendAPIError(e, http.StatusBadRequest, err.Error()) + } + + // Save the final configuration to disk. + if err := f.config.Replace(newConf); err != nil { + logger.Error().Err(err).Msg("error saving configuration file") + return sendAPIError(e, http.StatusInternalServerError, "setup assistant: error saving configuration file: %v", err) + } + + return e.NoContent(http.StatusNoContent) +} + func (f *Flamenco) GetConfigurationFile(e echo.Context) error { config := f.config.Get() return e.JSON(http.StatusOK, config) diff --git a/internal/manager/api_impl/meta_test.go b/internal/manager/api_impl/meta_test.go index 491bb700..a3956dbd 100644 --- a/internal/manager/api_impl/meta_test.go +++ b/internal/manager/api_impl/meta_test.go @@ -17,6 +17,7 @@ import ( "github.com/stretchr/testify/require" "projects.blender.org/studio/flamenco/internal/manager/config" "projects.blender.org/studio/flamenco/pkg/api" + shaman_config "projects.blender.org/studio/flamenco/pkg/shaman/config" ) func TestGetVariables(t *testing.T) { @@ -401,3 +402,91 @@ func metaTestFixtures(t *testing.T) (mockedFlamenco, func()) { return mf, finish } + +// TestUpdateConfigurationFile checks to see if JSON attributes actually overwrite the Config struct attributes +func TestUpdateConfigurationFile(t *testing.T) { + + mf, finish := metaTestFixtures(t) + defer finish() + + doTest := func(body config.Conf) config.Conf { + + var replacedConfig config.Conf + // Mock the loading and saving of the config. + mf.config.EXPECT().Replace(body).Do(func(newConfig config.Conf) { + replacedConfig = newConfig + }) + + // Call the API. + echoCtx := mf.prepareMockedJSONRequest(body) + err := mf.flamenco.UpdateConfigurationFile(echoCtx) + + require.NoError(t, err) + + assertResponseNoContent(t, echoCtx) + + return replacedConfig + } + + // Test situation where manager name is updated + { + form := config.Conf{ + Base: config.Base{ + ManagerName: "abc", + }, + } + + updatedConfig := doTest(form) + assert.Equal(t, form.Base.ManagerName, updatedConfig.Base.ManagerName) + // Other settings should be set to their zero values. + assert.Equal(t, form.Base.Listen, updatedConfig.Listen) + assert.Equal(t, form.Variables, updatedConfig.Variables) + } + + // Test situation where listen is updated + { + form := config.Conf{ + Base: config.Base{ + Listen: ":3000", + }, + } + + updatedConfig := doTest(form) + assert.Equal(t, form.Base.Listen, updatedConfig.Base.Listen) + // Other settings should be set to their zero values. + assert.Equal(t, form.Base.Shaman.Enabled, updatedConfig.Base.Shaman.Enabled) + assert.Equal(t, form.Base.ManagerName, updatedConfig.Base.ManagerName) + assert.Equal(t, form.Variables, updatedConfig.Variables) + } + + // Test situation where shaman enabled is updated + { + form := config.Conf{ + Base: config.Base{ + Shaman: shaman_config.Config{ + Enabled: true, + }, + }, + } + + updatedConfig := doTest(form) + assert.Equal(t, form.Base.Shaman.Enabled, updatedConfig.Base.Shaman.Enabled) + // Other settings should be set to their zero values. + assert.Equal(t, form.Base.Listen, updatedConfig.Base.Listen) + assert.Equal(t, form.Base.ManagerName, updatedConfig.Base.ManagerName) + assert.Equal(t, form.Variables, updatedConfig.Variables) + } + + // Test situation where shaman enabled is omitted defaults to false + { + form := config.Conf{ + Base: config.Base{ + Shaman: shaman_config.Config{}, + }, + } + + updatedConfig := doTest(form) + assert.Equal(t, false, updatedConfig.Base.Shaman.Enabled) + assert.Equal(t, false, form.Base.Shaman.Enabled) + } +} diff --git a/internal/manager/api_impl/mocks/api_impl_mock.gen.go b/internal/manager/api_impl/mocks/api_impl_mock.gen.go index 13f1e4cb..44902000 100644 --- a/internal/manager/api_impl/mocks/api_impl_mock.gen.go +++ b/internal/manager/api_impl/mocks/api_impl_mock.gen.go @@ -996,6 +996,20 @@ func (mr *MockConfigServiceMockRecorder) NewVariableToValueConverter(arg0, arg1 return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NewVariableToValueConverter", reflect.TypeOf((*MockConfigService)(nil).NewVariableToValueConverter), arg0, arg1) } +// Replace mocks base method. +func (m *MockConfigService) Replace(arg0 config.Conf) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Replace", arg0) + ret0, _ := ret[0].(error) + return ret0 +} + +// Replace indicates an expected call of Replace. +func (mr *MockConfigServiceMockRecorder) Replace(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Replace", reflect.TypeOf((*MockConfigService)(nil).Replace), arg0) +} + // ResolveVariables mocks base method. func (m *MockConfigService) ResolveVariables(arg0 config.VariableAudience, arg1 config.VariablePlatform) map[string]config.ResolvedVariable { m.ctrl.T.Helper() diff --git a/internal/manager/config/config.go b/internal/manager/config/config.go index 8c52269c..ae4260a0 100644 --- a/internal/manager/config/config.go +++ b/internal/manager/config/config.go @@ -67,71 +67,71 @@ type TestTasks struct { // ConfMeta contains configuration file metadata. type ConfMeta struct { // Version of the config file structure. - Version int `yaml:"version"` + Version int `json:"version" yaml:"version"` } // Base contains those settings that are shared by all configuration versions. type Base struct { - Meta ConfMeta `yaml:"_meta"` + Meta ConfMeta `json:"_meta" yaml:"_meta"` - ManagerName string `yaml:"manager_name"` + ManagerName string `json:"manager_name" yaml:"manager_name"` - DatabaseDSN string `yaml:"database"` - DBIntegrityCheck duration.Duration `yaml:"database_check_period"` + DatabaseDSN string `json:"database" yaml:"database"` + DBIntegrityCheck duration.Duration `json:"database_check_period" yaml:"database_check_period"` - Listen string `yaml:"listen"` + Listen string `json:"listen" yaml:"listen"` - SSDPDiscovery bool `yaml:"autodiscoverable"` + SSDPDiscovery bool `json:"autodiscoverable" yaml:"autodiscoverable"` // LocalManagerStoragePath is where the Manager stores its files, like task // logs, last-rendered images, etc. - LocalManagerStoragePath string `yaml:"local_manager_storage_path"` + LocalManagerStoragePath string `json:"local_manager_storage_path" yaml:"local_manager_storage_path"` // SharedStoragePath is where files shared between Manager and Workers go, // like the blend files of a render job. - SharedStoragePath string `yaml:"shared_storage_path"` + SharedStoragePath string `json:"shared_storage_path" yaml:"shared_storage_path"` - Shaman shaman_config.Config `yaml:"shaman"` + Shaman shaman_config.Config `json:"shaman" yaml:"shaman"` - TaskTimeout duration.Duration `yaml:"task_timeout"` - WorkerTimeout duration.Duration `yaml:"worker_timeout"` + TaskTimeout duration.Duration `json:"task_timeout" yaml:"task_timeout"` + WorkerTimeout duration.Duration `json:"worker_timeout" yaml:"worker_timeout"` /* This many failures (on a given job+task type combination) will ban a worker * from that task type on that job. */ - BlocklistThreshold int `yaml:"blocklist_threshold"` + BlocklistThreshold int `json:"blocklist_threshold" yaml:"blocklist_threshold"` // When this many workers have tried the task and failed, it will be hard-failed // (even when there are workers left that could technically retry the task). - TaskFailAfterSoftFailCount int `yaml:"task_fail_after_softfail_count"` + TaskFailAfterSoftFailCount int `json:"task_fail_after_softfail_count" yaml:"task_fail_after_softfail_count"` - MQTT MQTTConfig `yaml:"mqtt"` + MQTT MQTTConfig `json:"mqtt" yaml:"mqtt"` } // MQTTConfig contains the configuration options for MQTT broker (idea for the future) and client. type MQTTConfig struct { - Client eventbus.MQTTClientConfig `yaml:"client"` + Client eventbus.MQTTClientConfig `json:"client" yaml:"client"` } // Conf is the latest version of the configuration. // Currently it is version 3. type Conf struct { - Base `yaml:",inline"` + Base `json:",inline" yaml:",inline"` // Store GOOS in a variable so it can be modified by unit tests, making the // test independent of the actual platform. - currentGOOS VariablePlatform `yaml:"-"` + currentGOOS VariablePlatform `json:"-" yaml:"-"` // Variable name → Variable definition - Variables map[string]Variable `yaml:"variables"` + Variables map[string]Variable `json:"variables" yaml:"variables"` // Implicit variables work as regular variables, but do not get written to the // configuration file. - implicitVariables map[string]Variable `yaml:"-"` + implicitVariables map[string]Variable `json:"-" yaml:"-"` // audience + platform + variable name → variable value. // Used to look up variables for a given platform and audience. // The 'audience' is never "all" or ""; only concrete audiences are stored here. - VariablesLookup map[VariableAudience]map[VariablePlatform]map[string]string `yaml:"-"` + VariablesLookup map[VariableAudience]map[VariablePlatform]map[string]string `json:"-" yaml:"-"` } // Variable defines a configuration variable. diff --git a/internal/manager/config/service.go b/internal/manager/config/service.go index 2293f7bc..e25c83ae 100644 --- a/internal/manager/config/service.go +++ b/internal/manager/config/service.go @@ -26,6 +26,17 @@ func (s *Service) ForceFirstRun() { s.forceFirstRun = true } +func (s *Service) Replace(config Conf) error { + if err := config.Overwrite(); err != nil { + return err + } + + // Replace the in-memory config with the new one + s.config = config + log.Info().Str("filename", configFilename).Msg("in-memory configuration file replaced") + return nil +} + // Load parses the flamenco-manager.yaml file, and returns whether this is // likely to be the first run or not. func (s *Service) Load() (bool, error) { diff --git a/internal/manager/eventbus/mqtt_client.go b/internal/manager/eventbus/mqtt_client.go index 50ef496e..43dc7301 100644 --- a/internal/manager/eventbus/mqtt_client.go +++ b/internal/manager/eventbus/mqtt_client.go @@ -44,12 +44,12 @@ var _ Forwarder = (*MQTTForwarder)(nil) // MQTTClientConfig contains the MQTT client configuration. type MQTTClientConfig struct { - BrokerURL string `yaml:"broker"` - ClientID string `yaml:"clientID"` - TopicPrefix string `yaml:"topic_prefix"` + BrokerURL string `json:"broker" yaml:"broker"` + ClientID string `json:"clientID" yaml:"clientID"` + TopicPrefix string `json:"topic_prefix" yaml:"topic_prefix"` - Username string `yaml:"username"` - Password string `yaml:"password"` + Username string `json:"username" yaml:"username"` + Password string `json:"password" yaml:"password"` } type mqttQueuedMessage struct { diff --git a/internal/worker/mocks/client.gen.go b/internal/worker/mocks/client.gen.go index 3a8ecbfb..f9073549 100644 --- a/internal/worker/mocks/client.gen.go +++ b/internal/worker/mocks/client.gen.go @@ -1456,6 +1456,46 @@ func (mr *MockFlamencoClientMockRecorder) TaskUpdateWithResponse(arg0, arg1, arg return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "TaskUpdateWithResponse", reflect.TypeOf((*MockFlamencoClient)(nil).TaskUpdateWithResponse), varargs...) } +// UpdateConfigurationFileWithBodyWithResponse mocks base method. +func (m *MockFlamencoClient) UpdateConfigurationFileWithBodyWithResponse(arg0 context.Context, arg1 string, arg2 io.Reader, arg3 ...api.RequestEditorFn) (*api.UpdateConfigurationFileResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{arg0, arg1, arg2} + for _, a := range arg3 { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "UpdateConfigurationFileWithBodyWithResponse", varargs...) + ret0, _ := ret[0].(*api.UpdateConfigurationFileResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// UpdateConfigurationFileWithBodyWithResponse indicates an expected call of UpdateConfigurationFileWithBodyWithResponse. +func (mr *MockFlamencoClientMockRecorder) UpdateConfigurationFileWithBodyWithResponse(arg0, arg1, arg2 interface{}, arg3 ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{arg0, arg1, arg2}, arg3...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateConfigurationFileWithBodyWithResponse", reflect.TypeOf((*MockFlamencoClient)(nil).UpdateConfigurationFileWithBodyWithResponse), varargs...) +} + +// UpdateConfigurationFileWithResponse mocks base method. +func (m *MockFlamencoClient) UpdateConfigurationFileWithResponse(arg0 context.Context, arg1 api.UpdateConfigurationFileJSONRequestBody, arg2 ...api.RequestEditorFn) (*api.UpdateConfigurationFileResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{arg0, arg1} + for _, a := range arg2 { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "UpdateConfigurationFileWithResponse", varargs...) + ret0, _ := ret[0].(*api.UpdateConfigurationFileResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// UpdateConfigurationFileWithResponse indicates an expected call of UpdateConfigurationFileWithResponse. +func (mr *MockFlamencoClientMockRecorder) UpdateConfigurationFileWithResponse(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{arg0, arg1}, arg2...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateConfigurationFileWithResponse", reflect.TypeOf((*MockFlamencoClient)(nil).UpdateConfigurationFileWithResponse), varargs...) +} + // UpdateWorkerTagWithBodyWithResponse mocks base method. func (m *MockFlamencoClient) UpdateWorkerTagWithBodyWithResponse(arg0 context.Context, arg1, arg2 string, arg3 io.Reader, arg4 ...api.RequestEditorFn) (*api.UpdateWorkerTagResponse, error) { m.ctrl.T.Helper() diff --git a/pkg/api/flamenco-openapi.yaml b/pkg/api/flamenco-openapi.yaml index cd12d83d..aea80ffa 100644 --- a/pkg/api/flamenco-openapi.yaml +++ b/pkg/api/flamenco-openapi.yaml @@ -141,7 +141,26 @@ paths: application/yaml: schema: type: string - + put: + summary: Overwrites the configuration file. It does not actively reload the new configuration. + tags: [meta] + operationId: updateConfigurationFile + requestBody: + description: The configuration values as a JSON + required: true + content: + application/json: + schema: + type: object + description: Currently there is no validation for the request body. The caller must ensure the configuration is valid. + responses: + "204": + description: The configuration was successfully stored to flamenco-manager.yaml + default: + description: Unable to save the configuration file + content: + application/json: + schema: { $ref: "#/components/schemas/Error" } /api/v3/configuration/variables/{audience}/{platform}: summary: Endpoint for getting the variables from Flamenco Manager's configuration. get: diff --git a/pkg/api/openapi_client.gen.go b/pkg/api/openapi_client.gen.go index b2fcab68..3a6c96e1 100644 --- a/pkg/api/openapi_client.gen.go +++ b/pkg/api/openapi_client.gen.go @@ -111,6 +111,11 @@ type ClientInterface interface { // GetConfigurationFile request GetConfigurationFile(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error) + // UpdateConfigurationFile request with any body + UpdateConfigurationFileWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + + UpdateConfigurationFile(ctx context.Context, body UpdateConfigurationFileJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) + // SaveSetupAssistantConfig request with any body SaveSetupAssistantConfigWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) @@ -384,6 +389,30 @@ func (c *Client) GetConfigurationFile(ctx context.Context, reqEditors ...Request return c.Client.Do(req) } +func (c *Client) UpdateConfigurationFileWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewUpdateConfigurationFileRequestWithBody(c.Server, contentType, body) + 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) UpdateConfigurationFile(ctx context.Context, body UpdateConfigurationFileJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewUpdateConfigurationFileRequest(c.Server, body) + 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) SaveSetupAssistantConfigWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { req, err := NewSaveSetupAssistantConfigRequestWithBody(c.Server, contentType, body) if err != nil { @@ -1373,6 +1402,46 @@ func NewGetConfigurationFileRequest(server string) (*http.Request, error) { return req, nil } +// NewUpdateConfigurationFileRequest calls the generic UpdateConfigurationFile builder with application/json body +func NewUpdateConfigurationFileRequest(server string, body UpdateConfigurationFileJSONRequestBody) (*http.Request, error) { + var bodyReader io.Reader + buf, err := json.Marshal(body) + if err != nil { + return nil, err + } + bodyReader = bytes.NewReader(buf) + return NewUpdateConfigurationFileRequestWithBody(server, "application/json", bodyReader) +} + +// NewUpdateConfigurationFileRequestWithBody generates requests for UpdateConfigurationFile with any type of body +func NewUpdateConfigurationFileRequestWithBody(server string, contentType string, body io.Reader) (*http.Request, error) { + var err error + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/api/v3/configuration/file") + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest("PUT", queryURL.String(), body) + if err != nil { + return nil, err + } + + req.Header.Add("Content-Type", contentType) + + return req, nil +} + // NewSaveSetupAssistantConfigRequest calls the generic SaveSetupAssistantConfig builder with application/json body func NewSaveSetupAssistantConfigRequest(server string, body SaveSetupAssistantConfigJSONRequestBody) (*http.Request, error) { var bodyReader io.Reader @@ -3296,6 +3365,11 @@ type ClientWithResponsesInterface interface { // GetConfigurationFile request GetConfigurationFileWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*GetConfigurationFileResponse, error) + // UpdateConfigurationFile request with any body + UpdateConfigurationFileWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*UpdateConfigurationFileResponse, error) + + UpdateConfigurationFileWithResponse(ctx context.Context, body UpdateConfigurationFileJSONRequestBody, reqEditors ...RequestEditorFn) (*UpdateConfigurationFileResponse, error) + // SaveSetupAssistantConfig request with any body SaveSetupAssistantConfigWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*SaveSetupAssistantConfigResponse, error) @@ -3601,6 +3675,28 @@ func (r GetConfigurationFileResponse) StatusCode() int { return 0 } +type UpdateConfigurationFileResponse struct { + Body []byte + HTTPResponse *http.Response + JSONDefault *Error +} + +// Status returns HTTPResponse.Status +func (r UpdateConfigurationFileResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r UpdateConfigurationFileResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + type SaveSetupAssistantConfigResponse struct { Body []byte HTTPResponse *http.Response @@ -4788,6 +4884,23 @@ func (c *ClientWithResponses) GetConfigurationFileWithResponse(ctx context.Conte return ParseGetConfigurationFileResponse(rsp) } +// UpdateConfigurationFileWithBodyWithResponse request with arbitrary body returning *UpdateConfigurationFileResponse +func (c *ClientWithResponses) UpdateConfigurationFileWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*UpdateConfigurationFileResponse, error) { + rsp, err := c.UpdateConfigurationFileWithBody(ctx, contentType, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseUpdateConfigurationFileResponse(rsp) +} + +func (c *ClientWithResponses) UpdateConfigurationFileWithResponse(ctx context.Context, body UpdateConfigurationFileJSONRequestBody, reqEditors ...RequestEditorFn) (*UpdateConfigurationFileResponse, error) { + rsp, err := c.UpdateConfigurationFile(ctx, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseUpdateConfigurationFileResponse(rsp) +} + // SaveSetupAssistantConfigWithBodyWithResponse request with arbitrary body returning *SaveSetupAssistantConfigResponse func (c *ClientWithResponses) SaveSetupAssistantConfigWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*SaveSetupAssistantConfigResponse, error) { rsp, err := c.SaveSetupAssistantConfigWithBody(ctx, contentType, body, reqEditors...) @@ -5550,6 +5663,32 @@ func ParseGetConfigurationFileResponse(rsp *http.Response) (*GetConfigurationFil return response, nil } +// ParseUpdateConfigurationFileResponse parses an HTTP response from a UpdateConfigurationFileWithResponse call +func ParseUpdateConfigurationFileResponse(rsp *http.Response) (*UpdateConfigurationFileResponse, error) { + bodyBytes, err := ioutil.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &UpdateConfigurationFileResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && true: + var dest Error + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSONDefault = &dest + + } + + return response, nil +} + // ParseSaveSetupAssistantConfigResponse parses an HTTP response from a SaveSetupAssistantConfigWithResponse call func ParseSaveSetupAssistantConfigResponse(rsp *http.Response) (*SaveSetupAssistantConfigResponse, error) { bodyBytes, err := ioutil.ReadAll(rsp.Body) diff --git a/pkg/api/openapi_server.gen.go b/pkg/api/openapi_server.gen.go index 507ff154..aadf661a 100644 --- a/pkg/api/openapi_server.gen.go +++ b/pkg/api/openapi_server.gen.go @@ -28,6 +28,9 @@ type ServerInterface interface { // Retrieve the configuration of Flamenco Manager. // (GET /api/v3/configuration/file) GetConfigurationFile(ctx echo.Context) error + // Overwrites the configuration file. It does not actively reload the new configuration. + // (PUT /api/v3/configuration/file) + UpdateConfigurationFile(ctx echo.Context) error // Update the Manager's configuration, and restart it in fully functional mode. // (POST /api/v3/configuration/setup-assistant) SaveSetupAssistantConfig(ctx echo.Context) error @@ -231,6 +234,15 @@ func (w *ServerInterfaceWrapper) GetConfigurationFile(ctx echo.Context) error { return err } +// UpdateConfigurationFile converts echo context to params. +func (w *ServerInterfaceWrapper) UpdateConfigurationFile(ctx echo.Context) error { + var err error + + // Invoke the callback with all the unmarshalled arguments + err = w.Handler.UpdateConfigurationFile(ctx) + return err +} + // SaveSetupAssistantConfig converts echo context to params. func (w *ServerInterfaceWrapper) SaveSetupAssistantConfig(ctx echo.Context) error { var err error @@ -1007,6 +1019,7 @@ func RegisterHandlersWithBaseURL(router EchoRouter, si ServerInterface, baseURL router.POST(baseURL+"/api/v3/configuration/check/blender", wrapper.CheckBlenderExePath) router.POST(baseURL+"/api/v3/configuration/check/shared-storage", wrapper.CheckSharedStoragePath) router.GET(baseURL+"/api/v3/configuration/file", wrapper.GetConfigurationFile) + router.PUT(baseURL+"/api/v3/configuration/file", wrapper.UpdateConfigurationFile) router.POST(baseURL+"/api/v3/configuration/setup-assistant", wrapper.SaveSetupAssistantConfig) router.GET(baseURL+"/api/v3/configuration/shared-storage/:audience/:platform", wrapper.GetSharedStorage) router.GET(baseURL+"/api/v3/configuration/variables/:audience/:platform", wrapper.GetVariables) diff --git a/pkg/api/openapi_spec.gen.go b/pkg/api/openapi_spec.gen.go index 132610b9..9eb8c25c 100644 --- a/pkg/api/openapi_spec.gen.go +++ b/pkg/api/openapi_spec.gen.go @@ -18,236 +18,238 @@ import ( // Base64 encoded, gzipped, json marshaled Swagger object var swaggerSpec = []string{ - "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", - "/qj6WxIBAA==", + "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", + "nPoidOEcttzB1a9X/xkAAP//EfYh4QEVAQA=", } // GetSwagger returns the content of the embedded swagger specification file diff --git a/pkg/api/openapi_types.gen.go b/pkg/api/openapi_types.gen.go index 2202f73f..732e9cda 100644 --- a/pkg/api/openapi_types.gen.go +++ b/pkg/api/openapi_types.gen.go @@ -930,6 +930,9 @@ type CheckBlenderExePathJSONBody PathCheckInput // CheckSharedStoragePathJSONBody defines parameters for CheckSharedStoragePath. type CheckSharedStoragePathJSONBody PathCheckInput +// UpdateConfigurationFileJSONBody defines parameters for UpdateConfigurationFile. +type UpdateConfigurationFileJSONBody map[string]interface{} + // SaveSetupAssistantConfigJSONBody defines parameters for SaveSetupAssistantConfig. type SaveSetupAssistantConfigJSONBody SetupAssistantConfig @@ -1002,6 +1005,9 @@ type CheckBlenderExePathJSONRequestBody CheckBlenderExePathJSONBody // CheckSharedStoragePathJSONRequestBody defines body for CheckSharedStoragePath for application/json ContentType. type CheckSharedStoragePathJSONRequestBody CheckSharedStoragePathJSONBody +// UpdateConfigurationFileJSONRequestBody defines body for UpdateConfigurationFile for application/json ContentType. +type UpdateConfigurationFileJSONRequestBody UpdateConfigurationFileJSONBody + // SaveSetupAssistantConfigJSONRequestBody defines body for SaveSetupAssistantConfig for application/json ContentType. type SaveSetupAssistantConfigJSONRequestBody SaveSetupAssistantConfigJSONBody diff --git a/pkg/shaman/config/config.go b/pkg/shaman/config/config.go index 680a7f47..b90a466b 100644 --- a/pkg/shaman/config/config.go +++ b/pkg/shaman/config/config.go @@ -43,11 +43,11 @@ const ( type Config struct { // Used only for unit tests, so that they know where the temporary // directory created for this test is located. - TestTempDir string `yaml:"-"` + TestTempDir string `json:"-" yaml:"-"` - Enabled bool `yaml:"enabled"` - StoragePath string `yaml:"-"` // Needs to be set externally, not saved in config. - GarbageCollect GarbageCollect `yaml:"garbageCollect"` + Enabled bool `json:"enabled" yaml:"enabled"` + StoragePath string `json:"-" yaml:"-"` // Needs to be set externally, not saved in config. + GarbageCollect GarbageCollect `json:"garbageCollect" yaml:"garbageCollect"` } // GarbageCollect contains the config options for the GC. @@ -59,7 +59,7 @@ type GarbageCollect struct { // Used by the -gc CLI arg to silently disable the garbage collector // while we're performing a manual sweep. - SilentlyDisable bool `yaml:"-"` + SilentlyDisable bool `json:"-" yaml:"-"` } // FileStorePath returns the sub-directory of the configured storage path, diff --git a/web/app/src/manager-api/manager/MetaApi.js b/web/app/src/manager-api/manager/MetaApi.js index 26f6e97c..a0eb9ed0 100644 --- a/web/app/src/manager-api/manager/MetaApi.js +++ b/web/app/src/manager-api/manager/MetaApi.js @@ -478,4 +478,49 @@ export default class MetaApi { } + /** + * Overwrites the configuration file. It does not actively reload the new configuration. + * @param {Object.} body The configuration values as a JSON + * @return {Promise} a {@link https://www.promisejs.org/|Promise}, with an object containing HTTP response + */ + updateConfigurationFileWithHttpInfo(body) { + let postBody = body; + // verify the required parameter 'body' is set + if (body === undefined || body === null) { + throw new Error("Missing the required parameter 'body' when calling updateConfigurationFile"); + } + + let pathParams = { + }; + let queryParams = { + }; + let headerParams = { + }; + let formParams = { + }; + + let authNames = []; + let contentTypes = ['application/json']; + let accepts = ['application/json']; + let returnType = null; + return this.apiClient.callApi( + '/api/v3/configuration/file', 'PUT', + pathParams, queryParams, headerParams, formParams, postBody, + authNames, contentTypes, accepts, returnType, null + ); + } + + /** + * Overwrites the configuration file. It does not actively reload the new configuration. + * @param {Object.} body The configuration values as a JSON + * @return {Promise} a {@link https://www.promisejs.org/|Promise} + */ + updateConfigurationFile(body) { + return this.updateConfigurationFileWithHttpInfo(body) + .then(function(response_and_data) { + return response_and_data.data; + }); + } + + }