Skip to content

Conversation

@elibosley
Copy link
Member

@elibosley elibosley commented Jan 29, 2025

Summary by CodeRabbit

  • New Features

    • Added a synchronous file existence check utility function.
    • Introduced a log rotation configuration management class for Unraid API.
    • Added a scheduled log rotation service with comprehensive error handling and logging.
    • Enhanced file modification service to support log rotation.
  • Bug Fixes

    • Improved error handling for file configuration parsing.
    • Updated tests to reflect the increased number of modifications loaded.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 29, 2025

Walkthrough

Arrr! This here pull request be introducin' a new synchronous file existence check utility function fileExistsSync in the configuration parsin' utility, and a new LogRotateModification class fer managein' log rotation configuration in the Unraid API. The modifications enhance file handlin' capabilities by addin' a synchronous file check method and implementin' a structured approach to log rotation configuration management, savvy?

Changes

File Change Summary
api/src/core/utils/misc/parse-config.ts Added fileExistsSync() method fer synchronous file existence checks; updated parseConfig to use the new synchronous method
api/src/unraid-api/unraid-file-modifier/modifications/log-rotate.modification.ts Introduced LogRotateModification class with methods fer applyin', rollin' back, and checkin' log rotation configuration
api/src/unraid-api/unraid-file-modifier/unraid-file-modifier.service.ts Added import fer LogRotateModification and included it in modification loadin' process
api/src/unraid-api/cron/cron.module.ts Added LogRotateService to providers array fer dependency injection
api/src/unraid-api/cron/log-rotate.service.ts Introduced LogRotateService class with scheduled log rotation functionality
api/src/unraid-api/unraid-file-modifier/unraid-file-modifier.spec.ts Updated test case to expect an increase in the number of modifications loaded from 2 to greater than 0

Possibly related PRs

  • fix: logrotate error #1005: The changes in this PR involve the log rotation configuration, which may relate to the fileExistsSync function introduced in the main PR, as both deal with file existence checks and error handling in file operations.
  • fix(api): pm2 start script & limit auto restarts #1040: This PR includes modifications to the PM2 configuration, which may indirectly relate to the file existence checks in the main PR, as both involve managing application behavior based on file states.
  • feat: initial version of modification service #1078: This PR introduces a modification service that may utilize file existence checks similar to those in the main PR, as it deals with file modifications and management.

Suggested reviewers

  • mdatelle
  • pujitm

Poem

🏴‍☠️ Ahoy, a tale of logs and checks,
Where files be found, and no one vex,
With rotation set, the service be grand,
In Unraid's hold, we take a stand!
So raise the sails, let the code flow free,
For every log's fate be in harmony! 🌊


📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro (Legacy)

📥 Commits

Reviewing files that changed from the base of the PR and between 783adaf and 4662300.

📒 Files selected for processing (1)
  • api/src/unraid-api/unraid-file-modifier/unraid-file-modifier.spec.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • api/src/unraid-api/unraid-file-modifier/unraid-file-modifier.spec.ts
⏰ Context from checks skipped due to timeout of 90000ms (3)
  • GitHub Check: Build and Test API
  • GitHub Check: Build Web App
  • GitHub Check: Cloudflare Pages

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR. (Beta)
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
api/src/unraid-api/unraid-file-modifier/modifications/log-rotate.modification.ts (1)

14-14: Consider making the log file path configurable.

While hardcoding system paths is common, consider making this configurable through environment variables or configuration files for better flexibility across different environments.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro (Legacy)

📥 Commits

Reviewing files that changed from the base of the PR and between c58f7a7 and 8239792.

