mirror of
https://github.com/nestriness/cdc-file-transfer.git
synced 2026-05-01 19:33:06 +03:00
[cdc_stream] Add a CLI client to start/stop asset streaming sessions (#4)
Implements the cdc_stream client and adjusts asset streaming in various places to work better outside of a GGP environment. This CL tries to get quoting for SSH commands right. It also brings back the ability to start a streaming session from asset_stream_manager. Also cleans up Bazel targets setup. Since the sln file is now in root, it is no longer necessary to prepend ../ to relative filenames to make clicking on errors work.
This commit is contained in:
@@ -9,6 +9,7 @@ cc_binary(
|
||||
deps = [
|
||||
":asset_stream_config",
|
||||
":session_management_server",
|
||||
"//cdc_stream",
|
||||
"//common:log",
|
||||
"//common:path",
|
||||
"//common:sdk_util",
|
||||
|
||||
@@ -26,9 +26,6 @@
|
||||
#include "common/status_macros.h"
|
||||
#include "json/json.h"
|
||||
|
||||
ABSL_DECLARE_FLAG(std::string, src_dir);
|
||||
ABSL_DECLARE_FLAG(std::string, instance_ip);
|
||||
ABSL_DECLARE_FLAG(uint16_t, instance_port);
|
||||
ABSL_DECLARE_FLAG(int, verbosity);
|
||||
ABSL_DECLARE_FLAG(bool, debug);
|
||||
ABSL_DECLARE_FLAG(bool, singlethreaded);
|
||||
@@ -42,6 +39,14 @@ ABSL_DECLARE_FLAG(uint32_t, access_idle_timeout);
|
||||
ABSL_DECLARE_FLAG(int, manifest_updater_threads);
|
||||
ABSL_DECLARE_FLAG(int, file_change_wait_duration_ms);
|
||||
|
||||
// Development flags.
|
||||
ABSL_DECLARE_FLAG(std::string, dev_src_dir);
|
||||
ABSL_DECLARE_FLAG(std::string, dev_user_host);
|
||||
ABSL_DECLARE_FLAG(uint16_t, dev_ssh_port);
|
||||
ABSL_DECLARE_FLAG(std::string, dev_ssh_command);
|
||||
ABSL_DECLARE_FLAG(std::string, dev_scp_command);
|
||||
ABSL_DECLARE_FLAG(std::string, dev_mount_dir);
|
||||
|
||||
// Declare AS20 flags, so that AS30 can be used on older SDKs simply by
|
||||
// replacing the binary. Note that the RETIRED_FLAGS macro can't be used
|
||||
// because the flags contain dashes. This code mimics the macro.
|
||||
@@ -62,9 +67,6 @@ const auto RETIRED_FLAGS_REG_allow_edge =
|
||||
namespace cdc_ft {
|
||||
|
||||
AssetStreamConfig::AssetStreamConfig() {
|
||||
src_dir_ = absl::GetFlag(FLAGS_src_dir);
|
||||
instance_ip_ = absl::GetFlag(FLAGS_instance_ip);
|
||||
instance_port_ = absl::GetFlag(FLAGS_instance_port);
|
||||
session_cfg_.verbosity = absl::GetFlag(FLAGS_verbosity);
|
||||
session_cfg_.fuse_debug = absl::GetFlag(FLAGS_debug);
|
||||
session_cfg_.fuse_singlethreaded = absl::GetFlag(FLAGS_singlethreaded);
|
||||
@@ -80,6 +82,13 @@ AssetStreamConfig::AssetStreamConfig() {
|
||||
absl::GetFlag(FLAGS_manifest_updater_threads);
|
||||
session_cfg_.file_change_wait_duration_ms =
|
||||
absl::GetFlag(FLAGS_file_change_wait_duration_ms);
|
||||
|
||||
dev_src_dir_ = absl::GetFlag(FLAGS_dev_src_dir);
|
||||
dev_target_.user_host = absl::GetFlag(FLAGS_dev_user_host);
|
||||
dev_target_.ssh_port = absl::GetFlag(FLAGS_dev_ssh_port);
|
||||
dev_target_.ssh_command = absl::GetFlag(FLAGS_dev_ssh_command);
|
||||
dev_target_.scp_command = absl::GetFlag(FLAGS_dev_scp_command);
|
||||
dev_target_.mount_dir = absl::GetFlag(FLAGS_dev_mount_dir);
|
||||
}
|
||||
|
||||
AssetStreamConfig::~AssetStreamConfig() = default;
|
||||
@@ -105,7 +114,6 @@ absl::Status AssetStreamConfig::LoadFromFile(const std::string& path) {
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
ASSIGN_VAR(src_dir_, src_dir, String);
|
||||
ASSIGN_VAR(session_cfg_.verbosity, verbosity, Int);
|
||||
ASSIGN_VAR(session_cfg_.fuse_debug, debug, Bool);
|
||||
ASSIGN_VAR(session_cfg_.fuse_singlethreaded, singlethreaded, Bool);
|
||||
@@ -144,7 +152,6 @@ absl::Status AssetStreamConfig::LoadFromFile(const std::string& path) {
|
||||
|
||||
std::string AssetStreamConfig::ToString() {
|
||||
std::ostringstream ss;
|
||||
ss << "src_dir = " << src_dir_ << std::endl;
|
||||
ss << "verbosity = " << session_cfg_.verbosity
|
||||
<< std::endl;
|
||||
ss << "debug = " << session_cfg_.fuse_debug
|
||||
@@ -166,6 +173,14 @@ std::string AssetStreamConfig::ToString() {
|
||||
<< session_cfg_.manifest_updater_threads << std::endl;
|
||||
ss << "file_change_wait_duration_ms = "
|
||||
<< session_cfg_.file_change_wait_duration_ms << std::endl;
|
||||
ss << "dev_src_dir = " << dev_src_dir_ << std::endl;
|
||||
ss << "dev_user_host = " << dev_target_.user_host << std::endl;
|
||||
ss << "dev_ssh_port = " << dev_target_.ssh_port << std::endl;
|
||||
ss << "dev_ssh_command = " << dev_target_.ssh_command
|
||||
<< std::endl;
|
||||
ss << "dev_scp_command = " << dev_target_.scp_command
|
||||
<< std::endl;
|
||||
ss << "dev_mount_dir = " << dev_target_.mount_dir << std::endl;
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
|
||||
#include "absl/status/status.h"
|
||||
#include "asset_stream_manager/session_config.h"
|
||||
#include "session.h"
|
||||
|
||||
namespace cdc_ft {
|
||||
|
||||
@@ -38,7 +39,6 @@ class AssetStreamConfig {
|
||||
// Loads a configuration from the JSON file at |path| and overrides any config
|
||||
// values that are set in this file. Sample json file:
|
||||
// {
|
||||
// "src_dir":"C:\\path\\to\\assets",
|
||||
// "verbosity":3,
|
||||
// "debug":0,
|
||||
// "singlethreaded":0,
|
||||
@@ -67,34 +67,29 @@ class AssetStreamConfig {
|
||||
// read from the JSON file.
|
||||
std::string GetFlagReadErrors();
|
||||
|
||||
// Workstation directory to stream. Should usually be empty since mounts are
|
||||
// triggered by the CLI or the partner portal via a gRPC call, but useful
|
||||
// during development.
|
||||
const std::string& src_dir() const { return src_dir_; }
|
||||
|
||||
// IP address of the instance to stream to. Should usually be empty since
|
||||
// mounts are triggered by the CLI or the partner portal via a gRPC call, but
|
||||
// useful during development.
|
||||
const std::string& instance_ip() const { return instance_ip_; }
|
||||
|
||||
// IP address of the instance to stream to. Should usually be unset (0) since
|
||||
// mounts are triggered by the CLI or the partner portal via a gRPC call, but
|
||||
// useful during development.
|
||||
const uint16_t instance_port() const { return instance_port_; }
|
||||
|
||||
// Session configuration.
|
||||
const SessionConfig session_cfg() const { return session_cfg_; }
|
||||
const SessionConfig& session_cfg() const { return session_cfg_; }
|
||||
|
||||
// Workstation directory to be streamed. Used for development purposes only
|
||||
// to start a session right away when the service starts up. See dev CLI args.
|
||||
const std::string& dev_src_dir() const { return dev_src_dir_; }
|
||||
|
||||
// Session target. Used for development purposes only to start a session right
|
||||
// away when the service starts up. See dev CLI args.
|
||||
const SessionTarget& dev_target() const { return dev_target_; }
|
||||
|
||||
// Whether to log to a file or to stdout.
|
||||
bool log_to_stdout() const { return log_to_stdout_; }
|
||||
|
||||
private:
|
||||
std::string src_dir_;
|
||||
std::string instance_ip_;
|
||||
uint16_t instance_port_ = 0;
|
||||
SessionConfig session_cfg_;
|
||||
bool log_to_stdout_ = false;
|
||||
|
||||
// Configuration used for development. Allows users to specify a session
|
||||
// via the service's command line.
|
||||
std::string dev_src_dir_;
|
||||
SessionTarget dev_target_;
|
||||
|
||||
// Use a set, so the flags are sorted alphabetically.
|
||||
std::set<std::string> flags_read_from_file_;
|
||||
|
||||
|
||||
@@ -66,10 +66,9 @@
|
||||
</ItemDefinitionGroup>
|
||||
<!-- Bazel setup -->
|
||||
<PropertyGroup>
|
||||
<BazelTargets>//asset_stream_manager</BazelTargets>
|
||||
<BazelTargets>//asset_stream_manager //cdc_stream</BazelTargets>
|
||||
<BazelOutputFile>asset_stream_manager.exe</BazelOutputFile>
|
||||
<BazelIncludePaths>..\;..\third_party\absl;..\third_party\jsoncpp\include;..\third_party\blake3\c;..\third_party\googletest\googletest\include;..\third_party\protobuf\src;..\third_party\grpc\include;..\bazel-out\x64_windows-dbg\bin;$(VC_IncludePath);$(WindowsSDK_IncludePath)</BazelIncludePaths>
|
||||
<BazelSourcePathPrefix>..\/</BazelSourcePathPrefix>
|
||||
</PropertyGroup>
|
||||
<Import Project="..\NMakeBazelProject.targets" />
|
||||
<!-- For some reason, msbuild doesn't include this file, so copy it explicitly. -->
|
||||
|
||||
@@ -29,13 +29,10 @@ namespace {
|
||||
constexpr char kFuseFilename[] = "cdc_fuse_fs";
|
||||
constexpr char kLibFuseFilename[] = "libfuse.so";
|
||||
constexpr char kFuseStdoutPrefix[] = "cdc_fuse_fs_stdout";
|
||||
constexpr char kRemoteToolsBinDir[] = "~/.cache/cdc_file_transfer/";
|
||||
|
||||
// Mount point for FUSE on the gamelet.
|
||||
constexpr char kMountDir[] = "/mnt/workstation";
|
||||
constexpr char kRemoteToolsBinDir[] = "~/.cache/cdc-file-transfer/bin/";
|
||||
|
||||
// Cache directory on the gamelet to store data chunks.
|
||||
constexpr char kCacheDir[] = "/var/cache/asset_streaming";
|
||||
constexpr char kCacheDir[] = "~/.cache/cdc-file-transfer/chunks";
|
||||
|
||||
} // namespace
|
||||
|
||||
@@ -56,23 +53,14 @@ absl::Status CdcFuseManager::Deploy() {
|
||||
std::string exe_dir;
|
||||
RETURN_IF_ERROR(path::GetExeDir(&exe_dir), "Failed to get exe directory");
|
||||
|
||||
std::string local_exe_path = path::Join(exe_dir, kFuseFilename);
|
||||
std::string local_lib_path = path::Join(exe_dir, kLibFuseFilename);
|
||||
// Set the cwd to the exe dir and pass the filenames to scp. Otherwise, some
|
||||
// scp implementations can get confused and create the wrong remote filenames.
|
||||
path::SetCwd(exe_dir);
|
||||
|
||||
#ifdef _DEBUG
|
||||
// Sync FUSE to the gamelet in debug. Debug builds are rather large, so
|
||||
// there's a gain from using sync.
|
||||
LOG_DEBUG("Syncing FUSE");
|
||||
RETURN_IF_ERROR(
|
||||
remote_util_->Sync({local_exe_path, local_lib_path}, kRemoteToolsBinDir),
|
||||
"Failed to sync FUSE to gamelet");
|
||||
LOG_DEBUG("Syncing FUSE succeeded");
|
||||
#else
|
||||
// Copy FUSE to the gamelet. This is usually faster in production since it
|
||||
// doesn't have to deploy ggp__server first.
|
||||
// Copy FUSE to the gamelet.
|
||||
LOG_DEBUG("Copying FUSE");
|
||||
RETURN_IF_ERROR(remote_util_->Scp({local_exe_path, local_lib_path},
|
||||
kRemoteToolsBinDir, true),
|
||||
RETURN_IF_ERROR(remote_util_->Scp({kFuseFilename, kLibFuseFilename},
|
||||
kRemoteToolsBinDir, /*compress=*/false),
|
||||
"Failed to copy FUSE to gamelet");
|
||||
LOG_DEBUG("Copying FUSE succeeded");
|
||||
|
||||
@@ -82,12 +70,12 @@ absl::Status CdcFuseManager::Deploy() {
|
||||
RETURN_IF_ERROR(remote_util_->Chmod("a+x", remotePath),
|
||||
"Failed to set executable flag on FUSE");
|
||||
LOG_DEBUG("Making FUSE succeeded");
|
||||
#endif
|
||||
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
absl::Status CdcFuseManager::Start(uint16_t local_port, uint16_t remote_port,
|
||||
absl::Status CdcFuseManager::Start(const std::string& mount_dir,
|
||||
uint16_t local_port, uint16_t remote_port,
|
||||
int verbosity, bool debug,
|
||||
bool singlethreaded, bool enable_stats,
|
||||
bool check, uint64_t cache_capacity,
|
||||
@@ -115,15 +103,18 @@ absl::Status CdcFuseManager::Start(uint16_t local_port, uint16_t remote_port,
|
||||
// Build the remote command.
|
||||
std::string remotePath = path::JoinUnix(kRemoteToolsBinDir, kFuseFilename);
|
||||
std::string remote_command = absl::StrFormat(
|
||||
"LD_LIBRARY_PATH=%s %s --instance='%s' "
|
||||
"--components='%s' --port=%i --cache_dir=%s "
|
||||
"mkdir -p %s; LD_LIBRARY_PATH=%s %s "
|
||||
"--instance=%s "
|
||||
"--components=%s --port=%i --cache_dir=%s "
|
||||
"--verbosity=%i --cleanup_timeout=%i --access_idle_timeout=%i --stats=%i "
|
||||
"--check=%i --cache_capacity=%u -- -o allow_root -o ro -o nonempty -o "
|
||||
"auto_unmount %s%s%s",
|
||||
kRemoteToolsBinDir, remotePath, instance_, component_args, remote_port,
|
||||
kCacheDir, verbosity, cleanup_timeout_sec, access_idle_timeout_sec,
|
||||
enable_stats, check, cache_capacity, kMountDir, debug ? " -d" : "",
|
||||
singlethreaded ? " -s" : "");
|
||||
kRemoteToolsBinDir, kRemoteToolsBinDir, remotePath,
|
||||
RemoteUtil::QuoteForSsh(instance_),
|
||||
RemoteUtil::QuoteForSsh(component_args), remote_port, kCacheDir,
|
||||
verbosity, cleanup_timeout_sec, access_idle_timeout_sec, enable_stats,
|
||||
check, cache_capacity, RemoteUtil::QuoteForSsh(mount_dir),
|
||||
debug ? " -d" : "", singlethreaded ? " -s" : "");
|
||||
|
||||
bool needs_deploy = false;
|
||||
RETURN_IF_ERROR(
|
||||
|
||||
@@ -40,6 +40,7 @@ class CdcFuseManager {
|
||||
// |remote_port| to the workstation's |local_port|. Deploys the binary if
|
||||
// necessary.
|
||||
//
|
||||
// |mount_dir| is the remote directory where to mount the FUSE.
|
||||
// |verbosity| is the log verbosity used by the filesystem.
|
||||
// |debug| puts the filesystem into debug mode if set to true. This also
|
||||
// causes the process to run in the foreground, so that logs are piped through
|
||||
@@ -51,10 +52,10 @@ class CdcFuseManager {
|
||||
// |cleanup_timeout_sec| defines the data provider cleanup timeout in seconds.
|
||||
// |access_idle_timeout_sec| defines the number of seconds after which data
|
||||
// provider is considered to be access-idling.
|
||||
absl::Status Start(uint16_t local_port, uint16_t remote_port, int verbosity,
|
||||
bool debug, bool singlethreaded, bool enable_stats,
|
||||
bool check, uint64_t cache_capacity,
|
||||
uint32_t cleanup_timeout_sec,
|
||||
absl::Status Start(const std::string& mount_dir, uint16_t local_port,
|
||||
uint16_t remote_port, int verbosity, bool debug,
|
||||
bool singlethreaded, bool enable_stats, bool check,
|
||||
uint64_t cache_capacity, uint32_t cleanup_timeout_sec,
|
||||
uint32_t access_idle_timeout_sec);
|
||||
|
||||
// Stops the CDC FUSE.
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#include <iomanip>
|
||||
|
||||
#include "absl/strings/str_format.h"
|
||||
#include "absl/strings/str_replace.h"
|
||||
#include "absl/strings/str_split.h"
|
||||
#include "asset_stream_manager/multi_session.h"
|
||||
#include "asset_stream_manager/session_manager.h"
|
||||
@@ -26,11 +27,21 @@
|
||||
#include "common/process.h"
|
||||
#include "common/sdk_util.h"
|
||||
#include "common/status.h"
|
||||
#include "google/protobuf/text_format.h"
|
||||
#include "manifest/manifest_updater.h"
|
||||
|
||||
using TextFormat = google::protobuf::TextFormat;
|
||||
|
||||
namespace cdc_ft {
|
||||
namespace {
|
||||
|
||||
std::string RequestToString(const google::protobuf::Message& request) {
|
||||
std::string str;
|
||||
google::protobuf::TextFormat::PrintToString(request, &str);
|
||||
if (!str.empty() && str.back() == '\n') str.pop_back();
|
||||
return absl::StrReplaceAll(str, {{"\n", ", "}});
|
||||
}
|
||||
|
||||
// Parses |instance_name| of the form
|
||||
// "organizations/{org-id}/projects/{proj-id}/pools/{pool-id}/gamelets/{gamelet-id}"
|
||||
// into parts. The pool id is not returned.
|
||||
@@ -102,48 +113,18 @@ LocalAssetsStreamManagerServiceImpl::~LocalAssetsStreamManagerServiceImpl() =
|
||||
grpc::Status LocalAssetsStreamManagerServiceImpl::StartSession(
|
||||
grpc::ServerContext* /*context*/, const StartSessionRequest* request,
|
||||
StartSessionResponse* /*response*/) {
|
||||
LOG_INFO("RPC:StartSession(gamelet_name='%s', workstation_directory='%s'",
|
||||
request->gamelet_name(), request->workstation_directory());
|
||||
LOG_INFO("RPC:StartSession(%s)", RequestToString(*request));
|
||||
|
||||
metrics::DeveloperLogEvent evt;
|
||||
evt.as_manager_data = std::make_unique<metrics::AssetStreamingManagerData>();
|
||||
evt.as_manager_data->session_start_data =
|
||||
std::make_unique<metrics::SessionStartData>();
|
||||
evt.as_manager_data->session_start_data->absl_status = absl::StatusCode::kOk;
|
||||
evt.as_manager_data->session_start_data->status =
|
||||
metrics::SessionStartStatus::kOk;
|
||||
evt.as_manager_data->session_start_data->origin =
|
||||
ConvertOrigin(request->origin());
|
||||
|
||||
// Parse instance/project/org id.
|
||||
absl::Status status;
|
||||
MultiSession* ms = nullptr;
|
||||
std::string instance_id, project_id, organization_id, instance_ip;
|
||||
uint16_t instance_port = 0;
|
||||
if (!ParseInstanceName(request->gamelet_name(), &instance_id, &project_id,
|
||||
&organization_id)) {
|
||||
status = absl::InvalidArgumentError(absl::StrFormat(
|
||||
"Failed to parse instance name '%s'", request->gamelet_name()));
|
||||
} else {
|
||||
evt.project_id = project_id;
|
||||
evt.organization_id = organization_id;
|
||||
|
||||
status = InitSsh(instance_id, project_id, organization_id, &instance_ip,
|
||||
&instance_port);
|
||||
|
||||
if (status.ok()) {
|
||||
status = session_manager_->StartSession(
|
||||
instance_id, project_id, organization_id, instance_ip, instance_port,
|
||||
request->workstation_directory(), &ms,
|
||||
&evt.as_manager_data->session_start_data->status);
|
||||
}
|
||||
}
|
||||
metrics::DeveloperLogEvent evt;
|
||||
std::string instance_id;
|
||||
absl::Status status = StartSessionInternal(request, &instance_id, &ms, &evt);
|
||||
|
||||
evt.as_manager_data->session_start_data->absl_status = status.code();
|
||||
if (ms) {
|
||||
evt.as_manager_data->session_start_data->concurrent_session_count =
|
||||
ms->GetSessionCount();
|
||||
if (!instance_id.empty() && ms->HasSessionForInstance(instance_id)) {
|
||||
if (!instance_id.empty() && ms->HasSession(instance_id)) {
|
||||
ms->RecordSessionEvent(std::move(evt), metrics::EventType::kSessionStart,
|
||||
instance_id);
|
||||
} else {
|
||||
@@ -166,9 +147,14 @@ grpc::Status LocalAssetsStreamManagerServiceImpl::StartSession(
|
||||
grpc::Status LocalAssetsStreamManagerServiceImpl::StopSession(
|
||||
grpc::ServerContext* /*context*/, const StopSessionRequest* request,
|
||||
StopSessionResponse* /*response*/) {
|
||||
LOG_INFO("RPC:StopSession(gamelet_id='%s')", request->gamelet_id());
|
||||
LOG_INFO("RPC:StopSession(%s)", RequestToString(*request));
|
||||
|
||||
absl::Status status = session_manager_->StopSession(request->gamelet_id());
|
||||
std::string instance_id =
|
||||
!request->gamelet_id().empty() // Stadia use case
|
||||
? request->gamelet_id()
|
||||
: absl::StrCat(request->user_host(), ":", request->mount_dir());
|
||||
|
||||
absl::Status status = session_manager_->StopSession(instance_id);
|
||||
if (status.ok()) {
|
||||
LOG_INFO("StopSession() succeeded");
|
||||
} else {
|
||||
@@ -177,6 +163,86 @@ grpc::Status LocalAssetsStreamManagerServiceImpl::StopSession(
|
||||
return ToGrpcStatus(status);
|
||||
}
|
||||
|
||||
absl::Status LocalAssetsStreamManagerServiceImpl::StartSessionInternal(
|
||||
const StartSessionRequest* request, std::string* instance_id,
|
||||
MultiSession** ms, metrics::DeveloperLogEvent* evt) {
|
||||
instance_id->clear();
|
||||
*ms = nullptr;
|
||||
evt->as_manager_data = std::make_unique<metrics::AssetStreamingManagerData>();
|
||||
evt->as_manager_data->session_start_data =
|
||||
std::make_unique<metrics::SessionStartData>();
|
||||
evt->as_manager_data->session_start_data->absl_status = absl::StatusCode::kOk;
|
||||
evt->as_manager_data->session_start_data->status =
|
||||
metrics::SessionStartStatus::kOk;
|
||||
evt->as_manager_data->session_start_data->origin =
|
||||
ConvertOrigin(request->origin());
|
||||
|
||||
if (!(request->gamelet_name().empty() ^ request->user_host().empty())) {
|
||||
return absl::InvalidArgumentError(
|
||||
"Must set either gamelet_name or user_host.");
|
||||
}
|
||||
|
||||
if (request->mount_dir().empty()) {
|
||||
return absl::InvalidArgumentError("mount_dir cannot be empty.");
|
||||
}
|
||||
|
||||
SessionTarget target;
|
||||
if (!request->gamelet_name().empty()) {
|
||||
ASSIGN_OR_RETURN(target,
|
||||
GetTargetForStadia(*request, instance_id, &evt->project_id,
|
||||
&evt->organization_id));
|
||||
} else {
|
||||
target = GetTarget(*request, instance_id);
|
||||
}
|
||||
|
||||
return session_manager_->StartSession(
|
||||
*instance_id, request->workstation_directory(), target, evt->project_id,
|
||||
evt->organization_id, ms,
|
||||
&evt->as_manager_data->session_start_data->status);
|
||||
}
|
||||
|
||||
absl::StatusOr<SessionTarget>
|
||||
LocalAssetsStreamManagerServiceImpl::GetTargetForStadia(
|
||||
const StartSessionRequest& request, std::string* instance_id,
|
||||
std::string* project_id, std::string* organization_id) {
|
||||
SessionTarget target;
|
||||
target.mount_dir = request.mount_dir();
|
||||
target.ssh_command = request.ssh_command();
|
||||
target.scp_command = request.scp_command();
|
||||
|
||||
// Parse instance/project/org id.
|
||||
if (!ParseInstanceName(request.gamelet_name(), instance_id, project_id,
|
||||
organization_id)) {
|
||||
return absl::InvalidArgumentError(absl::StrFormat(
|
||||
"Failed to parse instance name '%s'", request.gamelet_name()));
|
||||
}
|
||||
|
||||
// Run 'ggp ssh init' to determine IP (host) and port.
|
||||
std::string instance_ip;
|
||||
uint16_t instance_port = 0;
|
||||
RETURN_IF_ERROR(InitSsh(*instance_id, *project_id, *organization_id,
|
||||
&instance_ip, &instance_port));
|
||||
|
||||
target.user_host = "cloudcast@" + instance_ip;
|
||||
target.ssh_port = instance_port;
|
||||
return target;
|
||||
}
|
||||
|
||||
SessionTarget LocalAssetsStreamManagerServiceImpl::GetTarget(
|
||||
const StartSessionRequest& request, std::string* instance_id) {
|
||||
SessionTarget target;
|
||||
target.user_host = request.user_host();
|
||||
target.mount_dir = request.mount_dir();
|
||||
target.ssh_command = request.ssh_command();
|
||||
target.scp_command = request.scp_command();
|
||||
target.ssh_port = request.port() > 0 && request.port() <= UINT16_MAX
|
||||
? static_cast<uint16_t>(request.port())
|
||||
: RemoteUtil::kDefaultSshPort;
|
||||
|
||||
*instance_id = absl::StrCat(target.user_host, ":", target.mount_dir);
|
||||
return target;
|
||||
}
|
||||
|
||||
metrics::RequestOrigin LocalAssetsStreamManagerServiceImpl::ConvertOrigin(
|
||||
StartSessionRequestOrigin origin) const {
|
||||
switch (origin) {
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
|
||||
#include "absl/status/status.h"
|
||||
#include "absl/status/statusor.h"
|
||||
#include "asset_stream_manager/session.h"
|
||||
#include "asset_stream_manager/session_config.h"
|
||||
#include "metrics/metrics.h"
|
||||
#include "proto/local_assets_stream_manager.grpc.pb.h"
|
||||
@@ -66,6 +67,27 @@ class LocalAssetsStreamManagerServiceImpl final
|
||||
StopSessionResponse* response) override;
|
||||
|
||||
private:
|
||||
// Internal implementation of StartSession(). Returns the unique session
|
||||
// identifier |instance_id|, the created or retrieved MultiSession |ms| as
|
||||
// well as the filled metrics event |evt|.
|
||||
absl::Status LocalAssetsStreamManagerServiceImpl::StartSessionInternal(
|
||||
const StartSessionRequest* request, std::string* instance_id,
|
||||
MultiSession** ms, metrics::DeveloperLogEvent* evt);
|
||||
|
||||
// Stadia-specific: Returns a SessionTarget from a gamelet name and fills in
|
||||
// the gamelet's |instance_id|, |project_id| and |organization_id|.
|
||||
// Used if request.gamelet_name() is set.
|
||||
// Fails if the gamelet name fails to parse or if ggp ssh init fails.
|
||||
absl::StatusOr<SessionTarget> GetTargetForStadia(
|
||||
const StartSessionRequest& request, std::string* instance_id,
|
||||
std::string* project_id, std::string* organization_id);
|
||||
|
||||
// Returns a SessionTarget from the corresponding fields in |request|.
|
||||
// |instance_id| is set to [user@]host:mount_dir.
|
||||
// Used if request.gamelet_name() is not set.
|
||||
SessionTarget GetTarget(const StartSessionRequest& request,
|
||||
std::string* instance_id);
|
||||
|
||||
// Convert StartSessionRequest enum to metrics enum.
|
||||
metrics::RequestOrigin ConvertOrigin(StartSessionRequestOrigin origin) const;
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#include "asset_stream_manager/local_assets_stream_manager_service_impl.h"
|
||||
#include "asset_stream_manager/session_management_server.h"
|
||||
#include "asset_stream_manager/session_manager.h"
|
||||
#include "common/grpc_status.h"
|
||||
#include "common/log.h"
|
||||
#include "common/path.h"
|
||||
#include "common/process.h"
|
||||
@@ -49,15 +50,19 @@ absl::Status Run(const AssetStreamConfig& cfg) {
|
||||
background_service.SetExitCallback(
|
||||
[&sm_server]() { return sm_server.Shutdown(); });
|
||||
|
||||
RETURN_IF_ERROR(sm_server.Start(kSessionManagementPort));
|
||||
if (!cfg.src_dir().empty()) {
|
||||
MultiSession* ms_unused;
|
||||
metrics::SessionStartStatus status_unused;
|
||||
RETURN_IF_ERROR(session_manager.StartSession(
|
||||
/*instance_id=*/cfg.instance_ip(), /*project_id=*/std::string(),
|
||||
/*organization_id=*/std::string(), cfg.instance_ip(),
|
||||
cfg.instance_port(), cfg.src_dir(), &ms_unused, &status_unused));
|
||||
if (!cfg.dev_src_dir().empty()) {
|
||||
localassetsstreammanager::StartSessionRequest request;
|
||||
request.set_workstation_directory(cfg.dev_src_dir());
|
||||
request.set_user_host(cfg.dev_target().user_host);
|
||||
request.set_mount_dir(cfg.dev_target().mount_dir);
|
||||
request.set_port(cfg.dev_target().ssh_port);
|
||||
request.set_ssh_command(cfg.dev_target().ssh_command);
|
||||
request.set_scp_command(cfg.dev_target().scp_command);
|
||||
localassetsstreammanager::StartSessionResponse response;
|
||||
RETURN_ABSL_IF_ERROR(
|
||||
session_service.StartSession(nullptr, &request, &response));
|
||||
}
|
||||
RETURN_IF_ERROR(sm_server.Start(kSessionManagementPort));
|
||||
sm_server.RunUntilShutdown();
|
||||
return absl::OkStatus();
|
||||
}
|
||||
@@ -96,18 +101,6 @@ const auto RETIRED_FLAGS_REG_allow_edge =
|
||||
} // namespace
|
||||
} // namespace cdc_ft
|
||||
|
||||
ABSL_FLAG(std::string, src_dir, "",
|
||||
"Start a streaming session immediately from the given Windows path. "
|
||||
"Used during development. Must have exactly one gamelet reserved or "
|
||||
"specify the target gamelet with --instance.");
|
||||
ABSL_FLAG(std::string, instance_ip, "",
|
||||
"Connect to the instance with the given IP address for this session. "
|
||||
"This flag is ignored unless --src_dir is set as well. Used "
|
||||
"during development. ");
|
||||
ABSL_FLAG(uint16_t, instance_port, 0,
|
||||
"Connect to the instance through the given SSH port. "
|
||||
"This flag is ignored unless --src_dir is set as well. Used "
|
||||
"during development. ");
|
||||
ABSL_FLAG(int, verbosity, 2, "Verbosity of the log output");
|
||||
ABSL_FLAG(bool, debug, false, "Run FUSE filesystem in debug mode");
|
||||
ABSL_FLAG(bool, singlethreaded, false,
|
||||
@@ -132,6 +125,28 @@ ABSL_FLAG(uint32_t, access_idle_timeout, cdc_ft::DataProvider::kAccessIdleSec,
|
||||
"Do not run instance cache cleanups for this many seconds after the "
|
||||
"last file access");
|
||||
|
||||
// Development args.
|
||||
ABSL_FLAG(std::string, dev_src_dir, "",
|
||||
"Start a streaming session immediately from the given Windows path. "
|
||||
"Used during development. Must also specify --dev_user_host and "
|
||||
"--dev_mount_dir and possibly other --dev flags, depending on the "
|
||||
"SSH setup");
|
||||
ABSL_FLAG(std::string, dev_user_host, "",
|
||||
"Username and host to stream to, of the form [user@]host. Used "
|
||||
"during development. See --dev_src_dir for more info.");
|
||||
ABSL_FLAG(uint16_t, dev_ssh_port, cdc_ft::RemoteUtil::kDefaultSshPort,
|
||||
"SSH port to use for the connection to the host. Used during "
|
||||
"development. See --dev_src_dir for more info.");
|
||||
ABSL_FLAG(std::string, dev_ssh_command, "",
|
||||
"Ssh command and extra flags to use for the connection to the host. "
|
||||
"Used during development. See --dev_src_dir for more info.");
|
||||
ABSL_FLAG(std::string, dev_scp_command, "",
|
||||
"Scp command and extra flags to use for the connection to the host. "
|
||||
"Used during development. See --dev_src_dir for more info.");
|
||||
ABSL_FLAG(std::string, dev_mount_dir, "",
|
||||
"Directory on the host to stream to. Used during development. See "
|
||||
"--dev_src_dir for more info.");
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
absl::ParseCommandLine(argc, argv);
|
||||
|
||||
|
||||
@@ -478,7 +478,7 @@ absl::Status MultiSession::Shutdown() {
|
||||
while (!sessions_.empty()) {
|
||||
std::string instance_id = sessions_.begin()->first;
|
||||
RETURN_IF_ERROR(StopSession(instance_id),
|
||||
"Failed to stop session for instance id %s", instance_id);
|
||||
"Failed to stop session for instance id '%s'", instance_id);
|
||||
sessions_.erase(instance_id);
|
||||
}
|
||||
|
||||
@@ -499,10 +499,9 @@ absl::Status MultiSession::Status() {
|
||||
}
|
||||
|
||||
absl::Status MultiSession::StartSession(const std::string& instance_id,
|
||||
const SessionTarget& target,
|
||||
const std::string& project_id,
|
||||
const std::string& organization_id,
|
||||
const std::string& instance_ip,
|
||||
uint16_t instance_port) {
|
||||
const std::string& organization_id) {
|
||||
absl::MutexLock lock(&sessions_mutex_);
|
||||
|
||||
if (sessions_.find(instance_id) != sessions_.end()) {
|
||||
@@ -523,9 +522,8 @@ absl::Status MultiSession::StartSession(const std::string& instance_id,
|
||||
metrics_recorder_->GetMetricsService(),
|
||||
metrics_recorder_->MultiSessionId(), project_id, organization_id);
|
||||
|
||||
auto session =
|
||||
std::make_unique<Session>(instance_id, instance_ip, instance_port, cfg_,
|
||||
process_factory_, std::move(metrics_recorder));
|
||||
auto session = std::make_unique<Session>(
|
||||
instance_id, target, cfg_, process_factory_, std::move(metrics_recorder));
|
||||
RETURN_IF_ERROR(session->Start(local_asset_stream_port_,
|
||||
kAssetStreamPortFirst, kAssetStreamPortLast));
|
||||
|
||||
@@ -552,7 +550,7 @@ absl::Status MultiSession::StopSession(const std::string& instance_id) {
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
bool MultiSession::HasSessionForInstance(const std::string& instance_id) {
|
||||
bool MultiSession::HasSession(const std::string& instance_id) {
|
||||
absl::ReaderMutexLock lock(&sessions_mutex_);
|
||||
return sessions_.find(instance_id) != sessions_.end();
|
||||
}
|
||||
@@ -663,19 +661,19 @@ void MultiSession::OnContentSent(size_t byte_count, size_t chunck_count,
|
||||
std::string instance_id) {
|
||||
if (instance_id.empty()) {
|
||||
// |instance_id| is empty only in case when manifest wasn't acknowledged by
|
||||
// the gamelet yet (ConfigStreamServiceImpl::AckManifestIdReceived was not
|
||||
// the instance yet (ConfigStreamServiceImpl::AckManifestIdReceived was not
|
||||
// invoked). This means MultiSession::StartSession is still waiting for
|
||||
// manifest acknowledge and |sessions_mutex_| is currently locked. In this
|
||||
// case invoking MultiSession::FindSession and waiting for |sessions_mutex_|
|
||||
// to get unlocked will block the current thread, which is also responsible
|
||||
// for receiving a call at ConfigStreamServiceImpl::AckManifestIdReceived.
|
||||
// This causes a deadlock and leads to a DeadlineExceeded error.
|
||||
LOG_WARNING("Can not record session content for an empty instance_id.");
|
||||
LOG_WARNING("Cannot record session content for an empty instance_id.");
|
||||
return;
|
||||
}
|
||||
Session* session = FindSession(instance_id);
|
||||
if (session == nullptr) {
|
||||
LOG_WARNING("Failed to find active session by instrance id: %s",
|
||||
LOG_WARNING("Failed to find active session by instance id '%s'",
|
||||
instance_id);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -36,6 +36,7 @@ namespace cdc_ft {
|
||||
|
||||
class ProcessFactory;
|
||||
class Session;
|
||||
struct SessionTarget;
|
||||
using ManifestUpdatedCb = std::function<void()>;
|
||||
|
||||
// Updates the manifest and runs a file watcher in a background thread.
|
||||
@@ -65,7 +66,7 @@ class MultiSessionRunner {
|
||||
// Stops updating the manifest and |server_|.
|
||||
absl::Status Shutdown() ABSL_LOCKS_EXCLUDED(mutex_);
|
||||
|
||||
// Waits until a manifest is ready and the gamelet |instance_id| has
|
||||
// Waits until a manifest is ready and the session for |instance_id| has
|
||||
// acknowledged the reception of the currently set manifest id. |fuse_timeout|
|
||||
// is the timeout for waiting for the FUSE manifest ack. The time required to
|
||||
// generate the manifest is not part of this timeout as this could take a
|
||||
@@ -172,30 +173,29 @@ class MultiSession {
|
||||
// Not thread-safe.
|
||||
absl::Status Status();
|
||||
|
||||
// Starts a new streaming session to the instance with given |instance_id| and
|
||||
// Starts a new streaming session to the instance described by |target| and
|
||||
// waits until the FUSE has received the initial manifest id.
|
||||
// Returns an error if a session for that instance already exists.
|
||||
// |instance_id| is the instance id of the target remote instance.
|
||||
// |project_id| is id of the project that contains the instance.
|
||||
// |organization_id| is id of the organization that contains the instance.
|
||||
// |instance_ip| is the IP address of the instance.
|
||||
// |instance_port| is the SSH port for connecting to the remote instance.
|
||||
// |instance_id| is a unique id for the remote instance and mount directory,
|
||||
// e.g. user@host:mount_dir.
|
||||
// |target| identifies the remote target and how to connect to it.
|
||||
// |project_id| is the project that owns the instance. Stadia only.
|
||||
// |organization_id| is organization that contains the instance. Stadia only.
|
||||
// Thread-safe.
|
||||
absl::Status StartSession(const std::string& instance_id,
|
||||
const SessionTarget& target,
|
||||
const std::string& project_id,
|
||||
const std::string& organization_id,
|
||||
const std::string& instance_ip,
|
||||
uint16_t instance_port)
|
||||
const std::string& organization_id)
|
||||
ABSL_LOCKS_EXCLUDED(sessions_mutex_);
|
||||
|
||||
// Starts a new streaming session to the gamelet with given |instance_id|.
|
||||
// Stops the session for the given |instance_id|.
|
||||
// Returns a NotFound error if a session for that instance does not exists.
|
||||
// Thread-safe.
|
||||
absl::Status StopSession(const std::string& instance_id)
|
||||
ABSL_LOCKS_EXCLUDED(sessions_mutex_);
|
||||
|
||||
// Returns true if there is an existing session for |instance_id|.
|
||||
bool HasSessionForInstance(const std::string& instance_id)
|
||||
bool HasSession(const std::string& instance_id)
|
||||
ABSL_LOCKS_EXCLUDED(sessions_mutex_);
|
||||
|
||||
// Returns true if the FUSE process is up and running for an existing session
|
||||
@@ -226,7 +226,7 @@ class MultiSession {
|
||||
void RecordMultiSessionEvent(metrics::DeveloperLogEvent event,
|
||||
metrics::EventType code);
|
||||
|
||||
// Record an event for a session associated with the |instance|.
|
||||
// Record an event for a session associated with the |instance_id|.
|
||||
void RecordSessionEvent(metrics::DeveloperLogEvent event,
|
||||
metrics::EventType code,
|
||||
const std::string& instance_id);
|
||||
|
||||
@@ -26,7 +26,7 @@ namespace cdc_ft {
|
||||
namespace {
|
||||
|
||||
// Timeout for initial gamelet connection.
|
||||
constexpr double kInstanceConnectionTimeoutSec = 10.0f;
|
||||
constexpr double kInstanceConnectionTimeoutSec = 60.0f;
|
||||
|
||||
metrics::DeveloperLogEvent GetEventWithHeartBeatData(size_t bytes,
|
||||
size_t chunks) {
|
||||
@@ -40,18 +40,24 @@ metrics::DeveloperLogEvent GetEventWithHeartBeatData(size_t bytes,
|
||||
|
||||
} // namespace
|
||||
|
||||
Session::Session(std::string instance_id, std::string instance_ip,
|
||||
uint16_t instance_port, SessionConfig cfg,
|
||||
ProcessFactory* process_factory,
|
||||
Session::Session(std::string instance_id, const SessionTarget& target,
|
||||
SessionConfig cfg, ProcessFactory* process_factory,
|
||||
std::unique_ptr<SessionMetricsRecorder> metrics_recorder)
|
||||
: instance_id_(std::move(instance_id)),
|
||||
mount_dir_(target.mount_dir),
|
||||
cfg_(std::move(cfg)),
|
||||
process_factory_(process_factory),
|
||||
remote_util_(cfg_.verbosity, cfg_.quiet, process_factory,
|
||||
/*forward_output_to_logging=*/true),
|
||||
metrics_recorder_(std::move(metrics_recorder)) {
|
||||
assert(metrics_recorder_);
|
||||
remote_util_.SetUserHostAndPort(instance_ip, instance_port);
|
||||
remote_util_.SetUserHostAndPort(target.user_host, target.ssh_port);
|
||||
if (!target.ssh_command.empty()) {
|
||||
remote_util_.SetSshCommand(target.ssh_command);
|
||||
}
|
||||
if (!target.scp_command.empty()) {
|
||||
remote_util_.SetScpCommand(target.scp_command);
|
||||
}
|
||||
}
|
||||
|
||||
Session::~Session() {
|
||||
@@ -80,9 +86,10 @@ absl::Status Session::Start(int local_port, int first_remote_port,
|
||||
fuse_ = std::make_unique<CdcFuseManager>(instance_id_, process_factory_,
|
||||
&remote_util_);
|
||||
RETURN_IF_ERROR(
|
||||
fuse_->Start(local_port, remote_port, cfg_.verbosity, cfg_.fuse_debug,
|
||||
cfg_.fuse_singlethreaded, cfg_.stats, cfg_.fuse_check,
|
||||
cfg_.fuse_cache_capacity, cfg_.fuse_cleanup_timeout_sec,
|
||||
fuse_->Start(mount_dir_, local_port, remote_port, cfg_.verbosity,
|
||||
cfg_.fuse_debug, cfg_.fuse_singlethreaded, cfg_.stats,
|
||||
cfg_.fuse_check, cfg_.fuse_cache_capacity,
|
||||
cfg_.fuse_cleanup_timeout_sec,
|
||||
cfg_.fuse_access_idle_timeout_sec),
|
||||
"Failed to start instance component");
|
||||
return absl::OkStatus();
|
||||
|
||||
@@ -32,17 +32,29 @@ class CdcFuseManager;
|
||||
class ProcessFactory;
|
||||
class Process;
|
||||
|
||||
// Manages the connection of a workstation to a single gamelet.
|
||||
// Defines a remote target and how to connect to it.
|
||||
struct SessionTarget {
|
||||
// SSH username and hostname of the remote target, formed as [user@]host.
|
||||
std::string user_host;
|
||||
// Port to use for SSH connections to the remote target.
|
||||
uint16_t ssh_port;
|
||||
// Ssh command to use to connect to the remote target.
|
||||
std::string ssh_command;
|
||||
// Scp command to use to copy files to the remote target.
|
||||
std::string scp_command;
|
||||
// Directory on the remote target where to mount the streamed directory.
|
||||
std::string mount_dir;
|
||||
};
|
||||
|
||||
// Manages the connection of a workstation to a single remote instance.
|
||||
class Session {
|
||||
public:
|
||||
// |instance_id| is a unique id for the remote instance.
|
||||
// |instance_ip| is the IP address of the remote instance.
|
||||
// |instance_port| is the SSH tunnel port for connecting to the instance.
|
||||
// |target| identifies the remote target and how to connect to it.
|
||||
// |cfg| contains generic configuration parameters for the session.
|
||||
// |process_factory| abstracts process creation.
|
||||
Session(std::string instance_id, std::string instance_ip,
|
||||
uint16_t instance_port, SessionConfig cfg,
|
||||
ProcessFactory* process_factory,
|
||||
Session(std::string instance_id, const SessionTarget& target,
|
||||
SessionConfig cfg, ProcessFactory* process_factory,
|
||||
std::unique_ptr<SessionMetricsRecorder> metrics_recorder);
|
||||
~Session();
|
||||
|
||||
@@ -71,6 +83,7 @@ class Session {
|
||||
|
||||
private:
|
||||
const std::string instance_id_;
|
||||
const std::string mount_dir_;
|
||||
const SessionConfig cfg_;
|
||||
ProcessFactory* const process_factory_;
|
||||
|
||||
|
||||
@@ -59,10 +59,10 @@ absl::Status SessionManager::Shutdown() {
|
||||
}
|
||||
|
||||
absl::Status SessionManager::StartSession(
|
||||
const std::string& instance_id, const std::string& project_id,
|
||||
const std::string& organization_id, const std::string& instance_ip,
|
||||
uint16_t instance_port, const std::string& src_dir,
|
||||
MultiSession** multi_session, metrics::SessionStartStatus* metrics_status) {
|
||||
const std::string& instance_id, const std::string& src_dir,
|
||||
const SessionTarget& target, const std::string& project_id,
|
||||
const std::string& organization_id, MultiSession** multi_session,
|
||||
metrics::SessionStartStatus* metrics_status) {
|
||||
*multi_session = nullptr;
|
||||
*metrics_status = metrics::SessionStartStatus::kOk;
|
||||
|
||||
@@ -83,7 +83,7 @@ absl::Status SessionManager::StartSession(
|
||||
// Early out if we are streaming the workstation dir to the given gamelet.
|
||||
MultiSession* ms = GetMultiSession(src_dir);
|
||||
*multi_session = ms;
|
||||
if (ms && ms->HasSessionForInstance(instance_id)) {
|
||||
if (ms && ms->HasSession(instance_id)) {
|
||||
if (ms->IsSessionHealthy(instance_id)) {
|
||||
LOG_INFO("Reusing existing session");
|
||||
return absl::OkStatus();
|
||||
@@ -95,8 +95,8 @@ absl::Status SessionManager::StartSession(
|
||||
// We could also fall through, but this might restart the MultiSession.
|
||||
status = ms->StopSession(instance_id);
|
||||
if (status.ok()) {
|
||||
status = ms->StartSession(instance_id, project_id, organization_id,
|
||||
instance_ip, instance_port);
|
||||
status =
|
||||
ms->StartSession(instance_id, target, project_id, organization_id);
|
||||
}
|
||||
if (!status.ok()) {
|
||||
*metrics_status = metrics::SessionStartStatus::kRestartSessionError;
|
||||
@@ -129,8 +129,7 @@ absl::Status SessionManager::StartSession(
|
||||
// Start the session.
|
||||
LOG_INFO("Starting streaming session from path '%s' to instance '%s'",
|
||||
src_dir, instance_id);
|
||||
status = ms->StartSession(instance_id, project_id, organization_id,
|
||||
instance_ip, instance_port);
|
||||
status = ms->StartSession(instance_id, target, project_id, organization_id);
|
||||
if (!status.ok()) {
|
||||
*metrics_status = metrics::SessionStartStatus::kStartSessionError;
|
||||
}
|
||||
@@ -164,15 +163,16 @@ absl::StatusOr<MultiSession*> SessionManager::GetOrCreateMultiSession(
|
||||
return iter->second.get();
|
||||
}
|
||||
|
||||
absl::Status SessionManager::StopSessionInternal(const std::string& instance) {
|
||||
absl::Status SessionManager::StopSessionInternal(
|
||||
const std::string& instance_id) {
|
||||
absl::Status status;
|
||||
for (const auto& [key, ms] : sessions_) {
|
||||
if (!ms->HasSessionForInstance(instance)) continue;
|
||||
if (!ms->HasSession(instance_id)) continue;
|
||||
|
||||
LOG_INFO("Stopping session streaming from '%s' to instance '%s'",
|
||||
ms->src_dir(), instance);
|
||||
RETURN_IF_ERROR(ms->StopSession(instance),
|
||||
"Failed to stop session for instance '%s'", instance);
|
||||
ms->src_dir(), instance_id);
|
||||
RETURN_IF_ERROR(ms->StopSession(instance_id),
|
||||
"Failed to stop session for instance '%s'", instance_id);
|
||||
|
||||
// Session was stopped. If the MultiSession is empty now, delete it.
|
||||
if (ms->Empty()) {
|
||||
@@ -187,7 +187,7 @@ absl::Status SessionManager::StopSessionInternal(const std::string& instance) {
|
||||
}
|
||||
|
||||
return absl::NotFoundError(
|
||||
absl::StrFormat("No session for instance id '%s' found", instance));
|
||||
absl::StrFormat("No session for instance '%s' found", instance_id));
|
||||
}
|
||||
|
||||
} // namespace cdc_ft
|
||||
|
||||
@@ -30,43 +30,46 @@ namespace cdc_ft {
|
||||
|
||||
class MultiSession;
|
||||
class ProcessFactory;
|
||||
struct SessionTarget;
|
||||
|
||||
// Implements a service to start and stop streaming sessions as a server.
|
||||
// The corresponding clients are implemented by the ggp CLI and SDK Proxy.
|
||||
// The CLI triggers StartSession() from `ggp instance mount --local-dir` and
|
||||
// StopSession() from `ggp instance unmount`. SDK Proxy invokes StartSession()
|
||||
// when a user starts a new game from the partner portal and sets an `Asset
|
||||
// streaming directory` in the `Advanced settings` in the `Play settings`
|
||||
// dialog.
|
||||
// This service is owned by SessionManagementServer.
|
||||
// Adds logic around MultiSession to start and stop streaming sessions. Makes
|
||||
// sure that some invariants are maintained, like no two streaming sessions
|
||||
// exist to the same target user@host:dir.
|
||||
class SessionManager {
|
||||
public:
|
||||
SessionManager(SessionConfig cfg, ProcessFactory* process_factory,
|
||||
metrics::MetricsService* metrics_service);
|
||||
~SessionManager();
|
||||
|
||||
// Starts a session and populates |multi_session| and |metrics_status|.
|
||||
// Starts a new session or reuses an existing one.
|
||||
// |instance_id| is a unique id for the remote instance and mount directory,
|
||||
// e.g. user@host:mount_dir.
|
||||
// |src_dir| is the local directory to stream.
|
||||
// |target| identifies the remote target and how to connect to it.
|
||||
// |project_id| is the project that owns the instance. Stadia only.
|
||||
// |organization_id| is organization that contains the instance. Stadia only.
|
||||
// Populates |multi_session| and |metrics_status| on success.
|
||||
absl::Status StartSession(const std::string& instance_id,
|
||||
const std::string& src_dir,
|
||||
const SessionTarget& target,
|
||||
const std::string& project_id,
|
||||
const std::string& organization_id,
|
||||
const std::string& instance_ip,
|
||||
uint16_t instance_port, const std::string& src_dir,
|
||||
MultiSession** multi_session,
|
||||
metrics::SessionStartStatus* metrics_status)
|
||||
ABSL_LOCKS_EXCLUDED(sessions_mutex_);
|
||||
|
||||
// Stops the session for the given |instance|. Returns a NotFound error if no
|
||||
// session exists.
|
||||
absl::Status StopSession(const std::string& instance)
|
||||
// Stops the session for the given |instance_id|.
|
||||
// Returns a NotFound error if no session exists.
|
||||
absl::Status StopSession(const std::string& instance_id)
|
||||
ABSL_LOCKS_EXCLUDED(sessions_mutex_);
|
||||
|
||||
// Shuts down all existing MultiSessions.
|
||||
absl::Status Shutdown() ABSL_LOCKS_EXCLUDED(sessions_mutex_);
|
||||
|
||||
private:
|
||||
// Stops the session for the given |instance|. Returns a NotFound error if no
|
||||
// session exists.
|
||||
absl::Status StopSessionInternal(const std::string& instance)
|
||||
// Stops the session for the given |instance_id|. Returns a NotFound error if
|
||||
// no session exists.
|
||||
absl::Status StopSessionInternal(const std::string& instance_id)
|
||||
ABSL_EXCLUSIVE_LOCKS_REQUIRED(sessions_mutex_);
|
||||
|
||||
// Returns the MultiSession for the given workstation directory |src_dir| or
|
||||
|
||||
Reference in New Issue
Block a user