Example #1
0
        async IAsyncEnumerable <DescribeStackEventsResponse> IPaginator <DescribeStackEventsResponse> .PaginateAsync(CancellationToken cancellationToken = default)
        {
            if (Interlocked.Exchange(ref _isPaginatorInUse, 1) != 0)
            {
                throw new System.InvalidOperationException("Paginator has already been consumed and cannot be reused. Please create a new instance.");
            }
            var nextToken = _request.NextToken;
            DescribeStackEventsResponse response;

            do
            {
                _request.NextToken = nextToken;
                response           = await _client.DescribeStackEventsAsync(_request, cancellationToken).ConfigureAwait(false);

                nextToken = response.NextToken;
                cancellationToken.ThrowIfCancellationRequested();
                yield return(response);
            }while (nextToken != null);
        }
Example #2
0
        public static async Task <string> GetMostRecentStackEventIdAsync(this IAmazonCloudFormation cfnClient, string stackName)
        {
            try {
                var response = await cfnClient.DescribeStackEventsAsync(new DescribeStackEventsRequest {
                    StackName = stackName
                });

                var mostRecentStackEvent = response.StackEvents.First();

                // make sure the stack is not already in an update operation
                if (!mostRecentStackEvent.IsFinalStackEvent())
                {
                    throw new System.InvalidOperationException("stack appears to be undergoing an update operation");
                }
                return(mostRecentStackEvent.EventId);
            } catch (AmazonCloudFormationException) {
                // NOTE (2018-12-11, bjorg): exception is thrown when stack doesn't exist; ignore it
            }
            return(null);
        }
Example #3
0
        private static async Task <List <StackEvent> > GetLatestEventsAsync(ILogger log, IAmazonCloudFormation cloudformation, string stackName, DateTime mintimeStampForEvents, string mostRecentEventId)
        {
            var noNewEvents = false;
            var events      = new List <StackEvent>();

            DescribeStackEventsResponse response = null;

            do
            {
                var request = new DescribeStackEventsRequest {
                    StackName = stackName
                };
                if (response != null)
                {
                    request.NextToken = response.NextToken;
                }

                try
                {
                    response = await cloudformation.DescribeStackEventsAsync(request).ConfigureAwait(false);
                }
                catch (Exception e)
                {
                    log.Error(e, "Error getting events for stack");
                    throw;
                }

                foreach (var evnt in response.StackEvents)
                {
                    if (string.Equals(evnt.EventId, mostRecentEventId) || evnt.Timestamp < mintimeStampForEvents)
                    {
                        noNewEvents = true;
                        break;
                    }
                    events.Add(evnt);
                }
            } while (!noNewEvents && !string.IsNullOrEmpty(response.NextToken));

            return(events);
        }
 private Amazon.CloudFormation.Model.DescribeStackEventsResponse CallAWSServiceOperation(IAmazonCloudFormation client, Amazon.CloudFormation.Model.DescribeStackEventsRequest request)
 {
     Utils.Common.WriteVerboseEndpointMessage(this, client.Config, "AWS CloudFormation", "DescribeStackEvents");
     try
     {
         #if DESKTOP
         return(client.DescribeStackEvents(request));
         #elif CORECLR
         return(client.DescribeStackEventsAsync(request).GetAwaiter().GetResult());
         #else
                 #error "Unknown build edition"
         #endif
     }
     catch (AmazonServiceException exc)
     {
         var webException = exc.InnerException as System.Net.WebException;
         if (webException != null)
         {
             throw new Exception(Utils.Common.FormatNameResolutionFailureMessage(client.Config, webException.Message), webException);
         }
         throw;
     }
 }
Example #5
0
        private async Task <List <StackEvent> > GetStackEvents(string?lastEventId, string stackId, DeployContext context, CancellationToken cancellationToken)
        {
            cancellationToken.ThrowIfCancellationRequested();

            var request = new DescribeStackEventsRequest {
                StackName = stackId
            };
            var response = await cloudformation.DescribeStackEventsAsync(request, cancellationToken);

            var relevantEvents = new List <StackEvent>();

            foreach (var @event in response.StackEvents)
            {
                if (@event.EventId == lastEventId || @event.Timestamp < context.Timestamp)
                {
                    break;
                }

                relevantEvents.Add(@event);
            }

            relevantEvents.Reverse();
            return(relevantEvents);
        }
