Skip to content

fix: add structured name parameter to SnapmExistsError - #1006

Open
0x5t4l1n wants to merge 4 commits into
snapshotmanager:mainfrom
0x5t4l1n:fix/snapm-exists-error-generic-format
Open

0x5t4l1n wants to merge 4 commits into
snapshotmanager:mainfrom
0x5t4l1n:fix/snapm-exists-error-generic-format

Conversation

@0x5t4l1n

@0x5t4l1n 0x5t4l1n commented Aug 2, 2026

Copy link
Copy Markdown

Summary

Fixes #500 — raise sites composed their own message strings, making the error format inconsistent and making it impossible for callers to programmatically inspect which snapshot set conflicted.

New file snapm/snapm_exists_error.py:

raise SnapmExistsError("my-snapset")
# SnapmExistsError: Snapshot set 'my-snapset' already exists
err.name  # → 'my-snapset'

Test plan

  • SnapmExistsError("foo").name == "foo"
  • str(SnapmExistsError("foo")) == "Snapshot set 'foo' already exists"
  • Custom message: SnapmExistsError("foo", "custom msg")

Summary by CodeRabbit

  • Bug Fixes
    • Added clearer error handling when attempting to create a snapshot or snapshot set that already exists.
    • Error messages now identify the conflicting snapshot name and provide a default explanation when no custom message is supplied.

Summary by CodeRabbit

  • Bug Fixes
    • Improved duplicate-resource error messages for snapshot sets, schedules, boot entries, and revert entries.
    • Added clearer, more specific errors when attempting to create resources that already exist.
    • Preserved resource names in error details to make conflicts easier to identify.

…tmanager#500

Resolves snapshotmanager#500. SnapmExistsError raise sites composed their own message
strings, making the error format inconsistent across the codebase.
Add a structured name parameter so all raise sites produce a uniform
message and callers can programmatically inspect which snapshot set
already existed without parsing the message string.
@coderabbitai

coderabbitai Bot commented Aug 2, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 40e1af19-2675-4101-a309-1e4197241ea4

📥 Commits

Reviewing files that changed from the base of the PR and between f4c9b60 and 604119a.

📒 Files selected for processing (2)
  • snapm/_snapm.py
  • snapm/manager/_manager.py

Included review availability: Your plan provides up to 1 included review per hour; 0 remain after this review.


Walkthrough

The change adds parameterized, resource-specific existence exceptions and updates manager duplicate checks to raise the matching exception for schedules, snapshot sets, boot entries, and revert entries.

Changes

Snapshot existence error

Layer / File(s) Summary
Exception contract
snapm/_snapm.py
SnapmExistsError now formats default messages from a resource name and accepts custom messages. Four resource-specific subclasses store the relevant name attribute and are exported.
Manager duplicate checks
snapm/manager/_manager.py
Duplicate schedule, snapshot-set, boot-entry, and revert-entry creation now raises the corresponding specific exception type.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Merge Risk: ⚪ Minimal · up to 60411

The change is localized to structured duplicate-resource errors and does not introduce an actionable merge-blocking risk; it is merge-ready after normal checks and review.

