Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
100 commits
Select commit Hold shift + click to select a range
35788c9
Add go.mof and main file
ppawlowski Apr 27, 2025
0b3a0e7
Add `cmd` package with install instructions
ppawlowski Apr 27, 2025
cd307bd
Add `config` package
ppawlowski Apr 27, 2025
88a27e9
Add `logger` package
ppawlowski Apr 27, 2025
2c0a52c
Add `nodejs` package
ppawlowski Apr 27, 2025
92cd543
Add `service` package
ppawlowski Apr 27, 2025
c23e21c
Add `utils` package
ppawlowski Apr 27, 2025
5e1d160
Add build shell script
ppawlowski Apr 27, 2025
7c950b5
Add GetOSDetails function
ppawlowski Apr 30, 2025
5a5395b
Handle properly node js archive for ARM
ppawlowski Apr 30, 2025
42f4277
Add debug information about os and architecture
ppawlowski Apr 30, 2025
163e275
Add helm flag
ppawlowski Apr 30, 2025
b6ff1ad
Add initial installer readme
ppawlowski Apr 30, 2025
c0dac59
Check if sudo command is available
ppawlowski May 1, 2025
9650add
Add sysvinit support
ppawlowski May 1, 2025
c0df83f
Replace flag with pflag
ppawlowski May 5, 2025
05889fa
Adjust PATH in linux services templates
ppawlowski May 5, 2025
2ce9a8b
Replace node with generic linux tools in build script
ppawlowski May 5, 2025
55f6689
Sync with the latest changes from the main branch
ppawlowski May 6, 2025
4ffeb72
Add tip about exec permissions; fix typos
ppawlowski May 7, 2025
f6ca052
Bump nodejs to v20.19.1
ppawlowski May 7, 2025
6e5be33
Merge branch 'feat-device-linux-installer' of github.com:FlowFuse/dev…
ppawlowski May 7, 2025
bff4a96
Update main install file
ppawlowski May 8, 2025
3c05a91
Update deviceagent installation on windows
ppawlowski May 8, 2025
eb80c09
Adjust utils to run on windows, remove unused functions
ppawlowski May 8, 2025
9216f93
Add support for windows to service package
ppawlowski May 8, 2025
b698090
Add macos support for multiple utils functions
ppawlowski May 12, 2025
ddea292
Add support for macos to nodejs package
ppawlowski May 12, 2025
0045fbf
Add support for macos to service package
ppawlowski May 12, 2025
1a121b0
Update manula build script
ppawlowski May 12, 2025
b29d8be
Update installer/go/README.md
ppawlowski May 12, 2025
93b43b2
Update installer/go/README.md
ppawlowski May 12, 2025
db7814f
Update installer/go/README.md
ppawlowski May 12, 2025
70e3d73
Update installer/go/README.md
ppawlowski May 12, 2025
061b80e
Add `--otc-no-start` flag to device-agent configure command
ppawlowski May 12, 2025
5e68f0f
Merge branch 'main' of github.com:FlowFuse/device-agent into feat-dev…
ppawlowski May 12, 2025
1db68df
Run device-agent to allow flows import
ppawlowski May 15, 2025
bbfdc65
Verify if workdir and device config file exists
ppawlowski May 15, 2025
a0c6fb9
Merge branch 'feat-device-linux-installer' of github.com:FlowFuse/dev…
ppawlowski May 15, 2025
eaff83e
Improve user creation debug messages on windows
ppawlowski May 15, 2025
7b1e5a1
Add logs rotation; optimise nssm commands
ppawlowski May 15, 2025
f8fa034
Add workdir to service description
ppawlowski May 15, 2025
172006b
Add pre-check to Uninstall function
ppawlowski May 16, 2025
a6c1f59
Merge branch 'feat-device-linux-installer' of github.com:FlowFuse/dev…
ppawlowski May 16, 2025
5c07a02
Remove setEnvPath function
ppawlowski May 22, 2025
6abe5e9
Add SetEnvPath and checkEnvPath functions
ppawlowski May 22, 2025
41efa49
Fix PATH value
ppawlowski May 22, 2025
88d6205
Fix PATH, remove unused function
ppawlowski May 22, 2025
0c63747
Add `YesNoPrompt` function
ppawlowski May 26, 2025
dfe75fd
Add `validate` package
ppawlowski May 26, 2025
2618246
Run pre-install validation
ppawlowski May 26, 2025
a8920b1
Merge branch 'feat-device-linux-installer' of github.com:FlowFuse/dev…
ppawlowski May 26, 2025
b3c0f7c
Merge branch 'feat-device-win-installer' of github.com:FlowFuse/devic…
ppawlowski May 26, 2025
8cd206b
Enable custom npm cache dir for linux and darwin
ppawlowski May 27, 2025
b83039a
Ensure proper service file path; fix log dir creation; fix service fi…
ppawlowski May 27, 2025
aee6e50
Remove user homedir on macos; create proper chown command in ExtractT…
ppawlowski May 27, 2025
5e080ee
Add logs rotation for darwin
ppawlowski Jun 1, 2025
e703ea7
Remove workdir content istead of workdir itself
ppawlowski Jun 1, 2025
b0b51ec
Replace build.sh with makefile
ppawlowski Jun 1, 2025
dade8ef
Replace build.sh with makefile
ppawlowski Jun 1, 2025
385c370
Uninstall service on PreInstall
ppawlowski Jun 1, 2025
fc3e94a
Merge branch 'feat-device-linux-installer' of github.com:FlowFuse/dev…
ppawlowski Jun 1, 2025
be0d833
Add windows build to makefile
ppawlowski Jun 1, 2025
cb0fe64
Merge branch 'feat-device-win-installer' of github.com:FlowFuse/devic…
ppawlowski Jun 1, 2025
348bf34
Add darwin build to makefile
ppawlowski Jun 1, 2025
8cc96df
Update gitignore
ppawlowski Jun 2, 2025
6d8e7a9
Update macos build dir
ppawlowski Jun 2, 2025
f1c270e
Add phony targets to makefile
ppawlowski Jun 4, 2025
907e74a
Adjust the order of preinstall check
ppawlowski Jun 4, 2025
3d90268
Adjust error message in `checkWindowsPermissions`
ppawlowski Jun 4, 2025
51033ae
Handle path to `device.yml` properly in `checkConfigFileExists`
ppawlowski Jun 4, 2025
799a92c
Fix fullPath value in `RemoveWorkingDirectory` function
ppawlowski Jun 4, 2025
5cb0e77
Adjust workdir content removal approach for windows in `RemoveWorking…
ppawlowski Jun 4, 2025
0bbb7f0
Simplify windows service removal
ppawlowski Jun 4, 2025
66495ff
Fix typo in build target
ppawlowski Jun 4, 2025
d3c283d
Merge branch 'feat-device-linux-installer' of github.com:FlowFuse/dev…
ppawlowski Jun 4, 2025
44cfbbe
Merge branch 'feat-device-win-installer' of github.com:FlowFuse/devic…
ppawlowski Jun 4, 2025
983ab2a
Simplify binary path declaration in `InstallWindows` function
ppawlowski Jun 4, 2025
cd9b58a
Merge branch 'feat-device-win-installer' of github.com:FlowFuse/devic…
ppawlowski Jun 4, 2025
a445349
Add support for alpine/openrc linux systems
ppawlowski Jun 5, 2025
9b69489
Run config as root to access regular users flows
ppawlowski Jun 5, 2025
bae1fd2
Merge branch 'feat-device-linux-installer' of github.com:FlowFuse/dev…
ppawlowski Jun 5, 2025
20a1bb8
Merge branch 'feat-device-win-installer' of github.com:FlowFuse/devic…
ppawlowski Jun 5, 2025
0957a37
Run checkLibstdcExists on linux only
ppawlowski Jun 6, 2025
41737fc
Merge branch 'feat-device-linux-installer' of github.com:FlowFuse/dev…
ppawlowski Jun 6, 2025
678a0c1
Merge branch 'feat-device-win-installer' of github.com:FlowFuse/devic…
ppawlowski Jun 6, 2025
dccd7eb
Fix permissions set after device configuration
ppawlowski Jun 6, 2025
6d6b541
Merge branch 'feat-device-win-installer' of github.com:FlowFuse/devic…
ppawlowski Jun 6, 2025
9de9084
Improve makefile
ppawlowski Jun 6, 2025
0cdb412
Improve installer readme
ppawlowski Jun 6, 2025
89c21ca
Add macos management commands
ppawlowski Jun 6, 2025
21de8e9
Fix winget link
ppawlowski Jun 6, 2025
eb75033
Simplify build info in readme
ppawlowski Jun 6, 2025
4aebc61
Merge branch 'feat-device-linux-installer' of github.com:FlowFuse/dev…
ppawlowski Jun 6, 2025
83a8be6
Merge branch 'feat-device-win-installer' of github.com:FlowFuse/devic…
ppawlowski Jun 6, 2025
9415eaa
Add git clone command to the readme
ppawlowski Jun 6, 2025
98ca302
Merge branch 'feat-device-linux-installer' of github.com:FlowFuse/dev…
ppawlowski Jun 6, 2025
b685f51
Merge branch 'feat-device-win-installer' of github.com:FlowFuse/devic…
ppawlowski Jun 6, 2025
7718798
Extend installation summary
ppawlowski Jun 17, 2025
fd710cc
Merge branch 'main' of github.com:FlowFuse/device-agent into feat-dev…
ppawlowski Jun 25, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
/node_modules/
var/
/out/
/installer/go/out/
3 changes: 3 additions & 0 deletions installer/go/cmd/install.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,9 @@ func Install(nodeVersion, agentVersion, installerDir string, url string, otc str

logger.Info("FlowFuse Device Agent installation completed successfully!")
logger.Info("The service is now running and will start automatically on system boot.")
logger.Info("You can now return to the FlowFuse platform and start creating Node-RED flows on your device")
logger.Info("For information on how to manage the FlowFuse Device Agent,")
logger.Info(" please refer to the documentation at https://github.com/FlowFuse/device-agent/installer/go/README.md")

logger.LogFunctionExit("Install", "success", nil)
return nil
Expand Down
2 changes: 2 additions & 0 deletions installer/go/makefile
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ build: ## builds the application for all platforms
GOARCH=arm64 GOOS=linux go build -ldflags "-X main.version=${VERSION}" -o ./out/linux/${APP_NAME}-linux-arm64 main.go
GOARCH=arm GOOS=linux go build -ldflags "-X main.version=${VERSION}" -o ./out/linux/${APP_NAME}-linux-arm main.go
GOARCH=amd64 GOOS=windows go build -ldflags "-X main.version=${VERSION}" -o ./out/windows/${APP_NAME}-windows-amd64.exe main.go
GOOS=darwin GOARCH=amd64 go build -ldflags "-X main.version=${VERSION}" -o ./out/macos/${APP_NAME}-macos-amd64 main.go
GOOS=darwin GOARCH=arm64 go build -ldflags "-X main.version=${VERSION}" -o ./out/macos/${APP_NAME}-macos-arm64 main.go

clean: ## cleans the build artifacts
go clean
Expand Down
10 changes: 5 additions & 5 deletions installer/go/pkg/nodejs/deviceagent.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,8 @@ func InstallDeviceAgent(version string, baseDir string) error {
var installCmd *exec.Cmd
npmPrefix := fmt.Sprintf("npm_config_prefix=%s", nodeBaseDir)
switch runtime.GOOS {
case "linux":
installCmd = exec.Command("sudo", "--preserve-env=PATH", "-u", serviceUser, npmBinPath, "install", "-g", packageName)
case "linux", "darwin":
installCmd = exec.Command("sudo", "--preserve-env=PATH", "-u", serviceUser, npmBinPath, "install", "-g", "--cache", filepath.Join(nodeBaseDir, ".npm-cache"), packageName)
env := os.Environ()
installCmd.Env = append(env, npmPrefix, newPath)
case "windows":
Expand Down Expand Up @@ -100,7 +100,7 @@ func UninstallDeviceAgent(baseDir string) error {
var uninstallCmd *exec.Cmd
npmPrefix := fmt.Sprintf("npm_config_prefix=%s", nodeBaseDir)
switch runtime.GOOS {
case "linux":
case "linux", "darwin":
uninstallCmd = exec.Command("sudo", "--preserve-env=PATH", "-u", serviceUser, npmBinPath, "uninstall", "-g", packageName)
env := os.Environ()
uninstallCmd.Env = append(env, npmPrefix, newPath)
Expand Down Expand Up @@ -170,7 +170,7 @@ func ConfigureDeviceAgent(url string, token string, baseDir string) error {
}

// Getting full path to flowfuse-device-agent binary
if runtime.GOOS == "linux" {
if runtime.GOOS == "linux" || runtime.GOOS == "darwin" {
deviceAgentPath = filepath.Join(nodeBinDirPath, "flowfuse-device-agent")
} else {
deviceAgentPath = filepath.Join(nodeBinDirPath, "flowfuse-device-agent.cmd")
Expand All @@ -179,7 +179,7 @@ func ConfigureDeviceAgent(url string, token string, baseDir string) error {
// Create configure command
var configureCmd *exec.Cmd
switch runtime.GOOS {
case "linux":
case "linux", "darwin":
configureCmd = exec.Command("sudo", "--preserve-env=PATH", deviceAgentPath, "-o", token, "-u", url, "--otc-no-start")
env := os.Environ()
configureCmd.Env = append(env, newPath)
Expand Down
8 changes: 5 additions & 3 deletions installer/go/pkg/nodejs/nodejs.go
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ func GetNodeBinDir() string {
// installNodeJs installs the specified version of Node.js.
// It creates the necessary installation directory with appropriate permissions,
// downloads the Node.js binary from the official source, and extracts it.
// On Linux, it uses sudo to create the installation directory and set permissions.
// On Linux and MacOS, it uses sudo to create the installation directory and set permissions.
//
// Parameters:
// - version: The Node.js version to install (e.g., "16.14.2")
Expand All @@ -193,7 +193,7 @@ func installNodeJs(version string) error {
logger.Info("Installing Node.js %s...", version)

// Create the installation directory
if runtime.GOOS == "linux" {
if runtime.GOOS == "linux" || runtime.GOOS == "darwin" {
logger.Debug("Creating directory %s (requires sudo)...", nodeBaseDir)
mkdirCmd := exec.Command("sudo", "mkdir", "-p", nodeBaseDir)
if output, err := mkdirCmd.CombinedOutput(); err != nil {
Expand Down Expand Up @@ -270,6 +270,8 @@ func getNodeDownloadURL(version string) (string, error) {
return fmt.Sprintf("%s/node-v%s-linux-%s.tar.gz", baseUrl, version, arch), nil
case "windows":
return fmt.Sprintf("%s/node-v%s-win-%s.zip", baseUrl, version, arch), nil
case "darwin":
return fmt.Sprintf("%s/node-v%s-darwin-%s.tar.gz", baseUrl, version, arch), nil
default:
return "", fmt.Errorf("unsupported operating system: %s", runtime.GOOS)
}
Expand Down Expand Up @@ -335,7 +337,7 @@ func downloadAndExtractNode(url, version string) error {
}

// Set the correct permissions for executable files
if runtime.GOOS == "linux" {
if runtime.GOOS == "linux" || runtime.GOOS == "darwin" {
logger.Debug("Setting execute permissions for Node.js binaries...")

chownCmd := exec.Command("sudo", "chown", "-R", utils.ServiceUsername, nodeBaseDir)
Expand Down
251 changes: 251 additions & 0 deletions installer/go/pkg/service/darwin.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,251 @@
package service

import (
"fmt"
"os"
"os/exec"
"path/filepath"
"text/template"

"github.com/flowfuse/device-agent-installer/pkg/logger"
"github.com/flowfuse/device-agent-installer/pkg/nodejs"
"github.com/flowfuse/device-agent-installer/pkg/utils"
)

// LaunchdConfig holds the data for the launchd template
type LaunchdConfig struct {
Label string
WorkDir string
LogFile string
ErrorFile string
User string
NodeBinDir string
}

// newsyslogConfig holds the data for the newsyslog configuration
type newsyslogConfig struct {
LogFile string
ErrorFile string
User string
}

// Global variables
const serviceLabel = "com.flowfuse.device-agent"
const serviceFilePath = "/Library/LaunchDaemons/" + serviceLabel + ".plist"

// InstallDarwin installs the service on macOS using launchd
// It creates a plist file in the LaunchDaemons directory and sets the necessary permissions
// It also creates a log directory for the service
//
// Parameters:
// - workDir: The working directory where the service will operate
//
// Returns:
// - error: nil if successful, otherwise an error explaining what went wrong
func InstallDarwin(workDir string) error {
serviceUser := utils.ServiceUsername

// Create the log directory
logDir := filepath.Join(workDir, "logs")
mkdirCmd := exec.Command("sudo", "mkdir", "-p", logDir)
if output, err := mkdirCmd.CombinedOutput(); err != nil {
return fmt.Errorf("failed to create directory %s: %w\nOutput: %s", logDir, err, output)
}
logger.Debug("Setting ownership of %s to %s...", logDir, serviceUser)
chownCmd := exec.Command("sudo", "chown", "-R", serviceUser, logDir)
if output, err := chownCmd.CombinedOutput(); err != nil {
return fmt.Errorf("failed to set logs directory ownership: %w\nOutput: %s", err, output)
}

logFilePath := filepath.Join(logDir, "flowfuse-device-agent.log")
errorLogFilePath := filepath.Join(logDir, "flowfuse-device-agent-error.log")

config := LaunchdConfig{
Label: serviceLabel,
WorkDir: workDir,
LogFile: logFilePath,
ErrorFile: errorLogFilePath,
User: serviceUser,
NodeBinDir: nodejs.GetNodeBinDir(),
}

tmpl, err := template.New("launchd").Parse(launchdTemplate)
if err != nil {
return fmt.Errorf("failed to parse launchd template: %w", err)
}

tmpFile, err := os.CreateTemp("", "flowfuse-service-")
if err != nil {
return fmt.Errorf("failed to create temp file: %w", err)
}
defer os.Remove(tmpFile.Name())

if err := tmpl.Execute(tmpFile, config); err != nil {
return fmt.Errorf("failed to execute service template: %w", err)
}
tmpFile.Close()

copyCmd := exec.Command("sudo", "cp","-X", tmpFile.Name(), serviceFilePath)
if output, err := copyCmd.CombinedOutput(); err != nil {
return fmt.Errorf("failed to copy service file: %w\nOutput: %s", err, output)
}

chownCmd = exec.Command("sudo", "chown", "root:wheel", serviceFilePath)
if output, err := chownCmd.CombinedOutput(); err != nil {
return fmt.Errorf("failed to set service file ownership: %w\nOutput: %s", err, output)
}

chmodCmd := exec.Command("sudo", "chmod", "644", serviceFilePath)
if err := chmodCmd.Run(); err != nil {
return fmt.Errorf("failed to set service file permissions: %w", err)
}

loadCmd := exec.Command("sudo", "launchctl", "load", "-w", serviceFilePath)
if output, err := loadCmd.CombinedOutput(); err != nil {
return fmt.Errorf("failed to load launchd service: %w\nOutput: %s", err, output)
}

createNewsyslogConfig(serviceUser, logFilePath, errorLogFilePath)

return nil
}

// StartDarwin starts the service on macOS
// It uses launchctl to start the service and checks its status
//
// Returns:
// - error: nil if successful, otherwise an error explaining what went wrong
func StartDarwin() error {
startCmd := exec.Command("sudo", "launchctl", "start", serviceLabel)
if output, err := startCmd.CombinedOutput(); err != nil {
logger.Error("Failed to start service: %s", err)
return fmt.Errorf("failed to start service: %w\nOutput: %s", err, output)
}

listCmd := exec.Command("launchctl", "list", serviceLabel)
listOutput, _ := listCmd.CombinedOutput()
logger.Debug("Service status:\n%s", listOutput)

return nil
}

// StopDarwin stops the service on macOS
// It uses launchctl to stop the service
//
// Returns:
// - error: nil if successful, otherwise an error explaining what went wrong
func StopDarwin() error {
stopCmd := exec.Command("sudo", "launchctl", "stop", serviceLabel)
if output, err := stopCmd.CombinedOutput(); err != nil {
logger.Error("Failed to stop service: %s", err)
return fmt.Errorf("failed to stop service: %w\nOutput: %s", err, output)
}
return nil
}

// UninstallDarwin removes the service on macOS
// It stops and unloads the service using launchctl and removes the plist file
//
// Returns:
// - error: nil if successful, otherwise an error explaining what went wrong
func UninstallDarwin() error {
if IsInstalledDarwin() {
StopDarwin()
// Unload the service
unloadCmd := exec.Command("sudo", "launchctl", "unload", "-w", serviceFilePath)
_ = unloadCmd.Run() // Ignore errors
// Remove the service file
removeCmd := exec.Command("sudo", "rm", "-f", serviceFilePath)
if output, err := removeCmd.CombinedOutput(); err != nil {
return fmt.Errorf("failed to remove service file: %w\nOutput: %s", err, output)
}
// Remove the newsyslog configuration file
nsConfFilePath := filepath.Join("/etc/newsyslog.d/", fmt.Sprintf("%s.conf", serviceLabel))
removeCmd = exec.Command("sudo", "rm", "-rf", nsConfFilePath)
if output, err := removeCmd.CombinedOutput(); err != nil {
return fmt.Errorf("failed to remove newsyslog configuration file: %w\nOutput: %s", err, output)
}
}

return nil
}

// IsInstalledDarwin checks if the service is installed on macOS
// It checks if the service is running and if the plist file exists
//
// Returns:
// - bool: true if the service is installed, false otherwise
func IsInstalledDarwin() bool {
listCmd := exec.Command("sudo", "launchctl", "list", serviceLabel)
// Check if service is running
serviceRunning := listCmd.Run() == nil

// Check if service file exists
_, err := os.Stat(serviceFilePath)
fileExists := err == nil

return serviceRunning && fileExists
}

// createNewsyslogConfig creates a configuration file for the newsyslog service
// to manage log rotation for the FlowFuse Device Agent. It generates the configuration
// based on the provided service user, log file, and error file paths, then installs it
// in /etc/newsyslog.d/ with appropriate permissions.
//
// Parameters:
// - serviceUser: The user under which the service runs
// - logFile: Path to the main log file that needs rotation
// - errorFile: Path to the error log file that needs rotation
//
// Returns:
// - error: An error if any step in the process fails, nil on success
func createNewsyslogConfig(serviceUser, logFile, errorFile string) error {
logger.Debug("Creating log files rotation configuration for FlowFuse Device Agent...")

nsDir := "/etc/newsyslog.d/"
if _, err := os.Stat(nsDir); os.IsNotExist(err) {
return fmt.Errorf("%s directory does not exist", nsDir)
}

confFilePath := filepath.Join(nsDir, fmt.Sprintf("%s.conf", serviceLabel))
logger.Debug("Configuration file path: %s", confFilePath)
config := newsyslogConfig{
LogFile: logFile,
ErrorFile: errorFile,
User: serviceUser,
}

tmpl, err := template.New("newsyslog").Parse(newsyslogTemplate)
if err != nil {
return fmt.Errorf("failed to parse newsyslog template: %w", err)
}

tmpFile, err := os.CreateTemp("", "flowfuse-device-agent-ns-conf-")
if err != nil {
return fmt.Errorf("failed to create temp file: %w", err)
}
defer os.Remove(tmpFile.Name())

if err := tmpl.Execute(tmpFile, config); err != nil {
return fmt.Errorf("failed to execute nsconf template: %w", err)
}
tmpFile.Close()

copyCmd := exec.Command("sudo", "cp", "-X", tmpFile.Name(), confFilePath)
if output, err := copyCmd.CombinedOutput(); err != nil {
return fmt.Errorf("failed to copy nsconf file: %w\nOutput: %s", err, output)
}

chownCmd := exec.Command("sudo", "chown", "root:wheel", confFilePath)
if output, err := chownCmd.CombinedOutput(); err != nil {
return fmt.Errorf("failed to set nsconf file ownership: %w\nOutput: %s", err, output)
}

chmodCmd := exec.Command("sudo", "chmod", "644", confFilePath)
if err := chmodCmd.Run(); err != nil {
return fmt.Errorf("failed to set nsconf file permissions: %w", err)
}

logger.Debug("Log files rotation configuration created successfully at %s", confFilePath)
return nil
}
Loading