Skip to content

Commit

Permalink
Tidy up some experimental features (#14944)
Browse files Browse the repository at this point in the history
1. Remove 'extensionRegistry' experimental feature.
2. Remove 'dynamicTypeLoading' experimental feature - instead of being
based on the registry path ending in "/az", activate based on the name
supplied in the extension configuration ("AzureResourceManager").
3. Remove the "extension alias" bicepconfig capability to avoid exposing
it - it's not fully implemented, and needs more design work.
###### Microsoft Reviewers: [Open in
CodeFlow](https://microsoft.github.io/open-pr/?codeflow=https://github.com/Azure/bicep/pull/14944)
  • Loading branch information
anthony-c-martin authored Aug 30, 2024
1 parent 0aa6523 commit d5079a7
Show file tree
Hide file tree
Showing 44 changed files with 78 additions and 667 deletions.
8 changes: 0 additions & 8 deletions docs/experimental-features.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,20 +11,12 @@ The following features can be optionally enabled through your `bicepconfig.json`
### `assertions`
Should be enabled in tandem with `testFramework` experimental feature flag for expected functionality. Allows you to author boolean assertions using the `assert` keyword comparing the actual value of a parameter, variable, or resource name to an expected value. Assert statements can only be written directly within the Bicep file whose resources they reference. For more information, see [Bicep Experimental Test Framework](https://github.com/Azure/bicep/issues/11967).

### `dynamicTypeLoading`
Requires `extensibility` to be enabled. If enabled, users are able to fetch the azure resource type definitions from an OCI Registry as a runtime dependency. To fetch the type definitions the following syntax can be used. For example `extension 'br:mcr.microsoft.com/bicep/extensions/[email protected]' as az`.
The extension definitions also support aliasing via `bicepconfig.json` similar to [`moduleAliases`](https://learn.microsoft.com/azure/azure-resource-manager/bicep/bicep-config-modules#aliases-for-modules). For example `extension 'br/public:[email protected]' as az`.

### `extendableParamFiles`
Enables the ability to extend bicepparam files from other bicepparam files. For more information, see [Extendable Bicep Params Files](./experimental/extendable-param-files.md).

### `extensibility`
Allows Bicep to use an extensibility model to deploy non-ARM resources. Currently, we support Kubernetes extension ([Bicep Kubernetes extension](https://learn.microsoft.com/en-us/azure/azure-resource-manager/bicep/bicep-extensibility-kubernetes-provider)) and Microsoft Graph extension ([Bicep templates for Microsoft Graph](https://aka.ms/graphbicep)).

### `extensionRegistry`
Requires `dynamicTypeLoading` and `extensibility` to be enabled. If enabled, users are able to fetch the third party resource type definitions from an OCI Registry as a runtime dependency. To fetch the type definitions the following syntax can be used. For example `extension 'br:thirdpartyregistry.azurecr.io/bicep/extension/[email protected]' as thirdparty`.
The extension definitions also support aliasing via `bicepconfig.json` similar to [`moduleAliases`](https://learn.microsoft.com/azure/azure-resource-manager/bicep/bicep-config-modules#aliases-for-modules). For example `extension 'br/public:[email protected]' as thirdparty`.

### `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.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,6 @@
]
},
"experimentalFeaturesEnabled": {
"extensibility": true,
"dynamicTypeLoading": true,
"extensionRegistry": true
"extensibility": true
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@
"credentialPrecedence": ["AzureCLI"]
},
"experimentalFeaturesEnabled": {
"extensibility": true,
"dynamicTypeLoading": true,
"extensionRegistry": true
"extensibility": true
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
{
"experimentalFeaturesEnabled": {
"extensibility": true,
"extensionRegistry": true,
"localDeploy": true
},
"extensions": {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
{
"experimentalFeaturesEnabled": {
"extensibility": true,
"extensionRegistry": true,
"localDeploy": true
},
"extensions": {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
{
"experimentalFeaturesEnabled": {
"extensibility": true,
"extensionRegistry": true,
"localDeploy": true
},
"extensions": {
Expand Down
104 changes: 0 additions & 104 deletions src/Bicep.Cli.IntegrationTests/BuildCommandTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -101,110 +101,6 @@ public async Task Build_Valid_SingleFile_WithTemplateSpecReference_ShouldSucceed
actualLocation: compiledFilePath);
}

[DataTestMethod]
//[DataRow("br:mcr.microsoft.com/bicep/extension/az", true, LanguageConstants.BicepPublicMcrRegistry)]
//[DataRow("br/public:az", true, LanguageConstants.BicepPublicMcrRegistry)]
[DataRow("br/contoso:az", true, "contoso.azurecr.io")]
//[DataRow("br/mcr:az", true, LanguageConstants.BicepPublicMcrRegistry)]
//[DataRow("br:contoso.azurecr.io/bicep/extensions/az", true, "contoso.azurecr.io")]
// Negative
// [DataRow("az", false)] - commented out while we graciously deprecate the legacy provider declaration syntax.
//[DataRow("br:invalid.azureacr.io/bicep/extensions/az", false)]
//[DataRow("br/unknown:az", false)]
public async Task Build_Valid_SingleFile_WithExtensionDeclarationStatement(
string extensionDeclarationSyntax,
bool shouldSucceed,
string containingFolder = "")
{
// SETUP
// 1. create a mock registry client
var hosts = new[] {
LanguageConstants.BicepPublicMcrRegistry,
"contoso.azurecr.io",
"invalid.azureacr.io"
};
(var clientFactory, var blobClients) = RegistryUtils.CreateMockRegistryClients(hosts.Select(host => (host, "bicep/extensions/az")).ToArray());


// 2. upload a manifest and its blob layer
foreach (var ((uri, _), client) in blobClients)
{
if (uri.Host.Contains("invalid")) { continue; }
var layer = await client.UploadBlobAsync(BinaryData.FromString(""));
var config = await client.UploadBlobAsync(BinaryData.FromString("{}"));
await client.SetManifestAsync(BicepTestConstants.GetBicepExtensionManifest(layer, config), "2.0.0");
}

// 3. create a main.bicep and save it to a output directory
var bicepFile = $"""
extension '{extensionDeclarationSyntax}:2.0.0'
""";
var tempDirectory = FileHelper.GetUniqueTestOutputPath(TestContext);
Directory.CreateDirectory(tempDirectory);
var bicepFilePath = Path.Combine(tempDirectory, "main.bicep");
File.WriteAllText(bicepFilePath, bicepFile);

var bicepConfigFile = """
{
"extensionAliases" : {
"br": {
"contoso": {
"registry": "contoso.azurecr.io",
"extensionPath": "bicep/extensions"
},
"mcr": {
"registry": "mcr.microsoft.com",
"extensionPath": "bicep/extensions"
}
}
}
}
""";
var bicepConfigPath = Path.Combine(tempDirectory, "bicepconfig.json");
File.WriteAllText(bicepConfigPath, bicepConfigFile);

// 4. create a settings object with the mock registry client and relevant features enabled
var settings = new InvocationSettings(new(TestContext, RegistryEnabled: true, ExtensibilityEnabled: true, DynamicTypeLoadingEnabled: true), clientFactory, Repository.Create<ITemplateSpecRepositoryFactory>().Object);

// TEST
// 5. run bicep build
var (output, error, result) = await TextWriterHelper.InvokeWriterAction((@out, err)
=> new Program(new(Output: @out, Error: err), services
=>
{
if (settings.FeatureOverrides is { })
{
services.WithFeatureOverrides(settings.FeatureOverrides);
}
services
.WithEmptyAzResources()
.AddSingleton(settings.Environment ?? BicepTestConstants.EmptyEnvironment)
.AddSingleton(settings.ClientFactory)
.AddSingleton(settings.TemplateSpecRepositoryFactory);
})
.RunAsync(["build", bicepFilePath], CancellationToken.None));

// ASSERT
// 6. assert 'bicep build' completed successfully
using (new AssertionScope())
{
result.Should().Be(shouldSucceed ? 0 : 1);
output.Should().BeEmpty();
if (shouldSucceed)
{
AssertNoErrors(error);
}
}
if (shouldSucceed)
{
// 7. assert the extension files were restored to the cache directory
Directory.Exists(settings.FeatureOverrides!.CacheRootDirectory).Should().BeTrue();
var extensionDir = Path.Combine(settings.FeatureOverrides.CacheRootDirectory!, ArtifactReferenceSchemes.Oci, containingFolder, "bicep$extensions$az", "2.0.0$");
Directory.EnumerateFiles(extensionDir).ToList().Select(Path.GetFileName).Should().BeEquivalentTo(new List<string> { "types.tgz", "lock", "manifest", "metadata" });
}
}

[DataTestMethod]
[DynamicData(nameof(GetValidDataSets), DynamicDataSourceType.Method, DynamicDataDisplayNameDeclaringType = typeof(DataSet), DynamicDataDisplayName = nameof(DataSet.GetDisplayName))]
public async Task Build_Valid_SingleFile_WithTemplateSpecReference_ToStdOut_ShouldSucceed(DataSet dataSet)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ public async Task Publish_extension_should_succeed_to_filesystem()
var publishResult = await Bicep(services => services.WithFileSystem(fs), ["publish-extension", indexPath, "--target", targetPath]);
publishResult.Should().Succeed().And.NotHaveStdout();

var services = new ServiceBuilder().WithFileSystem(fs).WithFeatureOverrides(new(ExtensibilityEnabled: true, ExtensionRegistry: true));
var services = new ServiceBuilder().WithFileSystem(fs).WithFeatureOverrides(new(ExtensibilityEnabled: true));
var compileResult = await CompilationHelper.RestoreAndCompile(services, """
extension '../../target/extension.tgz'

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using Bicep.Core.Configuration;
using Bicep.Core.Diagnostics;
using Bicep.Core.Semantics.Namespaces;
using Bicep.Core.TypeSystem.Providers;
using Bicep.Core.UnitTests;
using Bicep.Core.UnitTests.Assertions;
using Bicep.Core.UnitTests.Mock;
Expand All @@ -20,17 +21,30 @@ namespace Bicep.Core.IntegrationTests
{

[TestClass]
public class DynamicAzTypesTests : TestBase
public class AzTypesViaRegistryTests : TestBase
{
private static readonly string EmptyIndexJson = """
{
"resources": {},
"resourceFunctions": {},
"settings": {
"name": "AzureResourceManager",
"version": "1.2.3",
"isSingleton": true
}
}
""";


private async Task<ServiceBuilder> GetServices()
{
var indexJson = FileHelper.SaveResultFile(TestContext, "types/index.json", """{"resources": {}, "resourceFunctions": {}}""");
var indexJson = FileHelper.SaveResultFile(TestContext, "types/index.json", EmptyIndexJson);

var cacheRoot = FileHelper.GetUniqueTestOutputPath(TestContext);
Directory.CreateDirectory(cacheRoot);

var services = new ServiceBuilder()
.WithFeatureOverrides(new(ExtensibilityEnabled: true, DynamicTypeLoadingEnabled: true, CacheRootDirectory: cacheRoot))
.WithFeatureOverrides(new(ExtensibilityEnabled: true, CacheRootDirectory: cacheRoot))
.WithContainerRegistryClientFactory(RegistryHelper.CreateOciClientForAzExtension());

await RegistryHelper.PublishAzExtension(services.Build(), indexJson);
Expand All @@ -52,89 +66,8 @@ private async Task<ServiceBuilder> ServicesWithTestExtensionArtifact(ArtifactReg
Directory.CreateDirectory(cacheRoot);

return new ServiceBuilder()
.WithFeatureOverrides(new(
ExtensibilityEnabled: true,
DynamicTypeLoadingEnabled: true,
CacheRootDirectory: cacheRoot))
.WithContainerRegistryClientFactory(clientFactory);
}

[TestMethod]
public async Task Az_namespace_can_be_used_without_configuration()
{
var services = await GetServices();
var result = await CompilationHelper.RestoreAndCompile(services, ("main.bicep", @$"
extension 'br/public:az:{BicepTestConstants.BuiltinAzExtensionVersion}'
"));

result.Should().GenerateATemplate();
result.ExcludingLinterDiagnostics().Should().NotHaveAnyDiagnostics();
}

[TestMethod]
public async Task Az_namespace_errors_with_configuration()
{
var services = await GetServices();
var result = await CompilationHelper.RestoreAndCompile(services, @$"
extension 'br/public:az:{BicepTestConstants.BuiltinAzExtensionVersion}' with {{}}
");

result.Should().NotGenerateATemplate();
result.ExcludingLinterDiagnostics().Should().HaveDiagnostics(new[] {
("BCP205", DiagnosticLevel.Error, "Extension \"az\" does not support configuration."),
});
}

[TestMethod]
public async Task Az_namespace_can_be_used_with_alias()
{
var services = await GetServices();
var result = await CompilationHelper.RestoreAndCompile(services, @$"
extension 'br/public:az:{BicepTestConstants.BuiltinAzExtensionVersion}' as testAlias
");

result.Should().GenerateATemplate();
result.Compilation.GetEntrypointSemanticModel().Root.ExtensionDeclarations.Should().Contain(x => x.Name.Equals("testAlias"));
}

[TestMethod]
public async Task Az_namespace_can_be_used_with_bicepconfig_extension_alias()
{
var services = await GetServices();

services = services.WithConfigurationPatch(c => c.WithExtensionAliases("""
{
"br": {
"customAlias": {
"registry": "mcr.microsoft.com",
"extensionPath": "bicep/extensions"
}
}
}
"""));

var result = await CompilationHelper.RestoreAndCompile(services, @$"
extension 'br/customAlias:az:{BicepTestConstants.BuiltinAzExtensionVersion}'
");

result.Should().GenerateATemplate();
result.Compilation.GetEntrypointSemanticModel().Root.ExtensionDeclarations.Should().Contain(x => x.Name.Equals("az"));
}

[TestMethod]
public async Task Inlined_Az_namespace_alias_is_not_specified_in_config_yields_diagnostic()
{
var services = new ServiceBuilder()
.WithFeatureOverrides(new(ExtensibilityEnabled: true, DynamicTypeLoadingEnabled: true));

var result = await CompilationHelper.RestoreAndCompile(services, @"
extension 'br/notFound:az:0.2.661'
");

result.Should().NotGenerateATemplate();
result.Should().HaveDiagnostics([
("BCP379", DiagnosticLevel.Error, "The OCI artifact extension alias name \"notFound\" does not exist in the built-in Bicep configuration."),
]);
.WithFeatureOverrides(new(ExtensibilityEnabled: true, CacheRootDirectory: cacheRoot))
.WithContainerRegistryClientFactory(clientFactory);
}

[TestMethod]
Expand All @@ -146,7 +79,7 @@ public async Task Bicep_module_artifact_specified_in_extension_declaration_synta
var clientFactory = RegistryHelper.CreateMockRegistryClients((testArtifact.RegistryAddress, testArtifact.RepositoryPath)).factoryMock;
var services = new ServiceBuilder()
.WithFileSystem(fsMock)
.WithFeatureOverrides(new(ExtensibilityEnabled: true, DynamicTypeLoadingEnabled: true))
.WithFeatureOverrides(new(ExtensibilityEnabled: true))
.WithContainerRegistryClientFactory(clientFactory);

await RegistryHelper.PublishModuleToRegistryAsync(
Expand Down Expand Up @@ -221,7 +154,7 @@ public async Task Repository_not_found_in_registry(
mockBlobClient.Object);

var services = new ServiceBuilder()
.WithFeatureOverrides(new(ExtensibilityEnabled: true, DynamicTypeLoadingEnabled: true))
.WithFeatureOverrides(new(ExtensibilityEnabled: true))
.WithContainerRegistryClientFactory(containerRegistryFactoryBuilder.Build().clientFactory);

// ACT
Expand Down Expand Up @@ -349,7 +282,7 @@ public async Task Az_namespace_can_be_loaded_dynamically_using_extension_configu
"1.0.0-fake");
var services = await ServicesWithTestExtensionArtifact(
artifactRegistryAddress,
ThirdPartyTypeHelper.GetTypesTgzBytesFromFiles(("index.json", """{"resources": {}, "resourceFunctions": {}}""")));
ThirdPartyTypeHelper.GetTypesTgzBytesFromFiles(("index.json", EmptyIndexJson)));
services = services.WithConfigurationPatch(c => c.WithExtensions($$"""
{
"az": "{{artifactRegistryAddress.ToSpecificationString(':')}}"
Expand All @@ -362,7 +295,7 @@ extension az
//ASSERT
result.Should().GenerateATemplate();
result.Template.Should().NotBeNull();
result.Template.Should().HaveValueAtPath("$.imports.az.version", AzNamespaceType.EmbeddedAzExtensionVersion);
result.Template.Should().HaveValueAtPath("$.imports.az.version", AzNamespaceType.Settings.TemplateExtensionVersion);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,7 @@ namespace Bicep.Core.IntegrationTests
[TestClass]
public class CentralizedExtensionVersionManagementTests : TestBase
{
private ServiceBuilder Services => new ServiceBuilder()
.WithFeatureOverrides(new(
ExtensibilityEnabled: true,
DynamicTypeLoadingEnabled: true));
private ServiceBuilder Services => new ServiceBuilder().WithFeatureOverrides(new(ExtensibilityEnabled: true));

[TestMethod]
[DynamicData(nameof(ExtensionsConfig_SupportForConfigManagedExtensionDeclarationSyntax_When_ExtensionIsBuiltIn_TestCases))]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ private static ServiceBuilder GetServiceBuilder(IFileSystem fileSystem, string r
var clientFactory = RegistryHelper.CreateMockRegistryClient(registryHost, repositoryPath);

return new ServiceBuilder()
.WithFeatureOverrides(new(ExtensibilityEnabled: true, ExtensionRegistry: true, DynamicTypeLoadingEnabled: true))
.WithFeatureOverrides(new(ExtensibilityEnabled: true))
.WithFileSystem(fileSystem)
.WithContainerRegistryClientFactory(clientFactory);
}
Expand Down
Loading

0 comments on commit d5079a7

Please sign in to comment.