🚥 Pre-merge checks | ✅ 8 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 44.44% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 9 functions across 2 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (8 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly identifies the main change: adding a structured name parameter to SnapmExistsError.
Linked Issues check ✅ Passed The changes satisfy issue #500 by adding generic error-message formatting and updating manager raise sites to use resource-specific structured exceptions.
Out of Scope Changes check ✅ Passed The exception subclasses, exports, relocation, and manager updates directly support the requested SnapmExistsError formatting and raise-site changes.
No-Hardcoded-Secrets ✅ Passed No hardcoded secret was introduced. The net PR diff changes only snapm/_snapm.py and snapm/manager/_manager.py; added literals are exception messages and exported class names. Secret-pattern, cred…
No-Weak-Crypto ✅ Passed No weak-crypto issue was introduced. The PR changes only exception classes, exports, imports, and duplicate-resource raise sites in snapm/_snapm.py and snapm/manager/_manager.py. The complete PR p…
No-Injection-Vectors ✅ Passed PASS — no injection vector was introduced. The PR only adds exception classes and changes raise sites. Added-line analysis found no SQL, shell=True, eval, exec, pickle.loads, yaml.load, or os.system. …
No-Sensitive-Data-In-Logs ✅ Passed No sensitive-data logging was introduced. The pull request changes exception classes and four manager raise sites only; the diff adds no logger or print calls. The new messages contain validated snaps…
Full details: No-Hardcoded-Secrets

Explanation

No hardcoded secret was introduced. The net PR diff changes only snapm/_snapm.py and snapm/manager/_manager.py; added literals are exception messages and exported class names. Secret-pattern, credential-URL, private-key, and long-base64 scans found no matches. Risk: none under this check.

Full details: No-Weak-Crypto

Explanation

No weak-crypto issue was introduced. The PR changes only exception classes, exports, imports, and duplicate-resource raise sites in snapm/_snapm.py and snapm/manager/_manager.py. The complete PR patch contains no MD5, SHA1, DES, 3DES, RC4, Blowfish, ECB, custom crypto, or secret-comparison usage. Existing MD5 references are outside the changed code and do not establish PR causality. Security risk: none identified; no security impact.

Full details: No-Injection-Vectors

Explanation

PASS — no injection vector was introduced. The PR only adds exception classes and changes raise sites. Added-line analysis found no SQL, shell=True, eval, exec, pickle.loads, yaml.load, or os.system. The new .format(name=name) call performs fixed-template string formatting and does not execute input. Existing subprocess calls use argument lists and were not changed. Security risk: none identified; no injection impact.

Full details: No-Sensitive-Data-In-Logs

Explanation

No sensitive-data logging was introduced. The pull request changes exception classes and four manager raise sites only; the diff adds no logger or print calls. The new messages contain validated snapshot-set or schedule names, not passwords, tokens, API keys, PII, session IDs, hostnames, or customer records. The duplicate-resource paths already logged equivalent names through the existing CLI error handler, so the pull request does not create a new exposure. Security risk: none identified.

  • Fix all pre-merge checks with AI
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@snapm/snapm_exists_error.py`:
- Line 35: Update the __init__ method’s msg parameter annotation from str to
Optional[str], preserving its None default and existing constructor behavior.
- Around line 35-37: Update the SnapmExistsError call in the manager flow around
the existing caller to pass the raw snapshot name as the constructor’s name
argument, rather than a preformatted complete message. Preserve the structured
self.name contract and let SnapmExistsError.__init__ generate the default
message.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 239d1d63-51b6-48fa-afe0-619043cc1bb6

📥 Commits

Reviewing files that changed from the base of the PR and between 880f8d5 and f4c9b60.

📒 Files selected for processing (1)
  • snapm/snapm_exists_error.py

Comment thread snapm/snapm_exists_error.py Outdated

_default_template = "Snapshot set {name!r} already exists"

def __init__(self, name: str, msg: str = None) -> None:

@coderabbitai coderabbitai Bot Aug 2, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail
ruff check snapm/snapm_exists_error.py --select RUF013

Repository: snapshotmanager/snapm

Length of output: 872


🏁 Script executed:

#!/bin/bash
set -euo pipefail
sed -n '1,80p' snapm/snapm_exists_error.py

Repository: snapshotmanager/snapm

Length of output: 1379


Annotate the optional message as Optional[str].

msg can be None because it defaults to None and the constructor accepts omitted and custom messages. RUF013 flags msg: str = None; use Optional[str] so type checking accepts the documented API.

🧰 Tools
🪛 Ruff (0.16.0)

[warning] 35-35: PEP 484 prohibits implicit Optional

Convert to Optional[T]

(RUF013)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@snapm/snapm_exists_error.py` at line 35, Update the __init__ method’s msg
parameter annotation from str to Optional[str], preserving its None default and
existing constructor behavior.

Source: Linters/SAST tools

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This is valid: msg must have Optional[str] type since it can accept None.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

@bmr-cymru, confirmed. This is a low-severity type-correctness issue. The current annotation conflicts with the None default and can cause Ruff and static type-check failures. Update msg to Optional[str] and import Optional from typing.

You are interacting with an AI system.

Comment thread snapm/snapm_exists_error.py Outdated
@packit-as-a-service

Copy link
Copy Markdown

Congratulations! One of the builds has completed. 🍾

You can install the built RPMs by following these steps:

  • sudo dnf install -y 'dnf*-command(copr)'
  • dnf copr enable packit/snapshotmanager-snapm-1006
  • And now you can install the packages.

Please note that the RPMs should be used only in a testing environment.

@bmr-cymru

Copy link
Copy Markdown
Contributor

Fixed whitespace in summary.

@bmr-cymru bmr-cymru left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Hi @0x5t4l1n ! Thanks for your interest in contributing to snapm!

This PR is a good start, and the changes you have made to SnapmExistsError are going in the right direction.

There's a bit more work needed here to make this functional & compliant with our project standards: I'm happy to help if you're interested in revising this PR to include these changes.

First off: the patch is currently missing the DCO (Signed-off-by) and Assisted-by tags (from the summary it looks like you are using Claude Code). See the CONTRIBUTING.md.

As for the code, it looks good but so far your new exception class is not used (the patch just adds the new file, but does not import or use the new class anywhere). The new class also seems to be using Google style docstring formatting - the project uses Sphinx throughout. You can read more about this in the CONTRIBUTING.md (under "Coding Style"), or directly in the Sphinx Docs.

Comment thread snapm/snapm_exists_error.py Outdated
"""Structured SnapmExistsError raised when a snapshot set already exists."""


class SnapmExistsError(Exception):

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

All exception classes in snapm should inherit from SnapmError (or one of its child classes), rather than bare Exception.

Comment thread snapm/snapm_exists_error.py Outdated
@@ -0,0 +1,37 @@
# Copyright Red Hat

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I don't think there's currently a good justification for splitting this out into a separate file. If we did this, then we would do it for all the exception classes (e.g. creating snapm/_exceptions.py), and then import and re-export them in the base snapm package so that they still appear at the same import location for clients.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Also, feel free to add your own copyright when making additions/changes: there's no need for you to assign copyright ownership to Red Hat when contributing.

Comment thread snapm/snapm_exists_error.py Outdated
Raising with a custom message::

raise SnapmExistsError("my-snapset", "my-snapset is taken; choose another name")
"""

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Comment thread snapm/snapm_exists_error.py Outdated

_default_template = "Snapshot set {name!r} already exists"

def __init__(self, name: str, msg: str = None) -> None:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This is valid: msg must have Optional[str] type since it can accept None.

Comment thread snapm/snapm_exists_error.py Outdated
@bmr-cymru

Copy link
Copy Markdown
Contributor

@0x5t4l1n any response to the review comments?

… subclasses

- SnapmExistsError now lives in snapm/_snapm.py (not a separate file)
- Inherits from SnapmError instead of Exception
- Adds SnapmSnapsetExistsError, SnapmScheduleExistsError,
  SnapmBootEntryExistsError, SnapmRevertEntryExistsError subclasses
- Each subclass sets a resource-specific name attribute
- Docstrings use Sphinx notation
- All four Manager raise sites updated to use the appropriate subclass
- New classes exported via __all__

Signed-off-by: Stalin <git@w4nn4d13.tech>
Replace all four SnapmExistsError raise sites with the appropriate
subclass: SnapmSnapsetExistsError, SnapmScheduleExistsError,
SnapmBootEntryExistsError, and SnapmRevertEntryExistsError.

Signed-off-by: Stalin <git@w4nn4d13.tech>

@bmr-cymru bmr-cymru left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Thanks for updating the PR! This is looking better now - the only change I'd like to see (other than squashing the original commits) is to drop the msg argument from the subclasses.

Comment thread snapm/_snapm.py

_default_template = "Snapshot set named {name!r} already exists"

def __init__(self, name: str, msg: Optional[str] = None) -> None:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I don't see a use for passing a custom msg here: let's simplify things and constrain the resource-specific subclasses to just reporting their own name with the provided template. I think this reduces the likelihood of misuse and confusing behaviour.

(Same comment for the other classes).

Comment thread snapm/snapm_exists_error.py Outdated
@@ -1,37 +0,0 @@
# Copyright Red Hat

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Please squash/rebase to remove this from the history (rather than an incremental commit on the earlier changes).

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.

Add generic message format to SnapmExistsError

2 participants