Skip to content

refactor: update handle function of cc_mounts - #5498

Merged
TheRealFalcon merged 3 commits into
canonical:mainfrom
TheRealFalcon:refactor-mounts
Aug 2, 2024
Merged

refactor: update handle function of cc_mounts#5498
TheRealFalcon merged 3 commits into
canonical:mainfrom
TheRealFalcon:refactor-mounts

Conversation

@TheRealFalcon

Copy link
Copy Markdown
Contributor

Proposed Commit Message

refactor: update handle function of cc_mounts

The handle function of cc_mounts was hard to grok and had one of the
highest cyclomatic complexity scores in the codebase. Functionally,
the code should be unchanged.

Additional Context

Test Steps

Merge type

  • Squash merge using "Proposed Commit Message"
  • Rebase and merge unique commits. Requires commit messages per-commit each referencing the pull request number (#<PR_NUM>)

@TheRealFalcon

Copy link
Copy Markdown
Contributor Author

cc @a-dubs

The handle function of cc_mounts was hard to grok and had one of the
highest cyclomatic complexity scores in the codebase. Functionally,
the code should be unchanged.

@a-dubs a-dubs 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.

Changes look good! Left one little comment/nit but not going to block on it.

Also I was wondering if it would be worth the time to add further unit tests or integration tests before merging to ensure this refactor didn't break anything?

Comment thread cloudinit/config/cc_mounts.py Outdated
def sanitized_devname_is_valid(
original: str, sanitized: Optional[str], fstab_devs: Dict[str, str]
) -> bool:
"""Get if the device name is valid and log if not."""

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.

could we reword this description a little?

@TheRealFalcon

Copy link
Copy Markdown
Contributor Author

Also I was wondering if it would be worth the time to add further unit tests or integration tests before merging to ensure this refactor didn't break anything?

The last two commits in #5459 added some coverage for things I knew I was changing here. Got any specific ideas?

@a-dubs

a-dubs commented Jul 11, 2024

Copy link
Copy Markdown
Contributor

@TheRealFalcon After reading back over all of the cc_mounts unit tests, I realize there is indeed coverage for all the functions you have created. My original thought was, "oh wow thats a lot of new helper functions, we should unit test those!!!". But I now see that the unit tests pretty thoroughly test the functions that rely on these helpers, so their functionality is pretty well tested, just not directly. So I think the effort of adding new unit tests is not worth the reward.

@TheRealFalcon

Copy link
Copy Markdown
Contributor Author

This PR still needs another reviewer @holmanb or @blackboxsw

@blackboxsw blackboxsw self-assigned this Jul 22, 2024

@blackboxsw blackboxsw left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Minor nits and I think a potentially significant question on santize_mounts_configuration to see if we are accidentally dropping items from mount config lines if they contain None.

Comment thread cloudinit/config/cc_mounts.py Outdated
Comment on lines +470 to +472
default_already_exists = any(
cfgm[0] == default_mount[0] for cfgm in mounts
)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Nice, I initially wondering if this may be less performant than default_mount[0] in [cfgm[0] for cfgm in mounts] but the any clause gives us a chance to early exit on first True seen so it's probably a bit better than the alternative.

Also another alternative would be to create mount_paths = set(cfgmnt=[0] for cfgmnt in mounts) outside the scope of the for loop to initialize it for use in both iterations of default_mounts.

Anyhow, nothing more than a discussion point. Not critical as far as processing cost.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Those work too, but I think I prefer it as-is.

"""
actlist = []
dev_denylist = []
for line in mounts[::-1]:

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Why are we walking through mounts in reverse only to re-reverse the list order once we populate actlist? Is it because we generally expect the None field to be the highest index in our mounts list so we can persist it locally as a dev_denylist?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Because we have some weird rules that would allow a mount declaration like this:

/dev/sda1 /mnt1
/dev/sda1 None
/dev/sda1 /mnt2

The None on line 2 means "disregard this line and any /dev/sda1 entries that came before it". The first reversal is to make it easy to know which lines to throw out that we come upon. If we previously saw a None having the same first entry, we know to throw it out.

Since we accumulated our act list in reverse order, the last reverse is to put the list back into it's original order (after we've removed the lines we want to remove).

Comment on lines +544 to +547
updated_cfg = add_default_mounts_to_cfg(
updated_cfg, default_mount_options, fstab_devs, device_aliases, cloud
)
updated_cfg = remove_nonexistent_devices(updated_cfg)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Could remove_nonexistent_devices be performed because add_default_mounts_to_cfg since all we are doing in add_default_mounts_to_cfg is adding known valid default mount config for ephemeral and swap if they aren't already present?

Suggested change
updated_cfg = add_default_mounts_to_cfg(
updated_cfg, default_mount_options, fstab_devs, device_aliases, cloud
)
updated_cfg = remove_nonexistent_devices(updated_cfg)
updated_cfg = remove_nonexistent_devices(updated_cfg)
updated_cfg = add_default_mounts_to_cfg(
updated_cfg, default_mount_options, fstab_devs, device_aliases, cloud
)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

No actually. I could specify ephemeral None to tell cloud-init not to render the epehermal device. On add_default_mounts_to_cfg, it'll see that I have already defined it, and then not touch the mount list, then later remove it on the remove_nonexistent_devices call. If I switch the order, cloud-init will remove it from the list, then add the default back in. We'll get the default added to the mount list when we didn't want it.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Good, thanks for the correction/explanation there.

Comment thread cloudinit/config/cc_mounts.py Outdated
Comment on lines +414 to +416
updated_line = [
str(token) for token in updated_line if token is not None
]

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This has the side-effect of truncating items in the list that have the value of None. Is this what we want here?

I think we want the following:

Suggested change
updated_line = [
str(token) for token in updated_line if token is not None
]
updated_line = [
None if token is None else str(token) for token in updated_line
]

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Ahh, good find. I reworked the code there a bit to align with what we had before and also updated a test case that should pass on main but fail pre-fixing this.

Comment thread cloudinit/config/cc_mounts.py Outdated
@TheRealFalcon

Copy link
Copy Markdown
Contributor Author

@blackboxsw , this should be ready for re-review

@TheRealFalcon
TheRealFalcon requested a review from blackboxsw July 29, 2024 17:00

@blackboxsw blackboxsw left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

LGTM. Thanks for the fixup of potentially redacted values and unittest additions. Validated before and after expected values in /etc/fstab mounts remain unchanged in integration testing and test coverage looks good.

This may have conflicts in merge with upstream/main to resolve.

device_aliases = cfg.get("device_aliases", {})

for i in range(len(cfgmnt)):
return fstab_lines, fstab_devs, fstab_removed

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I wondered about defining a namedtuple here for readability and usability of the return value from this function but I don't think it's worth the minor overhead here and we only have one call-site to keep aligned with this return value, so not a lot of reuse potential there.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Agreed. Since it's just one site it doesn't seem like it would add much value.

@TheRealFalcon
TheRealFalcon merged commit d15a770 into canonical:main Aug 2, 2024
@TheRealFalcon
TheRealFalcon deleted the refactor-mounts branch August 2, 2024 18:21
holmanb pushed a commit to holmanb/cloud-init that referenced this pull request Aug 2, 2024
The handle function of cc_mounts was hard to grok and had one of the
highest cyclomatic complexity scores in the codebase. Functionally,
the code should be unchanged.
holmanb pushed a commit that referenced this pull request Aug 6, 2024
The handle function of cc_mounts was hard to grok and had one of the
highest cyclomatic complexity scores in the codebase. Functionally,
the code should be unchanged.
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.

3 participants