Skip to content

Commit

Permalink
Add 'localDeploy' experimental feature, base extension nuget library
Browse files Browse the repository at this point in the history
  • Loading branch information
anthony-c-martin committed Jun 3, 2024
1 parent 6dd8bf7 commit c6804d8
Show file tree
Hide file tree
Showing 22 changed files with 776 additions and 0 deletions.
9 changes: 9 additions & 0 deletions Bicep.sln
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tools", "Tools", "{C6CAFEE8
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Bicep.Tools.Benchmark", "src\Bicep.Tools.Benchmark\Bicep.Tools.Benchmark.csproj", "{A4127F6F-A282-47F3-BAFE-6A154F994B4E}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{8F8DCFBC-A0DC-4E40-93C8-B4FB99FBD757}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Bicep.Local.Extension", "src\Bicep.Local.Extension\Bicep.Local.Extension.csproj", "{E84C0368-0D02-4284-A3CB-110B14FA8314}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -169,6 +173,10 @@ Global
{A4127F6F-A282-47F3-BAFE-6A154F994B4E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A4127F6F-A282-47F3-BAFE-6A154F994B4E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A4127F6F-A282-47F3-BAFE-6A154F994B4E}.Release|Any CPU.Build.0 = Release|Any CPU
{E84C0368-0D02-4284-A3CB-110B14FA8314}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E84C0368-0D02-4284-A3CB-110B14FA8314}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E84C0368-0D02-4284-A3CB-110B14FA8314}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E84C0368-0D02-4284-A3CB-110B14FA8314}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand All @@ -194,6 +202,7 @@ Global
{8C2280F2-1E08-4022-B26B-59622DD8B126} = {90126655-7A34-4F5B-A41A-50A468F4632D}
{9F596D8D-5CDB-4830-B73C-26C33C0227D7} = {90126655-7A34-4F5B-A41A-50A468F4632D}
{A4127F6F-A282-47F3-BAFE-6A154F994B4E} = {C6CAFEE8-7779-4F48-BEA1-D59D3CD91A56}
{E84C0368-0D02-4284-A3CB-110B14FA8314} = {8F8DCFBC-A0DC-4E40-93C8-B4FB99FBD757}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {21F77282-91E7-4304-B1EF-FADFA4F39E37}
Expand Down
3 changes: 3 additions & 0 deletions docs/experimental-features.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ Allows Bicep to use a provider model to deploy non-ARM resources. Currently, we
### `legacyFormatter`
Enables code formatting with the legacy formatter. This feature flag is introduced to ensure a safer transition to the v2 formatter that implements a pretty-printing algorithm. It is intended for temporary use and will be phased out soon.

