diff --git a/all_files.vcxitems b/all_files.vcxitems index 1dcab1d..9206eba 100644 --- a/all_files.vcxitems +++ b/all_files.vcxitems @@ -16,6 +16,8 @@ + + @@ -37,6 +39,7 @@ + diff --git a/cdc_rsync/BUILD b/cdc_rsync/BUILD index 5734cec..29a4fed 100644 --- a/cdc_rsync/BUILD +++ b/cdc_rsync/BUILD @@ -79,6 +79,7 @@ cc_library( ":file_finder_and_sender", ":parallel_file_opener", ":progress_tracker", + ":server_arch", ":zstd_stream", "//cdc_rsync/base:cdc_interface", "//cdc_rsync/base:message_pump", @@ -172,6 +173,28 @@ cc_test( ], ) +cc_library( + name = "server_arch", + srcs = ["server_arch.cc"], + hdrs = ["server_arch.h"], + deps = [ + "//common:path", + "//common:remote_util", + "@com_google_absl//absl/strings", + ], +) + +cc_test( + name = "server_arch_test", + srcs = ["server_arch_test.cc"], + deps = [ + ":server_arch", + "//common:test_main", + "@com_google_absl//absl/strings", + "@com_google_googletest//:gtest", + ], +) + cc_library( name = "zstd_stream", srcs = ["zstd_stream.cc"], diff --git a/cdc_rsync/cdc_rsync.vcxproj b/cdc_rsync/cdc_rsync.vcxproj index 4037160..c095d52 100644 --- a/cdc_rsync/cdc_rsync.vcxproj +++ b/cdc_rsync/cdc_rsync.vcxproj @@ -66,20 +66,21 @@ - //cdc_rsync + //cdc_rsync //cdc_rsync_server cdc_rsync.exe ..\;..\third_party\absl;..\bazel-cdc-file-transfer\external\com_github_blake3\c;..\bazel-stadia-file-transfer\external\com_github_zstd\lib;..\third_party\googletest\googletest\include;..\bazel-cdc-file-transfer\external\com_google_protobuf\src;$(VC_IncludePath);$(WindowsSDK_IncludePath) - - $(SolutionDir)bazel-out\k8-$(BazelCompilationMode)\bin\cdc_rsync_server\cdc_rsync_server + + $(SolutionDir)bazel-out\x64_windows-$(BazelCompilationMode)\bin\cdc_rsync_server\cdc_rsync_server.exe - - + + + - --> diff --git a/cdc_rsync/cdc_rsync_client.cc b/cdc_rsync/cdc_rsync_client.cc index 7d0f3af..677f7ec 100644 --- a/cdc_rsync/cdc_rsync_client.cc +++ b/cdc_rsync/cdc_rsync_client.cc @@ -25,6 +25,7 @@ #include "cdc_rsync/parallel_file_opener.h" #include "cdc_rsync/progress_tracker.h" #include "cdc_rsync/protos/messages.pb.h" +#include "cdc_rsync/server_arch.h" #include "cdc_rsync/zstd_stream.h" #include "common/gamelet_component.h" #include "common/log.h" @@ -44,8 +45,7 @@ constexpr int kExitCodeCouldNotExecute = 126; // Bash exit code if binary was not found. constexpr int kExitCodeNotFound = 127; -constexpr char kCdcServerFilename[] = "cdc_rsync_server"; -constexpr char kRemoteToolsBinDir[] = "~/.cache/cdc-file-transfer/bin/"; +constexpr char kCdcRsyncFilename[] = "cdc_rsync.exe"; SetOptionsRequest::FilterRule::Type ToProtoType(PathFilter::Rule::Type type) { switch (type) { @@ -120,16 +120,21 @@ CdcRsyncClient::~CdcRsyncClient() { } absl::Status CdcRsyncClient::Run() { + int port; + ASSIGN_OR_RETURN(port, FindAvailablePort(), "Failed to find available port"); + + ServerArch server_arch(ServerArch::Detect(destination_)); + // Start the server process. - absl::Status status = StartServer(); + absl::Status status = StartServer(port, server_arch); if (HasTag(status, Tag::kDeployServer)) { // Gamelet components are not deployed or out-dated. Deploy and retry. - status = DeployServer(); + status = DeployServer(server_arch); if (!status.ok()) { return WrapStatus(status, "Failed to deploy server"); } - status = StartServer(); + status = StartServer(port, server_arch); } if (!status.ok()) { return WrapStatus(status, "Failed to start server"); @@ -161,7 +166,27 @@ absl::Status CdcRsyncClient::Run() { return status; } -absl::Status CdcRsyncClient::StartServer() { +absl::StatusOr CdcRsyncClient::FindAvailablePort() { + // Find available local and remote ports for port forwarding. + // If only one port is in the given range, try that without checking. + if (options_.forward_port_first >= options_.forward_port_last) { + return options_.forward_port_first; + } + + absl::StatusOr port = + port_manager_.ReservePort(options_.connection_timeout_sec); + if (absl::IsDeadlineExceeded(port.status())) { + // Server didn't respond in time. + return SetTag(port.status(), Tag::kConnectionTimeout); + } + if (absl::IsResourceExhausted(port.status())) { + // Port in use. + return SetTag(port.status(), Tag::kAddressInUse); + } + return port; +} + +absl::Status CdcRsyncClient::StartServer(int port, const ServerArch& arch) { assert(!server_process_); // Components are expected to reside in the same dir as the executable. @@ -173,46 +198,19 @@ absl::Status CdcRsyncClient::StartServer() { std::vector components; status = GameletComponent::Get( - {path::Join(component_dir, kCdcServerFilename)}, &components); + {path::Join(component_dir, arch.CdcServerFilename())}, &components); if (!status.ok()) { return MakeStatus( "Required instance component not found. Make sure the file " - "cdc_rsync_server resides in the same folder as cdc_rsync.exe."); + "%s resides in the same folder as %s.", + arch.CdcServerFilename(), kCdcRsyncFilename); } std::string component_args = GameletComponent::ToCommandLineArgs(components); - - // Find available local and remote ports for port forwarding. - // If only one port is in the given range, try that without checking. - int port = options_.forward_port_first; - if (options_.forward_port_first < options_.forward_port_last) { - absl::StatusOr port_res = - port_manager_.ReservePort(options_.connection_timeout_sec); - constexpr char kErrorMsg[] = "Failed to find available port"; - if (absl::IsDeadlineExceeded(port_res.status())) { - // Server didn't respond in time. - return SetTag(WrapStatus(port_res.status(), kErrorMsg), - Tag::kConnectionTimeout); - } - if (absl::IsResourceExhausted(port_res.status())) - return SetTag(WrapStatus(port_res.status(), kErrorMsg), - Tag::kAddressInUse); - if (!port_res.ok()) - return WrapStatus(port_res.status(), "Failed to find available port"); - port = *port_res; - } - - std::string remote_server_path = - std::string(kRemoteToolsBinDir) + kCdcServerFilename; - // Test existence manually to prevent misleading bash output message - // "bash: .../cdc_rsync_server: No such file or directory". - // Also create the bin dir because otherwise scp below might fail. - std::string remote_command = - absl::StrFormat("mkdir -p %s; if [ ! -f %s ]; then exit %i; fi; %s %i %s", - kRemoteToolsBinDir, remote_server_path, kExitCodeNotFound, - remote_server_path, port, component_args); + std::string remote_command = arch.GetStartServerCommand( + kExitCodeNotFound, absl::StrFormat("%i %s", port, component_args)); ProcessStartInfo start_info = remote_util_.BuildProcessStartInfoForSshPortForwardAndCommand( - port, port, false, remote_command); + port, port, /*reverse=*/false, remote_command); start_info.name = "cdc_rsync_server"; // Capture stdout, but forward to stdout for debugging purposes. @@ -239,7 +237,7 @@ absl::Status CdcRsyncClient::StartServer() { if (!status.ok()) { // Some internal process error. Note that this does NOT mean that // cdc_rsync_server does not exist. In that case, the ssh process exits with - // code 127. + // code kExitCodeNotFound. return status; } if (is_timeout) { @@ -394,7 +392,7 @@ absl::Status CdcRsyncClient::Sync() { return status; } -absl::Status CdcRsyncClient::DeployServer() { +absl::Status CdcRsyncClient::DeployServer(const ServerArch& arch) { assert(!server_process_); std::string exe_dir; @@ -416,27 +414,21 @@ absl::Status CdcRsyncClient::DeployServer() { printer_.Print(deploy_msg, true, Util::GetConsoleWidth()); // scp cdc_rsync_server to a temp location on the gamelet. - std::string remoteServerTmpPath = - absl::StrFormat("%s%s.%s", kRemoteToolsBinDir, kCdcServerFilename, - Util::GenerateUniqueId()); - std::string localServerPath = path::Join(exe_dir, kCdcServerFilename); + std::string remoteServerPath = + arch.RemoteToolsBinDir(ServerArch::UseCase::kScp) + + arch.CdcServerFilename(); + std::string remoteServerTmpPath = remoteServerPath + Util::GenerateUniqueId(); + std::string localServerPath = path::Join(exe_dir, arch.CdcServerFilename()); status = remote_util_.Scp({localServerPath}, remoteServerTmpPath, /*compress=*/true); if (!status.ok()) { return WrapStatus(status, "Failed to copy cdc_rsync_server to instance"); } - // Do 3 things in one SSH command, to save time: - // - Make the old cdc_rsync_server writable (if it exists). - // - Make the new cdc_rsync_server executable. - // - Replace the old cdc_rsync_server by the new one. - std::string old_path = RemoteUtil::QuoteForWindows( - std::string(kRemoteToolsBinDir) + kCdcServerFilename); - std::string new_path = RemoteUtil::QuoteForWindows(remoteServerTmpPath); - std::string replace_cmd = absl::StrFormat( - " ([ ! -f %s ] || chmod u+w %s) && chmod a+x %s && mv %s %s", old_path, - old_path, new_path, new_path, old_path); - status = remote_util_.Run(replace_cmd, "chmod && chmod && mv"); + // Replace cdc_rsync_server file by the temp file. + std::string replace_cmd = + arch.GetDeployReplaceCommand(remoteServerPath, remoteServerTmpPath); + status = remote_util_.Run(replace_cmd, "replace"); if (!status.ok()) { return WrapStatus(status, "Failed to replace old cdc_rsync_server by new one"); diff --git a/cdc_rsync/cdc_rsync_client.h b/cdc_rsync/cdc_rsync_client.h index 055fb69..d6e9b63 100644 --- a/cdc_rsync/cdc_rsync_client.h +++ b/cdc_rsync/cdc_rsync_client.h @@ -31,6 +31,7 @@ namespace cdc_ft { class Process; +class ServerArch; class ZstdStream; class CdcRsyncClient { @@ -71,9 +72,12 @@ class CdcRsyncClient { absl::Status Run(); private: + // Finds available local and remote ports for port forwarding. + absl::StatusOr FindAvailablePort(); + // Starts the server process. If the method returns a status with tag // |kTagDeployServer|, Run() calls DeployServer() and tries again. - absl::Status StartServer(); + absl::Status StartServer(int port, const ServerArch& arch); // Stops the server process. absl::Status StopServer(); @@ -85,7 +89,7 @@ class CdcRsyncClient { absl::Status Sync(); // Copies all gamelet components to the gamelet. - absl::Status DeployServer(); + absl::Status DeployServer(const ServerArch& arch); // Sends relevant options to the server. absl::Status SendOptions(); diff --git a/cdc_rsync/server_arch.cc b/cdc_rsync/server_arch.cc new file mode 100644 index 0000000..d0fb0a5 --- /dev/null +++ b/cdc_rsync/server_arch.cc @@ -0,0 +1,158 @@ +// Copyright 2022 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "cdc_rsync/server_arch.h" + +#include + +#include "absl/strings/match.h" +#include "absl/strings/str_format.h" +#include "common/path.h" +#include "common/remote_util.h" + +namespace cdc_ft { + +constexpr char kErrorFailedToGetKnownFolderPath[] = + "error_failed_to_get_known_folder_path"; +constexpr char kErrorArchTypeUnhandled[] = "arch_type_unhandled"; + +// static +ServerArch::Type ServerArch::Detect(const std::string& destination) { + // Path starting with ~ or / -> Linux. + if (absl::StartsWith(destination, "~") || + absl::StartsWith(destination, "/")) { + return Type::kLinux; + } + + // Path starting with C: etc. -> Windows. + if (!path::GetDrivePrefix(destination).empty()) { + return Type::kWindows; + } + + // Path with only / -> Linux. + if (absl::StrContains(destination, "/") && + !absl::StrContains(destination, "\\")) { + return Type::kLinux; + } + + // Path with only \\ -> Windows. + if (absl::StrContains(destination, "\\") && + !absl::StrContains(destination, "/")) { + return Type::kWindows; + } + + // Default to Linux. + return Type::kLinux; +} + +ServerArch::ServerArch(Type type) : type_(type) {} + +ServerArch::~ServerArch() {} + +std::string ServerArch::CdcServerFilename() const { + switch (type_) { + case Type::kWindows: + return "cdc_rsync_server.exe"; + case Type::kLinux: + return "cdc_rsync_server"; + default: + assert(!kErrorArchTypeUnhandled); + return std::string(); + } +} + +std::string ServerArch::RemoteToolsBinDir(UseCase use_case) const { + switch (type_) { + case Type::kWindows: { + // TODO(ljusten): Unfortunately, scp doesn't seem to support shell var + // expansion, so %AppData% can't be used. This relies on scp copying + // files relative to %UserProfile% and %AppData% mapping to + // "AppData\\Roaming" relative to that. + std::string app_data = + use_case == UseCase::kScp ? "AppData\\Roaming" : "$env:appdata"; + return app_data + "\\cdc-file-transfer\\bin\\"; + } + case Type::kLinux: + return "~/.cache/cdc-file-transfer/bin/"; + default: + assert(!kErrorArchTypeUnhandled); + return std::string(); + } +} + +std::string ServerArch::GetStartServerCommand(int exit_code_not_found, + const std::string& args) const { + std::string server_dir = RemoteToolsBinDir(UseCase::kSsh); + std::string server_path = + RemoteUtil::QuoteForSsh(server_dir + CdcServerFilename()); + path::EnsureDoesNotEndWithPathSeparator(&server_dir); + server_dir = RemoteUtil::QuoteForSsh(server_dir); + + switch (type_) { + case Type::kWindows: + // TODO(ljusten): On Windows, ssh does not seem to forward the Powershell + // exit code (exit_code_not_found) to the process. However, that's really + // a minor issue and means we display "Deploying server..." instead of + // "Server not deployed. Deploying..."; + return RemoteUtil::QuoteForWindows( + absl::StrFormat("Set-StrictMode -Version 2; " + "$ErrorActionPreference = 'Stop'; " + "$server_dir = %s; " + "$server_path = %s; " + "$u=New-Item $server_dir -ItemType Directory -Force; " + "if (-not (Test-Path -Path $server_path)) { " + " exit %i; " + "} " + "& $server_path %s", + server_dir, server_path, exit_code_not_found, args)); + case Type::kLinux: + return absl::StrFormat( + "mkdir -p %s; if [ ! -f %s ]; then exit %i; fi; %s %s", server_dir, + server_path, exit_code_not_found, server_path, args); + default: + assert(!kErrorArchTypeUnhandled); + return std::string(); + } +} + +std::string ServerArch::GetDeployReplaceCommand( + const std::string& old_server_path, + const std::string& new_server_path) const { + const std::string old_path = RemoteUtil::QuoteForSsh(old_server_path); + const std::string new_path = RemoteUtil::QuoteForSsh(new_server_path); + + switch (type_) { + case Type::kWindows: + return RemoteUtil::QuoteForWindows(absl::StrFormat( + "Set-StrictMode -Version 2; " + "$ErrorActionPreference = 'Stop'; " + "$old_path = %s; " + "$new_path = %s; " + "if (Test-Path -Path $old_path) { " + " Get-Item -Path $old_path | " + " Set-ItemProperty -Name IsReadOnly -Value $false; " + "} " + "Move-Item -Path $new_path -Destination $old_path -Force", + old_path, new_path)); + case Type::kLinux: + return absl::StrFormat( + "([ ! -f %s ] || chmod u+w %s) && chmod a+x %s && mv %s %s", old_path, + old_path, new_path, new_path, old_path); + default: + assert(!kErrorArchTypeUnhandled); + return std::string(); + } +} + +} // namespace cdc_ft diff --git a/cdc_rsync/server_arch.h b/cdc_rsync/server_arch.h new file mode 100644 index 0000000..7be2ca0 --- /dev/null +++ b/cdc_rsync/server_arch.h @@ -0,0 +1,76 @@ +/* + * Copyright 2022 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef CDC_RSYNC_SERVER_ARCH_H_ +#define CDC_RSYNC_SERVER_ARCH_H_ + +#include + +namespace cdc_ft { + +// Abstracts all architecture specifics of cdc_rsync_server deployment. +class ServerArch { + public: + enum class Type { + kLinux = 0, + kWindows = 1, + }; + + enum class UseCase { kSsh = 0, kScp = 1 }; + + // Detects the architecture type based on the destination path, e.g. path + // starting with C: indicate Windows. + static Type Detect(const std::string& destination); + + ServerArch(Type type); + ~ServerArch(); + + // Returns the arch-specific filename of cdc_rsync_server[.exe]. + std::string CdcServerFilename() const; + + // Returns the arch-specific directory where cdc_rsync_server is deployed. + // On Windows, |use_case| determines what type of env variables to use: + // - kSsh uses $env:appdata and works for ssh commands. + // - kScp uses AppData\\Roaming and works for scp commands. + // On Linux, this flag is ignored. + std::string RemoteToolsBinDir(UseCase use_case) const; + + // Returns an arch-specific SSH shell command that gets invoked in order to + // start cdc_rsync_server. The command + // - creates RemoteToolsBinDir() if it does not exist (so that the server can + // be deployed there subsequently; scp can't create directories), + // - returns |exit_code_not_found| if cdc_rsync_server does not exist (to + // prevent the confusing bash output message + // "bash: .../cdc_rsync_server: No such file or directory"), and + // - runs the server with the provided |args|. + std::string GetStartServerCommand(int exit_code_not_found, + const std::string& args) const; + + // Returns an arch-specific SSH shell command that gets invoked after + // cdc_rsync_server has been copied to a temp location. The command + // - makes the old cdc_rsync_server writable (if it exists), + // - makes the new cdc_rsync_server executable (Linux only) and + // - replaces the old cdc_rsync_server by the new one. + std::string GetDeployReplaceCommand(const std::string& old_server_path, + const std::string& new_server_path) const; + + private: + Type type_; +}; + +} // namespace cdc_ft + +#endif // CDC_RSYNC_SERVER_ARCH_H_ diff --git a/cdc_rsync/server_arch_test.cc b/cdc_rsync/server_arch_test.cc new file mode 100644 index 0000000..0cb6f5b --- /dev/null +++ b/cdc_rsync/server_arch_test.cc @@ -0,0 +1,107 @@ +// Copyright 2022 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "cdc_rsync/server_arch.h" + +#include "absl/strings/match.h" +#include "gtest/gtest.h" + +namespace cdc_ft { +namespace { + +constexpr auto kLinux = ServerArch::Type::kLinux; +constexpr auto kWindows = ServerArch::Type::kWindows; + +TEST(ServerArchTest, DetectsLinuxIfPathStartsWithSlashOrTilde) { + EXPECT_EQ(ServerArch::Detect("/linux/path"), kLinux); + EXPECT_EQ(ServerArch::Detect("/linux\\path"), kLinux); + EXPECT_EQ(ServerArch::Detect("~/linux/path"), kLinux); + EXPECT_EQ(ServerArch::Detect("~/linux\\path"), kLinux); + EXPECT_EQ(ServerArch::Detect("~\\linux\\path"), kLinux); +} + +TEST(ServerArchTest, DetectsWindowsIfPathStartsWithDrive) { + EXPECT_EQ(ServerArch::Detect("C:\\win\\path"), kWindows); + EXPECT_EQ(ServerArch::Detect("D:win"), kWindows); + EXPECT_EQ(ServerArch::Detect("Z:\\win/path"), kWindows); +} + +TEST(ServerArchTest, DetectsLinuxIfPathOnlyHasForwardSlashes) { + EXPECT_EQ(ServerArch::Detect("linux/path"), kLinux); +} + +TEST(ServerArchTest, DetectsWindowsIfPathOnlyHasBackSlashes) { + EXPECT_EQ(ServerArch::Detect("\\win\\path"), kWindows); +} + +TEST(ServerArchTest, DetectsLinuxByDefault) { + EXPECT_EQ(ServerArch::Detect("/mixed\\path"), kLinux); + EXPECT_EQ(ServerArch::Detect("/mixed\\path"), kLinux); + EXPECT_EQ(ServerArch::Detect("C\\linux/path"), kLinux); + EXPECT_EQ(ServerArch::Detect(""), kLinux); +} + +TEST(ServerArchTest, CdcServerFilename) { + EXPECT_FALSE( + absl::StrContains(ServerArch(kLinux).CdcServerFilename(), "exe")); + EXPECT_TRUE( + absl::StrContains(ServerArch(kWindows).CdcServerFilename(), "exe")); +} + +TEST(ServerArchTest, RemoteToolsBinDir) { + const std::string linux_ssh_dir = + ServerArch(kLinux).RemoteToolsBinDir(ServerArch::UseCase::kSsh); + EXPECT_TRUE(absl::StrContains(linux_ssh_dir, "/")); + + const std::string linux_scp_dir = + ServerArch(kLinux).RemoteToolsBinDir(ServerArch::UseCase::kScp); + EXPECT_EQ(linux_ssh_dir, linux_scp_dir); + + const std::string win_ssh_dir = + ServerArch(kWindows).RemoteToolsBinDir(ServerArch::UseCase::kSsh); + EXPECT_TRUE(absl::StrContains(win_ssh_dir, "\\")); + EXPECT_TRUE(absl::StrContains(win_ssh_dir, "$env:appdata")); + + std::string win_scp_dir = + ServerArch(kWindows).RemoteToolsBinDir(ServerArch::UseCase::kScp); + EXPECT_TRUE(absl::StrContains(win_scp_dir, "\\")); + EXPECT_TRUE(absl::StrContains(win_scp_dir, "AppData\\Roaming")); +} + +TEST(ServerArchTest, GetStartServerCommand) { + std::string cmd = ServerArch(kWindows).GetStartServerCommand(123, "foo bar"); + EXPECT_TRUE(absl::StrContains(cmd, "123")); + EXPECT_TRUE(absl::StrContains(cmd, "foo bar")); + EXPECT_TRUE(absl::StrContains(cmd, "New-Item ")); + + cmd = ServerArch(kLinux).GetStartServerCommand(123, "foo bar"); + EXPECT_TRUE(absl::StrContains(cmd, "123")); + EXPECT_TRUE(absl::StrContains(cmd, "foo bar")); + EXPECT_TRUE(absl::StrContains(cmd, "mkdir -p")); +} + +TEST(ServerArchTest, GetDeployReplaceCommand) { + std::string cmd = ServerArch(kWindows).GetDeployReplaceCommand("aaa", "bbb"); + EXPECT_TRUE(absl::StrContains(cmd, "aaa")); + EXPECT_TRUE(absl::StrContains(cmd, "bbb")); + EXPECT_TRUE(absl::StrContains(cmd, "Move-Item ")); + + cmd = ServerArch(kLinux).GetDeployReplaceCommand("aaa", "bbb"); + EXPECT_TRUE(absl::StrContains(cmd, "aaa")); + EXPECT_TRUE(absl::StrContains(cmd, "bbb")); + EXPECT_TRUE(absl::StrContains(cmd, "mv ")); +} + +} // namespace +} // namespace cdc_ft diff --git a/cdc_rsync_server/cdc_rsync_server.cc b/cdc_rsync_server/cdc_rsync_server.cc index f7a01f2..c96f9ca 100644 --- a/cdc_rsync_server/cdc_rsync_server.cc +++ b/cdc_rsync_server/cdc_rsync_server.cc @@ -35,6 +35,12 @@ namespace { // Suffix for the patched file created from the basis file and the diff. constexpr char kIntermediatePathSuffix[] = ".__cdc_rsync_temp__"; +#if PLATFORM_WINDOWS +constexpr char kServerFilename[] = "cdc_rsync_server.exe"; +#elif PLATFORM_LINUX +constexpr char kServerFilename[] = "cdc_rsync_server"; +#endif + uint16_t kExecutableBits = path::MODE_IXUSR | path::MODE_IXGRP | path::MODE_IXOTH; @@ -160,8 +166,8 @@ bool CdcRsyncServer::CheckComponents( } std::vector our_components; - status = GameletComponent::Get( - {path::Join(component_dir, "cdc_rsync_server")}, &our_components); + status = GameletComponent::Get({path::Join(component_dir, kServerFilename)}, + &our_components); if (!status.ok() || components != our_components) { return false; } diff --git a/integration_tests/cdc_rsync/connection_test.py b/integration_tests/cdc_rsync/connection_test.py index dd3085d..4cc44e5 100644 --- a/integration_tests/cdc_rsync/connection_test.py +++ b/integration_tests/cdc_rsync/connection_test.py @@ -57,7 +57,7 @@ class ConnectionTest(test_base.CdcRsyncTest): res = utils.run_rsync(self.local_data_path, bad_host + ":" + self.remote_base_dir) self.assertEqual(res.returncode, RETURN_CODE_GENERIC_ERROR) - self.assertIn('lost connection', str(res.stderr)) + self.assertIn('Failed to find available ports', str(res.stderr)) def test_contimeout(self): """Runs rsync with --contimeout option for an invalid ip. diff --git a/integration_tests/cdc_rsync/deployment_test.py b/integration_tests/cdc_rsync/deployment_test.py index 7a628a9..e38414b 100644 --- a/integration_tests/cdc_rsync/deployment_test.py +++ b/integration_tests/cdc_rsync/deployment_test.py @@ -45,9 +45,9 @@ class DeploymentTest(test_base.CdcRsyncTest): utils.get_ssh_command_output('rm %s' % (REMOTE_FOLDER + file + '.tmp')) def test_no_server(self): - """Checks that cdc_rsync_server is uploaded if not present on the gamelet. + """Checks that cdc_rsync_server is uploaded if not present remotely. - 1) Wipes ‘/opt/developer/tools/bin/’ on the gamelet. + 1) Wipes |REMOTE_FOLDER|. 2) Uploads a file. 3) Verifies that cdc_rsync_server exists in that folder. """ diff --git a/integration_tests/cdc_rsync/upload_test.py b/integration_tests/cdc_rsync/upload_test.py index 61c58be..cfcbb53 100644 --- a/integration_tests/cdc_rsync/upload_test.py +++ b/integration_tests/cdc_rsync/upload_test.py @@ -71,15 +71,6 @@ class UploadTest(test_base.CdcRsyncTest): self.assertTrue( utils.sha1_matches(self.local_data_path, self.remote_data_path)) - def test_backslash_in_dest_folder(self): - r"""Verifies uploading to \mnt\developer.""" - - filepath = os.path.join(self.local_base_dir, 'file1.txt') - utils.create_test_file(filepath, 1) - res = utils.run_rsync(filepath, self.remote_base_dir.replace('/', '\\')) - self.assertTrue(utils.files_count_is(res, missing=1)) - self._assert_remote_dir_contains(['file1.txt']) - def test_backslash_in_source_folder(self): r"""Verifies uploading from /source/folder.""" @@ -858,7 +849,7 @@ class UploadTest(test_base.CdcRsyncTest): remote_exe_path = self.remote_base_dir + os.path.basename(local_exe_path) remote_elf_path = self.remote_base_dir + os.path.basename(local_elf_path) - # Copy the files to the gamelet. + # Copy the files to the remote instance. res = utils.run_rsync(local_exe_path, local_elf_path, self.remote_base_dir) self._assert_rsync_success(res) diff --git a/integration_tests/cdc_stream/consistency_test.py b/integration_tests/cdc_stream/consistency_test.py index 842c02a..41b2747 100644 --- a/integration_tests/cdc_stream/consistency_test.py +++ b/integration_tests/cdc_stream/consistency_test.py @@ -216,7 +216,7 @@ class ConsistencyTest(test_base.CdcStreamTest): return files, dirs def _recreate_data(self, files, dirs): - """Recreate test data and check that it can be read on a gamelet. + """Recreate test data and check that it can be read on the remote instance. Args: files (list of strings): List of relative file paths. @@ -396,7 +396,7 @@ class ConsistencyTest(test_base.CdcStreamTest): return sha1sum_local def sha1sum_remote_batch(self): - """Calculate sha1sum of files in the streamed directory on the gamelet. + """Calculate sha1sum of files in the remote streamed directory. Returns: string: Concatenated sha1 hashes with relative posix file names. @@ -413,7 +413,7 @@ class ConsistencyTest(test_base.CdcStreamTest): return sha1sum_remote def _test_random_dir_content(self, files, dirs): - """Check the streamed randomly generated directory's content on gamelet. + """Check the streamed randomly generated remote directory's content. Args: files (list of strings): List of relative file paths to check. @@ -460,7 +460,7 @@ class ConsistencyTest(test_base.CdcStreamTest): """ self._mount_with_data(files, dirs) - # Remove directory on workstation => empty directory on gamelet. + # Remove directory on workstation => empty remote directory. utils.get_ssh_command_output(self.ls_cmd) utils.remove_test_directory(self.local_base_dir) diff --git a/integration_tests/cdc_stream/directory_test.py b/integration_tests/cdc_stream/directory_test.py index 3105e2f..f4cfdfa 100644 --- a/integration_tests/cdc_stream/directory_test.py +++ b/integration_tests/cdc_stream/directory_test.py @@ -59,7 +59,7 @@ class DirectoryTest(test_base.CdcStreamTest): self._assert_cdc_fuse_mounted() original = utils.get_ssh_command_output(self.ls_cmd) - # Remove directory on workstation => empty directory on gamelet. + # Remove directory on workstation => empty remote directory. utils.remove_test_directory(self.local_base_dir) self.assertTrue(self._wait_until_remote_dir_changed(original)) self._test_dir_content(files=[], dirs=[]) @@ -101,7 +101,7 @@ class DirectoryTest(test_base.CdcStreamTest): self._assert_cdc_fuse_mounted() original = utils.get_ssh_command_output(self.ls_cmd) - # Remove directory on workstation => empty directory on gamelet. + # Remove directory on workstation => empty remote directory. utils.remove_test_directory(self.local_base_dir) self.assertTrue(self._wait_until_remote_dir_changed(original)) self._test_dir_content(files=[], dirs=[]) diff --git a/integration_tests/cdc_stream/general_test.py b/integration_tests/cdc_stream/general_test.py index 9d77d54..9ff084c 100644 --- a/integration_tests/cdc_stream/general_test.py +++ b/integration_tests/cdc_stream/general_test.py @@ -40,7 +40,7 @@ class GeneralTest(test_base.CdcStreamTest): self._assert_cdc_fuse_mounted() def test_update_file(self): - """File updates are visible on gamelet.""" + """File updates are visible on remote instance.""" filename = 'file1.txt' utils.create_test_file(os.path.join(self.local_base_dir, filename), 1024) self._start() @@ -56,7 +56,7 @@ class GeneralTest(test_base.CdcStreamTest): self.assertGreater(self._get_cache_size_in_bytes(), cache_size) def test_add_file(self): - """New file is visible on gamelet.""" + """New file is visible on remote instance.""" self._start() self._test_dir_content(files=[], dirs=[]) cache_size = self._get_cache_size_in_bytes() @@ -71,7 +71,7 @@ class GeneralTest(test_base.CdcStreamTest): self.assertGreater(self._get_cache_size_in_bytes(), cache_size) def test_change_mtime(self): - """Change of mtime is visible on gamelet.""" + """Change of mtime is visible on remote instance.""" filename = 'file1.txt' file_local_path = os.path.join(self.local_base_dir, filename) utils.create_test_file(file_local_path, 1024) @@ -92,7 +92,7 @@ class GeneralTest(test_base.CdcStreamTest): self.assertGreater(self._get_cache_size_in_bytes(), cache_size) def test_remove_file(self): - """File removal is visible on gamelet.""" + """File removal is visible on remote instance.""" filename = 'file1.txt' file_local_path = os.path.join(self.local_base_dir, filename) utils.create_test_file(file_local_path, 1024) @@ -127,7 +127,7 @@ class GeneralTest(test_base.CdcStreamTest): self.assertGreater(self._get_cache_size_in_bytes(), cache_size) def test_add_directory(self): - """A new directory is visible on gamelet.""" + """A new directory is visible on remote instance.""" self._start() self._test_dir_content(files=[], dirs=[]) cache_size = self._get_cache_size_in_bytes() @@ -143,7 +143,7 @@ class GeneralTest(test_base.CdcStreamTest): self.assertGreater(self._get_cache_size_in_bytes(), cache_size) def test_remove_directory(self): - """A directory removal is visible on gamelet.""" + """A directory removal is visible on remote instance.""" directory = 'dir1\\' dir_local_path = os.path.join(self.local_base_dir, directory) @@ -162,7 +162,7 @@ class GeneralTest(test_base.CdcStreamTest): self.assertGreater(self._get_cache_size_in_bytes(), cache_size) def test_rename_directory(self): - """A renamed directory is visible on gamelet.""" + """A renamed directory is visible on remote instance.""" directory = 'dir1\\' dir_local_path = os.path.join(self.local_base_dir, directory) @@ -183,7 +183,7 @@ class GeneralTest(test_base.CdcStreamTest): self.assertGreater(self._get_cache_size_in_bytes(), cache_size) def test_detect_executables(self): - """Executable bits are propagated to gamelet.""" + """Executable bits are propagated to remote instance.""" # Add an .exe, an ELF file and a .sh file to the streamed directory. cdc_stream_dir = os.path.dirname(utils.CDC_STREAM_PATH) exe_filename = os.path.basename(utils.CDC_STREAM_PATH) diff --git a/integration_tests/cdc_stream/test_base.py b/integration_tests/cdc_stream/test_base.py index c3d8ec7..bdf70f4 100644 --- a/integration_tests/cdc_stream/test_base.py +++ b/integration_tests/cdc_stream/test_base.py @@ -227,7 +227,7 @@ class CdcStreamTest(unittest.TestCase): return False def _test_dir_content(self, files, dirs, is_exe=False): - """Check the streamed directory's content on gamelet. + """Check the streamed directory's content on remote instance. Args: files (list of strings): List of relative file paths to check.