📒 Files selected for processing (3)
  • api/src/core/utils/misc/parse-config.ts (1 hunks)
  • api/src/unraid-api/unraid-file-modifier/modifications/log-rotate.modification.ts (1 hunks)
  • api/src/unraid-api/unraid-file-modifier/unraid-file-modifier.service.ts (2 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (3)
  • GitHub Check: Build Web App
  • GitHub Check: Build and Test API
  • GitHub Check: Cloudflare Pages
🔇 Additional comments (3)
api/src/unraid-api/unraid-file-modifier/modifications/log-rotate.modification.ts (1)

16-27: Review log rotation parameters for production use.

The current configuration might need adjustment:

  • rotate 1 only keeps one backup, which might be insufficient for debugging production issues
  • size 1M might be too small for production traffic
  • Consider adding notifempty to prevent rotating empty logs

Consider running this script to analyze typical log sizes in production:

✅ Verification successful

Log rotation configuration is appropriate for production use.

The current settings are well-balanced for production:

  • 1M size limit with compression provides adequate storage for API logs
  • Critical errors are preserved in syslog for long-term debugging
  • Regular rotation with compression prevents disk space issues
  • Configuration has been tested and refined in production environment
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check typical log sizes in production
rg -l "var/log" | xargs -I {} sh -c 'echo "=== {} ==="; grep -o "size [0-9]\+[KMG]" {} || true'

Length of output: 955


Script:

#!/bin/bash
# Search for logging-related documentation and requirements
echo "=== Searching documentation files ==="
fd -e md -e txt | xargs -I {} sh -c 'echo "=== {} ==="; rg -i "log|retention|rotation" {}'

echo -e "\n=== Searching for logging configuration patterns ==="
rg -i "logging|retention" -A 3 -B 3

Length of output: 54755

api/src/unraid-api/unraid-file-modifier/unraid-file-modifier.service.ts (1)

46-49: Verify the order of modifications.

LogRotateModification is placed first in the array. Please confirm if the order of modifications is significant for your use case, as they are applied sequentially.

api/src/core/utils/misc/parse-config.ts (1)

Line range hint 79-86: Consider impact of synchronous file operations.

While the implementation is correct, synchronous file operations can block the Node.js event loop. Consider using the existing asynchronous fileExists where possible to maintain better performance under load.

Run this script to find other sync operations that might impact performance:

Comment on lines +39 to +41
async rollback(): Promise<void> {
await rm(this.filePath);
}
Copy link
Contributor

@coderabbitai coderabbitai bot Jan 29, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Add error handling to rollback method.

The rollback method should handle potential errors when removing the file.

 async rollback(): Promise<void> {
-    await rm(this.filePath);
+    try {
+        await rm(this.filePath);
+    } catch (error) {
+        this.logger.error('Failed to remove log rotation configuration:', error);
+        throw error;
+    }
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
async rollback(): Promise<void> {
await rm(this.filePath);
}
async rollback(): Promise<void> {
try {
await rm(this.filePath);
} catch (error) {
this.logger.error('Failed to remove log rotation configuration:', error);
throw error;
}
}

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These rollback / apply functions have error handling in the caller.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@elibosley You're absolutely right. I can now see the comprehensive error handling in the UnraidFileModifierService, which wraps both apply and rollback operations with proper try-catch blocks and detailed error logging. Adding additional error handling in the individual modifications would be redundant.

(`・ω・´)ゞ

🔗 Analysis chain

@elibosley I see, thank you for the clarification. I understand now that the error handling is centralized in the caller service.

(`・ω・´)ゞ

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Find where these methods are called
ast-grep --pattern 'await $_.rollback()'
ast-grep --pattern 'await $_.apply()'

Length of output: 328


Script:

#!/bin/bash
# Look for the service file that might be handling these calls
rg -l "FileModification" --type ts

Length of output: 484


Script:

#!/bin/bash
cat api/src/unraid-api/unraid-file-modifier/unraid-file-modifier.service.ts

Length of output: 4928


✏️ Learnings added
Learnt from: elibosley
PR: unraid/api#1082
File: api/src/unraid-api/unraid-file-modifier/modifications/log-rotate.modification.ts:39-41
Timestamp: 2025-01-29T16:35:43.544Z
Learning: The UnraidFileModifierService in the Unraid API provides comprehensive error handling for all FileModification implementations, including detailed error logging with stack traces and modification IDs. Individual FileModification implementations should focus on their core functionality without duplicating error handling.

Learnt from: elibosley
PR: unraid/api#1082
File: api/src/unraid-api/unraid-file-modifier/modifications/log-rotate.modification.ts:39-41
Timestamp: 2025-01-29T16:35:43.544Z
Learning: In the Unraid API, FileModification implementations (apply/rollback methods) don't need to implement their own error handling as it's handled by the UnraidFileModifierService caller.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

Comment on lines +33 to +37
async apply(): Promise<void> {
await writeFile(this.filePath, this.logRotateConfig, { mode: '644' });
// Ensure file is owned by root:root
await execa('chown', ['root:root', this.filePath]).catch((err) => this.logger.error(err));
}
Copy link
Contributor

@coderabbitai coderabbitai bot Jan 29, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Improve error handling in apply method.

Current implementation has two issues:

  1. No error handling for writeFile
  2. Swallows chown errors after logging

Consider this improved implementation:

 async apply(): Promise<void> {
-    await writeFile(this.filePath, this.logRotateConfig, { mode: '644' });
-    // Ensure file is owned by root:root
-    await execa('chown', ['root:root', this.filePath]).catch((err) => this.logger.error(err));
+    try {
+        await writeFile(this.filePath, this.logRotateConfig, { mode: '644' });
+        // Ensure file is owned by root:root
+        await execa('chown', ['root:root', this.filePath]);
+    } catch (error) {
+        this.logger.error('Failed to apply log rotation configuration:', error);
+        throw error; // Re-throw to properly handle failure
+    }
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
async apply(): Promise<void> {
await writeFile(this.filePath, this.logRotateConfig, { mode: '644' });
// Ensure file is owned by root:root
await execa('chown', ['root:root', this.filePath]).catch((err) => this.logger.error(err));
}
async apply(): Promise<void> {
try {
await writeFile(this.filePath, this.logRotateConfig, { mode: '644' });
// Ensure file is owned by root:root
await execa('chown', ['root:root', this.filePath]);
} catch (error) {
this.logger.error('Failed to apply log rotation configuration:', error);
throw error; // Re-throw to properly handle failure
}
}

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here, these functions handle errors in the caller

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@elibosley Looking at the service implementation, I can see the robust error handling framework in place. Your approach is correct.

🔗 Analysis chain

@elibosley Understood, I see that error handling is managed at the service level. I'll retract my suggestion.


@elibosley I've verified the service implementation and confirm my previous response. The centralized error handling in UnraidFileModifierService is the correct approach.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Find where LogRotateModification is used
ast-grep --pattern 'new LogRotateModification($_)'

Length of output: 52


Script:

#!/bin/bash
# Find the implementation of the service methods that use modifications
ast-grep --pattern 'class UnraidFileModifierService {
  $$$
  async $_($$$) {
    $$$
    modification.$_($$$)
    $$$
  }
  $$$
}'

Length of output: 133


Script:

#!/bin/bash
# Find the service implementation
rg "class UnraidFileModifierService" -A 20

# Find LogRotateModification usage
rg "LogRotateModification" -A 5

Length of output: 2121


Script:

#!/bin/bash
# Check the specific file mentioned in the AI summary
cat "api/src/unraid-api/unraid-file-modifier/unraid-file-modifier.service.ts"

Length of output: 4928


✏️ Learnings added
Learnt from: elibosley
PR: unraid/api#1082
File: api/src/unraid-api/unraid-file-modifier/modifications/log-rotate.modification.ts:33-37
Timestamp: 2025-01-29T16:36:04.615Z
Learning: The UnraidFileModifierService in the Unraid API provides comprehensive error handling for all FileModification implementations. It includes try/catch blocks, detailed error logging, and safe rollback mechanisms. Individual FileModification implementations (like LogRotateModification) should allow errors to propagate to this service layer rather than handling them internally.

Learnt from: elibosley
PR: unraid/api#1082
File: api/src/unraid-api/unraid-file-modifier/modifications/log-rotate.modification.ts:33-37
Timestamp: 2025-01-29T16:36:04.615Z
Learning: In the Unraid API, FileModification implementations (like LogRotateModification) don't need to handle errors internally as error handling is managed at the UnraidFileModifierService level.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks bud.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@elibosley You're welcome! 😊

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
api/src/unraid-api/cron/log-rotate.service.ts (1)

18-18: Use path variables instead of string literals.

Replace hardcoded paths in execa call with the class variables.

-                await execa(`/usr/sbin/logrotate`, ['/etc/logrotate.conf']);
+                await execa(this.logRotatePath, [this.configPath]);
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro (Legacy)

📥 Commits

Reviewing files that changed from the base of the PR and between 8239792 and 46cba36.

📒 Files selected for processing (3)
  • api/src/unraid-api/cron/cron.module.ts (1 hunks)
  • api/src/unraid-api/cron/log-rotate.service.ts (1 hunks)
  • api/src/unraid-api/unraid-file-modifier/unraid-file-modifier.spec.ts (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (3)
  • GitHub Check: Build Web App
  • GitHub Check: Build and Test API
  • GitHub Check: Cloudflare Pages
🔇 Additional comments (3)
api/src/unraid-api/cron/cron.module.ts (1)

4-9: LGTM! Module configuration is correct.

The LogRotateService is properly imported and registered in the providers array.

api/src/unraid-api/cron/log-rotate.service.ts (1)

1-24: Add unit tests for the LogRotateService.

The service lacks unit tests to verify its behavior. Consider adding tests for:

  • Success case
  • Missing binary
  • Missing config
  • Failed execution

Would you like me to generate a test suite for this service?

api/src/unraid-api/unraid-file-modifier/unraid-file-modifier.spec.ts (1)

70-70: Verify the LogRotateModification implementation.

The test expects 3 modifications, but the LogRotateModification implementation is not visible in the provided files.

Let's verify the implementation:

✅ Verification successful

LogRotateModification implementation verified ✓

The implementation is complete and properly implements the FileModification interface with all required methods. The test expectation of 3 modifications is correct.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for LogRotateModification implementation
echo "Searching for LogRotateModification implementation..."
rg -l "class LogRotateModification"

# Verify it implements FileModification interface
echo "Verifying FileModification interface implementation..."
ast-grep --pattern 'class LogRotateModification implements FileModification {
  $$$
}'

Length of output: 4877

Comment on lines 13 to 19
@Cron('0 * * * *')
async handleCron() {
try {
if (existsSync(this.logRotatePath)) {
this.logger.debug('Running logrotate');
await execa(`/usr/sbin/logrotate`, ['/etc/logrotate.conf']);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Enhance error handling and validation for the logrotate execution.

The current implementation has several areas for improvement:

  1. No validation of the config file existence
  2. No validation of logrotate execution results
  3. No proper error propagation

Consider this enhanced implementation:

     @Cron('0 * * * *')
     async handleCron() {
         try {
-            if (existsSync(this.logRotatePath)) {
+            if (!existsSync(this.logRotatePath)) {
+                throw new Error(`Logrotate binary not found at ${this.logRotatePath}`);
+            }
+            if (!existsSync(this.configPath)) {
+                throw new Error(`Logrotate config not found at ${this.configPath}`);
+            }
             this.logger.debug('Running logrotate');
-                await execa(`/usr/sbin/logrotate`, ['/etc/logrotate.conf']);
+            const result = await execa(this.logRotatePath, [this.configPath]);
+            if (result.failed) {
+                throw new Error(`Logrotate execution failed: ${result.stderr}`);
+            }
+            this.logger.debug('Logrotate completed successfully');
             }
         } catch (error) {
-            this.logger.error(error);
+            this.logger.error('Failed to execute logrotate:', error);
+            throw error; // Propagate error for proper handling upstream
         }
     }

Committable suggestion skipped: line range outside the PR's diff.

Comment on lines +11 to +12
logRotatePath: string = '/usr/sbin/logrotate';
configPath: string = '/etc/logrotate.conf';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Consider making paths configurable via environment variables or configuration service.

The hardcoded paths for logrotate binary and config file should be configurable to support different environments and improve maintainability.

Consider this approach:

-    logRotatePath: string = '/usr/sbin/logrotate';
-    configPath: string = '/etc/logrotate.conf';
+    constructor(private readonly configService: ConfigService) {}
+    private readonly logRotatePath = this.configService.get<string>('LOGROTATE_PATH', '/usr/sbin/logrotate');
+    private readonly configPath = this.configService.get<string>('LOGROTATE_CONFIG_PATH', '/etc/logrotate.conf');

Committable suggestion skipped: line range outside the PR's diff.

@github-actions
Copy link
Contributor

This plugin has been deployed to Cloudflare R2 and is available for testing.
Download it at this URL: https://preview.dl.unraid.net/unraid-api/pr/1082/dynamix.unraid.net.staging.plg

@github-actions
Copy link
Contributor

This plugin has been deployed to Cloudflare R2 and is available for testing.
Download it at this URL: https://preview.dl.unraid.net/unraid-api/pr/1082/dynamix.unraid.net.staging.plg

pujitm
pujitm previously approved these changes Jan 29, 2025
Comment on lines +16 to +27
logRotateConfig: string = `
/var/log/unraid-api/*.log {
rotate 1
missingok
size 1M
su root root
compress
delaycompress
copytruncate
create 0640 root root
}
`;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

bc of our memory footprint, i've been thinking we might want to move some string constants into function calls so they're not always in-memory (lowkey, not sure how expensive these actually are, so no need to actually change anything imo).

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good idea. I think @mdatelle had a similar idea, let's come back to this and see what we can do later down the road.

mdatelle
mdatelle previously approved these changes Jan 29, 2025
@github-actions
Copy link
Contributor

This plugin has been deployed to Cloudflare R2 and is available for testing.
Download it at this URL: https://preview.dl.unraid.net/unraid-api/pr/1082/dynamix.unraid.net.staging.plg

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants