Skip to content

Conversation

@creativeprojects
Copy link
Owner

Remove existing systemd schedule when the unit type differs (user -> system or system -> user)

This is because of the introduction of the user_logged_on permission for systemd scheduling.

@coderabbitai
Copy link

coderabbitai bot commented Mar 30, 2025

Walkthrough

The changes update the link checker’s command parameters in GitHub Actions workflows and the Makefile, modifying connection limits and the User-Agent string. Additionally, the backup configuration in the example has been enhanced with a network check. Permission checks in various scheduling handlers have been refactored to use a new IsRoot() method. Significant refactoring occurs within the systemd package by encapsulating filesystem and user logic in a new Unit struct—with corresponding updates in unit generation, reading, drop-in file management, and tests. Finally, the user package receives a new method to determine root privileges.

Changes

File(s) Change Summary
.github/workflows/doc.yml, .github/workflows/release-doc.yml, Makefile Update link checker parameters: replace --max-connections with --max-connections-per-host=8, change the User-Agent from curl to Muffet, and adjust the exclusion list.
examples/linux.yaml Add backup scheduling directives: a commented systemd-drop-in-files line and an after-network-online: true flag to ensure the backup runs only after network availability.
schedule/handler_crond.go, schedule/handler_darwin.go, schedule/handler_systemd.go Refactor permission checks by utilising user.IsRoot(); in handler_systemd.go, add logic to manage duplicate units and streamline user context handling.
systemd/drop_ins.go, systemd/generate.go, systemd/read.go, systemd/generate_test.go, systemd/read_test.go Refactor systemd unit operations into methods on a new Unit struct that encapsulates filesystem (afero.Fs) and user data; update associated tests accordingly.
user/user.go Add a new IsRoot() method to the User struct to determine root privileges.

Sequence Diagram(s)

sequenceDiagram
    participant D as Developer
    participant U as Unit Instance
    participant FS as FileSystem (afero.Fs)
    participant user as User

    D->>U: NewUnit(user)
    D->>U: Generate(config)
    U->>U: Call GetUserDir()
    U->>U: Load template (loadTemplate)
    U->>FS: Write unit file
    FS-->>U: File written confirmation
    U-->>D: Return success
Loading

Possibly related PRs

  • creativeprojects/resticprofile#425: Updates link checking parameters in GitHub Actions workflows, similar to the modifications made here.

Suggested labels

enhancement, bug

✨ Finishing Touches
  • 📝 Generate Docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 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.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai plan to trigger planning for file edits and PR creation.
  • @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

@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: 1

🧹 Nitpick comments (1)
.github/workflows/release-doc.yml (1)

52-52: Updated broken links command parameters.

The cmd_params string now increases the connection limit to 8 per host and updates the User-Agent to "Muffet/2.10.8". Please check that the inclusion of --skip-tls-verification is intentional and that these settings meet your project's network testing requirements.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ac3b810 and f236cae.

📒 Files selected for processing (13)
  • .github/workflows/doc.yml (1 hunks)
  • .github/workflows/release-doc.yml (1 hunks)
  • Makefile (1 hunks)
  • examples/linux.yaml (1 hunks)
  • schedule/handler_crond.go (1 hunks)
  • schedule/handler_darwin.go (1 hunks)
  • schedule/handler_systemd.go (6 hunks)
  • systemd/drop_ins.go (5 hunks)
  • systemd/generate.go (6 hunks)
  • systemd/generate_test.go (22 hunks)
  • systemd/read.go (2 hunks)
  • systemd/read_test.go (7 hunks)
  • user/user.go (1 hunks)
🧰 Additional context used
🧬 Code Definitions (2)
systemd/generate_test.go (3)
user/user.go (1)
  • User (3-9)
systemd/generate.go (3)
  • GetSystemDir (257-259)
  • Unit (115-118)
  • Config (93-113)
schedule/config.go (1)
  • Config (10-27)
schedule/handler_systemd.go (4)
user/user_windows.go (1)
  • Current (9-23)
systemd/generate.go (5)
  • UserUnit (70-70)
  • SystemUnit (71-71)
  • NewUnit (120-125)
  • Config (93-113)
  • Unit (115-118)
schedule/permission.go (2)
  • PermissionFromConfig (16-30)
  • Permission (7-7)
schedule/config.go (1)
  • Config (10-27)
🪛 golangci-lint (1.64.8)
systemd/generate.go

234-234: Error return value of afero.Walk is not checked

(errcheck)

