private static async Task WaitForChangeSetStatus(string changeSetId, string stackName, ChangeSetStatus expectedStatus, Credentials credentials)
        {
            DescribeChangeSetResponse response = await DescribeChangeSetStatus(changeSetId, stackName, credentials);

            while (response.Status != expectedStatus)
            {
                if (response.Status == ChangeSetStatus.FAILED)
                {
                    if (response.StatusReason == "No updates are to be performed.")
                    {
                        Console.WriteLine(response.StatusReason);

                        throw new EmptyChangeException();
                    }

                    throw new System.InvalidOperationException($"Unexpected status {response.Status} - {response.StatusReason}");
                }

                Console.WriteLine($"{response.Status}...");

                await Task.Delay(TimeSpan.FromSeconds(15));

                response = await DescribeChangeSetStatus(changeSetId, stackName, credentials);
            }

            Console.WriteLine($"Change set {changeSetId} for stack {stackName} successfully reached {response.Status} status.");
        }
Exemplo n.º 2
0
        /// <summary>
        /// Unmarshaller the response from the service to the response class.
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        public override AmazonWebServiceResponse Unmarshall(XmlUnmarshallerContext context)
        {
            DescribeChangeSetResponse response = new DescribeChangeSetResponse();

            context.Read();
            int targetDepth = context.CurrentDepth;

            while (context.ReadAtDepth(targetDepth))
            {
                if (context.IsStartElement)
                {
                    if (context.TestExpression("DescribeChangeSetResult", 2))
                    {
                        UnmarshallResult(context, response);
                        continue;
                    }

                    if (context.TestExpression("ResponseMetadata", 2))
                    {
                        response.ResponseMetadata = ResponseMetadataUnmarshaller.Instance.Unmarshall(context);
                    }
                }
            }

            return(response);
        }
Exemplo n.º 3
0
 /// <inheritdoc />
 public IDictionary <string, int> LogChangeset(DescribeChangeSetResponse changes)
 {
     return(new Dictionary <string, int>());
 }
Exemplo n.º 4
0
        /// <summary>
        /// Unmarshaller the response from the service to the response class.
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        public override AmazonWebServiceResponse Unmarshall(JsonUnmarshallerContext context)
        {
            DescribeChangeSetResponse response = new DescribeChangeSetResponse();

            context.Read();
            int targetDepth = context.CurrentDepth;

            while (context.ReadAtDepth(targetDepth))
            {
                if (context.TestExpression("ChangeSet", targetDepth))
                {
                    var unmarshaller = new ListUnmarshaller <ChangeSummary, ChangeSummaryUnmarshaller>(ChangeSummaryUnmarshaller.Instance);
                    response.ChangeSet = unmarshaller.Unmarshall(context);
                    continue;
                }
                if (context.TestExpression("ChangeSetArn", targetDepth))
                {
                    var unmarshaller = StringUnmarshaller.Instance;
                    response.ChangeSetArn = unmarshaller.Unmarshall(context);
                    continue;
                }
                if (context.TestExpression("ChangeSetId", targetDepth))
                {
                    var unmarshaller = StringUnmarshaller.Instance;
                    response.ChangeSetId = unmarshaller.Unmarshall(context);
                    continue;
                }
                if (context.TestExpression("ChangeSetName", targetDepth))
                {
                    var unmarshaller = StringUnmarshaller.Instance;
                    response.ChangeSetName = unmarshaller.Unmarshall(context);
                    continue;
                }
                if (context.TestExpression("EndTime", targetDepth))
                {
                    var unmarshaller = StringUnmarshaller.Instance;
                    response.EndTime = unmarshaller.Unmarshall(context);
                    continue;
                }
                if (context.TestExpression("FailureDescription", targetDepth))
                {
                    var unmarshaller = StringUnmarshaller.Instance;
                    response.FailureDescription = unmarshaller.Unmarshall(context);
                    continue;
                }
                if (context.TestExpression("StartTime", targetDepth))
                {
                    var unmarshaller = StringUnmarshaller.Instance;
                    response.StartTime = unmarshaller.Unmarshall(context);
                    continue;
                }
                if (context.TestExpression("Status", targetDepth))
                {
                    var unmarshaller = StringUnmarshaller.Instance;
                    response.Status = unmarshaller.Unmarshall(context);
                    continue;
                }
            }

            return(response);
        }