### `localDeploy`
Enables local deployment capability. See [Bicep Local Providers](https://github.com/anthony-c-martin/bicep-local-providers) for more information.

### `optionalModuleNames`
Enabling this feature makes the `name` property in the body of `module` declarations optional. When a `module` omits the `name` property with the feature enabled, the Bicep compiler will automatically generate an expression for the name of the resulting nested deployment in the JSON. If you specify the `name` property, the compiler will use the specified expression in the resulting JSON. For more information, see [Added optional module names as an experimental feature](https://github.com/Azure/bicep/pull/12600).

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ public void GetBuiltInConfiguration_NoParameter_ReturnsBuiltInConfigurationWithA
"dynamicTypeLoading": false,
"providerRegistry": false,
"optionalModuleNames": false,
"localDeploy": false,
"resourceDerivedTypes": false
},
"formatting": {
Expand Down Expand Up @@ -204,6 +205,7 @@ public void GetBuiltInConfiguration_DisableAllAnalyzers_ReturnsBuiltInConfigurat
"dynamicTypeLoading": false,
"providerRegistry": false,
"optionalModuleNames": false,
"localDeploy": false,
"resourceDerivedTypes": false
},
"formatting": {
Expand Down Expand Up @@ -317,6 +319,7 @@ public void GetBuiltInConfiguration_DisableAnalyzers_ReturnsBuiltInConfiguration
"dynamicTypeLoading": false,
"providerRegistry": false,
"optionalModuleNames": false,
"localDeploy": false,
"resourceDerivedTypes": false
},
"formatting": {
Expand Down Expand Up @@ -758,6 +761,7 @@ public void GetConfiguration_ValidCustomConfiguration_OverridesBuiltInConfigurat
"dynamicTypeLoading": false,
"providerRegistry": false,
"optionalModuleNames": false,
"localDeploy": false,
"resourceDerivedTypes": false
},
"formatting": {
Expand Down
3 changes: 3 additions & 0 deletions src/Bicep.Core.UnitTests/Features/FeatureProviderOverrides.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ public record FeatureProviderOverrides(
bool? DynamicTypeLoadingEnabled = default,
bool? ProviderRegistry = default,
bool? OptionalModuleNamesEnabled = default,
bool? LocalDeployEnabled = default,
bool? ResourceDerivedTypesEnabled = default,
string? AssemblyVersion = BicepTestConstants.DevAssemblyFileVersion)
{
Expand All @@ -37,6 +38,7 @@ public FeatureProviderOverrides(
bool? DynamicTypeLoadingEnabled = default,
bool? ProviderRegistry = default,
bool? OptionalModuleNamesEnabled = default,
bool? LocalDeployEnabled = default,
bool? ResourceDerivedTypesEnabled = default,
string? AssemblyVersion = BicepTestConstants.DevAssemblyFileVersion
) : this(
Expand All @@ -53,6 +55,7 @@ public FeatureProviderOverrides(
DynamicTypeLoadingEnabled,
ProviderRegistry,
OptionalModuleNamesEnabled,
LocalDeployEnabled,
ResourceDerivedTypesEnabled,
AssemblyVersion)
{ }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,5 +40,7 @@ public OverriddenFeatureProvider(IFeatureProvider features, FeatureProviderOverr

public bool OptionalModuleNamesEnabled => overrides.OptionalModuleNamesEnabled ?? features.OptionalModuleNamesEnabled;

public bool LocalDeployEnabled => overrides.LocalDeployEnabled ?? features.LocalDeployEnabled;

public bool ResourceDerivedTypesEnabled => overrides.ResourceDerivedTypesEnabled ?? features.ResourceDerivedTypesEnabled;
}
5 changes: 5 additions & 0 deletions src/Bicep.Core.UnitTests/Utils/CompilationHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,11 @@ public static async Task<CompilationResult> RestoreAndCompile(ServiceBuilder ser

var (uriDictionary, entryUri) = CreateFileDictionary(filesToAppend, "main.bicep");

return await RestoreAndCompile(services, uriDictionary, entryUri);
}

public static async Task<CompilationResult> RestoreAndCompile(ServiceBuilder services, IReadOnlyDictionary<Uri, string> uriDictionary, Uri entryUri)
{
var compiler = services.Build().GetCompiler();
var compilation = await compiler.CreateCompilation(entryUri, CreateWorkspace(uriDictionary));

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ public record ExperimentalFeaturesEnabled(
bool DynamicTypeLoading,
bool ProviderRegistry,
bool OptionalModuleNames,
bool LocalDeploy,
bool ResourceDerivedTypes)
{
public static ExperimentalFeaturesEnabled Bind(JsonElement element)
Expand Down
2 changes: 2 additions & 0 deletions src/Bicep.Core/Features/FeatureProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ public FeatureProvider(RootConfiguration configuration)

public bool OptionalModuleNamesEnabled => configuration.ExperimentalFeaturesEnabled.OptionalModuleNames;

public bool LocalDeployEnabled => configuration.ExperimentalFeaturesEnabled.LocalDeploy;

public bool ResourceDerivedTypesEnabled => configuration.ExperimentalFeaturesEnabled.ResourceDerivedTypes;

private static bool ReadBooleanEnvVar(string envVar, bool defaultValue)
Expand Down
3 changes: 3 additions & 0 deletions src/Bicep.Core/Features/IFeatureProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ public interface IFeatureProvider

bool OptionalModuleNamesEnabled { get; }

bool LocalDeployEnabled { get; }

bool ResourceDerivedTypesEnabled { get; }

IEnumerable<(string name, bool impactsCompilation, bool usesExperimentalArmEngineFeature)> EnabledFeatureMetadata
Expand All @@ -49,6 +51,7 @@ public interface IFeatureProvider
(TestFrameworkEnabled, CoreResources.ExperimentalFeatureNames_TestFramework, false, false),
(AssertsEnabled, CoreResources.ExperimentalFeatureNames_Asserts, true, true),
(OptionalModuleNamesEnabled, CoreResources.ExperimentalFeatureNames_OptionalModuleNames, true, false),
(LocalDeployEnabled, "Enable local deploy", false, false),
(ResourceDerivedTypesEnabled, CoreResources.ExperimentalFeatureNames_ResourceDerivedTypes, true, false),
})
{
Expand Down
3 changes: 3 additions & 0 deletions src/Bicep.Core/FileSystem/PathHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -247,5 +247,8 @@ public static string GetRelativePath(Uri source, Uri target)

return relativeUri;
}

public static Uri ResolveFilePath(Uri parentFileUri, string childFilePath)
=> TryResolveFilePath(parentFileUri, childFilePath) ?? throw new InvalidOperationException($"Failed to resolve file path for URI {parentFileUri} and child path {childFilePath}");
}
}
21 changes: 21 additions & 0 deletions src/Bicep.Local.Extension/Bicep.Local.Extension.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<AssemblyName>Azure.Bicep.Local.Extension</AssemblyName>
<RootNamespace>Bicep.Local.Extension</RootNamespace>
<EnableNuget>true</EnableNuget>
<PackageTags>Azure;ResourceManager;ARM;Deployments;Templates;Bicep</PackageTags>
<Description>
Bicep compiler extension functionality.
The Bicep team has made this NuGet package publicly available on nuget.org. While it is public, it is not a supported package. Any dependency you take on this package will be done at your own risk and we reserve the right to push breaking changes to this package at any time.
</Description>
</PropertyGroup>

<ItemGroup>
<Protobuf Include="extension.proto" GrpcServices="Both" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="CommandLineParser" Version="2.9.1" />
<PackageReference Include="Grpc.AspNetCore" Version="2.62.0" />
</ItemGroup>
</Project>
60 changes: 60 additions & 0 deletions src/Bicep.Local.Extension/Protocol/IResourceHandler.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

using System.Collections.Immutable;
using System.Text.Json.Nodes;
using System.Text.Json.Serialization.Metadata;

namespace Bicep.Local.Extension.Protocol;

public record ExtensibilityOperationRequest(
ExtensibleImportData Import,
ExtensibleResourceData Resource);

public record ExtensibilityOperationResponse(
ExtensibleResourceData? Resource,
ExtensibleResourceMetadata? ResourceMetadata,
ImmutableArray<ExtensibilityError>? Errors);

public record ExtensibleImportData(
string Provider,
string Version,
JsonObject? Config);

public record ExtensibleResourceData(
string Type,
JsonObject? Properties);

public record ExtensibleResourceMetadata(
ImmutableArray<string>? ReadOnlyProperties,
ImmutableArray<string>? ImmutableProperties,
ImmutableArray<string>? DynamicProperties);

public record ExtensibilityError(
string Code,
string Message,
string Target);

public interface IGenericResourceHandler
{
Task<ExtensibilityOperationResponse> Save(
ExtensibilityOperationRequest request,
CancellationToken cancellationToken);

Task<ExtensibilityOperationResponse> PreviewSave(
ExtensibilityOperationRequest request,
CancellationToken cancellationToken);

Task<ExtensibilityOperationResponse> Get(
ExtensibilityOperationRequest request,
CancellationToken cancellationToken);

Task<ExtensibilityOperationResponse> Delete(
ExtensibilityOperationRequest request,
CancellationToken cancellationToken);
}

public interface IResourceHandler : IGenericResourceHandler
{
string ResourceType { get; }
}
35 changes: 35 additions & 0 deletions src/Bicep.Local.Extension/Protocol/ResourceDispatcher.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

using System.Collections.Immutable;

namespace Bicep.Local.Extension.Protocol;

public class ResourceDispatcher
{
private readonly IGenericResourceHandler? genericResourceHandler;
private readonly ImmutableDictionary<string, IResourceHandler> resourceHandlers;

public ResourceDispatcher(
IGenericResourceHandler? genericResourceHandler,
ImmutableDictionary<string, IResourceHandler> resourceHandlers)
{
this.genericResourceHandler = genericResourceHandler;
this.resourceHandlers = resourceHandlers;
}

public IGenericResourceHandler GetHandler(string resourceType)
{
if (this.resourceHandlers.TryGetValue(resourceType, out var handler))
{
return handler;
}

if (this.genericResourceHandler is {})
{
return this.genericResourceHandler;
}

throw new ArgumentException($"Resource type '{resourceType}' is not supported.");
}
}
41 changes: 41 additions & 0 deletions src/Bicep.Local.Extension/Protocol/ResourceDispatcherBuilder.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

using System.Collections.Immutable;

namespace Bicep.Local.Extension.Protocol;

public class ResourceDispatcherBuilder
{
private IGenericResourceHandler? genericResourceHandler;
private readonly Dictionary<string, IResourceHandler> resourceHandlers = new(StringComparer.OrdinalIgnoreCase);

public ResourceDispatcherBuilder AddHandler(IResourceHandler handler)
{
if (!this.resourceHandlers.TryAdd(handler.ResourceType, handler))
{
throw new ArgumentException($"Resource type '{handler.ResourceType}' has already been registered.");
}

this.resourceHandlers[handler.ResourceType] = handler;
return this;
}

public ResourceDispatcherBuilder AddGenericHandler(IGenericResourceHandler handler)
{
if (this.genericResourceHandler is not null)
{
throw new ArgumentException($"Generic resource handler has already been registered.");
}

this.genericResourceHandler = handler;
return this;
}

public ResourceDispatcher Build()
{
return new(
this.genericResourceHandler,
this.resourceHandlers.ToImmutableDictionary(StringComparer.OrdinalIgnoreCase));
}
}
Loading

0 comments on commit c6804d8

Please sign in to comment.