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

Enable use of relative volume host-paths #227

Merged
merged 12 commits into from
Sep 15, 2023
Merged

Conversation

JonathonReinhart
Copy link
Owner

@JonathonReinhart JonathonReinhart commented Sep 13, 2023

This enables the use of relative paths in a volume hostpath.

Previously, if one put this in .scuba.yml:

volumes:
    /foo: foo
    # or
    /foo:
        hostpath: foo

foo would be interpreted by Docker as a named volume, resulting in an empty directory at /foo, which is certainly not what the user intended.

Or, if one tried to work around that problem and do this:

volumes:
    /foo: ./foo

Then Docker would complain about the use of a relative path (a specifier containing a / but not starting with /), and suggest the use of an absolute path.

Now, when scuba encounters a relative path in a volume hostpath, it is interpreted as relative to the scuba root (the directory in which .scbua.yml is found). To avoid ambiguity with the old behavior, relative paths must start with ./ or ../.

I also considered interpreting relative paths as relative to the current working directory, but that seemed like a much less useful approach:

  • The common scenario would be to mount a project folder somewhere else in the filesystem:
    volumes:
      /var/cualqomm/license:
          hostpath: ./cq_license
  • I can't think of a scenario where I would want a bind mount (to a fixed container-path) to change depending on where my current directory is within my project tree.
    • If this was truly desired, one could use $PWD/foo.
  • The scheme whereby relative paths in config files are relative to the directory containing the config file is intuitive and widely-used:
    • In Scuba
      • !from_yaml (ref)
    • In .gitconfig (ref)
    • In ADMan config (ref)
    • In Docker Compose:
      • env_file (ref)
      • extends.file (ref)
      • service volumes (ref)

Fixes #218

This avoids ambiguious or erroneous behavior when hostdir is relative
and passed to docker:
- If it doesn't contain a slash, it is interpreted as a named volume,
  which is not what the user intended, since it is called "hostpath".
- If it does contain a slash, docker aborts and suggests passing an
  absolute path.

See #218.
Note that base_dir is marked Optional[Path] and defaults to None.
If a relative path is encountered but base_dir is not given, a
ConfigError is raised. This is to support two use cases:

1. Intentionally forbidding the use of relative paths.
   (This will be used for volume container-paths.)

2. Simplifying tests using programmatic access where base_dir is
   irrelevant.
@github-actions
Copy link

github-actions bot commented Sep 13, 2023

Test Results

    5 files      5 suites   1m 52s ⏱️
169 tests 167 ✔️   2 💤 0
845 runs  835 ✔️ 10 💤 0

Results for commit d4a61bc.

♻️ This comment has been updated with latest results.

@JonathonReinhart
Copy link
Owner Author

I think I want to restrict the relative paths to starting with ./ (or ../), just like Docker Compose does.

While the long form is pretty unambiguous (given that it says hostpath):

volumes:
  /foo:
    hostpath: foo

The short form is currently (unintentionally, and undocumented) interpreted as a named volume.

volumes:
  /foo: foo  # Is this a relative path or a named volume?

Requiring it to start with a ./ (in both cases) makes it 100% clear it is a relative path.

I'd rather start with this strict approach and relax as necessary, rather than starting with it loosely defined and trying to tighten it up later.

JonathonReinhart added a commit that referenced this pull request Sep 13, 2023
JonathonReinhart added a commit that referenced this pull request Sep 13, 2023
@JonathonReinhart JonathonReinhart force-pushed the volume-relative-path branch 3 times, most recently from 6adb232 to c7082a2 Compare September 13, 2023 20:56
@xanarin
Copy link
Collaborator

xanarin commented Sep 15, 2023

I think the behavioral choices here make a lot of sense. I agree that a named volume is not what I would expect from having a bare directory name, and I like that scuba does accept a bare directory name either and instead forces the user to use ./ or ../ to formally confirm their intention. I also agree that those relative paths should be based on the .scuba.yml directory. I think basing in on CWD would just be a footgun.

I think in the worst case, this feature will be made less frustrating to an unsuspecting user because of #195. If a non-existent relative host path is created, it will at least be created with permissions such that the user will easily be able to delete it without having to escalate to root. But I think that scenario is very unlikely since this is a newly-supported option.

