Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add library support for local deployments #14237

Merged
merged 2 commits into from
Jun 5, 2024
Merged

Conversation

anthony-c-martin
Copy link
Member

@anthony-c-martin anthony-c-martin commented Jun 4, 2024

Add Bicep.Local.Deploy library containing "local deployment" logic.

The full spec is available here - unfortunately only available to Microsoft employees currently.

Microsoft Reviewers: Open in CodeFlow

Copy link
Contributor

github-actions bot commented Jun 4, 2024

Test this change out locally with the following install scripts (Action run 9409014946)

VSCode
  • Mac/Linux
    bash <(curl -Ls https://aka.ms/bicep/nightly-vsix.sh) --run-id 9409014946
  • Windows
    iex "& { $(irm https://aka.ms/bicep/nightly-vsix.ps1) } -RunId 9409014946"
Azure CLI
  • Mac/Linux
    bash <(curl -Ls https://aka.ms/bicep/nightly-cli.sh) --run-id 9409014946
  • Windows
    iex "& { $(irm https://aka.ms/bicep/nightly-cli.ps1) } -RunId 9409014946"

Copy link
Contributor

github-actions bot commented Jun 4, 2024

Dotnet Test Results

    35 files   -     62      35 suites   - 62   11m 58s ⏱️ - 21m 10s
10 631 tests  -    265  10 630 ✅  -    265  1 💤 ±0  0 ❌ ±0 
12 833 runs   - 25 635  12 832 ✅  - 25 633  1 💤  - 2  0 ❌ ±0 

Results for commit bc33931. ± Comparison against base commit 46a412b.

♻️ This comment has been updated with latest results.

@anthony-c-martin anthony-c-martin force-pushed the ant/local_libs branch 2 times, most recently from e52e251 to 9023c38 Compare June 4, 2024 16:29
davidcho23
davidcho23 previously approved these changes Jun 4, 2024
@anthony-c-martin anthony-c-martin enabled auto-merge (squash) June 5, 2024 13:53
@davidcho23 davidcho23 self-requested a review June 5, 2024 14:33
@davidcho23 davidcho23 dismissed their stale review June 5, 2024 14:34

Mistake

@anthony-c-martin anthony-c-martin disabled auto-merge June 5, 2024 15:52
@@ -48,7 +48,7 @@ public static GrpcChannel CreateChannel(string socketPath)
};

// The URL is not used, but it must be a valid URI.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is this URL needed for then?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's parsed initially by the GrpcChannel API (which throws if it's invalid), but the actual socket connection logic is overridden by setting the HttpHandler

}
}

private async Task SaveDeployment(DeploymentPlan deploymentPlan)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just to clarify, SaveDeployment means that the deployment is kept track of locally, correct?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm using in-memory data providers, so when we "save" an entity, we are replacing it in memory. This is only available while the process is running - it is not persisted afterwards.


public string[] PreviewFunctionalityExternalPermittedTenants { get; set; } = new string[] { };

public string[] PreviewFunctionalityPublicFeatures { get; set; } = new string[] {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm probably a little confused but where does this get checked compared to the ServiceDirectory.json? Or is it not at all?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not so relevant for this particular feature, but I'm forced to define a value for it.


public static async Task<Result> Deploy(LocalExtensibilityHandler extensibilityHandler, string templateString, string parametersString, CancellationToken cancellationToken)
{
var services = new ServiceCollection()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I assume this is a short-cut, or de we want to register and build the service provider every time .Deploy is invoked?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's a short-cut for now - https://msazure.visualstudio.com/One/_workitems/edit/28299061 would be the proper implementation for it.

}
finally
{
await dispatcher.StopAsync();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where does distacher.Start() happens?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great question, I think it must be happening inside the deployment engine. It's important to stop regardless, so that we don't leak jobs when running in the language server.

return new(
deploymentEngine.CreateDeploymentDefinition(entity, requestContext.ApiVersion),
operationEntities
.Where(operation => operation.TargetResource is not null)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When is TargetResource null?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The deployment last job creates a single operation for "processing outputs" - this filters it down to just resource operations.

{
await Task.Delay(500, cancellationToken);

entity = await dataProvider.FindDeployment(context.SubscriptionId, context.ResourceGroupName, context.DeploymentName);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This part is interesting, in ARM, we go off the async header and poll for the status, here we don't have such option, something we could explore at some point is standing up a local deployments server, so we can reuse the same deploymentsWorkflow rather than implementing a local deployment engine.

Also, a TODO to add here is to gracefully handle an exception thrown by volatile datastore also to not loop indefinitely or until the cancellation is requested

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be nice to avoid polling here - it's a consequence of low visibility into ResourceStack's job processing internals. I've cut the polling down to every 20ms to make it more responsive as it should be very low-cost.

var parameters = new JObject {
["$schema"] = "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
["contentVersion"] = "1.0.0.0",
["parameters"] = request.Resource.Properties["parameters"],
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TODO, account for parametersLink and templateLink?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good callout - something to think through.

{
switch (request.Resource.Type)
{
case "Microsoft.Resources/deployments": {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If this is Az extensibility provider, can this type be any azure resource type?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not currently - it's out of scope for the first phase.


private async Task<ExtensibilityOperationResponse> CallProvider(string method, IExtensibilityProvider provider, ExtensibilityOperationRequest request, CancellationToken cancellationToken)
{
return method switch
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TODO: move to enum

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good callout!

@anthony-c-martin anthony-c-martin merged commit f22ddf9 into main Jun 5, 2024
49 of 50 checks passed
@anthony-c-martin anthony-c-martin deleted the ant/local_libs branch June 5, 2024 19:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants