Commit 7a29279c authored by Joel Hockey's avatar Joel Hockey Committed by Commit Bot

Add CrostiniSharePath class to manage sharing folders with container

Added/moved signalling logic into fake clients:
* FakeConciergeClient::StartTerminaVm sends OnTremplinStartedSignal.
* FakeCiceroneClient::CreateLxdContainer sends OnLxdContainerCreatedSignal
* FakeCiceroneClient::StartContainer sends OnContainerStartedSignal

Bug: 878324
Change-Id: I7bc8caabc5f4811595d9d58c893e2beda88918dc
Reviewed-on: https://chromium-review.googlesource.com/1193525Reviewed-by: default avatarRyo Hashimoto <hashimoto@chromium.org>
Reviewed-by: default avatarNicholas Verne <nverne@chromium.org>
Commit-Queue: Joel Hockey <joelhockey@chromium.org>
Cr-Commit-Position: refs/heads/master@{#588337}
parent a1e334b1
......@@ -31,6 +31,7 @@ source_set("chromeos") {
"//chromeos:cicerone_proto",
"//chromeos:concierge_proto",
"//chromeos:power_manager_proto",
"//chromeos:seneschal_proto",
"//chromeos:vm_applications_apps_proto",
"//components/policy/proto",
"//content/app/resources",
......@@ -577,6 +578,8 @@ source_set("chromeos") {
"crostini/crostini_registry_service_factory.h",
"crostini/crostini_remover.cc",
"crostini/crostini_remover.h",
"crostini/crostini_share_path.cc",
"crostini/crostini_share_path.h",
"crostini/crostini_util.cc",
"crostini/crostini_util.h",
"crostini/crosvm_metrics.cc",
......@@ -2048,6 +2051,7 @@ source_set("unit_tests") {
"certificate_provider/certificate_provider_service_unittest.cc",
"child_accounts/usage_time_limit_processor_unittest.cc",
"crostini/crostini_manager_unittest.cc",
"crostini/crostini_share_path_unittest.cc",
"crostini/crosvm_metrics_unittest.cc",
"crostini/crosvm_process_list_unittest.cc",
"customization/customization_document_unittest.cc",
......
......@@ -28,107 +28,6 @@ const char kContainerName[] = "container_name";
class CrostiniManagerTest : public testing::Test {
public:
class SignalingFakeConciergeClient : public chromeos::FakeConciergeClient {
public:
void StartTerminaVm(
const vm_tools::concierge::StartVmRequest& request,
chromeos::DBusMethodCallback<vm_tools::concierge::StartVmResponse>
callback) override {
signal_.set_owner_id(request.owner_id());
signal_.set_vm_name(request.name());
chromeos::FakeConciergeClient::StartTerminaVm(
request,
base::BindOnce(&SignalingFakeConciergeClient::OnStartTerminaVm,
base::Unretained(this), std::move(callback)));
}
private:
void OnStartTerminaVm(
chromeos::DBusMethodCallback<vm_tools::concierge::StartVmResponse>
callback,
base::Optional<vm_tools::concierge::StartVmResponse> reply) {
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE,
base::BindOnce(&SignalingFakeConciergeClient::OnTremplinStarted,
base::Unretained(this)));
std::move(callback).Run(reply);
}
void OnTremplinStarted() {
if (signal_sent_) {
return;
}
crostini::CrostiniManager::GetInstance()->OnTremplinStarted(signal_);
signal_sent_ = true;
}
vm_tools::cicerone::TremplinStartedSignal signal_;
bool signal_sent_ = false;
};
// TODO(timloh): Consider moving this logic into the fake itself.
class SignalingFakeCiceroneClient : public chromeos::FakeCiceroneClient {
public:
void CreateLxdContainer(
const vm_tools::cicerone::CreateLxdContainerRequest& request,
chromeos::DBusMethodCallback<
vm_tools::cicerone::CreateLxdContainerResponse> callback) override {
lxd_container_created_signal_.set_owner_id(request.owner_id());
lxd_container_created_signal_.set_vm_name(request.vm_name());
lxd_container_created_signal_.set_container_name(
request.container_name());
chromeos::FakeCiceroneClient::CreateLxdContainer(request,
std::move(callback));
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE,
base::BindOnce(&SignalingFakeCiceroneClient::OnLxdContainerCreated,
base::Unretained(this)));
}
void SetUpLxdContainerUser(
const vm_tools::cicerone::SetUpLxdContainerUserRequest& request,
chromeos::DBusMethodCallback<
vm_tools::cicerone::SetUpLxdContainerUserResponse> callback)
override {
container_started_signal_.set_owner_id(request.owner_id());
container_started_signal_.set_vm_name(request.vm_name());
container_started_signal_.set_container_name(request.container_name());
chromeos::FakeCiceroneClient::SetUpLxdContainerUser(request,
std::move(callback));
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE,
base::BindOnce(&SignalingFakeCiceroneClient::OnContainerStarted,
base::Unretained(this)));
}
private:
void OnLxdContainerCreated() {
if (lxd_container_created_signal_sent_) {
return;
}
crostini::CrostiniManager::GetInstance()->OnLxdContainerCreated(
lxd_container_created_signal_);
lxd_container_created_signal_sent_ = true;
}
void OnContainerStarted() {
if (container_started_signal_sent_) {
return;
}
crostini::CrostiniManager::GetInstance()->OnContainerStarted(
container_started_signal_);
container_started_signal_sent_ = true;
}
vm_tools::cicerone::LxdContainerCreatedSignal lxd_container_created_signal_;
vm_tools::cicerone::ContainerStartedSignal container_started_signal_;
bool lxd_container_created_signal_sent_ = false;
bool container_started_signal_sent_ = false;
};
void CreateDiskImageClientErrorCallback(base::OnceClosure closure,
ConciergeClientResult result,
const base::FilePath& file_path) {
......@@ -209,18 +108,25 @@ class CrostiniManagerTest : public testing::Test {
}
CrostiniManagerTest()
: fake_cicerone_client_(new SignalingFakeCiceroneClient()),
fake_concierge_client_(new SignalingFakeConciergeClient()),
: fake_cicerone_client_(new chromeos::FakeCiceroneClient()),
fake_concierge_client_(new chromeos::FakeConciergeClient()),
scoped_task_environment_(
base::test::ScopedTaskEnvironment::MainThreadType::UI),
test_browser_thread_bundle_(
content::TestBrowserThreadBundle::REAL_IO_THREAD) {
chromeos::DBusThreadManager::GetSetterForTesting()->SetCiceroneClient(
base::WrapUnique(fake_cicerone_client_));
chromeos::DBusThreadManager::GetSetterForTesting()->SetConciergeClient(
base::WrapUnique(fake_concierge_client_));
// Initialization between D-Bus, fake clients, and the singleton
// CrostiniManager is tricky since CrostiniManager is a global singleton and
// doesn't add itself as an observer for the new Concierge/Cicerone clients
// created for each test. We must first get a handle on the D-Bus setter
// and initialize it, then reset CrostiniManager and add it as an observer
// for the clients in this test, then set the fake clients into D-Bus.
auto dbus_setter = chromeos::DBusThreadManager::GetSetterForTesting();
chromeos::DBusThreadManager::Initialize();
CrostiniManager::GetInstance()->ResetForTesting();
fake_concierge_client_->AddObserver(CrostiniManager::GetInstance());
fake_cicerone_client_->AddObserver(CrostiniManager::GetInstance());
dbus_setter->SetConciergeClient(base::WrapUnique(fake_concierge_client_));
dbus_setter->SetCiceroneClient(base::WrapUnique(fake_cicerone_client_));
}
~CrostiniManagerTest() override { chromeos::DBusThreadManager::Shutdown(); }
......
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/chromeos/crostini/crostini_share_path.h"
#include "base/bind.h"
#include "base/optional.h"
#include "chrome/browser/chromeos/crostini/crostini_manager.h"
#include "chrome/browser/chromeos/crostini/crostini_util.h"
#include "chrome/browser/profiles/profile.h"
#include "chromeos/dbus/concierge/service.pb.h"
#include "chromeos/dbus/dbus_thread_manager.h"
#include "chromeos/dbus/seneschal_client.h"
namespace crostini {
// static
CrostiniSharePath* CrostiniSharePath::GetInstance() {
return base::Singleton<CrostiniSharePath>::get();
}
CrostiniSharePath::CrostiniSharePath() : weak_ptr_factory_(this) {}
CrostiniSharePath::~CrostiniSharePath() {}
void CrostiniSharePath::SharePath(
Profile* profile,
std::string vm_name,
std::string path,
base::OnceCallback<void(bool, std::string)> callback) {
base::Optional<vm_tools::concierge::VmInfo> vm_info =
crostini::CrostiniManager::GetInstance()->GetVmInfo(profile,
std::move(vm_name));
if (!vm_info) {
std::move(callback).Run(false, "Cannot share, VM not running");
return;
}
vm_tools::seneschal::SharePathRequest request;
request.set_handle(vm_info->seneschal_server_handle());
request.mutable_shared_path()->set_path(path);
request.mutable_shared_path()->set_writable(true);
request.set_storage_location(
vm_tools::seneschal::SharePathRequest::DOWNLOADS);
request.set_owner_id(CryptohomeIdForProfile(profile));
chromeos::DBusThreadManager::Get()->GetSeneschalClient()->SharePath(
request, base::BindOnce(&CrostiniSharePath::OnSharePathResponse,
weak_ptr_factory_.GetWeakPtr(), std::move(path),
std::move(callback)));
}
void CrostiniSharePath::OnSharePathResponse(
std::string path,
base::OnceCallback<void(bool, std::string)> callback,
base::Optional<vm_tools::seneschal::SharePathResponse> response) const {
if (!response.has_value()) {
std::move(callback).Run(false, "Error sharing " + path);
return;
}
std::move(callback).Run(response.value().success(),
response.value().failure_reason());
}
} // namespace crostini
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_CHROMEOS_CROSTINI_CROSTINI_SHARE_PATH_H_
#define CHROME_BROWSER_CHROMEOS_CROSTINI_CROSTINI_SHARE_PATH_H_
#include "base/macros.h"
#include "base/memory/singleton.h"
#include "base/memory/weak_ptr.h"
#include "base/optional.h"
#include "chromeos/dbus/seneschal/seneschal_service.pb.h"
class Profile;
namespace crostini {
class CrostiniSharePath {
public:
// Returns the singleton instance of CrostiniSharePath.
static CrostiniSharePath* GetInstance();
void SharePath(Profile* profile,
std::string vm_name,
std::string path,
base::OnceCallback<void(bool, std::string)> callback);
void OnSharePathResponse(
std::string path,
base::OnceCallback<void(bool, std::string)> callback,
base::Optional<vm_tools::seneschal::SharePathResponse> response) const;
private:
friend struct base::DefaultSingletonTraits<CrostiniSharePath>;
CrostiniSharePath();
~CrostiniSharePath();
base::WeakPtrFactory<CrostiniSharePath> weak_ptr_factory_;
DISALLOW_COPY_AND_ASSIGN(CrostiniSharePath);
};
} // namespace crostini
#endif // CHROME_BROWSER_CHROMEOS_CROSTINI_CROSTINI_SHARE_PATH_H_
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/chromeos/crostini/crostini_share_path.h"
#include "base/bind.h"
#include "base/run_loop.h"
#include "base/test/scoped_task_environment.h"
#include "chrome/browser/chromeos/crostini/crostini_manager.h"
#include "chrome/test/base/testing_profile.h"
#include "chromeos/dbus/dbus_thread_manager.h"
#include "chromeos/dbus/fake_cicerone_client.h"
#include "chromeos/dbus/fake_concierge_client.h"
#include "chromeos/dbus/fake_seneschal_client.h"
#include "content/public/test/test_browser_thread_bundle.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace crostini {
class CrostiniSharePathTest : public testing::Test {
public:
void SharePathSuccessStartTerminaVmCallback(ConciergeClientResult result) {
EXPECT_TRUE(fake_concierge_client_->start_termina_vm_called());
EXPECT_EQ(result, ConciergeClientResult::SUCCESS);
CrostiniSharePath::GetInstance()->SharePath(
profile(), "vm-running-success", "path",
base::BindOnce(&CrostiniSharePathTest::SharePathSuccessCallback,
base::Unretained(this), run_loop()->QuitClosure()));
}
void SharePathSuccessCallback(base::OnceClosure closure,
bool success,
std::string failure_reason) {
EXPECT_TRUE(fake_seneschal_client_->share_path_called());
EXPECT_EQ(success, true);
EXPECT_EQ(failure_reason, "");
std::move(closure).Run();
}
void SharePathErrorStartTerminaVmCallback(ConciergeClientResult result) {
EXPECT_TRUE(fake_concierge_client_->start_termina_vm_called());
EXPECT_EQ(result, ConciergeClientResult::SUCCESS);
CrostiniSharePath::GetInstance()->SharePath(
profile(), "vm-running-error", "path",
base::BindOnce(&CrostiniSharePathTest::SharePathErrorCallback,
base::Unretained(this), run_loop()->QuitClosure()));
}
void SharePathErrorCallback(base::OnceClosure closure,
bool success,
std::string failure_reason) {
EXPECT_TRUE(fake_seneschal_client_->share_path_called());
EXPECT_EQ(success, false);
EXPECT_EQ(failure_reason, "test failure");
std::move(closure).Run();
}
void SharePathErrorVmNotRunningCallback(base::OnceClosure closure,
bool success,
std::string failure_reason) {
EXPECT_FALSE(fake_seneschal_client_->share_path_called());
EXPECT_EQ(success, false);
EXPECT_EQ(failure_reason, "Cannot share, VM not running");
std::move(closure).Run();
}
CrostiniSharePathTest()
: fake_seneschal_client_(new chromeos::FakeSeneschalClient()),
fake_concierge_client_(new chromeos::FakeConciergeClient()),
fake_cicerone_client_(new chromeos::FakeCiceroneClient()),
scoped_task_environment_(
base::test::ScopedTaskEnvironment::MainThreadType::UI),
test_browser_thread_bundle_(
content::TestBrowserThreadBundle::REAL_IO_THREAD) {
// Initialization between D-Bus, fake clients, and the singleton
// CrostiniManager is tricky since CrostiniManager is a global singleton and
// doesn't add itself as an observer for the new Concierge/Cicerone clients
// created for each test. We must first get a handle on the D-Bus setter
// and initialize it, then reset CrostiniManager and add it as an observer
// for the clients in this test, then set the fake clients into D-Bus.
auto dbus_setter = chromeos::DBusThreadManager::GetSetterForTesting();
chromeos::DBusThreadManager::Initialize();
CrostiniManager::GetInstance()->ResetForTesting();
fake_concierge_client_->AddObserver(CrostiniManager::GetInstance());
fake_cicerone_client_->AddObserver(CrostiniManager::GetInstance());
dbus_setter->SetConciergeClient(base::WrapUnique(fake_concierge_client_));
dbus_setter->SetCiceroneClient(base::WrapUnique(fake_cicerone_client_));
dbus_setter->SetSeneschalClient(base::WrapUnique(fake_seneschal_client_));
}
~CrostiniSharePathTest() override { chromeos::DBusThreadManager::Shutdown(); }
void SetUp() override {
run_loop_ = std::make_unique<base::RunLoop>();
profile_ = std::make_unique<TestingProfile>();
}
void TearDown() override {
run_loop_.reset();
profile_.reset();
}
protected:
base::RunLoop* run_loop() { return run_loop_.get(); }
Profile* profile() { return profile_.get(); }
// Owned by chromeos::DBusThreadManager
chromeos::FakeSeneschalClient* fake_seneschal_client_;
chromeos::FakeConciergeClient* fake_concierge_client_;
chromeos::FakeCiceroneClient* fake_cicerone_client_;
std::unique_ptr<base::RunLoop>
run_loop_; // run_loop_ must be created on the UI thread.
std::unique_ptr<TestingProfile> profile_;
private:
base::test::ScopedTaskEnvironment scoped_task_environment_;
content::TestBrowserThreadBundle test_browser_thread_bundle_;
DISALLOW_COPY_AND_ASSIGN(CrostiniSharePathTest);
};
TEST_F(CrostiniSharePathTest, Success) {
vm_tools::concierge::StartVmResponse start_vm_response;
start_vm_response.set_success(true);
start_vm_response.mutable_vm_info()->set_seneschal_server_handle(123);
fake_concierge_client_->set_start_vm_response(start_vm_response);
CrostiniManager::GetInstance()->StartTerminaVm(
"test", "vm-running-success", base::FilePath("path"),
base::BindOnce(
&CrostiniSharePathTest::SharePathSuccessStartTerminaVmCallback,
base::Unretained(this)));
run_loop()->Run();
}
TEST_F(CrostiniSharePathTest, SharePathError) {
vm_tools::concierge::StartVmResponse start_vm_response;
start_vm_response.set_success(true);
start_vm_response.mutable_vm_info()->set_seneschal_server_handle(123);
fake_concierge_client_->set_start_vm_response(start_vm_response);
vm_tools::seneschal::SharePathResponse share_path_response;
share_path_response.set_success(false);
share_path_response.set_failure_reason("test failure");
fake_seneschal_client_->set_share_path_response(share_path_response);
CrostiniManager::GetInstance()->StartTerminaVm(
"test", "vm-running-error", base::FilePath("path"),
base::BindOnce(
&CrostiniSharePathTest::SharePathErrorStartTerminaVmCallback,
base::Unretained(this)));
run_loop()->Run();
}
TEST_F(CrostiniSharePathTest, SharePathErrorVmNotRunning) {
CrostiniSharePath::GetInstance()->SharePath(
profile(), "vm-not-running", "path",
base::BindOnce(&CrostiniSharePathTest::SharePathErrorVmNotRunningCallback,
base::Unretained(this), run_loop()->QuitClosure()));
run_loop()->Run();
}
} // namespace crostini
......@@ -104,6 +104,15 @@ void FakeCiceroneClient::CreateLxdContainer(
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE,
base::BindOnce(std::move(callback), create_lxd_container_response_));
// Trigger CiceroneClient::Observer::NotifyLxdContainerCreatedSignal.
vm_tools::cicerone::LxdContainerCreatedSignal signal;
signal.set_owner_id(request.owner_id());
signal.set_vm_name(request.vm_name());
signal.set_container_name(request.container_name());
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(&FakeCiceroneClient::NotifyLxdContainerCreated,
base::Unretained(this), std::move(signal)));
}
void FakeCiceroneClient::StartLxdContainer(
......@@ -131,6 +140,36 @@ void FakeCiceroneClient::SetUpLxdContainerUser(
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE,
base::BindOnce(std::move(callback), setup_lxd_container_user_response_));
// Trigger CiceroneClient::Observer::NotifyContainerStartedSignal.
vm_tools::cicerone::ContainerStartedSignal signal;
signal.set_owner_id(request.owner_id());
signal.set_vm_name(request.vm_name());
signal.set_container_name(request.container_name());
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(&FakeCiceroneClient::NotifyContainerStarted,
base::Unretained(this), std::move(signal)));
}
void FakeCiceroneClient::NotifyLxdContainerCreated(
const vm_tools::cicerone::LxdContainerCreatedSignal& proto) {
for (auto& observer : observer_list_) {
observer.OnLxdContainerCreated(proto);
}
}
void FakeCiceroneClient::NotifyContainerStarted(
const vm_tools::cicerone::ContainerStartedSignal& proto) {
for (auto& observer : observer_list_) {
observer.OnContainerStarted(proto);
}
}
void FakeCiceroneClient::NotifyTremplinStarted(
const vm_tools::cicerone::TremplinStartedSignal& proto) {
for (auto& observer : observer_list_) {
observer.OnTremplinStarted(proto);
}
}
} // namespace chromeos
......@@ -170,6 +170,14 @@ class CHROMEOS_EXPORT FakeCiceroneClient : public CiceroneClient {
setup_lxd_container_user_response_ = setup_lxd_container_user_response;
}
// Additional functions to allow tests to trigger Signals.
void NotifyLxdContainerCreated(
const vm_tools::cicerone::LxdContainerCreatedSignal& signal);
void NotifyContainerStarted(
const vm_tools::cicerone::ContainerStartedSignal& signal);
void NotifyTremplinStarted(
const vm_tools::cicerone::TremplinStartedSignal& signal);
protected:
void Init(dbus::Bus* bus) override {}
......
......@@ -7,10 +7,12 @@
#include <utility>
#include "base/threading/thread_task_runner_handle.h"
#include "chromeos/dbus/dbus_thread_manager.h"
#include "chromeos/dbus/fake_cicerone_client.h"
namespace chromeos {
FakeConciergeClient::FakeConciergeClient() {
FakeConciergeClient::FakeConciergeClient() : weak_ptr_factory_(this) {
InitializeProtoResponses();
}
FakeConciergeClient::~FakeConciergeClient() = default;
......@@ -64,6 +66,22 @@ void FakeConciergeClient::StartTerminaVm(
start_termina_vm_called_ = true;
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(std::move(callback), start_vm_response_));
// Trigger CiceroneClient::Observer::NotifyTremplinStartedSignal.
vm_tools::cicerone::TremplinStartedSignal tremplin_started_signal;
tremplin_started_signal.set_vm_name(request.name());
tremplin_started_signal.set_owner_id(request.owner_id());
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(&FakeConciergeClient::NotifyTremplinStarted,
weak_ptr_factory_.GetWeakPtr(),
std::move(tremplin_started_signal)));
}
void FakeConciergeClient::NotifyTremplinStarted(
const vm_tools::cicerone::TremplinStartedSignal& signal) {
static_cast<FakeCiceroneClient*>(
chromeos::DBusThreadManager::Get()->GetCiceroneClient())
->NotifyTremplinStarted(signal);
}
void FakeConciergeClient::StopVm(
......
......@@ -5,7 +5,9 @@
#ifndef CHROMEOS_DBUS_FAKE_CONCIERGE_CLIENT_H_
#define CHROMEOS_DBUS_FAKE_CONCIERGE_CLIENT_H_
#include "base/memory/weak_ptr.h"
#include "base/observer_list.h"
#include "chromeos/dbus/cicerone_client.h"
#include "chromeos/dbus/concierge_client.h"
namespace chromeos {
......@@ -128,6 +130,9 @@ class CHROMEOS_EXPORT FakeConciergeClient : public ConciergeClient {
private:
void InitializeProtoResponses();
void NotifyTremplinStarted(
const vm_tools::cicerone::TremplinStartedSignal& signal);
bool create_disk_image_called_ = false;
bool destroy_disk_image_called_ = false;
bool list_vm_disks_called_ = false;
......@@ -145,6 +150,10 @@ class CHROMEOS_EXPORT FakeConciergeClient : public ConciergeClient {
base::ObserverList<Observer>::Unchecked observer_list_;
// Note: This should remain the last member so it'll be destroyed and
// invalidate its weak pointers before any other members are destroyed.
base::WeakPtrFactory<FakeConciergeClient> weak_ptr_factory_;
DISALLOW_COPY_AND_ASSIGN(FakeConciergeClient);
};
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment