Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ public static partial class CdkExtensions
public static T? GetSingleResourceInScope<T>(this Azure.Provisioning.IConstruct construct) where T : Azure.Provisioning.Resource { throw null; }
public static T? GetSingleResource<T>(this Azure.Provisioning.IConstruct construct) where T : Azure.Provisioning.Resource { throw null; }
}
public abstract partial class Construct : Azure.Provisioning.IConstruct, System.ClientModel.Primitives.IPersistableModel<Azure.Provisioning.Construct>
public abstract partial class Construct : Azure.Provisioning.IConstruct
{
protected Construct(Azure.Provisioning.IConstruct? scope, string name, Azure.Provisioning.ConstructScope constructScope = Azure.Provisioning.ConstructScope.ResourceGroup, System.Guid? tenantId = default(System.Guid?), System.Guid? subscriptionId = default(System.Guid?), string? envName = null, Azure.Provisioning.ResourceManager.ResourceGroup? resourceGroup = null) { }
public Azure.Provisioning.ConstructScope ConstructScope { get { throw null; } }
Expand All @@ -20,12 +20,10 @@ public void AddOutput(Azure.Provisioning.Output output) { }
public void AddParameter(Azure.Provisioning.Parameter parameter) { }
public void AddResource(Azure.Provisioning.Resource resource) { }
public System.Collections.Generic.IEnumerable<Azure.Provisioning.IConstruct> GetConstructs(bool recursive = true) { throw null; }
public System.Collections.Generic.IEnumerable<Azure.Provisioning.Resource> GetExistingResources(bool recursive = true) { throw null; }
public System.Collections.Generic.IEnumerable<Azure.Provisioning.Output> GetOutputs(bool recursive = true) { throw null; }
public System.Collections.Generic.IEnumerable<Azure.Provisioning.Parameter> GetParameters(bool recursive = true) { throw null; }
public System.Collections.Generic.IEnumerable<Azure.Provisioning.Resource> GetResources(bool recursive = true) { throw null; }
Azure.Provisioning.Construct System.ClientModel.Primitives.IPersistableModel<Azure.Provisioning.Construct>.Create(System.BinaryData data, System.ClientModel.Primitives.ModelReaderWriterOptions options) { throw null; }
string System.ClientModel.Primitives.IPersistableModel<Azure.Provisioning.Construct>.GetFormatFromOptions(System.ClientModel.Primitives.ModelReaderWriterOptions options) { throw null; }
System.BinaryData System.ClientModel.Primitives.IPersistableModel<Azure.Provisioning.Construct>.Write(System.ClientModel.Primitives.ModelReaderWriterOptions options) { throw null; }
protected T UseExistingResource<T>(T? resource, System.Func<T> create) where T : Azure.Provisioning.Resource { throw null; }
}
public enum ConstructScope
Expand All @@ -49,6 +47,7 @@ public partial interface IConstruct
void AddParameter(Azure.Provisioning.Parameter parameter);
void AddResource(Azure.Provisioning.Resource resource);
System.Collections.Generic.IEnumerable<Azure.Provisioning.IConstruct> GetConstructs(bool recursive = true);
System.Collections.Generic.IEnumerable<Azure.Provisioning.Resource> GetExistingResources(bool recursive = true);
System.Collections.Generic.IEnumerable<Azure.Provisioning.Output> GetOutputs(bool recursive = true);
System.Collections.Generic.IEnumerable<Azure.Provisioning.Parameter> GetParameters(bool recursive = true);
System.Collections.Generic.IEnumerable<Azure.Provisioning.Resource> GetResources(bool recursive = true);
Expand Down Expand Up @@ -176,8 +175,8 @@ public partial class ResourceGroup : Azure.Provisioning.Resource<Azure.ResourceM
public static partial class ResourceManagerExtensions
{
public static Azure.Provisioning.ResourceManager.ResourceGroup AddResourceGroup(this Azure.Provisioning.IConstruct construct) { throw null; }
public static Azure.Provisioning.ResourceManager.ResourceGroup GetOrAddResourceGroup(this Azure.Provisioning.IConstruct construct) { throw null; }
public static Azure.Provisioning.ResourceManager.Subscription GetOrCreateSubscription(this Azure.Provisioning.IConstruct construct, System.Guid? subscriptionId = default(System.Guid?)) { throw null; }
public static Azure.Provisioning.ResourceManager.ResourceGroup? GetResourceGroup(this Azure.Provisioning.IConstruct construct) { throw null; }
}
public partial class Subscription : Azure.Provisioning.Resource<Azure.ResourceManager.Resources.SubscriptionData>
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ public static partial class CdkExtensions
public static T? GetSingleResourceInScope<T>(this Azure.Provisioning.IConstruct construct) where T : Azure.Provisioning.Resource { throw null; }
public static T? GetSingleResource<T>(this Azure.Provisioning.IConstruct construct) where T : Azure.Provisioning.Resource { throw null; }
}
public abstract partial class Construct : Azure.Provisioning.IConstruct, System.ClientModel.Primitives.IPersistableModel<Azure.Provisioning.Construct>
public abstract partial class Construct : Azure.Provisioning.IConstruct
{
protected Construct(Azure.Provisioning.IConstruct? scope, string name, Azure.Provisioning.ConstructScope constructScope = Azure.Provisioning.ConstructScope.ResourceGroup, System.Guid? tenantId = default(System.Guid?), System.Guid? subscriptionId = default(System.Guid?), string? envName = null, Azure.Provisioning.ResourceManager.ResourceGroup? resourceGroup = null) { }
public Azure.Provisioning.ConstructScope ConstructScope { get { throw null; } }
Expand All @@ -20,12 +20,10 @@ public void AddOutput(Azure.Provisioning.Output output) { }
public void AddParameter(Azure.Provisioning.Parameter parameter) { }
public void AddResource(Azure.Provisioning.Resource resource) { }
public System.Collections.Generic.IEnumerable<Azure.Provisioning.IConstruct> GetConstructs(bool recursive = true) { throw null; }
public System.Collections.Generic.IEnumerable<Azure.Provisioning.Resource> GetExistingResources(bool recursive = true) { throw null; }
public System.Collections.Generic.IEnumerable<Azure.Provisioning.Output> GetOutputs(bool recursive = true) { throw null; }
public System.Collections.Generic.IEnumerable<Azure.Provisioning.Parameter> GetParameters(bool recursive = true) { throw null; }
public System.Collections.Generic.IEnumerable<Azure.Provisioning.Resource> GetResources(bool recursive = true) { throw null; }
Azure.Provisioning.Construct System.ClientModel.Primitives.IPersistableModel<Azure.Provisioning.Construct>.Create(System.BinaryData data, System.ClientModel.Primitives.ModelReaderWriterOptions options) { throw null; }
string System.ClientModel.Primitives.IPersistableModel<Azure.Provisioning.Construct>.GetFormatFromOptions(System.ClientModel.Primitives.ModelReaderWriterOptions options) { throw null; }
System.BinaryData System.ClientModel.Primitives.IPersistableModel<Azure.Provisioning.Construct>.Write(System.ClientModel.Primitives.ModelReaderWriterOptions options) { throw null; }
protected T UseExistingResource<T>(T? resource, System.Func<T> create) where T : Azure.Provisioning.Resource { throw null; }
}
public enum ConstructScope
Expand All @@ -49,6 +47,7 @@ public partial interface IConstruct
void AddParameter(Azure.Provisioning.Parameter parameter);
void AddResource(Azure.Provisioning.Resource resource);
System.Collections.Generic.IEnumerable<Azure.Provisioning.IConstruct> GetConstructs(bool recursive = true);
System.Collections.Generic.IEnumerable<Azure.Provisioning.Resource> GetExistingResources(bool recursive = true);
System.Collections.Generic.IEnumerable<Azure.Provisioning.Output> GetOutputs(bool recursive = true);
System.Collections.Generic.IEnumerable<Azure.Provisioning.Parameter> GetParameters(bool recursive = true);
System.Collections.Generic.IEnumerable<Azure.Provisioning.Resource> GetResources(bool recursive = true);
Expand Down Expand Up @@ -176,8 +175,8 @@ public partial class ResourceGroup : Azure.Provisioning.Resource<Azure.ResourceM
public static partial class ResourceManagerExtensions
{
public static Azure.Provisioning.ResourceManager.ResourceGroup AddResourceGroup(this Azure.Provisioning.IConstruct construct) { throw null; }
public static Azure.Provisioning.ResourceManager.ResourceGroup GetOrAddResourceGroup(this Azure.Provisioning.IConstruct construct) { throw null; }
public static Azure.Provisioning.ResourceManager.Subscription GetOrCreateSubscription(this Azure.Provisioning.IConstruct construct, System.Guid? subscriptionId = default(System.Guid?)) { throw null; }
public static Azure.Provisioning.ResourceManager.ResourceGroup? GetResourceGroup(this Azure.Provisioning.IConstruct construct) { throw null; }
}
public partial class Subscription : Azure.Provisioning.Resource<Azure.ResourceManager.Resources.SubscriptionData>
{
Expand Down
237 changes: 13 additions & 224 deletions sdk/provisioning/Azure.Provisioning/src/Construct.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,17 @@
// Licensed under the MIT License.

using System;
using System.ClientModel.Primitives;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Azure.Provisioning.ResourceManager;
using Microsoft.Extensions.Azure;

namespace Azure.Provisioning
{
/// <summary>
/// Basic building block of a set of resources in Azure.
/// </summary>
#pragma warning disable AZC0012 // Avoid single word type names
public abstract class Construct : IConstruct, IPersistableModel<Construct>
public abstract class Construct : IConstruct
#pragma warning restore AZC0012 // Avoid single word type names
{
private List<Parameter> _parameters;
Expand Down Expand Up @@ -67,11 +64,6 @@ internal Construct(
Subscription? subscription = default,
ResourceGroup? resourceGroup = default)
{
if (scope is null && constructScope == ConstructScope.ResourceGroup)
{
throw new ArgumentException($"Scope cannot be null if construct scope is is {nameof(ConstructScope.ResourceGroup)}");
}

Scope = scope;
Scope?.AddConstruct(this);
_resources = new List<Resource>();
Expand All @@ -84,7 +76,7 @@ internal Construct(
ConstructScope = constructScope;
if (constructScope == ConstructScope.ResourceGroup)
{
ResourceGroup = resourceGroup ?? scope!.ResourceGroup ?? scope.GetOrAddResourceGroup();
ResourceGroup = resourceGroup ?? scope?.ResourceGroup ?? scope?.GetOrAddResourceGroup();
}
if (constructScope == ConstructScope.Subscription)
{
Expand Down Expand Up @@ -124,6 +116,17 @@ public IEnumerable<Resource> GetResources(bool recursive = true)
return result;
}

/// <inheritdoc/>
public IEnumerable<Resource> GetExistingResources(bool recursive = true)
{
IEnumerable<Resource> result = _existingResources;
if (recursive)
{
result = result.Concat(GetConstructs(false).SelectMany(c => c.GetExistingResources(true)));
}
return result;
}

/// <inheritdoc/>
public IEnumerable<IConstruct> GetConstructs(bool recursive = true)
{
Expand Down Expand Up @@ -180,219 +183,5 @@ public void AddOutput(Output output)
{
_outputs.Add(output);
}

private string GetScopeName()
{
return ResourceGroup?.Name ?? (Subscription != null ? $"subscription('{Subscription.Name}')" : "tenant()");
}

private BinaryData SerializeModuleReference(ModelReaderWriterOptions options)
{
using var stream = new MemoryStream();
stream.WriteLine($"module {Name} './resources/{Name}/{Name}.bicep' = {{");
stream.WriteLine($" name: '{Name}'");
stream.WriteLine($" scope: {GetScopeName()}");

var parametersToWrite = new HashSet<Parameter>();
var outputs = new HashSet<Output>(GetOutputs());
foreach (var p in GetParameters(false))
{
if (!ShouldExposeParameter(p, outputs))
{
continue;
}
parametersToWrite.Add(p);
}
if (parametersToWrite.Count > 0)
{
stream.WriteLine($" params: {{");
foreach (var parameter in parametersToWrite)
{
stream.WriteLine($" {parameter.Name}: {parameter.GetParameterString(Scope!)}");
}
stream.WriteLine($" }}");
}
stream.WriteLine($"}}");

return new BinaryData(stream.GetBuffer().AsMemory(0, (int)stream.Position));
}

private BinaryData SerializeModule(ModelReaderWriterOptions options)
{
using var stream = new MemoryStream();

WriteScopeLine(stream);

WriteParameters(stream);

WriteExistingResources(stream);

foreach (var resource in GetResources(false))
{
if (resource is Tenant)
{
continue;
}
stream.WriteLine();
WriteLines(0, ModelReaderWriter.Write(resource, options), stream, resource);
}

foreach (var construct in GetConstructs(false))
{
stream.WriteLine();
stream.Write(ModelReaderWriter.Write(construct, new ModelReaderWriterOptions("bicep-module")).ToArray());
}

WriteOutputs(stream);

return new BinaryData(stream.GetBuffer().AsMemory(0, (int)stream.Position));
}

private void WriteExistingResources(MemoryStream stream)
{
foreach (var resource in _existingResources)
{
stream.WriteLine();
stream.WriteLine($"resource {resource.Name} '{resource.Id.ResourceType}@{resource.Version}' existing = {{");
stream.WriteLine($" name: '{resource.Name}'");
stream.WriteLine($"}}");
}
}

private void WriteScopeLine(MemoryStream stream)
{
if (ConstructScope != ConstructScope.ResourceGroup)
{
stream.WriteLine($"targetScope = '{ConstructScope.ToString().ToCamelCase()}'{Environment.NewLine}");
}
}

internal void WriteOutputs(MemoryStream stream)
{
if (GetOutputs().Any())
{
stream.WriteLine();
}

var outputsToWrite = new HashSet<Output>();
GetAllOutputsRecursive(this, outputsToWrite, false);
foreach (var output in outputsToWrite)
{
string value;
if (output.IsLiteral || ReferenceEquals(this, output.Resource.ModuleScope))
{
value = output.IsLiteral ? $"'{output.Value}'" : output.Value;
}
else
{
value = $"{output.Resource.ModuleScope!.Name}.outputs.{output.Name}";
}
string name = output.Name;
stream.WriteLine($"output {name} string = {value}");
}
}

private void GetAllOutputsRecursive(IConstruct construct, HashSet<Output> visited, bool isChild)
{
if (!isChild)
{
foreach (var output in construct.GetOutputs())
{
if (!visited.Contains(output))
{
visited.Add(output);
}
}
}
}

private void WriteParameters(MemoryStream stream)
{
var parametersToWrite = new HashSet<Parameter>();
GetAllParametersRecursive(this, parametersToWrite, false);
var outputs = new HashSet<Output>(GetOutputs());
foreach (var parameter in parametersToWrite)
{
if (!ShouldExposeParameter(parameter, outputs))
{
continue;
}
string defaultValue = parameter.DefaultValue is null ? string.Empty : $" = '{parameter.DefaultValue}'";

if (parameter.IsSecure)
stream.WriteLine($"@secure()");

stream.WriteLine($"@description('{parameter.Description}')");
stream.WriteLine($"param {parameter.Name} string{defaultValue}{Environment.NewLine}");
}
}

private bool ShouldExposeParameter(Parameter parameter, HashSet<Output> outputs)
{
// Don't expose the parameter if the output that was used to create the parameter is already in scope.
return parameter.Output == null || !outputs.Contains(parameter.Output);
}

private void GetAllParametersRecursive(IConstruct construct, HashSet<Parameter> visited, bool isChild)
{
if (!isChild)
{
foreach (var parameter in construct.GetParameters())
{
if (!visited.Contains(parameter))
{
visited.Add(parameter);
}
}
foreach (var child in construct.GetConstructs(false))
{
GetAllParametersRecursive(child, visited, isChild);
}
}
}

private static void WriteLines(int depth, BinaryData data, MemoryStream stream, Resource resource)
{
string indent = new string(' ', depth * 2);
string[] lines = data.ToString().Split(Environment.NewLine.ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
for (int i = 0; i < lines.Length; i++)
{
string lineToWrite = lines[i];

ReadOnlySpan<char> line = lines[i].AsSpan();
int start = 0;
while (line.Length > start && line[start] == ' ')
{
start++;
}
line = line.Slice(start);
int end = line.IndexOf(':');
if (end > 0)
{
// foo: 1
// foo: 'something.url'
string name = line.Slice(0, end).ToString();
if (resource.ParameterOverrides.TryGetValue(name, out var value))
{
lineToWrite = $"{new string(' ', start)}{name}: {value}";
}
}
stream.WriteLine($"{indent}{lineToWrite}");
}
}

BinaryData IPersistableModel<Construct>.Write(ModelReaderWriterOptions options) => (options.Format) switch
{
"bicep" => SerializeModule(options),
"bicep-module" => SerializeModuleReference(options),
_ => throw new FormatException($"Unsupported format {options.Format}")
};

Construct IPersistableModel<Construct>.Create(BinaryData data, ModelReaderWriterOptions options)
{
throw new NotImplementedException();
}

string IPersistableModel<Construct>.GetFormatFromOptions(ModelReaderWriterOptions options) => "bicep";
}
}
5 changes: 5 additions & 0 deletions sdk/provisioning/Azure.Provisioning/src/IConstruct.cs
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,11 @@ public interface IConstruct
/// <param name="recursive">Include all child constructs.</param>
public IEnumerable<Resource> GetResources(bool recursive = true);
/// <summary>
/// Gets all existing resources in the construct.
/// </summary>
/// <param name="recursive">Include all child constructs.</param>
public IEnumerable<Resource> GetExistingResources(bool recursive = true);
/// <summary>
/// Gets all child constructs in the construct.
/// </summary>
/// <param name="recursive">Include all child constructs.</param>
Expand Down
Loading