// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
using System;
using System.Threading.Tasks;
using AmazonGameLiftPlugin.Core.DeploymentManagement.Models;
using AmazonGameLiftPlugin.Core.Shared;
namespace AmazonGameLift.Editor
{
///
/// A base type for the scenario scripts.
///
public abstract class DeployerBase
{
private const int ChangeSetPollPeriodMs = 1000;
private readonly Delay _delay;
public abstract string DisplayName { get; }
public abstract string Description { get; }
public abstract string HelpUrl { get; }
public abstract string ScenarioFolder { get; }
public abstract bool HasGameServer { get; }
public virtual int PreferredUiOrder { get; }
internal DeploymentRequestFactory RequestFactory { get; set; }
protected CoreApi GameLiftCoreApi { get; }
protected DeployerBase(Delay delay, CoreApi coreApi)
{
_delay = delay ?? throw new ArgumentNullException(nameof(delay));
GameLiftCoreApi = coreApi ?? throw new ArgumentNullException(nameof(coreApi));
RequestFactory = new DeploymentRequestFactory(coreApi);
}
protected DeployerBase()
{
_delay = new Delay();
GameLiftCoreApi = CoreApi.SharedInstance;
RequestFactory = new DeploymentRequestFactory(GameLiftCoreApi);
}
/// For all parameters.
public virtual async Task StartDeployment(string scenarioFolderPath, string buildFolderPath,
string gameName, bool isDevelopmentBuild, ConfirmChangesDelegate confirmChanges)
{
if (confirmChanges is null)
{
throw new ArgumentNullException(nameof(confirmChanges));
}
(DeploymentRequest request, bool success, Response failedResponse) = RequestFactory
.CreateRequest(scenarioFolderPath, gameName, isDevelopmentBuild);
if (!success)
{
return Response.Fail(new DeploymentResponse(failedResponse));
}
ValidateCfnTemplateResponse validateResponse = GameLiftCoreApi.ValidateCfnTemplate(
request.Profile, request.Region, request.CfnTemplatePath);
if (!validateResponse.Success)
{
return Response.Fail(new DeploymentResponse(validateResponse));
}
if (HasGameServer)
{
request = RequestFactory.WithServerBuild(request, buildFolderPath);
}
(DeploymentResponse createResponse, DescribeChangeSetResponse describeResponse) = await CreateChangeSet(request);
if (!createResponse.Success)
{
return createResponse;
}
(bool checkSuccess, bool checkConfirmed, Response checkFailedResponse) = await
CheckChangeConfirmation(request, describeResponse, confirmChanges);
if (!checkSuccess)
{
return Response.Fail(new DeploymentResponse(checkFailedResponse));
}
if (!checkConfirmed)
{
GameLiftCoreApi.DeleteChangeSet(request.Profile, request.Region, request.StackName, request.ChangeSetName);
return Response.Fail(new DeploymentResponse(ErrorCode.OperationCancelled));
}
DeploymentResponse deploymentResponse = await Task.Run(() => Deploy(request));
if (!deploymentResponse.Success)
{
return Response.Fail(deploymentResponse);
}
return Response.Ok(new DeploymentResponse(new DeploymentId(request, DisplayName)));
}
internal virtual async Task<(bool success, bool confirmed, Response failedResponse)> CheckChangeConfirmation(
DeploymentRequest request, DescribeChangeSetResponse changeSetResponse, ConfirmChangesDelegate confirmChanges)
{
DescribeStackResponse existsResponse = GameLiftCoreApi.DescribeStack(request.Profile, request.Region, request.StackName);
if (!existsResponse.Success)
{
return (false, false, existsResponse);
}
if (existsResponse.StackStatus == StackStatus.ReviewInProgress)
{
return (true, true, null);
}
var confirmRequest = new ConfirmChangesRequest
{
Region = request.Region,
StackId = changeSetResponse.StackId,
ChangeSetId = changeSetResponse.ChangeSetId,
Changes = changeSetResponse.Changes,
};
bool confirmed = await confirmChanges.Invoke(confirmRequest);
return (true, confirmed, null);
}
internal virtual async Task<(DeploymentResponse, DescribeChangeSetResponse)> CreateChangeSet(DeploymentRequest request)
{
CreateChangeSetResponse createResponse = GameLiftCoreApi.CreateChangeSet(
request.Profile, request.Region, request.BucketName, request.StackName,
request.CfnTemplatePath, request.ParametersPath, request.GameName, request.LambdaFolderPath, request.BuildS3Key);
if (!createResponse.Success)
{
return (Response.Fail(new DeploymentResponse(createResponse)), null);
}
var poller = new UntilResponseFailurePoller(_delay);
DescribeChangeSetResponse describeResponse = await poller.Poll(ChangeSetPollPeriodMs,
() => GameLiftCoreApi.DescribeChangeSet(request.Profile, request.Region, request.StackName, createResponse.CreatedChangeSetName),
stopCondition: target => target.ExecutionStatus != ChangeSetExecutionStatus.Unavailable);
if (!describeResponse.Success)
{
return (Response.Fail(new DeploymentResponse(describeResponse)), null);
}
if (describeResponse.ExecutionStatus != ChangeSetExecutionStatus.Available)
{
return (Response.Fail(new DeploymentResponse(ErrorCode.ChangeSetStatusInvalid)), null);
}
request.ChangeSetName = createResponse.CreatedChangeSetName;
return (Response.Ok(new DeploymentResponse()), describeResponse);
}
protected abstract Task Deploy(DeploymentRequest request);
}
}