Exemplo n.º 5
0
        /// <summary>
        /// Logs a change set.
        /// The base implementation merely calculates column widths that a concrete implementation can use to render the change set details.
        /// </summary>
        /// <param name="changes">The changes.</param>
        /// <returns>
        /// Map of change column name to max width required.
        /// </returns>
        public override IDictionary <string, int> LogChangeset(DescribeChangeSetResponse changes)
        {
            if (!changes.Changes.Any())
            {
                this.LogInformation("No changes to stack resources. Other changes such as Outputs may be present.");
                return(new Dictionary <string, int>());
            }

            this.changesetDetails.Add(
                new ChangesetDetails
            {
                Changes       = changes.Changes,
                ChangeSetName = changes.ChangeSetName,
                CreationTime  = changes.CreationTime,
                Description   = changes.Description,
                StackName     = changes.StackName
            });

            var columnWidths       = base.LogChangeset(changes);
            var foregroundColorMap = new Dictionary <string, ConsoleColor>
            {
                { "Add", ConsoleColor.Green },
                { "Import", ConsoleColor.Cyan },
                { "Modify", ConsoleColor.Yellow },
                { "Remove", ConsoleColor.Red },
                { "False", ConsoleColor.Green },
                { "True", ConsoleColor.Red },
                { "Conditional", ConsoleColor.Yellow }
            };

            var ui = this.cmdlet.Host.UI;
            var bg = ui.RawUI.BackgroundColor;

            // Adjust column widths for headings
            var       actionColWidth            = Math.Max(columnWidths["Action"], 6);
            var       logicalResourceIdColWidth = Math.Max(columnWidths["LogicalResourceId"], 10);
            var       resourceTypeColWidth      = Math.Max(columnWidths["ResourceType"], 4);
            var       replacementColWidth       = Math.Max(columnWidths["Replacement"], 11);
            const int ScopeColWidth             = 9;

            var changeFormatString =
                $"{{0,-{actionColWidth}}} {{1,-{logicalResourceIdColWidth}}} {{2,-{resourceTypeColWidth}}} {{3,-{replacementColWidth}}} {{4,-{ScopeColWidth}}} {{5}}";

            // Resize window to be wide enough for a reasonable amount of Physical ID per line
            this.ResizeWindow(string.Format(changeFormatString, "x", "x", "x", "x", "x", Padding30).Length);

            var leftIndent = GetLeftMarginForLastColumn(
                actionColWidth,
                logicalResourceIdColWidth,
                resourceTypeColWidth,
                replacementColWidth,
                ScopeColWidth);

            var maxLineLength = ui.RawUI.WindowSize.Width - leftIndent;

            this.LogInformation("Changes to be made...\n");
            this.LogInformation(changeFormatString, "Action", "Logical ID", "Type", "Replacement", "Scope", "Physical ID");
            this.LogInformation(changeFormatString, "------", "----------", "----", "-----------", "-----", "-----------");

            foreach (var r in changes.Changes.Select(change => change.ResourceChange))
            {
                ui.Write(foregroundColorMap[r.Action.Value], bg, r.Action.Value.PadRight(actionColWidth + 1));
                ui.Write(r.LogicalResourceId.PadRight(logicalResourceIdColWidth + 1));
                ui.Write(r.ResourceType.PadRight(resourceTypeColWidth + 1));

                if (r.Replacement == null)
                {
                    ui.Write(string.Empty.PadRight(replacementColWidth + 1));
                }
                else
                {
                    ui.Write(
                        foregroundColorMap[r.Replacement.Value],
                        bg,
                        r.Replacement.Value.PadRight(replacementColWidth + 1));
                }

                if (r.Scope != null && r.Scope.Any())
                {
                    ui.Write(
                        string.Join(",", r.Scope.Select(sc => sc[0].ToString().ToUpperInvariant()))
                        .PadRight(ScopeColWidth + 1));
                }
                else
                {
                    ui.Write(" ".PadRight(ScopeColWidth + 1));
                }

                List <string> lines;

                if (r.PhysicalResourceId == null)
                {
                    ui.WriteLine();
                    continue;
                }

                if (r.PhysicalResourceId.Contains(' '))
                {
                    var charCount = 0;

                    lines = r.PhysicalResourceId.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries)
                            .GroupBy(w => (charCount += w.Length + 1) / (maxLineLength - 1))
                            .Select(g => string.Join(" ", g)).ToList();
                }
                else
                {
                    lines = Regex.Matches(r.PhysicalResourceId, $".{{0,{maxLineLength}}}").Cast <Match>()
                            .Select(x => x.Value).ToList();
                }

                ui.WriteLine(lines.First());

                foreach (var line in lines.Skip(1).Where(l => !string.IsNullOrWhiteSpace(l)))
                {
                    ui.WriteLine(new string(' ', leftIndent) + line);
                }
            }

            this.LogInformation(string.Empty);
            return(columnWidths);
        }
        /// <summary>Updates a stack.</summary>
        /// <param name="confirmationFunc">
        /// A callback that should return <c>true</c> or <c>false</c> as to whether to whether the caller confirms the given change set and therefore to continue with the stack update.
        /// If this parameter is <c>null</c>, then <c>true</c> is assumed.
        /// </param>
        /// <exception cref="StackOperationException">Change set creation failed for reasons other than 'no change'</exception>
        /// <returns>Operation result.</returns>
        public async Task <CloudFormationResult> UpdateStackAsync(Func <DescribeChangeSetResponse, bool> confirmationFunc)
        {
            var stack = await this.stackOperations.GetStackAsync(this.stackName);

            // Check stack state first
            var operationalState = await this.stackOperations.GetStackOperationalStateAsync(stack.StackId);

            // ReSharper disable once SwitchStatementMissingSomeEnumCasesNoDefault
            switch (operationalState)
            {
            case StackOperationalState.Deleting:
            case StackOperationalState.DeleteFailed:

                throw new StackOperationException(stack, operationalState);

            case StackOperationalState.Broken:

                this.context.Logger.LogWarning("Stack is in a failed state from previous operation. Update may fail.");
                break;

            case StackOperationalState.Busy:

                if (this.waitForInProgressUpdate)
                {
                    // Track stack until update completes
                    // Wait for previous update to complete
                    // Time from which to start polling events
                    this.lastEventTime = await this.GetMostRecentStackEvent(stack.StackId);

                    this.context.Logger.LogInformation(
                        "Stack {0} is currently being updated by another process",
                        stack.StackName);
                    this.context.Logger.LogInformation("Following its progress while waiting...\n");
                    stack = await this.WaitStackOperationAsync(stack.StackId, false);

                    this.context.Logger.LogInformation(string.Empty);

                    var currentState = await this.stackOperations.GetStackOperationalStateAsync(stack.StackId);

                    if (currentState != StackOperationalState.Ready)
                    {
                        throw new StackOperationException(stack, currentState);
                    }
                }
                else
                {
                    throw new StackOperationException(stack, StackOperationalState.Busy);
                }

                break;
            }

            // If we get here, stack is in Ready state
            var changeSetName     = CreateChangeSetName();
            var resourcesToImport = await this.GetResourcesToImportAsync();

            var changeSetRequest = new CreateChangeSetRequest
            {
                ChangeSetName         = changeSetName,
                ChangeSetType         = resourcesToImport != null ? ChangeSetType.IMPORT : ChangeSetType.UPDATE,
                Parameters            = this.GetStackParametersForUpdate(this.templateResolver, stack),
                Capabilities          = this.capabilities.Select(c => c.Value).ToList(),
                StackName             = stack.StackId,
                ClientToken           = this.clientToken,
                RoleARN               = this.roleArn,
                NotificationARNs      = this.notificationARNs,
                RollbackConfiguration = this.rollbackConfiguration,
                ResourceTypes         = this.resourceType,
                Tags = this.tags,
                ResourcesToImport = resourcesToImport,
                TemplateBody      = this.templateResolver.ArtifactContent,
                TemplateURL       =
                    this.usePreviousTemplate ? null : this.templateResolver.ArtifactUrl,
                UsePreviousTemplate = this.usePreviousTemplate,
                IncludeNestedStacks = this.includeNestedStacks
            };

            this.context.Logger.LogInformation($"Creating changeset {changeSetName} for {this.GetStackNameWithDescription()}");

            if (this.includeNestedStacks)
            {
                this.context.Logger.LogInformation("IncludeNestedChangesets is enabled. This may take some time...");
            }

            var changesetArn = (await this.client.CreateChangeSetAsync(changeSetRequest)).Id;

            var stat = ChangeSetStatus.CREATE_PENDING;
            var describeChangeSetRequest = new DescribeChangeSetRequest {
                ChangeSetName = changesetArn
            };

            DescribeChangeSetResponse describeChangeSetResponse = null;

            while (!(stat == ChangeSetStatus.CREATE_COMPLETE || stat == ChangeSetStatus.FAILED))
            {
                Thread.Sleep(this.waitPollTime / 2);
                describeChangeSetResponse = await this.client.DescribeChangeSetAsync(describeChangeSetRequest);

                stat = describeChangeSetResponse.Status;
            }

            if (stat == ChangeSetStatus.FAILED)
            {
                // ReSharper disable once PossibleNullReferenceException - we will go round the above loop at least once
                var reason = describeChangeSetResponse.StatusReason;

                if (reason.Contains("Access Denied") && this.usePreviousTemplate)
                {
                    // Likely that lifecycle policy has removed the previous template.
                    throw new
                          StackOperationException("Unable to create changeset: It is probable that the template has been explicitly deleted or removed by lifecycle policy on your bucket. Please retry specifying the path to the template file");
                }

                if (!NoChangeMessages.Any(msg => reason.StartsWith(msg)))
                {
                    throw new StackOperationException($"Unable to create changeset: {reason}");
                }

                this.context.Logger.LogInformation("No changes to stack were detected.");

                if (this.deleteNoopChangeSet)
                {
                    await this.client.DeleteChangeSetAsync(
                        new DeleteChangeSetRequest { ChangeSetName = changesetArn });

                    this.context.Logger.LogInformation($"Deleted changeset {changeSetName}");
                }

                return(new CloudFormationResult
                {
                    ChangesetResponse = this.deleteNoopChangeSet ? null : describeChangeSetResponse,
                    StackArn = stack.StackId,
                    StackOperationResult = StackOperationResult.NoChange
                });
            }

            // If we get here, emit details, then apply the changeset.
            // ReSharper disable once PossibleNullReferenceException - we will go round the above loop at least once
            if (this.includeNestedStacks)
            {
                // Base stack
                this.context.Logger.LogInformation($"Root Stack: {this.stackName}");
                this.context.Logger.LogChangeset(describeChangeSetResponse);

                // Walk all nested stacks and emit changes for each
                await EmitNestedStackChangesets(describeChangeSetResponse);
            }
            else
            {
                this.context.Logger.LogChangeset(describeChangeSetResponse);
            }

            if (this.changesetOnly)
            {
                this.context.Logger.LogInformation(
                    // ReSharper disable once PossibleNullReferenceException - 'response' cannot be null. DescribeChangeSetAsync has been called at least once to make it here.
                    $"Changeset {describeChangeSetResponse.ChangeSetName} created for stack {stack.StackName}");
                this.context.Logger.LogInformation("Not updating stack since CreateChangesetOnly = true");
                return(new CloudFormationResult
                {
                    ChangesetResponse = describeChangeSetResponse,
                    StackArn = stack.StackId,
                    StackOperationResult = StackOperationResult.NoChange
                });
            }

            if (confirmationFunc != null)
            {
                // Confirm the changeset before proceeding
                if (!confirmationFunc(describeChangeSetResponse))
                {
                    return(new CloudFormationResult
                    {
                        ChangesetResponse = describeChangeSetResponse,
                        StackArn = stack.StackId,
                        StackOperationResult = StackOperationResult.NoChange
                    });
                }
            }

            // Check nobody else has jumped in before us
            var currentState2 = await this.stackOperations.GetStackOperationalStateAsync(stack.StackId);

            if (currentState2 != StackOperationalState.Ready)
            {
                throw new StackOperationException(stack, currentState2);
            }

            // Time from which to start polling events
            this.lastEventTime = await this.GetMostRecentStackEvent(stack.StackId);

            this.context.Logger.LogInformation($"Updating {this.GetStackNameWithDescription()}\n");

            if (resourcesToImport != null)
            {
                // Have to do this by changeset
                await this.client.ExecuteChangeSetAsync(new ExecuteChangeSetRequest { ChangeSetName = changesetArn });
            }
            else
            {
                await this.client.UpdateStackAsync(await this.GetUpdateRequestWithPolicyFromChangesetRequestAsync(changeSetRequest));
            }

            if (this.followOperation)
            {
                await this.WaitStackOperationAsync(stack.StackId, true);

                return(new CloudFormationResult
                {
                    ChangesetResponse = describeChangeSetResponse,
                    StackArn = stack.StackId,
                    StackOperationResult = StackOperationResult.StackUpdated
                });
            }

            return(new CloudFormationResult
            {
                ChangesetResponse = describeChangeSetResponse,
                StackArn = stack.StackId,
                StackOperationResult = StackOperationResult.StackUpdateInProgress
            });

            async Task EmitNestedStackChangesets(DescribeChangeSetResponse parentChangeSetResponse)
            {
                // Recursively discover nested stacks and emit changesets for each
                foreach (var nested in parentChangeSetResponse.Changes.Where(
                             c => c.ResourceChange.ResourceType == "AWS::CloudFormation::Stack"))
                {
                    // Locate nested stack's changeset. It's parent ID will be set to the ID in parentChangeSetResponse
                    var summary = (await this.client.ListChangeSetsAsync(
                                       new ListChangeSetsRequest
                    {
                        StackName = nested.ResourceChange.PhysicalResourceId
                    })).Summaries.First(
                        s => s.ParentChangeSetId == parentChangeSetResponse.ChangeSetId);

                    var nestedResponse = await this.client.DescribeChangeSetAsync(
                        new DescribeChangeSetRequest
                    {
                        ChangeSetName = summary.ChangeSetName,
                        StackName     = nested.ResourceChange.PhysicalResourceId
                    });

                    this.context.Logger.LogInformation($"Nested Stack: {nested.ResourceChange.LogicalResourceId}");
                    this.context.Logger.LogChangeset(nestedResponse);
                    await EmitNestedStackChangesets(nestedResponse);
                }
            }
        }
