Skip to content

Commit

Permalink
issue-2674: filestore-client ln command + support for RenameNode, Unl…
Browse files Browse the repository at this point in the history
…inkNode + basic support for RenameNode in filesystems with directories in shards (#2823)
  • Loading branch information
qkrorlqr authored Jan 10, 2025
1 parent 3c4d811 commit b580cd5
Show file tree
Hide file tree
Showing 10 changed files with 133 additions and 10 deletions.
14 changes: 14 additions & 0 deletions cloud/filestore/apps/client/lib/command.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -532,6 +532,20 @@ NProto::TListNodesResponse TFileStoreCommand::ListAll(
return fullResult;
}

TString TFileStoreCommand::ReadLink(ISession& session, ui64 nodeId)
{
auto request = CreateRequest<NProto::TReadLinkRequest>();
request->SetNodeId(nodeId);

auto response = WaitFor(session.ReadLink(
PrepareCallContext(),
std::move(request)));

CheckResponse(response);

return response.GetSymLink();
}

////////////////////////////////////////////////////////////////////////////////

TEndpointCommand::TEndpointCommand()
Expand Down
1 change: 1 addition & 0 deletions cloud/filestore/apps/client/lib/command.h
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,7 @@ class TFileStoreCommand
const TString& fsId,
ui64 parentId,
bool disableMultiTabletForwarding);
TString ReadLink(ISession& session, ui64 nodeId);

class TSessionGuard final
{
Expand Down
11 changes: 11 additions & 0 deletions cloud/filestore/apps/client/lib/diff.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,17 @@ class TDiffCommand final
DumpLine(RDiff() + "DATA: ", rhash);
}
}

if (lattr.GetType() == NProto::E_LINK_NODE
&& rattr.GetType() == NProto::E_LINK_NODE)
{
const auto lcontent = ReadLink(lsession, lattr.GetId());
const auto rcontent = ReadLink(rsession, rattr.GetId());
if (lcontent != rcontent) {
DumpLine(LDiff() + "LINK: ", lcontent);
DumpLine(RDiff() + "LINK: ", rcontent);
}
}
}

bool Execute() override
Expand Down
2 changes: 2 additions & 0 deletions cloud/filestore/apps/client/lib/factory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ TCommandPtr NewKickEndpointCommand();
TCommandPtr NewListClusterNodesCommand();
TCommandPtr NewListEndpointsCommand();
TCommandPtr NewListFileStoresCommand();
TCommandPtr NewLnCommand();
TCommandPtr NewLsCommand();
TCommandPtr NewMkDirCommand();
TCommandPtr NewMountCommand();
Expand Down Expand Up @@ -61,6 +62,7 @@ static const TMap<TString, TFactoryFunc> Commands = {
{ "listclusternodes", NewListClusterNodesCommand },
{ "listendpoints", NewListEndpointsCommand },
{ "listfilestores", NewListFileStoresCommand },
{ "ln", NewLnCommand },
{ "ls", NewLsCommand },
{ "mkdir", NewMkDirCommand },
{ "mount", NewMountCommand },
Expand Down
67 changes: 67 additions & 0 deletions cloud/filestore/apps/client/lib/ln.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
#include "command.h"

#include <cloud/filestore/public/api/protos/fs.pb.h>

#include <util/stream/file.h>
#include <util/system/sysstat.h>

namespace NCloud::NFileStore::NClient {

namespace {

////////////////////////////////////////////////////////////////////////////////

class TLnCommand final
: public TFileStoreCommand
{
private:
TString Path;
TString SymLink;

public:
TLnCommand()
{
Opts.AddLongOption("path")
.Required()
.RequiredArgument("PATH")
.StoreResult(&Path);

Opts.AddLongOption("symlink")
.Required()
.RequiredArgument("PATH")
.StoreResult(&SymLink);
}

bool Execute() override
{
auto sessionGuard = CreateSession();
auto& session = sessionGuard.AccessSession();

auto resolved = ResolvePath(session, Path, true);

Y_ENSURE(resolved.size() >= 2, "root node can't be ln target");

auto request = CreateRequest<NProto::TCreateNodeRequest>();
request->SetNodeId(resolved[resolved.size() - 2].Node.GetId());
request->SetName(TString(resolved.back().Name));
request->MutableSymLink()->SetTargetPath(SymLink);

auto response = WaitFor(session.CreateNode(
PrepareCallContext(),
std::move(request)));

CheckResponse(response);
return true;
}
};

} // namespace

////////////////////////////////////////////////////////////////////////////////

TCommandPtr NewLnCommand()
{
return std::make_shared<TLnCommand>();
}

} // namespace NCloud::NFileStore::NClient
1 change: 1 addition & 0 deletions cloud/filestore/apps/client/lib/ya.make
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ SRCS(
list_cluster_nodes.cpp
list_endpoints.cpp
list_filestores.cpp
ln.cpp
ls.cpp
mkdir.cpp
mount.cpp
Expand Down
7 changes: 4 additions & 3 deletions cloud/filestore/libs/storage/api/service.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,6 @@ namespace NCloud::NFileStore::NStorage {
xxx(DestroyCheckpoint, __VA_ARGS__) \
\
xxx(ResolvePath, __VA_ARGS__) \
xxx(UnlinkNode, __VA_ARGS__) \
xxx(RenameNode, __VA_ARGS__) \
xxx(ReadLink, __VA_ARGS__) \
\
// FILESTORE_SERVICE_REQUESTS_FWD

Expand All @@ -44,6 +41,10 @@ namespace NCloud::NFileStore::NStorage {
xxx(SetNodeXAttr, __VA_ARGS__) \
xxx(ListNodeXAttr, __VA_ARGS__) \
xxx(RemoveNodeXAttr, __VA_ARGS__) \
\
xxx(UnlinkNode, __VA_ARGS__) \
xxx(RenameNode, __VA_ARGS__) \
xxx(ReadLink, __VA_ARGS__) \
// FILESTORE_SERVICE_REQUESTS_FWD_TO_SHARD_BY_NODE_ID

#define FILESTORE_SERVICE_REQUESTS_FWD_TO_SHARD_BY_HANDLE(xxx, ...) \
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,5 @@
"ShardFileSystemId": "masked_for_test_stability",
"Name": "a1"
}
]
]- LINK: /does/not/matter/2
+ LINK: /does/not/matter/3
33 changes: 27 additions & 6 deletions cloud/filestore/tests/client_sharded_dir/test.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,23 +52,30 @@ def __write_some_data(client, fs_id, path, data):
client.write(fs_id, path, "--data", data_file)


_DIR = 1
_FILE = 2
_SYMLINK = 3


class FsItem:

def __init__(self, path, is_dir, data):
def __init__(self, path, node_type, data):
self.path = path
self.is_dir = is_dir
self.node_type = node_type
self.data = data


def __fill_fs(client, fs_id, items):
for item in items:
if item.is_dir:
if item.node_type == _DIR:
client.mkdir(fs_id, item.path)
else:
elif item.node_type == _FILE:
if item.data is not None:
__write_some_data(client, fs_id, item.path, item.data)
else:
client.touch(fs_id, item.path)
else:
client.ln(fs_id, item.path, "--symlink", item.data)


def test_nonsharded_vs_sharded_fs():
Expand All @@ -87,10 +94,13 @@ def test_nonsharded_vs_sharded_fs():
3 * int(SHARD_SIZE / BLOCK_SIZE))

