// Copyright Epic Games, Inc. All Rights Reserved. using System; using System.Collections.Generic; using System.IO; using System.Text; using System.Text.Json; using System.Threading.Tasks; using System.Xml; using EpicGames.Core; using Microsoft.Extensions.Logging; using UnrealBuildBase; namespace AutomationTool.Tasks { /// /// Parameters for a AWS ECS deploy task /// public class AwsEcsDeployTaskParameters { /// /// Task definition file to use /// [TaskParameter(Optional = false)] public string TaskDefinitionFile { get; set; } /// /// Docker image to set in new task definition (will replace %%DOCKER_PATTERN%% with this value) /// [TaskParameter(Optional = false)] public string DockerImage { get; set; } /// /// App version to set in new task definition (will replace %%VERSION%% with this value) /// [TaskParameter(Optional = true)] public string Version { get; set; } /// /// Cluster ARN representing AWS ECS cluster to operate on /// [TaskParameter(Optional = false)] public string Cluster { get; set; } /// /// Service name to update and deploy to /// [TaskParameter(Optional = false)] public string Service { get; set; } /// /// Environment variables /// [TaskParameter(Optional = true)] public string Environment { get; set; } /// /// File to read environment from /// [TaskParameter(Optional = true)] public string EnvironmentFile { get; set; } /// /// Write output to the log /// [TaskParameter(Optional = true)] public bool LogOutput { get; set; } = false; } /// /// Creates a new AWS ECS task definition and updates the ECS service to use this new revision of the task def /// [TaskElement("Aws-EcsDeploy", typeof(AwsEcsDeployTaskParameters))] public class AwsEcsDeployTask : SpawnTaskBase { /// /// Parameters for this task /// readonly AwsEcsDeployTaskParameters _parameters; /// /// Construct an AWS ECS deploy task /// /// Parameters for the task public AwsEcsDeployTask(AwsEcsDeployTaskParameters parameters) { _parameters = parameters; } /// /// ExecuteAsync the task. /// /// Information about the current job /// Set of build products produced by this node. /// Mapping from tag names to the set of files they include public override async Task ExecuteAsync(JobContext job, HashSet buildProducts, Dictionary> tagNameToFileSet) { string taskDefTemplate = await File.ReadAllTextAsync(ResolveFile(_parameters.TaskDefinitionFile).FullName); string taskDefRendered = taskDefTemplate.Replace("%%DOCKER_IMAGE%%", _parameters.DockerImage, StringComparison.Ordinal); if (_parameters.Version != null) { taskDefRendered = taskDefRendered.Replace("%%VERSION%%", _parameters.Version, StringComparison.Ordinal); } FileReference tempTaskDefFile = FileReference.Combine(Unreal.RootDirectory, "Engine", "Intermediate", "Build", "AwsEcsDeployTaskTemp.json"); DirectoryReference.CreateDirectory(tempTaskDefFile.Directory); await File.WriteAllTextAsync(tempTaskDefFile.FullName, taskDefRendered, new UTF8Encoding(encoderShouldEmitUTF8Identifier: false)); IProcessResult createTaskDefResult = await SpawnTaskBase.ExecuteAsync("aws", $"ecs register-task-definition --cli-input-json \"file://{tempTaskDefFile.FullName}\"", envVars: ParseEnvVars(_parameters.Environment, _parameters.EnvironmentFile), logOutput: _parameters.LogOutput); JsonDocument taskDefJson = JsonDocument.Parse(createTaskDefResult.Output); string taskDefFamily = taskDefJson.RootElement.GetProperty("taskDefinition").GetProperty("family").GetString(); string taskDefRevision = taskDefJson.RootElement.GetProperty("taskDefinition").GetProperty("revision").ToString(); string @params = $"ecs update-service --cluster {_parameters.Cluster} --service {_parameters.Service} --task-definition {taskDefFamily}:{taskDefRevision}"; await SpawnTaskBase.ExecuteAsync("aws", @params, envVars: ParseEnvVars(_parameters.Environment, _parameters.EnvironmentFile), logOutput: _parameters.LogOutput); Logger.LogInformation("Service {Service} updated to use new task def {TaskDefFamily}:{TaskDefRevision}", _parameters.Service, taskDefFamily, taskDefRevision); } /// /// Output this task out to an XML writer. /// public override void Write(XmlWriter writer) { Write(writer, _parameters); } /// /// Find all the tags which are used as inputs to this task /// /// The tag names which are read by this task public override IEnumerable FindConsumedTagNames() { yield break; } /// /// Find all the tags which are modified by this task /// /// The tag names which are modified by this task public override IEnumerable FindProducedTagNames() { yield break; } } }