// Copyright 2019 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_ASH_CROSTINI_ANSIBLE_ANSIBLE_MANAGEMENT_SERVICE_H_
#define CHROME_BROWSER_ASH_CROSTINI_ANSIBLE_ANSIBLE_MANAGEMENT_SERVICE_H_
#include <string>
#include "base/functional/callback.h"
#include "base/memory/raw_ptr.h"
#include "chrome/browser/ash/crostini/crostini_manager.h"
#include "chrome/browser/ash/guest_os/guest_id.h"
#include "components/keyed_service/core/keyed_service.h"
class Profile;
namespace views {
class Widget;
} // namespace views
namespace crostini {
struct AnsibleConfiguration {
AnsibleConfiguration(std::string playbook,
base::FilePath path,
base::OnceCallback<void(bool success)> callback);
AnsibleConfiguration(base::FilePath path,
base::OnceCallback<void(bool success)> callback);
~AnsibleConfiguration();
std::string playbook;
base::FilePath path;
base::OnceCallback<void(bool success)> callback;
};
// TODO(okalitova): Install Ansible from backports repo once this is feasible.
extern const char kCrostiniDefaultAnsibleVersion[];
// AnsibleManagementService is responsible for Crostini default
// container management using Ansible.
class AnsibleManagementService : public KeyedService,
public LinuxPackageOperationProgressObserver {
public:
// Observer class for Ansible Management Service related events.
class Observer : public base::CheckedObserver {
public:
~Observer() override = default;
virtual void OnAnsibleSoftwareConfigurationStarted(
const guest_os::GuestId& container_id) = 0;
virtual void OnAnsibleSoftwareConfigurationProgress(
const guest_os::GuestId& container_id,
const std::vector<std::string>& status_lines) {}
virtual void OnAnsibleSoftwareConfigurationFinished(
const guest_os::GuestId& container_id,
bool success) = 0;
virtual void OnAnsibleSoftwareInstall(
const guest_os::GuestId& container_id) {}
virtual void OnApplyAnsiblePlaybook(const guest_os::GuestId& container_id) {
}
// Mainly for testing purposes only. Signals observers that the UI element
// is ready for interaction for a particular configuration task.
virtual void OnAnsibleSoftwareConfigurationUiPrompt(
const guest_os::GuestId& container_id,
bool interactive) {}
};
static AnsibleManagementService* GetForProfile(Profile* profile);
explicit AnsibleManagementService(Profile* profile);
AnsibleManagementService(const AnsibleManagementService&) = delete;
AnsibleManagementService& operator=(const AnsibleManagementService&) = delete;
~AnsibleManagementService() override;
// Preconfigures a container with a specified Ansible playbook.
virtual void ConfigureContainer(
const guest_os::GuestId& container_id,
base::FilePath playbook,
base::OnceCallback<void(bool success)> callback);
// LinuxPackageOperationProgressObserver:
void OnInstallLinuxPackageProgress(const guest_os::GuestId& container_id,
InstallLinuxPackageProgressStatus status,
int progress_percent,
const std::string& error_message) override;
void OnUninstallPackageProgress(const guest_os::GuestId& container_id,
UninstallPackageProgressStatus status,
int progress_percent) override;
virtual void OnApplyAnsiblePlaybookProgress(
const vm_tools::cicerone::ApplyAnsiblePlaybookProgressSignal& signal);
// Gets the input from the user-facing dialog to determine whether or not a
// configuration task should be retried.
void RetryConfiguration(const guest_os::GuestId& container_id);
void CancelConfiguration(const guest_os::GuestId& container_id);
void CompleteConfiguration(const guest_os::GuestId& container_id,
bool success);
void AddObserver(Observer* observer);
void RemoveObserver(Observer* observer);
// Sets up and returns a mock instance of AnsibleManagementService.
static AnsibleManagementService* SetUpMockAnsibleManagementServiceForTesting(
Profile* profile);
views::Widget* GetDialogWidgetForTesting(
const guest_os::GuestId& container_id);
void AddConfigurationTaskForTesting(const guest_os::GuestId& container_id,
views::Widget* widget);
private:
bool IsCancelled(const guest_os::GuestId& container_id);
// Helper function to create the UI elements needed. We won't need to keep
// track of this because it'll be self-destructing.
void CreateUiElement(const guest_os::GuestId& container_id);
void OnInstallAnsibleInContainer(const guest_os::GuestId& container_id,
CrostiniResult result);
void GetAnsiblePlaybookToApply(const guest_os::GuestId& container_id);
void OnAnsiblePlaybookRetrieved(const guest_os::GuestId& container_id,
bool success);
void ApplyAnsiblePlaybook(const guest_os::GuestId& container_id);
void OnApplyAnsiblePlaybook(
const guest_os::GuestId& container_id,
std::optional<vm_tools::cicerone::ApplyAnsiblePlaybookResponse> response);
// Helper function that runs relevant callback and notifies observers.
void OnConfigurationFinished(const guest_os::GuestId& container_id,
bool success);
raw_ptr<Profile> profile_;
base::ObserverList<Observer> observers_;
std::map<guest_os::GuestId, std::unique_ptr<AnsibleConfiguration>>
configuration_tasks_;
// We don't really need to know about these, but keeping them so we can access
// for testing purposes.
std::map<guest_os::GuestId, views::Widget*> ui_elements_;
base::WeakPtrFactory<AnsibleManagementService> weak_ptr_factory_;
};
} // namespace crostini
#endif // CHROME_BROWSER_ASH_CROSTINI_ANSIBLE_ANSIBLE_MANAGEMENT_SERVICE_H_