Copy link
Collaborator

@xanarin xanarin left a comment

Choose a reason for hiding this comment

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

Overall, this looks great. A few small questions and documentation changes are all the useful feedback I can give. I also love the additional documentation on the exceptions that a function can raise! Between that and the new type annotations the codebase is definitely getting easier to spot-check and reason about.

docs/configuration.rst Show resolved Hide resolved
scuba/config.py Show resolved Hide resolved
tests/test_scuba.py Show resolved Hide resolved
@JonathonReinhart
Copy link
Owner Author

I also love the additional documentation on the exceptions that a function can raise!

You can thank the Google Python Style Guide for that 😃. (Not all of it I like though...)

Between that and the new type annotations the codebase is definitely getting easier to spot-check and reason about.

Yes! It's so much easier to comprehend, particularly in the case of complex dict or tuple types. Also, mypy is surprisingly fast (likely due to its cache), so especially in cases like this where there was lots of plubing / refatoring, much of the initial edit-save-check cycle was done with static analysis only. Then test_config.py which is pretty fast. And only then did I run test_main.py which is slow because it actually runs Docker.

With this, and some of the Python 2 tech debt paid off (#226), I'm pretty happy with the codebase these days.

JonathonReinhart and others added 10 commits September 15, 2023 06:24
This will allow us to add additional arguments (e.g., scuba_root).

A make_config() wrapper is used in test_scuba.py for simplicity, since
existing tests already use kwargs-style call, and moving to the more
verbose single-dict argument would increase verbosity unnecessarily.
Starting from load_config(), this passes scuba_root (the directory
in which .scuba.yml is found) through the hierarchy to
ScubaVolume.from_dict().

This will enable us to use scuba_root in config value expansion,
particularly volume paths.

Note that scuba_root is marked Optional[Path] to support an empty or
default ScubaConfig, or one that is programatically created without a
scuba_root directory. This must be accounted for when it is used.
When a relative path is used in a volume hostpath field, it is now
considered relative to scuba_root, the directory in which .scuba.yml is
found.

Fixes #218
This also adds a relative path volume to the example.

This also changes the description to only reference Docker bind-mounts
since that's all Scuba currently supports (and will likely ever
support).
This helps when comparing paths that include . and ..
@JonathonReinhart
Copy link
Owner Author

Thanks for the thorough review @xanarin!

@JonathonReinhart JonathonReinhart merged commit 49fc57c into main Sep 15, 2023
@JonathonReinhart JonathonReinhart deleted the volume-relative-path branch September 15, 2023 06:34
@haboustak
Copy link
Contributor

Unfortunately, this MR introduced a regression in some code I was recently reviewing. We had projects written in Go that were storing their GOMODCACHE between invocations of scuba into a go-mod-cache named volume.

For what it's worth, I'm not in support of this usage of scuba volumes, but there's at least one instance of existing code that made use of it., and if it was written once, it was pasted at least 4 more times.

As I understand the usage, the intent was to mount the named volume at /go/pkg/mod/cache inside the container.

ENV GOMODCACHE /go/pkg/mod/cache
# requires scuba 2.10.0
volumes:
  ${HOME}/.cache/go-build: ${HOME}/.cache/go-build
  /go/pkg/mod/cache: go-mod-cache

@JonathonReinhart
Copy link
Owner Author

which is certainly not what the user intended.

Well that will teach me not to assume I understand my users.

To avoid ambiguity with the old behavior, relative paths must start with ./ or ../.

Whew, looks like this was a good idea.

Thanks for pointing this out. I'll open a bug to track the fix.

@JonathonReinhart
Copy link
Owner Author

Opened #248.

JonathonReinhart added a commit that referenced this pull request Mar 22, 2024
Named volumes were unintentionally supported prior to #227. This
restores the prior behavior while remaining unambiguiously compatible
with relative bind-mounts added in #227.

This also adds support for explicit named volumes in complex form
configuration.

Fixes #248
@JonathonReinhart JonathonReinhart modified the milestones: v2.12, v2.13 Mar 24, 2024
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.

Detect or handle relative host paths in volumes
3 participants