Example #6
0
        public static async Task <(Stack Stack, bool Success)> TrackStackUpdateAsync(
            this IAmazonCloudFormation cfnClient,
            string stackName,
            string stackId,
            string mostRecentStackEventId,
            ModuleNameMappings nameMappings = null,
            LogErrorDelegate logError       = null
            )
        {
            var seenEventIds = new HashSet <string>();
            var foundMostRecentStackEvent = (mostRecentStackEventId == null);
            var request = new DescribeStackEventsRequest {
                StackName = stackId ?? stackName
            };
            var eventList        = new List <StackEvent>();
            var ansiLinesPrinted = 0;

            // iterate as long as the stack is being created/updated
            var active  = true;
            var success = false;

            while (active)
            {
                await Task.Delay(TimeSpan.FromSeconds(3));

                // fetch as many events as possible for the current stack
                var events = new List <StackEvent>();
                try {
                    var response = await cfnClient.DescribeStackEventsAsync(request);

                    events.AddRange(response.StackEvents);
                } catch (System.Net.Http.HttpRequestException e) when((e.InnerException is System.Net.Sockets.SocketException) && (e.InnerException.Message == "No such host is known"))
                {
                    // ignore network issues and just try again
                    continue;
                }
                events.Reverse();

                // skip any events that preceded the most recent event before the stack update operation
                while (!foundMostRecentStackEvent && events.Any())
                {
                    var evt = events.First();
                    if (evt.EventId == mostRecentStackEventId)
                    {
                        foundMostRecentStackEvent = true;
                    }
                    seenEventIds.Add(evt.EventId);
                    events.RemoveAt(0);
                }
                if (!foundMostRecentStackEvent)
                {
                    throw new ApplicationException($"unable to find starting event for stack: {stackName}");
                }

                // report only on new events
                foreach (var evt in events.Where(evt => !seenEventIds.Contains(evt.EventId)))
                {
                    UpdateEvent(evt);
                    if (!seenEventIds.Add(evt.EventId))
                    {
                        // we found an event we already saw in the past, no point in looking at more events
                        break;
                    }
                    if (IsFinalStackEvent(evt) && (evt.LogicalResourceId == stackName))
                    {
                        // event signals stack creation/update completion; time to stop
                        active  = false;
                        success = IsSuccessfulFinalStackEvent(evt);
                        break;
                    }
                }
                RenderEvents();
            }
            if (!success)
            {
                return(Stack : null, Success : false);
            }

            // describe stack and report any output values
            var description = await cfnClient.DescribeStacksAsync(new DescribeStacksRequest {
                StackName = stackName
            });

            return(Stack : description.Stacks.FirstOrDefault(), Success : success);

            // local function
            string TranslateLogicalIdToFullName(string logicalId)
            {
                var fullName = logicalId;

                nameMappings?.ResourceNameMappings?.TryGetValue(logicalId, out fullName);
                return(fullName ?? logicalId);
            }

            string TranslateResourceTypeToFullName(string awsType)
            {
                var fullName = awsType;

                nameMappings?.TypeNameMappings?.TryGetValue(awsType, out fullName);
                return(fullName ?? awsType);
            }

            void RenderEvents()
            {
                if (Settings.UseAnsiConsole)
                {
                    if (ansiLinesPrinted > 0)
                    {
                        Console.Write(AnsiTerminal.MoveLineUp(ansiLinesPrinted));
                    }
                    var maxResourceStatusLength   = eventList.Any() ? eventList.Max(evt => evt.ResourceStatus.ToString().Length) : 0;
                    var maxResourceTypeNameLength = eventList.Any() ? eventList.Max(evt => TranslateResourceTypeToFullName(evt.ResourceType).Length) : 0;
                    foreach (var evt in eventList)
                    {
                        var resourceStatus = evt.ResourceStatus.ToString();
                        var resourceType   = TranslateResourceTypeToFullName(evt.ResourceType);
                        if (_ansiStatusColorCodes.TryGetValue(evt.ResourceStatus, out var ansiColor))
                        {
                            // print resource status
                            Console.Write(ansiColor);
                            Console.Write(resourceStatus);
                            Console.Write(AnsiTerminal.Reset);
                            Console.Write("".PadRight(maxResourceStatusLength - resourceStatus.Length + 4));

                            // print resource type
                            Console.Write(resourceType);
                            Console.Write("".PadRight(maxResourceTypeNameLength - resourceType.Length + 4));

                            // print resource name
                            Console.Write(TranslateLogicalIdToFullName(evt.LogicalResourceId));

                            // print status reason
                            if ((logError == null) && (evt.ResourceStatusReason != null))
                            {
                                Console.Write($" ({evt.ResourceStatusReason})");
                            }
                        }
                        else
                        {
                            Console.Write($"{resourceStatus}    {resourceType}    {TranslateLogicalIdToFullName(evt.LogicalResourceId)}{(evt.ResourceStatusReason != null ? $" ({evt.ResourceStatusReason})" : "")}");
                        }
                        Console.Write(AnsiTerminal.ClearEndOfLine);
                        Console.WriteLine();
                    }
                    ansiLinesPrinted = eventList.Count;
                }
            }

            void UpdateEvent(StackEvent evt)
            {
                if (Settings.UseAnsiConsole)
                {
                    var index = eventList.FindIndex(e => e.LogicalResourceId == evt.LogicalResourceId);
                    if (index < 0)
                    {
                        eventList.Add(evt);
                    }
                    else
                    {
                        eventList[index] = evt;
                    }
                }
                else
                {
                    Console.WriteLine($"{evt.ResourceStatus,-35} {TranslateResourceTypeToFullName(evt.ResourceType),-55} {TranslateLogicalIdToFullName(evt.LogicalResourceId)}{(evt.ResourceStatusReason != null ? $" ({evt.ResourceStatusReason})" : "")}");
                }

                // capture failed operation as an error
                switch (evt.ResourceStatus)
                {
                case "CREATE_FAILED":
                case "ROLLBACK_FAILED":
                case "UPDATE_FAILED":
                case "DELETE_FAILED":
                case "UPDATE_ROLLBACK_FAILED":
                case "UPDATE_ROLLBACK_IN_PROGRESS":
                    if (evt.ResourceStatusReason != "Resource creation cancelled")
                    {
                        logError?.Invoke($"{evt.ResourceStatus} {TranslateLogicalIdToFullName(evt.LogicalResourceId)} [{TranslateResourceTypeToFullName(evt.ResourceType)}]: {evt.ResourceStatusReason}", /*Exception*/ null);
                    }
                    break;
                }
            }
        }