mirror of
https://github.com/nestriness/cdc-file-transfer.git
synced 2026-01-30 12:25:35 +02:00
[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:
@@ -16,6 +16,8 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClCompile Include="$(MSBuildThisFileDirectory)absl_helper\jedec_size_flag.cc" />
|
<ClCompile Include="$(MSBuildThisFileDirectory)absl_helper\jedec_size_flag.cc" />
|
||||||
<ClCompile Include="$(MSBuildThisFileDirectory)cdc_rsync\base\socket.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_config.cc" />
|
||||||
<ClCompile Include="$(MSBuildThisFileDirectory)cdc_stream\asset_stream_server.cc" />
|
<ClCompile Include="$(MSBuildThisFileDirectory)cdc_stream\asset_stream_server.cc" />
|
||||||
<ClCompile Include="$(MSBuildThisFileDirectory)cdc_stream\background_service_impl.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)cdc_stream\stop_service_command.cc" />
|
||||||
<ClCompile Include="$(MSBuildThisFileDirectory)common\port_range_parser.cc" />
|
<ClCompile Include="$(MSBuildThisFileDirectory)common\port_range_parser.cc" />
|
||||||
<ClCompile Include="$(MSBuildThisFileDirectory)common\port_range_parser_test.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" />
|
<ClInclude Include="$(MSBuildThisFileDirectory)cdc_stream\stop_command.cc" />
|
||||||
<ClCompile Include="$(MSBuildThisFileDirectory)cdc_stream\testing_asset_stream_server.cc" />
|
<ClCompile Include="$(MSBuildThisFileDirectory)cdc_stream\testing_asset_stream_server.cc" />
|
||||||
<ClCompile Include="$(MSBuildThisFileDirectory)cdc_fuse_fs\asset.cc" />
|
<ClCompile Include="$(MSBuildThisFileDirectory)cdc_fuse_fs\asset.cc" />
|
||||||
|
|||||||
@@ -79,6 +79,7 @@ cc_library(
|
|||||||
":file_finder_and_sender",
|
":file_finder_and_sender",
|
||||||
":parallel_file_opener",
|
":parallel_file_opener",
|
||||||
":progress_tracker",
|
":progress_tracker",
|
||||||
|
":server_arch",
|
||||||
":zstd_stream",
|
":zstd_stream",
|
||||||
"//cdc_rsync/base:cdc_interface",
|
"//cdc_rsync/base:cdc_interface",
|
||||||
"//cdc_rsync/base:message_pump",
|
"//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(
|
cc_library(
|
||||||
name = "zstd_stream",
|
name = "zstd_stream",
|
||||||
srcs = ["zstd_stream.cc"],
|
srcs = ["zstd_stream.cc"],
|
||||||
|
|||||||
@@ -66,20 +66,21 @@
|
|||||||
</ItemDefinitionGroup>
|
</ItemDefinitionGroup>
|
||||||
<!-- Bazel setup -->
|
<!-- Bazel setup -->
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<BazelTargets>//cdc_rsync</BazelTargets>
|
<BazelTargets>//cdc_rsync //cdc_rsync_server</BazelTargets>
|
||||||
<BazelOutputFile>cdc_rsync.exe</BazelOutputFile>
|
<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>
|
<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>
|
</PropertyGroup>
|
||||||
<Import Project="..\NMakeBazelProject.targets" />
|
<Import Project="..\NMakeBazelProject.targets" />
|
||||||
<!-- For some reason, msbuild doesn't include this file, so copy it explicitly. -->
|
<!-- 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>
|
<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>
|
</PropertyGroup>
|
||||||
<Target Name="CopyServer" Inputs="$(CdcRsyncServerFile)" Outputs="$(OutDir)cdc_rsync_server" AfterTargets="Build">
|
<Target Name="CopyServer" Inputs="$(CdcRsyncLinuxServerFile);$(CdcRsyncWindowsServerFile)" Outputs="$(OutDir)cdc_rsync_server;$(OutDir)cdc_rsync_server.exe" AfterTargets="Build">
|
||||||
<Copy SourceFiles="$(CdcRsyncServerFile)" DestinationFiles="$(OutDir)cdc_rsync_server" />
|
<!-- <Copy SourceFiles="$(CdcRsyncLinuxServerFile)" DestinationFiles="$(OutDir)cdc_rsync_server" SkipUnchangedFiles="true" /> -->
|
||||||
|
<Copy SourceFiles="$(CdcRsyncWindowsServerFile)" DestinationFiles="$(OutDir)cdc_rsync_server.exe" SkipUnchangedFiles="true" />
|
||||||
</Target>
|
</Target>
|
||||||
-->
|
|
||||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||||
<ImportGroup Label="ExtensionTargets">
|
<ImportGroup Label="ExtensionTargets">
|
||||||
</ImportGroup>
|
</ImportGroup>
|
||||||
|
|||||||
@@ -25,6 +25,7 @@
|
|||||||
#include "cdc_rsync/parallel_file_opener.h"
|
#include "cdc_rsync/parallel_file_opener.h"
|
||||||
#include "cdc_rsync/progress_tracker.h"
|
#include "cdc_rsync/progress_tracker.h"
|
||||||
#include "cdc_rsync/protos/messages.pb.h"
|
#include "cdc_rsync/protos/messages.pb.h"
|
||||||
|
#include "cdc_rsync/server_arch.h"
|
||||||
#include "cdc_rsync/zstd_stream.h"
|
#include "cdc_rsync/zstd_stream.h"
|
||||||
#include "common/gamelet_component.h"
|
#include "common/gamelet_component.h"
|
||||||
#include "common/log.h"
|
#include "common/log.h"
|
||||||
@@ -44,8 +45,7 @@ constexpr int kExitCodeCouldNotExecute = 126;
|
|||||||
// Bash exit code if binary was not found.
|
// Bash exit code if binary was not found.
|
||||||
constexpr int kExitCodeNotFound = 127;
|
constexpr int kExitCodeNotFound = 127;
|
||||||
|
|
||||||
constexpr char kCdcServerFilename[] = "cdc_rsync_server";
|
constexpr char kCdcRsyncFilename[] = "cdc_rsync.exe";
|
||||||
constexpr char kRemoteToolsBinDir[] = "~/.cache/cdc-file-transfer/bin/";
|
|
||||||
|
|
||||||
SetOptionsRequest::FilterRule::Type ToProtoType(PathFilter::Rule::Type type) {
|
SetOptionsRequest::FilterRule::Type ToProtoType(PathFilter::Rule::Type type) {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
@@ -120,16 +120,21 @@ CdcRsyncClient::~CdcRsyncClient() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
absl::Status CdcRsyncClient::Run() {
|
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.
|
// Start the server process.
|
||||||
absl::Status status = StartServer();
|
absl::Status status = StartServer(port, server_arch);
|
||||||
if (HasTag(status, Tag::kDeployServer)) {
|
if (HasTag(status, Tag::kDeployServer)) {
|
||||||
// Gamelet components are not deployed or out-dated. Deploy and retry.
|
// Gamelet components are not deployed or out-dated. Deploy and retry.
|
||||||
status = DeployServer();
|
status = DeployServer(server_arch);
|
||||||
if (!status.ok()) {
|
if (!status.ok()) {
|
||||||
return WrapStatus(status, "Failed to deploy server");
|
return WrapStatus(status, "Failed to deploy server");
|
||||||
}
|
}
|
||||||
|
|
||||||
status = StartServer();
|
status = StartServer(port, server_arch);
|
||||||
}
|
}
|
||||||
if (!status.ok()) {
|
if (!status.ok()) {
|
||||||
return WrapStatus(status, "Failed to start server");
|
return WrapStatus(status, "Failed to start server");
|
||||||
@@ -161,7 +166,27 @@ absl::Status CdcRsyncClient::Run() {
|
|||||||
return status;
|
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_);
|
assert(!server_process_);
|
||||||
|
|
||||||
// Components are expected to reside in the same dir as the executable.
|
// Components are expected to reside in the same dir as the executable.
|
||||||
@@ -173,46 +198,19 @@ absl::Status CdcRsyncClient::StartServer() {
|
|||||||
|
|
||||||
std::vector<GameletComponent> components;
|
std::vector<GameletComponent> components;
|
||||||
status = GameletComponent::Get(
|
status = GameletComponent::Get(
|
||||||
{path::Join(component_dir, kCdcServerFilename)}, &components);
|
{path::Join(component_dir, arch.CdcServerFilename())}, &components);
|
||||||
if (!status.ok()) {
|
if (!status.ok()) {
|
||||||
return MakeStatus(
|
return MakeStatus(
|
||||||
"Required instance component not found. Make sure the file "
|
"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);
|
std::string component_args = GameletComponent::ToCommandLineArgs(components);
|
||||||
|
std::string remote_command = arch.GetStartServerCommand(
|
||||||
// Find available local and remote ports for port forwarding.
|
kExitCodeNotFound, absl::StrFormat("%i %s", port, component_args));
|
||||||
// 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);
|
|
||||||
ProcessStartInfo start_info =
|
ProcessStartInfo start_info =
|
||||||
remote_util_.BuildProcessStartInfoForSshPortForwardAndCommand(
|
remote_util_.BuildProcessStartInfoForSshPortForwardAndCommand(
|
||||||
port, port, false, remote_command);
|
port, port, /*reverse=*/false, remote_command);
|
||||||
start_info.name = "cdc_rsync_server";
|
start_info.name = "cdc_rsync_server";
|
||||||
|
|
||||||
// Capture stdout, but forward to stdout for debugging purposes.
|
// Capture stdout, but forward to stdout for debugging purposes.
|
||||||
@@ -239,7 +237,7 @@ absl::Status CdcRsyncClient::StartServer() {
|
|||||||
if (!status.ok()) {
|
if (!status.ok()) {
|
||||||
// Some internal process error. Note that this does NOT mean that
|
// 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
|
// cdc_rsync_server does not exist. In that case, the ssh process exits with
|
||||||
// code 127.
|
// code kExitCodeNotFound.
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
if (is_timeout) {
|
if (is_timeout) {
|
||||||
@@ -394,7 +392,7 @@ absl::Status CdcRsyncClient::Sync() {
|
|||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
absl::Status CdcRsyncClient::DeployServer() {
|
absl::Status CdcRsyncClient::DeployServer(const ServerArch& arch) {
|
||||||
assert(!server_process_);
|
assert(!server_process_);
|
||||||
|
|
||||||
std::string exe_dir;
|
std::string exe_dir;
|
||||||
@@ -416,27 +414,21 @@ absl::Status CdcRsyncClient::DeployServer() {
|
|||||||
printer_.Print(deploy_msg, true, Util::GetConsoleWidth());
|
printer_.Print(deploy_msg, true, Util::GetConsoleWidth());
|
||||||
|
|
||||||
// scp cdc_rsync_server to a temp location on the gamelet.
|
// scp cdc_rsync_server to a temp location on the gamelet.
|
||||||
std::string remoteServerTmpPath =
|
std::string remoteServerPath =
|
||||||
absl::StrFormat("%s%s.%s", kRemoteToolsBinDir, kCdcServerFilename,
|
arch.RemoteToolsBinDir(ServerArch::UseCase::kScp) +
|
||||||
Util::GenerateUniqueId());
|
arch.CdcServerFilename();
|
||||||
std::string localServerPath = path::Join(exe_dir, kCdcServerFilename);
|
std::string remoteServerTmpPath = remoteServerPath + Util::GenerateUniqueId();
|
||||||
|
std::string localServerPath = path::Join(exe_dir, arch.CdcServerFilename());
|
||||||
status = remote_util_.Scp({localServerPath}, remoteServerTmpPath,
|
status = remote_util_.Scp({localServerPath}, remoteServerTmpPath,
|
||||||
/*compress=*/true);
|
/*compress=*/true);
|
||||||
if (!status.ok()) {
|
if (!status.ok()) {
|
||||||
return WrapStatus(status, "Failed to copy cdc_rsync_server to instance");
|
return WrapStatus(status, "Failed to copy cdc_rsync_server to instance");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Do 3 things in one SSH command, to save time:
|
// Replace cdc_rsync_server file by the temp file.
|
||||||
// - Make the old cdc_rsync_server writable (if it exists).
|
std::string replace_cmd =
|
||||||
// - Make the new cdc_rsync_server executable.
|
arch.GetDeployReplaceCommand(remoteServerPath, remoteServerTmpPath);
|
||||||
// - Replace the old cdc_rsync_server by the new one.
|
status = remote_util_.Run(replace_cmd, "replace");
|
||||||
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");
|
|
||||||
if (!status.ok()) {
|
if (!status.ok()) {
|
||||||
return WrapStatus(status,
|
return WrapStatus(status,
|
||||||
"Failed to replace old cdc_rsync_server by new one");
|
"Failed to replace old cdc_rsync_server by new one");
|
||||||
|
|||||||
@@ -31,6 +31,7 @@
|
|||||||
namespace cdc_ft {
|
namespace cdc_ft {
|
||||||
|
|
||||||
class Process;
|
class Process;
|
||||||
|
class ServerArch;
|
||||||
class ZstdStream;
|
class ZstdStream;
|
||||||
|
|
||||||
class CdcRsyncClient {
|
class CdcRsyncClient {
|
||||||
@@ -71,9 +72,12 @@ class CdcRsyncClient {
|
|||||||
absl::Status Run();
|
absl::Status Run();
|
||||||
|
|
||||||
private:
|
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
|
// Starts the server process. If the method returns a status with tag
|
||||||
// |kTagDeployServer|, Run() calls DeployServer() and tries again.
|
// |kTagDeployServer|, Run() calls DeployServer() and tries again.
|
||||||
absl::Status StartServer();
|
absl::Status StartServer(int port, const ServerArch& arch);
|
||||||
|
|
||||||
// Stops the server process.
|
// Stops the server process.
|
||||||
absl::Status StopServer();
|
absl::Status StopServer();
|
||||||
@@ -85,7 +89,7 @@ class CdcRsyncClient {
|
|||||||
absl::Status Sync();
|
absl::Status Sync();
|
||||||
|
|
||||||
// Copies all gamelet components to the gamelet.
|
// Copies all gamelet components to the gamelet.
|
||||||
absl::Status DeployServer();
|
absl::Status DeployServer(const ServerArch& arch);
|
||||||
|
|
||||||
// Sends relevant options to the server.
|
// Sends relevant options to the server.
|
||||||
absl::Status SendOptions();
|
absl::Status SendOptions();
|
||||||
|
|||||||
158
cdc_rsync/server_arch.cc
Normal file
158
cdc_rsync/server_arch.cc
Normal 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
76
cdc_rsync/server_arch.h
Normal 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_
|
||||||
107
cdc_rsync/server_arch_test.cc
Normal file
107
cdc_rsync/server_arch_test.cc
Normal 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
|
||||||
@@ -35,6 +35,12 @@ namespace {
|
|||||||
// Suffix for the patched file created from the basis file and the diff.
|
// Suffix for the patched file created from the basis file and the diff.
|
||||||
constexpr char kIntermediatePathSuffix[] = ".__cdc_rsync_temp__";
|
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 =
|
uint16_t kExecutableBits =
|
||||||
path::MODE_IXUSR | path::MODE_IXGRP | path::MODE_IXOTH;
|
path::MODE_IXUSR | path::MODE_IXGRP | path::MODE_IXOTH;
|
||||||
|
|
||||||
@@ -160,8 +166,8 @@ bool CdcRsyncServer::CheckComponents(
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::vector<GameletComponent> our_components;
|
std::vector<GameletComponent> our_components;
|
||||||
status = GameletComponent::Get(
|
status = GameletComponent::Get({path::Join(component_dir, kServerFilename)},
|
||||||
{path::Join(component_dir, "cdc_rsync_server")}, &our_components);
|
&our_components);
|
||||||
if (!status.ok() || components != our_components) {
|
if (!status.ok() || components != our_components) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ class ConnectionTest(test_base.CdcRsyncTest):
|
|||||||
res = utils.run_rsync(self.local_data_path,
|
res = utils.run_rsync(self.local_data_path,
|
||||||
bad_host + ":" + self.remote_base_dir)
|
bad_host + ":" + self.remote_base_dir)
|
||||||
self.assertEqual(res.returncode, RETURN_CODE_GENERIC_ERROR)
|
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):
|
def test_contimeout(self):
|
||||||
"""Runs rsync with --contimeout option for an invalid ip.
|
"""Runs rsync with --contimeout option for an invalid ip.
|
||||||
|
|||||||
@@ -45,9 +45,9 @@ class DeploymentTest(test_base.CdcRsyncTest):
|
|||||||
utils.get_ssh_command_output('rm %s' % (REMOTE_FOLDER + file + '.tmp'))
|
utils.get_ssh_command_output('rm %s' % (REMOTE_FOLDER + file + '.tmp'))
|
||||||
|
|
||||||
def test_no_server(self):
|
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.
|
2) Uploads a file.
|
||||||
3) Verifies that cdc_rsync_server exists in that folder.
|
3) Verifies that cdc_rsync_server exists in that folder.
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -71,15 +71,6 @@ class UploadTest(test_base.CdcRsyncTest):
|
|||||||
self.assertTrue(
|
self.assertTrue(
|
||||||
utils.sha1_matches(self.local_data_path, self.remote_data_path))
|
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):
|
def test_backslash_in_source_folder(self):
|
||||||
r"""Verifies uploading from /source/folder."""
|
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_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)
|
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)
|
res = utils.run_rsync(local_exe_path, local_elf_path, self.remote_base_dir)
|
||||||
self._assert_rsync_success(res)
|
self._assert_rsync_success(res)
|
||||||
|
|
||||||
|
|||||||
@@ -216,7 +216,7 @@ class ConsistencyTest(test_base.CdcStreamTest):
|
|||||||
return files, dirs
|
return files, dirs
|
||||||
|
|
||||||
def _recreate_data(self, 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:
|
Args:
|
||||||
files (list of strings): List of relative file paths.
|
files (list of strings): List of relative file paths.
|
||||||
@@ -396,7 +396,7 @@ class ConsistencyTest(test_base.CdcStreamTest):
|
|||||||
return sha1sum_local
|
return sha1sum_local
|
||||||
|
|
||||||
def sha1sum_remote_batch(self):
|
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:
|
Returns:
|
||||||
string: Concatenated sha1 hashes with relative posix file names.
|
string: Concatenated sha1 hashes with relative posix file names.
|
||||||
@@ -413,7 +413,7 @@ class ConsistencyTest(test_base.CdcStreamTest):
|
|||||||
return sha1sum_remote
|
return sha1sum_remote
|
||||||
|
|
||||||
def _test_random_dir_content(self, files, dirs):
|
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:
|
Args:
|
||||||
files (list of strings): List of relative file paths to check.
|
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)
|
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.get_ssh_command_output(self.ls_cmd)
|
||||||
utils.remove_test_directory(self.local_base_dir)
|
utils.remove_test_directory(self.local_base_dir)
|
||||||
|
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ class DirectoryTest(test_base.CdcStreamTest):
|
|||||||
self._assert_cdc_fuse_mounted()
|
self._assert_cdc_fuse_mounted()
|
||||||
original = utils.get_ssh_command_output(self.ls_cmd)
|
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)
|
utils.remove_test_directory(self.local_base_dir)
|
||||||
self.assertTrue(self._wait_until_remote_dir_changed(original))
|
self.assertTrue(self._wait_until_remote_dir_changed(original))
|
||||||
self._test_dir_content(files=[], dirs=[])
|
self._test_dir_content(files=[], dirs=[])
|
||||||
@@ -101,7 +101,7 @@ class DirectoryTest(test_base.CdcStreamTest):
|
|||||||
self._assert_cdc_fuse_mounted()
|
self._assert_cdc_fuse_mounted()
|
||||||
original = utils.get_ssh_command_output(self.ls_cmd)
|
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)
|
utils.remove_test_directory(self.local_base_dir)
|
||||||
self.assertTrue(self._wait_until_remote_dir_changed(original))
|
self.assertTrue(self._wait_until_remote_dir_changed(original))
|
||||||
self._test_dir_content(files=[], dirs=[])
|
self._test_dir_content(files=[], dirs=[])
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ class GeneralTest(test_base.CdcStreamTest):
|
|||||||
self._assert_cdc_fuse_mounted()
|
self._assert_cdc_fuse_mounted()
|
||||||
|
|
||||||
def test_update_file(self):
|
def test_update_file(self):
|
||||||
"""File updates are visible on gamelet."""
|
"""File updates are visible on remote instance."""
|
||||||
filename = 'file1.txt'
|
filename = 'file1.txt'
|
||||||
utils.create_test_file(os.path.join(self.local_base_dir, filename), 1024)
|
utils.create_test_file(os.path.join(self.local_base_dir, filename), 1024)
|
||||||
self._start()
|
self._start()
|
||||||
@@ -56,7 +56,7 @@ class GeneralTest(test_base.CdcStreamTest):
|
|||||||
self.assertGreater(self._get_cache_size_in_bytes(), cache_size)
|
self.assertGreater(self._get_cache_size_in_bytes(), cache_size)
|
||||||
|
|
||||||
def test_add_file(self):
|
def test_add_file(self):
|
||||||
"""New file is visible on gamelet."""
|
"""New file is visible on remote instance."""
|
||||||
self._start()
|
self._start()
|
||||||
self._test_dir_content(files=[], dirs=[])
|
self._test_dir_content(files=[], dirs=[])
|
||||||
cache_size = self._get_cache_size_in_bytes()
|
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)
|
self.assertGreater(self._get_cache_size_in_bytes(), cache_size)
|
||||||
|
|
||||||
def test_change_mtime(self):
|
def test_change_mtime(self):
|
||||||
"""Change of mtime is visible on gamelet."""
|
"""Change of mtime is visible on remote instance."""
|
||||||
filename = 'file1.txt'
|
filename = 'file1.txt'
|
||||||
file_local_path = os.path.join(self.local_base_dir, filename)
|
file_local_path = os.path.join(self.local_base_dir, filename)
|
||||||
utils.create_test_file(file_local_path, 1024)
|
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)
|
self.assertGreater(self._get_cache_size_in_bytes(), cache_size)
|
||||||
|
|
||||||
def test_remove_file(self):
|
def test_remove_file(self):
|
||||||
"""File removal is visible on gamelet."""
|
"""File removal is visible on remote instance."""
|
||||||
filename = 'file1.txt'
|
filename = 'file1.txt'
|
||||||
file_local_path = os.path.join(self.local_base_dir, filename)
|
file_local_path = os.path.join(self.local_base_dir, filename)
|
||||||
utils.create_test_file(file_local_path, 1024)
|
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)
|
self.assertGreater(self._get_cache_size_in_bytes(), cache_size)
|
||||||
|
|
||||||
def test_add_directory(self):
|
def test_add_directory(self):
|
||||||
"""A new directory is visible on gamelet."""
|
"""A new directory is visible on remote instance."""
|
||||||
self._start()
|
self._start()
|
||||||
self._test_dir_content(files=[], dirs=[])
|
self._test_dir_content(files=[], dirs=[])
|
||||||
cache_size = self._get_cache_size_in_bytes()
|
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)
|
self.assertGreater(self._get_cache_size_in_bytes(), cache_size)
|
||||||
|
|
||||||
def test_remove_directory(self):
|
def test_remove_directory(self):
|
||||||
"""A directory removal is visible on gamelet."""
|
"""A directory removal is visible on remote instance."""
|
||||||
directory = 'dir1\\'
|
directory = 'dir1\\'
|
||||||
dir_local_path = os.path.join(self.local_base_dir, directory)
|
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)
|
self.assertGreater(self._get_cache_size_in_bytes(), cache_size)
|
||||||
|
|
||||||
def test_rename_directory(self):
|
def test_rename_directory(self):
|
||||||
"""A renamed directory is visible on gamelet."""
|
"""A renamed directory is visible on remote instance."""
|
||||||
directory = 'dir1\\'
|
directory = 'dir1\\'
|
||||||
dir_local_path = os.path.join(self.local_base_dir, directory)
|
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)
|
self.assertGreater(self._get_cache_size_in_bytes(), cache_size)
|
||||||
|
|
||||||
def test_detect_executables(self):
|
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.
|
# Add an .exe, an ELF file and a .sh file to the streamed directory.
|
||||||
cdc_stream_dir = os.path.dirname(utils.CDC_STREAM_PATH)
|
cdc_stream_dir = os.path.dirname(utils.CDC_STREAM_PATH)
|
||||||
exe_filename = os.path.basename(utils.CDC_STREAM_PATH)
|
exe_filename = os.path.basename(utils.CDC_STREAM_PATH)
|
||||||
|
|||||||
@@ -227,7 +227,7 @@ class CdcStreamTest(unittest.TestCase):
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
def _test_dir_content(self, files, dirs, is_exe=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:
|
Args:
|
||||||
files (list of strings): List of relative file paths to check.
|
files (list of strings): List of relative file paths to check.
|
||||||
|
|||||||
Reference in New Issue
Block a user