[cdc_rsync] Add initial support for Windows (#51)

Adds a ServerArch class whose job it is to encapsulate differences
between Windows and Linux cdc_rsync_servers. It detects the type
based on a heuristic in the destination path. This is not fool proof
and will probably require further work, like falling back to the other
type if the detected one doesn't work.

Uses the ServerArch class to determine the different commands to start
the server and to deploy the server.

Note that the functionality is not well tested on Windows yet, but
copying plain files works.
This commit is contained in:
Lutz Justen
2023-01-17 13:34:14 +01:00
committed by GitHub
parent af9038b4dd
commit a8b948b323
16 changed files with 455 additions and 94 deletions

View File

@@ -16,6 +16,8 @@
<ItemGroup>
<ClCompile Include="$(MSBuildThisFileDirectory)absl_helper\jedec_size_flag.cc" />
<ClCompile Include="$(MSBuildThisFileDirectory)cdc_rsync\base\socket.cc" />
<ClCompile Include="$(MSBuildThisFileDirectory)cdc_rsync\server_arch.cc" />
<ClCompile Include="$(MSBuildThisFileDirectory)cdc_rsync\server_arch_test.cc" />
<ClCompile Include="$(MSBuildThisFileDirectory)cdc_stream\asset_stream_config.cc" />
<ClCompile Include="$(MSBuildThisFileDirectory)cdc_stream\asset_stream_server.cc" />
<ClCompile Include="$(MSBuildThisFileDirectory)cdc_stream\background_service_impl.cc" />
@@ -37,6 +39,7 @@
<ClCompile Include="$(MSBuildThisFileDirectory)cdc_stream\stop_service_command.cc" />
<ClCompile Include="$(MSBuildThisFileDirectory)common\port_range_parser.cc" />
<ClCompile Include="$(MSBuildThisFileDirectory)common\port_range_parser_test.cc" />
<ClInclude Include="$(MSBuildThisFileDirectory)cdc_rsync\server_arch.h" />
<ClInclude Include="$(MSBuildThisFileDirectory)cdc_stream\stop_command.cc" />
<ClCompile Include="$(MSBuildThisFileDirectory)cdc_stream\testing_asset_stream_server.cc" />
<ClCompile Include="$(MSBuildThisFileDirectory)cdc_fuse_fs\asset.cc" />

View File

@@ -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"],

View File

@@ -66,20 +66,21 @@
</ItemDefinitionGroup>
<!-- Bazel setup -->
<PropertyGroup>
<BazelTargets>//cdc_rsync</BazelTargets>
<BazelTargets>//cdc_rsync //cdc_rsync_server</BazelTargets>
<BazelOutputFile>cdc_rsync.exe</BazelOutputFile>
<BazelIncludePaths>..\;..\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)</BazelIncludePaths>
</PropertyGroup>
<Import Project="..\NMakeBazelProject.targets" />
<!-- For some reason, msbuild doesn't include this file, so copy it explicitly. -->
<!-- TODO: Reenable once we can cross-compile these.
<!-- TODO: Reenable copying the Linux file once we can cross-compile these. -->
<PropertyGroup>
<CdcRsyncServerFile>$(SolutionDir)bazel-out\k8-$(BazelCompilationMode)\bin\cdc_rsync_server\cdc_rsync_server</CdcRsyncServerFile>
<!-- <CdcRsyncLinuxServerFile>$(SolutionDir)bazel-out\k8-$(BazelCompilationMode)\bin\cdc_rsync_server\cdc_rsync_server</CdcRsyncLinuxServerFile> -->
<CdcRsyncWindowsServerFile>$(SolutionDir)bazel-out\x64_windows-$(BazelCompilationMode)\bin\cdc_rsync_server\cdc_rsync_server.exe</CdcRsyncWindowsServerFile>
</PropertyGroup>
<Target Name="CopyServer" Inputs="$(CdcRsyncServerFile)" Outputs="$(OutDir)cdc_rsync_server" AfterTargets="Build">
<Copy SourceFiles="$(CdcRsyncServerFile)" DestinationFiles="$(OutDir)cdc_rsync_server" />
<Target Name="CopyServer" Inputs="$(CdcRsyncLinuxServerFile);$(CdcRsyncWindowsServerFile)" Outputs="$(OutDir)cdc_rsync_server;$(OutDir)cdc_rsync_server.exe" AfterTargets="Build">
<!-- <Copy SourceFiles="$(CdcRsyncLinuxServerFile)" DestinationFiles="$(OutDir)cdc_rsync_server" SkipUnchangedFiles="true" /> -->
<Copy SourceFiles="$(CdcRsyncWindowsServerFile)" DestinationFiles="$(OutDir)cdc_rsync_server.exe" SkipUnchangedFiles="true" />
</Target>
-->
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>

View File

@@ -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<int> 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<int> 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<GameletComponent> 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<int> 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");

View File

@@ -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<int> 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();

158
cdc_rsync/server_arch.cc Normal file
View File

@@ -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 <filesystem>
#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

76
cdc_rsync/server_arch.h Normal file
View File

@@ -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 <string>
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_

View File

@@ -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

View File

@@ -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<GameletComponent> 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;
}

View File

@@ -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.

View File

@@ -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.
"""

View File

@@ -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)

View File

@@ -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)

View File

@@ -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=[])

View File

@@ -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)

View File

@@ -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.