Commit 55d9796a authored by Olya Kalitova's avatar Olya Kalitova Committed by Commit Bot

Add check whether Crostini container is already configured

Adds check whether Crostini container is already configured, so that
default Crostini container configuration is triggered only when default
Crostini container is not yet successfully configured using currently
set CrostiniAnsiblePlaybook policy.

Before this CL: container is reconfigured each time container is started
if policy is specified & feature flag is set.

TEST: unit_tests --gtest_filter="*Ansible*"
Bug: 1024859
Change-Id: If1dc83a8130d35a9aa4eba5c8f5e8b39ea1e0dbf
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2000701
Commit-Queue: Olya Kalitova <okalitova@chromium.org>
Reviewed-by: default avatarNic Hollingum <hollingum@google.com>
Reviewed-by: default avatarNikita Podguzov <nikitapodguzov@chromium.org>
Cr-Commit-Position: refs/heads/master@{#732810}
parent 825d53a1
...@@ -40,6 +40,12 @@ AnsibleManagementService::~AnsibleManagementService() = default; ...@@ -40,6 +40,12 @@ AnsibleManagementService::~AnsibleManagementService() = default;
void AnsibleManagementService::ConfigureDefaultContainer( void AnsibleManagementService::ConfigureDefaultContainer(
base::OnceCallback<void(bool success)> callback) { base::OnceCallback<void(bool success)> callback) {
if (!ShouldConfigureDefaultContainer(profile_)) {
LOG(ERROR) << "Trying to configure default Crostini container when it "
<< "should not be configured";
std::move(callback).Run(false);
return;
}
DCHECK(!configuration_finished_callback_); DCHECK(!configuration_finished_callback_);
configuration_finished_callback_ = std::move(callback); configuration_finished_callback_ = std::move(callback);
...@@ -86,15 +92,11 @@ void AnsibleManagementService::OnInstallLinuxPackageProgress( ...@@ -86,15 +92,11 @@ void AnsibleManagementService::OnInstallLinuxPackageProgress(
switch (status) { switch (status) {
case InstallLinuxPackageProgressStatus::SUCCEEDED: { case InstallLinuxPackageProgressStatus::SUCCEEDED: {
CrostiniManager::GetForProfile(profile_)
->RemoveLinuxPackageOperationProgressObserver(this);
GetAnsiblePlaybookToApply(); GetAnsiblePlaybookToApply();
return; return;
} }
case InstallLinuxPackageProgressStatus::FAILED: case InstallLinuxPackageProgressStatus::FAILED:
LOG(ERROR) << "Ansible installation failed"; LOG(ERROR) << "Ansible installation failed";
CrostiniManager::GetForProfile(profile_)
->RemoveLinuxPackageOperationProgressObserver(this);
OnConfigurationFinished(false); OnConfigurationFinished(false);
return; return;
// TODO(okalitova): Report Ansible downloading/installation progress. // TODO(okalitova): Report Ansible downloading/installation progress.
...@@ -176,6 +178,7 @@ void AnsibleManagementService::OnApplyAnsiblePlaybook( ...@@ -176,6 +178,7 @@ void AnsibleManagementService::OnApplyAnsiblePlaybook(
VLOG(1) << "Ansible playbook application has been started successfully"; VLOG(1) << "Ansible playbook application has been started successfully";
// Waiting for Ansible playbook application progress being reported. // Waiting for Ansible playbook application progress being reported.
// TODO(https://crbug.com/1043060): Add a timeout after which we stop waiting.
} }
void AnsibleManagementService::OnApplyAnsiblePlaybookProgress( void AnsibleManagementService::OnApplyAnsiblePlaybookProgress(
...@@ -216,6 +219,12 @@ void AnsibleManagementService::OnUninstallPackageProgress( ...@@ -216,6 +219,12 @@ void AnsibleManagementService::OnUninstallPackageProgress(
void AnsibleManagementService::OnConfigurationFinished(bool success) { void AnsibleManagementService::OnConfigurationFinished(bool success) {
DCHECK(configuration_finished_callback_); DCHECK(configuration_finished_callback_);
if (success) {
profile_->GetPrefs()->SetBoolean(prefs::kCrostiniDefaultContainerConfigured,
true);
}
CrostiniManager::GetForProfile(profile_)
->RemoveLinuxPackageOperationProgressObserver(this);
for (auto& observer : observers_) { for (auto& observer : observers_) {
observer.OnAnsibleSoftwareConfigurationFinished(success); observer.OnAnsibleSoftwareConfigurationFinished(success);
} }
......
...@@ -48,6 +48,17 @@ class AnsibleManagementServiceTest : public testing::Test { ...@@ -48,6 +48,17 @@ class AnsibleManagementServiceTest : public testing::Test {
return ansible_management_service_; return ansible_management_service_;
} }
void ExecuteSuccessfulConfigurationFlow() {
ansible_management_service()->ConfigureDefaultContainer(
configuration_finished_mock_callback_.Get());
// Should wait for Cicerone response for Linux package install request.
base::RunLoop().RunUntilIdle();
test_helper_->SendSucceededInstallSignal();
// AnsibleManagementService should read playbook file content.
task_environment_.RunUntilIdle();
test_helper_->SendSucceededApplySignal();
}
content::BrowserTaskEnvironment task_environment_; content::BrowserTaskEnvironment task_environment_;
std::unique_ptr<AnsibleManagementTestHelper> test_helper_; std::unique_ptr<AnsibleManagementTestHelper> test_helper_;
base::MockCallback<base::OnceCallback<void(bool)>> base::MockCallback<base::OnceCallback<void(bool)>>
...@@ -74,7 +85,6 @@ TEST_F(AnsibleManagementServiceTest, ConfigureDefaultContainerSuccess) { ...@@ -74,7 +85,6 @@ TEST_F(AnsibleManagementServiceTest, ConfigureDefaultContainerSuccess) {
base::RunLoop().RunUntilIdle(); base::RunLoop().RunUntilIdle();
test_helper_->SendSucceededInstallSignal(); test_helper_->SendSucceededInstallSignal();
base::RunLoop().RunUntilIdle();
task_environment_.RunUntilIdle(); task_environment_.RunUntilIdle();
test_helper_->SendSucceededApplySignal(); test_helper_->SendSucceededApplySignal();
...@@ -104,8 +114,47 @@ TEST_F(AnsibleManagementServiceTest, ConfigureDefaultContainerApplyFail) { ...@@ -104,8 +114,47 @@ TEST_F(AnsibleManagementServiceTest, ConfigureDefaultContainerApplyFail) {
base::RunLoop().RunUntilIdle(); base::RunLoop().RunUntilIdle();
test_helper_->SendSucceededInstallSignal(); test_helper_->SendSucceededInstallSignal();
base::RunLoop().RunUntilIdle();
task_environment_.RunUntilIdle(); task_environment_.RunUntilIdle();
} }
TEST_F(AnsibleManagementServiceTest,
CouldNotConfigureContainerAfterSuccessfullConfiguration) {
test_helper_->SetUpAnsibleInstallation(
vm_tools::cicerone::InstallLinuxPackageResponse::STARTED);
test_helper_->SetUpPlaybookApplication(
vm_tools::cicerone::ApplyAnsiblePlaybookResponse::STARTED);
EXPECT_CALL(configuration_finished_mock_callback_, Run(true)).Times(1);
ExecuteSuccessfulConfigurationFlow();
EXPECT_CALL(configuration_finished_mock_callback_, Run(false)).Times(1);
ansible_management_service()->ConfigureDefaultContainer(
configuration_finished_mock_callback_.Get());
base::RunLoop().RunUntilIdle();
}
TEST_F(AnsibleManagementServiceTest,
CouldConfigureContainerAfterFailedConfiguration) {
test_helper_->SetUpAnsibleInstallation(
vm_tools::cicerone::InstallLinuxPackageResponse::FAILED);
EXPECT_CALL(configuration_finished_mock_callback_, Run(false)).Times(1);
// Unsuccessful sequence of events.
ansible_management_service()->ConfigureDefaultContainer(
configuration_finished_mock_callback_.Get());
base::RunLoop().RunUntilIdle();
CloseCrostiniAnsibleSoftwareConfigViewForTesting();
// Setup for success.
test_helper_->SetUpAnsibleInstallation(
vm_tools::cicerone::InstallLinuxPackageResponse::STARTED);
test_helper_->SetUpPlaybookApplication(
vm_tools::cicerone::ApplyAnsiblePlaybookResponse::STARTED);
EXPECT_CALL(configuration_finished_mock_callback_, Run(true)).Times(1);
ExecuteSuccessfulConfigurationFlow();
}
} // namespace crostini } // namespace crostini
...@@ -48,9 +48,13 @@ const char kUserCrostiniRootAccessAllowedByPolicy[] = ...@@ -48,9 +48,13 @@ const char kUserCrostiniRootAccessAllowedByPolicy[] =
// A file path preference representing a user level enterprise policy that // A file path preference representing a user level enterprise policy that
// specifies Ansible playbook to be applied to the default Crostini container. // specifies Ansible playbook to be applied to the default Crostini container.
// Value is empty when there is no playbook to be applied specified though // Value is empty when there is no playbook to be applied specified though
// policy or playbook specified has already been applied successfully. // policy.
const char kCrostiniAnsiblePlaybookFilePath[] = const char kCrostiniAnsiblePlaybookFilePath[] =
"crostini.ansible_playbook_file_path"; "crostini.ansible_playbook_file_path";
// A boolean preference representing whether default Crostini container is
// successfully configured by kCrostiniAnsiblePlaybook user policy.
const char kCrostiniDefaultContainerConfigured[] =
"crostini.default_container_configured";
// A boolean preference controlling Crostini usage reporting. // A boolean preference controlling Crostini usage reporting.
const char kReportCrostiniUsageEnabled[] = "crostini.usage_reporting_enabled"; const char kReportCrostiniUsageEnabled[] = "crostini.usage_reporting_enabled";
...@@ -103,6 +107,7 @@ void RegisterProfilePrefs(PrefRegistrySimple* registry) { ...@@ -103,6 +107,7 @@ void RegisterProfilePrefs(PrefRegistrySimple* registry) {
registry->RegisterBooleanPref(kUserCrostiniRootAccessAllowedByPolicy, true); registry->RegisterBooleanPref(kUserCrostiniRootAccessAllowedByPolicy, true);
registry->RegisterFilePathPref(kCrostiniAnsiblePlaybookFilePath, registry->RegisterFilePathPref(kCrostiniAnsiblePlaybookFilePath,
base::FilePath()); base::FilePath());
registry->RegisterBooleanPref(kCrostiniDefaultContainerConfigured, false);
registry->RegisterDictionaryPref( registry->RegisterDictionaryPref(
kCrostiniTerminalSettings, base::DictionaryValue(), kCrostiniTerminalSettings, base::DictionaryValue(),
user_prefs::PrefRegistrySyncable::SYNCABLE_PREF); user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
......
...@@ -24,6 +24,7 @@ extern const char kUserCrostiniExportImportUIAllowedByPolicy[]; ...@@ -24,6 +24,7 @@ extern const char kUserCrostiniExportImportUIAllowedByPolicy[];
extern const char kVmManagementCliAllowedByPolicy[]; extern const char kVmManagementCliAllowedByPolicy[];
extern const char kUserCrostiniRootAccessAllowedByPolicy[]; extern const char kUserCrostiniRootAccessAllowedByPolicy[];
extern const char kCrostiniAnsiblePlaybookFilePath[]; extern const char kCrostiniAnsiblePlaybookFilePath[];
extern const char kCrostiniDefaultContainerConfigured[];
extern const char kReportCrostiniUsageEnabled[]; extern const char kReportCrostiniUsageEnabled[];
extern const char kCrostiniLastLaunchTerminaComponentVersion[]; extern const char kCrostiniLastLaunchTerminaComponentVersion[];
......
...@@ -338,9 +338,11 @@ bool IsCrostiniRunning(Profile* profile) { ...@@ -338,9 +338,11 @@ bool IsCrostiniRunning(Profile* profile) {
bool ShouldConfigureDefaultContainer(Profile* profile) { bool ShouldConfigureDefaultContainer(Profile* profile) {
const base::FilePath ansible_playbook_file_path = const base::FilePath ansible_playbook_file_path =
profile->GetPrefs()->GetFilePath(prefs::kCrostiniAnsiblePlaybookFilePath); profile->GetPrefs()->GetFilePath(prefs::kCrostiniAnsiblePlaybookFilePath);
bool default_container_configured = profile->GetPrefs()->GetBoolean(
prefs::kCrostiniDefaultContainerConfigured);
return base::FeatureList::IsEnabled( return base::FeatureList::IsEnabled(
features::kCrostiniAnsibleInfrastructure) && features::kCrostiniAnsibleInfrastructure) &&
!ansible_playbook_file_path.empty(); !default_container_configured && !ansible_playbook_file_path.empty();
} }
// TODO(davidmunro): Answer based on flag and current container version. // TODO(davidmunro): Answer based on flag and current container version.
......
...@@ -39,6 +39,8 @@ void CrostiniAnsiblePlaybookExternalDataHandler::OnExternalDataCleared( ...@@ -39,6 +39,8 @@ void CrostiniAnsiblePlaybookExternalDataHandler::OnExternalDataCleared(
} }
profile->GetPrefs()->ClearPref( profile->GetPrefs()->ClearPref(
crostini::prefs::kCrostiniAnsiblePlaybookFilePath); crostini::prefs::kCrostiniAnsiblePlaybookFilePath);
profile->GetPrefs()->ClearPref(
crostini::prefs::kCrostiniDefaultContainerConfigured);
} }
void CrostiniAnsiblePlaybookExternalDataHandler::OnExternalDataFetched( void CrostiniAnsiblePlaybookExternalDataHandler::OnExternalDataFetched(
...@@ -54,6 +56,8 @@ void CrostiniAnsiblePlaybookExternalDataHandler::OnExternalDataFetched( ...@@ -54,6 +56,8 @@ void CrostiniAnsiblePlaybookExternalDataHandler::OnExternalDataFetched(
} }
profile->GetPrefs()->SetFilePath( profile->GetPrefs()->SetFilePath(
crostini::prefs::kCrostiniAnsiblePlaybookFilePath, file_path); crostini::prefs::kCrostiniAnsiblePlaybookFilePath, file_path);
profile->GetPrefs()->SetBoolean(
crostini::prefs::kCrostiniDefaultContainerConfigured, false);
} }
void CrostiniAnsiblePlaybookExternalDataHandler::RemoveForAccountId( void CrostiniAnsiblePlaybookExternalDataHandler::RemoveForAccountId(
...@@ -66,6 +70,8 @@ void CrostiniAnsiblePlaybookExternalDataHandler::RemoveForAccountId( ...@@ -66,6 +70,8 @@ void CrostiniAnsiblePlaybookExternalDataHandler::RemoveForAccountId(
} }
profile->GetPrefs()->ClearPref( profile->GetPrefs()->ClearPref(
crostini::prefs::kCrostiniAnsiblePlaybookFilePath); crostini::prefs::kCrostiniAnsiblePlaybookFilePath);
profile->GetPrefs()->ClearPref(
crostini::prefs::kCrostiniDefaultContainerConfigured);
} }
} // namespace policy } // namespace policy
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
#include "chrome/browser/chromeos/crostini/crostini_util.h" #include "chrome/browser/chromeos/crostini/crostini_util.h"
#include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/views/crostini/crostini_browser_test_util.h" #include "chrome/browser/ui/views/crostini/crostini_browser_test_util.h"
#include "chrome/common/chrome_features.h"
#include "chrome/grit/generated_resources.h" #include "chrome/grit/generated_resources.h"
#include "content/public/browser/network_service_instance.h" #include "content/public/browser/network_service_instance.h"
#include "services/network/test/test_network_connection_tracker.h" #include "services/network/test/test_network_connection_tracker.h"
...@@ -24,7 +25,10 @@ class CrostiniAnsibleSoftwareConfigViewBrowserTest ...@@ -24,7 +25,10 @@ class CrostiniAnsibleSoftwareConfigViewBrowserTest
container_id_(crostini::kCrostiniDefaultVmName, container_id_(crostini::kCrostiniDefaultVmName,
crostini::kCrostiniDefaultContainerName), crostini::kCrostiniDefaultContainerName),
network_connection_tracker_( network_connection_tracker_(
network::TestNetworkConnectionTracker::CreateInstance()) {} network::TestNetworkConnectionTracker::CreateInstance()) {
scoped_feature_list_.InitAndEnableFeature(
features::kCrostiniAnsibleInfrastructure);
}
// CrostiniDialogBrowserTest: // CrostiniDialogBrowserTest:
void ShowUi(const std::string& name) override { void ShowUi(const std::string& name) override {
...@@ -114,6 +118,7 @@ class CrostiniAnsibleSoftwareConfigViewBrowserTest ...@@ -114,6 +118,7 @@ class CrostiniAnsibleSoftwareConfigViewBrowserTest
std::unique_ptr<network::TestNetworkConnectionTracker> std::unique_ptr<network::TestNetworkConnectionTracker>
network_connection_tracker_; network_connection_tracker_;
std::unique_ptr<crostini::AnsibleManagementTestHelper> test_helper_; std::unique_ptr<crostini::AnsibleManagementTestHelper> test_helper_;
base::test::ScopedFeatureList scoped_feature_list_;
}; };
IN_PROC_BROWSER_TEST_F(CrostiniAnsibleSoftwareConfigViewBrowserTest, IN_PROC_BROWSER_TEST_F(CrostiniAnsibleSoftwareConfigViewBrowserTest,
......
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