Commit 556a0e18 authored by Olya Kalitova's avatar Olya Kalitova Committed by Commit Bot

Add call to Cicerone to start Ansible playbook application

Adds call from CrostiniAnsibleManagementService to Cicerone in order to
start Ansible playbook application.

TEST=unit_tests --gtest_filter="Crostini*"

Bug: 1000287
Change-Id: I2424856aff05d9a9d908fbaada43525b40943edd
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1782562
Commit-Queue: Olya Kalitova <okalitova@chromium.org>
Reviewed-by: default avatarNicholas Verne <nverne@chromium.org>
Reviewed-by: default avatarRyo Hashimoto <hashimoto@chromium.org>
Cr-Commit-Position: refs/heads/master@{#695568}
parent afce5fbf
......@@ -7,7 +7,9 @@
#include "base/logging.h"
#include "base/no_destructor.h"
#include "chrome/browser/chromeos/crostini/crostini_manager_factory.h"
#include "chrome/browser/chromeos/crostini/crostini_util.h"
#include "chrome/browser/profiles/profile.h"
#include "chromeos/dbus/dbus_thread_manager.h"
#include "components/keyed_service/content/browser_context_dependency_manager.h"
#include "components/keyed_service/content/browser_context_keyed_service_factory.h"
......@@ -48,6 +50,10 @@ class AnsibleManagementServiceFactory
}
};
chromeos::CiceroneClient* GetCiceroneClient() {
return chromeos::DBusThreadManager::Get()->GetCiceroneClient();
}
} // namespace
AnsibleManagementService* AnsibleManagementService::GetForProfile(
......@@ -123,8 +129,71 @@ void AnsibleManagementService::OnUninstallPackageProgress(
}
void AnsibleManagementService::ApplyAnsiblePlaybookToDefaultContainer(
const std::string& playbook) {
NOTIMPLEMENTED();
const std::string& playbook,
base::OnceCallback<void(bool success)> callback) {
if (!GetCiceroneClient()->IsApplyAnsiblePlaybookProgressSignalConnected()) {
// Technically we could still start the application, but we wouldn't be able
// to detect when the application completes, successfully or otherwise.
LOG(ERROR)
<< "Attempted to apply playbook when progress signal not connected.";
std::move(callback).Run(/*success=*/false);
return;
}
DCHECK(ansible_playbook_application_finished_callback_.is_null());
ansible_playbook_application_finished_callback_ = std::move(callback);
vm_tools::cicerone::ApplyAnsiblePlaybookRequest request;
request.set_owner_id(CryptohomeIdForProfile(profile_));
request.set_vm_name(std::move(kCrostiniDefaultContainerName));
request.set_container_name(std::move(kCrostiniDefaultContainerName));
request.set_playbook(std::move(playbook));
GetCiceroneClient()->ApplyAnsiblePlaybook(
std::move(request),
base::BindOnce(&AnsibleManagementService::OnApplyAnsiblePlaybook,
weak_ptr_factory_.GetWeakPtr()));
}
void AnsibleManagementService::OnApplyAnsiblePlaybook(
base::Optional<vm_tools::cicerone::ApplyAnsiblePlaybookResponse> response) {
if (!response) {
LOG(ERROR) << "Failed to apply Ansible playbook. Empty response.";
std::move(ansible_playbook_application_finished_callback_)
.Run(/*success=*/false);
return;
}
if (response->status() ==
vm_tools::cicerone::ApplyAnsiblePlaybookResponse::FAILED) {
LOG(ERROR) << "Failed to apply Ansible playbook: "
<< response->failure_reason();
std::move(ansible_playbook_application_finished_callback_)
.Run(/*success=*/false);
return;
}
VLOG(1) << "Ansible playbook application has been started successfully";
// Waiting for Ansible playbook application progress being reported.
}
void AnsibleManagementService::OnApplyAnsiblePlaybookProgress(
vm_tools::cicerone::ApplyAnsiblePlaybookProgressSignal::Status status) {
switch (status) {
case vm_tools::cicerone::ApplyAnsiblePlaybookProgressSignal::SUCCEEDED:
std::move(ansible_playbook_application_finished_callback_)
.Run(/*success=*/true);
break;
case vm_tools::cicerone::ApplyAnsiblePlaybookProgressSignal::FAILED:
std::move(ansible_playbook_application_finished_callback_)
.Run(/*success=*/false);
break;
case vm_tools::cicerone::ApplyAnsiblePlaybookProgressSignal::IN_PROGRESS:
// TODO(okalitova): Report Ansible playbook application progress.
break;
default:
NOTREACHED();
}
}
} // namespace crostini
......@@ -29,10 +29,14 @@ class AnsibleManagementService : public KeyedService,
explicit AnsibleManagementService(Profile* profile);
~AnsibleManagementService() override;
// |callback| is called once Ansible installation is finished.
void InstallAnsibleInDefaultContainer(
base::OnceCallback<void(bool success)> callback);
void ApplyAnsiblePlaybookToDefaultContainer(const std::string& playbook);
// |callback| is called once Ansible playbook application is finished.
void ApplyAnsiblePlaybookToDefaultContainer(
const std::string& playbook,
base::OnceCallback<void(bool success)> callback);
// LinuxPackageOperationProgressObserver:
void OnInstallLinuxPackageProgress(const std::string& vm_name,
......@@ -44,12 +48,23 @@ class AnsibleManagementService : public KeyedService,
UninstallPackageProgressStatus status,
int progress_percent) override;
void OnApplyAnsiblePlaybookProgress(
vm_tools::cicerone::ApplyAnsiblePlaybookProgressSignal::Status status);
private:
void OnInstallAnsibleInDefaultContainer(CrostiniResult result);
// Callback for
// CrostiniAnsibleManagementService::ApplyAnsiblePlaybookToDefaultContainer
void OnApplyAnsiblePlaybook(
base::Optional<vm_tools::cicerone::ApplyAnsiblePlaybookResponse>
response);
Profile* profile_;
base::OnceCallback<void(bool success)>
ansible_installation_finished_callback_;
base::OnceCallback<void(bool success)>
ansible_playbook_application_finished_callback_;
base::WeakPtrFactory<AnsibleManagementService> weak_ptr_factory_;
DISALLOW_COPY_AND_ASSIGN(AnsibleManagementService);
......
......@@ -22,11 +22,13 @@ class AnsibleManagementServiceTest : public testing::Test {
fake_cicerone_client_ = static_cast<chromeos::FakeCiceroneClient*>(
chromeos::DBusThreadManager::Get()->GetCiceroneClient());
profile_ = std::make_unique<TestingProfile>();
crostini_manager_ = CrostiniManager::GetForProfile(profile_.get());
ansible_management_service_ =
std::make_unique<AnsibleManagementService>(profile_.get());
AnsibleManagementService::GetForProfile(profile_.get());
}
~AnsibleManagementServiceTest() override {
ansible_management_service_.reset();
ansible_management_service_->Shutdown();
crostini_manager_->Shutdown();
profile_.reset();
chromeos::DBusThreadManager::Shutdown();
}
......@@ -35,7 +37,7 @@ class AnsibleManagementServiceTest : public testing::Test {
Profile* profile() { return profile_.get(); }
AnsibleManagementService* ansible_management_service() {
return ansible_management_service_.get();
return ansible_management_service_;
}
void SendSucceededInstallSignal() {
......@@ -49,11 +51,25 @@ class AnsibleManagementServiceTest : public testing::Test {
fake_cicerone_client_->InstallLinuxPackageProgress(signal);
}
void SendSucceededApplicationSignal() {
vm_tools::cicerone::ApplyAnsiblePlaybookProgressSignal signal;
signal.set_owner_id(CryptohomeIdForProfile(profile()));
signal.set_vm_name(kCrostiniDefaultVmName);
signal.set_container_name(kCrostiniDefaultContainerName);
signal.set_status(
vm_tools::cicerone::ApplyAnsiblePlaybookProgressSignal::SUCCEEDED);
fake_cicerone_client_->NotifyApplyAnsiblePlaybookProgress(signal);
}
content::BrowserTaskEnvironment task_environment_;
std::unique_ptr<TestingProfile> profile_;
std::unique_ptr<AnsibleManagementService> ansible_management_service_;
CrostiniManager* crostini_manager_;
AnsibleManagementService* ansible_management_service_;
base::MockCallback<base::OnceCallback<void(bool)>>
ansible_installation_finished_mock_callback_;
base::MockCallback<base::OnceCallback<void(bool)>>
ansible_playbook_application_finished_mock_callback_;
// Owned by chromeos::DBusThreadManager
chromeos::FakeCiceroneClient* fake_cicerone_client_;
......@@ -93,4 +109,41 @@ TEST_F(AnsibleManagementServiceTest, InstallAnsibleInDefaultContainerFail) {
base::RunLoop().RunUntilIdle();
}
TEST_F(AnsibleManagementServiceTest,
ApplyAnsiblePlaybookToDefaultContainerSuccess) {
vm_tools::cicerone::ApplyAnsiblePlaybookResponse response;
response.set_status(
vm_tools::cicerone::ApplyAnsiblePlaybookResponse::STARTED);
fake_cicerone_client_->set_apply_ansible_playbook_response(response);
EXPECT_CALL(ansible_playbook_application_finished_mock_callback_, Run(true))
.Times(1);
ansible_management_service()->ApplyAnsiblePlaybookToDefaultContainer(
/*playbook=*/"",
ansible_playbook_application_finished_mock_callback_.Get());
// Actually starts applying Ansible playbook.
base::RunLoop().RunUntilIdle();
SendSucceededApplicationSignal();
}
TEST_F(AnsibleManagementServiceTest,
ApplyAnsiblePlaybookToDefaultContainerFail) {
vm_tools::cicerone::ApplyAnsiblePlaybookResponse response;
response.set_status(vm_tools::cicerone::ApplyAnsiblePlaybookResponse::FAILED);
fake_cicerone_client_->set_apply_ansible_playbook_response(response);
EXPECT_CALL(ansible_playbook_application_finished_mock_callback_, Run(false))
.Times(1);
ansible_management_service()->ApplyAnsiblePlaybookToDefaultContainer(
/*playbook=*/"",
ansible_playbook_application_finished_mock_callback_.Get());
// Actually starts applying Ansible playbook.
base::RunLoop().RunUntilIdle();
}
} // namespace crostini
......@@ -2126,6 +2126,16 @@ void CrostiniManager::OnUninstallPackageProgress(
}
}
void CrostiniManager::OnApplyAnsiblePlaybookProgress(
const vm_tools::cicerone::ApplyAnsiblePlaybookProgressSignal& signal) {
if (signal.owner_id() != owner_id_)
return;
// TODO(okalitova): Add an observer.
AnsibleManagementService::GetForProfile(profile_)
->OnApplyAnsiblePlaybookProgress(signal.status());
}
void CrostiniManager::OnUninstallPackageOwningFile(
CrostiniResultCallback callback,
base::Optional<vm_tools::cicerone::UninstallPackageOwningFileResponse>
......
......@@ -467,6 +467,9 @@ class CrostiniManager : public KeyedService,
override;
void OnPendingAppListUpdates(
const vm_tools::cicerone::PendingAppListUpdatesSignal& signal) override;
void OnApplyAnsiblePlaybookProgress(
const vm_tools::cicerone::ApplyAnsiblePlaybookProgressSignal& signal)
override;
// chromeos::PowerManagerClient::Observer overrides:
void SuspendImminent(power_manager::SuspendImminent::Reason reason) override;
......
......@@ -90,6 +90,10 @@ class CiceroneClientImpl : public CiceroneClient {
return is_pending_app_list_updates_signal_connected_;
}
bool IsApplyAnsiblePlaybookProgressSignalConnected() override {
return is_apply_ansible_playbook_progress_signal_connected_;
}
void LaunchContainerApplication(
const vm_tools::cicerone::LaunchContainerApplicationRequest& request,
DBusMethodCallback<vm_tools::cicerone::LaunchContainerApplicationResponse>
......@@ -413,6 +417,29 @@ class CiceroneClientImpl : public CiceroneClient {
weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
}
void ApplyAnsiblePlaybook(
const vm_tools::cicerone::ApplyAnsiblePlaybookRequest& request,
DBusMethodCallback<vm_tools::cicerone::ApplyAnsiblePlaybookResponse>
callback) override {
dbus::MethodCall method_call(
vm_tools::cicerone::kVmCiceroneInterface,
vm_tools::cicerone::kApplyAnsiblePlaybookMethod);
dbus::MessageWriter writer(&method_call);
if (!writer.AppendProtoAsArrayOfBytes(request)) {
LOG(ERROR) << "Failed to encode ApplyAnsiblePlaybookRequest protobuf";
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(std::move(callback), base::nullopt));
return;
}
cicerone_proxy_->CallMethod(
&method_call, kDefaultTimeout.InMilliseconds(),
base::BindOnce(&CiceroneClientImpl::OnDBusProtoResponse<
vm_tools::cicerone::ApplyAnsiblePlaybookResponse>,
weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
}
void WaitForServiceToBeAvailable(
dbus::ObjectProxy::WaitForServiceToBeAvailableCallback callback)
override {
......@@ -517,6 +544,14 @@ class CiceroneClientImpl : public CiceroneClient {
weak_ptr_factory_.GetWeakPtr()),
base::BindOnce(&CiceroneClientImpl::OnSignalConnected,
weak_ptr_factory_.GetWeakPtr()));
cicerone_proxy_->ConnectToSignal(
vm_tools::cicerone::kVmCiceroneInterface,
vm_tools::cicerone::kApplyAnsiblePlaybookProgressSignal,
base::BindRepeating(
&CiceroneClientImpl::OnApplyAnsiblePlaybookProgressSignal,
weak_ptr_factory_.GetWeakPtr()),
base::BindOnce(&CiceroneClientImpl::OnSignalConnected,
weak_ptr_factory_.GetWeakPtr()));
}
private:
......@@ -681,6 +716,18 @@ class CiceroneClientImpl : public CiceroneClient {
}
}
void OnApplyAnsiblePlaybookProgressSignal(dbus::Signal* signal) {
vm_tools::cicerone::ApplyAnsiblePlaybookProgressSignal proto;
dbus::MessageReader reader(signal);
if (!reader.PopArrayOfBytesAsProto(&proto)) {
LOG(ERROR) << "Failed to parse proto from DBus Signal";
return;
}
for (auto& observer : observer_list_) {
observer.OnApplyAnsiblePlaybookProgress(proto);
}
}
void OnSignalConnected(const std::string& interface_name,
const std::string& signal_name,
bool is_connected) {
......@@ -719,6 +766,9 @@ class CiceroneClientImpl : public CiceroneClient {
} else if (signal_name ==
vm_tools::cicerone::kPendingAppListUpdatesSignal) {
is_pending_app_list_updates_signal_connected_ = is_connected;
} else if (signal_name ==
vm_tools::cicerone::kApplyAnsiblePlaybookProgressSignal) {
is_apply_ansible_playbook_progress_signal_connected_ = is_connected;
} else {
NOTREACHED();
}
......@@ -740,6 +790,7 @@ class CiceroneClientImpl : public CiceroneClient {
bool is_export_lxd_container_progress_signal_connected_ = false;
bool is_import_lxd_container_progress_signal_connected_ = false;
bool is_pending_app_list_updates_signal_connected_ = false;
bool is_apply_ansible_playbook_progress_signal_connected_ = false;
// Note: This should remain the last member so it'll be destroyed and
// invalidate its weak pointers before any other members are destroyed.
......
......@@ -83,6 +83,12 @@ class COMPONENT_EXPORT(CHROMEOS_DBUS) CiceroneClient : public DBusClient {
virtual void OnPendingAppListUpdates(
const vm_tools::cicerone::PendingAppListUpdatesSignal& signal) = 0;
// This is signaled from the container while a playbook is being applied
// via ApplyAnsiblePlaybook.
virtual void OnApplyAnsiblePlaybookProgress(
const vm_tools::cicerone::ApplyAnsiblePlaybookProgressSignal&
signal) = 0;
protected:
virtual ~Observer() = default;
};
......@@ -137,6 +143,9 @@ class COMPONENT_EXPORT(CHROMEOS_DBUS) CiceroneClient : public DBusClient {
// PendingAppListUpdatesSignal.
virtual bool IsPendingAppListUpdatesSignalConnected() = 0;
// This should be true prior to calling ApplyAnsiblePlaybook.
virtual bool IsApplyAnsiblePlaybookProgressSignalConnected() = 0;
// Launches an application inside a running Container.
// |callback| is called after the method call finishes.
virtual void LaunchContainerApplication(
......@@ -238,6 +247,13 @@ class COMPONENT_EXPORT(CHROMEOS_DBUS) CiceroneClient : public DBusClient {
DBusMethodCallback<vm_tools::cicerone::CancelImportLxdContainerResponse>
callback) = 0;
// Applies Ansible playbook.
// |callback| is called after the method call finishes.
virtual void ApplyAnsiblePlaybook(
const vm_tools::cicerone::ApplyAnsiblePlaybookRequest& request,
DBusMethodCallback<vm_tools::cicerone::ApplyAnsiblePlaybookResponse>
callback) = 0;
// Registers |callback| to run when the Cicerone service becomes available.
// If the service is already available, or if connecting to the name-owner-
// changed signal fails, |callback| will be run once asynchronously.
......
......@@ -94,6 +94,10 @@ bool FakeCiceroneClient::IsImportLxdContainerProgressSignalConnected() {
return is_import_lxd_container_progress_signal_connected_;
}
bool FakeCiceroneClient::IsApplyAnsiblePlaybookProgressSignalConnected() {
return is_apply_ansible_playbook_progress_signal_connected_;
}
// Currently no tests need to change the output of this method. If you want to
// add one, make it return a variable like the above examples.
bool FakeCiceroneClient::IsPendingAppListUpdatesSignalConnected() {
......@@ -280,6 +284,15 @@ void FakeCiceroneClient::CancelImportLxdContainer(
cancel_import_lxd_container_response_));
}
void FakeCiceroneClient::ApplyAnsiblePlaybook(
const vm_tools::cicerone::ApplyAnsiblePlaybookRequest& request,
DBusMethodCallback<vm_tools::cicerone::ApplyAnsiblePlaybookResponse>
callback) {
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE,
base::BindOnce(std::move(callback), apply_ansible_playbook_response_));
}
void FakeCiceroneClient::NotifyLxdContainerCreated(
const vm_tools::cicerone::LxdContainerCreatedSignal& proto) {
for (auto& observer : observer_list_) {
......@@ -343,4 +356,11 @@ void FakeCiceroneClient::NotifyPendingAppListUpdates(
}
}
void FakeCiceroneClient::NotifyApplyAnsiblePlaybookProgress(
const vm_tools::cicerone::ApplyAnsiblePlaybookProgressSignal& signal) {
for (auto& observer : observer_list_) {
observer.OnApplyAnsiblePlaybookProgress(signal);
}
}
} // namespace chromeos
......@@ -37,6 +37,7 @@ class COMPONENT_EXPORT(CHROMEOS_DBUS) FakeCiceroneClient
bool IsExportLxdContainerProgressSignalConnected() override;
bool IsImportLxdContainerProgressSignalConnected() override;
bool IsPendingAppListUpdatesSignalConnected() override;
bool IsApplyAnsiblePlaybookProgressSignalConnected() override;
void LaunchContainerApplication(
const vm_tools::cicerone::LaunchContainerApplicationRequest& request,
DBusMethodCallback<vm_tools::cicerone::LaunchContainerApplicationResponse>
......@@ -100,6 +101,10 @@ class COMPONENT_EXPORT(CHROMEOS_DBUS) FakeCiceroneClient
const vm_tools::cicerone::CancelImportLxdContainerRequest& request,
DBusMethodCallback<vm_tools::cicerone::CancelImportLxdContainerResponse>
callback) override;
void ApplyAnsiblePlaybook(
const vm_tools::cicerone::ApplyAnsiblePlaybookRequest& request,
DBusMethodCallback<vm_tools::cicerone::ApplyAnsiblePlaybookResponse>
callback) override;
void WaitForServiceToBeAvailable(
dbus::ObjectProxy::WaitForServiceToBeAvailableCallback callback) override;
......@@ -232,10 +237,14 @@ class COMPONENT_EXPORT(CHROMEOS_DBUS) FakeCiceroneClient
void set_lxd_container_os_release(vm_tools::cicerone::OsRelease os_release) {
lxd_container_os_release_ = std::move(os_release);
}
void set_send_container_started_signal(bool send) {
send_container_started_signal_ = send;
}
void set_apply_ansible_playbook_response(
const vm_tools::cicerone::ApplyAnsiblePlaybookResponse&
apply_ansible_playbook_response) {
apply_ansible_playbook_response_ = apply_ansible_playbook_response;
}
// Additional functions to allow tests to trigger Signals.
void NotifyLxdContainerCreated(
......@@ -258,6 +267,8 @@ class COMPONENT_EXPORT(CHROMEOS_DBUS) FakeCiceroneClient
const vm_tools::cicerone::UninstallPackageProgressSignal& signal);
void NotifyPendingAppListUpdates(
const vm_tools::cicerone::PendingAppListUpdatesSignal& signal);
void NotifyApplyAnsiblePlaybookProgress(
const vm_tools::cicerone::ApplyAnsiblePlaybookProgressSignal& signal);
protected:
void Init(dbus::Bus* bus) override {}
......@@ -274,6 +285,7 @@ class COMPONENT_EXPORT(CHROMEOS_DBUS) FakeCiceroneClient
bool is_lxd_container_starting_signal_connected_ = true;
bool is_export_lxd_container_progress_signal_connected_ = true;
bool is_import_lxd_container_progress_signal_connected_ = true;
bool is_apply_ansible_playbook_progress_signal_connected_ = true;
std::string last_container_username_;
bool send_container_started_signal_ = true;
......@@ -313,6 +325,8 @@ class COMPONENT_EXPORT(CHROMEOS_DBUS) FakeCiceroneClient
cancel_export_lxd_container_response_;
vm_tools::cicerone::CancelImportLxdContainerResponse
cancel_import_lxd_container_response_;
vm_tools::cicerone::ApplyAnsiblePlaybookResponse
apply_ansible_playbook_response_;
vm_tools::cicerone::OsRelease lxd_container_os_release_;
......
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