🪛 GitHub Check: Build and test (1.24, ubuntu-latest)
systemd/generate.go

[failure] 234-234:
Error return value of afero.Walk is not checked (errcheck)

🪛 GitHub Actions: Build
systemd/generate.go

[error] 234-234: Error return value of afero.Walk is not checked (errcheck)

🔇 Additional comments (31)
Makefile (1)

301-304: Refined checklinks target in Makefile.

The updated checklinks rule uses muffet with a stricter limit of 8 connections per host and dynamically injects the muffet version into the User-Agent header using $$(muffet --version). Please verify that this dynamic header substitution consistently yields the expected format in your CI environment, ensuring no unexpected whitespace or format issues arise.

.github/workflows/doc.yml (1)

58-58: Enhanced link checker invocation in workflow.

The command parameters have been modified to exclude specific domains, set a fixed buffer size and timeout, and adopt the new connection and header settings. Note the addition of --color=always—please ensure that forcing coloured output does not lead to undesired artefacts when run in a headless CI context.

user/user.go (1)

11-15: Well-designed method for checking root privileges.

This new IsRoot() method centralizes the logic for determining if a user has root privileges, making the code more maintainable and consistent across the codebase. The method checks both the SudoRoot flag and the user ID, covering all cases where a user might have elevated privileges.

schedule/handler_darwin.go (1)

271-271: Good refactoring to use the new IsRoot() method.

Replacing the direct check of SudoRoot and Uid == 0 with the new IsRoot() method improves code readability and ensures consistent privilege checking across the codebase.

examples/linux.yaml (2)

101-101: Example drop-in configuration for systemd.

This commented example demonstrates how to specify systemd drop-in files, which is helpful for users who need to customize systemd unit files.


108-108: Good addition of network check for backup scheduling.

Adding after-network-online: true ensures that backup operations only proceed when network connectivity is available, which is crucial for remote repository backups. This prevents failed backup attempts when the network is unavailable.

schedule/handler_crond.go (1)

205-205: Good refactoring to use the new IsRoot() method.

Replacing the direct check of SudoRoot and Uid == 0 with the new IsRoot() method improves code readability and ensures consistent privilege checking across the codebase.

systemd/drop_ins.go (2)

26-38: Encapsulated check for timer drop-in looks fine.
No issues spotted; the method neatly switches to using u.fs.Open with proper error handling.


40-100: Well-structured drop-in creation method.
The usage of u.fs for file operations is clear and consistent. Error handling on directory creation, reading, and removal is suitably done.

systemd/read.go (2)

19-59: Instance-based read method is coherent.
Migrating to a receiver method allows better encapsulation. The error propagation and validation for .service suffix appear correct.


63-99: Reading systemd unit content now leverages u.fs.
Using afero.ReadFile aligns well with the new struct-based approach. No immediate concerns noted.

systemd/read_test.go (1)

10-183: Enhanced test coverage for different users and working directories.
Adopting the Unit{fs: fs, user: ...} approach is consistent with the refactoring. Parallel tests and updated environment usage show good practice.

systemd/generate.go (3)

115-124: Introducing the Unit struct is a logical design enhancement.
Bundling fs and user neatly clarifies ownership and file operations.


128-229: Systemd unit generation method is functionally correct.
The changes unify filesystem interaction and user details in a neat, object-oriented style. No major performance or security concerns identified.


283-301: Loading templates from the filesystem is well-structured.
The fallback to default templates if filename is empty is clear, and using u.fs.Open suits the new approach.

systemd/generate_test.go (8)

16-20: Good addition of test user objects for improved test clarity.

These user objects make test scenarios more explicit and provide clear differentiation between standard user, sudo user, and root user contexts, which is essential for testing systemd scheduling permissions properly.


23-24: Nice improvement with test parallelization and isolated file systems.

Making tests parallel and giving each test its own isolated filesystem improves test performance and eliminates potential state interference between tests.


30-32: Good refactoring to use explicit filesystem references.

The test now properly references the in-memory filesystem for assertions, making the relationship between the test filesystem and assertions clear.


33-44: Clean implementation of Unit struct usage.

The change to use the Unit struct with its filesystem and user parameters encapsulates the dependencies well and follows good object-oriented design principles.


71-83: Explicit user assignment in test configuration.

Good addition of the test user in the Generate call, and explicitly setting the username in the configuration. This makes the test more self-contained and explicit about its test scenario.


90-90: Clear home directory reference using test user.

Using the test user's HomeDir property instead of os.UserHomeDir() makes the test more deterministic and independent of the actual system environment.


