-
Notifications
You must be signed in to change notification settings - Fork 124
WIP: Vine Shared Filesystem Access via Symlinks #3976
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
base: master
Are you sure you want to change the base?
Changes from all commits
e4d04b5
05a94de
4dcdb3e
041a9aa
77d39e6
49282f7
54dfa10
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -126,7 +126,7 @@ def __del__(self): | |||||
| pass | ||||||
|
|
||||||
| @staticmethod | ||||||
| def _determine_mount_flags(watch=False, failure_only=False, success_only=False, strict_input=False, mount_symlink=False): | ||||||
| def _determine_mount_flags(watch=False, failure_only=False, success_only=False, strict_input=False, mount_hardlink=False ): | ||||||
| flags = cvine.VINE_TRANSFER_ALWAYS | ||||||
| if watch: | ||||||
| flags |= cvine.VINE_WATCH | ||||||
|
|
@@ -136,8 +136,8 @@ def _determine_mount_flags(watch=False, failure_only=False, success_only=False, | |||||
| flags |= cvine.VINE_SUCCESS_ONLY | ||||||
| if strict_input: | ||||||
| flags |= cvine.VINE_FIXED_LOCATION | ||||||
| if mount_symlink: | ||||||
| flags |= cvine.VINE_MOUNT_SYMLINK | ||||||
| if mount_hardlink: | ||||||
| flags |= cvine.VINE_MOUNT_HARDLINK | ||||||
| return flags | ||||||
|
|
||||||
| @staticmethod | ||||||
|
|
@@ -341,12 +341,12 @@ def add_feature(self, name): | |||||
| # >>> f = m.declare_untar(url) | ||||||
| # >>> task.add_input(f,"data") | ||||||
| # @endcode | ||||||
| def add_input(self, file, remote_name, strict_input=False, mount_symlink=False): | ||||||
| def add_input(self, file, remote_name, strict_input=False, mount_hardlink=False ): | ||||||
| # SWIG expects strings | ||||||
| if not isinstance(remote_name, str): | ||||||
| raise TypeError(f"remote_name {remote_name} is not a str") | ||||||
|
|
||||||
| flags = Task._determine_mount_flags(strict_input=strict_input, mount_symlink=mount_symlink) | ||||||
| flags = Task._determine_mount_flags(strict_input=strict_input,mount_hardlink=mount_hardlink) | ||||||
|
||||||
| flags = Task._determine_mount_flags(strict_input=strict_input,mount_hardlink=mount_hardlink) | |
| flags = Task._determine_mount_flags(strict_input=strict_input, mount_hardlink=mount_hardlink) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,27 @@ | ||
|
|
||
| #include "vine_symlink.h" | ||
|
Comment on lines
+1
to
+2
|
||
|
|
||
| #include "xxmalloc.h" | ||
| #include <stdlib.h> | ||
|
|
||
| struct vine_symlink *vine_symlink_create(const char *name, const char *target) | ||
| { | ||
| struct vine_symlink *s = malloc(sizeof(*s)); | ||
| s->name = xxstrdup(name); | ||
| s->target = xxstrdup(target); | ||
| return s; | ||
| } | ||
|
|
||
| struct vine_symlink *vine_symlink_copy(struct vine_symlink *s) | ||
| { | ||
| return vine_symlink_create(s->name, s->target); | ||
| } | ||
|
|
||
| void vine_symlink_delete(struct vine_symlink *s) | ||
| { | ||
| if (!s) | ||
| return; | ||
| free(s->name); | ||
| free(s->target); | ||
| free(s); | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,29 @@ | ||
| /* | ||
| Copyright (C) 2024- The University of Notre Dame | ||
| This software is distributed under the GNU General Public License. | ||
| See the file COPYING for details. | ||
| */ | ||
|
|
||
| #ifndef VINE_SYMLINK_H | ||
| #define VINE_SYMLINK_H | ||
|
|
||
| /* | ||
| A vine symlink is a lightweight structure representing a symlink | ||
| to be added to the sandbox namespace of a task as it runs. | ||
| Note that a symlink is not treated as either an input file | ||
| or an output file in the vine infrastructure, because there are | ||
| no file contents to cache or move. It is simply an addition | ||
| to the task namespace performed just prior to execution. | ||
| */ | ||
|
|
||
| struct vine_symlink { | ||
| char *name; | ||
| char *target; | ||
| }; | ||
|
|
||
| struct vine_symlink * vine_symlink_create( const char *name, const char *target ); | ||
| struct vine_symlink * vine_symlink_copy( struct vine_symlink *s ); | ||
| void vine_symlink_delete( struct vine_symlink *s ); | ||
|
|
||
| #endif | ||
|
|
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -9,6 +9,7 @@ See the file COPYING for details. | |
| #include "vine_cache_file.h" | ||
| #include "vine_file.h" | ||
| #include "vine_mount.h" | ||
| #include "vine_symlink.h" | ||
| #include "vine_task.h" | ||
| #include "vine_worker.h" | ||
|
|
||
|
|
@@ -81,20 +82,25 @@ static int stage_input_file(struct vine_process *p, struct vine_mount *m, struct | |
| vine_cache_status_t status; | ||
| status = vine_cache_ensure(cache, f->cached_name); | ||
| if (status == VINE_CACHE_STATUS_READY) { | ||
| /* The sandbox path is permitted to have leading directory elements. */ | ||
| create_dir_parents(sandbox_path, 0777); | ||
| debug(D_VINE, "input: link %s -> %s", cache_path, sandbox_path); | ||
| if (m->flags & VINE_MOUNT_SYMLINK) { | ||
| /* If the user has requested a symlink, just do that b/c it is faster for large dirs. */ | ||
|
|
||
| if(m->flags&VINE_MOUNT_HARDLINK) { | ||
| /* Rare case: If requested, hard-link each element of the cache object */ | ||
| /* This can be quite expensive for large directory trees. */ | ||
| debug(D_VINE, "input: hardlink %s -> %s", cache_path, sandbox_path); | ||
| result = file_link_recursive(cache_path, sandbox_path, 1); | ||
| } else { | ||
| /* Normally, just symlink from the sandbox to the cache path. */ | ||
| /* This is a relatively cheap operation, and most apps won't notice. */ | ||
| debug(D_VINE, "input: symlink %s -> %s", cache_path, sandbox_path); | ||
| result = symlink(cache_path, sandbox_path); | ||
| /* Change sense of Unix result to true/false. */ | ||
| result = !result; | ||
| } else { | ||
| /* Otherwise recursively hard-link the object into the sandbox. */ | ||
| result = file_link_recursive(cache_path, sandbox_path, 1); | ||
| } | ||
|
|
||
| if (!result) | ||
| debug(D_VINE, "couldn't link %s into sandbox as %s: %s", cache_path, sandbox_path, strerror(errno)); | ||
|
|
||
| } else { | ||
| debug(D_VINE, "input: %s is not ready in the cache!", f->cached_name); | ||
| result = 0; | ||
|
|
@@ -134,6 +140,7 @@ int vine_sandbox_stagein(struct vine_process *p, struct vine_cache *cache) | |
| int result = 1; | ||
|
|
||
| struct vine_mount *m; | ||
| struct vine_symlink *s; | ||
|
|
||
| /* For each input mount, stage it into the sandbox. */ | ||
|
|
||
|
|
@@ -144,6 +151,17 @@ int vine_sandbox_stagein(struct vine_process *p, struct vine_cache *cache) | |
| break; | ||
| } | ||
|
|
||
| /* For each requested symlink, create it in the sandbox */ | ||
|
|
||
| LIST_ITERATE(t->symlink_list, s) | ||
| { | ||
| result = symlink(s->target, s->name); | ||
| if (result != 0) { | ||
| debug(D_VINE, "unable to symlink %s -> %s: %s", s->name, s->target, strerror(errno)); | ||
| break; | ||
| } | ||
|
Comment on lines
+158
to
+162
|
||
| } | ||
|
|
||
| /* If any of the output mounts have the MKDIR flag, then create those empty dirs. */ | ||
|
|
||
| LIST_ITERATE(t->output_mounts, m) | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -116,19 +116,19 @@ def next_output_name(): | |||||||||
| t = q.wait(wait_time) | ||||||||||
| report_task(t, "success", 0, [path.join(test_dir, output_name)]) | ||||||||||
|
|
||||||||||
| # same thing, but this time symlink the input directory. | ||||||||||
| # same thing, but this time hardlink the input directory. | ||||||||||
| output_name = next_output_name() | ||||||||||
| t = vine.Task(f"cd my_dir && ./{exec_name} {input_name} 2>&1 > {output_name}") | ||||||||||
| in_dir = q.declare_file(test_dir, cache=True) | ||||||||||
| t.add_input(exec_file, exec_name) | ||||||||||
| t.add_input(in_dir, "my_dir", mount_symlink=True) | ||||||||||
| t.add_input(in_dir, "my_dir", mount_hardlink=True) | ||||||||||
| output_file = q.declare_file(path.join(test_dir, output_name), cache=False) | ||||||||||
| t.add_output(output_file, path.join("my_dir", output_name)) | ||||||||||
|
|
||||||||||
| q.submit(t) | ||||||||||
| t = q.wait(wait_time) | ||||||||||
| report_task(t, "success", 0, [path.join(test_dir, output_name)]) | ||||||||||
|
|
||||||||||
| # we bring back the outputs from a directory: | ||||||||||
| output_name = next_output_name() | ||||||||||
| t = vine.Task(f"mkdir outs && ./{exec_name} {input_name} 2>&1 > outs/{output_name}") | ||||||||||
|
|
@@ -141,6 +141,19 @@ def next_output_name(): | |||||||||
| t = q.wait(wait_time) | ||||||||||
| report_task(t, "success", 0, [path.join(test_dir, "outs", output_name)]) | ||||||||||
|
|
||||||||||
| # use symlink to access a shared filesystem: | ||||||||||
| output_name = next_output_name() | ||||||||||
| t = vine.Task(f"mkdir outs && ./{exec_name} {input_name} 2>&1 > outs/{output_name}") | ||||||||||
| t.add_symlink("input","infile" ) | ||||||||||
| t.add_symlink("output","outfile" ) | ||||||||||
|
Comment on lines
+147
to
+148
|
||||||||||
| t.add_symlink("input","infile" ) | |
| t.add_symlink("output","outfile" ) | |
| t.add_symlink("input", input_file) | |
| t.add_symlink("output", path.join(test_dir, "outs", output_name)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[nitpick] Extra space before the closing parenthesis. Should be
mount_hardlink=False)without the trailing space.