Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add DBus node for swap size and swappiness config #222

Merged
merged 2 commits into from
Feb 28, 2025
Merged

Conversation

sairon
Copy link
Member

@sairon sairon commented Feb 28, 2025

Add /io/hass/os/Config/Swap object that exposes interface with properties controlling the swap size and swappiness configuration added in [1] and [2]. There are no checks whether this supported on the target system (while swappiness would probably have effect also on Supervised installs, swap size can't be controlled there). These checks should be implemented on upper layer (i.e. Supervisor).

[1] home-assistant/operating-system#3882
[2] home-assistant/operating-system#3884

Summary by CodeRabbit

  • New Features
    • Introduced a user interface for configuring system swap settings, allowing you to view and adjust memory swap size and swappiness parameters with enhanced validation.
  • Refactor
    • Streamlined service initialization by separating swap configuration and time synchronization into distinct modules for improved stability and modularity.

@sairon sairon requested a review from agners February 28, 2025 10:12
Copy link

coderabbitai bot commented Feb 28, 2025

📝 Walkthrough

Walkthrough

The changes introduce a new D-Bus interface for managing swap configuration in the system via the file config/swap/swap.go. This file defines methods to get and set swap size and swappiness, including necessary validation and error handling. Additionally, the package in config/timesyncd/timesyncd.go is renamed from config to timesyncd, and the main.go file is updated to import and initialize the new swap and timesyncd D-Bus services instead of the original config package.

Changes

File Change Summary
config/swap/swap.go Implements a new D-Bus interface for swap management. Adds methods: InitializeDBus, getSwapSize, getSwappiness, setSwapSize, and setSwappiness, and introduces a swap struct. Includes input validation, default value handling, and error reporting via D-Bus.
config/timesyncd/timesyncd.go Renames the package from config to timesyncd, changing the namespace under which the file is organized.
main.go Updates import statements by removing config and adding swap and timesyncd. Replaces the call from config.InitializeDBus(conn) to calling swap.InitializeDBus(conn) and timesyncd.InitializeDBus(conn) to initialize the corresponding D-Bus services.

Sequence Diagram(s)

sequenceDiagram
    participant Main
    participant DBus
    participant SwapService
    participant TimesyncdService

    Main->>DBus: Create DBus connection
    Main->>SwapService: InitializeDBus(conn)
    SwapService->>DBus: Export swap interface
    Main->>TimesyncdService: InitializeDBus(conn)
    TimesyncdService->>DBus: Export timesyncd interface
Loading
sequenceDiagram
    participant Client
    participant SwapService
    participant ConfigFile

    Client->>SwapService: Invoke get/set swap method
    alt Get Request
        SwapService->>ConfigFile: Read swap size/swappiness
        ConfigFile-->>SwapService: Return current value
        SwapService-->>Client: Return value
    else Set Request
        SwapService->>ConfigFile: Validate and update value
        alt Validation Succeeds
            ConfigFile-->>SwapService: Confirm update
            SwapService-->>Client: Return success
        else Validation Fails
            SwapService-->>Client: Return D-Bus error
        end
    end
Loading

Warning

There were issues while running some tools. Please review the errors and either fix the tool’s configuration or disable the tool if it’s a critical failure.

🔧 golangci-lint (1.62.2)

Error: can't load config: the Go language version (go1.23) used to build golangci-lint is lower than the targeted Go version (1.24)
Failed executing command with error: can't load config: the Go language version (go1.23) used to build golangci-lint is lower than the targeted Go version (1.24)

✨ Finishing Touches
  • 📝 Generate Docstrings

Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ 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 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 or @coderabbitai title 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: 0

🧹 Nitpick comments (4)
main.go (1)

75-76: Consider adding error handling or logging here.
Calls to swap.InitializeDBus(conn) and timesyncd.InitializeDBus(conn) do not check for errors. If these initialization calls fail, the application will proceed without clear feedback. It’s often best practice to log or handle potential errors here.

config/swap/swap.go (3)

29-32: Be mindful of concurrency with shared fields.
The swap struct references global variables optSwapSize and optSwappiness. If multiple DBus calls or threads were to change these properties in quick succession, it might create race conditions. Consider using localized fields or synchronization primitives to ensure thread safety.


64-83: Ensure comprehensive swap size validation.
While the current regex covers typical patterns, users might provide unorthodox formats (e.g., “1024kB”). Ensure the regex and subsequent checks explicitly document which formats are supported to reduce confusion.


105-160: Consider system capability checks.
This DBus node attempts to configure swap size and swappiness. If the environment doesn’t support changing swap size (e.g., some Supervised installs), you might log a message or return an error if you detect unsupported behavior. This aligns with the PR note that such checks could be handled elsewhere, but a warning log might prevent confusion.

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 856e157 and 1708e8b.

📒 Files selected for processing (3)
  • config/swap/swap.go (1 hunks)
  • config/timesyncd/timesyncd.go (1 hunks)
  • main.go (2 hunks)
✅ Files skipped from review due to trivial changes (1)
  • config/timesyncd/timesyncd.go
🔇 Additional comments (4)
main.go (1)

15-16: Nice modular reorganization.
By directly importing the swap and timesyncd packages, you've made the code more cohesive and easier to maintain. This matches the PR objective of splitting concerns into distinct modules.

config/swap/swap.go (3)

34-46: Validate missing configuration more explicitly.
When swapFileEditor.Find(...) fails to locate a line or read from the file, the function silently returns an empty string. Adding a more explicit log or handling (e.g., debug log) can help in diagnosing misconfigurations or missing swap settings.


48-62: Gracefully notify on default swappiness usage.
Falling back to the default swappiness value is reasonable, but consider logging a warning or debug message. It provides better visibility into how the system is configured if the file is missing or has invalid data.


85-103: Check that int32 casting aligns with potential usage.
Swappiness is being cast to int32. Though valid for typical usage (0–100), be sure there are no edge cases where a larger integer might be passed over DBus.

ifaceName = "io.hass.os.Config.Swap"
swapPath = "/etc/default/haos-swapfile"
swappinessPath = "/etc/sysctl.d/15-swappiness.conf"
defaultSwappiness = 1
Copy link
Member

Choose a reason for hiding this comment

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

That seems to be the default in the config file, but not the default default of the kernel 🤔

Should we maybe just read the current value using sysctl instead in case the file doesn't exist? 🤔

Copy link
Member Author

Choose a reason for hiding this comment

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

I was aware of this. The kernel default would never apply as HAOS sets it in read-only part of the FS but I haven't taken Supervised in account. I would have preferred to return it empty too as the "default" but as it's int, it's not nullable. The other drawback is that once you get this default, first call to set the value will persist it in the /etc/sysctl.d override.

If you don't have better idea, we could also read /proc/sys/vm/swappiness (as it has less overhead than calling systctl).

Copy link
Member

Choose a reason for hiding this comment

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

If you don't have better idea, we could also read /proc/sys/vm/swappiness (as it has less overhead than calling systctl).

Yeah I think that is quite a good and portable solution. If "our" config file isn't there, obviously that is the how the system defaults to through whatever other means it has been set. 👍

Add /io/hass/os/Config/Swap object that exposes interface with
properties controlling the swap size and swappiness configuration added
in [1] and [2]. There are no checks whether this supported on the target
system (while swappiness would probably have effect also on Supervised
installs, swap size can't be controlled there). These checks should be
implemented on upper layer (i.e. Supervisor).

[1] home-assistant/operating-system#3882
[2] home-assistant/operating-system#3884
@sairon sairon requested a review from agners February 28, 2025 13:08
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

🧹 Nitpick comments (5)
config/swap/swap.go (5)

35-51: Consider more robust error handling for kernel swappiness.

The function correctly reads swappiness from kernel procfs, but logs errors and returns a default of 60 when either reading or parsing fails. This approach was discussed and approved in previous reviews, but consider adding more context to error messages to help with debugging.

-		logging.Error.Printf("Failed to read kernel swappiness: %s", err)
+		logging.Error.Printf("Failed to read kernel swappiness from /proc/sys/vm/swappiness: %s", err)

53-65: Silent error handling in getSwapSize could make debugging difficult.

The function silently returns an empty string on errors, which could make debugging difficult in production environments. Consider at least logging the error when it occurs.

	found, err := swapFileEditor.Find(`^SWAPSIZE=`, "", true)
	if found == nil || err != nil {
+		if err != nil {
+			logging.Debug.Printf("Failed to read swap size from %s: %s", swapPath, err)
+		}
		return ""
	}

83-93: SwapSize validation regex could be more specific.

The current regex allows for any combination of units, including invalid ones like "KB" or "GB". Consider using a more specific regex that matches only valid size specifications like "512M" or "1G".

-	re := regexp.MustCompile(`^\d+([KMG]?(i?B)?)?$`)
+	re := regexp.MustCompile(`^\d+([KMG]|[KMG]iB|[KMG]B)?$`)

94-102: Consider adding logging for successful swap size changes.

For audit purposes, it would be helpful to log when the swap size is successfully changed.

	if err := swapFileEditor.Present(params); err != nil {
		return dbus.MakeFailedError(fmt.Errorf("failed to set swap size: %w", err))
	}

+	logging.Info.Printf("Swap size set to %s", swapSize)
	return nil

104-122: Consider adding logging for successful swappiness changes.

Similar to the swap size function, it would be helpful to log when the swappiness value is successfully changed.

	if err := swappinessEditor.Present(params); err != nil {
		return dbus.MakeFailedError(fmt.Errorf("failed to set swappiness: %w", err))
	}

+	logging.Info.Printf("Swappiness set to %d", swappiness)
	return nil
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 1708e8b and 60ebe6d.

📒 Files selected for processing (3)
  • config/swap/swap.go (1 hunks)
  • config/timesyncd/timesyncd.go (1 hunks)
  • main.go (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • config/timesyncd/timesyncd.go
🔇 Additional comments (7)
main.go (2)

15-16: Import changes look good.

The imports for the new swap package and renamed timesyncd package (formerly part of config) are correctly added.


75-76: Initialization of D-Bus connections looks good.

The swap.InitializeDBus(conn) and timesyncd.InitializeDBus(conn) calls properly initialize the new D-Bus interfaces, replacing the former config.InitializeDBus(conn) approach with more modular service initialization.

config/swap/swap.go (5)

3-14: Imports look good and are well organized.

The imports include all necessary packages for D-Bus communication, file operations, and regex pattern matching.


16-21: Constants are well-defined.

The constants clearly define the D-Bus object path, interface name, and file paths for configuration.


67-81: getSwappiness implementation looks good.

The function correctly tries to read swappiness from the configuration file and falls back to kernel swappiness if not found or on error. The approach aligns with the previous review discussions.


124-179: InitializeDBus implementation is well-structured.

The function properly sets up the D-Bus interface, exports properties and methods, and sets up introspection. The implementation follows the pattern used elsewhere in the codebase.


1-180:

❓ Verification inconclusive

Consider adding system capability checks.

As mentioned in the PR description, the implementation doesn't check whether swap size and swappiness configurations are supported on the target system. While the author suggests these checks should be handled at a higher level, it might be worth considering adding some basic capability detection to provide better error messages when unsupported operations are attempted.

This could be especially relevant for supervised installations where swap size cannot be controlled.


🌐 Web query:

How to detect if a Linux system supports swap file configuration?

💡 Result:

To determine if a Linux system supports swap file configuration, follow these steps:

1. Check Filesystem Compatibility
Swap file support depends on the underlying filesystem type:

  • Use df -T /target_directory to identify the filesystem where you plan to create the swap file.
  • Supported: ext4, XFS, Btrfs (with Copy-on-Write disabled), F2FS, JFS [4].
  • Unsupported: ZFS, Bcachefs [4].

2. Verify Kernel Support
Most modern Linux kernels (≥ 4.x) natively support swap files on compatible filesystems. Confirm with:

uname -r

3. Test Swap File Creation
Attempt these commands in a safe test environment:

sudo fallocate -l 1G /test_swapfile  # Create a test file
sudo chmod 600 /test_swapfile        # Set permissions
sudo mkswap /test_swapfile           # Format as swap

If no errors occur, your system supports swap file creation [2][5][8].

4. Check for Special Cases

  • Btrfs: Requires disabling Copy-on-Write (CoW) for the swap file:
    chattr +C /swapfile                # Disable CoW before allocating space [5].
  • Encrypted Swap: Requires additional setup like LUKS or dm-crypt [4].

5. Validate Active Swap
After enabling a test swap file:

sudo swapon /test_swapfile           # Activate
cat /proc/swaps                      # Verify activation [2][4]
free -h                              # Check total swap space [2][8]

If all steps succeed without errors or warnings related to unsupported operations, your system fully supports swap file configuration. For legacy systems or niche filesystems (e.g., ZFS), use a dedicated swap partition instead [4][8].

Citations:


Action Required: Enhance swap configuration with basic system capability detection

The current implementation doesn’t verify if the target system supports modifying swap settings. While the design assumes such checks are handled at a higher level, adding lightweight detection here could improve error messaging—especially for supervised installations where swap size control might be unsupported. For example, consider:

  • Filesystem verification: Check the filesystem type (using commands like df -T) to ensure it’s one of the supported types (e.g., ext4, XFS, Btrfs with CoW disabled) since filesystems like ZFS typically don't support swap files.
  • Kernel version check: Ensure the running kernel (e.g., via uname -r) meets the minimum version (Linux ≥ 4.x) for swap file support.
  • Test swap file creation: Optionally, simulate swap file creation (using mkswap and swapon) in a controlled environment to proactively catch unsupported configurations.

Implementing these checks could help provide clearer feedback when swap operations are attempted on unsupported systems.

@sairon sairon merged commit 5e71717 into main Feb 28, 2025
4 checks passed
@sairon sairon deleted the add-config-swap branch February 28, 2025 13:12
sairon added a commit to home-assistant/operating-system that referenced this pull request Feb 28, 2025
Adds new DBus APIs for NTP servers and swap:
 - home-assistant/os-agent#207
 - home-assistant/os-agent#222
sairon added a commit to home-assistant/operating-system that referenced this pull request Feb 28, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants