Commit 9ee4f7f1 authored by Daniel Ng's avatar Daniel Ng Committed by Commit Bot

borealis: Add borealis launch watcher

This object allows us to queue up events to be run once we observe that
the borealis vm has started (or until a specified timeout).

Tests: Built and ran tests.
Bug: b/162562622
Change-Id: Ieaee96af72bc7fc41b90a48111ef8998af6cdf97
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2473781Reviewed-by: default avatarNic Hollingum <hollingum@google.com>
Commit-Queue: Daniel Ng <danielng@google.com>
Cr-Commit-Position: refs/heads/master@{#818378}
parent f83b8afb
...@@ -853,6 +853,8 @@ source_set("chromeos") { ...@@ -853,6 +853,8 @@ source_set("chromeos") {
"borealis/borealis_installer_factory.h", "borealis/borealis_installer_factory.h",
"borealis/borealis_installer_impl.cc", "borealis/borealis_installer_impl.cc",
"borealis/borealis_installer_impl.h", "borealis/borealis_installer_impl.h",
"borealis/borealis_launch_watcher.cc",
"borealis/borealis_launch_watcher.h",
"borealis/borealis_prefs.cc", "borealis/borealis_prefs.cc",
"borealis/borealis_prefs.h", "borealis/borealis_prefs.h",
"borealis/borealis_service.cc", "borealis/borealis_service.cc",
...@@ -3262,6 +3264,7 @@ source_set("unit_tests") { ...@@ -3262,6 +3264,7 @@ source_set("unit_tests") {
"borealis/borealis_context_manager_unittest.cc", "borealis/borealis_context_manager_unittest.cc",
"borealis/borealis_features_unittest.cc", "borealis/borealis_features_unittest.cc",
"borealis/borealis_installer_unittest.cc", "borealis/borealis_installer_unittest.cc",
"borealis/borealis_launch_watcher_unittest.cc",
"borealis/borealis_task_unittest.cc", "borealis/borealis_task_unittest.cc",
"camera_mic/vm_camera_mic_manager_unittest.cc", "camera_mic/vm_camera_mic_manager_unittest.cc",
"cert_provisioning/cert_provisioning_invalidator_unittest.cc", "cert_provisioning/cert_provisioning_invalidator_unittest.cc",
......
// Copyright 2020 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/borealis/borealis_launch_watcher.h"
#include "base/threading/thread_task_runner_handle.h"
#include "chrome/browser/chromeos/profiles/profile_helper.h"
#include "chromeos/dbus/dbus_thread_manager.h"
namespace borealis {
BorealisLaunchWatcher::BorealisLaunchWatcher(Profile* profile,
std::string vm_name)
: owner_id_(chromeos::ProfileHelper::GetUserIdHashFromProfile(profile)),
vm_name_(vm_name) {
chromeos::DBusThreadManager::Get()->GetCiceroneClient()->AddObserver(this);
}
BorealisLaunchWatcher::~BorealisLaunchWatcher() {
chromeos::DBusThreadManager::Get()->GetCiceroneClient()->RemoveObserver(this);
}
void BorealisLaunchWatcher::AwaitLaunch(OnLaunchCallback callback) {
if (container_started_signal_) {
std::move(callback).Run(container_started_signal_->container_name());
} else {
if (callback_queue_.empty()) {
base::SequencedTaskRunnerHandle::Get()->PostDelayedTask(
FROM_HERE,
base::BindOnce(&BorealisLaunchWatcher::TimeoutCallback,
weak_factory_.GetWeakPtr()),
timeout_);
}
callback_queue_.push(std::move(callback));
}
}
void BorealisLaunchWatcher::OnContainerStarted(
const vm_tools::cicerone::ContainerStartedSignal& signal) {
if (signal.owner_id() == owner_id_ && signal.vm_name() == vm_name_ &&
!container_started_signal_) {
container_started_signal_ = signal;
while (!callback_queue_.empty()) {
std::move(callback_queue_.front())
.Run(container_started_signal_->container_name());
callback_queue_.pop();
}
}
}
void BorealisLaunchWatcher::TimeoutCallback() {
while (!callback_queue_.empty()) {
std::move(callback_queue_.front()).Run(base::nullopt);
callback_queue_.pop();
}
}
} // namespace borealis
// Copyright 2020 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_BOREALIS_BOREALIS_LAUNCH_WATCHER_H_
#define CHROME_BROWSER_CHROMEOS_BOREALIS_BOREALIS_LAUNCH_WATCHER_H_
#include "base/callback.h"
#include "base/containers/queue.h"
#include "base/memory/weak_ptr.h"
#include "chromeos/dbus/cicerone_client.h"
class Profile;
namespace borealis {
// Watches to see if a specified VM (from a given owner id and vm name) was
// started.
class BorealisLaunchWatcher : public chromeos::CiceroneClient::Observer {
public:
using OnLaunchCallback =
base::OnceCallback<void(base::Optional<std::string>)>;
BorealisLaunchWatcher(Profile* profile, std::string vm_name);
BorealisLaunchWatcher(const BorealisLaunchWatcher&) = delete;
BorealisLaunchWatcher() = delete;
BorealisLaunchWatcher& operator=(const BorealisLaunchWatcher&) = delete;
~BorealisLaunchWatcher() override;
// Adds a callback to be run when the VM is launched, the callback will be run
// immediately if the VM has already been launched. If no event has been
// observed after |timeout_|, then the callback is run anyway. If successful
// the callback will be run with the name of the container that started,
// otherwise it will be run with nullopt.
void AwaitLaunch(OnLaunchCallback callback);
void SetTimeoutForTesting(base::TimeDelta time) { timeout_ = time; }
private:
void TimeoutCallback();
// CiceroneClient::Observer:
void OnContainerStarted(
const vm_tools::cicerone::ContainerStartedSignal& signal) override;
std::string owner_id_;
std::string vm_name_;
base::TimeDelta timeout_ = base::TimeDelta::FromMilliseconds(5000);
base::Optional<vm_tools::cicerone::ContainerStartedSignal>
container_started_signal_;
base::queue<OnLaunchCallback> callback_queue_;
base::WeakPtrFactory<BorealisLaunchWatcher> weak_factory_{this};
};
} // namespace borealis
#endif // CHROME_BROWSER_CHROMEOS_BOREALIS_BOREALIS_LAUNCH_WATCHER_H_
// Copyright 2020 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/borealis/borealis_launch_watcher.h"
#include <memory>
#include "chrome/browser/chromeos/profiles/profile_helper.h"
#include "chrome/test/base/testing_profile.h"
#include "chromeos/dbus/dbus_thread_manager.h"
#include "chromeos/dbus/fake_cicerone_client.h"
#include "content/public/test/browser_task_environment.h"
#include "testing/gmock/include/gmock/gmock.h"
namespace borealis {
namespace {
class CallbackForTestingExpectation {
public:
base::OnceCallback<void(base::Optional<std::string>)> GetCallback() {
return base::BindOnce(&CallbackForTestingExpectation::Callback,
base::Unretained(this));
}
MOCK_METHOD(void, Callback, (base::Optional<std::string>), ());
};
class BorealisLaunchWatcherTest : public testing::Test {
public:
BorealisLaunchWatcherTest() = default;
BorealisLaunchWatcherTest(const BorealisLaunchWatcherTest&) = delete;
BorealisLaunchWatcherTest& operator=(const BorealisLaunchWatcherTest&) =
delete;
~BorealisLaunchWatcherTest() override = default;
protected:
void SetUp() override {
chromeos::DBusThreadManager::Initialize();
fake_cicerone_client_ = static_cast<chromeos::FakeCiceroneClient*>(
chromeos::DBusThreadManager::Get()->GetCiceroneClient());
}
void TearDown() override {
chromeos::DBusThreadManager::Shutdown();
profile_.reset();
}
// Owned by DBusThreadManager
chromeos::FakeCiceroneClient* fake_cicerone_client_ = nullptr;
std::unique_ptr<TestingProfile> profile_;
content::BrowserTaskEnvironment task_environment_;
private:
void CreateProfile() {
TestingProfile::Builder profile_builder;
profile_builder.SetProfileName("defaultprofile");
profile_ = profile_builder.Build();
}
};
TEST_F(BorealisLaunchWatcherTest, VmStartsCallbackRan) {
testing::StrictMock<CallbackForTestingExpectation> callback_expectation;
BorealisLaunchWatcher watcher(profile_.get(), "FooVm");
vm_tools::cicerone::ContainerStartedSignal signal;
signal.set_owner_id(
chromeos::ProfileHelper::GetUserIdHashFromProfile(profile_.get()));
signal.set_vm_name("FooVm");
signal.set_container_name("FooContainer");
EXPECT_CALL(callback_expectation,
Callback(base::Optional<std::string>("FooContainer")));
watcher.AwaitLaunch(callback_expectation.GetCallback());
fake_cicerone_client_->NotifyContainerStarted(std::move(signal));
task_environment_.RunUntilIdle();
}
TEST_F(BorealisLaunchWatcherTest, VmTimesOutCallbackRan) {
testing::StrictMock<CallbackForTestingExpectation> callback_expectation;
BorealisLaunchWatcher watcher(profile_.get(), "FooVm");
watcher.SetTimeoutForTesting(base::TimeDelta::FromMilliseconds(0));
EXPECT_CALL(callback_expectation,
Callback(base::Optional<std::string>(base::nullopt)));
watcher.AwaitLaunch(callback_expectation.GetCallback());
task_environment_.RunUntilIdle();
}
TEST_F(BorealisLaunchWatcherTest, VmAlreadyStartedCallbackRan) {
testing::StrictMock<CallbackForTestingExpectation> callback_expectation;
BorealisLaunchWatcher watcher(profile_.get(), "FooVm");
vm_tools::cicerone::ContainerStartedSignal signal;
signal.set_owner_id(
chromeos::ProfileHelper::GetUserIdHashFromProfile(profile_.get()));
signal.set_vm_name("FooVm");
signal.set_container_name("FooContainer");
EXPECT_CALL(callback_expectation,
Callback(base::Optional<std::string>("FooContainer")));
fake_cicerone_client_->NotifyContainerStarted(std::move(signal));
watcher.AwaitLaunch(callback_expectation.GetCallback());
task_environment_.RunUntilIdle();
}
TEST_F(BorealisLaunchWatcherTest, VmStartsMultipleCallbacksRan) {
testing::StrictMock<CallbackForTestingExpectation> callback_expectation;
BorealisLaunchWatcher watcher(profile_.get(), "FooVm");
vm_tools::cicerone::ContainerStartedSignal signal;
signal.set_owner_id(
chromeos::ProfileHelper::GetUserIdHashFromProfile(profile_.get()));
signal.set_vm_name("FooVm");
signal.set_container_name("FooContainer");
EXPECT_CALL(callback_expectation,
Callback(base::Optional<std::string>("FooContainer")))
.Times(2);
watcher.AwaitLaunch(callback_expectation.GetCallback());
watcher.AwaitLaunch(callback_expectation.GetCallback());
fake_cicerone_client_->NotifyContainerStarted(std::move(signal));
task_environment_.RunUntilIdle();
}
TEST_F(BorealisLaunchWatcherTest, VmTimesOutMultipleCallbacksRan) {
testing::StrictMock<CallbackForTestingExpectation> callback_expectation;
BorealisLaunchWatcher watcher(profile_.get(), "FooVm");
watcher.SetTimeoutForTesting(base::TimeDelta::FromMilliseconds(0));
EXPECT_CALL(callback_expectation,
Callback(base::Optional<std::string>(base::nullopt)))
.Times(2);
watcher.AwaitLaunch(callback_expectation.GetCallback());
watcher.AwaitLaunch(callback_expectation.GetCallback());
task_environment_.RunUntilIdle();
}
TEST_F(BorealisLaunchWatcherTest, OtherVmsStartBorealisTimesOutCallbackRan) {
testing::StrictMock<CallbackForTestingExpectation> callback_expectation;
BorealisLaunchWatcher watcher(profile_.get(), "FooVm");
watcher.SetTimeoutForTesting(base::TimeDelta::FromMilliseconds(0));
vm_tools::cicerone::ContainerStartedSignal signal1;
signal1.set_owner_id("not-the-owner");
signal1.set_vm_name("FooVm");
vm_tools::cicerone::ContainerStartedSignal signal2;
signal2.set_owner_id(
chromeos::ProfileHelper::GetUserIdHashFromProfile(profile_.get()));
signal2.set_vm_name("not-FooVm");
EXPECT_CALL(callback_expectation,
Callback(base::Optional<std::string>(base::nullopt)));
fake_cicerone_client_->NotifyContainerStarted(std::move(signal1));
fake_cicerone_client_->NotifyContainerStarted(std::move(signal2));
watcher.AwaitLaunch(callback_expectation.GetCallback());
task_environment_.RunUntilIdle();
}
} // namespace
} // namespace borealis
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