From c3061078502600c484c9c277bfd7958d13429fa4 Mon Sep 17 00:00:00 2001 From: subha-sa <116541423+subha-sa@users.noreply.github.com> Date: Tue, 11 Mar 2025 15:19:19 -0400 Subject: [PATCH] [Wait-And-Retry] Decorator output standardization (#16603) This is an incremental PR that introduces the WaitUntil and RetryOn decorators to the Bicep language. The PR introduces changes on WaitUntil and RetryOn decorators where the output of the decorators is standardized based on this PR https://github.com/Azure/bicep-reps/pull/13 Context: Users are facing issues with certain RPs (example SRP), where due to latency in replication or RP itself responded with success even before the resource is ready for use. In such a case, dependent resource deployments fail, because the resource isn't available according to ARM or some properties of the resource isn't available like list keys for example. https://github.com/Azure/bicep/issues/1013 for reference. Previous PRs https://github.com/Azure/bicep/pull/16167 https://github.com/Azure/bicep/pull/16342 ###### Microsoft Reviewers: [Open in CodeFlow](https://microsoft.github.io/open-pr/?codeflow=https://github.com/Azure/bicep/pull/16603) --------- Co-authored-by: Subha Sambandamurthy --- .../Decorators/RetryOnDecoratorTests.cs | 16 +++++----- .../Decorators/WaitUntilDecoratorTests.cs | 16 +++++----- src/Bicep.Core/Emit/TemplateWriter.cs | 16 +++++++--- src/Bicep.Core/Intermediate/Expression.cs | 4 +-- .../Namespaces/SystemNamespaceType.cs | 31 ++++--------------- 5 files changed, 35 insertions(+), 48 deletions(-) diff --git a/src/Bicep.Core.IntegrationTests/Decorators/RetryOnDecoratorTests.cs b/src/Bicep.Core.IntegrationTests/Decorators/RetryOnDecoratorTests.cs index a30bef65203..47b957d2b00 100644 --- a/src/Bicep.Core.IntegrationTests/Decorators/RetryOnDecoratorTests.cs +++ b/src/Bicep.Core.IntegrationTests/Decorators/RetryOnDecoratorTests.cs @@ -54,10 +54,10 @@ public void RetryOnDecorator_ValidScenario() } "); - var retryOnJObject = new JObject + var retryOnJObject = new JArray { - ["exceptionCodes"] = new JArray("ResourceNotFound", "ServerError"), - ["retryCount"] = 1 + new JArray("ResourceNotFound", "ServerError"), + 1 }; using (new AssertionScope()) @@ -65,7 +65,7 @@ public void RetryOnDecorator_ValidScenario() diagnostics.ExcludingLinterDiagnostics().Should().BeEmpty(); template.Should().NotBeNull() - .And.HaveValueAtPath("$.resources[0].options.retryOn", retryOnJObject); + .And.HaveValueAtPath("$.resources[0].@options.retryOn", retryOnJObject); } } @@ -214,10 +214,10 @@ public void RetryOnDecoratorWithCollections_ValidScenario() }] "); - var retryOnJObject = new JObject + var retryOnJObject = new JArray { - ["exceptionCodes"] = new JArray("ResourceNotFound", "ServerError"), - ["retryCount"] = 1 + new JArray("ResourceNotFound", "ServerError"), + 1 }; using (new AssertionScope()) @@ -225,7 +225,7 @@ public void RetryOnDecoratorWithCollections_ValidScenario() diagnostics.ExcludingLinterDiagnostics().Should().BeEmpty(); template.Should().NotBeNull() - .And.HaveValueAtPath("$.resources[0].options.retryOn", retryOnJObject); + .And.HaveValueAtPath("$.resources[0].@options.retryOn", retryOnJObject); } } diff --git a/src/Bicep.Core.IntegrationTests/Decorators/WaitUntilDecoratorTests.cs b/src/Bicep.Core.IntegrationTests/Decorators/WaitUntilDecoratorTests.cs index 496cde33f2b..30a005921f1 100644 --- a/src/Bicep.Core.IntegrationTests/Decorators/WaitUntilDecoratorTests.cs +++ b/src/Bicep.Core.IntegrationTests/Decorators/WaitUntilDecoratorTests.cs @@ -35,10 +35,10 @@ public void WaitUntilDecorator_ValidScenario(string input, string output) var (template, diagnostics, _) = CompilationHelper.Compile(services, fileContent); - var waitUntilObject = new JObject + var waitUntilObject = new JArray { - ["expression"] = output, - ["maxWaitTime"] = "PT20S" + output, + "PT20S" }; using (new AssertionScope()) @@ -46,7 +46,7 @@ public void WaitUntilDecorator_ValidScenario(string input, string output) diagnostics.ExcludingLinterDiagnostics().Should().BeEmpty(); template.Should().NotBeNull() - .And.HaveValueAtPath("$.resources[0].options.waitUntil", waitUntilObject); + .And.HaveValueAtPath("$.resources[0].@options.waitUntil", waitUntilObject); } } @@ -183,10 +183,10 @@ public void WaitUntilDecoratorWithCollection_ValidScenario(string input, string var (template, diagnostics, _) = CompilationHelper.Compile(services, fileContent); - var waitUntilObject = new JObject + var waitUntilObject = new JArray { - ["expression"] = output, - ["maxWaitTime"] = "PT20S" + output, + "PT20S" }; using (new AssertionScope()) @@ -194,7 +194,7 @@ public void WaitUntilDecoratorWithCollection_ValidScenario(string input, string diagnostics.ExcludingLinterDiagnostics().Should().BeEmpty(); template.Should().NotBeNull() - .And.HaveValueAtPath("$.resources[0].options.waitUntil", waitUntilObject); + .And.HaveValueAtPath("$.resources[0].@options.waitUntil", waitUntilObject); } } diff --git a/src/Bicep.Core/Emit/TemplateWriter.cs b/src/Bicep.Core/Emit/TemplateWriter.cs index 99a8176b0c2..a67b57e1a92 100644 --- a/src/Bicep.Core/Emit/TemplateWriter.cs +++ b/src/Bicep.Core/Emit/TemplateWriter.cs @@ -1259,21 +1259,27 @@ private void EmitResource(ExpressionEmitter emitter, ImmutableArray + emitter.EmitObjectProperty("@options", () => { if (resource.RetryOn is not null) { - emitter.EmitObjectProperty("retryOn", () => + emitter.EmitArrayProperty("retryOn", () => { - emitter.EmitObjectProperties(resource.RetryOn); + foreach (var item in resource.RetryOn.Items) + { + emitter.EmitExpression(item); + } }); } if (resource.WaitUntil is not null) { - emitter.EmitObjectProperty("waitUntil", () => + emitter.EmitArrayProperty("waitUntil", () => { - emitter.EmitObjectProperties(resource.WaitUntil); + foreach (var item in resource.WaitUntil.Items) + { + emitter.EmitExpression(item); + } }); } diff --git a/src/Bicep.Core/Intermediate/Expression.cs b/src/Bicep.Core/Intermediate/Expression.cs index 335408eb602..eefdc68d583 100644 --- a/src/Bicep.Core/Intermediate/Expression.cs +++ b/src/Bicep.Core/Intermediate/Expression.cs @@ -493,8 +493,8 @@ public record DeclaredResourceExpression( Expression Body, ImmutableArray DependsOn, Expression? Description = null, - ObjectExpression? RetryOn = null, - ObjectExpression? WaitUntil = null + ArrayExpression? RetryOn = null, + ArrayExpression? WaitUntil = null ) : DescribableExpression(SourceSyntax, Description) { public override void Accept(IExpressionVisitor visitor) diff --git a/src/Bicep.Core/Semantics/Namespaces/SystemNamespaceType.cs b/src/Bicep.Core/Semantics/Namespaces/SystemNamespaceType.cs index 2f5760835ba..4d104924649 100644 --- a/src/Bicep.Core/Semantics/Namespaces/SystemNamespaceType.cs +++ b/src/Bicep.Core/Semantics/Namespaces/SystemNamespaceType.cs @@ -2036,21 +2036,11 @@ private static Expression WaitUntilEvaluator(FunctionCallExpression functionCall { if (decorated is DeclaredResourceExpression declaredResourceExpression) { - var waitUntilProperties = ImmutableArray.Create( - new - ( - null, - new StringLiteralExpression(null, "expression"), - functionCall.Parameters[0] - ), - new - ( - null, - new StringLiteralExpression(null, "maxWaitTime"), + var waitUntilParameters = ImmutableArray.Create( + functionCall.Parameters[0], functionCall.Parameters[1] - ) ); - return declaredResourceExpression with { WaitUntil = new ObjectExpression(null, waitUntilProperties) }; + return declaredResourceExpression with { WaitUntil = new ArrayExpression(null, waitUntilParameters)}; } return decorated; @@ -2060,26 +2050,17 @@ private static Expression RetryOnEvaluator(FunctionCallExpression functionCall, { if (decorated is DeclaredResourceExpression declaredResourceExpression) { - var retryOnProperties = new List + var retryOnParameters = new List { - new - ( - null, - new StringLiteralExpression(null, "exceptionCodes"), functionCall.Parameters[0] - ) }; if (functionCall.Parameters.Length > 1) { - retryOnProperties.Add( - new( - null, - new StringLiteralExpression(null, "retryCount"), + retryOnParameters.Add( functionCall.Parameters[1] - ) ); } - return declaredResourceExpression with { RetryOn = new ObjectExpression(null, [.. retryOnProperties]) }; + return declaredResourceExpression with { RetryOn = new ArrayExpression(null, [.. retryOnParameters]) }; } return decorated; }