def _d(path):
return FsItem(path, True, None)
return FsItem(path, _DIR, None)

def _f(path, data=None):
return FsItem(path, False, data)
return FsItem(path, _FILE, data)

def _l(path, symlink):
return FsItem(path, _SYMLINK, symlink)

items = [
_d("/a0"),
Expand All @@ -117,12 +127,23 @@ def _f(path, data=None):
_f("/a1/b2/f14.txt", "zzzzz4"),
_d("/a1/b2/c1"),
_f("/a1/b2/f15.txt", "ZZZZZZZZZZZ"),
_l("/a1/b2/l1", "/does/not/matter"),
_f("/a1/b2/f16.txt", "ZZZZZZZZZZZ2"),
_d("/a1/b3"),
]

__fill_fs(client, "fs0", items)
__fill_fs(client, "fs1", items)

# checking that mv, rm and ln work properly
client.mv("fs0", "/a0/b0/c0/d0/f9.txt", "/a0/b0/c0/d0/f9_moved.txt")
client.mv("fs1", "/a0/b0/c0/d0/f9.txt", "/a0/b0/c0/d0/f9_moved.txt")
client.rm("fs0", "/a1/b2/f16.txt")
client.rm("fs1", "/a1/b2/f16.txt")
# checking that readlink works (indirectly - via diff)
client.ln("fs0", "/a1/b2/l2", "--symlink", "/does/not/matter/2")
client.ln("fs1", "/a1/b2/l2", "--symlink", "/does/not/matter/3")

out = __exec_ls(client, "fs0", "/", "--disable-multitablet-forwarding")
out += __exec_ls(client, "fs1", "/", "--disable-multitablet-forwarding")
out += client.diff("fs0", "fs1")
Expand Down
4 changes: 4 additions & 0 deletions cloud/filestore/tests/python/lib/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -354,6 +354,10 @@ def touch(self, cmd):
def rm(self, cmd):
return common.execute(cmd, env=self.__env, check_exit_code=self.__check_exit_code).stdout

@standard_command("ln")
def ln(self, cmd):
return common.execute(cmd, env=self.__env, check_exit_code=self.__check_exit_code).stdout


def create_endpoint(client, filesystem, socket_path, socket_prefix, endpoint_storage_dir, mount_seqno=0, readonly=False):
_uid = str(uuid.uuid4())
Expand Down

0 comments on commit b580cd5

Please sign in to comment.