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

Added core types needed by CloudMachine incubation to Azure.Core.Experimental #37787

Merged
merged 13 commits into from
Jul 25, 2023
Merged
1 change: 1 addition & 0 deletions eng/Packages.Data.props
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@
<PackageReference Update="System.ValueTuple" Version="4.5.0" />
<PackageReference Update="Microsoft.Bcl.AsyncInterfaces" Version="1.1.1" />
<PackageReference Update="Microsoft.CSharp" Version="4.7.0" />
<PackageReference Update="Microsoft.Extensions.Configuration.Abstractions" Version="7.0.0" />

<!-- Azure SDK packages -->
<PackageReference Update="Azure.Communication.Identity" Version="1.2.0" />
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,20 @@
namespace Azure
{
public partial class CloudMachine
{
public CloudMachine(Microsoft.Extensions.Configuration.IConfiguration configuration) { }
public CloudMachine(System.IO.Stream configurationContent) { }
public CloudMachine(string configurationFile = ".\\cloudconfig.json") { }
public string DisplayName { get { throw null; } set { } }
public string Id { get { throw null; } }
public string Region { get { throw null; } }
public string SubscriptionId { get { throw null; } }
public static Azure.CloudMachine Create(string subscriptionId, string region) { throw null; }
[System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)]
public void Save(System.IO.Stream stream) { }
[System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)]
public void Save(string filepath) { }
}
[System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)]
public readonly partial struct Value
{
Expand Down Expand Up @@ -106,3 +121,12 @@ public readonly partial struct Value
[System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]public bool TryGetValue<T>(out T value) { throw null; }
}
}
namespace Azure.Core
{
[System.AttributeUsageAttribute(System.AttributeTargets.Assembly | System.AttributeTargets.Class, Inherited=false, AllowMultiple=true)]
public partial class ProvisionableTemplateAttribute : System.Attribute
{
public ProvisionableTemplateAttribute(string resourceName) { }
public string ResourceName { get { throw null; } }
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,20 @@
namespace Azure
{
public partial class CloudMachine
{
public CloudMachine(Microsoft.Extensions.Configuration.IConfiguration configuration) { }
public CloudMachine(System.IO.Stream configurationContent) { }
public CloudMachine(string configurationFile = ".\\cloudconfig.json") { }
public string DisplayName { get { throw null; } set { } }
public string Id { get { throw null; } }
public string Region { get { throw null; } }
public string SubscriptionId { get { throw null; } }
public static Azure.CloudMachine Create(string subscriptionId, string region) { throw null; }
[System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)]
public void Save(System.IO.Stream stream) { }
[System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)]
public void Save(string filepath) { }
}
[System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)]
public readonly partial struct Value
{
Expand Down Expand Up @@ -106,3 +121,12 @@ public readonly partial struct Value
[System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]public bool TryGetValue<T>(out T value) { throw null; }
}
}
namespace Azure.Core
{
[System.AttributeUsageAttribute(System.AttributeTargets.Assembly | System.AttributeTargets.Class, Inherited=false, AllowMultiple=true)]
public partial class ProvisionableTemplateAttribute : System.Attribute
{
public ProvisionableTemplateAttribute(string resourceName) { }
public string ResourceName { get { throw null; } }
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,20 @@
namespace Azure
{
public partial class CloudMachine
{
public CloudMachine(Microsoft.Extensions.Configuration.IConfiguration configuration) { }
public CloudMachine(System.IO.Stream configurationContent) { }
public CloudMachine(string configurationFile = ".\\cloudconfig.json") { }
public string DisplayName { get { throw null; } set { } }
public string Id { get { throw null; } }
public string Region { get { throw null; } }
public string SubscriptionId { get { throw null; } }
public static Azure.CloudMachine Create(string subscriptionId, string region) { throw null; }
[System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)]
public void Save(System.IO.Stream stream) { }
[System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)]
public void Save(string filepath) { }
}
[System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)]
public readonly partial struct Value
{
Expand Down Expand Up @@ -106,3 +121,12 @@ public readonly partial struct Value
[System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]public bool TryGetValue<T>(out T value) { throw null; }
}
}
namespace Azure.Core
{
[System.AttributeUsageAttribute(System.AttributeTargets.Assembly | System.AttributeTargets.Class, Inherited=false, AllowMultiple=true)]
public partial class ProvisionableTemplateAttribute : System.Attribute
{
public ProvisionableTemplateAttribute(string resourceName) { }
public string ResourceName { get { throw null; } }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
<ItemGroup>
<PackageReference Include="Azure.Core" />
<PackageReference Include="Microsoft.CSharp" />
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions"/>
</ItemGroup>

<ItemGroup>
Expand Down
253 changes: 253 additions & 0 deletions sdk/core/Azure.Core.Experimental/src/CloudMachine.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,253 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using System;
using System.ComponentModel;
using System.IO;
using System.Text.Json;
using Microsoft.Extensions.Configuration;

namespace Azure
{
/// <summary>
/// Stores configuration of a CloudMachine.
/// </summary>
public class CloudMachine
{
/// <summary>
/// Unique identifier of a CloudMachine. It's the name of the resource group of all the CloudMachine resources.
/// </summary>
public string Id { get; private set; }

/// <summary>
/// Friendly name of CloudMachine. It's stored as a Tag of all Azure resources associated with the machine.
/// </summary>
public string DisplayName { get; set; }

/// <summary>
/// Azure subscription ID.
/// </summary>
public string SubscriptionId { get; private set; }

/// <summary>
/// Azure region, e.g. westus2
/// </summary>
public string Region { get; private set; }

private int Version { get; set; }

/// <summary>
/// Creates a new CloudMachine
/// </summary>
/// <param name="subscriptionId"></param>
/// <param name="region"></param>
/// <returns></returns>
/// <remarks>DisplayName is initialized to id. It can be changed by setting the DisplayName property.</remarks>
public static CloudMachine Create(string subscriptionId, string region)
{
if (string.IsNullOrEmpty(subscriptionId)) throw new ArgumentNullException(nameof(subscriptionId));
if (string.IsNullOrEmpty(region)) throw new ArgumentNullException(nameof(region));

var id = GenerateCloudMachineId();
var defaultDisplayName = $"{id}@{DateTime.UtcNow:d}";
var cm = new CloudMachine(id, defaultDisplayName, subscriptionId, region, 1);
return cm;
}

/// <summary>
/// Loads CloudMachine settings from configurationFile
/// </summary>
/// <param name="configurationFile"></param>
/// <exception cref="InvalidCloudMachineConfigurationException"></exception>
public CloudMachine(string configurationFile = ".\\cloudconfig.json")
{
try
{
byte[] configurationContent = File.ReadAllBytes(configurationFile);
var document = JsonDocument.Parse(configurationContent);
JsonElement json = document.RootElement.GetProperty("CloudMachine");

Id = ReaderString(json, "id", configurationFile);
SubscriptionId = ReaderString(json, "subscriptionId", configurationFile);
Region = ReaderString(json, "region", configurationFile);
DisplayName = ReaderString(json, "name", configurationFile);
Version = ReaderInt32(json, "version", configurationFile);
}
catch (InvalidCloudMachineConfigurationException)
{
throw;
}
catch (Exception e)
{
throw new InvalidCloudMachineConfigurationException(configurationFile, setting: null, e);
}
}

/// <summary>
/// Loads CloudMachine settings from stream.
/// </summary>
/// <param name="configurationContent"></param>
/// <exception cref="InvalidCloudMachineConfigurationException"></exception>
public CloudMachine(Stream configurationContent)
{
try
{
var document = JsonDocument.Parse(configurationContent);
JsonElement json = document.RootElement.GetProperty("CloudMachine");

Id = ReaderString(json, "id", nameof(configurationContent));
SubscriptionId = ReaderString(json, "subscriptionId", nameof(configurationContent));
Region = ReaderString(json, "region", nameof(configurationContent));
DisplayName = ReaderString(json, "name", nameof(configurationContent));
Version = ReaderInt32(json, "version", nameof(configurationContent));
}
catch (InvalidCloudMachineConfigurationException)
{
throw;
}
catch (Exception e)
{
throw new InvalidCloudMachineConfigurationException(nameof(configurationContent), setting: null, e);
}
}

/// <summary>
/// Loads CloudMachine settings from configuration system
/// </summary>
public CloudMachine(IConfiguration configuration)
{
try {
Id = ReadString(configuration, "CloudMachine:id");
SubscriptionId = ReadString(configuration, "CloudMachine:subscriptionId");
Region = ReadString(configuration, "CloudMachine:region");
DisplayName = ReadString(configuration, "CloudMachine:name");
Version = ReadInt32(configuration, "CloudMachine:version");
}
catch (InvalidCloudMachineConfigurationException) {
throw;
}
catch (Exception e) {
throw new InvalidCloudMachineConfigurationException(nameof(IConfiguration), setting: null, e);
}
}

private CloudMachine(string id, string displayName, string subscriptionId, string region, int version)
{
if (string.IsNullOrEmpty(id)) throw new ArgumentNullException(nameof(id));
if (string.IsNullOrEmpty(displayName)) throw new ArgumentNullException(nameof(displayName));
if (string.IsNullOrEmpty(subscriptionId)) throw new ArgumentNullException(nameof(subscriptionId));
if (string.IsNullOrEmpty(region)) throw new ArgumentNullException(nameof(region));
if (version<0) throw new ArgumentOutOfRangeException(nameof(version));

Id = id;
DisplayName = displayName;
SubscriptionId = subscriptionId;
Region = region;
Version = version;
}

/// <summary>
/// Save CloudMachine configuration to a stream
/// </summary>
/// <param name="stream"></param>
[EditorBrowsable(EditorBrowsableState.Never)]
public void Save(Stream stream)
{
var options = new JsonWriterOptions() { Indented = true };
using var json = new Utf8JsonWriter(stream, options);
json.WriteStartObject();
json.WriteStartObject("CloudMachine");
json.WriteString("id", Id);
json.WriteString("name", DisplayName);
json.WriteString("subscriptionId", SubscriptionId);
json.WriteString("region", Region);
json.WriteNumber("version", Version);
json.WriteEndObject();
json.WriteEndObject();
json.Flush();
}

/// <summary>
/// Save CloudMachine configuration to a file
/// </summary>
/// <param name="filepath"></param>
[EditorBrowsable(EditorBrowsableState.Never)]
public void Save(string filepath)
{
using var stream = File.OpenWrite(filepath);
Save(stream);
}

private static string ReaderString(JsonElement json, string key, string configurationStoreDisplayName)
{
try {
var value = json.GetProperty(key).GetString()!;
if (string.IsNullOrEmpty(value)) throw new ArgumentNullException(key);
return value;
}
catch (Exception e) {
throw new InvalidCloudMachineConfigurationException(configurationStoreDisplayName, key, e);
}
}
private static int ReaderInt32(JsonElement json, string key, string configurationStoreDisplayName)
{
try {
var value = json.GetProperty(key).GetInt32();
return value;
}
catch (Exception e) {
throw new InvalidCloudMachineConfigurationException(configurationStoreDisplayName, key, e);
}
}

private static string ReadString(IConfiguration configuration, string key)
{
var value = configuration[key]!;
if (string.IsNullOrEmpty(value)) throw new ArgumentNullException(key);
return value;
}
private static int ReadInt32(IConfiguration configuration, string key)
{
var valueText = configuration[key];
if (string.IsNullOrEmpty(valueText)) throw new ArgumentNullException(key);
var value = int.Parse(valueText);
return value;
}

private static string GenerateCloudMachineId()
{
var guid = Guid.NewGuid();
var guidString = guid.ToString("N");
var cnId = "cm" + guidString.Substring(0, 15); // we can increase it to 20, but the template name cannot be that long
return cnId;
}

// helper to to throw the right exception
private static Stream OpenStream(string configurationFile)
{
try
{
return File.OpenRead(configurationFile);
}
catch (Exception e)
{
throw new InvalidCloudMachineConfigurationException(configurationFile, setting: null, e);
}
}

internal class InvalidCloudMachineConfigurationException : InvalidOperationException
{
public InvalidCloudMachineConfigurationException(string configurationStoreDisplayName, string? setting, Exception innerException) :
base(CreateMessage(configurationStoreDisplayName, setting), innerException)
{ }

public static string CreateMessage(string configurationStoreDisplayName, string? setting)
{
if (setting != null)
return $"ERROR: Configuration setting {setting} not found in {configurationStoreDisplayName} or invalid format.";
else
return $"ERROR: Configuration store {configurationStoreDisplayName} not found or invalid format.";
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using System;

namespace Azure.Core
{
/// <summary>
/// Attribute used to describe a deployment template.
/// </summary>
[AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Class, Inherited = false, AllowMultiple = true)]
public class ProvisionableTemplateAttribute : Attribute
{
/// <summary>
/// Deployment teplate stored in resources.
/// </summary>
/// <param name="resourceName"></param>
public ProvisionableTemplateAttribute(string resourceName)
=> ResourceName = resourceName;

/// <summary>
/// Name of assembly resource file containing a deployment template.
/// </summary>
public string ResourceName { get; private set; }
}
}
Loading