117-117: Well-structured user directory path construction.

The test correctly builds the systemd user directory path based on the test user's home directory, which aligns with how systemd locates user unit files.


614-619: Good helper function refactoring for file assertions.

The helper functions for file assertions are well-designed with proper error handling and descriptive failure messages, making test failures more informative.

schedule/handler_systemd.go (8)

111-112: Improved user handling with dedicated variable.

Using a dedicated variable for the current user improves readability and allows for consistent user context throughout the function.


118-134: Important permission transition handling to prevent duplicate units.

This crucial addition checks for and removes existing units when changing permission types (between user and system). This prevents the confusing situation where the same job might have multiple units with different permissions.

The PR objective is well-implemented here, handling the scenario where permission changes from user to system or vice versa. This code ensures clean transitions between unit types, which is especially important with the new user_logged_on permission.


136-137: Good refactoring to use the Unit abstraction.

Using systemd.NewUnit(u) leverages the new Unit struct abstraction, which properly encapsulates filesystem and user context for systemd operations.


192-194: Consistent user handling in RemoveJob.

Good consistency in user handling between CreateJob and RemoveJob methods, using the same pattern of obtaining the current user and determining the unit type based on permissions.


220-220: Proper use of Unit's GetUserDir method.

Using the unit's GetUserDir method instead of a direct systemd call follows the encapsulation pattern established by the Unit struct.


266-271: Sensible handling of root user with user units.

This special case handles a limitation in journalctl where it doesn't accept the "-M user@" parameter when running as root. The warning message helps users understand why they might not see logs.


324-324: Simplified permission check using IsRoot.

Using the user.IsRoot() method instead of the more verbose condition improves code clarity and maintainability.


506-506: Consistent Unit usage in config reading.

Using systemd.NewUnit(user.Current()).Read() follows the same pattern as elsewhere in the code, ensuring consistent handling of user context when reading unit configurations.

Comment on lines 234 to 240
afero.Walk(u.fs, dropInDir, func(path string, info fs.FileInfo, err error) error {
if err != nil {
return err
}
_ = u.fs.Chown(path, u.user.Uid, u.user.Gid)
return nil
})
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Handle the return value from afero.Walk to prevent pipeline failures.
Currently, the returned error is ignored, causing a lint and build failure. A simple fix is to check this error and propagate it:

- afero.Walk(u.fs, dropInDir, func(path string, info fs.FileInfo, err error) error {
+ if err := afero.Walk(u.fs, dropInDir, func(path string, info fs.FileInfo, err error) error {
    if err != nil {
        return err
    }
    _ = u.fs.Chown(path, u.user.Uid, u.user.Gid)
    return nil
- })
+ }); err != nil {
+    return err
+ }
📝 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
afero.Walk(u.fs, dropInDir, func(path string, info fs.FileInfo, err error) error {
if err != nil {
return err
}
_ = u.fs.Chown(path, u.user.Uid, u.user.Gid)
return nil
})
if err := afero.Walk(u.fs, dropInDir, func(path string, info fs.FileInfo, err error) error {
if err != nil {
return err
}
_ = u.fs.Chown(path, u.user.Uid, u.user.Gid)
return nil
}); err != nil {
return err
}
🧰 Tools
🪛 golangci-lint (1.64.8)

234-234: Error return value of afero.Walk is not checked

(errcheck)

🪛 GitHub Check: Build and test (1.24, ubuntu-latest)

[failure] 234-234:
Error return value of afero.Walk is not checked (errcheck)

🪛 GitHub Actions: Build

[error] 234-234: Error return value of afero.Walk is not checked (errcheck)

Copy link

@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: 0

♻️ Duplicate comments (1)
systemd/generate.go (1)

234-240: ⚠️ Potential issue

Missing error handling for afero.Walk

The error returned from the afero.Walk function is not checked, which could lead to silent failures during directory traversal.

Apply this fix:

- _ = afero.Walk(u.fs, dropInDir, func(path string, info fs.FileInfo, err error) error {
+ if err := afero.Walk(u.fs, dropInDir, func(path string, info fs.FileInfo, err error) error {
  if err != nil {
    return err
  }
  _ = u.fs.Chown(path, u.user.Uid, u.user.Gid)
  return nil
- })
+ }); err != nil {
+   return err
+ }
🧹 Nitpick comments (1)
systemd/generate.go (1)

198-198: Consider logging Chown errors

The code ignores errors from u.fs.Chown calls. While these errors might not be critical enough to halt execution, logging them would provide useful debugging information.

