Skip to content

Commit

Permalink
[Wait-And-Retry] Decorator output standardization (#16603)
Browse files Browse the repository at this point in the history
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
Azure/bicep-reps#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.
#1013 for reference.

Previous PRs
#16167
#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 <[email protected]>
  • Loading branch information
subha-sa and Subha Sambandamurthy authored Mar 11, 2025
1 parent 67d80a3 commit c306107
Show file tree
Hide file tree
Showing 5 changed files with 35 additions and 48 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -54,18 +54,18 @@ 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())
{
diagnostics.ExcludingLinterDiagnostics().Should().BeEmpty();

template.Should().NotBeNull()
.And.HaveValueAtPath("$.resources[0].options.retryOn", retryOnJObject);
.And.HaveValueAtPath("$.resources[0].@options.retryOn", retryOnJObject);
}
}

Expand Down Expand Up @@ -214,18 +214,18 @@ 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())
{
diagnostics.ExcludingLinterDiagnostics().Should().BeEmpty();

template.Should().NotBeNull()
.And.HaveValueAtPath("$.resources[0].options.retryOn", retryOnJObject);
.And.HaveValueAtPath("$.resources[0].@options.retryOn", retryOnJObject);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,18 +35,18 @@ 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())
{
diagnostics.ExcludingLinterDiagnostics().Should().BeEmpty();

template.Should().NotBeNull()
.And.HaveValueAtPath("$.resources[0].options.waitUntil", waitUntilObject);
.And.HaveValueAtPath("$.resources[0].@options.waitUntil", waitUntilObject);
}
}

Expand Down Expand Up @@ -183,18 +183,18 @@ 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())
{
diagnostics.ExcludingLinterDiagnostics().Should().BeEmpty();

template.Should().NotBeNull()
.And.HaveValueAtPath("$.resources[0].options.waitUntil", waitUntilObject);
.And.HaveValueAtPath("$.resources[0].@options.waitUntil", waitUntilObject);
}
}

Expand Down
16 changes: 11 additions & 5 deletions src/Bicep.Core/Emit/TemplateWriter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1259,21 +1259,27 @@ private void EmitResource(ExpressionEmitter emitter, ImmutableArray<ExtensionExp
// Emit the options property
if (resource.RetryOn is not null || resource.WaitUntil is not null)
{
emitter.EmitObjectProperty("options", () =>
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);
}
});
}

Expand Down
4 changes: 2 additions & 2 deletions src/Bicep.Core/Intermediate/Expression.cs
Original file line number Diff line number Diff line change
Expand Up @@ -493,8 +493,8 @@ public record DeclaredResourceExpression(
Expression Body,
ImmutableArray<ResourceDependencyExpression> 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)
Expand Down
31 changes: 6 additions & 25 deletions src/Bicep.Core/Semantics/Namespaces/SystemNamespaceType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2036,21 +2036,11 @@ private static Expression WaitUntilEvaluator(FunctionCallExpression functionCall
{
if (decorated is DeclaredResourceExpression declaredResourceExpression)
{
var waitUntilProperties = ImmutableArray.Create<ObjectPropertyExpression>(
new
(
null,
new StringLiteralExpression(null, "expression"),
functionCall.Parameters[0]
),
new
(
null,
new StringLiteralExpression(null, "maxWaitTime"),
var waitUntilParameters = ImmutableArray.Create<Expression>(
functionCall.Parameters[0],
functionCall.Parameters[1]
)
);
return declaredResourceExpression with { WaitUntil = new ObjectExpression(null, waitUntilProperties) };
return declaredResourceExpression with { WaitUntil = new ArrayExpression(null, waitUntilParameters)};
}

return decorated;
Expand All @@ -2060,26 +2050,17 @@ private static Expression RetryOnEvaluator(FunctionCallExpression functionCall,
{
if (decorated is DeclaredResourceExpression declaredResourceExpression)
{
var retryOnProperties = new List<ObjectPropertyExpression>
var retryOnParameters = new List<Expression>
{
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;
}
Expand Down

0 comments on commit c306107

Please sign in to comment.