Editor/Window/Containers/ContainersFleetUpdatePopup.cs (364 lines of code) (raw):
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
using Amazon.ECR.Model;
using AmazonGameLiftPlugin.Core.ContainerManagement.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using UnityEditor;
using UnityEngine;
using UnityEngine.UIElements;
namespace AmazonGameLift.Editor
{
public class ContainersFleetUpdatePopup : EditorWindow
{
// Unity window fields
private static VisualTreeAsset m_VisualTreeAsset;
private VisualElement _root;
private readonly TextProvider _textProvider = TextProviderFactory.Create();
public Action OnConfirm;
private const float MinPopupWidth = 1000f;
private const float MinPopupHeight = 330f;
private const float MaxPopupWidth = 2 * MinPopupWidth;
private const float MaxPopupHeight = 3 * MinPopupHeight;
// Visual elements
private VisualElement _containerServerBuildInputRow;
private VisualElement _containerServerExecutableInputRow;
private VisualElement _containerECRImageDropdownRow;
private VisualElement _dockerImageIDInputRow;
private VisualElement _containerImageTagInputRow;
// Common elements
private StateManager _stateManager;
private StatusBox _statusBox;
private Button _updateButton;
private Button _visitAwsConsoleButton;
private Button _cancelButton;
// Building docker
private TextField _buildDirectoryInput;
private TextField _buildExecutableInput;
private Button _serverFolderButton;
private Button _serverFileButton;
// Using docker
private TextField _dockerImageIdInput;
// Using ECR repository
private DropdownField _selectECRRepositoryDropdown;
private DropdownField _selectECRImageDropdown;
private TextField _containerImageTagInput;
private readonly Dictionary<string, string> _ecrRepoNameUriMap = new Dictionary<string, string>();
private DescribeECRRepositoriesResponse _ecrRepositories;
// Deployment scenario
private DeploymentScenariosInput _deploymentScenarioInput;
private DeploymentScenarios _selectedDeploymentScenario;
// Always present settings
private TextField _containerPortRangeInput;
private TextField _containerTotalMemoryInput;
private TextField _containerTotalVcpuInput;
private Label _invalidPortRangeLabel;
private Label _invalidMemoryLimitLabel;
private Label _invalidVcpuLimitLabel;
private Label _invalidContainerImageTagLabel;
private Label _invalidGameServerBuildLabel;
private Label _invalidGameServerExecutableLabel;
private Label _invalidECRRepoLabel;
private Label _invalidECRImageLabel;
private Label _invalidDockerImageIDLabel;
private CoreApi _coreApi;
private ContainersUserInputValidation _inputValidator;
public void OnEnable()
{
_root = rootVisualElement;
m_VisualTreeAsset = UnityEngine.Resources.Load<VisualTreeAsset>("EditorWindow/Components/Containers/ContainersFleetUpdatePopup");
_root.Add(m_VisualTreeAsset.Instantiate());
titleContent = new GUIContent(_textProvider.Get(Strings.ContainersFleetUpdatePopupWindowTitle));
maxSize = new Vector2(MaxPopupWidth, MaxPopupHeight);
minSize = new Vector2(MinPopupWidth, MinPopupHeight);
}
public void Init(StateManager stateManager)
{
_stateManager = stateManager;
_coreApi = CoreApi.SharedInstance;
ReferenceComponents();
_inputValidator = new ContainersUserInputValidation(GetErrorMessageMappings(), GetInputMappings());
RegisterCallbacks();
var scenarioContainer = _root.Q("ContainersDeploymentScenarios");
_deploymentScenarioInput =
new DeploymentScenariosInput(scenarioContainer, _stateManager.ContainerDeploymentScenario,
_stateManager.IsBootstrapped() && _stateManager.IsInContainersRegion(), _stateManager, () => _stateManager.ContainerDeploymentScenario);
_deploymentScenarioInput.OnValueChanged += value => { _selectedDeploymentScenario = value; };
PopulateComponents();
LocalizeText();
_inputValidator.OnValidationEvent += EnableDisableUpdateButton;
EnableDisableUpdateButton();
}
private void ReferenceComponents()
{
_containerServerBuildInputRow = _root.Q<VisualElement>("ContainerServerBuildInputRow");
_containerServerExecutableInputRow = _root.Q<VisualElement>("ContainerServerExecutableInputRow");
_containerECRImageDropdownRow = _root.Q<VisualElement>("ContainerECRImageDropdownRow");
_dockerImageIDInputRow = _root.Q<VisualElement>("DockerImageIDRow");
_containerImageTagInputRow = _root.Q<VisualElement>("ContainerImageTagInputRow");
_buildDirectoryInput = _root.Q<TextField>("ContainerGameServerBuildInput");
_buildExecutableInput = _root.Q<TextField>("ContainerGameServerExecutableInput");
_dockerImageIdInput = _root.Q<TextField>("DockerImageIDInput");
_containerPortRangeInput = _root.Q<TextField>("ContainerPortRangeInput");
_containerTotalMemoryInput = _root.Q<TextField>("ContainerTotalMemoryInput");
_containerTotalVcpuInput = _root.Q<TextField>("ContainerTotalVcpuInput");
_containerImageTagInput = _root.Q<TextField>("ContainerImageTagInput");
_invalidPortRangeLabel = _root.Q<Label>("ContainerConnectionPortRangeInvalidMessage");
_invalidMemoryLimitLabel = _root.Q<Label>("ContainerTotalMemoryInvalidMessage");
_invalidVcpuLimitLabel = _root.Q<Label>("ContainerTotalVcpuInvalidMessage");
_invalidContainerImageTagLabel = _root.Q<Label>("ContainerImageTagInvalidMessage");
_invalidDockerImageIDLabel = _root.Q<Label>("DockerImageIDInvalidMessage");
_invalidECRImageLabel = _root.Q<Label>("SelectECRImageDropdownInvalidMessage");
_invalidECRRepoLabel = _root.Q<Label>("SelectECRRepositoryDropdownInvalidMessage");
_invalidGameServerExecutableLabel = _root.Q<Label>("ContainerGameServerExecutableInputInvalidMessage");
_invalidGameServerBuildLabel = _root.Q<Label>("ContainerGameServerBuildInputInvalidMessage");
_selectECRRepositoryDropdown = _root.Q<DropdownField>("SelectECRRepositoryDropdown");
_selectECRImageDropdown = _root.Q<DropdownField>("SelectECRImageDropdown");
_serverFileButton = _root.Q<Button>("ContainerGameServerExecutableButton");
_serverFolderButton = _root.Q<Button>("ContainerGameServerBuildButton");
_visitAwsConsoleButton = _root.Q<Button>("ContainersFleetUpdatePopupVisitConsoleButton");
_updateButton = _root.Q<Button>(Strings.ContainersFleetUpdatePopupUpdateButton);
_cancelButton = _root.Q<Button>(Strings.ContainersFleetUpdatePopupCancelButton);
_statusBox = _root.Q<StatusBox>("ContainersFleetUpdatePopupStatusBox");
_statusBox.Show(StatusBox.StatusBoxType.Warning,
_textProvider.Get(Strings.ContainersFleetUpdateStatusBoxText), null, Urls.AwsFreeTier, _textProvider.Get(Strings.ContainersFleetUpdateStatusBoxButtonText));
}
private void RegisterCallbacks()
{
// buttons
_visitAwsConsoleButton.RegisterCallback<ClickEvent>(_ => Application.OpenURL(Urls.AboutGameLift));
_cancelButton.RegisterCallback<ClickEvent>(_ => Close());
_updateButton.RegisterCallback<ClickEvent>(_ =>
{
if (_inputValidator.AllInputsValid())
{
SaveValues();
OnConfirm?.Invoke();
Close();
}
else
{
EnableDisableUpdateButton();
}
});
_serverFolderButton.RegisterCallback<ClickEvent>(_ =>
{
var value = EditorUtility.OpenFolderPanel("Game Server Build Directory Path",
Application.dataPath, "");
_buildDirectoryInput.value = value;
});
_serverFileButton.RegisterCallback<ClickEvent>(_ =>
{
var value = EditorUtility.OpenFilePanel("Game Server Build Executable Path",
_stateManager.ContainerGameServerBuildPath, "");
_buildExecutableInput.value = value;
});
//dropdowns
_selectECRRepositoryDropdown.RegisterValueChangedCallback(evt => {
PopulateECRImagesDropdown(evt.newValue);
});
//validations
_inputValidator.RegisterValidationCallbacks();
}
private void PopulateComponents()
{
// Building docker
_buildDirectoryInput.value = _stateManager.ContainerGameServerBuildPath;
_buildExecutableInput.value = _stateManager.ContainerGameServerExecutable;
// Using docker
_dockerImageIdInput.value = GetOrDefault(_stateManager.ContainerDockerImageId, ContainersUserInputValidation.DEFAULT_IMAGE_TAG);
// Using ECR repository
_selectECRRepositoryDropdown.value = _stateManager.ContainerECRRepositoryName;
_selectECRImageDropdown.value = _stateManager.ContainerECRImageId;
// Image tag
_containerImageTagInput.value = GetOrDefault(_stateManager.ContainerImageTag, ContainersUserInputValidation.DEFAULT_IMAGE_TAG);
// Always present settings
_containerPortRangeInput.value = GetOrDefault(_stateManager.ContainerPortRange, ContainersUserInputValidation.DEFAULT_PORT_RANGE);
_containerTotalMemoryInput.value = GetOrDefault(_stateManager.ContainerTotalMemory, ContainersUserInputValidation.DEFAULT_MEMORY_LIMIT);
_containerTotalVcpuInput.value = GetOrDefault(_stateManager.ContainerTotalVcpu, ContainersUserInputValidation.DEFAULT_VCPU_LIMIT);
PopulateECRRepositoriesDropdown();
if (_stateManager.ContainerQuestionnaireScenario == ContainerScenarios.HaveContainerImageInEcr)
{
PopulateECRImagesDropdown(_stateManager.ContainerECRRepositoryName);
}
ShowHideComponents();
}
private Dictionary<ContainersUserInputType, Label> GetErrorMessageMappings()
{
var errorMessagesMappings = new Dictionary<ContainersUserInputType, Label>
{
{ ContainersUserInputType.ConnectionPortRangeInput, _invalidPortRangeLabel },
{ ContainersUserInputType.ContainerImageTagInput, _invalidContainerImageTagLabel },
{ ContainersUserInputType.DockerImageInput, _invalidDockerImageIDLabel },
{ ContainersUserInputType.EcrImageDropdown, _invalidECRImageLabel },
{ ContainersUserInputType.EcrRepositoryDropdown, _invalidECRRepoLabel },
{ ContainersUserInputType.GameServerExecutableInput, _invalidGameServerExecutableLabel },
{ ContainersUserInputType.GameServerFolderInput, _invalidGameServerBuildLabel },
{ ContainersUserInputType.MemoryLimitInput, _invalidMemoryLimitLabel },
{ ContainersUserInputType.VcpuLimitInput, _invalidVcpuLimitLabel }
};
return errorMessagesMappings;
}
private Dictionary<ContainersUserInputType, VisualElement> GetInputMappings()
{
var inputMappings = new Dictionary<ContainersUserInputType, VisualElement>
{
{ ContainersUserInputType.ConnectionPortRangeInput, _containerPortRangeInput },
{ ContainersUserInputType.ContainerImageTagInput, _containerImageTagInput },
{ ContainersUserInputType.DockerImageInput, _dockerImageIdInput },
{ ContainersUserInputType.EcrImageDropdown, _selectECRImageDropdown },
{ ContainersUserInputType.EcrRepositoryDropdown, _selectECRRepositoryDropdown },
{ ContainersUserInputType.GameServerExecutableInput, _buildExecutableInput },
{ ContainersUserInputType.GameServerFolderInput, _buildDirectoryInput },
{ ContainersUserInputType.MemoryLimitInput, _containerTotalMemoryInput },
{ ContainersUserInputType.VcpuLimitInput, _containerTotalVcpuInput }
};
return inputMappings;
}
private void EnableDisableUpdateButton()
{
_updateButton.SetEnabled(_inputValidator.AllInputsValid());
}
private void HideAll()
{
StatefulInput.Hide(_containerServerBuildInputRow);
StatefulInput.Hide(_containerServerExecutableInputRow);
StatefulInput.Hide(_containerECRImageDropdownRow);
StatefulInput.Hide(_dockerImageIDInputRow);
StatefulInput.Hide(_containerImageTagInputRow);
StatefulInput.Hide(_invalidECRImageLabel);
StatefulInput.Hide(_invalidContainerImageTagLabel);
StatefulInput.Hide(_invalidPortRangeLabel);
StatefulInput.Hide(_invalidMemoryLimitLabel);
StatefulInput.Hide(_invalidVcpuLimitLabel);
StatefulInput.Hide(_invalidGameServerBuildLabel);
StatefulInput.Hide(_invalidGameServerExecutableLabel);
StatefulInput.Hide(_invalidECRRepoLabel);
StatefulInput.Hide(_invalidDockerImageIDLabel);
}
private void ShowHideComponents()
{
// always active
List<ContainersUserInputType> activeInputs = new List<ContainersUserInputType>
{
ContainersUserInputType.VcpuLimitInput,
ContainersUserInputType.MemoryLimitInput,
ContainersUserInputType.ConnectionPortRangeInput,
ContainersUserInputType.EcrRepositoryDropdown,
};
HideAll();
switch (_stateManager.ContainerQuestionnaireScenario)
{
case ContainerScenarios.NoContainerImageNoExistingEcrRepo:
case ContainerScenarios.NoContainerImageUseExistingEcrRepo:
StatefulInput.Show(_containerServerBuildInputRow);
StatefulInput.Show(_containerServerExecutableInputRow);
StatefulInput.Show(_containerImageTagInputRow);
activeInputs.Add(ContainersUserInputType.GameServerFolderInput);
activeInputs.Add(ContainersUserInputType.GameServerExecutableInput);
activeInputs.Add(ContainersUserInputType.ContainerImageTagInput);
break;
case ContainerScenarios.HaveContainerImageInDocker:
StatefulInput.Show(_dockerImageIDInputRow);
StatefulInput.Show(_containerImageTagInputRow);
activeInputs.Add(ContainersUserInputType.DockerImageInput);
activeInputs.Add(ContainersUserInputType.ContainerImageTagInput);
break;
case ContainerScenarios.HaveContainerImageInEcr:
StatefulInput.Show(_containerECRImageDropdownRow);
activeInputs.Add(ContainersUserInputType.EcrImageDropdown);
break;
default:
throw new InvalidOperationException(
$"Expected questionnaire to be filled out before popup opened. State was {_stateManager.ContainerQuestionnaireScenario}.");
}
_inputValidator.SetActiveInputs(activeInputs);
}
private void SaveValues()
{
_stateManager.ContainerPortRange = _containerPortRangeInput.value;
_stateManager.ContainerTotalMemory = _containerTotalMemoryInput.value;
_stateManager.ContainerTotalVcpu = _containerTotalVcpuInput.value;
_stateManager.ContainerDeploymentScenario = _selectedDeploymentScenario;
_stateManager.ContainerECRRepositoryName = _selectECRRepositoryDropdown.value;
_stateManager.ContainerECRRepositoryUri = _ecrRepoNameUriMap[_selectECRRepositoryDropdown.value];
switch (_stateManager.ContainerQuestionnaireScenario)
{
case ContainerScenarios.NoContainerImageNoExistingEcrRepo:
case ContainerScenarios.NoContainerImageUseExistingEcrRepo:
_stateManager.ContainerGameServerBuildPath = _buildDirectoryInput.value;
_stateManager.ContainerGameServerExecutable = _buildExecutableInput.value;
_stateManager.ContainerImageTag = _containerImageTagInput.value;
break;
case ContainerScenarios.HaveContainerImageInDocker:
_stateManager.ContainerDockerImageId = _dockerImageIdInput.value;
_stateManager.ContainerImageTag = _containerImageTagInput.value;
break;
case ContainerScenarios.HaveContainerImageInEcr:
_stateManager.ContainerECRImageId = _selectECRImageDropdown.value;
break;
default:
throw new InvalidOperationException(
$"Expected questionnaire to be filled out before popup opened. State was {_stateManager.ContainerQuestionnaireScenario}.");
}
}
private static string GetOrDefault(string value, string defaultValue)
{
return string.IsNullOrEmpty(value) ? defaultValue : value;
}
private void LocalizeText()
{
var l = new ElementLocalizer(_root);
var strings = new[]
{
Strings.ContainerGameServerBuildLabel,
Strings.ContainerGameServerExecutableLabel,
Strings.DockerImageIDLabel,
Strings.ContainersFleetUpdatePopupDescription,
Strings.ContainersFleetUpdateDeploymentDetailsTitle,
Strings.SelectECRRepositoryLabel,
Strings.ContainerSelectImageLabel,
Strings.ContainerImageTagLabel,
Strings.ContainerConnectionPortRangeLabel,
Strings.ContainerConnectionPortRangeInvalidMessage,
Strings.ContainerTotalMemoryLabel,
Strings.ContainerTotalMemoryInvalidMessage,
Strings.ContainerTotalVcpuLabel,
Strings.ContainerTotalVcpuInvalidMessage,
Strings.ContainersFleetUpdatePopupVisitConsoleButtonLabel,
Strings.ContainersFleetUpdatePopupCancelButton,
Strings.ContainersFleetUpdatePopupUpdateButton,
};
foreach (var s in strings)
{
l.SetElementText(s, s);
}
}
private void PopulateECRImagesDropdown(string repositoryName)
{
if (_stateManager.IsBootstrapped() && _stateManager.IsInContainersRegion())
{
if (!string.IsNullOrEmpty(repositoryName))
{
var images = _coreApi.ListECRImages(_stateManager.ProfileName, _stateManager.Region, repositoryName);
List<string> choices = images.ECRImages.Select(image => string.IsNullOrEmpty(image.ImageTag) ? image.ImageDigest : image.ImageTag).ToList();
_selectECRImageDropdown.choices = choices;
if (!string.IsNullOrEmpty(_stateManager.ContainerECRImageId) && choices.Contains(_stateManager.ContainerECRImageId))
{
_selectECRImageDropdown.value = _stateManager.ContainerECRImageId;
return;
}
}
}
_selectECRImageDropdown.value = null;
}
private void PopulateECRRepositoriesDropdown()
{
if (_stateManager.IsBootstrapped() && _stateManager.IsInContainersRegion())
{
_ecrRepositories = _coreApi.DescribeECRRepositories(_stateManager.ProfileName, _stateManager.Region);
if (_ecrRepositories != null && _ecrRepositories.ECRRepositories != null)
{
List<string> choices = _ecrRepositories.ECRRepositories.Select(repository => repository.RepositoryName).ToList();
_selectECRRepositoryDropdown.choices = choices;
foreach (Repository repo in _ecrRepositories.ECRRepositories)
{
if (!_ecrRepoNameUriMap.ContainsKey(repo.RepositoryName))
{
_ecrRepoNameUriMap.Add(repo.RepositoryName, repo.RepositoryUri);
}
}
if (!string.IsNullOrEmpty(_stateManager.ContainerECRRepositoryName) && choices.Contains(_stateManager.ContainerECRRepositoryName))
{
_selectECRRepositoryDropdown.value = _stateManager.ContainerECRRepositoryName;
return;
}
}
}
_selectECRRepositoryDropdown.value = null;
}
}
}