- _ = u.fs.Chown(filePathName, u.user.Uid, u.user.Gid)
+ if err := u.fs.Chown(filePathName, u.user.Uid, u.user.Gid); err != nil {
+   clog.Debugf("failed to change ownership of %s: %v", filePathName, err)
+ }

Also applies to: 220-220, 238-238

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f236cae and e5238e1.

📒 Files selected for processing (1)
  • systemd/generate.go (6 hunks)
🧰 Additional context used
🧬 Code Definitions (1)
systemd/generate.go (3)
user/user.go (1)
  • User (3-9)
schedule/config.go (1)
  • Config (10-27)
util/collect/collect.go (2)
  • All (7-14)
  • Not (17-20)
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: Build and test (1.24, windows-latest)
🔇 Additional comments (9)
systemd/generate.go (9)

115-118: Good structuring with the new Unit type

The introduction of a Unit struct encapsulating filesystem operations and user information is a solid refactoring. This improves testability and maintainability by reducing reliance on global state.


120-125: Constructor follows Go idiomatic patterns

The NewUnit constructor properly initializes the struct with the OS filesystem and user information. This is the recommended pattern for creating new instances in Go.


142-152: Environment variable handling improved

The environment handling is now more explicit and controlled. Setting HOME based on the user's home directory ensures consistent behaviour across different execution contexts.


196-199: File ownership management for user units

The file ownership management when creating user units with sudo privileges is a good security practice, ensuring that the files are owned by the original user rather than root.

Also applies to: 218-221, 232-241


224-225: Usage of collect package is elegant

Using the collect package with its generic functions for filtering drop-in files is a clean, functional approach that improves code readability.


248-254: GetUserDir method refactoring

Moving GetUserDir to a method on Unit aligns with the overall refactoring pattern and allows it to use the Unit's internal state for user directory resolution.


257-259: Simple accessor for system directory

The new GetSystemDir function provides a clean interface for accessing the system directory, consistent with the package's design.


283-300: Template loading encapsulation

Refactoring loadTemplate as a method on Unit aligns with the overall design approach and enables cleaner testing through dependency injection.


229-229:

Details

✅ Verification successful

Missing createDropIns method definition

The code calls u.createDropIns but I don't see its definition in this file. Ensure this method is properly defined elsewhere in the package.

Run this script to verify the method exists:


🏁 Script executed:

#!/bin/bash
# Search for the createDropIns method definition
rg -i --type go "func \(\w+ Unit\) createDropIns" --no-filename
echo "-----"
# Check if the method is used in other files
rg -i --type go "createDropIns" --no-filename

Length of output: 323


Verified: The createDropIns method is defined

Upon verification, I confirmed that the createDropIns method is indeed defined as shown below:

func (u Unit) createDropIns(dir string, files []string) error {

No further action is required.

@codecov
Copy link

codecov bot commented Mar 30, 2025

Codecov Report

Attention: Patch coverage is 49.46237% with 47 lines in your changes missing coverage. Please review.

Project coverage is 79.22%. Comparing base (ac3b810) to head (e5238e1).
Report is 1 commits behind head on master.

Files with missing lines Patch % Lines
systemd/generate.go 50.00% 18 Missing and 5 partials ⚠️
schedule/handler_systemd.go 34.48% 17 Missing and 2 partials ⚠️
systemd/drop_ins.go 75.00% 0 Missing and 2 partials ⚠️
user/user.go 0.00% 2 Missing ⚠️
schedule/handler_darwin.go 0.00% 1 Missing ⚠️

❌ Your patch status has failed because the patch coverage (49.46%) is below the target coverage (70.00%). You can increase the patch coverage or adjust the target coverage.

Additional details and impacted files
@@            Coverage Diff             @@
##           master     #476      +/-   ##
==========================================
- Coverage   79.46%   79.22%   -0.24%     
==========================================
  Files         132      133       +1     
  Lines       13169    13204      +35     
==========================================
- Hits        10464    10460       -4     
- Misses       2292     2326      +34     
- Partials      413      418       +5     
Flag Coverage Δ
unittests 79.22% <49.46%> (-0.24%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@creativeprojects creativeprojects merged commit e490c42 into master Mar 30, 2025
8 of 9 checks passed
@creativeprojects creativeprojects deleted the set-schedule-permissions branch March 30, 2025 14:01
@creativeprojects creativeprojects added this to the v0.30.0 milestone Mar 30, 2025
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.

2 participants