/// <summary> /// Gets the most recent stack event. /// </summary> /// <param name="stackId">The stack identifier.</param> /// <returns>Timestamp of most recent event</returns> private async Task <DateTime> GetMostRecentStackEvent(string stackId) { // CF returns events in descending chronological order var response = await PollyHelper.ExecuteWithPolly( () => this.client.DescribeStackEventsAsync( new DescribeStackEventsRequest { StackName = stackId })); return(response.StackEvents.Any() ? response.StackEvents.First().Timestamp : DateTime.MinValue); }
/// <summary>Waits for a stack operation. to compete, sending stack event messages to the log.</summary> /// <param name="stackArn"> ARN or name of stack to wait on.</param> /// <param name="throwOnFailure">if set to <c>true</c> [throw on failure].</param> /// <returns>The stack being waited on.</returns> private async Task <Stack> WaitStackOperationAsync(string stackArn, bool throwOnFailure) { var describeStacksRequest = new DescribeStacksRequest { StackName = stackArn }; while (true) { Thread.Sleep(this.waitPollTime); var stack = (await PollyHelper.ExecuteWithPolly( () => this.client.DescribeStacksAsync(describeStacksRequest))).Stacks.First(); // Have we finished? var isComplete = stack.StackStatus.Value.EndsWith("COMPLETE") || stack.StackStatus.Value.EndsWith("FAILED"); // Get events and render them var events = (await GetEventsAsync(stackArn)).OrderBy(e => e.Timestamp).ToList(); if (events.Any()) { // Most recent event is last this.lastEventTime = events.Last().Timestamp; // Now output them oldest first foreach (var evt in events.OrderBy(e => e.Timestamp)) { this.context.Logger.LogStackEvent(evt); } } if (isComplete) { // Operation finished if (Regex.IsMatch(stack.StackStatus.Value, "(ROLLBACK|FAILED)") && throwOnFailure) { throw new StackOperationException(stack); } // We done return(stack); } } // Local recursive function to collect events from nested stacks async Task <IEnumerable <StackEvent> > GetEventsAsync(string stackArnLocal) { string nextToken = null; var allEvents = new List <StackEvent>(); // Get events for this stack do { var response = await PollyHelper.ExecuteWithPolly( () => this.client.DescribeStackEventsAsync( new DescribeStackEventsRequest { // ReSharper disable once AccessToModifiedClosure StackName = stackArnLocal, NextToken = nextToken })); nextToken = response.NextToken; allEvents.AddRange(response.StackEvents.Where(e => e.Timestamp > this.lastEventTime)); if (response.StackEvents.Any() && response.StackEvents.Last().Timestamp <= this.lastEventTime) { // Break here as remaining events will be older. nextToken = null; } }while (nextToken != null); // Enumerate any nested stack resources and recurse into each foreach (var nested in (await PollyHelper.ExecuteWithPolly( () => this.client.DescribeStackResourcesAsync( new DescribeStackResourcesRequest { StackName = stackArnLocal }))) .StackResources.Where( r => r.ResourceType == "AWS::CloudFormation::Stack" && !string.IsNullOrEmpty(r.PhysicalResourceId))) { allEvents.AddRange(await GetEventsAsync(nested.PhysicalResourceId)); } return(allEvents); } }