private async Task <bool> WaitForChangeSetBeingAvailableAsync(string changeSetId, bool updated) { try { var request = new DescribeChangeSetRequest { ChangeSetName = changeSetId }; this.Logger.WriteLine($"... Waiting for change set to be reviewed"); DescribeChangeSetResponse response; do { Thread.Sleep(POLLING_PERIOD); response = await this.CloudFormationClient.DescribeChangeSetAsync(request); } while (response.Status == ChangeSetStatus.CREATE_IN_PROGRESS || response.Status == ChangeSetStatus.CREATE_PENDING); if (response.Status == ChangeSetStatus.FAILED) { this.Logger.WriteLine($"Failed to create CloudFormation change set: {response.StatusReason}"); return(false); } return(true); } catch (Exception e) { throw new LambdaToolsException($"Error getting status of change set: {e.Message}", LambdaToolsException.ErrorCode.CloudFormationDescribeChangeSet, e); } }
internal DescribeChangeSetResponse DescribeChangeSet(DescribeChangeSetRequest request) { var marshaller = new DescribeChangeSetRequestMarshaller(); var unmarshaller = DescribeChangeSetResponseUnmarshaller.Instance; return(Invoke <DescribeChangeSetRequest, DescribeChangeSetResponse>(request, marshaller, unmarshaller)); }
private async Task WaitForChangeSetCreate(string stackId, DeployContext context, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); Console.WriteLine("Waiting for changeset creation to complete..."); var status = ChangeSetStatus.CREATE_PENDING; var reason = string.Empty; while (status != ChangeSetStatus.CREATE_COMPLETE) { cancellationToken.ThrowIfCancellationRequested(); if (status == ChangeSetStatus.FAILED) { throw new Exception($"Change set {context.ChangeSetName} failed: {reason}"); } await Task.Delay(1000, cancellationToken); var request = new DescribeChangeSetRequest { ChangeSetName = context.ChangeSetName, StackName = stackId }; var response = await cloudformation.DescribeChangeSetAsync(request, cancellationToken); status = response.Status; reason = response.StatusReason; } }
/// <summary> /// Initiates the asynchronous execution of the DescribeChangeSet operation. /// </summary> /// /// <param name="request">Container for the necessary parameters to execute the DescribeChangeSet operation.</param> /// <param name="cancellationToken"> /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. /// </param> /// <returns>The task object representing the asynchronous operation.</returns> public Task <DescribeChangeSetResponse> DescribeChangeSetAsync(DescribeChangeSetRequest request, System.Threading.CancellationToken cancellationToken = default(CancellationToken)) { var marshaller = new DescribeChangeSetRequestMarshaller(); var unmarshaller = DescribeChangeSetResponseUnmarshaller.Instance; return(InvokeAsync <DescribeChangeSetRequest, DescribeChangeSetResponse>(request, marshaller, unmarshaller, cancellationToken)); }
/// <summary> /// Provides information about a given change set. /// </summary> /// <param name="request">Container for the necessary parameters to execute the DescribeChangeSet service method.</param> /// <param name="cancellationToken"> /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. /// </param> /// /// <returns>The response from the DescribeChangeSet service method, as returned by MarketplaceCatalog.</returns> /// <exception cref="Amazon.MarketplaceCatalog.Model.AccessDeniedException"> /// Access is denied. /// </exception> /// <exception cref="Amazon.MarketplaceCatalog.Model.InternalServiceException"> /// There was an internal service exception. /// </exception> /// <exception cref="Amazon.MarketplaceCatalog.Model.ResourceNotFoundException"> /// The specified resource wasn't found. /// </exception> /// <exception cref="Amazon.MarketplaceCatalog.Model.ThrottlingException"> /// Too many requests. /// </exception> /// <exception cref="Amazon.MarketplaceCatalog.Model.ValidationException"> /// An error occurred during validation. /// </exception> /// <seealso href="http://docs.aws.amazon.com/goto/WebAPI/marketplace-catalog-2018-09-17/DescribeChangeSet">REST API Reference for DescribeChangeSet Operation</seealso> public virtual Task <DescribeChangeSetResponse> DescribeChangeSetAsync(DescribeChangeSetRequest request, System.Threading.CancellationToken cancellationToken = default(CancellationToken)) { var options = new InvokeOptions(); options.RequestMarshaller = DescribeChangeSetRequestMarshaller.Instance; options.ResponseUnmarshaller = DescribeChangeSetResponseUnmarshaller.Instance; return(InvokeAsync <DescribeChangeSetResponse>(request, options, cancellationToken)); }
/// <summary> /// Provides information about a given change set. /// </summary> /// <param name="request">Container for the necessary parameters to execute the DescribeChangeSet service method.</param> /// /// <returns>The response from the DescribeChangeSet service method, as returned by MarketplaceCatalog.</returns> /// <exception cref="Amazon.MarketplaceCatalog.Model.AccessDeniedException"> /// Access is denied. /// </exception> /// <exception cref="Amazon.MarketplaceCatalog.Model.InternalServiceException"> /// There was an internal service exception. /// </exception> /// <exception cref="Amazon.MarketplaceCatalog.Model.ResourceNotFoundException"> /// The specified resource wasn't found. /// </exception> /// <exception cref="Amazon.MarketplaceCatalog.Model.ThrottlingException"> /// Too many requests. /// </exception> /// <exception cref="Amazon.MarketplaceCatalog.Model.ValidationException"> /// An error occurred during validation. /// </exception> /// <seealso href="http://docs.aws.amazon.com/goto/WebAPI/marketplace-catalog-2018-09-17/DescribeChangeSet">REST API Reference for DescribeChangeSet Operation</seealso> public virtual DescribeChangeSetResponse DescribeChangeSet(DescribeChangeSetRequest request) { var options = new InvokeOptions(); options.RequestMarshaller = DescribeChangeSetRequestMarshaller.Instance; options.ResponseUnmarshaller = DescribeChangeSetResponseUnmarshaller.Instance; return(Invoke <DescribeChangeSetResponse>(request, options)); }
/// <summary> /// Initiates the asynchronous execution of the DescribeChangeSet operation. /// </summary> /// /// <param name="request">Container for the necessary parameters to execute the DescribeChangeSet operation on AmazonMarketplaceCatalogClient.</param> /// <param name="callback">An AsyncCallback delegate that is invoked when the operation completes.</param> /// <param name="state">A user-defined state object that is passed to the callback procedure. Retrieve this object from within the callback /// procedure using the AsyncState property.</param> /// /// <returns>An IAsyncResult that can be used to poll or wait for results, or both; this value is also needed when invoking EndDescribeChangeSet /// operation.</returns> /// <seealso href="http://docs.aws.amazon.com/goto/WebAPI/marketplace-catalog-2018-09-17/DescribeChangeSet">REST API Reference for DescribeChangeSet Operation</seealso> public virtual IAsyncResult BeginDescribeChangeSet(DescribeChangeSetRequest request, AsyncCallback callback, object state) { var options = new InvokeOptions(); options.RequestMarshaller = DescribeChangeSetRequestMarshaller.Instance; options.ResponseUnmarshaller = DescribeChangeSetResponseUnmarshaller.Instance; return(BeginInvoke(request, options, callback, state)); }
private async Task <List <Change> > WaitForChangeSetAsync(string changeSetId) { // wait until change-set if available var changeSetRequest = new DescribeChangeSetRequest { ChangeSetName = changeSetId }; var changes = new List <Change>(); while (true) { await Task.Delay(TimeSpan.FromSeconds(3)); var changeSetResponse = await Settings.CfnClient.DescribeChangeSetAsync(changeSetRequest); if (changeSetResponse.Status == ChangeSetStatus.CREATE_PENDING) { // wait until the change-set is CREATE_COMPLETE continue; } if (changeSetResponse.Status == ChangeSetStatus.CREATE_IN_PROGRESS) { // wait until the change-set is CREATE_COMPLETE continue; } if (changeSetResponse.Status == ChangeSetStatus.CREATE_COMPLETE) { changes.AddRange(changeSetResponse.Changes); if (changeSetResponse.NextToken != null) { changeSetRequest.NextToken = changeSetResponse.NextToken; continue; } return(changes); } if (changeSetResponse.Status == ChangeSetStatus.FAILED) { if (changeSetResponse.StatusReason.StartsWith("The submitted information didn't contain changes.", StringComparison.Ordinal)) { return(new List <Change>()); } LogError($"change-set failed: {changeSetResponse.StatusReason}"); return(null); } LogError($"unexpected change-set status: {changeSetResponse.ExecutionStatus}"); return(null); } }
private static async Task <DeployStackResult> WaitForChangeSetBeingAvailableAsync(ILogger log, IAmazonCloudFormation cloudformation, string changeSetId) { try { var request = new DescribeChangeSetRequest { ChangeSetName = changeSetId }; log.Information("... Waiting for change set to be reviewed"); DescribeChangeSetResponse response; do { await Task.Delay(POLLING_PERIOD).ConfigureAwait(false); response = await cloudformation.DescribeChangeSetAsync(request).ConfigureAwait(false); } while (response.Status == ChangeSetStatus.CREATE_IN_PROGRESS || response.Status == ChangeSetStatus.CREATE_PENDING); if (response.Status == ChangeSetStatus.FAILED) { if (response.StatusReason == "The submitted information didn't contain changes. Submit different information to create a change set.") { log.Information("Cloudformation update not needed."); return(DeployStackResult.NoChanges); } log.Information($"Failed to create CloudFormation change set: {response.StatusReason}"); return(DeployStackResult.Failed); } return(DeployStackResult.Ok); } catch (Exception e) { log.Error(e, "Error getting status of change set"); throw; } }
/// <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); } } }