Exemplo n.º 7
0
        private static void UnmarshallResult(XmlUnmarshallerContext context, DescribeChangeSetResponse response)
        {
            int originalDepth = context.CurrentDepth;
            int targetDepth   = originalDepth + 1;

            if (context.IsStartOfDocument)
            {
                targetDepth += 2;
            }

            while (context.ReadAtDepth(originalDepth))
            {
                if (context.IsStartElement || context.IsAttribute)
                {
                    if (context.TestExpression("Capabilities/member", targetDepth))
                    {
                        var unmarshaller = StringUnmarshaller.Instance;
                        var item         = unmarshaller.Unmarshall(context);
                        response.Capabilities.Add(item);
                        continue;
                    }
                    if (context.TestExpression("Changes/member", targetDepth))
                    {
                        var unmarshaller = ChangeUnmarshaller.Instance;
                        var item         = unmarshaller.Unmarshall(context);
                        response.Changes.Add(item);
                        continue;
                    }
                    if (context.TestExpression("ChangeSetId", targetDepth))
                    {
                        var unmarshaller = StringUnmarshaller.Instance;
                        response.ChangeSetId = unmarshaller.Unmarshall(context);
                        continue;
                    }
                    if (context.TestExpression("ChangeSetName", targetDepth))
                    {
                        var unmarshaller = StringUnmarshaller.Instance;
                        response.ChangeSetName = unmarshaller.Unmarshall(context);
                        continue;
                    }
                    if (context.TestExpression("CreationTime", targetDepth))
                    {
                        var unmarshaller = DateTimeUnmarshaller.Instance;
                        response.CreationTime = unmarshaller.Unmarshall(context);
                        continue;
                    }
                    if (context.TestExpression("Description", targetDepth))
                    {
                        var unmarshaller = StringUnmarshaller.Instance;
                        response.Description = unmarshaller.Unmarshall(context);
                        continue;
                    }
                    if (context.TestExpression("ExecutionStatus", targetDepth))
                    {
                        var unmarshaller = StringUnmarshaller.Instance;
                        response.ExecutionStatus = unmarshaller.Unmarshall(context);
                        continue;
                    }
                    if (context.TestExpression("IncludeNestedStacks", targetDepth))
                    {
                        var unmarshaller = BoolUnmarshaller.Instance;
                        response.IncludeNestedStacks = unmarshaller.Unmarshall(context);
                        continue;
                    }
                    if (context.TestExpression("NextToken", targetDepth))
                    {
                        var unmarshaller = StringUnmarshaller.Instance;
                        response.NextToken = unmarshaller.Unmarshall(context);
                        continue;
                    }
                    if (context.TestExpression("NotificationARNs/member", targetDepth))
                    {
                        var unmarshaller = StringUnmarshaller.Instance;
                        var item         = unmarshaller.Unmarshall(context);
                        response.NotificationARNs.Add(item);
                        continue;
                    }
                    if (context.TestExpression("Parameters/member", targetDepth))
                    {
                        var unmarshaller = ParameterUnmarshaller.Instance;
                        var item         = unmarshaller.Unmarshall(context);
                        response.Parameters.Add(item);
                        continue;
                    }
                    if (context.TestExpression("ParentChangeSetId", targetDepth))
                    {
                        var unmarshaller = StringUnmarshaller.Instance;
                        response.ParentChangeSetId = unmarshaller.Unmarshall(context);
                        continue;
                    }
                    if (context.TestExpression("RollbackConfiguration", targetDepth))
                    {
                        var unmarshaller = RollbackConfigurationUnmarshaller.Instance;
                        response.RollbackConfiguration = unmarshaller.Unmarshall(context);
                        continue;
                    }
                    if (context.TestExpression("RootChangeSetId", targetDepth))
                    {
                        var unmarshaller = StringUnmarshaller.Instance;
                        response.RootChangeSetId = unmarshaller.Unmarshall(context);
                        continue;
                    }
                    if (context.TestExpression("StackId", targetDepth))
                    {
                        var unmarshaller = StringUnmarshaller.Instance;
                        response.StackId = unmarshaller.Unmarshall(context);
                        continue;
                    }
                    if (context.TestExpression("StackName", targetDepth))
                    {
                        var unmarshaller = StringUnmarshaller.Instance;
                        response.StackName = unmarshaller.Unmarshall(context);
                        continue;
                    }
                    if (context.TestExpression("Status", targetDepth))
                    {
                        var unmarshaller = StringUnmarshaller.Instance;
                        response.Status = unmarshaller.Unmarshall(context);
                        continue;
                    }
                    if (context.TestExpression("StatusReason", targetDepth))
                    {
                        var unmarshaller = StringUnmarshaller.Instance;
                        response.StatusReason = unmarshaller.Unmarshall(context);
                        continue;
                    }
                    if (context.TestExpression("Tags/member", targetDepth))
                    {
                        var unmarshaller = TagUnmarshaller.Instance;
                        var item         = unmarshaller.Unmarshall(context);
                        response.Tags.Add(item);
                        continue;
                    }
                }
            }

            return;
        }
 /// <summary>
 /// Logs a change set.
 /// </summary>
 /// <param name="changes">The changes.</param>
 public override IDictionary <string, int> LogChangeset(DescribeChangeSetResponse changes)
 {
     this.ChangeSets.Add(changes);
     return(base.LogChangeset(changes));
 }
 /// <summary>
 /// Logs a change set.
 /// The base implementation merely calculates column widths that a concrete implementation can use to render the change set details.
 /// </summary>
 /// <param name="changes">The changes.</param>
 /// <returns>
 /// Map of change column name to max width required.
 /// </returns>
 public virtual IDictionary <string, int> LogChangeset(DescribeChangeSetResponse changes)
 {
     return(GetChangesetColumnWidths(changes.Changes));
 }