Add --list-volumes to list named volumes
Long-only (no free short letter left), lists each volume's name and host directory, tab-aligned the same way -l/--list-images already aligns name:tag output -- list_volumes_command() reuses that scheme. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Gv3s5jckJKzh6JkMoi2Akz
This commit is contained in:
@@ -17,13 +17,15 @@ status); this file stays the dense, file-by-file reference. Still early-stage.
|
||||
Source layout (all under `src/`):
|
||||
- `main.cpp` — CLI entry point, dependency checks, orchestration (`mount_image()`,
|
||||
`run_container()`, `cleanup_image()`, `unmount_image()`, `list_images_command()`,
|
||||
`create_volume_command()`). `run_container()` unconditionally calls
|
||||
`read_oci_image_config()` and reuses the result for two independent defaults: the
|
||||
command to run (`Entrypoint ++ Cmd`) when none is given on the command line, and,
|
||||
when `--user` wasn't given, the sandboxed process's user/group (`config.User`,
|
||||
split into `OciImageConfig::user`/`group`) — an explicit `--user`/`--group` on the
|
||||
command line always takes precedence. `create_volume_command()` implements
|
||||
`-v/--volume <name> <directory>`: see `config_file.{h,cpp}` below for what a
|
||||
`create_volume_command()`, `list_volumes_command()`). `run_container()`
|
||||
unconditionally calls `read_oci_image_config()` and reuses the result for two
|
||||
independent defaults: the command to run (`Entrypoint ++ Cmd`) when none is given
|
||||
on the command line, and, when `--user` wasn't given, the sandboxed process's
|
||||
user/group (`config.User`, split into `OciImageConfig::user`/`group`) — an
|
||||
explicit `--user`/`--group` on the command line always takes precedence.
|
||||
`create_volume_command()` implements `-v/--volume <name> <directory>`;
|
||||
`list_volumes_command()` implements `--list-volumes` (same tab-alignment scheme as
|
||||
`list_images_command()`, reused as-is) — see `config_file.{h,cpp}` below for what a
|
||||
"volume" means here (a distinct concept from `OciImageConfig::volumes`).
|
||||
- `oci_image.{h,cpp}` — validates/parses the OCI Image Layout tar (libarchive +
|
||||
nlohmann_json) and extracts layer blobs. `list_oci_images()` scans a directory
|
||||
@@ -152,8 +154,8 @@ Build directory is `buildDir/` (already configured).
|
||||
(see `priv_drop_helper.cpp` in "Project state")
|
||||
- Run the executable: `./buildDir/slocker-lite -m <image.tar>` (see `--help` for the
|
||||
full flag list: `-m/--mount`, `-r/--run`, `-u/--umount`, `-c/--cleanup`,
|
||||
`-l/--list-images`, `-n/--no-nsenter`, `--user`, `--group`, `-t/--test`,
|
||||
`--log-level`, `-h/--help`, `-V/--version`)
|
||||
`-l/--list-images`, `-n/--no-nsenter`, `--user`, `--group`, `-v/--volume`,
|
||||
`--list-volumes`, `-t/--test`, `--log-level`, `-h/--help`, `-V/--version`)
|
||||
- Run tests: `meson test -C buildDir`
|
||||
|
||||
## Code style
|
||||
|
||||
@@ -58,6 +58,7 @@ slocker-lite -u|--umount <layer-id>
|
||||
slocker-lite -c|--cleanup <layer-id>
|
||||
slocker-lite -l|--list-images <directory>
|
||||
slocker-lite -v|--volume <name> <directory>
|
||||
slocker-lite --list-volumes
|
||||
slocker-lite -t|--test
|
||||
slocker-lite -h|--help
|
||||
slocker-lite -V|--version
|
||||
@@ -74,6 +75,7 @@ slocker-lite -V|--version
|
||||
| `--group <group>` | With `--user`, use this group (name or numeric gid) instead of the user's primary group. |
|
||||
| `-l, --list-images <dir>` | List OCI Image Layout tars (`*.tar`, `*.tar.*`) found directly in `<dir>`, with their `name:tag`. |
|
||||
| `-v, --volume <name> <dir>` | Create a named volume mapped to a host directory (created if missing), recorded in the config file's `volumes` section. Fails if the name or directory is already used by an existing volume. |
|
||||
| `--list-volumes` | List all named volumes (see `-v/--volume`) with their host directory. |
|
||||
| `-t, --test` | Print which `bwrap --unshare-xxx` namespaces the running kernel supports. |
|
||||
| `--log-level <level>` | Set log verbosity (`trace`, `debug`, `info`, `warn`, `error`, `critical`, `off`). |
|
||||
| `-h, --help` | Print usage and exit. |
|
||||
@@ -99,6 +101,9 @@ sudo ./buildDir/slocker-lite -r myimage.tar --user git
|
||||
|
||||
# Create a named volume backed by a host directory
|
||||
./buildDir/slocker-lite -v mydata ~/slocker-volumes/mydata
|
||||
|
||||
# List all named volumes
|
||||
./buildDir/slocker-lite --list-volumes
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
+39
-7
@@ -41,16 +41,18 @@ namespace {
|
||||
|
||||
constexpr std::array<std::string_view, 2> kRequiredTools = {"containers-storage", "bwrap"};
|
||||
|
||||
enum class Mode { kNone, kMount, kUnmount, kTest, kRun, kCleanup, kListImages, kVolume };
|
||||
enum class Mode { kNone, kMount, kUnmount, kTest, kRun, kCleanup, kListImages, kVolume, kListVolumes };
|
||||
|
||||
// --log-level/--user/--group have no short form (--log-level's was freed up so -l
|
||||
// could become --list-images; -u is already --umount), so they need long-option
|
||||
// vals outside the printable-char range short options use.
|
||||
// --log-level/--user/--group/--list-volumes have no short form (--log-level's was
|
||||
// freed up so -l could become --list-images; -u is already --umount; --list-volumes
|
||||
// has no natural free letter left), so they need long-option vals outside the
|
||||
// printable-char range short options use.
|
||||
constexpr int kLogLevelOpt = 256;
|
||||
constexpr int kUserOpt = 257;
|
||||
constexpr int kGroupOpt = 258;
|
||||
constexpr int kListVolumesOpt = 259;
|
||||
|
||||
constexpr std::array<struct option, 14> kLongOptions = {{
|
||||
constexpr std::array<struct option, 15> kLongOptions = {{
|
||||
{"help", no_argument, nullptr, 'h'},
|
||||
{"version", no_argument, nullptr, 'V'},
|
||||
{"test", no_argument, nullptr, 't'},
|
||||
@@ -64,6 +66,7 @@ constexpr std::array<struct option, 14> kLongOptions = {{
|
||||
{"user", required_argument, nullptr, kUserOpt},
|
||||
{"group", required_argument, nullptr, kGroupOpt},
|
||||
{"volume", required_argument, nullptr, 'v'},
|
||||
{"list-volumes", no_argument, nullptr, kListVolumesOpt},
|
||||
{nullptr, 0, nullptr, 0},
|
||||
}};
|
||||
|
||||
@@ -75,6 +78,7 @@ void print_usage(const char* prog) {
|
||||
" {0} -c|--cleanup <layer-id>\n"
|
||||
" {0} -l|--list-images <directory>\n"
|
||||
" {0} -v|--volume <name> <directory>\n"
|
||||
" {0} --list-volumes\n"
|
||||
" {0} -t|--test\n"
|
||||
" {0} -h|--help\n"
|
||||
" {0} -V|--version\n"
|
||||
@@ -109,6 +113,8 @@ void print_usage(const char* prog) {
|
||||
" -v, --volume <name> <dir> create a named volume mapped to a host directory\n"
|
||||
" (created if missing), recorded in the config\n"
|
||||
" file's volumes section\n"
|
||||
" --list-volumes list all named volumes (see -v/--volume) with\n"
|
||||
" their host directory\n"
|
||||
" -t, --test run the test suite\n"
|
||||
" --log-level <level> set log verbosity (trace, debug, info, warn,\n"
|
||||
" error, critical, off)\n"
|
||||
@@ -300,6 +306,25 @@ int create_volume_command(const std::string& name, const std::string& directory,
|
||||
return 0;
|
||||
}
|
||||
|
||||
int list_volumes_command(const AppConfig& config) {
|
||||
size_t max_len = 0;
|
||||
for (const auto& volume : config.volumes) {
|
||||
max_len = std::max(max_len, volume.name.size());
|
||||
}
|
||||
|
||||
// Same tab-alignment scheme as list_images_command(): pad each name to one
|
||||
// 8-column tab stop past the longest one, however long that is.
|
||||
constexpr size_t kTabWidth = 8;
|
||||
size_t target_tabs = max_len / kTabWidth + 1;
|
||||
|
||||
for (const auto& volume : config.volumes) {
|
||||
size_t tabs_used = volume.name.size() / kTabWidth;
|
||||
size_t tabs_needed = target_tabs > tabs_used ? target_tabs - tabs_used : 1;
|
||||
fmt::print("{}{}{}\n", volume.name, std::string(tabs_needed, '\t'), volume.directory);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int run_container(const std::filesystem::path& image_tar,
|
||||
const std::vector<std::string>& requested_command, bool use_nsenter,
|
||||
const std::optional<std::string>& user, const std::optional<std::string>& group) {
|
||||
@@ -387,7 +412,8 @@ int main(int argc, char* argv[]) {
|
||||
case 'r':
|
||||
case 'c':
|
||||
case 'l':
|
||||
case 'v': {
|
||||
case 'v':
|
||||
case kListVolumesOpt: {
|
||||
Mode requested;
|
||||
switch (opt) {
|
||||
case 't':
|
||||
@@ -408,9 +434,12 @@ int main(int argc, char* argv[]) {
|
||||
case 'l':
|
||||
requested = Mode::kListImages;
|
||||
break;
|
||||
default:
|
||||
case 'v':
|
||||
requested = Mode::kVolume;
|
||||
break;
|
||||
default:
|
||||
requested = Mode::kListVolumes;
|
||||
break;
|
||||
}
|
||||
if (mode != Mode::kNone && mode != requested) {
|
||||
spdlog::error("multiple actions specified");
|
||||
@@ -484,6 +513,9 @@ int main(int argc, char* argv[]) {
|
||||
if (mode == Mode::kVolume) {
|
||||
return create_volume_command(mode_arg, argv[optind], config_path, *config);
|
||||
}
|
||||
if (mode == Mode::kListVolumes) {
|
||||
return list_volumes_command(*config);
|
||||
}
|
||||
if (mode == Mode::kRun) {
|
||||
std::vector<std::string> command(argv + optind, argv + argc);
|
||||
// As root, containers-storage mount doesn't need to reexec into a private
|
||||
|
||||
Reference in New Issue
Block a user