diff --git a/src/DevicesRestApi/Controllers/ClustersController.cs b/src/DevicesRestApi/Controllers/ClustersController.cs index d0fedf8..24f40a4 100644 --- a/src/DevicesRestApi/Controllers/ClustersController.cs +++ b/src/DevicesRestApi/Controllers/ClustersController.cs @@ -37,7 +37,7 @@ public class ClustersController : ControllerBase } if (!_clusterConfigurationFile.DirectoryExists()) { - _logger.LogError($"Cluster not found: {clusterName}"); + _logger.LogError($"Cluster not found: {_clusterConfigurationFile.GetDirectoryPath()}"); return new NotFound("Cluster not found"); } diff --git a/src/DevicesRestApi/Controllers/DevicesController.cs b/src/DevicesRestApi/Controllers/DevicesController.cs new file mode 100644 index 0000000..9412704 --- /dev/null +++ b/src/DevicesRestApi/Controllers/DevicesController.cs @@ -0,0 +1,147 @@ +namespace DevicesRestApi.Controllers; + +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Options; +using System.Diagnostics; + +using DevicesRestApi.Helpers; + +[ApiController] +[Route("[controller]")] +public class DevicesController : ControllerBase +{ + private readonly ILogger _logger; + + private readonly AppSettings _appSettings; + + public DevicesController( + ILogger logger, + IOptions appSettings + ) { + _logger = logger; + _appSettings = appSettings.Value; + } + + [HttpPost] + [Route("~/devices/{deviceName}")] + public IActionResult UpdateDeviceConfiguration(string deviceName, UpdateDeviceRequest request) + { + DeviceConfigurationFile configurationFile = null; + try { + configurationFile = GetConfigurationFile(deviceName); + } + catch (ConfigurationException e) + { + _logger.LogWarning(e.Message); + return BadRequest(new{ + message = e.Message, + }); + } + var command = createCommand( + configurationFile.GetFilePath(), + request.clusterName + ); + + executeCommand(command); + + return new Ok("Device configuration updated"); + } + + [HttpDelete] + [Route("~/devices/{deviceName}")] + public IActionResult DeleteDeviceConfiguration(string deviceName) + { + DeviceConfigurationFile configurationFile = null; + try { + configurationFile = GetConfigurationFile(deviceName); + } + catch (ConfigurationException e) + { + _logger.LogWarning(e.Message); + return BadRequest(new{ + message = e.Message, + }); + } + + var command = createCommand( + configurationFile.GetFilePath(), + "no-cluster" + ); + + executeCommand(command); + + return new Ok("Device configuration updated"); + } + + private DeviceConfigurationFile GetConfigurationFile(string deviceName) + { + DeviceConfigurationFile configurationFile = null; + try { + configurationFile = new DeviceConfigurationFile(deviceName, _appSettings); + } + catch (ArgumentException e) + { + _logger.LogError($"Something went wrong: {e}"); + throw new ConfigurationException($"Something went wrong: {e}"); + } + if (!configurationFile.FileExists()) + { + var message = "Configuration file doesn't exist"; + _logger.LogError(message); + throw new ConfigurationException(message); + } + return configurationFile; + } + + private string createCommand(string filePath, string clusterName) + { + return $"sed -i -E 's/^project\\s?=.*/project = {clusterName}/' {filePath}"; + } + + private void executeCommand(string command) + { + _logger.LogDebug($"Executing the command: {command}"); + + var process = new Process + { + StartInfo = new ProcessStartInfo + { + FileName = "/bin/bash", + Arguments = $"-c \"{command}\"", + RedirectStandardOutput = true, + RedirectStandardError = true, + UseShellExecute = false, + CreateNoWindow = true + } + }; + + process.Start(); + + string error = process.StandardError.ReadToEnd(); + + process.WaitForExit(); + + if (process.ExitCode > 0) + { + var message = error ?? "Unknown error"; + message = $"An error occurred during the execution of the command: {message}"; + _logger.LogError(message); + throw new Exception(message); + } + } + + public class CommandRequest + { + public string ClusterName { get; set; } + public string DeviceName { get; set; } + } + + public class ConfigurationException : Exception + { + public ConfigurationException() : base() { } + + public ConfigurationException(string message) : base(message) { } + + public ConfigurationException(string message, Exception inner) : base(message, inner) { } + } +} diff --git a/src/DevicesRestApi/Helpers/AppSettings.cs b/src/DevicesRestApi/Helpers/AppSettings.cs index 3fe24b0..37bbfac 100644 --- a/src/DevicesRestApi/Helpers/AppSettings.cs +++ b/src/DevicesRestApi/Helpers/AppSettings.cs @@ -4,5 +4,5 @@ public class AppSettings { public string ApiKey { get; set; } = string.Empty; - public string ClustersRootPath { get; set; } = string.Empty; + public string RootPath { get; set; } = string.Empty; } diff --git a/src/DevicesRestApi/Helpers/ClusterConfigurationFile.cs b/src/DevicesRestApi/Helpers/ClusterConfigurationFile.cs index 7890456..6102d78 100644 --- a/src/DevicesRestApi/Helpers/ClusterConfigurationFile.cs +++ b/src/DevicesRestApi/Helpers/ClusterConfigurationFile.cs @@ -15,20 +15,26 @@ public class ClusterConfigurationFile AppSettings appSettings ) { _clusterName = clusterName; - _appSettings = appSettings; - _rootDirectory = _appSettings.ClustersRootPath; - if (string.IsNullOrEmpty(_clusterName)) { throw new ArgumentException("The provided cluster name is null or empty."); } - if (string.IsNullOrEmpty(_rootDirectory)) + + _appSettings = appSettings; + var rootPath = _appSettings.RootPath; + if (string.IsNullOrEmpty(rootPath)) { - throw new ArgumentException("Clusters root directory is not configured."); + throw new ArgumentException("Root path is not configured."); + } + + _rootDirectory = Path.Combine(rootPath, "projects"); + if (!Directory.Exists(_rootDirectory)) + { + throw new ArgumentException($"Clusters directory doesn't exist: {_rootDirectory}."); } } - private string GetDirectoryPath() + public string GetDirectoryPath() { return Path.Combine(_rootDirectory, _clusterName); } diff --git a/src/DevicesRestApi/Helpers/DeviceConfigurationFile.cs b/src/DevicesRestApi/Helpers/DeviceConfigurationFile.cs new file mode 100644 index 0000000..67aeca0 --- /dev/null +++ b/src/DevicesRestApi/Helpers/DeviceConfigurationFile.cs @@ -0,0 +1,81 @@ +namespace DevicesRestApi.Helpers; + +public class DeviceConfigurationFile +{ + private readonly string _rootDirectory; + + private readonly string _deviceName; + + private const string SettingsFilename = "settings.conf"; + + private readonly AppSettings _appSettings; + + public DeviceConfigurationFile( + string deviceName, + AppSettings appSettings + ) { + _deviceName = deviceName; + if (string.IsNullOrEmpty(_deviceName)) + { + throw new ArgumentException("The provided device name is null or empty."); + } + + _appSettings = appSettings; + var rootPath = _appSettings.RootPath; + if (string.IsNullOrEmpty(rootPath)) + { + throw new ArgumentException("Root path is not configured."); + } + + _rootDirectory = Path.Combine(rootPath, "devices"); + if (!Directory.Exists(_rootDirectory)) + { + throw new ArgumentException($"Devices directory doesn't exist: {_rootDirectory}."); + } + } + + public string GetDirectoryPath() + { + return Path.Combine(_rootDirectory, _deviceName); + } + + public string GetFilePath() + { + return Path.Combine(GetDirectoryPath(), SettingsFilename); + } + + public bool FileExists() + { + return File.Exists(GetFilePath()); + } + + public bool DirectoryExists() + { + return Directory.Exists(GetDirectoryPath()); + } + + private void CreateDirectory() + { + Directory.CreateDirectory(GetDirectoryPath()); + } + + private void CreateFile() + { + using (FileStream fs = File.Create(GetFilePath())) + { + // The file is created and opened, we don't need to write anything to it + } + } + + public void Create() + { + if (!DirectoryExists()) + { + CreateDirectory(); + } + if (!FileExists()) + { + CreateFile(); + } + } +} \ No newline at end of file diff --git a/src/DevicesRestApi/Helpers/UpdateDeviceRequest.cs b/src/DevicesRestApi/Helpers/UpdateDeviceRequest.cs new file mode 100644 index 0000000..14de4f4 --- /dev/null +++ b/src/DevicesRestApi/Helpers/UpdateDeviceRequest.cs @@ -0,0 +1,6 @@ +namespace DevicesRestApi.Helpers; + +public class UpdateDeviceRequest +{ + public string clusterName { get; set; } +} \ No newline at end of file diff --git a/src/DevicesRestApi/appsettings.json b/src/DevicesRestApi/appsettings.json index eaba149..41d9775 100644 --- a/src/DevicesRestApi/appsettings.json +++ b/src/DevicesRestApi/appsettings.json @@ -8,7 +8,7 @@ }, "AppSettings": { "ApiKey": "api-key", - "ClustersRootPath": "/tmp/projects" + "RootPath": "/tmp" }, "Urls": "http://0.0.0.0:8765", "AllowedHosts": "*",