diff --git a/sdk/cloudmachine/Azure.CloudMachine.All/src/Azure.CloudMachine.All.csproj b/sdk/cloudmachine/Azure.CloudMachine.All/src/Azure.CloudMachine.All.csproj index 406fe8cbb1add..38cea841c6953 100644 --- a/sdk/cloudmachine/Azure.CloudMachine.All/src/Azure.CloudMachine.All.csproj +++ b/sdk/cloudmachine/Azure.CloudMachine.All/src/Azure.CloudMachine.All.csproj @@ -10,7 +10,7 @@ - + diff --git a/sdk/cloudmachine/Azure.CloudMachine.Web/src/CloudMachineExtensions.cs b/sdk/cloudmachine/Azure.CloudMachine.Web/src/CloudMachineExtensions.cs index a2c501a68cd2e..bec28911b0ff1 100644 --- a/sdk/cloudmachine/Azure.CloudMachine.Web/src/CloudMachineExtensions.cs +++ b/sdk/cloudmachine/Azure.CloudMachine.Web/src/CloudMachineExtensions.cs @@ -75,7 +75,7 @@ private static RequestDelegate CreateRequestDelegate(T service, MethodInfo im ParameterInfo[] parameters = interfaceMethod!.GetParameters(); object[] implementationArguments = new object[parameters.Length]; - foreach (var parameter in parameters) + foreach (ParameterInfo parameter in parameters) { implementationArguments[0] = await CreateArgumentAsync(parameter, request).ConfigureAwait(false); } @@ -128,13 +128,13 @@ private static async ValueTask CreateArgumentAsync(ParameterInfo paramet if (parameterType == typeof(byte[])) { - var bd = await BinaryData.FromStreamAsync(request.Body).ConfigureAwait(false); + BinaryData bd = await BinaryData.FromStreamAsync(request.Body).ConfigureAwait(false); return bd.ToArray(); } if (parameterType == typeof(BinaryData)) { string? contentType = request.ContentType; - var bd = await BinaryData.FromStreamAsync(request.Body, contentType).ConfigureAwait(false); + BinaryData bd = await BinaryData.FromStreamAsync(request.Body, contentType).ConfigureAwait(false); return bd; } if (parameterType == typeof(string)) @@ -164,10 +164,10 @@ private static async ValueTask CreateArgumentAsync(ParameterInfo paramet // TODO: this is a hack. We should use MRW private static object DeserializeModel(Type modelType, Stream stream) { - var fromJson = modelType.GetMethod("FromJson", BindingFlags.Static); + MethodInfo? fromJson = modelType.GetMethod("FromJson", BindingFlags.Static); if (fromJson == default) throw new InvalidOperationException($"{modelType} does not provide FromJson static method"); - object? deserialized = fromJson.Invoke(null, new object[] { stream }); + object? deserialized = fromJson.Invoke(null, [stream]); if (deserialized == default) throw new InvalidOperationException($"Failed to deserialize {modelType}"); return deserialized; diff --git a/sdk/cloudmachine/Azure.CloudMachine/api/Azure.CloudMachine.net8.0.cs b/sdk/cloudmachine/Azure.CloudMachine/api/Azure.CloudMachine.net8.0.cs index 2bfb5ef447534..1e6b182bb3f11 100644 --- a/sdk/cloudmachine/Azure.CloudMachine/api/Azure.CloudMachine.net8.0.cs +++ b/sdk/cloudmachine/Azure.CloudMachine/api/Azure.CloudMachine.net8.0.cs @@ -1,26 +1,67 @@ +namespace Azure +{ + public partial class RestCallFailedException : System.Exception + { + public RestCallFailedException(string message, System.ClientModel.Primitives.PipelineResponse response) { } + } + public partial class RestClient + { + public RestClient() { } + public RestClient(System.ClientModel.Primitives.PipelinePolicy auth) { } + public static Azure.RestClient Shared { get { throw null; } } + public System.ClientModel.Primitives.PipelineMessage Create(string method, System.Uri uri) { throw null; } + public System.ClientModel.Primitives.PipelineResponse Get(string uri, System.ClientModel.Primitives.RequestOptions options = null) { throw null; } + public System.ClientModel.Primitives.PipelineResponse Patch(string uri, System.ClientModel.BinaryContent content, System.ClientModel.Primitives.RequestOptions options = null) { throw null; } + public System.ClientModel.Primitives.PipelineResponse Post(string uri, System.ClientModel.BinaryContent content, System.ClientModel.Primitives.RequestOptions options = null) { throw null; } + public System.ClientModel.Primitives.PipelineResponse Put(string uri, System.ClientModel.BinaryContent content, System.ClientModel.Primitives.RequestOptions options = null) { throw null; } + public System.ClientModel.Primitives.PipelineResponse Send(System.ClientModel.Primitives.PipelineMessage message, System.ClientModel.Primitives.RequestOptions options = null) { throw null; } + } + public partial class RestClientOptions : System.ClientModel.Primitives.ClientPipelineOptions + { + public RestClientOptions() { } + } +} +namespace Azure.AI.OpenAI +{ + public partial class TokenCredentialAuthenticationPolicy : System.ClientModel.Primitives.PipelinePolicy + { + public TokenCredentialAuthenticationPolicy(Azure.Core.TokenCredential credential, System.Collections.Generic.IEnumerable scopes, System.TimeSpan? refreshOffset = default(System.TimeSpan?)) { } + public override void Process(System.ClientModel.Primitives.PipelineMessage message, System.Collections.Generic.IReadOnlyList pipeline, int currentIndex) { } + public override System.Threading.Tasks.ValueTask ProcessAsync(System.ClientModel.Primitives.PipelineMessage message, System.Collections.Generic.IReadOnlyList pipeline, int currentIndex) { throw null; } + } +} namespace Azure.CloudMachine { public partial class CloudMachineClient : Azure.CloudMachine.CloudMachineWorkspace { - protected CloudMachineClient() : base (default(Azure.Core.TokenCredential), default(Microsoft.Extensions.Configuration.IConfiguration)) { } - public CloudMachineClient(Azure.Core.TokenCredential credential = null, Microsoft.Extensions.Configuration.IConfiguration configuration = null) : base (default(Azure.Core.TokenCredential), default(Microsoft.Extensions.Configuration.IConfiguration)) { } + protected CloudMachineClient() : base (default(Azure.Core.TokenCredential), default(Microsoft.Extensions.Configuration.IConfiguration), default(System.Collections.Generic.IEnumerable)) { } + public CloudMachineClient(Azure.Core.TokenCredential credential = null, Microsoft.Extensions.Configuration.IConfiguration configuration = null, System.Collections.Generic.IEnumerable connections = null) : base (default(Azure.Core.TokenCredential), default(Microsoft.Extensions.Configuration.IConfiguration), default(System.Collections.Generic.IEnumerable)) { } public Azure.CloudMachine.MessagingServices Messaging { get { throw null; } } public Azure.CloudMachine.StorageServices Storage { get { throw null; } } } public partial class CloudMachineWorkspace : Azure.Core.ClientWorkspace { - public CloudMachineWorkspace(Azure.Core.TokenCredential credential = null, Microsoft.Extensions.Configuration.IConfiguration configuration = null) { } + public CloudMachineWorkspace(Azure.Core.TokenCredential credential = null, Microsoft.Extensions.Configuration.IConfiguration configuration = null, System.Collections.Generic.IEnumerable connections = null) : base (default(Azure.Core.TokenCredential)) { } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + public Azure.CloudMachine.ConnectionCollection Connections { get { throw null; } } [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] public string Id { get { throw null; } } [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] public override bool Equals(object obj) { throw null; } [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] - public override Azure.Core.ClientConnectionOptions GetConnectionOptions(System.Type clientType, string instanceId) { throw null; } + public override Azure.Core.ClientConnection GetConnectionOptions(string connectionId) { throw null; } [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] public override int GetHashCode() { throw null; } [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + public static string ReadOrCreateCloudMachineId() { throw null; } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] public override string ToString() { throw null; } } + public partial class ConnectionCollection : System.Collections.ObjectModel.KeyedCollection + { + public ConnectionCollection() { } + protected override string GetKeyForItem(Azure.Core.ClientConnection item) { throw null; } + } [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)] public readonly partial struct MessagingServices { @@ -39,6 +80,7 @@ internal StorageFile() { } public void Delete() { } public System.Threading.Tasks.Task DeleteAsync() { throw null; } public System.BinaryData Download() { throw null; } + public System.Threading.Tasks.Task DownloadAsync() { throw null; } [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] public override bool Equals(object obj) { throw null; } [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] @@ -56,12 +98,14 @@ public void Delete(string path) { } public System.Threading.Tasks.Task DeleteAsync(string path) { throw null; } public System.BinaryData Download(string path) { throw null; } public System.Threading.Tasks.Task DownloadAsync(string path) { throw null; } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + public Azure.Storage.Blobs.BlobContainerClient GetContainer(string containerName = null) { throw null; } public string Upload(System.BinaryData data, string name = null, bool overwrite = false) { throw null; } public string Upload(System.IO.Stream fileStream, string name = null, string contentType = null, bool overwrite = false) { throw null; } public System.Threading.Tasks.Task UploadAsync(System.BinaryData data, string name = null, bool overwrite = false) { throw null; } public System.Threading.Tasks.Task UploadAsync(System.IO.Stream fileStream, string name = null, string contentType = null, bool overwrite = false) { throw null; } - public string UploadJson(object json, string name = null, bool overwrite = false) { throw null; } - public System.Threading.Tasks.Task UploadJsonAsync(object json, string name = null, bool overwrite = false) { throw null; } + public string UploadJson(object serializable, string name = null, bool overwrite = false) { throw null; } + public System.Threading.Tasks.Task UploadJsonAsync(object serializable, string name = null, bool overwrite = false) { throw null; } public void WhenUploaded(System.Action function) { } public void WhenUploaded(System.Action function) { } } @@ -80,7 +124,8 @@ public static partial class AzureOpenAIExtensions public static void Add(this System.Collections.Generic.List messages, OpenAI.Chat.ChatCompletion completion) { } public static void Add(this System.Collections.Generic.List messages, System.Collections.Generic.IEnumerable entries) { } public static string AsText(this OpenAI.Chat.ChatCompletion completion) { throw null; } - public static string AsText(this OpenAI.Chat.ChatMessageContent completion) { throw null; } + public static string AsText(this OpenAI.Chat.ChatMessageContent content) { throw null; } + public static string AsText(this System.ClientModel.ClientResult completionResult) { throw null; } public static OpenAI.Chat.ChatClient GetOpenAIChatClient(this Azure.Core.ClientWorkspace workspace) { throw null; } public static OpenAI.Embeddings.EmbeddingClient GetOpenAIEmbeddingsClient(this Azure.Core.ClientWorkspace workspace) { throw null; } public static void Trim(this System.Collections.Generic.List messages) { } @@ -94,11 +139,6 @@ public void Add(System.Type functions) { } public string Call(OpenAI.Chat.ChatToolCall call) { throw null; } public string Call(string name, object[] arguments) { throw null; } public System.Collections.Generic.IEnumerable CallAll(System.Collections.Generic.IEnumerable toolCalls) { throw null; } - protected string ClrToJsonTypeUtf16(System.Type clrType) { throw null; } - protected System.ReadOnlySpan ClrToJsonTypeUtf8(System.Type clrType) { throw null; } - protected virtual string GetMethodInfoToDescription(System.Reflection.MethodInfo function) { throw null; } - protected virtual string GetMethodInfoToName(System.Reflection.MethodInfo function) { throw null; } - protected virtual string GetParameterInfoToDescription(System.Reflection.ParameterInfo parameter) { throw null; } public static implicit operator OpenAI.Chat.ChatCompletionOptions (Azure.CloudMachine.OpenAI.ChatTools tools) { throw null; } } public partial class EmbeddingsVectorbase @@ -135,36 +175,40 @@ protected VectorbaseStore() { } } namespace Azure.Core { + public enum ClientAuthenticationMethod + { + EntraId = 0, + ApiKey = 1, + Subclient = 2, + } public partial class ClientCache { public ClientCache() { } public T Get(System.Func value, string id = null) where T : class { throw null; } } - public enum ClientConnectionKind - { - EntraId = 0, - ApiKey = 1, - OutOfBand = 2, - } [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)] - public readonly partial struct ClientConnectionOptions + public readonly partial struct ClientConnection { private readonly object _dummy; private readonly int _dummyPrimitive; - public ClientConnectionOptions(string subclientId) { throw null; } - public ClientConnectionOptions(System.Uri endpoint, Azure.Core.TokenCredential credential) { throw null; } - public ClientConnectionOptions(System.Uri endpoint, string apiKey) { throw null; } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + public ClientConnection() { throw null; } + public ClientConnection(string id, string locator, Azure.Core.ClientAuthenticationMethod auth = Azure.Core.ClientAuthenticationMethod.EntraId) { throw null; } + public ClientConnection(string id, string locator, string apiKey) { throw null; } public string ApiKeyCredential { get { throw null; } } - public Azure.Core.ClientConnectionKind ConnectionKind { get { throw null; } } - public System.Uri Endpoint { get { throw null; } } + public Azure.Core.ClientAuthenticationMethod Authentication { get { throw null; } } public string Id { get { throw null; } } - public Azure.Core.TokenCredential TokenCredential { get { throw null; } } + public string Locator { get { throw null; } } + public override string ToString() { throw null; } + public System.Uri ToUri() { throw null; } } public abstract partial class ClientWorkspace { - protected ClientWorkspace() { } + protected ClientWorkspace(Azure.Core.TokenCredential credential) { } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + public Azure.Core.TokenCredential Credential { get { throw null; } } [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] public Azure.Core.ClientCache Subclients { get { throw null; } } - public abstract Azure.Core.ClientConnectionOptions GetConnectionOptions(System.Type clientType, string instanceId = null); + public abstract Azure.Core.ClientConnection GetConnectionOptions(string connectionId); } } diff --git a/sdk/cloudmachine/Azure.CloudMachine/api/Azure.CloudMachine.netstandard2.0.cs b/sdk/cloudmachine/Azure.CloudMachine/api/Azure.CloudMachine.netstandard2.0.cs index 2bfb5ef447534..1e6b182bb3f11 100644 --- a/sdk/cloudmachine/Azure.CloudMachine/api/Azure.CloudMachine.netstandard2.0.cs +++ b/sdk/cloudmachine/Azure.CloudMachine/api/Azure.CloudMachine.netstandard2.0.cs @@ -1,26 +1,67 @@ +namespace Azure +{ + public partial class RestCallFailedException : System.Exception + { + public RestCallFailedException(string message, System.ClientModel.Primitives.PipelineResponse response) { } + } + public partial class RestClient + { + public RestClient() { } + public RestClient(System.ClientModel.Primitives.PipelinePolicy auth) { } + public static Azure.RestClient Shared { get { throw null; } } + public System.ClientModel.Primitives.PipelineMessage Create(string method, System.Uri uri) { throw null; } + public System.ClientModel.Primitives.PipelineResponse Get(string uri, System.ClientModel.Primitives.RequestOptions options = null) { throw null; } + public System.ClientModel.Primitives.PipelineResponse Patch(string uri, System.ClientModel.BinaryContent content, System.ClientModel.Primitives.RequestOptions options = null) { throw null; } + public System.ClientModel.Primitives.PipelineResponse Post(string uri, System.ClientModel.BinaryContent content, System.ClientModel.Primitives.RequestOptions options = null) { throw null; } + public System.ClientModel.Primitives.PipelineResponse Put(string uri, System.ClientModel.BinaryContent content, System.ClientModel.Primitives.RequestOptions options = null) { throw null; } + public System.ClientModel.Primitives.PipelineResponse Send(System.ClientModel.Primitives.PipelineMessage message, System.ClientModel.Primitives.RequestOptions options = null) { throw null; } + } + public partial class RestClientOptions : System.ClientModel.Primitives.ClientPipelineOptions + { + public RestClientOptions() { } + } +} +namespace Azure.AI.OpenAI +{ + public partial class TokenCredentialAuthenticationPolicy : System.ClientModel.Primitives.PipelinePolicy + { + public TokenCredentialAuthenticationPolicy(Azure.Core.TokenCredential credential, System.Collections.Generic.IEnumerable scopes, System.TimeSpan? refreshOffset = default(System.TimeSpan?)) { } + public override void Process(System.ClientModel.Primitives.PipelineMessage message, System.Collections.Generic.IReadOnlyList pipeline, int currentIndex) { } + public override System.Threading.Tasks.ValueTask ProcessAsync(System.ClientModel.Primitives.PipelineMessage message, System.Collections.Generic.IReadOnlyList pipeline, int currentIndex) { throw null; } + } +} namespace Azure.CloudMachine { public partial class CloudMachineClient : Azure.CloudMachine.CloudMachineWorkspace { - protected CloudMachineClient() : base (default(Azure.Core.TokenCredential), default(Microsoft.Extensions.Configuration.IConfiguration)) { } - public CloudMachineClient(Azure.Core.TokenCredential credential = null, Microsoft.Extensions.Configuration.IConfiguration configuration = null) : base (default(Azure.Core.TokenCredential), default(Microsoft.Extensions.Configuration.IConfiguration)) { } + protected CloudMachineClient() : base (default(Azure.Core.TokenCredential), default(Microsoft.Extensions.Configuration.IConfiguration), default(System.Collections.Generic.IEnumerable)) { } + public CloudMachineClient(Azure.Core.TokenCredential credential = null, Microsoft.Extensions.Configuration.IConfiguration configuration = null, System.Collections.Generic.IEnumerable connections = null) : base (default(Azure.Core.TokenCredential), default(Microsoft.Extensions.Configuration.IConfiguration), default(System.Collections.Generic.IEnumerable)) { } public Azure.CloudMachine.MessagingServices Messaging { get { throw null; } } public Azure.CloudMachine.StorageServices Storage { get { throw null; } } } public partial class CloudMachineWorkspace : Azure.Core.ClientWorkspace { - public CloudMachineWorkspace(Azure.Core.TokenCredential credential = null, Microsoft.Extensions.Configuration.IConfiguration configuration = null) { } + public CloudMachineWorkspace(Azure.Core.TokenCredential credential = null, Microsoft.Extensions.Configuration.IConfiguration configuration = null, System.Collections.Generic.IEnumerable connections = null) : base (default(Azure.Core.TokenCredential)) { } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + public Azure.CloudMachine.ConnectionCollection Connections { get { throw null; } } [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] public string Id { get { throw null; } } [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] public override bool Equals(object obj) { throw null; } [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] - public override Azure.Core.ClientConnectionOptions GetConnectionOptions(System.Type clientType, string instanceId) { throw null; } + public override Azure.Core.ClientConnection GetConnectionOptions(string connectionId) { throw null; } [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] public override int GetHashCode() { throw null; } [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + public static string ReadOrCreateCloudMachineId() { throw null; } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] public override string ToString() { throw null; } } + public partial class ConnectionCollection : System.Collections.ObjectModel.KeyedCollection + { + public ConnectionCollection() { } + protected override string GetKeyForItem(Azure.Core.ClientConnection item) { throw null; } + } [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)] public readonly partial struct MessagingServices { @@ -39,6 +80,7 @@ internal StorageFile() { } public void Delete() { } public System.Threading.Tasks.Task DeleteAsync() { throw null; } public System.BinaryData Download() { throw null; } + public System.Threading.Tasks.Task DownloadAsync() { throw null; } [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] public override bool Equals(object obj) { throw null; } [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] @@ -56,12 +98,14 @@ public void Delete(string path) { } public System.Threading.Tasks.Task DeleteAsync(string path) { throw null; } public System.BinaryData Download(string path) { throw null; } public System.Threading.Tasks.Task DownloadAsync(string path) { throw null; } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + public Azure.Storage.Blobs.BlobContainerClient GetContainer(string containerName = null) { throw null; } public string Upload(System.BinaryData data, string name = null, bool overwrite = false) { throw null; } public string Upload(System.IO.Stream fileStream, string name = null, string contentType = null, bool overwrite = false) { throw null; } public System.Threading.Tasks.Task UploadAsync(System.BinaryData data, string name = null, bool overwrite = false) { throw null; } public System.Threading.Tasks.Task UploadAsync(System.IO.Stream fileStream, string name = null, string contentType = null, bool overwrite = false) { throw null; } - public string UploadJson(object json, string name = null, bool overwrite = false) { throw null; } - public System.Threading.Tasks.Task UploadJsonAsync(object json, string name = null, bool overwrite = false) { throw null; } + public string UploadJson(object serializable, string name = null, bool overwrite = false) { throw null; } + public System.Threading.Tasks.Task UploadJsonAsync(object serializable, string name = null, bool overwrite = false) { throw null; } public void WhenUploaded(System.Action function) { } public void WhenUploaded(System.Action function) { } } @@ -80,7 +124,8 @@ public static partial class AzureOpenAIExtensions public static void Add(this System.Collections.Generic.List messages, OpenAI.Chat.ChatCompletion completion) { } public static void Add(this System.Collections.Generic.List messages, System.Collections.Generic.IEnumerable entries) { } public static string AsText(this OpenAI.Chat.ChatCompletion completion) { throw null; } - public static string AsText(this OpenAI.Chat.ChatMessageContent completion) { throw null; } + public static string AsText(this OpenAI.Chat.ChatMessageContent content) { throw null; } + public static string AsText(this System.ClientModel.ClientResult completionResult) { throw null; } public static OpenAI.Chat.ChatClient GetOpenAIChatClient(this Azure.Core.ClientWorkspace workspace) { throw null; } public static OpenAI.Embeddings.EmbeddingClient GetOpenAIEmbeddingsClient(this Azure.Core.ClientWorkspace workspace) { throw null; } public static void Trim(this System.Collections.Generic.List messages) { } @@ -94,11 +139,6 @@ public void Add(System.Type functions) { } public string Call(OpenAI.Chat.ChatToolCall call) { throw null; } public string Call(string name, object[] arguments) { throw null; } public System.Collections.Generic.IEnumerable CallAll(System.Collections.Generic.IEnumerable toolCalls) { throw null; } - protected string ClrToJsonTypeUtf16(System.Type clrType) { throw null; } - protected System.ReadOnlySpan ClrToJsonTypeUtf8(System.Type clrType) { throw null; } - protected virtual string GetMethodInfoToDescription(System.Reflection.MethodInfo function) { throw null; } - protected virtual string GetMethodInfoToName(System.Reflection.MethodInfo function) { throw null; } - protected virtual string GetParameterInfoToDescription(System.Reflection.ParameterInfo parameter) { throw null; } public static implicit operator OpenAI.Chat.ChatCompletionOptions (Azure.CloudMachine.OpenAI.ChatTools tools) { throw null; } } public partial class EmbeddingsVectorbase @@ -135,36 +175,40 @@ protected VectorbaseStore() { } } namespace Azure.Core { + public enum ClientAuthenticationMethod + { + EntraId = 0, + ApiKey = 1, + Subclient = 2, + } public partial class ClientCache { public ClientCache() { } public T Get(System.Func value, string id = null) where T : class { throw null; } } - public enum ClientConnectionKind - { - EntraId = 0, - ApiKey = 1, - OutOfBand = 2, - } [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)] - public readonly partial struct ClientConnectionOptions + public readonly partial struct ClientConnection { private readonly object _dummy; private readonly int _dummyPrimitive; - public ClientConnectionOptions(string subclientId) { throw null; } - public ClientConnectionOptions(System.Uri endpoint, Azure.Core.TokenCredential credential) { throw null; } - public ClientConnectionOptions(System.Uri endpoint, string apiKey) { throw null; } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + public ClientConnection() { throw null; } + public ClientConnection(string id, string locator, Azure.Core.ClientAuthenticationMethod auth = Azure.Core.ClientAuthenticationMethod.EntraId) { throw null; } + public ClientConnection(string id, string locator, string apiKey) { throw null; } public string ApiKeyCredential { get { throw null; } } - public Azure.Core.ClientConnectionKind ConnectionKind { get { throw null; } } - public System.Uri Endpoint { get { throw null; } } + public Azure.Core.ClientAuthenticationMethod Authentication { get { throw null; } } public string Id { get { throw null; } } - public Azure.Core.TokenCredential TokenCredential { get { throw null; } } + public string Locator { get { throw null; } } + public override string ToString() { throw null; } + public System.Uri ToUri() { throw null; } } public abstract partial class ClientWorkspace { - protected ClientWorkspace() { } + protected ClientWorkspace(Azure.Core.TokenCredential credential) { } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + public Azure.Core.TokenCredential Credential { get { throw null; } } [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] public Azure.Core.ClientCache Subclients { get { throw null; } } - public abstract Azure.Core.ClientConnectionOptions GetConnectionOptions(System.Type clientType, string instanceId = null); + public abstract Azure.Core.ClientConnection GetConnectionOptions(string connectionId); } } diff --git a/sdk/cloudmachine/Azure.CloudMachine/src/AppConfigHelpers.cs b/sdk/cloudmachine/Azure.CloudMachine/src/AppConfigHelpers.cs new file mode 100644 index 0000000000000..fa4be22d4f3ec --- /dev/null +++ b/sdk/cloudmachine/Azure.CloudMachine/src/AppConfigHelpers.cs @@ -0,0 +1,87 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System.IO; +using System.Text.Json.Nodes; +using System.Text.Json; +using System; + +namespace Azure.CloudMachine; + +internal static class AppConfigHelpers +{ + internal static string ReadOrCreateCloudMachineId() + { + string appsettings = Path.Combine(".", "appsettings.json"); + + string cmid; + if (!File.Exists(appsettings)) + { + cmid = GenerateCloudMachineId(); + + using FileStream file = File.OpenWrite(appsettings); + Utf8JsonWriter writer = new(file); + writer.WriteStartObject(); + writer.WritePropertyName("CloudMachine"u8); + writer.WriteStartObject(); + writer.WriteString("ID"u8, cmid); + writer.WriteEndObject(); + writer.WriteEndObject(); + writer.Flush(); + file.Close(); + return cmid; + } + + using FileStream json = new FileStream(appsettings, FileMode.Open, FileAccess.Read, FileShare.Read); + using JsonDocument jd = JsonDocument.Parse(json); + JsonElement je = jd.RootElement; + // attempt to read CM configuration from existing configuration file + if (je.TryGetProperty("CloudMachine"u8, out JsonElement cm)) + { + if (!cm.TryGetProperty("ID"u8, out JsonElement id)) + { + throw new NotImplementedException(); + } + cmid = id.GetString(); + if (cmid == null) + throw new NotImplementedException(); + return cmid; + } + else + { // add CM configuration to existing file + json.Seek(0, SeekOrigin.Begin); + JsonNode root = JsonNode.Parse(json); + json.Close(); + if (root is null || root is not JsonObject obj) + throw new InvalidOperationException("Existing appsettings.json is not a valid JSON object"); + + var cmProperties = new JsonObject(); + cmid = GenerateCloudMachineId(); + cmProperties.Add("ID", cmid); + obj.Add("CloudMachine", cmProperties); + + using FileStream file = new FileStream(appsettings, FileMode.Open, FileAccess.Write, FileShare.None); + JsonWriterOptions writerOptions = new() + { + Indented = true, + }; + Utf8JsonWriter writer = new(file, writerOptions); + JsonSerializerOptions options = new() + { + WriteIndented = true, + }; + root.WriteTo(writer, options); + writer.Flush(); + } + + return cmid; + } + + 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; + } +} diff --git a/sdk/cloudmachine/Azure.CloudMachine/src/ClientCache.cs b/sdk/cloudmachine/Azure.CloudMachine/src/ClientCache.cs index 921cfcc4ea184..326ef047fbee4 100644 --- a/sdk/cloudmachine/Azure.CloudMachine/src/ClientCache.cs +++ b/sdk/cloudmachine/Azure.CloudMachine/src/ClientCache.cs @@ -12,7 +12,7 @@ namespace Azure.Core; /// public class ClientCache { - private readonly Dictionary<(Type, string), object> _clients = new Dictionary<(Type, string), object>(); + private readonly Dictionary<(Type, string), object> _clients = []; /// /// Gets a client from the cache. @@ -23,7 +23,7 @@ public class ClientCache /// public T Get(Func value, string id = default) where T: class { - var client = (typeof(T), id); + (Type, string) client = (typeof(T), id); lock (_clients) { if (_clients.TryGetValue(client, out object cached)) diff --git a/sdk/cloudmachine/Azure.CloudMachine/src/ClientConnection.cs b/sdk/cloudmachine/Azure.CloudMachine/src/ClientConnection.cs new file mode 100644 index 0000000000000..a360af007cfca --- /dev/null +++ b/sdk/cloudmachine/Azure.CloudMachine/src/ClientConnection.cs @@ -0,0 +1,100 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.ComponentModel; +using System.Text.Json.Serialization; + +namespace Azure.Core; + +/// +/// Represents the connection options for a client. +/// +public readonly struct ClientConnection +{ + /// + /// Do not use this constructor. It is only for the JSON serializer. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public ClientConnection() { } + + /// + /// Initializes a new instance of the struct with the specified endpoint and API key. + /// + /// + /// + /// The API key credential. + public ClientConnection(string id, string locator, string apiKey) + { + Id = id; + Locator = locator; + ApiKeyCredential = apiKey; + Authentication = ClientAuthenticationMethod.ApiKey; + } + + /// + /// Initializes a new instance of the struct with the specified subclient ID. + /// + /// + /// The subclient ID. + /// + public ClientConnection(string id, string locator, ClientAuthenticationMethod auth = ClientAuthenticationMethod.EntraId) + { + Id = id; + Locator = locator; + Authentication = auth; + } + + /// + /// Gets the kind of connection used by the client. + /// + public ClientAuthenticationMethod Authentication { get; } + + /// + /// Gets the key. + /// + public string Id { get; } + + /// + /// This is either URI or name, or something like that. + /// + public string Locator { get; } + + /// + /// Gets the API key credential. + /// + public string ApiKeyCredential { get; } + + /// + /// Converts the connection to a URI. + /// + /// + public Uri ToUri() => new(Locator); + + /// + /// Returns a string representation of the connection. + /// + /// + public override string ToString() => $"{Id} => {Locator}"; +} + +/// +/// Specifies the kind of connection used by the client. +/// +public enum ClientAuthenticationMethod +{ + /// + /// Represents a connection using Entra ID. + /// + EntraId, + + /// + /// Represents a connection using an API key. + /// + ApiKey, + + /// + /// Represents a connection using an out-of-band method. + /// + Subclient +} diff --git a/sdk/cloudmachine/Azure.CloudMachine/src/ClientWorkspace.cs b/sdk/cloudmachine/Azure.CloudMachine/src/ClientWorkspace.cs index 52ebda1f4216a..879e4099206c6 100644 --- a/sdk/cloudmachine/Azure.CloudMachine/src/ClientWorkspace.cs +++ b/sdk/cloudmachine/Azure.CloudMachine/src/ClientWorkspace.cs @@ -1,7 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -using System; using System.ComponentModel; namespace Azure.Core; @@ -13,102 +12,30 @@ namespace Azure.Core; public abstract class ClientWorkspace { /// - /// Retrieves the connection options for a specified client type and instance ID. - /// - /// The type of the client. - /// The instance ID of the client. - /// The connection options for the specified client type and instance ID. - public abstract ClientConnectionOptions GetConnectionOptions(Type clientType, string instanceId = default); - - /// - /// Gets the cache of subclients. - /// - [EditorBrowsable(EditorBrowsableState.Never)] - public ClientCache Subclients { get; } = new ClientCache(); -} - -/// -/// Represents the connection options for a client. -/// -public readonly struct ClientConnectionOptions -{ - /// - /// Initializes a new instance of the struct with the specified endpoint and API key. - /// - /// The endpoint URI. - /// The API key credential. - public ClientConnectionOptions(Uri endpoint, string apiKey) - { - Endpoint = endpoint; - ApiKeyCredential = apiKey; - ConnectionKind = ClientConnectionKind.ApiKey; - } - - /// - /// Initializes a new instance of the struct with the specified endpoint and token credential. - /// - /// The endpoint URI. - /// The token credential. - public ClientConnectionOptions(Uri endpoint, TokenCredential credential) - { - Endpoint = endpoint; - TokenCredential = credential; - ConnectionKind = ClientConnectionKind.EntraId; - } - - /// - /// Initializes a new instance of the struct with the specified subclient ID. + /// Initializes a new instance of the class with the specified token credential. /// - /// The subclient ID. - public ClientConnectionOptions(string subclientId) + /// + protected ClientWorkspace(TokenCredential credential) { - Id = subclientId; - ConnectionKind = ClientConnectionKind.OutOfBand; + Credential = credential; } /// - /// Gets the kind of connection used by the client. - /// - public ClientConnectionKind ConnectionKind { get; } - - /// - /// Gets the endpoint URI. - /// - public Uri Endpoint { get; } - - /// - /// Gets the subclient ID. - /// - public string Id { get; } - - /// - /// Gets the API key credential. + /// Retrieves the connection options for a specified client type and instance ID. /// - public string ApiKeyCredential { get; } + /// + /// The connection options for the specified client type and instance ID. + public abstract ClientConnection GetConnectionOptions(string connectionId); /// /// Gets the token credential. /// - public TokenCredential TokenCredential { get; } -} - -/// -/// Specifies the kind of connection used by the client. -/// -public enum ClientConnectionKind -{ - /// - /// Represents a connection using Entra ID. - /// - EntraId, - - /// - /// Represents a connection using an API key. - /// - ApiKey, + [EditorBrowsable(EditorBrowsableState.Never)] + public TokenCredential Credential { get; } /// - /// Represents a connection using an out-of-band method. + /// Gets the cache of subclients. /// - OutOfBand + [EditorBrowsable(EditorBrowsableState.Never)] + public ClientCache Subclients { get; } = new ClientCache(); } diff --git a/sdk/cloudmachine/Azure.CloudMachine/src/CloudMachineClient.cs b/sdk/cloudmachine/Azure.CloudMachine/src/CloudMachineClient.cs index 1d223f539be13..2d31947889926 100644 --- a/sdk/cloudmachine/Azure.CloudMachine/src/CloudMachineClient.cs +++ b/sdk/cloudmachine/Azure.CloudMachine/src/CloudMachineClient.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. +using System.Collections.Generic; using Azure.Core; using Microsoft.Extensions.Configuration; @@ -25,9 +26,10 @@ protected CloudMachineClient() /// /// The token credential. /// The configuration settings. - public CloudMachineClient(TokenCredential credential = default, IConfiguration configuration = default) + /// + public CloudMachineClient(TokenCredential credential = default, IConfiguration configuration = default, IEnumerable connections = default) #pragma warning restore AZC0007 // DO provide a minimal constructor that takes only the parameters required to connect to the service. - : base(credential, configuration) + : base(credential, configuration, connections) { Messaging = new MessagingServices(this); Storage = new StorageServices(this); diff --git a/sdk/cloudmachine/Azure.CloudMachine/src/CloudMachineWorkspace.cs b/sdk/cloudmachine/Azure.CloudMachine/src/CloudMachineWorkspace.cs index a0f8ecf1acb61..500365b346fcd 100644 --- a/sdk/cloudmachine/Azure.CloudMachine/src/CloudMachineWorkspace.cs +++ b/sdk/cloudmachine/Azure.CloudMachine/src/CloudMachineWorkspace.cs @@ -2,6 +2,8 @@ // Licensed under the MIT License. using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; using System.ComponentModel; using System.Diagnostics.CodeAnalysis; using System.IO; @@ -9,6 +11,7 @@ using System.Text.Json.Nodes; using Azure.Core; using Azure.Identity; +using Azure.Messaging.EventGrid.SystemEvents; using Microsoft.Extensions.Configuration; namespace Azure.CloudMachine; @@ -18,7 +21,11 @@ namespace Azure.CloudMachine; /// public class CloudMachineWorkspace : ClientWorkspace { - private TokenCredential Credential { get; } + /// + /// subclient connections. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public ConnectionCollection Connections { get; } = []; /// /// The cloud machine ID. @@ -31,70 +38,58 @@ public class CloudMachineWorkspace : ClientWorkspace /// /// /// + /// /// [SuppressMessage("Usage", "AZC0007:DO provide a minimal constructor that takes only the parameters required to connect to the service.", Justification = "")] - public CloudMachineWorkspace(TokenCredential credential = default, IConfiguration configuration = default) + public CloudMachineWorkspace(TokenCredential credential = default, IConfiguration configuration = default, IEnumerable connections = default) + : base(BuildCredentail(credential)) { - if (credential != default) + if (connections != default) { - Credential = credential; + Connections.AddRange(connections); } - else + + Id = configuration switch + { + null => AppConfigHelpers.ReadOrCreateCloudMachineId(), + _ => configuration["CloudMachine:ID"] ?? throw new Exception("CloudMachine:ID configuration value missing") + }; + } + + private static TokenCredential BuildCredentail(TokenCredential credential) + { + if (credential == default) { // This environment variable is set by the CloudMachine App Service feature during provisioning. - Credential = Environment.GetEnvironmentVariable("CLOUDMACHINE_MANAGED_IDENTITY_CLIENT_ID") switch + credential = Environment.GetEnvironmentVariable("CLOUDMACHINE_MANAGED_IDENTITY_CLIENT_ID") switch { string clientId when !string.IsNullOrEmpty(clientId) => new ManagedIdentityCredential(clientId), _ => new ChainedTokenCredential(new AzureCliCredential(), new AzureDeveloperCliCredential()) }; } - Id = configuration switch - { - null => ReadOrCreateCmid(), - _ => configuration["CloudMachine:ID"] ?? throw new Exception("CloudMachine:ID configuration value missing") - }; + return credential; } /// /// Retrieves the connection options for a specified client type and instance ID. /// - /// - /// + /// /// /// [EditorBrowsable(EditorBrowsableState.Never)] - public override ClientConnectionOptions GetConnectionOptions(Type clientType, string instanceId) + public override ClientConnection GetConnectionOptions(string connectionId) { - string clientId = clientType.FullName; - if (instanceId != null && instanceId.StartsWith("$")) - clientId = $"{clientType.FullName}{instanceId}"; - - switch (clientId) - { - case "Azure.Security.KeyVault.Secrets.SecretClient": - return new ClientConnectionOptions(new($"https://{Id}.vault.azure.net/"), Credential); - case "Azure.Messaging.ServiceBus.ServiceBusClient": - return new ClientConnectionOptions(new($"https://{Id}.servicebus.windows.net"), Credential); - case "Azure.Messaging.ServiceBus.ServiceBusSender": - return new ClientConnectionOptions(instanceId ?? "cm_servicebus_default_topic"); - case "Azure.Messaging.ServiceBus.ServiceBusProcessor": - return new ClientConnectionOptions("cm_servicebus_default_topic/cm_servicebus_subscription_default"); - case "Azure.Messaging.ServiceBus.ServiceBusProcessor$private": - return new ClientConnectionOptions("cm_servicebus_topic_private/cm_servicebus_subscription_private"); - case "Azure.Storage.Blobs.BlobContainerClient": - return new ClientConnectionOptions(new($"https://{Id}.blob.core.windows.net/{instanceId ?? "default"}"), Credential); - case "Azure.AI.OpenAI.AzureOpenAIClient": - return new ClientConnectionOptions(new($"https://{Id}.openai.azure.com"), Credential); - case "OpenAI.Chat.ChatClient": - return new ClientConnectionOptions($"{Id}_chat"); - case "OpenAI.Embeddings.EmbeddingClient": - return new ClientConnectionOptions($"{Id}_embedding"); - default: - throw new Exception($"unknown client {clientId}"); - } + return Connections[connectionId]; } + /// + /// Reads or creates the cloud machine ID. + /// + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public static string ReadOrCreateCloudMachineId() => AppConfigHelpers.ReadOrCreateCloudMachineId(); + /// [EditorBrowsable(EditorBrowsableState.Never)] public override bool Equals(object obj) => base.Equals(obj); @@ -106,79 +101,4 @@ public override ClientConnectionOptions GetConnectionOptions(Type clientType, st /// [EditorBrowsable(EditorBrowsableState.Never)] public override string ToString() => Id; - - // TODO: Decide if this should live here. - internal static string ReadOrCreateCmid() - { - string appsettings = Path.Combine(".", "appsettings.json"); - - string cmid; - if (!File.Exists(appsettings)) - { - cmid = GenerateCloudMachineId(); - - using FileStream file = File.OpenWrite(appsettings); - Utf8JsonWriter writer = new Utf8JsonWriter(file); - writer.WriteStartObject(); - writer.WritePropertyName("CloudMachine"u8); - writer.WriteStartObject(); - writer.WriteString("ID"u8, cmid); - writer.WriteEndObject(); - writer.WriteEndObject(); - writer.Flush(); - return cmid; - } - - using FileStream json = File.OpenRead(appsettings); - using JsonDocument jd = JsonDocument.Parse(json); - JsonElement je = jd.RootElement; - // attempt to read CM configuration from existing configuration file - if (je.TryGetProperty("CloudMachine"u8, out JsonElement cm)) - { - if (!cm.TryGetProperty("ID"u8, out JsonElement id)) - { - throw new NotImplementedException(); - } - cmid = id.GetString(); - if (cmid == null) - throw new NotImplementedException(); - return cmid; - } - else - { // add CM configuration to existing file - json.Seek(0, SeekOrigin.Begin); - JsonNode root = JsonNode.Parse(json); - json.Close(); - if (root is null || root is not JsonObject obj) - throw new InvalidOperationException("Existing appsettings.json is not a valid JSON object"); - - var cmProperties = new JsonObject(); - cmid = GenerateCloudMachineId(); - cmProperties.Add("ID", cmid); - obj.Add("CloudMachine", cmProperties); - - using FileStream file = File.OpenWrite(appsettings); - JsonWriterOptions writerOptions = new() - { - Indented = true, - }; - Utf8JsonWriter writer = new(file, writerOptions); - JsonSerializerOptions options = new() - { - WriteIndented = true, - }; - root.WriteTo(writer, options); - writer.Flush(); - } - - return cmid; - - 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; - } - } } diff --git a/sdk/cloudmachine/Azure.CloudMachine/src/ConnectionCollection.cs b/sdk/cloudmachine/Azure.CloudMachine/src/ConnectionCollection.cs new file mode 100644 index 0000000000000..1a5087da74e34 --- /dev/null +++ b/sdk/cloudmachine/Azure.CloudMachine/src/ConnectionCollection.cs @@ -0,0 +1,66 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Text.Json; +using System.Text.Json.Serialization; +using Azure.Core; + +namespace Azure.CloudMachine; + +/// +/// Represents the connection options for a client. +/// +[JsonConverter(typeof(ConnectionCollectionConverter))] +public class ConnectionCollection : KeyedCollection +{ + /// + /// Initializes a new instance of the class. + /// + /// + /// + protected override string GetKeyForItem(ClientConnection item) => item.Id; + + internal void AddRange(IEnumerable connections) + { + foreach (ClientConnection connection in connections) + Add(connection); + } +} + +internal class ConnectionCollectionConverter : JsonConverter +{ + public override ConnectionCollection Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + using JsonDocument document = JsonDocument.ParseValue(ref reader); + JsonElement json = document.RootElement; + + ConnectionCollection connections = []; + foreach (JsonElement connectionJson in json.EnumerateArray()) + { + string id = connectionJson.GetProperty("id").GetString(); + string locator = connectionJson.GetProperty("locator").GetString(); + string auth = connectionJson.GetProperty("auth").GetString(); + ClientAuthenticationMethod authMethod = (ClientAuthenticationMethod)Enum.Parse(typeof(ClientAuthenticationMethod), auth); + ClientConnection connection = new ClientConnection(id, locator, authMethod); + connections.Add(connection); + } + return connections; + } + + public override void Write(Utf8JsonWriter writer, ConnectionCollection value, JsonSerializerOptions options) + { + writer.WriteStartArray(); + foreach (ClientConnection connection in value) + { + writer.WriteStartObject(); + writer.WriteString("id", connection.Id); + writer.WriteString("locator", connection.Locator); + writer.WriteString("auth", connection.Authentication.ToString()); + writer.WriteEndObject(); + } + writer.WriteEndArray(); + } +} diff --git a/sdk/provisioning/Azure.Provisioning.CloudMachine/src/Core/LoggingPolicy.cs b/sdk/cloudmachine/Azure.CloudMachine/src/Core/LoggingPolicy.cs similarity index 89% rename from sdk/provisioning/Azure.Provisioning.CloudMachine/src/Core/LoggingPolicy.cs rename to sdk/cloudmachine/Azure.CloudMachine/src/Core/LoggingPolicy.cs index fd05bf879bda3..12fefe1e4aa27 100644 --- a/sdk/provisioning/Azure.Provisioning.CloudMachine/src/Core/LoggingPolicy.cs +++ b/sdk/cloudmachine/Azure.CloudMachine/src/Core/LoggingPolicy.cs @@ -13,7 +13,7 @@ internal class LoggingPolicy : PipelinePolicy { public LoggingPolicy() {} - public List AllowedHeaders { get; } = new List(["Content-Type", "Accept", "User-Agent", "x-ms-client-request-id"]); + public List AllowedHeaders { get; } = ["Content-Type", "Accept", "User-Agent", "x-ms-client-request-id"]; public override void Process(PipelineMessage message, IReadOnlyList pipeline, int currentIndex) { LogRequest(message); @@ -45,7 +45,7 @@ protected virtual void LogResponse(PipelineMessage message) } protected virtual string FormatRequestLog(PipelineMessage message) { - StringBuilder logMessage = new StringBuilder(); + StringBuilder logMessage = new(); FormatRequestLine(message, logMessage); FormatHeaders(message, logMessage); FormatContent(message, logMessage); @@ -53,9 +53,9 @@ protected virtual string FormatRequestLog(PipelineMessage message) { } protected virtual string FormatResponseLog(PipelineMessage message) { - StringBuilder logMessage = new StringBuilder(); + StringBuilder logMessage = new(); PipelineResponse response = message.Response!; - logMessage.Append(response.Status.ToString()); + logMessage.Append(response.Status); logMessage.Append(' '); logMessage.AppendLine(response.ReasonPhrase); FormatHeaders(message, logMessage); @@ -72,7 +72,7 @@ protected virtual void FormatRequestLine(PipelineMessage message, StringBuilder } protected virtual void FormatHeaders(PipelineMessage message, StringBuilder logMessage) { - foreach (var header in message.Request.Headers) + foreach (KeyValuePair header in message.Request.Headers) { if (AllowedHeaders.Contains(header.Key)) { diff --git a/sdk/provisioning/Azure.Provisioning.CloudMachine/src/Core/RestCLient/RestCallFailedException.cs b/sdk/cloudmachine/Azure.CloudMachine/src/Core/RestCLient/RestCallFailedException.cs similarity index 56% rename from sdk/provisioning/Azure.Provisioning.CloudMachine/src/Core/RestCLient/RestCallFailedException.cs rename to sdk/cloudmachine/Azure.CloudMachine/src/Core/RestCLient/RestCallFailedException.cs index 333a2934a308b..22d7a0c98243c 100644 --- a/sdk/provisioning/Azure.Provisioning.CloudMachine/src/Core/RestCLient/RestCallFailedException.cs +++ b/sdk/cloudmachine/Azure.CloudMachine/src/Core/RestCLient/RestCallFailedException.cs @@ -6,8 +6,16 @@ namespace Azure { + /// + /// Exception thrown when a REST call fails. + /// public class RestCallFailedException : Exception { + /// + /// Initializes a new instance of the class. + /// + /// + /// public RestCallFailedException(string message, PipelineResponse response) : base(message, new System.ClientModel.ClientResultException(response)) { } } diff --git a/sdk/cloudmachine/Azure.CloudMachine/src/Core/RestCLient/RestClient.cs b/sdk/cloudmachine/Azure.CloudMachine/src/Core/RestCLient/RestClient.cs new file mode 100644 index 0000000000000..648044c383817 --- /dev/null +++ b/sdk/cloudmachine/Azure.CloudMachine/src/Core/RestCLient/RestClient.cs @@ -0,0 +1,131 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.ClientModel.Primitives; +using System.ClientModel; + +namespace Azure +{ + /// + /// A simple REST client that sends HTTP requests and receives responses. + /// + public class RestClient + { + private readonly ClientPipeline _pipeline; + + /// + /// A shared instance of the RestClient class. + /// + public static RestClient Shared { get; } = new RestClient(); + + /// + /// Initializes a new instance of the RestClient class. + /// + public RestClient() : this(default(RestClientOptions)) + { + } + + /// + /// Initializes a new instance of the RestClient class with the specified authentication policy. + /// + /// + public RestClient(PipelinePolicy auth) : this(CreateOptions(auth)) + { + } + + private static RestClientOptions CreateOptions(PipelinePolicy auth) + { + RestClientOptions options = new(); + options.AddPolicy(auth, PipelinePosition.PerTry); + return options; + } + + private RestClient(RestClientOptions options = default) + { + options ??= new RestClientOptions(); + _pipeline = ClientPipeline.Create(options); + } + + /// + /// Sends a GET request to the specified URI. + /// + /// + /// + /// + public PipelineResponse Get(string uri, RequestOptions options = default) + { + PipelineMessage message = Create("GET", new Uri(uri)); + return Send(message, options); + } + + /// + /// Sends a POST request to the specified URI. + /// + /// + /// + /// + /// + public PipelineResponse Post(string uri, BinaryContent content, RequestOptions options = default) + { + PipelineMessage message = Create("POST", new Uri(uri)); + message.Request.Content = content; + return Send(message, options); + } + + /// + /// Sends a PUT request to the specified URI. + /// + /// + /// + /// + /// + public PipelineResponse Put(string uri, BinaryContent content, RequestOptions options = default) + { + PipelineMessage message = Create("PUT", new Uri(uri)); + message.Request.Content = content; + return Send(message, options); + } + + /// + /// Sends a DELETE request to the specified URI. + /// + /// + /// + /// + /// + public PipelineResponse Patch(string uri, BinaryContent content, RequestOptions options = default) + { + PipelineMessage message = Create("PATCH", new Uri(uri)); + message.Request.Content = content; + return Send(message, options); + } + + /// + /// Sends a request with the specified message. + /// + /// + /// + /// + public PipelineResponse Send(PipelineMessage message, RequestOptions options = default) + { + if (options != default) message.Apply(options); + _pipeline.Send(message); + return message.Response!; + } + + /// + /// Creates a new PipelineMessage with the specified method and URI. + /// + /// + /// + /// + public PipelineMessage Create(string method, Uri uri) + { + PipelineMessage message = _pipeline.CreateMessage(); + message.Request.Method = method; + message.Request.Uri = uri; + return message; + } + } +} diff --git a/sdk/provisioning/Azure.Provisioning.CloudMachine/src/Core/RestCLient/RestClientOptions.cs b/sdk/cloudmachine/Azure.CloudMachine/src/Core/RestCLient/RestClientOptions.cs similarity index 75% rename from sdk/provisioning/Azure.Provisioning.CloudMachine/src/Core/RestCLient/RestClientOptions.cs rename to sdk/cloudmachine/Azure.CloudMachine/src/Core/RestCLient/RestClientOptions.cs index c5013124286cc..daa6df9758e40 100644 --- a/sdk/provisioning/Azure.Provisioning.CloudMachine/src/Core/RestCLient/RestClientOptions.cs +++ b/sdk/cloudmachine/Azure.CloudMachine/src/Core/RestCLient/RestClientOptions.cs @@ -5,6 +5,9 @@ namespace Azure { + /// + /// Options for the REST client. + /// public class RestClientOptions : ClientPipelineOptions { } diff --git a/sdk/provisioning/Azure.Provisioning.CloudMachine/src/Core/RestCLient/TokenCredentialAuthenticationPolicy.cs b/sdk/cloudmachine/Azure.CloudMachine/src/Core/RestCLient/TokenCredentialAuthenticationPolicy.cs similarity index 76% rename from sdk/provisioning/Azure.Provisioning.CloudMachine/src/Core/RestCLient/TokenCredentialAuthenticationPolicy.cs rename to sdk/cloudmachine/Azure.CloudMachine/src/Core/RestCLient/TokenCredentialAuthenticationPolicy.cs index 1ec1f690bee8e..3cb1ae80ccd14 100644 --- a/sdk/provisioning/Azure.Provisioning.CloudMachine/src/Core/RestCLient/TokenCredentialAuthenticationPolicy.cs +++ b/sdk/cloudmachine/Azure.CloudMachine/src/Core/RestCLient/TokenCredentialAuthenticationPolicy.cs @@ -11,13 +11,23 @@ namespace Azure.AI.OpenAI; -internal partial class TokenCredentialAuthenticationPolicy : PipelinePolicy +/// +/// TokenCredentialAuthenticationPolicy is a pipeline policy that authenticates requests using a TokenCredential. +/// +public partial class TokenCredentialAuthenticationPolicy : PipelinePolicy { private readonly TokenCredential _credential; private readonly string[] _scopes; private readonly TimeSpan _refreshOffset; private AccessToken? _currentToken; + /// + /// Creates a new TokenCredentialAuthenticationPolicy instance. + /// + /// + /// + /// + /// public TokenCredentialAuthenticationPolicy(TokenCredential credential, IEnumerable scopes, TimeSpan? refreshOffset = null) { if (credential is null) throw new ArgumentNullException(nameof(credential)); @@ -28,6 +38,13 @@ public TokenCredentialAuthenticationPolicy(TokenCredential credential, IEnumerab _refreshOffset = refreshOffset ?? s_defaultRefreshOffset; } + /// + /// executes the policy. + /// + /// + /// + /// + public override void Process(PipelineMessage message, IReadOnlyList pipeline, int currentIndex) { if (message?.Request is not null) @@ -46,6 +63,13 @@ public override void Process(PipelineMessage message, IReadOnlyList + /// executes the policy asynchronously. + /// + /// + /// + /// + /// public override async ValueTask ProcessAsync(PipelineMessage message, IReadOnlyList pipeline, int currentIndex) { if (message?.Request is not null) @@ -75,7 +99,7 @@ private bool IsTokenFresh() private TokenRequestContext CreateRequestContext(PipelineRequest request) { - if (request.Headers.TryGetValue("x-ms-client-request-id", out string? messageClientId)) + if (request.Headers.TryGetValue("x-ms-client-request-id", out string messageClientId)) { return new TokenRequestContext(_scopes, messageClientId); } diff --git a/sdk/cloudmachine/Azure.CloudMachine/src/CoreServices/MessagingServices.cs b/sdk/cloudmachine/Azure.CloudMachine/src/CoreServices/MessagingServices.cs index 7b2b146da7421..c8b2060de45bf 100644 --- a/sdk/cloudmachine/Azure.CloudMachine/src/CoreServices/MessagingServices.cs +++ b/sdk/cloudmachine/Azure.CloudMachine/src/CoreServices/MessagingServices.cs @@ -13,6 +13,8 @@ namespace Azure.CloudMachine; /// public readonly struct MessagingServices { + internal const string DEFAULT_SB_TOPIC = "cm_servicebus_default_topic"; + private readonly CloudMachineClient _cm; internal MessagingServices(CloudMachineClient cm) => _cm = cm; @@ -46,8 +48,8 @@ public async Task SendJsonAsync(object serializable) /// public void WhenMessageReceived(Action received) { - var processor = _cm.Messaging.GetServiceBusProcessor(default); - var cm = _cm; + ServiceBusProcessor processor = _cm.Messaging.GetServiceBusProcessor(default); + CloudMachineClient cm = _cm; // TODO: How to unsubscribe? // TODO: Use a subscription filter to ignore Event Grid system events @@ -76,10 +78,10 @@ private ServiceBusSender GetServiceBusSender() return sender; } - internal ServiceBusProcessor GetServiceBusProcessor(string id) + internal ServiceBusProcessor GetServiceBusProcessor(string subscriptionName) { MessagingServices messagingServices = this; - ServiceBusProcessor processor = _cm.Subclients.Get(() => messagingServices.CreateProcessor(id), id); + ServiceBusProcessor processor = _cm.Subclients.Get(() => messagingServices.CreateProcessor(subscriptionName), subscriptionName); return processor; } @@ -87,22 +89,22 @@ private ServiceBusSender CreateSender() { ServiceBusClient client = GetServiceBusClient(); - ClientConnectionOptions connection = _cm.GetConnectionOptions(typeof(ServiceBusSender), default); - ServiceBusSender sender = client.CreateSender(connection.Id); + ClientConnection connection = _cm.GetConnectionOptions(DEFAULT_SB_TOPIC); + ServiceBusSender sender = client.CreateSender(connection.Locator); return sender; } private ServiceBusClient CreateClient() { - ClientConnectionOptions connection = _cm.GetConnectionOptions(typeof(ServiceBusClient), default); - ServiceBusClient client = new(connection.Endpoint!.AbsoluteUri, connection.TokenCredential); + ClientConnection connection = _cm.GetConnectionOptions(typeof(ServiceBusClient).FullName); + ServiceBusClient client = new(connection.ToUri().AbsoluteUri, _cm.Credential); return client; } - private ServiceBusProcessor CreateProcessor(string id) + private ServiceBusProcessor CreateProcessor(string subscriptionName) { ServiceBusClient client = GetServiceBusClient(); - ClientConnectionOptions connection = _cm.GetConnectionOptions(typeof(ServiceBusProcessor), id); - string[] topicAndSubscription = connection.Id.Split('/'); + ClientConnection connection = _cm.GetConnectionOptions(subscriptionName); + string[] topicAndSubscription = connection.Locator.Split('/'); ServiceBusProcessor processor = client.CreateProcessor(topicAndSubscription[0], topicAndSubscription[1], new() { MaxConcurrentCalls = 5 }); processor.ProcessErrorAsync += (args) => throw new Exception("error processing event", args.Exception); return processor; diff --git a/sdk/cloudmachine/Azure.CloudMachine/src/CoreServices/StorageFile.cs b/sdk/cloudmachine/Azure.CloudMachine/src/CoreServices/StorageFile.cs index e3c27ee57c986..ba36c0533efce 100644 --- a/sdk/cloudmachine/Azure.CloudMachine/src/CoreServices/StorageFile.cs +++ b/sdk/cloudmachine/Azure.CloudMachine/src/CoreServices/StorageFile.cs @@ -15,7 +15,7 @@ public class StorageFile { private readonly Response _response; - private StorageServices _storage; + private readonly StorageServices _storage; /// /// The path of the file in the storage account. @@ -23,7 +23,7 @@ public class StorageFile public string Path { get; internal set; } /// - /// The requestId for the storage operation that triggered this event + /// The requestId for the storage operation that triggered this event. /// public string RequestId { get; internal set; } @@ -46,6 +46,13 @@ public class StorageFile public BinaryData Download() => _storage.Download(Path); + /// + /// Downloads the file from the storage account. + /// + /// + public async Task DownloadAsync() + => await _storage.DownloadAsync(Path).ConfigureAwait(false); + // public async Task DownloadAsync() // => await _storage.DownloadBlobAsync(Path).ConfigureAwait(false); diff --git a/sdk/cloudmachine/Azure.CloudMachine/src/CoreServices/StorageServices.cs b/sdk/cloudmachine/Azure.CloudMachine/src/CoreServices/StorageServices.cs index 26fda12e48bce..5ab6d3c9cf160 100644 --- a/sdk/cloudmachine/Azure.CloudMachine/src/CoreServices/StorageServices.cs +++ b/sdk/cloudmachine/Azure.CloudMachine/src/CoreServices/StorageServices.cs @@ -12,6 +12,7 @@ using Azure.Storage.Blobs.Specialized; using Azure.Messaging.ServiceBus; using ContentType = Azure.Core.ContentType; +using System.ComponentModel; namespace Azure.CloudMachine; @@ -24,26 +25,22 @@ public readonly struct StorageServices internal StorageServices(CloudMachineClient cm) => _cm = cm; - private BlobContainerClient GetDefaultContainer() - { - CloudMachineClient cm = _cm; - BlobContainerClient container = _cm.Subclients.Get(() => - { - ClientConnectionOptions connection = cm.GetConnectionOptions(typeof(BlobContainerClient), default); - BlobContainerClient container = new(connection.Endpoint, connection.TokenCredential); - return container; - }); - return container; - } - - private BlobContainerClient GetContainer(string containerName) + // TODO: do we want Azure.Storage.Blobs in the public API? This would prevent us from using a custom implementation. + /// + /// Gets the container client. + /// + /// + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public BlobContainerClient GetContainer(string containerName = default) { - string blobContainerClientId = typeof(BlobContainerClient).FullName; + if (containerName == default) containerName = "default"; + string blobContainerClientId = $"{typeof(BlobContainerClient).FullName}@{containerName}"; CloudMachineClient cm = _cm; BlobContainerClient container = cm.Subclients.Get(() => { - ClientConnectionOptions connection = cm.GetConnectionOptions(typeof(BlobContainerClient), containerName); - BlobContainerClient container = new(connection.Endpoint, connection.TokenCredential); + ClientConnection connection = cm.GetConnectionOptions(blobContainerClientId); + BlobContainerClient container = new(connection.ToUri(), cm.Credential); return container; }); return container; @@ -52,26 +49,26 @@ private BlobContainerClient GetContainer(string containerName) /// /// Uploads a JSON object to the storage account. /// - /// + /// /// /// /// - public string UploadJson(object json, string name = default, bool overwrite = false) + public string UploadJson(object serializable, string name = default, bool overwrite = false) { - BinaryData data = BinaryData.FromObjectAsJson(json); + BinaryData data = BinaryData.FromObjectAsJson(serializable); return Upload(data, name, overwrite); } /// /// Uploads a JSON object to the storage account. /// - /// + /// /// /// /// - public async Task UploadJsonAsync(object json, string name = default, bool overwrite = false) + public async Task UploadJsonAsync(object serializable, string name = default, bool overwrite = false) { - BinaryData data = BinaryData.FromObjectAsJson(json); + BinaryData data = BinaryData.FromObjectAsJson(serializable); return await UploadAsync(data, name, overwrite).ConfigureAwait(false); } @@ -86,7 +83,7 @@ public async Task UploadJsonAsync(object json, string name = default, bo public string Upload(Stream fileStream, string name = default, string contentType = default, bool overwrite = false) { BlockBlobClient client = GetBlobClient(ref name); - BlobUploadOptions options = CreateUploadOptions(overwrite, contentType); + BlobUploadOptions options = StorageServices.CreateUploadOptions(overwrite, contentType); client.Upload(fileStream, options); return name; @@ -103,7 +100,7 @@ public string Upload(Stream fileStream, string name = default, string contentTyp public async Task UploadAsync(Stream fileStream, string name = default, string contentType = default, bool overwrite = false) { BlockBlobClient client = GetBlobClient(ref name); - BlobUploadOptions options = CreateUploadOptions(overwrite, contentType); + BlobUploadOptions options = StorageServices.CreateUploadOptions(overwrite, contentType); await client.UploadAsync(fileStream, options).ConfigureAwait(false); return name; @@ -111,15 +108,15 @@ public async Task UploadAsync(Stream fileStream, string name = default, private BlockBlobClient GetBlobClient(ref string name) { - BlobContainerClient container = GetDefaultContainer(); + BlobContainerClient container = GetContainer(default); if (name == default) name = $"b{Guid.NewGuid()}"; BlockBlobClient client = container.GetBlockBlobClient(name); return client; } - private BlobUploadOptions CreateUploadOptions(bool overwrite, string contentType) + private static BlobUploadOptions CreateUploadOptions(bool overwrite, string contentType) { - if (contentType == null) contentType = ContentType.ApplicationOctetStream.ToString(); + contentType ??= ContentType.ApplicationOctetStream.ToString(); BlobUploadOptions options = new() { Conditions = overwrite ? null : new BlobRequestConditions { IfNoneMatch = new ETag("*") }, @@ -206,15 +203,15 @@ public async Task DeleteAsync(string path) private BlobClient GetBlobClientFromPath(string path, string containerName) { - var _blobContainer = GetDefaultContainer(); - var blobPath = ConvertPathToBlobPath(path, _blobContainer); + BlobContainerClient _blobContainer = GetContainer(default); + string blobPath = ConvertPathToBlobPath(path, _blobContainer); if (containerName is null) { return _blobContainer.GetBlobClient(blobPath); } else { - var container = GetContainer(containerName); + BlobContainerClient container = GetContainer(containerName); container.CreateIfNotExists(); return container.GetBlobClient(blobPath); } @@ -242,7 +239,7 @@ public void WhenUploaded(Action function) { CloudMachineClient cm = _cm; // TODO (Pri 0): once the cache gets GCed, we will stop receiving events - ServiceBusProcessor processor = cm.Messaging.GetServiceBusProcessor("$private"); + ServiceBusProcessor processor = cm.Messaging.GetServiceBusProcessor("cm_servicebus_subscription_private"); // TODO: How to unsubscribe? processor.ProcessMessageAsync += async (args) => { diff --git a/sdk/cloudmachine/Azure.CloudMachine/src/GlobalSuppressions.cs b/sdk/cloudmachine/Azure.CloudMachine/src/GlobalSuppressions.cs new file mode 100644 index 0000000000000..57ea0ca077129 --- /dev/null +++ b/sdk/cloudmachine/Azure.CloudMachine/src/GlobalSuppressions.cs @@ -0,0 +1,7 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System.Diagnostics.CodeAnalysis; + +[assembly: SuppressMessage("Usage", "AZC0007:DO provide a minimal constructor that takes only the parameters required to connect to the service.", Justification = "", Scope = "member", Target = "~M:Azure.RestClient.#ctor")] +[assembly: SuppressMessage("Usage", "AZC0007:DO provide a minimal constructor that takes only the parameters required to connect to the service.", Justification = "", Scope = "member", Target = "~M:Azure.RestClient.#ctor(System.ClientModel.Primitives.PipelinePolicy)")] diff --git a/sdk/cloudmachine/Azure.CloudMachine/src/extensions/AzureOpenAIExtensions.cs b/sdk/cloudmachine/Azure.CloudMachine/src/extensions/AzureOpenAIExtensions.cs index 7a66c81f9d644..741d05b5eeedc 100644 --- a/sdk/cloudmachine/Azure.CloudMachine/src/extensions/AzureOpenAIExtensions.cs +++ b/sdk/cloudmachine/Azure.CloudMachine/src/extensions/AzureOpenAIExtensions.cs @@ -3,7 +3,6 @@ using System.ClientModel; using System.Collections.Generic; -using System.Runtime.CompilerServices; using System.Text; using Azure.AI.OpenAI; using Azure.Core; @@ -49,6 +48,13 @@ public static EmbeddingClient GetOpenAIEmbeddingsClient(this ClientWorkspace wor return embeddingsClient; } + /// + /// returns full text of all parts. + /// + /// + public static string AsText(this ClientResult completionResult) + => AsText(completionResult.Value); + /// /// returns full text of all parts. /// @@ -60,12 +66,12 @@ public static string AsText(this ChatCompletion completion) /// /// returns full text of all parts. /// - /// + /// /// - public static string AsText(this ChatMessageContent completion) + public static string AsText(this ChatMessageContent content) { StringBuilder sb = new(); - foreach (ChatMessageContentPart part in completion) + foreach (ChatMessageContentPart part in content) { switch (part.Kind) { @@ -82,28 +88,28 @@ public static string AsText(this ChatMessageContent completion) private static AzureOpenAIClient CreateAzureOpenAIClient(this ClientWorkspace workspace) { - ClientConnectionOptions connection = workspace.GetConnectionOptions(typeof(AzureOpenAIClient)); - if (connection.ConnectionKind == ClientConnectionKind.EntraId) + ClientConnection connection = workspace.GetConnectionOptions(typeof(AzureOpenAIClient).FullName); + if (connection.Authentication == ClientAuthenticationMethod.EntraId) { - return new(connection.Endpoint, connection.TokenCredential); + return new(connection.ToUri(), workspace.Credential); } else { - return new(connection.Endpoint, new ApiKeyCredential(connection.ApiKeyCredential!)); + return new(connection.ToUri(), new ApiKeyCredential(connection.ApiKeyCredential!)); } } private static ChatClient CreateChatClient(this ClientWorkspace workspace, AzureOpenAIClient client) { - ClientConnectionOptions connection = workspace.GetConnectionOptions(typeof(ChatClient)); - ChatClient chat = client.GetChatClient(connection.Id); + ClientConnection connection = workspace.GetConnectionOptions(typeof(ChatClient).FullName); + ChatClient chat = client.GetChatClient(connection.Locator); return chat; } private static EmbeddingClient CreateEmbeddingsClient(this ClientWorkspace workspace, AzureOpenAIClient client) { - ClientConnectionOptions connection = workspace.GetConnectionOptions(typeof(EmbeddingClient)); - EmbeddingClient embeddings = client.GetEmbeddingClient(connection.Id); + ClientConnection connection = workspace.GetConnectionOptions(typeof(EmbeddingClient).FullName); + EmbeddingClient embeddings = client.GetEmbeddingClient(connection.Locator); return embeddings; } diff --git a/sdk/cloudmachine/Azure.CloudMachine/src/extensions/ChatTools.cs b/sdk/cloudmachine/Azure.CloudMachine/src/extensions/ChatTools.cs index 2564750b6da71..be51567ab0f6b 100644 --- a/sdk/cloudmachine/Azure.CloudMachine/src/extensions/ChatTools.cs +++ b/sdk/cloudmachine/Azure.CloudMachine/src/extensions/ChatTools.cs @@ -17,8 +17,8 @@ public class ChatTools { private static readonly BinaryData s_noparams = BinaryData.FromString("""{ "type" : "object", "properties" : {} }"""); - private readonly Dictionary _methods = new Dictionary(); - private readonly List _definitions = new List(); + private readonly Dictionary _methods = []; + private readonly List _definitions = []; /// /// Initializes a new instance of the class. @@ -26,7 +26,7 @@ public class ChatTools /// public ChatTools(params Type[] tools) { - foreach (var functionHolder in tools) + foreach (Type functionHolder in tools) Add(functionHolder); } @@ -42,7 +42,7 @@ public ChatTools(params Type[] tools) public static implicit operator ChatCompletionOptions(ChatTools tools) { ChatCompletionOptions options = new(); - foreach (var tool in tools.Definitions) + foreach (ChatTool tool in tools.Definitions) { options.Tools.Add(tool); } @@ -68,7 +68,7 @@ public void Add(Type functions) public void Add(MethodInfo function) { var name = function.Name; - var chatTool = ChatTool.CreateFunctionTool(name, GetMethodInfoToDescription(function), ParametersToJson(function.GetParameters())); + var chatTool = ChatTool.CreateFunctionTool(name, MethodInfoToDescription(function), ChatTools.ParametersToJson(function.GetParameters())); _definitions.Add(chatTool); _methods[name] = function; } @@ -98,10 +98,10 @@ public string Call(ChatToolCall call) if (call.FunctionArguments != null) { var document = JsonDocument.Parse(call.FunctionArguments); - var json = document.RootElement; + JsonElement json = document.RootElement; foreach (JsonProperty argument in json.EnumerateObject()) { - var value = argument.Value; + JsonElement value = argument.Value; switch (value.ValueKind) { case JsonValueKind.String: @@ -122,7 +122,7 @@ public string Call(ChatToolCall call) } } var name = call.FunctionName; - var result = Call(name, arguments.ToArray()); + var result = Call(name, [.. arguments]); return result; } @@ -143,15 +143,10 @@ public IEnumerable CallAll(IEnumerable toolCalls) return messages; } - /// - /// Gets the description of a . - /// - /// - /// - protected virtual string GetMethodInfoToDescription(MethodInfo function) + private static string MethodInfoToDescription(MethodInfo function) { var description = function.Name; - var attribute = function.GetCustomAttribute(); + DescriptionAttribute attribute = function.GetCustomAttribute(); if (attribute != null) { description = attribute.Description; @@ -159,15 +154,10 @@ protected virtual string GetMethodInfoToDescription(MethodInfo function) return description; } - /// - /// Gets the description of a . - /// - /// - /// - protected virtual string GetParameterInfoToDescription(ParameterInfo parameter) + private static string ParameterInfoToDescription(ParameterInfo parameter) { var description = parameter.Name; - var attribute = parameter.GetCustomAttribute(); + DescriptionAttribute attribute = parameter.GetCustomAttribute(); if (attribute != null) { description = attribute.Description; @@ -175,29 +165,18 @@ protected virtual string GetParameterInfoToDescription(ParameterInfo parameter) return description; } - /// - /// Gets the name of a . - /// - /// - /// - protected virtual string GetMethodInfoToName(MethodInfo function) + private static string GetMethodInfoToName(MethodInfo function) { var sb = new StringBuilder(); sb.Append(function.Name); - foreach (var parameter in function.GetParameters()) + foreach (ParameterInfo parameter in function.GetParameters()) { - sb.Append($"_{ClrToJsonTypeUtf16(parameter.ParameterType)}"); + sb.Append($"_{ChatTools.ClrToJsonTypeUtf16(parameter.ParameterType)}"); } return sb.ToString(); } - /// - /// Converts a CLR type to a JSON Utf8 type. - /// - /// - /// - /// - protected ReadOnlySpan ClrToJsonTypeUtf8(Type clrType) + private static ReadOnlySpan ClrToJsonTypeUtf8(Type clrType) { if (clrType == typeof(double)) return "number"u8; @@ -209,13 +188,7 @@ protected ReadOnlySpan ClrToJsonTypeUtf8(Type clrType) throw new NotImplementedException(); } - /// - /// Converts a CLR type to a JSON Utf16 type. - /// - /// - /// - /// - protected string ClrToJsonTypeUtf16(Type clrType) + private static string ClrToJsonTypeUtf16(Type clrType) { if (clrType == typeof(double)) return "number"; @@ -227,13 +200,13 @@ protected string ClrToJsonTypeUtf16(Type clrType) throw new NotImplementedException(); } - private BinaryData ParametersToJson(ParameterInfo[] parameters) + private static BinaryData ParametersToJson(ParameterInfo[] parameters) { if (parameters.Length == 0) return s_noparams; var required = new List(); - MemoryStream stream = new MemoryStream(); + MemoryStream stream = new(); var writer = new Utf8JsonWriter(stream); writer.WriteStartObject(); writer.WriteString("type"u8, "object"u8); @@ -241,8 +214,8 @@ private BinaryData ParametersToJson(ParameterInfo[] parameters) foreach (ParameterInfo parameter in parameters) { writer.WriteStartObject(parameter.Name!); - writer.WriteString("type"u8, ClrToJsonTypeUtf8(parameter.ParameterType)); - writer.WriteString("description"u8, GetParameterInfoToDescription(parameter)); + writer.WriteString("type"u8, ChatTools.ClrToJsonTypeUtf8(parameter.ParameterType)); + writer.WriteString("description"u8, ChatTools.ParameterInfoToDescription(parameter)); writer.WriteEndObject(); if (!parameter.IsOptional || (parameter.HasDefaultValue && parameter.DefaultValue is not null)) required.Add(parameter.Name!); diff --git a/sdk/cloudmachine/Azure.CloudMachine/src/extensions/EmbeddingsVectorbase.cs b/sdk/cloudmachine/Azure.CloudMachine/src/extensions/EmbeddingsVectorbase.cs index 965a42876ed9b..ab1cf6a63892e 100644 --- a/sdk/cloudmachine/Azure.CloudMachine/src/extensions/EmbeddingsVectorbase.cs +++ b/sdk/cloudmachine/Azure.CloudMachine/src/extensions/EmbeddingsVectorbase.cs @@ -14,7 +14,7 @@ public class EmbeddingsVectorbase { private readonly EmbeddingClient _client; private readonly VectorbaseStore _store; - private readonly List _todo = new List(); + private readonly List _todo = []; private readonly int _chuckSize; /// @@ -73,9 +73,9 @@ private void ProcessToDo() { lock (_todo) { - var embeddings = _client.GenerateEmbeddings(_todo); + OpenAIEmbeddingCollection embeddings = _client.GenerateEmbeddings(_todo); - foreach (var embedding in embeddings.Value) + foreach (OpenAIEmbedding embedding in embeddings) { ReadOnlyMemory vector = embedding.ToFloats(); string item = _todo[(int)embedding.Index]; @@ -87,8 +87,8 @@ private void ProcessToDo() private ReadOnlyMemory GetEmbedding(string fact) { - var embedding = _client.GenerateEmbedding(fact); - return embedding.Value.ToFloats(); + OpenAIEmbedding embedding = _client.GenerateEmbedding(fact); + return embedding.ToFloats(); } private void ChunkFactAndAddToTodo(string text, int chunkSize) diff --git a/sdk/cloudmachine/Azure.CloudMachine/src/extensions/KeyVaultExtensions.cs b/sdk/cloudmachine/Azure.CloudMachine/src/extensions/KeyVaultExtensions.cs index ad6405da41f8c..9706b0a19f65d 100644 --- a/sdk/cloudmachine/Azure.CloudMachine/src/extensions/KeyVaultExtensions.cs +++ b/sdk/cloudmachine/Azure.CloudMachine/src/extensions/KeyVaultExtensions.cs @@ -20,10 +20,10 @@ public static class KeyVaultExtensions /// public static SecretClient GetKeyVaultSecretsClient(this ClientWorkspace workspace) { - ClientConnectionOptions connection = workspace.GetConnectionOptions(typeof(SecretClient)); - if (connection.ConnectionKind == ClientConnectionKind.EntraId) + ClientConnection connection = workspace.GetConnectionOptions(typeof(SecretClient).FullName); + if (connection.Authentication == ClientAuthenticationMethod.EntraId) { - return new(connection.Endpoint, connection.TokenCredential); + return new(connection.ToUri(), workspace.Credential); } throw new Exception("API key not supported"); } diff --git a/sdk/cloudmachine/Azure.CloudMachine/src/extensions/MemoryVectorbaseStore.cs b/sdk/cloudmachine/Azure.CloudMachine/src/extensions/MemoryVectorbaseStore.cs index e0e0428766fd8..6dfca9073eb98 100644 --- a/sdk/cloudmachine/Azure.CloudMachine/src/extensions/MemoryVectorbaseStore.cs +++ b/sdk/cloudmachine/Azure.CloudMachine/src/extensions/MemoryVectorbaseStore.cs @@ -8,7 +8,7 @@ namespace Azure.CloudMachine.OpenAI; internal class MemoryVectorbaseStore : VectorbaseStore { - private readonly List _entries = new List(); + private readonly List _entries = []; public override IEnumerable Find(ReadOnlyMemory vector, FindOptions options) { @@ -24,14 +24,14 @@ public override IEnumerable Find(ReadOnlyMemory vector, } distances.Sort(((float D1, int I1) v1, (float D2, int I2) v2) => v1.D1.CompareTo(v2.D2)); - var results = new List(options.MaxEntries); - var top = Math.Min(options.MaxEntries, distances.Count); + List results = new(options.MaxEntries); + int top = Math.Min(options.MaxEntries, distances.Count); for (int i = 0; i < top; i++) { - var distance = distances[i].Distance; + float distance = distances[i].Distance; if (distance > options.Threshold) break; - var index = distances[i].Index; + int index = distances[i].Index; results.Add(_entries[index]); } return results; @@ -41,8 +41,8 @@ public override int Add(VectorbaseEntry entry) { lock (_entries) { - var id = _entries.Count; - var newEntry = new VectorbaseEntry(entry.Vector, entry.Data, id); + int id = _entries.Count; + VectorbaseEntry newEntry = new(entry.Vector, entry.Data, id); _entries.Add(newEntry); return id; } @@ -50,7 +50,7 @@ public override int Add(VectorbaseEntry entry) public override void Add(IReadOnlyList entries) { - foreach (var entry in entries) + foreach (VectorbaseEntry entry in entries) { Add(entry); } diff --git a/sdk/cloudmachine/Azure.CloudMachine/src/extensions/VectorbaseStore.cs b/sdk/cloudmachine/Azure.CloudMachine/src/extensions/VectorbaseStore.cs index 6944fe2cf76fb..c25a4c25b39ab 100644 --- a/sdk/cloudmachine/Azure.CloudMachine/src/extensions/VectorbaseStore.cs +++ b/sdk/cloudmachine/Azure.CloudMachine/src/extensions/VectorbaseStore.cs @@ -59,10 +59,6 @@ public static float CosineSimilarity(ReadOnlySpan x, ReadOnlySpan public readonly struct VectorbaseEntry { - private readonly ReadOnlyMemory _vector; - private readonly int? _id; - private readonly BinaryData _data; - /// /// Initializes a new instance of the class. /// @@ -71,23 +67,23 @@ public readonly struct VectorbaseEntry /// public VectorbaseEntry(ReadOnlyMemory vector, BinaryData data, int? id = default) { - _vector = vector; - _data = data; - _id = id; + Vector = vector; + Data = data; + Id = id; } /// /// Gets the data associated with the entry. /// - public BinaryData Data => _data; + public BinaryData Data { get; } /// /// Gets the vector associated with the entry. /// - public ReadOnlyMemory Vector => _vector; + public ReadOnlyMemory Vector { get; } /// /// Gets the id associated with the entry. /// - public int? Id => _id; + public int? Id { get; } } diff --git a/sdk/cloudmachine/Azure.CloudMachine/tests/Azure.CloudMachine.Tests.csproj b/sdk/cloudmachine/Azure.CloudMachine/tests/Azure.CloudMachine.Tests.csproj index 7743315c7873e..72514fb231b79 100644 --- a/sdk/cloudmachine/Azure.CloudMachine/tests/Azure.CloudMachine.Tests.csproj +++ b/sdk/cloudmachine/Azure.CloudMachine/tests/Azure.CloudMachine.Tests.csproj @@ -8,7 +8,7 @@ - + diff --git a/sdk/cloudmachine/Azure.CloudMachine/tests/CloudMachineTests.cs b/sdk/cloudmachine/Azure.CloudMachine/tests/CloudMachineTests.cs index 1b24bcfbccb4c..9546906bc19ae 100644 --- a/sdk/cloudmachine/Azure.CloudMachine/tests/CloudMachineTests.cs +++ b/sdk/cloudmachine/Azure.CloudMachine/tests/CloudMachineTests.cs @@ -10,241 +10,115 @@ using Azure.CloudMachine.KeyVault; using NUnit.Framework; using OpenAI.Chat; -using Microsoft.Extensions.Configuration; -using System.Collections.Generic; -using Microsoft.Extensions.Primitives; -using System.Linq; -using System.IO; -using Azure.Provisioning; namespace Azure.CloudMachine.Tests; -public class CloudMachineTests +// TODO: we need recordings to enable these tests + +public partial class CloudMachineTests { - [Test] - public void Configuration() - { - CloudMachineCommands.Execute(["-bicep"], (infrastructure) => - { - infrastructure.AddFeature(new KeyVaultFeature()); - infrastructure.AddFeature(new OpenAIModel("gpt-35-turbo", "0125")); - infrastructure.AddFeature(new OpenAIModel("text-embedding-ada-002", "2", AIModelKind.Embedding)); - }, exitProcessIfHandled: false); - - CloudMachineWorkspace cm = new(); - Console.WriteLine(cm.Id); - var embeddings = cm.GetOpenAIEmbeddingsClient(); - } - - [Ignore("no recordings yet")] - [Test] - [TestCase([new string[] { "-bicep" }])] - [TestCase([new string[] { "" }])] - public void Storage(string[] args) - { - if (CloudMachineCommands.Execute(args, exitProcessIfHandled: false)) return; - - ManualResetEventSlim eventSlim = new(false); - CloudMachineClient cm = new(); - - cm.Storage.WhenUploaded((StorageFile file) => - { - var data = file.Download(); - Console.WriteLine(data.ToString()); - Assert.AreEqual("{\"Foo\":5,\"Bar\":true}", data.ToString()); - eventSlim.Set(); - }); - var uploaded = cm.Storage.UploadJson(new - { - Foo = 5, - Bar = true - }); - BinaryData downloaded = cm.Storage.Download(uploaded); - Console.WriteLine(downloaded.ToString()); - eventSlim.Wait(); - } - - [Ignore("no recordings yet")] - [Test] - [TestCase([new string[] { "-bicep" }])] - [TestCase([new string[] { "" }])] - public void OpenAI(string[] args) - { - if (CloudMachineCommands.Execute(args, (infrastructure) => - { - infrastructure.AddFeature(new OpenAIModel("gpt-35-turbo", "0125")); - }, exitProcessIfHandled: false)) return; - - CloudMachineWorkspace cm = new(); - ChatClient chat = cm.GetOpenAIChatClient(); - ChatCompletion completion = chat.CompleteChat("Is Azure programming easy?"); - Console.WriteLine(completion.AsText()); - } - - [Ignore("no recordings yet")] - [Test] - [TestCase([new string[] { "-bicep" }])] - [TestCase([new string[] { "" }])] - public void KeyVault(string[] args) - { - if (CloudMachineCommands.Execute(args, (cm) => - { - cm.AddFeature(new KeyVaultFeature()); - }, exitProcessIfHandled: false)) return; - - CloudMachineWorkspace cm = new(); - SecretClient secrets = cm.GetKeyVaultSecretsClient(); - secrets.SetSecret("testsecret", "don't tell anybody"); - } - - [Ignore("no recordings yet")] - [Test] - [TestCase([new string[] { "-bicep" }])] - [TestCase([new string[] { "" }])] - public void Messaging(string[] args) - { - CloudMachineCommands.Execute(args); - - CloudMachineClient cm = new(); - cm.Messaging.WhenMessageReceived(message => - { - Console.WriteLine(message); - Assert.True(message != null); - }); - cm.Messaging.SendJson(new - { - Foo = 5, - Bar = true - }); - } - - [Ignore("no recordings yet")] - [Test] - [TestCase([new string[] { "-bicep" }])] - [TestCase([new string[] { "" }])] - public void Demo(string[] args) - { - if (CloudMachineCommands.Execute(args, exitProcessIfHandled: false)) return; - - CloudMachineClient cm = new(); - - // setup - cm.Messaging.WhenMessageReceived((string message) => cm.Storage.Upload(BinaryData.FromString(message))); - cm.Storage.WhenUploaded((StorageFile file) => - { - var content = file.Download(); - ChatCompletion completion = cm.GetOpenAIChatClient().CompleteChat(content.ToString()); - Console.WriteLine(completion.Content[0].Text); - }); - - // go! - cm.Messaging.SendJson("Tell me something about Redmond, WA."); - } - - [Ignore("no recordings yet")] - [Test] - public void EmbeddingsDemo() - { - string[] testMessages = [ - "When did the continuation policy change for DefaultAzureCredential?", - "Do I love Seattle?", - "What is the current time?", - "What's the weather in Seattle?", - "Do you think I would like the weather there?", - ]; - - CloudMachineClient cm = new(default, new MockConfiguration("cmec4615e3fdfa44e")); - var chat = cm.GetOpenAIChatClient(); - var embeddings = cm.GetOpenAIEmbeddingsClient(); - - // helpers - ChatTools tools = new(typeof(MyFunctions)); - EmbeddingsVectorbase vectors = new(embeddings, null, 1000); - vectors.Add("I love Seattle."); - vectors.Add(File.ReadAllText(@"C:\Users\chriss\Desktop\Identity-README.md")); - - ChatCompletionOptions options = new(); - foreach (var definition in tools.Definitions) - { - options.Tools.Add(definition); - } - options.ToolChoice = ChatToolChoice.CreateAutoChoice(); - - List prompt = new(); - - foreach (var testMessage in testMessages) - { - Console.WriteLine($"u: {testMessage}"); - IEnumerable relatedItems = vectors.Find(testMessage); - foreach (VectorbaseEntry relatedItem in relatedItems) - { - prompt.Add(ChatMessage.CreateSystemMessage(relatedItem.Data.ToString())); - } - - prompt.Add(ChatMessage.CreateUserMessage(testMessage)); - CallOpenAI(); - // filter the prompt to only include the user message - var responses = prompt.Where(message => message is AssistantChatMessage).Select(m => m.Content[0].Text).ToList(); - } - void CallOpenAI() - { - bool requiresAction; - do - { - requiresAction = false; - var completion = chat.CompleteChat(prompt, options).Value; - switch (completion.FinishReason) - { - case ChatFinishReason.ToolCalls: - // TODO: figure out why the model is returning bogus tool call results. - // prompt.Add(new AssistantChatMessage(completion)); - // IEnumerable callResults = tools.CallAll(completion.ToolCalls); - // prompt.AddRange(callResults); - requiresAction = true; - break; - case ChatFinishReason.Length: - Console.WriteLine("Incomplete model output due to MaxTokens parameter or token limit exceeded."); - break; - case ChatFinishReason.ContentFilter: - Console.WriteLine("Omitted content due to a content filter flag."); - break; - case ChatFinishReason.Stop: - prompt.Add(new AssistantChatMessage(completion)); - break; - default: - throw new NotImplementedException("Unknown finish reason."); - } - } while (requiresAction); - } - } - - public static class MyFunctions - { - [System.ComponentModel.Description("Returns the current weather at the specified location")] - public static string GetCurrentWeather(string location, string? unit = default) => $"1 million degrees {unit}"; - - [System.ComponentModel.Description("Returns the user's current location")] - public static string GetCurrentLocation() => "Planet Earth"; - - [System.ComponentModel.Description("Returns the current time.")] - public static string GetCurrentTime() => DateTimeOffset.Now.ToString("t"); - } - - private class MockConfiguration : IConfiguration - { - private readonly string _cmId; - - public MockConfiguration(string cmId) - { - _cmId = cmId; - } - - public string? this[string key] { get => _cmId; set => throw new NotImplementedException(); } - - public IEnumerable GetChildren() => throw new NotImplementedException(); - - public IChangeToken GetReloadToken() => throw new NotImplementedException(); - - public IConfigurationSection GetSection(string key) => throw new NotImplementedException(); - } +// [Ignore("no recordings yet")] +// [Test] +// [TestCase([new string[] { "-bicep" }])] +// [TestCase([new string[] { "" }])] +// public void Storage(string[] args) +// { +// if (CloudMachineCommands.Execute(args, exitProcessIfHandled: false)) return; + +// ManualResetEventSlim eventSlim = new(false); +// CloudMachineClient cm = new(); + +// cm.Storage.WhenUploaded((StorageFile file) => +// { +// var data = file.Download(); +// Console.WriteLine(data.ToString()); +// Assert.AreEqual("{\"Foo\":5,\"Bar\":true}", data.ToString()); +// eventSlim.Set(); +// }); +// var uploaded = cm.Storage.UploadJson(new +// { +// Foo = 5, +// Bar = true +// }); +// BinaryData downloaded = cm.Storage.Download(uploaded); +// Console.WriteLine(downloaded.ToString()); +// eventSlim.Wait(); +// } + +// [Ignore("no recordings yet")] +// [Test] +// [TestCase([new string[] { "-bicep" }])] +// [TestCase([new string[] { "" }])] +// public void OpenAI(string[] args) +// { +// if (CloudMachineCommands.Execute(args, (infrastructure) => +// { +// infrastructure.AddFeature(new OpenAIModelFeature("gpt-35-turbo", "0125")); +// }, exitProcessIfHandled: false)) return; + +// CloudMachineWorkspace cm = new(); +// ChatClient chat = cm.GetOpenAIChatClient(); +// ChatCompletion completion = chat.CompleteChat("Is Azure programming easy?"); +// Console.WriteLine(completion.AsText()); +// } + +// [Ignore("no recordings yet")] +// [Test] +// [TestCase([new string[] { "-bicep" }])] +// [TestCase([new string[] { "" }])] +// public void KeyVault(string[] args) +// { +// if (CloudMachineCommands.Execute(args, (cm) => +// { +// cm.AddFeature(new KeyVaultFeature()); +// }, exitProcessIfHandled: false)) return; + +// CloudMachineWorkspace cm = new(); +// SecretClient secrets = cm.GetKeyVaultSecretsClient(); +// secrets.SetSecret("testsecret", "don't tell anybody"); +// } + +// [Ignore("no recordings yet")] +// [Test] +// [TestCase([new string[] { "-bicep" }])] +// [TestCase([new string[] { "" }])] +// public void Messaging(string[] args) +// { +// CloudMachineCommands.Execute(args); + +// CloudMachineClient cm = new(); +// cm.Messaging.WhenMessageReceived(message => +// { +// Console.WriteLine(message); +// Assert.True(message != null); +// }); +// cm.Messaging.SendJson(new +// { +// Foo = 5, +// Bar = true +// }); +// } + +// [Ignore("no recordings yet")] +// [Test] +// [TestCase([new string[] { "-bicep" }])] +// [TestCase([new string[] { "" }])] +// public void Demo(string[] args) +// { +// if (CloudMachineCommands.Execute(args, exitProcessIfHandled: false)) return; + +// CloudMachineClient cm = new(); + +// // setup +// cm.Messaging.WhenMessageReceived((string message) => cm.Storage.Upload(BinaryData.FromString(message))); +// cm.Storage.WhenUploaded((StorageFile file) => +// { +// var content = file.Download(); +// ChatCompletion completion = cm.GetOpenAIChatClient().CompleteChat(content.ToString()); +// Console.WriteLine(completion.Content[0].Text); +// }); + +// // go! +// cm.Messaging.SendJson("Tell me something about Redmond, WA."); +// } } diff --git a/sdk/cloudmachine/Azure.CloudMachine/tests/CloudMachineTests_rag.cs b/sdk/cloudmachine/Azure.CloudMachine/tests/CloudMachineTests_rag.cs new file mode 100644 index 0000000000000..c4061f87c55ff --- /dev/null +++ b/sdk/cloudmachine/Azure.CloudMachine/tests/CloudMachineTests_rag.cs @@ -0,0 +1,131 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +#nullable enable + +using System; +using System.Threading; +using Azure.Security.KeyVault.Secrets; +using Azure.CloudMachine.OpenAI; +using Azure.CloudMachine.KeyVault; +using NUnit.Framework; +using OpenAI.Chat; +using Microsoft.Extensions.Configuration; +using System.Collections.Generic; +using Microsoft.Extensions.Primitives; +using System.Linq; +using System.IO; +using System.Text.Json; +using Azure.Storage.Blobs; + +namespace Azure.CloudMachine.Tests; + +public partial class CloudMachineTests +{ + [Ignore("no recordings yet")] + [Test] + public void RagDemo() + { + string[] testMessages = [ + "When did the continuation policy change for DefaultAzureCredential?", + "Do I love Seattle?", + "What is the current time?", + "What's the weather in Seattle?", + "Do you think I would like the weather there?", + ]; + + CloudMachineClient cm = new(default, new MockConfiguration("cmec4615e3fdfa44e")); + var chat = cm.GetOpenAIChatClient(); + var embeddings = cm.GetOpenAIEmbeddingsClient(); + + // helpers + ChatTools tools = new(typeof(MyFunctions)); + EmbeddingsVectorbase vectors = new(embeddings, null, 1000); + vectors.Add("I love Seattle."); + vectors.Add(File.ReadAllText(@"C:\Users\chriss\Desktop\Identity-README.md")); + + ChatCompletionOptions options = new(); + foreach (var definition in tools.Definitions) + { + options.Tools.Add(definition); + } + options.ToolChoice = ChatToolChoice.CreateAutoChoice(); + + List prompt = new(); + + foreach (var testMessage in testMessages) + { + Console.WriteLine($"u: {testMessage}"); + IEnumerable relatedItems = vectors.Find(testMessage); + foreach (VectorbaseEntry relatedItem in relatedItems) + { + prompt.Add(ChatMessage.CreateSystemMessage(relatedItem.Data.ToString())); + } + + prompt.Add(ChatMessage.CreateUserMessage(testMessage)); + CallOpenAI(); + // filter the prompt to only include the user message + var responses = prompt.Where(message => message is AssistantChatMessage).Select(m => m.Content[0].Text).ToList(); + } + void CallOpenAI() + { + bool requiresAction; + do + { + requiresAction = false; + var completion = chat.CompleteChat(prompt, options).Value; + switch (completion.FinishReason) + { + case ChatFinishReason.ToolCalls: + // TODO: figure out why the model is returning bogus tool call results. + // prompt.Add(new AssistantChatMessage(completion)); + // IEnumerable callResults = tools.CallAll(completion.ToolCalls); + // prompt.AddRange(callResults); + requiresAction = true; + break; + case ChatFinishReason.Length: + Console.WriteLine("Incomplete model output due to MaxTokens parameter or token limit exceeded."); + break; + case ChatFinishReason.ContentFilter: + Console.WriteLine("Omitted content due to a content filter flag."); + break; + case ChatFinishReason.Stop: + prompt.Add(new AssistantChatMessage(completion)); + break; + default: + throw new NotImplementedException("Unknown finish reason."); + } + } while (requiresAction); + } + } + + public static class MyFunctions + { + [System.ComponentModel.Description("Returns the current weather at the specified location")] + public static string GetCurrentWeather(string location, string? unit = default) => $"1 million degrees {unit}"; + + [System.ComponentModel.Description("Returns the user's current location")] + public static string GetCurrentLocation() => "Planet Earth"; + + [System.ComponentModel.Description("Returns the current time.")] + public static string GetCurrentTime() => DateTimeOffset.Now.ToString("t"); + } + + private class MockConfiguration : IConfiguration + { + private readonly string _cmId; + + public MockConfiguration(string cmId) + { + _cmId = cmId; + } + + public string? this[string key] { get => _cmId; set => throw new NotImplementedException(); } + + public IEnumerable GetChildren() => throw new NotImplementedException(); + + public IChangeToken GetReloadToken() => throw new NotImplementedException(); + + public IConfigurationSection GetSection(string key) => throw new NotImplementedException(); + } +} diff --git a/sdk/cloudmachine/Azure.CloudMachine/tests/ConnectionTests.cs b/sdk/cloudmachine/Azure.CloudMachine/tests/ConnectionTests.cs new file mode 100644 index 0000000000000..33e28a6dbde7c --- /dev/null +++ b/sdk/cloudmachine/Azure.CloudMachine/tests/ConnectionTests.cs @@ -0,0 +1,86 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +#nullable enable + +using System; +using System.Linq; +using System.Text.Json; +using Azure.CloudMachine.KeyVault; +using Azure.CloudMachine.OpenAI; +using Azure.Storage.Blobs; +using NUnit.Framework; +using OpenAI.Chat; + +[assembly: NonParallelizable] + +namespace Azure.CloudMachine.Tests; + +public class ConnectionTests +{ + [Test] + [TestCase([new string[0]])] + public void TwoClients(string[] args) + { + CloudMachineInfrastructure infra = new(); + infra.AddFeature(new OpenAIModelFeature("gpt-35-turbo", "0125")); + if (args.Contains("-azd")) Azd.Init(infra); + + CloudMachineClient client = infra.GetClient(); + + ValidateClient(client); + } + + // this tests the scenario where provisioning is done in one app, but runtime is done by another app + // the connections needs to be serialized and deserialized + [Test] + public void TwoApps() + { + // app 1 (with a dependency on the CDK) + CloudMachineInfrastructure infra = new(); + infra.AddFeature(new OpenAIModelFeature("gpt-35-turbo", "0125")); + //if (args.Contains("-azd")) Azd.Init(infra); + BinaryData serializedConnections = BinaryData.FromObjectAsJson(infra.Connections); + + // app 2 (no dependency on the CDK) + ConnectionCollection deserializedConnections = JsonSerializer.Deserialize(serializedConnections)!; + CloudMachineClient client = new(connections: deserializedConnections); + + ValidateClient(client); + } + + // TODO: maybe this is too hacky. do we really need this? + [Test] + [TestCase([new string[0]])] + public void SingleClientAdd(string[] args) + { + CloudMachineClient client = new(); + client.AddFeature(new OpenAIModelFeature("gpt-35-turbo", "0125")); + + if (args.Contains("-azd")) Azd.Init(client); + + ChatClient chat = client.GetOpenAIChatClient(); + } + + [Test] + public void SingleClientConfigure() + { + CloudMachineClient client = new(); + client.Configure((infrastructure) => + { + infrastructure.AddFeature(new KeyVaultFeature()); + infrastructure.AddFeature(new OpenAIModelFeature("gpt-35-turbo", "0125")); + infrastructure.AddFeature(new OpenAIModelFeature("text-embedding-ada-002", "2", AIModelKind.Embedding)); + }); + ValidateClient(client); + var embeddings = client.GetOpenAIEmbeddingsClient(); + } + + private static void ValidateClient(CloudMachineClient client) + { + ChatClient chat = client.GetOpenAIChatClient(); + StorageServices storage = client.Storage; + BlobContainerClient container = storage.GetContainer(default); + MessagingServices messaging = client.Messaging; + } +} diff --git a/sdk/cloudmachine/Azure.CloudMachine/tests/GettingStarted.cs b/sdk/cloudmachine/Azure.CloudMachine/tests/GettingStarted.cs new file mode 100644 index 0000000000000..ee097e04ba377 --- /dev/null +++ b/sdk/cloudmachine/Azure.CloudMachine/tests/GettingStarted.cs @@ -0,0 +1,35 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +#nullable enable + +using System; +using Azure.CloudMachine.OpenAI; +using NUnit.Framework; +using System.Linq; +using OpenAI.Chat; + +namespace Azure.CloudMachine.Tests; + +public partial class CloudMachineTests +{ + [Test] + [TestCase([new string[] { "-azd" }])] + public void GettingStarted(string[] args) + { + CloudMachineInfrastructure infra = new(); + infra.AddFeature(new OpenAIModelFeature("gpt-35-turbo", "0125")); + + if (args.Contains("-azd")) { + Azd.Init(infra); + return; + } + + // TODO: we need to allow newing up the client. + CloudMachineClient client = infra.GetClient(); + + ChatClient chat = client.GetOpenAIChatClient(); + string completion = chat.CompleteChat("List all noble gases.").AsText(); + Console.WriteLine(completion); + } +} diff --git a/sdk/provisioning/Azure.Provisioning.CloudMachine/Azure.CloudMachine.sln b/sdk/cloudmachine/Azure.Provisioning.CloudMachine/Azure.CloudMachine.sln similarity index 75% rename from sdk/provisioning/Azure.Provisioning.CloudMachine/Azure.CloudMachine.sln rename to sdk/cloudmachine/Azure.Provisioning.CloudMachine/Azure.CloudMachine.sln index d2a38b3fc057e..0cc453bfe0e2a 100644 --- a/sdk/provisioning/Azure.Provisioning.CloudMachine/Azure.CloudMachine.sln +++ b/sdk/cloudmachine/Azure.Provisioning.CloudMachine/Azure.CloudMachine.sln @@ -7,15 +7,13 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Azure.Provisioning.CloudMac EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Azure.Provisioning.CloudMachine.Tests", "tests\Azure.Provisioning.CloudMachine.Tests.csproj", "{46DCEF27-4157-4FB6-A283-B8484EC45665}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Azure.CloudMachine", "..\..\cloudmachine\Azure.CloudMachine\src\Azure.CloudMachine.csproj", "{AE9C0E9B-27D9-4220-98DA-0CB6A7936277}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Azure.CloudMachine", "..\Azure.CloudMachine\src\Azure.CloudMachine.csproj", "{AE9C0E9B-27D9-4220-98DA-0CB6A7936277}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Azure.CloudMachine.Tests", "..\..\cloudmachine\Azure.CloudMachine\tests\Azure.CloudMachine.Tests.csproj", "{246D5F77-0151-40C5-8644-26A468A57CDC}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Azure.CloudMachine.Tests", "..\Azure.CloudMachine\tests\Azure.CloudMachine.Tests.csproj", "{246D5F77-0151-40C5-8644-26A468A57CDC}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Azure.Provisioning.Deployment", "..\Azure.Provisioning.Deployment\src\Azure.Provisioning.Deployment.csproj", "{4562F8C1-9FE3-4B68-BBB2-D052B49C915A}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Azure.CloudMachine.Web", "..\Azure.CloudMachine.Web\src\Azure.CloudMachine.Web.csproj", "{DD18D434-4665-43CA-97C0-4D63894EE23C}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Azure.CloudMachine.Web", "..\..\cloudmachine\Azure.CloudMachine.Web\src\Azure.CloudMachine.Web.csproj", "{DD18D434-4665-43CA-97C0-4D63894EE23C}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Azure.CloudMachine.All", "..\..\cloudmachine\Azure.CloudMachine.All\src\Azure.CloudMachine.All.csproj", "{4E651FF9-EF43-4302-B77B-83C08D8FD117}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Azure.CloudMachine.All", "..\Azure.CloudMachine.All\src\Azure.CloudMachine.All.csproj", "{4E651FF9-EF43-4302-B77B-83C08D8FD117}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -39,10 +37,6 @@ Global {246D5F77-0151-40C5-8644-26A468A57CDC}.Debug|Any CPU.Build.0 = Debug|Any CPU {246D5F77-0151-40C5-8644-26A468A57CDC}.Release|Any CPU.ActiveCfg = Release|Any CPU {246D5F77-0151-40C5-8644-26A468A57CDC}.Release|Any CPU.Build.0 = Release|Any CPU - {4562F8C1-9FE3-4B68-BBB2-D052B49C915A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {4562F8C1-9FE3-4B68-BBB2-D052B49C915A}.Debug|Any CPU.Build.0 = Debug|Any CPU - {4562F8C1-9FE3-4B68-BBB2-D052B49C915A}.Release|Any CPU.ActiveCfg = Release|Any CPU - {4562F8C1-9FE3-4B68-BBB2-D052B49C915A}.Release|Any CPU.Build.0 = Release|Any CPU {DD18D434-4665-43CA-97C0-4D63894EE23C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {DD18D434-4665-43CA-97C0-4D63894EE23C}.Debug|Any CPU.Build.0 = Debug|Any CPU {DD18D434-4665-43CA-97C0-4D63894EE23C}.Release|Any CPU.ActiveCfg = Release|Any CPU diff --git a/sdk/provisioning/Azure.Provisioning.CloudMachine/CHANGELOG.md b/sdk/cloudmachine/Azure.Provisioning.CloudMachine/CHANGELOG.md similarity index 100% rename from sdk/provisioning/Azure.Provisioning.CloudMachine/CHANGELOG.md rename to sdk/cloudmachine/Azure.Provisioning.CloudMachine/CHANGELOG.md diff --git a/sdk/provisioning/Azure.Provisioning.CloudMachine/README.md b/sdk/cloudmachine/Azure.Provisioning.CloudMachine/README.md similarity index 100% rename from sdk/provisioning/Azure.Provisioning.CloudMachine/README.md rename to sdk/cloudmachine/Azure.Provisioning.CloudMachine/README.md diff --git a/sdk/cloudmachine/Azure.Provisioning.CloudMachine/api/Azure.Provisioning.CloudMachine.net8.0.cs b/sdk/cloudmachine/Azure.Provisioning.CloudMachine/api/Azure.Provisioning.CloudMachine.net8.0.cs new file mode 100644 index 0000000000000..a526056e2055f --- /dev/null +++ b/sdk/cloudmachine/Azure.Provisioning.CloudMachine/api/Azure.Provisioning.CloudMachine.net8.0.cs @@ -0,0 +1,136 @@ +namespace Azure.CloudMachine +{ + public static partial class Azd + { + public static void Init(Azure.CloudMachine.CloudMachineClient client, string? infraDirectory = null) { } + public static void Init(Azure.CloudMachine.CloudMachineInfrastructure infra, string? infraDirectory = null) { } + } + public static partial class CloudMachineClientExtensions + { + public static T AddFeature(this Azure.CloudMachine.CloudMachineClient client, T feature) where T : Azure.Provisioning.CloudMachine.CloudMachineFeature { throw null; } + public static void Configure(this Azure.CloudMachine.CloudMachineClient client, System.Action? configure = null) { } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + public static Azure.CloudMachine.CloudMachineInfrastructure GetInfrastructure(this Azure.CloudMachine.CloudMachineClient client) { throw null; } + } + public static partial class CloudMachineCommands + { + public static bool Execute(string[] args, System.Action? configure = null, bool exitProcessIfHandled = true) { throw null; } + } + public partial class CloudMachineInfrastructure + { + public CloudMachineInfrastructure(string? cmId = null) { } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + public Azure.CloudMachine.ConnectionCollection Connections { get { throw null; } } + public Azure.CloudMachine.FeatureCollection Features { get { throw null; } } + public string Id { get { throw null; } } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + public Azure.Provisioning.Roles.UserAssignedIdentity Identity { get { throw null; } } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + public Azure.Provisioning.ProvisioningParameter PrincipalIdParameter { get { throw null; } } + public void AddEndpoints() { } + public T AddFeature(T feature) where T : Azure.Provisioning.CloudMachine.CloudMachineFeature { throw null; } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + public void AddResource(Azure.Provisioning.Primitives.NamedProvisionableConstruct resource) { } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + public Azure.Provisioning.ProvisioningPlan Build(Azure.Provisioning.ProvisioningBuildOptions? context = null) { throw null; } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + public override bool Equals(object? obj) { throw null; } + public Azure.CloudMachine.CloudMachineClient GetClient() { throw null; } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + public override int GetHashCode() { throw null; } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + public override string ToString() { throw null; } + } + public partial class FeatureCollection : System.Collections.Generic.IEnumerable, System.Collections.IEnumerable + { + internal FeatureCollection() { } + public System.Collections.Generic.IEnumerable FindAll() where T : Azure.Provisioning.CloudMachine.CloudMachineFeature { throw null; } + public System.Collections.Generic.IEnumerator GetEnumerator() { throw null; } + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { throw null; } + } + public partial class ServiceBusNamespaceFeature : Azure.Provisioning.CloudMachine.CloudMachineFeature + { + public ServiceBusNamespaceFeature(string name, Azure.Provisioning.ServiceBus.ServiceBusSkuName sku = Azure.Provisioning.ServiceBus.ServiceBusSkuName.Standard, Azure.Provisioning.ServiceBus.ServiceBusSkuTier tier = Azure.Provisioning.ServiceBus.ServiceBusSkuTier.Standard) { } + protected internal override void EmitConnections(Azure.CloudMachine.ConnectionCollection connections, string cmId) { } + protected override Azure.Provisioning.Primitives.ProvisionableResource EmitResources(Azure.CloudMachine.CloudMachineInfrastructure infrastructure) { throw null; } + } + public partial class ServiceBusSubscriptionFeature : Azure.Provisioning.CloudMachine.CloudMachineFeature + { + public ServiceBusSubscriptionFeature(string name, Azure.CloudMachine.ServiceBusTopicFeature parent) { } + protected internal override void EmitConnections(Azure.CloudMachine.ConnectionCollection connections, string cmId) { } + protected override Azure.Provisioning.Primitives.ProvisionableResource EmitResources(Azure.CloudMachine.CloudMachineInfrastructure infrastructure) { throw null; } + } + public partial class ServiceBusTopicFeature : Azure.Provisioning.CloudMachine.CloudMachineFeature + { + public ServiceBusTopicFeature(string name, Azure.CloudMachine.ServiceBusNamespaceFeature parent) { } + public string Name { get { throw null; } } + protected internal override void EmitConnections(Azure.CloudMachine.ConnectionCollection connections, string cmId) { } + protected override Azure.Provisioning.Primitives.ProvisionableResource EmitResources(Azure.CloudMachine.CloudMachineInfrastructure infrastructure) { throw null; } + } +} +namespace Azure.CloudMachine.AppService +{ + public partial class AppServiceFeature : Azure.Provisioning.CloudMachine.CloudMachineFeature + { + public AppServiceFeature() { } + public Azure.Provisioning.AppService.AppServiceSkuDescription Sku { get { throw null; } set { } } + protected override Azure.Provisioning.Primitives.ProvisionableResource EmitResources(Azure.CloudMachine.CloudMachineInfrastructure infrastructure) { throw null; } + } +} +namespace Azure.CloudMachine.KeyVault +{ + public partial class KeyVaultFeature : Azure.Provisioning.CloudMachine.CloudMachineFeature + { + public KeyVaultFeature(Azure.Provisioning.KeyVault.KeyVaultSku? sku = null) { } + public Azure.Provisioning.KeyVault.KeyVaultSku Sku { get { throw null; } set { } } + protected internal override void EmitConnections(Azure.CloudMachine.ConnectionCollection connections, string cmId) { } + protected override Azure.Provisioning.Primitives.ProvisionableResource EmitResources(Azure.CloudMachine.CloudMachineInfrastructure infrastructure) { throw null; } + } +} +namespace Azure.CloudMachine.OpenAI +{ + public enum AIModelKind + { + Chat = 0, + Embedding = 1, + } + public partial class OpenAIModelFeature : Azure.Provisioning.CloudMachine.CloudMachineFeature + { + public OpenAIModelFeature(string model, string modelVersion, Azure.CloudMachine.OpenAI.AIModelKind kind = Azure.CloudMachine.OpenAI.AIModelKind.Chat) { } + public string Model { get { throw null; } } + public string ModelVersion { get { throw null; } } + protected internal override void EmitConnections(Azure.CloudMachine.ConnectionCollection connections, string cmId) { } + protected internal override void EmitFeatures(Azure.CloudMachine.FeatureCollection features, string cmId) { } + protected override Azure.Provisioning.Primitives.ProvisionableResource EmitResources(Azure.CloudMachine.CloudMachineInfrastructure cm) { throw null; } + } +} +namespace Azure.Provisioning.CloudMachine +{ + public abstract partial class CloudMachineFeature + { + protected CloudMachineFeature() { } + protected internal System.Collections.Generic.Dictionary RequiredSystemRoles { get { throw null; } } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + public Azure.Provisioning.Primitives.ProvisionableResource Resource { get { throw null; } } + protected internal virtual void EmitConnections(Azure.CloudMachine.ConnectionCollection connections, string cmId) { } + protected internal virtual void EmitFeatures(Azure.CloudMachine.FeatureCollection features, string cmId) { } + protected abstract Azure.Provisioning.Primitives.ProvisionableResource EmitResources(Azure.CloudMachine.CloudMachineInfrastructure cm); + protected static T EnsureEmits(Azure.Provisioning.CloudMachine.CloudMachineFeature feature) { throw null; } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + public override bool Equals(object? obj) { throw null; } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + public override int GetHashCode() { throw null; } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + public override string ToString() { throw null; } + } +} +namespace System.ClientModel.TypeSpec +{ + public static partial class TypeSpecWriter + { + public static void WriteModel(System.IO.Stream output, System.Type model) { } + public static void WriteModel(System.IO.Stream output) { } + public static void WriteServer(System.IO.Stream output, System.Type service) { } + public static void WriteServer(System.IO.Stream output) { } + } +} diff --git a/sdk/cloudmachine/Azure.Provisioning.CloudMachine/api/Azure.Provisioning.CloudMachine.netstandard2.0.cs b/sdk/cloudmachine/Azure.Provisioning.CloudMachine/api/Azure.Provisioning.CloudMachine.netstandard2.0.cs new file mode 100644 index 0000000000000..a526056e2055f --- /dev/null +++ b/sdk/cloudmachine/Azure.Provisioning.CloudMachine/api/Azure.Provisioning.CloudMachine.netstandard2.0.cs @@ -0,0 +1,136 @@ +namespace Azure.CloudMachine +{ + public static partial class Azd + { + public static void Init(Azure.CloudMachine.CloudMachineClient client, string? infraDirectory = null) { } + public static void Init(Azure.CloudMachine.CloudMachineInfrastructure infra, string? infraDirectory = null) { } + } + public static partial class CloudMachineClientExtensions + { + public static T AddFeature(this Azure.CloudMachine.CloudMachineClient client, T feature) where T : Azure.Provisioning.CloudMachine.CloudMachineFeature { throw null; } + public static void Configure(this Azure.CloudMachine.CloudMachineClient client, System.Action? configure = null) { } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + public static Azure.CloudMachine.CloudMachineInfrastructure GetInfrastructure(this Azure.CloudMachine.CloudMachineClient client) { throw null; } + } + public static partial class CloudMachineCommands + { + public static bool Execute(string[] args, System.Action? configure = null, bool exitProcessIfHandled = true) { throw null; } + } + public partial class CloudMachineInfrastructure + { + public CloudMachineInfrastructure(string? cmId = null) { } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + public Azure.CloudMachine.ConnectionCollection Connections { get { throw null; } } + public Azure.CloudMachine.FeatureCollection Features { get { throw null; } } + public string Id { get { throw null; } } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + public Azure.Provisioning.Roles.UserAssignedIdentity Identity { get { throw null; } } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + public Azure.Provisioning.ProvisioningParameter PrincipalIdParameter { get { throw null; } } + public void AddEndpoints() { } + public T AddFeature(T feature) where T : Azure.Provisioning.CloudMachine.CloudMachineFeature { throw null; } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + public void AddResource(Azure.Provisioning.Primitives.NamedProvisionableConstruct resource) { } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + public Azure.Provisioning.ProvisioningPlan Build(Azure.Provisioning.ProvisioningBuildOptions? context = null) { throw null; } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + public override bool Equals(object? obj) { throw null; } + public Azure.CloudMachine.CloudMachineClient GetClient() { throw null; } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + public override int GetHashCode() { throw null; } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + public override string ToString() { throw null; } + } + public partial class FeatureCollection : System.Collections.Generic.IEnumerable, System.Collections.IEnumerable + { + internal FeatureCollection() { } + public System.Collections.Generic.IEnumerable FindAll() where T : Azure.Provisioning.CloudMachine.CloudMachineFeature { throw null; } + public System.Collections.Generic.IEnumerator GetEnumerator() { throw null; } + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { throw null; } + } + public partial class ServiceBusNamespaceFeature : Azure.Provisioning.CloudMachine.CloudMachineFeature + { + public ServiceBusNamespaceFeature(string name, Azure.Provisioning.ServiceBus.ServiceBusSkuName sku = Azure.Provisioning.ServiceBus.ServiceBusSkuName.Standard, Azure.Provisioning.ServiceBus.ServiceBusSkuTier tier = Azure.Provisioning.ServiceBus.ServiceBusSkuTier.Standard) { } + protected internal override void EmitConnections(Azure.CloudMachine.ConnectionCollection connections, string cmId) { } + protected override Azure.Provisioning.Primitives.ProvisionableResource EmitResources(Azure.CloudMachine.CloudMachineInfrastructure infrastructure) { throw null; } + } + public partial class ServiceBusSubscriptionFeature : Azure.Provisioning.CloudMachine.CloudMachineFeature + { + public ServiceBusSubscriptionFeature(string name, Azure.CloudMachine.ServiceBusTopicFeature parent) { } + protected internal override void EmitConnections(Azure.CloudMachine.ConnectionCollection connections, string cmId) { } + protected override Azure.Provisioning.Primitives.ProvisionableResource EmitResources(Azure.CloudMachine.CloudMachineInfrastructure infrastructure) { throw null; } + } + public partial class ServiceBusTopicFeature : Azure.Provisioning.CloudMachine.CloudMachineFeature + { + public ServiceBusTopicFeature(string name, Azure.CloudMachine.ServiceBusNamespaceFeature parent) { } + public string Name { get { throw null; } } + protected internal override void EmitConnections(Azure.CloudMachine.ConnectionCollection connections, string cmId) { } + protected override Azure.Provisioning.Primitives.ProvisionableResource EmitResources(Azure.CloudMachine.CloudMachineInfrastructure infrastructure) { throw null; } + } +} +namespace Azure.CloudMachine.AppService +{ + public partial class AppServiceFeature : Azure.Provisioning.CloudMachine.CloudMachineFeature + { + public AppServiceFeature() { } + public Azure.Provisioning.AppService.AppServiceSkuDescription Sku { get { throw null; } set { } } + protected override Azure.Provisioning.Primitives.ProvisionableResource EmitResources(Azure.CloudMachine.CloudMachineInfrastructure infrastructure) { throw null; } + } +} +namespace Azure.CloudMachine.KeyVault +{ + public partial class KeyVaultFeature : Azure.Provisioning.CloudMachine.CloudMachineFeature + { + public KeyVaultFeature(Azure.Provisioning.KeyVault.KeyVaultSku? sku = null) { } + public Azure.Provisioning.KeyVault.KeyVaultSku Sku { get { throw null; } set { } } + protected internal override void EmitConnections(Azure.CloudMachine.ConnectionCollection connections, string cmId) { } + protected override Azure.Provisioning.Primitives.ProvisionableResource EmitResources(Azure.CloudMachine.CloudMachineInfrastructure infrastructure) { throw null; } + } +} +namespace Azure.CloudMachine.OpenAI +{ + public enum AIModelKind + { + Chat = 0, + Embedding = 1, + } + public partial class OpenAIModelFeature : Azure.Provisioning.CloudMachine.CloudMachineFeature + { + public OpenAIModelFeature(string model, string modelVersion, Azure.CloudMachine.OpenAI.AIModelKind kind = Azure.CloudMachine.OpenAI.AIModelKind.Chat) { } + public string Model { get { throw null; } } + public string ModelVersion { get { throw null; } } + protected internal override void EmitConnections(Azure.CloudMachine.ConnectionCollection connections, string cmId) { } + protected internal override void EmitFeatures(Azure.CloudMachine.FeatureCollection features, string cmId) { } + protected override Azure.Provisioning.Primitives.ProvisionableResource EmitResources(Azure.CloudMachine.CloudMachineInfrastructure cm) { throw null; } + } +} +namespace Azure.Provisioning.CloudMachine +{ + public abstract partial class CloudMachineFeature + { + protected CloudMachineFeature() { } + protected internal System.Collections.Generic.Dictionary RequiredSystemRoles { get { throw null; } } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + public Azure.Provisioning.Primitives.ProvisionableResource Resource { get { throw null; } } + protected internal virtual void EmitConnections(Azure.CloudMachine.ConnectionCollection connections, string cmId) { } + protected internal virtual void EmitFeatures(Azure.CloudMachine.FeatureCollection features, string cmId) { } + protected abstract Azure.Provisioning.Primitives.ProvisionableResource EmitResources(Azure.CloudMachine.CloudMachineInfrastructure cm); + protected static T EnsureEmits(Azure.Provisioning.CloudMachine.CloudMachineFeature feature) { throw null; } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + public override bool Equals(object? obj) { throw null; } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + public override int GetHashCode() { throw null; } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + public override string ToString() { throw null; } + } +} +namespace System.ClientModel.TypeSpec +{ + public static partial class TypeSpecWriter + { + public static void WriteModel(System.IO.Stream output, System.Type model) { } + public static void WriteModel(System.IO.Stream output) { } + public static void WriteServer(System.IO.Stream output, System.Type service) { } + public static void WriteServer(System.IO.Stream output) { } + } +} diff --git a/sdk/provisioning/Azure.Provisioning.CloudMachine/src/Azure.Provisioning.CloudMachine.csproj b/sdk/cloudmachine/Azure.Provisioning.CloudMachine/src/Azure.Provisioning.CloudMachine.csproj similarity index 89% rename from sdk/provisioning/Azure.Provisioning.CloudMachine/src/Azure.Provisioning.CloudMachine.csproj rename to sdk/cloudmachine/Azure.Provisioning.CloudMachine/src/Azure.Provisioning.CloudMachine.csproj index ff39f04075f17..89a9b23ce3fc3 100644 --- a/sdk/provisioning/Azure.Provisioning.CloudMachine/src/Azure.Provisioning.CloudMachine.csproj +++ b/sdk/cloudmachine/Azure.Provisioning.CloudMachine/src/Azure.Provisioning.CloudMachine.csproj @@ -5,12 +5,13 @@ 1.0.0-beta.1 $(RequiredTargetFrameworks) 12 - + enable CS1591;AZC0007 + diff --git a/sdk/cloudmachine/Azure.Provisioning.CloudMachine/src/CloudMachineClientExtensions.cs b/sdk/cloudmachine/Azure.Provisioning.CloudMachine/src/CloudMachineClientExtensions.cs new file mode 100644 index 0000000000000..7559dd4437623 --- /dev/null +++ b/sdk/cloudmachine/Azure.Provisioning.CloudMachine/src/CloudMachineClientExtensions.cs @@ -0,0 +1,54 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.ComponentModel; +using Azure.Core; +using Azure.Provisioning.CloudMachine; + +namespace Azure.CloudMachine; + +public static class CloudMachineClientExtensions +{ + public static void Configure(this CloudMachineClient client, Action? configure = default) + { + CloudMachineInfrastructure cmi = new(client.Id); + if (configure != default) + { + configure(cmi); + } + foreach (ClientConnection clientConnection in cmi.Connections) + { + client.Connections.Add(clientConnection); + } + Azd.Init(cmi); + } + + public static T AddFeature(this CloudMachineClient client, T feature) where T : CloudMachineFeature + { + CloudMachineInfrastructure infra = client.GetInfrastructure(); + infra.AddFeature(feature); + CopyConnections(infra.Connections, client.Connections); + return feature; + } + + [EditorBrowsable(EditorBrowsableState.Never)] + public static CloudMachineInfrastructure GetInfrastructure(this CloudMachineClient client) + { + return client.Subclients.Get(() => + { + CloudMachineInfrastructure infra = new CloudMachineInfrastructure(client.Id); + return infra; + }); + } + + private static void CopyConnections(ConnectionCollection from, ConnectionCollection to) + { + foreach (var connection in from) + { + if (!to.Contains(connection)) { + to.Add(connection); + } + } + } +} diff --git a/sdk/cloudmachine/Azure.Provisioning.CloudMachine/src/CloudMachineInfrastructure/CloudMachineConnections.cs b/sdk/cloudmachine/Azure.Provisioning.CloudMachine/src/CloudMachineInfrastructure/CloudMachineConnections.cs new file mode 100644 index 0000000000000..c86355df92bad --- /dev/null +++ b/sdk/cloudmachine/Azure.Provisioning.CloudMachine/src/CloudMachineInfrastructure/CloudMachineConnections.cs @@ -0,0 +1,30 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using Azure.Core; + +namespace Azure.CloudMachine.Core +{ + internal static class CloudMachineConnections + { + public static readonly string DefaultBlobContainerName = "default"; + + public static ClientConnection CreateDefaultBlobContainerConnection(string cmId) + { + ClientConnection connection = new( + $"Azure.Storage.Blobs.BlobContainerClient@{DefaultBlobContainerName}", + $"https://{cmId}.blob.core.windows.net/{DefaultBlobContainerName}" + ); + return connection; + } + + public static ClientConnection CreateDefaultServiceBusConnection(string cmId) + { + ClientConnection connection = new( + "Azure.Messaging.ServiceBus.ServiceBusClient", + $"https://{cmId}.servicebus.windows.net/" + ); + return connection; + } + } +} diff --git a/sdk/cloudmachine/Azure.Provisioning.CloudMachine/src/CloudMachineInfrastructure/CloudMachineFeature.cs b/sdk/cloudmachine/Azure.Provisioning.CloudMachine/src/CloudMachineInfrastructure/CloudMachineFeature.cs new file mode 100644 index 0000000000000..a43077e5db0b2 --- /dev/null +++ b/sdk/cloudmachine/Azure.Provisioning.CloudMachine/src/CloudMachineInfrastructure/CloudMachineFeature.cs @@ -0,0 +1,60 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using Azure.CloudMachine; +using Azure.Provisioning.Primitives; + +namespace Azure.Provisioning.CloudMachine; + +public abstract class CloudMachineFeature +{ + private ProvisionableResource? _resource; + + protected abstract ProvisionableResource EmitResources(CloudMachineInfrastructure cm); + protected internal virtual void EmitConnections(ConnectionCollection connections, string cmId) { } + protected internal virtual void EmitFeatures(FeatureCollection features, string cmId) + => features.Add(this); + + internal ProvisionableResource Emit(CloudMachineInfrastructure cm) + { + if (_resource == null) + { + ProvisionableResource namedResource = EmitResources(cm); + _resource = namedResource; + } + return Resource; + } + + [EditorBrowsable(EditorBrowsableState.Never)] + public ProvisionableResource Resource { + get + { + if (_resource == null) + { + throw new InvalidOperationException("Feature has not been emitted yet."); + } + return _resource; + } + } + + protected internal Dictionary RequiredSystemRoles { get; } = []; + + protected static T EnsureEmits(CloudMachineFeature feature) + { + if (feature.Resource is T typed) + return typed; + throw new ArgumentException($"Expected resource of type {typeof(T).Name}, but got {feature.GetType().Name}"); + } + + [EditorBrowsable(EditorBrowsableState.Never)] + public override string ToString() => base.ToString()!; + + [EditorBrowsable(EditorBrowsableState.Never)] + public override int GetHashCode() => base.GetHashCode(); + + [EditorBrowsable(EditorBrowsableState.Never)] + public override bool Equals(object? obj) => base.Equals(obj); +} diff --git a/sdk/cloudmachine/Azure.Provisioning.CloudMachine/src/CloudMachineInfrastructure/CloudMachineInfrastructure.cs b/sdk/cloudmachine/Azure.Provisioning.CloudMachine/src/CloudMachineInfrastructure/CloudMachineInfrastructure.cs new file mode 100644 index 0000000000000..2e3c0adb91383 --- /dev/null +++ b/sdk/cloudmachine/Azure.Provisioning.CloudMachine/src/CloudMachineInfrastructure/CloudMachineInfrastructure.cs @@ -0,0 +1,130 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using Azure.Provisioning; +using Azure.Provisioning.CloudMachine; +using Azure.Provisioning.Expressions; +using Azure.Provisioning.Primitives; +using Azure.Provisioning.Roles; + +namespace Azure.CloudMachine; + +public class CloudMachineInfrastructure +{ + private readonly Infrastructure _infrastructure = new("cm"); + private readonly List _constrcuts = []; + + internal List Endpoints { get; } = []; + public FeatureCollection Features { get; } = new(); + + [EditorBrowsable(EditorBrowsableState.Never)] + public ConnectionCollection Connections { get; } = []; + + [EditorBrowsable(EditorBrowsableState.Never)] + public UserAssignedIdentity Identity { get; private set; } + public string Id { get; private set; } + + public CloudMachineClient GetClient() + { + CloudMachineClient client = new(connections: Connections); + return client; + } + + /// + /// The common principalId parameter. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public ProvisioningParameter PrincipalIdParameter => new("principalId", typeof(string)); + + public CloudMachineInfrastructure(string? cmId = default) + { + if (cmId == default) + { + cmId = CloudMachineWorkspace.ReadOrCreateCloudMachineId(); + } + Id = cmId; + + // setup CM identity + Identity = new UserAssignedIdentity("cm_identity"); + Identity.Name = Id; + _infrastructure.Add(new ProvisioningOutput("cm_managed_identity_id", typeof(string)) { Value = Identity.Id }); + + // Add core features + var storage = AddFeature(new StorageAccountFeature(Id)); + var blobs = AddFeature(new BlobServiceFeature(storage)); + var defaultContainer = AddFeature(new BlobContainerFeature(blobs)); + var sbNamespace = AddFeature(new ServiceBusNamespaceFeature(Id)); + var sbTopicPrivate = AddFeature(new ServiceBusTopicFeature("cm_servicebus_topic_private", sbNamespace)); + var sbTopicDefault = AddFeature(new ServiceBusTopicFeature("cm_servicebus_default_topic", sbNamespace)); + AddFeature(new ServiceBusSubscriptionFeature("cm_servicebus_subscription_private", sbTopicPrivate)); // TODO: should private connections not be in the Connections collection? + AddFeature(new ServiceBusSubscriptionFeature("cm_servicebus_subscription_default", sbTopicDefault)); + var systemTopic = AddFeature(new EventGridSystemTopicFeature(Id, storage, "Microsoft.Storage.StorageAccounts")); + AddFeature(new SystemTopicEventSubscriptionFeature("cm_eventgrid_subscription_blob", systemTopic, sbTopicPrivate, sbNamespace)); + } + + public T AddFeature(T feature) where T: CloudMachineFeature + { + feature.EmitFeatures(Features, Id); + feature.EmitConnections(Connections, Id); + return feature; + } + + public void AddEndpoints() + { + Type endpointsType = typeof(T); + if (!endpointsType.IsInterface) + throw new InvalidOperationException("Endpoints type must be an interface."); + Endpoints.Add(endpointsType); + } + + [EditorBrowsable(EditorBrowsableState.Never)] + public void AddResource(NamedProvisionableConstruct resource) + { + _constrcuts.Add(resource); + } + + [EditorBrowsable(EditorBrowsableState.Never)] + public ProvisioningPlan Build(ProvisioningBuildOptions? context = default) + { + Features.Emit(this); + + // Always add a default location parameter. + // azd assumes there will be a location parameter for every module. + // The Infrastructure location resolver will resolve unset Location properties to this parameter. + _infrastructure.Add(new ProvisioningParameter("location", typeof(string)) + { + Description = "The location for the resource(s) to be deployed.", + Value = BicepFunction.GetResourceGroup().Location + }); + + _infrastructure.Add(new ProvisioningParameter("principalId", typeof(string)) + { + Description = "The objectId of the current user principal.", + }); + + _infrastructure.Add(Identity); + + // Add any add-on resources to the infrastructure. + foreach (Provisionable resource in _constrcuts) + { + _infrastructure.Add(resource); + } + + context ??= new ProvisioningBuildOptions(); + // This must occur after the features have been emitted. + context.InfrastructureResolvers.Add(new RoleResolver(Features.RoleAnnotations, [Identity], [PrincipalIdParameter])); + return _infrastructure.Build(context); + } + + [EditorBrowsable(EditorBrowsableState.Never)] + public override string ToString() => Id; + + [EditorBrowsable(EditorBrowsableState.Never)] + public override int GetHashCode() => base.GetHashCode(); + + [EditorBrowsable(EditorBrowsableState.Never)] + public override bool Equals(object? obj) => base.Equals(obj); +} diff --git a/sdk/provisioning/Azure.Provisioning.CloudMachine/src/CDKLevel3/FeatureCollection.cs b/sdk/cloudmachine/Azure.Provisioning.CloudMachine/src/CloudMachineInfrastructure/FeatureCollection.cs similarity index 80% rename from sdk/provisioning/Azure.Provisioning.CloudMachine/src/CDKLevel3/FeatureCollection.cs rename to sdk/cloudmachine/Azure.Provisioning.CloudMachine/src/CloudMachineInfrastructure/FeatureCollection.cs index 50cdbfadbbff8..07e24da9bd102 100644 --- a/sdk/provisioning/Azure.Provisioning.CloudMachine/src/CDKLevel3/FeatureCollection.cs +++ b/sdk/cloudmachine/Azure.Provisioning.CloudMachine/src/CloudMachineInfrastructure/FeatureCollection.cs @@ -2,6 +2,7 @@ // Licensed under the MIT License. using System; +using System.Collections; using System.Collections.Generic; using System.Linq; using Azure.Provisioning.CloudMachine; @@ -9,7 +10,7 @@ namespace Azure.CloudMachine; -public class FeatureCollection +public class FeatureCollection : IEnumerable { private CloudMachineFeature[] _items = new CloudMachineFeature[4]; private int _count; @@ -20,6 +21,8 @@ public class FeatureCollection .SelectMany(d => d) .ToDictionary(kvp => kvp.Key, kvp => kvp.Value); + internal FeatureCollection() { } + public IEnumerable FindAll() where T : CloudMachineFeature { for (int i = 0; i < _count; i++) @@ -58,4 +61,15 @@ internal void Emit(CloudMachineInfrastructure infrastructure) feature.Emit(infrastructure); } } + + public IEnumerator GetEnumerator() + { + for (int i=0; i < _count; i++) + { + yield return _items[i]; + } + } + + IEnumerator IEnumerable.GetEnumerator() + => GetEnumerator(); } diff --git a/sdk/cloudmachine/Azure.Provisioning.CloudMachine/src/CloudMachineInfrastructure/RoleResolver.cs b/sdk/cloudmachine/Azure.Provisioning.CloudMachine/src/CloudMachineInfrastructure/RoleResolver.cs new file mode 100644 index 0000000000000..f0958b7ca60f8 --- /dev/null +++ b/sdk/cloudmachine/Azure.Provisioning.CloudMachine/src/CloudMachineInfrastructure/RoleResolver.cs @@ -0,0 +1,52 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; +using Azure.Provisioning; +using Azure.Provisioning.Authorization; +using Azure.Provisioning.Expressions; +using Azure.Provisioning.Primitives; +using Azure.Provisioning.Roles; + +namespace Azure.CloudMachine; + +internal class RoleResolver(Dictionary annotations, IEnumerable managedIdentities, IEnumerable> userPrincipals) : InfrastructureResolver +{ + public override IEnumerable ResolveResources(IEnumerable resources, ProvisioningBuildOptions options) + { + foreach (Provisionable provisionable in base.ResolveResources(resources, options)) + { + yield return provisionable; + if (annotations.TryGetValue(provisionable, out (string RoleName, string RoleId)[]? roles) && provisionable is ProvisionableResource resource && roles is not null) + { + foreach ((string RoleName, string RoleId) in roles) + { + foreach (BicepValue userPrincipal in userPrincipals) + { + yield return new RoleAssignment($"{resource.BicepIdentifier}_{userPrincipal.Value.ToString().Replace('-', '_')}_{RoleName}") + { + Name = BicepFunction.CreateGuid(resource.BicepIdentifier, userPrincipal, BicepFunction.GetSubscriptionResourceId("Microsoft.Authorization/roleDefinitions", RoleId)), + Scope = new IdentifierExpression(resource.BicepIdentifier), + PrincipalType = RoleManagementPrincipalType.User, + RoleDefinitionId = BicepFunction.GetSubscriptionResourceId("Microsoft.Authorization/roleDefinitions", RoleId), + PrincipalId = userPrincipal + }; + } + + foreach (UserAssignedIdentity identity in managedIdentities) + { + yield return new RoleAssignment($"{resource.BicepIdentifier}_{identity.BicepIdentifier}_{RoleName}") + { + Name = BicepFunction.CreateGuid(resource.BicepIdentifier, identity.Id, BicepFunction.GetSubscriptionResourceId("Microsoft.Authorization/roleDefinitions", RoleId)), + Scope = new IdentifierExpression(resource.BicepIdentifier), + PrincipalType = RoleManagementPrincipalType.ServicePrincipal, + RoleDefinitionId = BicepFunction.GetSubscriptionResourceId("Microsoft.Authorization/roleDefinitions", RoleId), + PrincipalId = identity.PrincipalId + }; + } + } + } + } + } +} diff --git a/sdk/provisioning/Azure.Provisioning.CloudMachine/src/CDKLevel3/EventGridSystemTopicFeature.cs b/sdk/cloudmachine/Azure.Provisioning.CloudMachine/src/FeaturesBuiltIn/EventGridSystemTopicFeature.cs similarity index 61% rename from sdk/provisioning/Azure.Provisioning.CloudMachine/src/CDKLevel3/EventGridSystemTopicFeature.cs rename to sdk/cloudmachine/Azure.Provisioning.CloudMachine/src/FeaturesBuiltIn/EventGridSystemTopicFeature.cs index d2f21333eee43..5f331be310e44 100644 --- a/sdk/provisioning/Azure.Provisioning.CloudMachine/src/CDKLevel3/EventGridSystemTopicFeature.cs +++ b/sdk/cloudmachine/Azure.Provisioning.CloudMachine/src/FeaturesBuiltIn/EventGridSystemTopicFeature.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. +using Azure.CloudMachine.Core; using Azure.Provisioning.CloudMachine; using Azure.Provisioning.EventGrid; using Azure.Provisioning.Expressions; @@ -10,25 +11,23 @@ namespace Azure.CloudMachine; -public class EventGridSystemTopicFeature(string name, CloudMachineFeature source) : CloudMachineFeature +internal class EventGridSystemTopicFeature(string topicName, CloudMachineFeature source, string topicType) : CloudMachineFeature { - protected override ProvisionableResource EmitCore(CloudMachineInfrastructure infrastructure) + protected override ProvisionableResource EmitResources(CloudMachineInfrastructure infrastructure) { - var topic = new SystemTopic("cm_eventgrid_topic", "2022-06-15") + var topic = new SystemTopic("cm_eventgrid_topic", InternalConstants.EventGridTopicVersion) { - TopicType = "Microsoft.Storage.StorageAccounts", - Source = ValidateIsOfType(source).Id, + TopicType = topicType, + Source = EnsureEmits(source).Id, Identity = new() { ManagedServiceIdentityType = ManagedServiceIdentityType.UserAssigned, UserAssignedIdentities = { { BicepFunction.Interpolate($"{infrastructure.Identity.Id}").Compile().ToString(), new UserAssignedIdentityDetails() } } }, - Name = name + Name = topicName }; infrastructure.AddResource(topic); - - Emitted = topic; return topic; } } diff --git a/sdk/cloudmachine/Azure.Provisioning.CloudMachine/src/FeaturesBuiltIn/InternalConstants.cs b/sdk/cloudmachine/Azure.Provisioning.CloudMachine/src/FeaturesBuiltIn/InternalConstants.cs new file mode 100644 index 0000000000000..6387aa81b0015 --- /dev/null +++ b/sdk/cloudmachine/Azure.Provisioning.CloudMachine/src/FeaturesBuiltIn/InternalConstants.cs @@ -0,0 +1,10 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +namespace Azure.CloudMachine.Core +{ + internal static class InternalConstants + { + internal const string EventGridTopicVersion = "2022-06-15"; + } +} diff --git a/sdk/provisioning/Azure.Provisioning.CloudMachine/src/CDKLevel3/ServiceBusNamespaceFeature.cs b/sdk/cloudmachine/Azure.Provisioning.CloudMachine/src/FeaturesBuiltIn/ServiceBusNamespaceFeature.cs similarity index 79% rename from sdk/provisioning/Azure.Provisioning.CloudMachine/src/CDKLevel3/ServiceBusNamespaceFeature.cs rename to sdk/cloudmachine/Azure.Provisioning.CloudMachine/src/FeaturesBuiltIn/ServiceBusNamespaceFeature.cs index 441fd1be69022..749b82feadb4a 100644 --- a/sdk/provisioning/Azure.Provisioning.CloudMachine/src/CDKLevel3/ServiceBusNamespaceFeature.cs +++ b/sdk/cloudmachine/Azure.Provisioning.CloudMachine/src/FeaturesBuiltIn/ServiceBusNamespaceFeature.cs @@ -1,9 +1,9 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -using Azure.Provisioning.Authorization; +using Azure.CloudMachine.Core; +using Azure.Core; using Azure.Provisioning.CloudMachine; -using Azure.Provisioning.Expressions; using Azure.Provisioning.Primitives; using Azure.Provisioning.ServiceBus; @@ -11,7 +11,7 @@ namespace Azure.CloudMachine; public class ServiceBusNamespaceFeature(string name, ServiceBusSkuName sku = ServiceBusSkuName.Standard, ServiceBusSkuTier tier = ServiceBusSkuTier.Standard) : CloudMachineFeature { - protected override ProvisionableResource EmitCore(CloudMachineInfrastructure infrastructure) + protected override ProvisionableResource EmitResources(CloudMachineInfrastructure infrastructure) { var _serviceBusNamespace = new ServiceBusNamespace("cm_servicebus") { @@ -37,7 +37,9 @@ protected override ProvisionableResource EmitCore(CloudMachineInfrastructure inf (ServiceBusBuiltInRole.GetBuiltInRoleName(ServiceBusBuiltInRole.AzureServiceBusDataOwner), ServiceBusBuiltInRole.AzureServiceBusDataOwner.ToString()), ]); - Emitted = _serviceBusNamespace; return _serviceBusNamespace; } + + protected internal override void EmitConnections(ConnectionCollection connections, string cmId) + => connections.Add(CloudMachineConnections.CreateDefaultServiceBusConnection(cmId)); } diff --git a/sdk/provisioning/Azure.Provisioning.CloudMachine/src/CDKLevel3/ServiceBusSubscriptionFeature.cs b/sdk/cloudmachine/Azure.Provisioning.CloudMachine/src/FeaturesBuiltIn/ServiceBusSubscriptionFeature.cs similarity index 69% rename from sdk/provisioning/Azure.Provisioning.CloudMachine/src/CDKLevel3/ServiceBusSubscriptionFeature.cs rename to sdk/cloudmachine/Azure.Provisioning.CloudMachine/src/FeaturesBuiltIn/ServiceBusSubscriptionFeature.cs index b60042fe63245..dd757020c1844 100644 --- a/sdk/provisioning/Azure.Provisioning.CloudMachine/src/CDKLevel3/ServiceBusSubscriptionFeature.cs +++ b/sdk/cloudmachine/Azure.Provisioning.CloudMachine/src/FeaturesBuiltIn/ServiceBusSubscriptionFeature.cs @@ -2,6 +2,7 @@ // Licensed under the MIT License. using System; +using Azure.Core; using Azure.Provisioning.CloudMachine; using Azure.Provisioning.Primitives; using Azure.Provisioning.ServiceBus; @@ -10,12 +11,12 @@ namespace Azure.CloudMachine; public class ServiceBusSubscriptionFeature(string name, ServiceBusTopicFeature parent) : CloudMachineFeature { - protected override ProvisionableResource EmitCore(CloudMachineInfrastructure infrastructure) + protected override ProvisionableResource EmitResources(CloudMachineInfrastructure infrastructure) { var subscription = new ServiceBusSubscription(name, "2021-11-01") { Name = name, - Parent = ValidateIsOfType(parent), + Parent = EnsureEmits(parent), IsClientAffine = false, LockDuration = TimeSpan.FromSeconds(30), RequiresSession = false, @@ -28,7 +29,15 @@ protected override ProvisionableResource EmitCore(CloudMachineInfrastructure inf }; infrastructure.AddResource(subscription); - Emitted = subscription; return subscription; } + + protected internal override void EmitConnections(ConnectionCollection connections, string cmId) + { + ClientConnection connection = new( + $"{name}", + $"{parent.Name}/{name}" + ); + connections.Add(connection); + } } diff --git a/sdk/cloudmachine/Azure.Provisioning.CloudMachine/src/FeaturesBuiltIn/ServiceBusTopicFeature.cs b/sdk/cloudmachine/Azure.Provisioning.CloudMachine/src/FeaturesBuiltIn/ServiceBusTopicFeature.cs new file mode 100644 index 0000000000000..e547085e41375 --- /dev/null +++ b/sdk/cloudmachine/Azure.Provisioning.CloudMachine/src/FeaturesBuiltIn/ServiceBusTopicFeature.cs @@ -0,0 +1,49 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using Azure.Core; +using Azure.Provisioning.CloudMachine; +using Azure.Provisioning.Primitives; +using Azure.Provisioning.ServiceBus; + +namespace Azure.CloudMachine; + +public class ServiceBusTopicFeature : CloudMachineFeature +{ + public ServiceBusTopicFeature(string name, ServiceBusNamespaceFeature parent) + { + Name = name; + _parent = parent; + } + + protected override ProvisionableResource EmitResources(CloudMachineInfrastructure infrastructure) + { + var topic = new ServiceBusTopic(Name, "2021-11-01") + { + Name = Name, + Parent = EnsureEmits(_parent), + MaxMessageSizeInKilobytes = 256, + DefaultMessageTimeToLive = TimeSpan.FromDays(14), + RequiresDuplicateDetection = false, + EnableBatchedOperations = true, + SupportOrdering = true, + Status = ServiceBusMessagingEntityStatus.Active + }; + + infrastructure.AddResource(topic); + return topic; + } + + protected internal override void EmitConnections(ConnectionCollection connections, string cmId) + { + connections.Add(new ClientConnection(Name, Name)); + } + + /// + /// The name of the topic. + /// + public string Name { get; } + + private ServiceBusNamespaceFeature _parent; +} diff --git a/sdk/cloudmachine/Azure.Provisioning.CloudMachine/src/FeaturesBuiltIn/StorageAccountFeature.cs b/sdk/cloudmachine/Azure.Provisioning.CloudMachine/src/FeaturesBuiltIn/StorageAccountFeature.cs new file mode 100644 index 0000000000000..772ba9c7eaeb5 --- /dev/null +++ b/sdk/cloudmachine/Azure.Provisioning.CloudMachine/src/FeaturesBuiltIn/StorageAccountFeature.cs @@ -0,0 +1,104 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System.Collections.Generic; +using System.Linq; +using Azure.CloudMachine.Core; +using Azure.Core; +using Azure.Provisioning; +using Azure.Provisioning.CloudMachine; +using Azure.Provisioning.Expressions; +using Azure.Provisioning.Primitives; +using Azure.Provisioning.Resources; +using Azure.Provisioning.Storage; + +namespace Azure.CloudMachine; + +internal class StorageAccountFeature : CloudMachineFeature +{ + private readonly StorageSkuName _skuName; + public string Name { get; } + + public StorageAccountFeature(string accountName, StorageSkuName sku = StorageSkuName.StandardLrs) + { + _skuName = sku; + Name = accountName; + } + + protected override ProvisionableResource EmitResources(CloudMachineInfrastructure infrastructure) + { + var storage = new StorageAccount("cm_storage", StorageAccount.ResourceVersions.V2023_01_01) + { + Name = Name, + Kind = StorageKind.StorageV2, + Sku = new StorageSku { Name = _skuName }, + IsHnsEnabled = true, + AllowBlobPublicAccess = false, + Identity = new() + { + ManagedServiceIdentityType = ManagedServiceIdentityType.UserAssigned, + UserAssignedIdentities = { { BicepFunction.Interpolate($"{infrastructure.Identity.Id}").Compile().ToString(), new UserAssignedIdentityDetails() } } + } + }; + infrastructure.AddResource(storage); + + RequiredSystemRoles.Add(storage, + [ + (StorageBuiltInRole.GetBuiltInRoleName(StorageBuiltInRole.StorageBlobDataContributor),StorageBuiltInRole.StorageBlobDataContributor.ToString()), + (StorageBuiltInRole.GetBuiltInRoleName(StorageBuiltInRole.StorageTableDataContributor), StorageBuiltInRole.StorageTableDataContributor.ToString()) + ] + ); + return storage; + } +} + +internal class BlobContainerFeature : CloudMachineFeature +{ + public string ContainerName { get; } + public BlobServiceFeature Parent { get; } + + public BlobContainerFeature(BlobServiceFeature parent, string? containerName = default) + { + if (containerName == default) containerName = CloudMachineConnections.DefaultBlobContainerName; + ContainerName = containerName; + Parent = parent; + } + protected override ProvisionableResource EmitResources(CloudMachineInfrastructure cm) + { + BlobContainer container = new($"cm_storage_blobs_container_{ContainerName}", "2023-01-01") + { + Parent = (BlobService)Parent.Resource, + Name = ContainerName + }; + cm.AddResource(container); + return container; + } + + protected internal override void EmitConnections(ConnectionCollection connections, string cmId) + { + ClientConnection connection = new( + $"Azure.Storage.Blobs.BlobContainerClient@{ContainerName}", + $"https://{Parent.Account.Name}.blob.core.windows.net/{ContainerName}" + ); + connections.Add(connection); + } +} + +internal class BlobServiceFeature : CloudMachineFeature +{ + public StorageAccountFeature Account { get; } + + public BlobServiceFeature(StorageAccountFeature account) + { + Account = account; + } + protected override ProvisionableResource EmitResources(CloudMachineInfrastructure cm) + { + BlobService blobs = new("cm_storage_blobs") + { + Parent = (StorageAccount)Account.Resource, + }; + cm.AddResource(blobs); + return blobs; + } +} diff --git a/sdk/provisioning/Azure.Provisioning.CloudMachine/src/CDKLevel3/SystemTopicEventSubscriptionFeature.cs b/sdk/cloudmachine/Azure.Provisioning.CloudMachine/src/FeaturesBuiltIn/SystemTopicEventSubscriptionFeature.cs similarity index 78% rename from sdk/provisioning/Azure.Provisioning.CloudMachine/src/CDKLevel3/SystemTopicEventSubscriptionFeature.cs rename to sdk/cloudmachine/Azure.Provisioning.CloudMachine/src/FeaturesBuiltIn/SystemTopicEventSubscriptionFeature.cs index 85faafb2435d8..ffc8fe92c4ee0 100644 --- a/sdk/provisioning/Azure.Provisioning.CloudMachine/src/CDKLevel3/SystemTopicEventSubscriptionFeature.cs +++ b/sdk/cloudmachine/Azure.Provisioning.CloudMachine/src/FeaturesBuiltIn/SystemTopicEventSubscriptionFeature.cs @@ -10,14 +10,14 @@ namespace Azure.CloudMachine; -public class SystemTopicEventSubscriptionFeature(string name, EventGridSystemTopicFeature parent, ServiceBusTopicFeature destination, ServiceBusNamespaceFeature parentNamespace) : CloudMachineFeature +internal class SystemTopicEventSubscriptionFeature(string name, EventGridSystemTopicFeature parent, ServiceBusTopicFeature destination, ServiceBusNamespaceFeature parentNamespace) : CloudMachineFeature { - protected override ProvisionableResource EmitCore(CloudMachineInfrastructure infrastructure) + protected override ProvisionableResource EmitResources(CloudMachineInfrastructure infrastructure) { - var serviceBusNamespace = ValidateIsOfType(parentNamespace); + ServiceBusNamespace serviceBusNamespace = EnsureEmits(parentNamespace); - var role = ServiceBusBuiltInRole.AzureServiceBusDataSender; - var roleAssignment = new RoleAssignment($"cm_servicebus_{ValidateIsOfType(parent).Name.Value}_role") + ServiceBusBuiltInRole role = ServiceBusBuiltInRole.AzureServiceBusDataSender; + var roleAssignment = new RoleAssignment($"cm_servicebus_{EnsureEmits(parent).Name.Value}_role") { Name = BicepFunction.CreateGuid(serviceBusNamespace.Id, infrastructure.Identity.Id, BicepFunction.GetSubscriptionResourceId("Microsoft.Authorization/roleDefinitions", role.ToString())), Scope = new IdentifierExpression(serviceBusNamespace.BicepIdentifier), @@ -26,7 +26,7 @@ protected override ProvisionableResource EmitCore(CloudMachineInfrastructure inf PrincipalId = infrastructure.Identity.PrincipalId, }; - var systemTopic = ValidateIsOfType(parent); + SystemTopic systemTopic = EnsureEmits(parent); var subscription = new SystemTopicEventSubscription("cm_eventgrid_subscription_blob", "2022-06-15") { Name = name, @@ -40,7 +40,7 @@ protected override ProvisionableResource EmitCore(CloudMachineInfrastructure inf }, Destination = new ServiceBusTopicEventSubscriptionDestination { - ResourceId = ValidateIsOfType(destination).Id + ResourceId = EnsureEmits(destination).Id } }, Filter = new EventSubscriptionFilter @@ -64,8 +64,6 @@ protected override ProvisionableResource EmitCore(CloudMachineInfrastructure inf infrastructure.AddResource(subscription); infrastructure.AddResource(roleAssignment); - - Emitted = subscription; return subscription; } } diff --git a/sdk/provisioning/Azure.Provisioning.CloudMachine/src/AzureSdkExtensions/AppServiceFeature.cs b/sdk/cloudmachine/Azure.Provisioning.CloudMachine/src/FeaturesExtensions/AppServiceFeature.cs similarity index 84% rename from sdk/provisioning/Azure.Provisioning.CloudMachine/src/AzureSdkExtensions/AppServiceFeature.cs rename to sdk/cloudmachine/Azure.Provisioning.CloudMachine/src/FeaturesExtensions/AppServiceFeature.cs index 4b72fc998eeaa..83d771f27482c 100644 --- a/sdk/provisioning/Azure.Provisioning.CloudMachine/src/AzureSdkExtensions/AppServiceFeature.cs +++ b/sdk/cloudmachine/Azure.Provisioning.CloudMachine/src/FeaturesExtensions/AppServiceFeature.cs @@ -13,16 +13,12 @@ public class AppServiceFeature : CloudMachineFeature { public AppServiceSkuDescription Sku { get; set; } - public AppServiceFeature(AppServiceSkuDescription? sku = default) + public AppServiceFeature() { - if (sku == default) - { - sku = new AppServiceSkuDescription { Tier = "Free", Name = "F1" }; - } - Sku = sku; + Sku = new AppServiceSkuDescription { Tier = "Free", Name = "F1" }; } - protected override ProvisionableResource EmitCore(CloudMachineInfrastructure infrastructure) + protected override ProvisionableResource EmitResources(CloudMachineInfrastructure infrastructure) { //Add a App Service to the CloudMachine infrastructure. AppServicePlan hostingPlan = new("cm_hosting_plan") @@ -51,8 +47,8 @@ protected override ProvisionableResource EmitCore(CloudMachineInfrastructure inf IsHttp20Enabled = true, MinTlsVersion = AppServiceSupportedTlsVersion.Tls1_2, IsWebSocketsEnabled = true, - AppSettings = new() - { + AppSettings = + [ // This is used by the CloudMachineWorkspace to detect that it is running in a deployed App Service. // The ClientId is used to create a ManagedIdentityCredential so that it wires up to our CloudMachine user-assigned identity. new AppServiceNameValuePair @@ -60,7 +56,7 @@ protected override ProvisionableResource EmitCore(CloudMachineInfrastructure inf Name = "CLOUDMACHINE_MANAGED_IDENTITY_CLIENT_ID", Value = infrastructure.Identity.ClientId }, - } + ] } }; infrastructure.AddResource(appService); diff --git a/sdk/provisioning/Azure.Provisioning.CloudMachine/src/AzureSdkExtensions/KeyVaultFeature.cs b/sdk/cloudmachine/Azure.Provisioning.CloudMachine/src/FeaturesExtensions/KeyVaultFeature.cs similarity index 76% rename from sdk/provisioning/Azure.Provisioning.CloudMachine/src/AzureSdkExtensions/KeyVaultFeature.cs rename to sdk/cloudmachine/Azure.Provisioning.CloudMachine/src/FeaturesExtensions/KeyVaultFeature.cs index a7ac7a24c7804..1e52c9d79ce2a 100644 --- a/sdk/provisioning/Azure.Provisioning.CloudMachine/src/AzureSdkExtensions/KeyVaultFeature.cs +++ b/sdk/cloudmachine/Azure.Provisioning.CloudMachine/src/FeaturesExtensions/KeyVaultFeature.cs @@ -1,8 +1,8 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -using System.Collections.Generic; -using Azure.Provisioning.Authorization; +using System; +using Azure.Core; using Azure.Provisioning.CloudMachine; using Azure.Provisioning.Expressions; using Azure.Provisioning.KeyVault; @@ -16,13 +16,16 @@ public class KeyVaultFeature : CloudMachineFeature public KeyVaultFeature(KeyVaultSku? sku = default) { - if (sku == null) - { - sku = new KeyVaultSku { Name = KeyVaultSkuName.Standard, Family = KeyVaultSkuFamily.A, }; - } + sku ??= new KeyVaultSku { Name = KeyVaultSkuName.Standard, Family = KeyVaultSkuFamily.A, }; Sku = sku; } - protected override ProvisionableResource EmitCore(CloudMachineInfrastructure infrastructure) + + protected internal override void EmitConnections(ConnectionCollection connections, string cmId) + { + connections.Add(new ClientConnection("Azure.Security.KeyVault.Secrets.SecretClient", $"https://{cmId}.vault.azure.net/")); + } + + protected override ProvisionableResource EmitResources(CloudMachineInfrastructure infrastructure) { // Add a KeyVault to the CloudMachine infrastructure. KeyVaultService keyVaultResource = new("cm_kv") @@ -31,7 +34,7 @@ protected override ProvisionableResource EmitCore(CloudMachineInfrastructure inf Properties = new KeyVaultProperties { - Sku = this.Sku, + Sku = Sku, TenantId = BicepFunction.GetSubscription().TenantId, EnabledForDeployment = true, AccessPolicies = [ diff --git a/sdk/provisioning/Azure.Provisioning.CloudMachine/src/AzureSdkExtensions/OpenAIFeature.cs b/sdk/cloudmachine/Azure.Provisioning.CloudMachine/src/FeaturesExtensions/OpenAIFeature.cs similarity index 60% rename from sdk/provisioning/Azure.Provisioning.CloudMachine/src/AzureSdkExtensions/OpenAIFeature.cs rename to sdk/cloudmachine/Azure.Provisioning.CloudMachine/src/FeaturesExtensions/OpenAIFeature.cs index 8b0fb82c3d0a5..8ce12b0bf57a5 100644 --- a/sdk/provisioning/Azure.Provisioning.CloudMachine/src/AzureSdkExtensions/OpenAIFeature.cs +++ b/sdk/cloudmachine/Azure.Provisioning.CloudMachine/src/FeaturesExtensions/OpenAIFeature.cs @@ -2,6 +2,7 @@ // Licensed under the MIT License. using System; using System.Collections.Generic; +using Azure.Core; using Azure.Provisioning.CloudMachine; using Azure.Provisioning.CognitiveServices; using Azure.Provisioning.Primitives; @@ -10,45 +11,30 @@ namespace Azure.CloudMachine.OpenAI; internal class OpenAIFeature : CloudMachineFeature { - private List _models = new List(); - public OpenAIFeature() { } - protected override ProvisionableResource EmitCore(CloudMachineInfrastructure cloudMachine) + protected override ProvisionableResource EmitResources(CloudMachineInfrastructure cloudMachine) { CognitiveServicesAccount cognitiveServices = CreateOpenAIAccount(cloudMachine); cloudMachine.AddResource(cognitiveServices); RequiredSystemRoles.Add(cognitiveServices, [(CognitiveServicesBuiltInRole.GetBuiltInRoleName(CognitiveServicesBuiltInRole.CognitiveServicesOpenAIContributor) ,CognitiveServicesBuiltInRole.CognitiveServicesOpenAIContributor.ToString())]); - Emitted = cognitiveServices; - - OpenAIModel? previous = null; - foreach (OpenAIModel model in _models) - { - model.Emit(cloudMachine); - if (previous != null) - { - model.Emitted.DependsOn.Add(previous.Emitted); - } - previous = model; - } - return cognitiveServices; } - internal void AddModel(OpenAIModel model) + protected internal override void EmitConnections(ConnectionCollection connections, string cmId) { - if (model.Account != null) + ClientConnection connection = new("Azure.AI.OpenAI.AzureOpenAIClient", $"https://{cmId}.openai.azure.com"); + + if (!connections.Contains(connection)) { - throw new InvalidOperationException("Model already added to an account"); + connections.Add(connection); } - model.Account = this; - _models.Add(model); } - internal CognitiveServicesAccount CreateOpenAIAccount(CloudMachineInfrastructure cm) + internal static CognitiveServicesAccount CreateOpenAIAccount(CloudMachineInfrastructure cm) { return new("openai") { diff --git a/sdk/cloudmachine/Azure.Provisioning.CloudMachine/src/FeaturesExtensions/OpenAIModelFeature.cs b/sdk/cloudmachine/Azure.Provisioning.CloudMachine/src/FeaturesExtensions/OpenAIModelFeature.cs new file mode 100644 index 0000000000000..6efcbd3b98924 --- /dev/null +++ b/sdk/cloudmachine/Azure.Provisioning.CloudMachine/src/FeaturesExtensions/OpenAIModelFeature.cs @@ -0,0 +1,124 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.Diagnostics; +using System.Linq; +using Azure.Core; +using Azure.Provisioning.CloudMachine; +using Azure.Provisioning.CognitiveServices; +using Azure.Provisioning.Primitives; + +namespace Azure.CloudMachine.OpenAI; + +public class OpenAIModelFeature : CloudMachineFeature +{ + public OpenAIModelFeature(string model, string modelVersion, AIModelKind kind = AIModelKind.Chat) { + Kind = kind; + Model = model; + ModelVersion = modelVersion; + } + + public string Model { get; } + public string ModelVersion { get; } + private AIModelKind Kind { get; } + + internal OpenAIFeature Account { get; set; } = default!; + + protected internal override void EmitFeatures(FeatureCollection features, string cmId) + { + // TODO: is it OK that we return the first one? + OpenAIFeature? openAI = features.FindAll().FirstOrDefault(); + if (openAI == default) + { + openAI = new OpenAIFeature(); // TODO: we need to add connection + features.Add(openAI); + } + Account = openAI; + features.Add(this); + } + + protected internal override void EmitConnections(ConnectionCollection connections, string cmId) + { + Account.EmitConnections(connections, cmId); + // add connections + switch (Kind) + { + case AIModelKind.Chat: + connections.Add(new ClientConnection("OpenAI.Chat.ChatClient", $"{cmId}_chat")); + break; + case AIModelKind.Embedding: + connections.Add(new ClientConnection("OpenAI.Embeddings.EmbeddingClient", $"{cmId}_embedding")); + break; + default: + throw new NotImplementedException(); + } + } + + protected override ProvisionableResource EmitResources(CloudMachineInfrastructure cm) + { + if (Account == null) throw new InvalidOperationException("Account must be set before emitting"); + if (Account.Resource == null) throw new InvalidOperationException("Account must be emitted before emitting"); + + string name = Kind switch + { + AIModelKind.Chat => $"{cm.Id}_chat", + AIModelKind.Embedding => $"{cm.Id}_embedding", + _ => throw new NotImplementedException() + }; + + CognitiveServicesAccount parent = (CognitiveServicesAccount)Account.Resource; + + CognitiveServicesAccountDeployment deployment = new($"openai_{name}", "2024-06-01-preview") { + Parent = parent, + Name = name, + Properties = new CognitiveServicesAccountDeploymentProperties() + { + Model = new CognitiveServicesAccountDeploymentModel() + { + Name = Model, + Format = "OpenAI", + Version = ModelVersion + }, + VersionUpgradeOption = DeploymentModelVersionUpgradeOption.OnceNewDefaultVersionAvailable, + RaiPolicyName = "Microsoft.DefaultV2", + }, + Sku = new CognitiveServicesSku + { + Capacity = 120, + Name = "Standard" + }, + }; + + // deployments need to have dependson set! + OpenAIModelFeature? previous = FindPrevious(cm, this); + if (previous != null) + { + if (previous.Resource == null) throw new InvalidOperationException("Previous must be emitted"); + CognitiveServicesAccountDeployment previousDeployment = (CognitiveServicesAccountDeployment)previous.Resource; + deployment.DependsOn.Add(previousDeployment); + } + + cm.AddResource(deployment); + return deployment; + + OpenAIModelFeature? FindPrevious(CloudMachineInfrastructure cm, OpenAIModelFeature current) + { + OpenAIModelFeature? previous = default; + foreach (var feature in cm.Features) + { + if (feature == current) + return previous; + if (feature is OpenAIModelFeature oaim) + previous = oaim; + } + throw new InvalidOperationException("current not found in infrastructure"); + } + } +} + +public enum AIModelKind +{ + Chat, + Embedding, +} diff --git a/sdk/provisioning/Azure.Provisioning.CloudMachine/src/GlobalSuppressions.cs b/sdk/cloudmachine/Azure.Provisioning.CloudMachine/src/GlobalSuppressions.cs similarity index 100% rename from sdk/provisioning/Azure.Provisioning.CloudMachine/src/GlobalSuppressions.cs rename to sdk/cloudmachine/Azure.Provisioning.CloudMachine/src/GlobalSuppressions.cs diff --git a/sdk/provisioning/Azure.Provisioning.CloudMachine/src/Properties/AssemblyInfo.cs b/sdk/cloudmachine/Azure.Provisioning.CloudMachine/src/Properties/AssemblyInfo.cs similarity index 100% rename from sdk/provisioning/Azure.Provisioning.CloudMachine/src/Properties/AssemblyInfo.cs rename to sdk/cloudmachine/Azure.Provisioning.CloudMachine/src/Properties/AssemblyInfo.cs diff --git a/sdk/cloudmachine/Azure.Provisioning.CloudMachine/src/Tooling/Azd.cs b/sdk/cloudmachine/Azure.Provisioning.CloudMachine/src/Tooling/Azd.cs new file mode 100644 index 0000000000000..9f5fcc7a1727d --- /dev/null +++ b/sdk/cloudmachine/Azure.Provisioning.CloudMachine/src/Tooling/Azd.cs @@ -0,0 +1,88 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System.IO; +using Azure.Provisioning; +using Azure.Provisioning.Primitives; +using Azure.Provisioning.Resources; +using Azure.Provisioning.Expressions; + +namespace Azure.CloudMachine; + +/// +/// Azure Developer CLI helpers. +/// +#pragma warning disable AZC0012 // Avoid single word type names +public static class Azd +#pragma warning restore AZC0012 // Avoid single word type names +{ + private const string MainBicepName = "main"; + private const string ResourceGroupVersion = "2024-03-01"; + + public static void Init(CloudMachineClient client, string? infraDirectory = default) + { + CloudMachineInfrastructure infra = client.GetInfrastructure(); + Init(infra, infraDirectory); + } + + public static void Init(CloudMachineInfrastructure infra, string? infraDirectory = default) + { + if (infraDirectory == default) infraDirectory = Path.Combine(".", "infra"); + + Directory.CreateDirectory(infraDirectory); + + infra.Build().Save(infraDirectory); + var cmid = CloudMachineWorkspace.ReadOrCreateCloudMachineId(); + + // main.bicep + var location = new ProvisioningParameter("location", typeof(string)); + var principalId = new ProvisioningParameter("principalId", typeof(string)); + + ResourceGroup rg = new(nameof(rg), ResourceGroupVersion) + { + Name = cmid, + Location = location + }; + + Infrastructure mainBicep = new("main") + { + TargetScope = DeploymentScope.Subscription + }; + ModuleImport import = new("cm", $"cm.bicep") + { + Name = "cm", + Scope = new IdentifierExpression(rg.BicepIdentifier) + }; + import.Parameters.Add(nameof(location), location); + import.Parameters.Add(nameof(principalId), principalId); + + mainBicep.Add(rg); + mainBicep.Add(import); + mainBicep.Add(location); + mainBicep.Add(principalId); + mainBicep.Build().Save(infraDirectory); + + WriteMainParametersFile(infraDirectory); + } + private static void WriteMainParametersFile(string infraDirectory) + { + File.WriteAllText(Path.Combine(infraDirectory, $"{MainBicepName}.parameters.json"), + """ + { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "environmentName": { + "value": "${AZURE_ENV_NAME}" + }, + "location" : { + "value" : "${AZURE_LOCATION}" + }, + "principalId": { + "value": "${AZURE_PRINCIPAL_ID}" + } + } + } + """); + } +} diff --git a/sdk/provisioning/Azure.Provisioning.CloudMachine/src/CDKLevel3/CloudMachineCommands.cs b/sdk/cloudmachine/Azure.Provisioning.CloudMachine/src/Tooling/CloudMachineCommands.cs similarity index 82% rename from sdk/provisioning/Azure.Provisioning.CloudMachine/src/CDKLevel3/CloudMachineCommands.cs rename to sdk/cloudmachine/Azure.Provisioning.CloudMachine/src/Tooling/CloudMachineCommands.cs index bf59371071c8c..4685dda5683e5 100644 --- a/sdk/provisioning/Azure.Provisioning.CloudMachine/src/CDKLevel3/CloudMachineCommands.cs +++ b/sdk/cloudmachine/Azure.Provisioning.CloudMachine/src/Tooling/CloudMachineCommands.cs @@ -1,24 +1,26 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -using System.IO; using System; -using System.ClientModel.TypeSpec; using System.ClientModel.Primitives; +using System.ClientModel.TypeSpec; +using System.Collections.Generic; +using System.IO; +using System.Text.Json; using Azure.AI.OpenAI; using Azure.Core; using Azure.Identity; -using System.Text.Json; namespace Azure.CloudMachine; -public class CloudMachineCommands +public static class CloudMachineCommands { public static bool Execute(string[] args, Action? configure = default, bool exitProcessIfHandled = true) { - if (args.Length < 1) return false; + if (args.Length < 1) + return false; - string cmid = AzdHelpers.ReadOrCreateCmid(); + string cmid = CloudMachineWorkspace.ReadOrCreateCloudMachineId(); CloudMachineInfrastructure cmi = new(cmid); if (configure != default) { @@ -27,13 +29,13 @@ public static bool Execute(string[] args, Action? co if (args[0] == "-bicep") { - GenerateBicep(cmi); + Azd.Init(cmi); return Handled(exitProcessIfHandled); } if (args[0] == "-tsp") { - GenerateTsp(cmi); + GenerateTsp(cmi.Endpoints); return Handled(exitProcessIfHandled); } @@ -46,9 +48,10 @@ public static bool Execute(string[] args, Action? co return false; - bool Handled(bool exitProcessIfHandled) + static bool Handled(bool exitProcessIfHandled) { - if (exitProcessIfHandled) Environment.Exit(0); + if (exitProcessIfHandled) + Environment.Exit(0); return true; } } @@ -63,9 +66,8 @@ private static void ListAzureOpenaAIModels(string cmid, string? option) ); string audience = "https://cognitiveservices.azure.com/.default"; TokenCredentialAuthenticationPolicy auth = new(credential, [audience]); - RestClient client = new RestClient(auth); + RestClient client = new(auth); string uri = $"https://{cmid}.openai.azure.com/openai/models?api-version=2024-10-21"; - RequestOptions options = new RequestOptions(); PipelineResponse response = client.Get(uri); if (response.Status != 200) { @@ -87,7 +89,8 @@ private static void ListAzureOpenaAIModels(string cmid, string? option) { scenario = "chat"; } - if (caps.GetProperty("embeddings"u8).GetBoolean() == true) { + if (caps.GetProperty("embeddings"u8).GetBoolean() == true) + { scenario = "embeddings"; } if (caps.GetProperty("inference"u8).GetBoolean() == false) @@ -95,7 +98,8 @@ private static void ListAzureOpenaAIModels(string cmid, string? option) scenario = default; // inference==false means model cannot be deployed } - if (scenario == default) continue; + if (scenario == default) + continue; if (scenario != default) { @@ -114,11 +118,11 @@ private static void ListAzureOpenaAIModels(string cmid, string? option) } } - private static void GenerateTsp(CloudMachineInfrastructure cmi) + private static void GenerateTsp(IEnumerable operationGroups) { - foreach (Type endpoints in cmi.Endpoints) + foreach (Type operationGroup in operationGroups) { - string name = endpoints.Name; + string name = operationGroup.Name; if (name.StartsWith("I")) name = name.Substring(1); string directory = Path.Combine(".", "tsp"); @@ -127,13 +131,7 @@ private static void GenerateTsp(CloudMachineInfrastructure cmi) if (File.Exists(tspFile)) File.Delete(tspFile); using FileStream stream = File.OpenWrite(tspFile); - TypeSpecWriter.WriteServer(stream, endpoints); + TypeSpecWriter.WriteServer(stream, operationGroup); } } - - private static void GenerateBicep(CloudMachineInfrastructure cmi) - { - string infraDirectory = Path.Combine(".", "infra"); - AzdHelpers.Init(infraDirectory, cmi); - } } diff --git a/sdk/provisioning/Azure.Provisioning.CloudMachine/src/Tsp/TypeSpecWriter.cs b/sdk/cloudmachine/Azure.Provisioning.CloudMachine/src/Tsp/TypeSpecWriter.cs similarity index 95% rename from sdk/provisioning/Azure.Provisioning.CloudMachine/src/Tsp/TypeSpecWriter.cs rename to sdk/cloudmachine/Azure.Provisioning.CloudMachine/src/Tsp/TypeSpecWriter.cs index f8fe852c9a0b1..2b1c6af5713b5 100644 --- a/sdk/provisioning/Azure.Provisioning.CloudMachine/src/Tsp/TypeSpecWriter.cs +++ b/sdk/cloudmachine/Azure.Provisioning.CloudMachine/src/Tsp/TypeSpecWriter.cs @@ -34,8 +34,8 @@ public static void WriteServer(Stream output, Type service) writer.WriteLine(); writer.WriteLine($"@client interface {name}Client {{"); - HashSet models = new HashSet(); - foreach (var method in service.GetMethods()) + HashSet models = []; + foreach (MethodInfo method in service.GetMethods()) { writer.Write(" "); WriteOperation(writer, method, models); @@ -44,7 +44,7 @@ public static void WriteServer(Stream output, Type service) writer.WriteLine(); writer.Flush(); - foreach (var model in models) + foreach (Type model in models) { WriteModel(output, model); } @@ -69,7 +69,7 @@ public static void WriteModel(Stream output, Type model) } public static void WriteModel(Stream output) { - var model = typeof(T); + Type model = typeof(T); WriteModel(output, model); } @@ -77,7 +77,7 @@ private static void WriteClassModel(StreamWriter writer, Type model) { writer.WriteLine($"model {model.Name} {{"); - foreach (var property in model.GetProperties()) + foreach (PropertyInfo property in model.GetProperties()) { WriteClassModelProperty(writer, property); } @@ -109,7 +109,7 @@ private static void WriteOperation(StreamWriter writer, MethodInfo method, HashS writer.Write($"{httpVerb} @route(\"{ToCamel(methodName)}\") {methodName}("); bool first = true; - foreach (var parameter in method.GetParameters()) + foreach (ParameterInfo parameter in method.GetParameters()) { Type parameterType = parameter.ParameterType; @@ -139,7 +139,7 @@ private static void WriteOperation(StreamWriter writer, MethodInfo method, HashS writer.WriteLine(") : {"); writer.WriteLine($" @statusCode statusCode: 200;"); - var returnType = method.ReturnType; + Type returnType = method.ReturnType; if (returnType == typeof(Task)) returnType = typeof(void); else if (returnType.IsGenericType && returnType.GetGenericTypeDefinition() == typeof(Task<>)) returnType = returnType.GetGenericArguments()[0]; @@ -197,7 +197,7 @@ private static bool IsModel(this Type type) private static string ToCamel(this string text) { - return $"{Char.ToLower(text[0])}{text.Substring(1)}"; + return $"{char.ToLower(text[0])}{text.Substring(1)}"; } private static string ToTspType(this Type type) { diff --git a/sdk/cloudmachine/Azure.Provisioning.CloudMachine/tests/Azure.Provisioning.CloudMachine.Tests.csproj b/sdk/cloudmachine/Azure.Provisioning.CloudMachine/tests/Azure.Provisioning.CloudMachine.Tests.csproj new file mode 100644 index 0000000000000..9393c88763dbb --- /dev/null +++ b/sdk/cloudmachine/Azure.Provisioning.CloudMachine/tests/Azure.Provisioning.CloudMachine.Tests.csproj @@ -0,0 +1,23 @@ + + + + 12 + + + + + + + + + + + + + + + + Always + + + diff --git a/sdk/cloudmachine/Azure.Provisioning.CloudMachine/tests/CommandsTests.cs b/sdk/cloudmachine/Azure.Provisioning.CloudMachine/tests/CommandsTests.cs new file mode 100644 index 0000000000000..0afa31a6a375e --- /dev/null +++ b/sdk/cloudmachine/Azure.Provisioning.CloudMachine/tests/CommandsTests.cs @@ -0,0 +1,16 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using NUnit.Framework; + +namespace Azure.CloudMachine.Tests; + +public class CommandsTests +{ + [Ignore("no recordings yet")] + [Test] + public void ListModels() + { + CloudMachineCommands.Execute(["-ai", "chat"], exitProcessIfHandled: false); + } +} diff --git a/sdk/provisioning/Azure.Provisioning.CloudMachine/tests/Data/GenerateBicep.bicep b/sdk/cloudmachine/Azure.Provisioning.CloudMachine/tests/Data/GenerateBicep.bicep similarity index 99% rename from sdk/provisioning/Azure.Provisioning.CloudMachine/tests/Data/GenerateBicep.bicep rename to sdk/cloudmachine/Azure.Provisioning.CloudMachine/tests/Data/GenerateBicep.bicep index 1bbb649a44b4f..eebc294e692e1 100644 --- a/sdk/provisioning/Azure.Provisioning.CloudMachine/tests/Data/GenerateBicep.bicep +++ b/sdk/cloudmachine/Azure.Provisioning.CloudMachine/tests/Data/GenerateBicep.bicep @@ -396,5 +396,3 @@ resource cm_website 'Microsoft.Web/sites@2024-04-01' = { } output cm_managed_identity_id string = cm_identity.id - -output storage_name string = cm_storage.name \ No newline at end of file diff --git a/sdk/cloudmachine/Azure.Provisioning.CloudMachine/tests/Data/JustCloudMachine.bicep b/sdk/cloudmachine/Azure.Provisioning.CloudMachine/tests/Data/JustCloudMachine.bicep new file mode 100644 index 0000000000000..4657dd5b9f348 --- /dev/null +++ b/sdk/cloudmachine/Azure.Provisioning.CloudMachine/tests/Data/JustCloudMachine.bicep @@ -0,0 +1,240 @@ +@description('The location for the resource(s) to be deployed.') +param location string = resourceGroup().location + +@description('The objectId of the current user principal.') +param principalId string + +resource cm_identity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' = { + name: 'cm0c420d2f21084cd' + location: location +} + +resource cm_storage 'Microsoft.Storage/storageAccounts@2023-01-01' = { + name: 'cm0c420d2f21084cd' + kind: 'StorageV2' + location: location + sku: { + name: 'Standard_LRS' + } + properties: { + allowBlobPublicAccess: false + isHnsEnabled: true + } + identity: { + type: 'UserAssigned' + userAssignedIdentities: { + '${cm_identity.id}': { } + } + } +} + +resource cm_storage_00000000_0000_0000_0000_000000000000_StorageBlobDataContributor 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + name: guid('cm_storage', principalId, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ba92f5b4-2d11-453d-a403-e96b0029c9fe')) + properties: { + principalId: principalId + roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ba92f5b4-2d11-453d-a403-e96b0029c9fe') + principalType: 'User' + } + scope: cm_storage +} + +resource cm_storage_cm_identity_StorageBlobDataContributor 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + name: guid('cm_storage', cm_identity.id, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ba92f5b4-2d11-453d-a403-e96b0029c9fe')) + properties: { + principalId: cm_identity.properties.principalId + roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ba92f5b4-2d11-453d-a403-e96b0029c9fe') + principalType: 'ServicePrincipal' + } + scope: cm_storage +} + +resource cm_storage_00000000_0000_0000_0000_000000000000_StorageTableDataContributor 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + name: guid('cm_storage', principalId, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0a9a7e1f-b9d0-4cc4-a60d-0319b160aaa3')) + properties: { + principalId: principalId + roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0a9a7e1f-b9d0-4cc4-a60d-0319b160aaa3') + principalType: 'User' + } + scope: cm_storage +} + +resource cm_storage_cm_identity_StorageTableDataContributor 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + name: guid('cm_storage', cm_identity.id, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0a9a7e1f-b9d0-4cc4-a60d-0319b160aaa3')) + properties: { + principalId: cm_identity.properties.principalId + roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0a9a7e1f-b9d0-4cc4-a60d-0319b160aaa3') + principalType: 'ServicePrincipal' + } + scope: cm_storage +} + +resource cm_storage_blobs 'Microsoft.Storage/storageAccounts/blobServices@2024-01-01' = { + name: 'default' + parent: cm_storage +} + +resource cm_storage_blobs_container_default 'Microsoft.Storage/storageAccounts/blobServices/containers@2023-01-01' = { + name: 'default' + parent: cm_storage_blobs +} + +resource cm_servicebus 'Microsoft.ServiceBus/namespaces@2024-01-01' = { + name: 'cm0c420d2f21084cd' + location: location + sku: { + name: 'Standard' + tier: 'Standard' + } +} + +resource cm_servicebus_00000000_0000_0000_0000_000000000000_AzureServiceBusDataOwner 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + name: guid('cm_servicebus', principalId, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '090c5cfd-751d-490a-894a-3ce6f1109419')) + properties: { + principalId: principalId + roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '090c5cfd-751d-490a-894a-3ce6f1109419') + principalType: 'User' + } + scope: cm_servicebus +} + +resource cm_servicebus_cm_identity_AzureServiceBusDataOwner 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + name: guid('cm_servicebus', cm_identity.id, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '090c5cfd-751d-490a-894a-3ce6f1109419')) + properties: { + principalId: cm_identity.properties.principalId + roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '090c5cfd-751d-490a-894a-3ce6f1109419') + principalType: 'ServicePrincipal' + } + scope: cm_servicebus +} + +resource cm_servicebus_auth_rule 'Microsoft.ServiceBus/namespaces/AuthorizationRules@2021-11-01' = { + name: take('cm_servicebus_auth_rule-${uniqueString(resourceGroup().id)}', 50) + properties: { + rights: [ + 'Listen' + 'Send' + 'Manage' + ] + } + parent: cm_servicebus +} + +resource cm_servicebus_topic_private 'Microsoft.ServiceBus/namespaces/topics@2021-11-01' = { + name: 'cm_servicebus_topic_private' + properties: { + defaultMessageTimeToLive: 'P14D' + enableBatchedOperations: true + maxMessageSizeInKilobytes: 256 + requiresDuplicateDetection: false + status: 'Active' + supportOrdering: true + } + parent: cm_servicebus +} + +resource cm_servicebus_default_topic 'Microsoft.ServiceBus/namespaces/topics@2021-11-01' = { + name: 'cm_servicebus_default_topic' + properties: { + defaultMessageTimeToLive: 'P14D' + enableBatchedOperations: true + maxMessageSizeInKilobytes: 256 + requiresDuplicateDetection: false + status: 'Active' + supportOrdering: true + } + parent: cm_servicebus +} + +resource cm_servicebus_subscription_private 'Microsoft.ServiceBus/namespaces/topics/subscriptions@2021-11-01' = { + name: 'cm_servicebus_subscription_private' + properties: { + deadLetteringOnFilterEvaluationExceptions: true + deadLetteringOnMessageExpiration: true + defaultMessageTimeToLive: 'P14D' + enableBatchedOperations: true + isClientAffine: false + lockDuration: 'PT30S' + maxDeliveryCount: 10 + requiresSession: false + status: 'Active' + } + parent: cm_servicebus_topic_private +} + +resource cm_servicebus_subscription_default 'Microsoft.ServiceBus/namespaces/topics/subscriptions@2021-11-01' = { + name: 'cm_servicebus_subscription_default' + properties: { + deadLetteringOnFilterEvaluationExceptions: true + deadLetteringOnMessageExpiration: true + defaultMessageTimeToLive: 'P14D' + enableBatchedOperations: true + isClientAffine: false + lockDuration: 'PT30S' + maxDeliveryCount: 10 + requiresSession: false + status: 'Active' + } + parent: cm_servicebus_default_topic +} + +resource cm_eventgrid_topic 'Microsoft.EventGrid/systemTopics@2022-06-15' = { + name: 'cm0c420d2f21084cd' + location: location + identity: { + type: 'UserAssigned' + userAssignedIdentities: { + '${cm_identity.id}': { } + } + } + properties: { + source: cm_storage.id + topicType: 'Microsoft.Storage.StorageAccounts' + } +} + +resource cm_eventgrid_subscription_blob 'Microsoft.EventGrid/systemTopics/eventSubscriptions@2022-06-15' = { + name: 'cm_eventgrid_subscription_blob' + properties: { + deliveryWithResourceIdentity: { + identity: { + type: 'UserAssigned' + userAssignedIdentity: cm_identity.id + } + destination: { + endpointType: 'ServiceBusTopic' + properties: { + resourceId: cm_servicebus_topic_private.id + } + } + } + eventDeliverySchema: 'EventGridSchema' + filter: { + includedEventTypes: [ + 'Microsoft.Storage.BlobCreated' + 'Microsoft.Storage.BlobDeleted' + 'Microsoft.Storage.BlobRenamed' + ] + enableAdvancedFilteringOnArrays: true + } + retryPolicy: { + maxDeliveryAttempts: 30 + eventTimeToLiveInMinutes: 1440 + } + } + parent: cm_eventgrid_topic + dependsOn: [ + cm_servicebus_cm0c420d2f21084cd_role + ] +} + +resource cm_servicebus_cm0c420d2f21084cd_role 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + name: guid(cm_servicebus.id, cm_identity.id, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '69a216fc-b8fb-44d8-bc22-1f3c2cd27a39')) + properties: { + principalId: cm_identity.properties.principalId + roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '69a216fc-b8fb-44d8-bc22-1f3c2cd27a39') + principalType: 'ServicePrincipal' + } + scope: cm_servicebus +} + +output cm_managed_identity_id string = cm_identity.id diff --git a/sdk/cloudmachine/Azure.Provisioning.CloudMachine/tests/Data/OpenAI.bicep b/sdk/cloudmachine/Azure.Provisioning.CloudMachine/tests/Data/OpenAI.bicep new file mode 100644 index 0000000000000..ef7b1529ef131 --- /dev/null +++ b/sdk/cloudmachine/Azure.Provisioning.CloudMachine/tests/Data/OpenAI.bicep @@ -0,0 +1,312 @@ +@description('The location for the resource(s) to be deployed.') +param location string = resourceGroup().location + +@description('The objectId of the current user principal.') +param principalId string + +resource cm_identity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' = { + name: 'cm0c420d2f21084cd' + location: location +} + +resource cm_storage 'Microsoft.Storage/storageAccounts@2023-01-01' = { + name: 'cm0c420d2f21084cd' + kind: 'StorageV2' + location: location + sku: { + name: 'Standard_LRS' + } + properties: { + allowBlobPublicAccess: false + isHnsEnabled: true + } + identity: { + type: 'UserAssigned' + userAssignedIdentities: { + '${cm_identity.id}': { } + } + } +} + +resource cm_storage_00000000_0000_0000_0000_000000000000_StorageBlobDataContributor 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + name: guid('cm_storage', principalId, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ba92f5b4-2d11-453d-a403-e96b0029c9fe')) + properties: { + principalId: principalId + roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ba92f5b4-2d11-453d-a403-e96b0029c9fe') + principalType: 'User' + } + scope: cm_storage +} + +resource cm_storage_cm_identity_StorageBlobDataContributor 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + name: guid('cm_storage', cm_identity.id, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ba92f5b4-2d11-453d-a403-e96b0029c9fe')) + properties: { + principalId: cm_identity.properties.principalId + roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ba92f5b4-2d11-453d-a403-e96b0029c9fe') + principalType: 'ServicePrincipal' + } + scope: cm_storage +} + +resource cm_storage_00000000_0000_0000_0000_000000000000_StorageTableDataContributor 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + name: guid('cm_storage', principalId, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0a9a7e1f-b9d0-4cc4-a60d-0319b160aaa3')) + properties: { + principalId: principalId + roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0a9a7e1f-b9d0-4cc4-a60d-0319b160aaa3') + principalType: 'User' + } + scope: cm_storage +} + +resource cm_storage_cm_identity_StorageTableDataContributor 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + name: guid('cm_storage', cm_identity.id, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0a9a7e1f-b9d0-4cc4-a60d-0319b160aaa3')) + properties: { + principalId: cm_identity.properties.principalId + roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0a9a7e1f-b9d0-4cc4-a60d-0319b160aaa3') + principalType: 'ServicePrincipal' + } + scope: cm_storage +} + +resource cm_storage_blobs 'Microsoft.Storage/storageAccounts/blobServices@2024-01-01' = { + name: 'default' + parent: cm_storage +} + +resource cm_storage_blobs_container_default 'Microsoft.Storage/storageAccounts/blobServices/containers@2023-01-01' = { + name: 'default' + parent: cm_storage_blobs +} + +resource cm_servicebus 'Microsoft.ServiceBus/namespaces@2024-01-01' = { + name: 'cm0c420d2f21084cd' + location: location + sku: { + name: 'Standard' + tier: 'Standard' + } +} + +resource cm_servicebus_00000000_0000_0000_0000_000000000000_AzureServiceBusDataOwner 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + name: guid('cm_servicebus', principalId, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '090c5cfd-751d-490a-894a-3ce6f1109419')) + properties: { + principalId: principalId + roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '090c5cfd-751d-490a-894a-3ce6f1109419') + principalType: 'User' + } + scope: cm_servicebus +} + +resource cm_servicebus_cm_identity_AzureServiceBusDataOwner 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + name: guid('cm_servicebus', cm_identity.id, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '090c5cfd-751d-490a-894a-3ce6f1109419')) + properties: { + principalId: cm_identity.properties.principalId + roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '090c5cfd-751d-490a-894a-3ce6f1109419') + principalType: 'ServicePrincipal' + } + scope: cm_servicebus +} + +resource cm_servicebus_auth_rule 'Microsoft.ServiceBus/namespaces/AuthorizationRules@2021-11-01' = { + name: take('cm_servicebus_auth_rule-${uniqueString(resourceGroup().id)}', 50) + properties: { + rights: [ + 'Listen' + 'Send' + 'Manage' + ] + } + parent: cm_servicebus +} + +resource cm_servicebus_topic_private 'Microsoft.ServiceBus/namespaces/topics@2021-11-01' = { + name: 'cm_servicebus_topic_private' + properties: { + defaultMessageTimeToLive: 'P14D' + enableBatchedOperations: true + maxMessageSizeInKilobytes: 256 + requiresDuplicateDetection: false + status: 'Active' + supportOrdering: true + } + parent: cm_servicebus +} + +resource cm_servicebus_default_topic 'Microsoft.ServiceBus/namespaces/topics@2021-11-01' = { + name: 'cm_servicebus_default_topic' + properties: { + defaultMessageTimeToLive: 'P14D' + enableBatchedOperations: true + maxMessageSizeInKilobytes: 256 + requiresDuplicateDetection: false + status: 'Active' + supportOrdering: true + } + parent: cm_servicebus +} + +resource cm_servicebus_subscription_private 'Microsoft.ServiceBus/namespaces/topics/subscriptions@2021-11-01' = { + name: 'cm_servicebus_subscription_private' + properties: { + deadLetteringOnFilterEvaluationExceptions: true + deadLetteringOnMessageExpiration: true + defaultMessageTimeToLive: 'P14D' + enableBatchedOperations: true + isClientAffine: false + lockDuration: 'PT30S' + maxDeliveryCount: 10 + requiresSession: false + status: 'Active' + } + parent: cm_servicebus_topic_private +} + +resource cm_servicebus_subscription_default 'Microsoft.ServiceBus/namespaces/topics/subscriptions@2021-11-01' = { + name: 'cm_servicebus_subscription_default' + properties: { + deadLetteringOnFilterEvaluationExceptions: true + deadLetteringOnMessageExpiration: true + defaultMessageTimeToLive: 'P14D' + enableBatchedOperations: true + isClientAffine: false + lockDuration: 'PT30S' + maxDeliveryCount: 10 + requiresSession: false + status: 'Active' + } + parent: cm_servicebus_default_topic +} + +resource cm_eventgrid_topic 'Microsoft.EventGrid/systemTopics@2022-06-15' = { + name: 'cm0c420d2f21084cd' + location: location + identity: { + type: 'UserAssigned' + userAssignedIdentities: { + '${cm_identity.id}': { } + } + } + properties: { + source: cm_storage.id + topicType: 'Microsoft.Storage.StorageAccounts' + } +} + +resource cm_eventgrid_subscription_blob 'Microsoft.EventGrid/systemTopics/eventSubscriptions@2022-06-15' = { + name: 'cm_eventgrid_subscription_blob' + properties: { + deliveryWithResourceIdentity: { + identity: { + type: 'UserAssigned' + userAssignedIdentity: cm_identity.id + } + destination: { + endpointType: 'ServiceBusTopic' + properties: { + resourceId: cm_servicebus_topic_private.id + } + } + } + eventDeliverySchema: 'EventGridSchema' + filter: { + includedEventTypes: [ + 'Microsoft.Storage.BlobCreated' + 'Microsoft.Storage.BlobDeleted' + 'Microsoft.Storage.BlobRenamed' + ] + enableAdvancedFilteringOnArrays: true + } + retryPolicy: { + maxDeliveryAttempts: 30 + eventTimeToLiveInMinutes: 1440 + } + } + parent: cm_eventgrid_topic + dependsOn: [ + cm_servicebus_cm0c420d2f21084cd_role + ] +} + +resource cm_servicebus_cm0c420d2f21084cd_role 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + name: guid(cm_servicebus.id, cm_identity.id, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '69a216fc-b8fb-44d8-bc22-1f3c2cd27a39')) + properties: { + principalId: cm_identity.properties.principalId + roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '69a216fc-b8fb-44d8-bc22-1f3c2cd27a39') + principalType: 'ServicePrincipal' + } + scope: cm_servicebus +} + +resource openai 'Microsoft.CognitiveServices/accounts@2024-10-01' = { + name: 'cm0c420d2f21084cd' + location: location + kind: 'OpenAI' + properties: { + customSubDomainName: 'cm0c420d2f21084cd' + publicNetworkAccess: 'Enabled' + } + sku: { + name: 'S0' + } +} + +resource openai_00000000_0000_0000_0000_000000000000_CognitiveServicesOpenAIContributor 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + name: guid('openai', principalId, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a001fd3d-188f-4b5d-821b-7da978bf7442')) + properties: { + principalId: principalId + roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a001fd3d-188f-4b5d-821b-7da978bf7442') + principalType: 'User' + } + scope: openai +} + +resource openai_cm_identity_CognitiveServicesOpenAIContributor 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + name: guid('openai', cm_identity.id, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a001fd3d-188f-4b5d-821b-7da978bf7442')) + properties: { + principalId: cm_identity.properties.principalId + roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a001fd3d-188f-4b5d-821b-7da978bf7442') + principalType: 'ServicePrincipal' + } + scope: openai +} + +resource openai_cm0c420d2f21084cd_chat 'Microsoft.CognitiveServices/accounts/deployments@2024-06-01-preview' = { + name: 'cm0c420d2f21084cd_chat' + properties: { + model: { + format: 'OpenAI' + name: 'gpt-35-turbo' + version: '0125' + } + raiPolicyName: 'Microsoft.DefaultV2' + versionUpgradeOption: 'OnceNewDefaultVersionAvailable' + } + sku: { + name: 'Standard' + capacity: 120 + } + parent: openai +} + +resource openai_cm0c420d2f21084cd_embedding 'Microsoft.CognitiveServices/accounts/deployments@2024-06-01-preview' = { + name: 'cm0c420d2f21084cd_embedding' + properties: { + model: { + format: 'OpenAI' + name: 'text-embedding-ada-002' + version: '2' + } + raiPolicyName: 'Microsoft.DefaultV2' + versionUpgradeOption: 'OnceNewDefaultVersionAvailable' + } + sku: { + name: 'Standard' + capacity: 120 + } + parent: openai + dependsOn: [ + openai_cm0c420d2f21084cd_chat + ] +} + +output cm_managed_identity_id string = cm_identity.id diff --git a/sdk/cloudmachine/Azure.Provisioning.CloudMachine/tests/ProvisioningTests.cs b/sdk/cloudmachine/Azure.Provisioning.CloudMachine/tests/ProvisioningTests.cs new file mode 100644 index 0000000000000..3645cf2c7ccf3 --- /dev/null +++ b/sdk/cloudmachine/Azure.Provisioning.CloudMachine/tests/ProvisioningTests.cs @@ -0,0 +1,64 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +#nullable enable + +using System; +using System.IO; +using System.Linq; +using Azure.CloudMachine.AppService; +using Azure.CloudMachine.KeyVault; +using Azure.CloudMachine.OpenAI; +using NUnit.Framework; + +[assembly: NonParallelizable] + +namespace Azure.CloudMachine.Tests; + +public class ProvisioningTests +{ + [Test] + public void JustCloudMachine() + { + CloudMachineInfrastructure infra = new("cm0c420d2f21084cd"); + string actualBicep = infra.Build().Compile().FirstOrDefault().Value; + string expectedBicep = LoadTestFile("JustCloudMachine.bicep"); + Assert.AreEqual(expectedBicep, actualBicep); + } + + [Test] + public void OpenAI() + { + CloudMachineInfrastructure infra = new("cm0c420d2f21084cd"); + infra.AddFeature(new OpenAIModelFeature("gpt-35-turbo", "0125")); + infra.AddFeature(new OpenAIModelFeature("text-embedding-ada-002", "2", AIModelKind.Embedding)); + + string actualBicep = infra.Build().Compile().FirstOrDefault().Value; + string expectedBicep = LoadTestFile("OpenAI.bicep"); + Assert.AreEqual(expectedBicep, actualBicep); + } + + [Test] + public void GenerateBicep() + { + CloudMachineInfrastructure infra = new("cm0c420d2f21084cd"); + infra.AddFeature(new KeyVaultFeature()); + infra.AddFeature(new OpenAIModelFeature("gpt-35-turbo", "0125")); + infra.AddFeature(new OpenAIModelFeature("text-embedding-ada-002", "2", AIModelKind.Embedding)); + infra.AddFeature(new AppServiceFeature()); + + string actualBicep = infra.Build().Compile().FirstOrDefault().Value; + string expectedBicep = LoadTestFile("GenerateBicep.bicep"); + Assert.AreEqual(expectedBicep, actualBicep); + } + + private static string LoadTestFile(string filename) + { + string contents = File.ReadAllText(Path.Combine(TestContext.CurrentContext.TestDirectory, "Data", filename)); + contents = contents.Replace("\r\n", Environment.NewLine); + while (contents.EndsWith(Environment.NewLine)) { + contents = contents.Substring(0, contents.Length - Environment.NewLine.Length); + } + return contents; + } +} diff --git a/sdk/provisioning/Azure.Provisioning.CloudMachine/tests/TdkTests.cs b/sdk/cloudmachine/Azure.Provisioning.CloudMachine/tests/TdkTests.cs similarity index 95% rename from sdk/provisioning/Azure.Provisioning.CloudMachine/tests/TdkTests.cs rename to sdk/cloudmachine/Azure.Provisioning.CloudMachine/tests/TdkTests.cs index bc0d0c4959551..8f9bd3641bbee 100644 --- a/sdk/provisioning/Azure.Provisioning.CloudMachine/tests/TdkTests.cs +++ b/sdk/cloudmachine/Azure.Provisioning.CloudMachine/tests/TdkTests.cs @@ -7,7 +7,6 @@ using System.ClientModel.TypeSpec; using System.IO; using System.Threading.Tasks; -using Microsoft.AspNetCore.Http; using NUnit.Framework; namespace Azure.CloudMachine.Tests; @@ -59,7 +58,7 @@ @get @route("send") Send(@query message: string) : { internal interface IAssistantService { [HttpPut] - Task UploadAsync(HttpRequest document); + Task UploadAsync(byte[] document); Task SendAsync([FromQuery] string message); } diff --git a/sdk/provisioning/Azure.Provisioning.CloudMachine/tests/appsettings.json b/sdk/cloudmachine/Azure.Provisioning.CloudMachine/tests/appsettings.json similarity index 100% rename from sdk/provisioning/Azure.Provisioning.CloudMachine/tests/appsettings.json rename to sdk/cloudmachine/Azure.Provisioning.CloudMachine/tests/appsettings.json diff --git a/sdk/provisioning/Azure.Provisioning.CloudMachine/api/Azure.Provisioning.CloudMachine.net8.0.cs b/sdk/provisioning/Azure.Provisioning.CloudMachine/api/Azure.Provisioning.CloudMachine.net8.0.cs deleted file mode 100644 index dedf171961694..0000000000000 --- a/sdk/provisioning/Azure.Provisioning.CloudMachine/api/Azure.Provisioning.CloudMachine.net8.0.cs +++ /dev/null @@ -1,138 +0,0 @@ -namespace Azure -{ - public partial class RestCallFailedException : System.Exception - { - public RestCallFailedException(string message, System.ClientModel.Primitives.PipelineResponse response) { } - } - public partial class RestClient - { - public RestClient() { } - public RestClient(System.ClientModel.Primitives.PipelinePolicy auth) { } - public static Azure.RestClient Shared { get { throw null; } } - public System.ClientModel.Primitives.PipelineMessage Create(string method, System.Uri uri) { throw null; } - public System.ClientModel.Primitives.PipelineResponse Get(string uri, System.ClientModel.Primitives.RequestOptions? options = null) { throw null; } - public System.ClientModel.Primitives.PipelineResponse Patch(string uri, System.ClientModel.BinaryContent content, System.ClientModel.Primitives.RequestOptions? options = null) { throw null; } - public System.ClientModel.Primitives.PipelineResponse Post(string uri, System.ClientModel.BinaryContent content, System.ClientModel.Primitives.RequestOptions? options = null) { throw null; } - public System.ClientModel.Primitives.PipelineResponse Put(string uri, System.ClientModel.BinaryContent content, System.ClientModel.Primitives.RequestOptions? options = null) { throw null; } - public System.ClientModel.Primitives.PipelineResponse Send(System.ClientModel.Primitives.PipelineMessage message, System.ClientModel.Primitives.RequestOptions? options = null) { throw null; } - } - public partial class RestClientOptions : System.ClientModel.Primitives.ClientPipelineOptions - { - public RestClientOptions() { } - } -} -namespace Azure.CloudMachine -{ - public partial class CloudMachineCommands - { - public CloudMachineCommands() { } - public static bool Execute(string[] args, System.Action? configure = null, bool exitProcessIfHandled = true) { throw null; } - } - public partial class CloudMachineInfrastructure - { - public CloudMachineInfrastructure(string cmId) { } - public Azure.CloudMachine.FeatureCollection Features { get { throw null; } } - public string Id { get { throw null; } } - public Azure.Provisioning.Roles.UserAssignedIdentity Identity { get { throw null; } } - public Azure.Provisioning.ProvisioningParameter PrincipalIdParameter { get { throw null; } } - public void AddEndpoints() { } - public void AddFeature(Azure.Provisioning.CloudMachine.CloudMachineFeature feature) { } - public void AddResource(Azure.Provisioning.Primitives.NamedProvisionableConstruct resource) { } - public Azure.Provisioning.ProvisioningPlan Build(Azure.Provisioning.ProvisioningBuildOptions? context = null) { throw null; } - } - public partial class EventGridSystemTopicFeature : Azure.Provisioning.CloudMachine.CloudMachineFeature - { - public EventGridSystemTopicFeature(string name, Azure.Provisioning.CloudMachine.CloudMachineFeature source) { } - protected override Azure.Provisioning.Primitives.ProvisionableResource EmitCore(Azure.CloudMachine.CloudMachineInfrastructure infrastructure) { throw null; } - } - public partial class FeatureCollection - { - public FeatureCollection() { } - public System.Collections.Generic.IEnumerable FindAll() where T : Azure.Provisioning.CloudMachine.CloudMachineFeature { throw null; } - } - public partial class ServiceBusNamespaceFeature : Azure.Provisioning.CloudMachine.CloudMachineFeature - { - public ServiceBusNamespaceFeature(string name, Azure.Provisioning.ServiceBus.ServiceBusSkuName sku = Azure.Provisioning.ServiceBus.ServiceBusSkuName.Standard, Azure.Provisioning.ServiceBus.ServiceBusSkuTier tier = Azure.Provisioning.ServiceBus.ServiceBusSkuTier.Standard) { } - protected override Azure.Provisioning.Primitives.ProvisionableResource EmitCore(Azure.CloudMachine.CloudMachineInfrastructure infrastructure) { throw null; } - } - public partial class ServiceBusSubscriptionFeature : Azure.Provisioning.CloudMachine.CloudMachineFeature - { - public ServiceBusSubscriptionFeature(string name, Azure.CloudMachine.ServiceBusTopicFeature parent) { } - protected override Azure.Provisioning.Primitives.ProvisionableResource EmitCore(Azure.CloudMachine.CloudMachineInfrastructure infrastructure) { throw null; } - } - public partial class ServiceBusTopicFeature : Azure.Provisioning.CloudMachine.CloudMachineFeature - { - public ServiceBusTopicFeature(string name, Azure.CloudMachine.ServiceBusNamespaceFeature parent) { } - protected override Azure.Provisioning.Primitives.ProvisionableResource EmitCore(Azure.CloudMachine.CloudMachineInfrastructure infrastructure) { throw null; } - } - public partial class StorageFeature : Azure.Provisioning.CloudMachine.CloudMachineFeature - { - public StorageFeature(string accountName, Azure.Provisioning.Storage.StorageSkuName sku = Azure.Provisioning.Storage.StorageSkuName.StandardLrs, System.Collections.Generic.IEnumerable? containerNames = null) { } - protected override Azure.Provisioning.Primitives.ProvisionableResource EmitCore(Azure.CloudMachine.CloudMachineInfrastructure infrastructure) { throw null; } - } - public partial class SystemTopicEventSubscriptionFeature : Azure.Provisioning.CloudMachine.CloudMachineFeature - { - public SystemTopicEventSubscriptionFeature(string name, Azure.CloudMachine.EventGridSystemTopicFeature parent, Azure.CloudMachine.ServiceBusTopicFeature destination, Azure.CloudMachine.ServiceBusNamespaceFeature parentNamespace) { } - protected override Azure.Provisioning.Primitives.ProvisionableResource EmitCore(Azure.CloudMachine.CloudMachineInfrastructure infrastructure) { throw null; } - } -} -namespace Azure.CloudMachine.AppService -{ - public partial class AppServiceFeature : Azure.Provisioning.CloudMachine.CloudMachineFeature - { - public AppServiceFeature(Azure.Provisioning.AppService.AppServiceSkuDescription? sku = null) { } - public Azure.Provisioning.AppService.AppServiceSkuDescription Sku { get { throw null; } set { } } - protected override Azure.Provisioning.Primitives.ProvisionableResource EmitCore(Azure.CloudMachine.CloudMachineInfrastructure infrastructure) { throw null; } - } -} -namespace Azure.CloudMachine.KeyVault -{ - public partial class KeyVaultFeature : Azure.Provisioning.CloudMachine.CloudMachineFeature - { - public KeyVaultFeature(Azure.Provisioning.KeyVault.KeyVaultSku? sku = null) { } - public Azure.Provisioning.KeyVault.KeyVaultSku Sku { get { throw null; } set { } } - protected override Azure.Provisioning.Primitives.ProvisionableResource EmitCore(Azure.CloudMachine.CloudMachineInfrastructure infrastructure) { throw null; } - } -} -namespace Azure.CloudMachine.OpenAI -{ - public enum AIModelKind - { - Chat = 0, - Embedding = 1, - } - public partial class OpenAIModel : Azure.Provisioning.CloudMachine.CloudMachineFeature - { - public OpenAIModel(string model, string modelVersion, Azure.CloudMachine.OpenAI.AIModelKind kind = Azure.CloudMachine.OpenAI.AIModelKind.Chat) { } - public string Model { get { throw null; } } - public string ModelVersion { get { throw null; } } - public override void AddTo(Azure.CloudMachine.CloudMachineInfrastructure cm) { } - protected override Azure.Provisioning.Primitives.ProvisionableResource EmitCore(Azure.CloudMachine.CloudMachineInfrastructure cm) { throw null; } - } -} -namespace Azure.Provisioning.CloudMachine -{ - public abstract partial class CloudMachineFeature - { - protected CloudMachineFeature() { } - [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] - public Azure.Provisioning.Primitives.ProvisionableResource Emitted { get { throw null; } protected set { } } - protected internal System.Collections.Generic.Dictionary RequiredSystemRoles { get { throw null; } } - [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] - public virtual void AddTo(Azure.CloudMachine.CloudMachineInfrastructure cm) { } - [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] - public void Emit(Azure.CloudMachine.CloudMachineInfrastructure cm) { } - protected abstract Azure.Provisioning.Primitives.ProvisionableResource EmitCore(Azure.CloudMachine.CloudMachineInfrastructure cm); - protected static T ValidateIsOfType(Azure.Provisioning.CloudMachine.CloudMachineFeature resource) { throw null; } - } -} -namespace System.ClientModel.TypeSpec -{ - public static partial class TypeSpecWriter - { - public static void WriteModel(System.IO.Stream output, System.Type model) { } - public static void WriteModel(System.IO.Stream output) { } - public static void WriteServer(System.IO.Stream output, System.Type service) { } - public static void WriteServer(System.IO.Stream output) { } - } -} diff --git a/sdk/provisioning/Azure.Provisioning.CloudMachine/api/Azure.Provisioning.CloudMachine.netstandard2.0.cs b/sdk/provisioning/Azure.Provisioning.CloudMachine/api/Azure.Provisioning.CloudMachine.netstandard2.0.cs deleted file mode 100644 index dedf171961694..0000000000000 --- a/sdk/provisioning/Azure.Provisioning.CloudMachine/api/Azure.Provisioning.CloudMachine.netstandard2.0.cs +++ /dev/null @@ -1,138 +0,0 @@ -namespace Azure -{ - public partial class RestCallFailedException : System.Exception - { - public RestCallFailedException(string message, System.ClientModel.Primitives.PipelineResponse response) { } - } - public partial class RestClient - { - public RestClient() { } - public RestClient(System.ClientModel.Primitives.PipelinePolicy auth) { } - public static Azure.RestClient Shared { get { throw null; } } - public System.ClientModel.Primitives.PipelineMessage Create(string method, System.Uri uri) { throw null; } - public System.ClientModel.Primitives.PipelineResponse Get(string uri, System.ClientModel.Primitives.RequestOptions? options = null) { throw null; } - public System.ClientModel.Primitives.PipelineResponse Patch(string uri, System.ClientModel.BinaryContent content, System.ClientModel.Primitives.RequestOptions? options = null) { throw null; } - public System.ClientModel.Primitives.PipelineResponse Post(string uri, System.ClientModel.BinaryContent content, System.ClientModel.Primitives.RequestOptions? options = null) { throw null; } - public System.ClientModel.Primitives.PipelineResponse Put(string uri, System.ClientModel.BinaryContent content, System.ClientModel.Primitives.RequestOptions? options = null) { throw null; } - public System.ClientModel.Primitives.PipelineResponse Send(System.ClientModel.Primitives.PipelineMessage message, System.ClientModel.Primitives.RequestOptions? options = null) { throw null; } - } - public partial class RestClientOptions : System.ClientModel.Primitives.ClientPipelineOptions - { - public RestClientOptions() { } - } -} -namespace Azure.CloudMachine -{ - public partial class CloudMachineCommands - { - public CloudMachineCommands() { } - public static bool Execute(string[] args, System.Action? configure = null, bool exitProcessIfHandled = true) { throw null; } - } - public partial class CloudMachineInfrastructure - { - public CloudMachineInfrastructure(string cmId) { } - public Azure.CloudMachine.FeatureCollection Features { get { throw null; } } - public string Id { get { throw null; } } - public Azure.Provisioning.Roles.UserAssignedIdentity Identity { get { throw null; } } - public Azure.Provisioning.ProvisioningParameter PrincipalIdParameter { get { throw null; } } - public void AddEndpoints() { } - public void AddFeature(Azure.Provisioning.CloudMachine.CloudMachineFeature feature) { } - public void AddResource(Azure.Provisioning.Primitives.NamedProvisionableConstruct resource) { } - public Azure.Provisioning.ProvisioningPlan Build(Azure.Provisioning.ProvisioningBuildOptions? context = null) { throw null; } - } - public partial class EventGridSystemTopicFeature : Azure.Provisioning.CloudMachine.CloudMachineFeature - { - public EventGridSystemTopicFeature(string name, Azure.Provisioning.CloudMachine.CloudMachineFeature source) { } - protected override Azure.Provisioning.Primitives.ProvisionableResource EmitCore(Azure.CloudMachine.CloudMachineInfrastructure infrastructure) { throw null; } - } - public partial class FeatureCollection - { - public FeatureCollection() { } - public System.Collections.Generic.IEnumerable FindAll() where T : Azure.Provisioning.CloudMachine.CloudMachineFeature { throw null; } - } - public partial class ServiceBusNamespaceFeature : Azure.Provisioning.CloudMachine.CloudMachineFeature - { - public ServiceBusNamespaceFeature(string name, Azure.Provisioning.ServiceBus.ServiceBusSkuName sku = Azure.Provisioning.ServiceBus.ServiceBusSkuName.Standard, Azure.Provisioning.ServiceBus.ServiceBusSkuTier tier = Azure.Provisioning.ServiceBus.ServiceBusSkuTier.Standard) { } - protected override Azure.Provisioning.Primitives.ProvisionableResource EmitCore(Azure.CloudMachine.CloudMachineInfrastructure infrastructure) { throw null; } - } - public partial class ServiceBusSubscriptionFeature : Azure.Provisioning.CloudMachine.CloudMachineFeature - { - public ServiceBusSubscriptionFeature(string name, Azure.CloudMachine.ServiceBusTopicFeature parent) { } - protected override Azure.Provisioning.Primitives.ProvisionableResource EmitCore(Azure.CloudMachine.CloudMachineInfrastructure infrastructure) { throw null; } - } - public partial class ServiceBusTopicFeature : Azure.Provisioning.CloudMachine.CloudMachineFeature - { - public ServiceBusTopicFeature(string name, Azure.CloudMachine.ServiceBusNamespaceFeature parent) { } - protected override Azure.Provisioning.Primitives.ProvisionableResource EmitCore(Azure.CloudMachine.CloudMachineInfrastructure infrastructure) { throw null; } - } - public partial class StorageFeature : Azure.Provisioning.CloudMachine.CloudMachineFeature - { - public StorageFeature(string accountName, Azure.Provisioning.Storage.StorageSkuName sku = Azure.Provisioning.Storage.StorageSkuName.StandardLrs, System.Collections.Generic.IEnumerable? containerNames = null) { } - protected override Azure.Provisioning.Primitives.ProvisionableResource EmitCore(Azure.CloudMachine.CloudMachineInfrastructure infrastructure) { throw null; } - } - public partial class SystemTopicEventSubscriptionFeature : Azure.Provisioning.CloudMachine.CloudMachineFeature - { - public SystemTopicEventSubscriptionFeature(string name, Azure.CloudMachine.EventGridSystemTopicFeature parent, Azure.CloudMachine.ServiceBusTopicFeature destination, Azure.CloudMachine.ServiceBusNamespaceFeature parentNamespace) { } - protected override Azure.Provisioning.Primitives.ProvisionableResource EmitCore(Azure.CloudMachine.CloudMachineInfrastructure infrastructure) { throw null; } - } -} -namespace Azure.CloudMachine.AppService -{ - public partial class AppServiceFeature : Azure.Provisioning.CloudMachine.CloudMachineFeature - { - public AppServiceFeature(Azure.Provisioning.AppService.AppServiceSkuDescription? sku = null) { } - public Azure.Provisioning.AppService.AppServiceSkuDescription Sku { get { throw null; } set { } } - protected override Azure.Provisioning.Primitives.ProvisionableResource EmitCore(Azure.CloudMachine.CloudMachineInfrastructure infrastructure) { throw null; } - } -} -namespace Azure.CloudMachine.KeyVault -{ - public partial class KeyVaultFeature : Azure.Provisioning.CloudMachine.CloudMachineFeature - { - public KeyVaultFeature(Azure.Provisioning.KeyVault.KeyVaultSku? sku = null) { } - public Azure.Provisioning.KeyVault.KeyVaultSku Sku { get { throw null; } set { } } - protected override Azure.Provisioning.Primitives.ProvisionableResource EmitCore(Azure.CloudMachine.CloudMachineInfrastructure infrastructure) { throw null; } - } -} -namespace Azure.CloudMachine.OpenAI -{ - public enum AIModelKind - { - Chat = 0, - Embedding = 1, - } - public partial class OpenAIModel : Azure.Provisioning.CloudMachine.CloudMachineFeature - { - public OpenAIModel(string model, string modelVersion, Azure.CloudMachine.OpenAI.AIModelKind kind = Azure.CloudMachine.OpenAI.AIModelKind.Chat) { } - public string Model { get { throw null; } } - public string ModelVersion { get { throw null; } } - public override void AddTo(Azure.CloudMachine.CloudMachineInfrastructure cm) { } - protected override Azure.Provisioning.Primitives.ProvisionableResource EmitCore(Azure.CloudMachine.CloudMachineInfrastructure cm) { throw null; } - } -} -namespace Azure.Provisioning.CloudMachine -{ - public abstract partial class CloudMachineFeature - { - protected CloudMachineFeature() { } - [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] - public Azure.Provisioning.Primitives.ProvisionableResource Emitted { get { throw null; } protected set { } } - protected internal System.Collections.Generic.Dictionary RequiredSystemRoles { get { throw null; } } - [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] - public virtual void AddTo(Azure.CloudMachine.CloudMachineInfrastructure cm) { } - [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] - public void Emit(Azure.CloudMachine.CloudMachineInfrastructure cm) { } - protected abstract Azure.Provisioning.Primitives.ProvisionableResource EmitCore(Azure.CloudMachine.CloudMachineInfrastructure cm); - protected static T ValidateIsOfType(Azure.Provisioning.CloudMachine.CloudMachineFeature resource) { throw null; } - } -} -namespace System.ClientModel.TypeSpec -{ - public static partial class TypeSpecWriter - { - public static void WriteModel(System.IO.Stream output, System.Type model) { } - public static void WriteModel(System.IO.Stream output) { } - public static void WriteServer(System.IO.Stream output, System.Type service) { } - public static void WriteServer(System.IO.Stream output) { } - } -} diff --git a/sdk/provisioning/Azure.Provisioning.CloudMachine/src/AzureSdkExtensions/OpenAIModel.cs b/sdk/provisioning/Azure.Provisioning.CloudMachine/src/AzureSdkExtensions/OpenAIModel.cs deleted file mode 100644 index 077927ecf33e2..0000000000000 --- a/sdk/provisioning/Azure.Provisioning.CloudMachine/src/AzureSdkExtensions/OpenAIModel.cs +++ /dev/null @@ -1,92 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -using System; -using System.Collections.Generic; -using System.Diagnostics; -using Azure.Provisioning.CloudMachine; -using Azure.Provisioning.CognitiveServices; -using Azure.Provisioning.Primitives; - -namespace Azure.CloudMachine.OpenAI; - -public class OpenAIModel : CloudMachineFeature -{ - public OpenAIModel(string model, string modelVersion, AIModelKind kind = AIModelKind.Chat) { - Kind = kind; - Model = model; - ModelVersion = modelVersion; - } - - public string Model { get; } - public string ModelVersion { get; } - private AIModelKind Kind { get; } - - internal OpenAIFeature Account { get; set; } = default!; - - private OpenAIFeature GetOrCreateOpenAI(CloudMachineInfrastructure cm) - { - foreach (OpenAIFeature feature in cm.Features.FindAll()) - { - return feature; - } - var openAI = new OpenAIFeature(); - cm.AddFeature(openAI); - return openAI; - } - - public override void AddTo(CloudMachineInfrastructure cm) - { - OpenAIFeature openAI = GetOrCreateOpenAI(cm); - openAI.AddModel(this); - } - - protected override ProvisionableResource EmitCore(CloudMachineInfrastructure cm) - { - string name = Kind switch - { - AIModelKind.Chat => $"{cm.Id}_chat", - AIModelKind.Embedding => $"{cm.Id}_embedding", - _ => throw new NotImplementedException() - }; - - Debug.Assert(Account != null); - var emitted = Account!.Emitted; - if (emitted == null) - { - Account.Emit(cm); - } - CognitiveServicesAccount parent = (CognitiveServicesAccount)Account!.Emitted; - - CognitiveServicesAccountDeployment deployment = new($"openai_{name}", "2024-06-01-preview") { - Parent = parent, - Name = name, - Properties = new CognitiveServicesAccountDeploymentProperties() - { - Model = new CognitiveServicesAccountDeploymentModel() - { - Name = Model, - Format = "OpenAI", - Version = ModelVersion - }, - VersionUpgradeOption = DeploymentModelVersionUpgradeOption.OnceNewDefaultVersionAvailable, - RaiPolicyName = "Microsoft.DefaultV2", - }, - Sku = new CognitiveServicesSku - { - Capacity = 120, - Name = "Standard" - } - }; - - cm.AddResource(deployment); - - return deployment; - } -} - -public enum AIModelKind -{ - Chat, - Embedding, -} diff --git a/sdk/provisioning/Azure.Provisioning.CloudMachine/src/CDKLevel3/AzdHelpers.cs b/sdk/provisioning/Azure.Provisioning.CloudMachine/src/CDKLevel3/AzdHelpers.cs deleted file mode 100644 index f2907aaceb68c..0000000000000 --- a/sdk/provisioning/Azure.Provisioning.CloudMachine/src/CDKLevel3/AzdHelpers.cs +++ /dev/null @@ -1,153 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -using System.IO; -using Azure.Provisioning; -using Azure.Provisioning.CloudMachine; -using Azure.Provisioning.Primitives; -using Azure.Provisioning.Resources; -using Azure.Provisioning.Expressions; -using System.Text.Json.Nodes; -using System.Text.Json; -using System; - -namespace Azure.CloudMachine; - -internal static class AzdHelpers -{ - private const string MainBicepName = "main"; - private const string ResourceGroupVersion = "2024-03-01"; - - internal static void Init(string infraDirectory, CloudMachineInfrastructure cmi) - { - Directory.CreateDirectory(infraDirectory); - - cmi.Build().Save(infraDirectory); - var cmid = ReadOrCreateCmid(); - - // main.bicep - var location = new ProvisioningParameter("location", typeof(string)); - var principalId = new ProvisioningParameter("principalId", typeof(string)); - - ResourceGroup rg = new(nameof(rg), ResourceGroupVersion) - { - Name = cmid, - Location = location - }; - - Infrastructure mainBicep = new("main") - { - TargetScope = DeploymentScope.Subscription - }; - ModuleImport import = new("cm", $"cm.bicep") - { - Name = "cm", - Scope = new IdentifierExpression(rg.BicepIdentifier) - }; - import.Parameters.Add(nameof(location), location); - import.Parameters.Add(nameof(principalId), principalId); - - mainBicep.Add(rg); - mainBicep.Add(import); - mainBicep.Add(location); - mainBicep.Add(principalId); - mainBicep.Build().Save(infraDirectory); - - WriteMainParametersFile(infraDirectory); - } - - private static void WriteMainParametersFile(string infraDirectory) - { - File.WriteAllText(Path.Combine(infraDirectory, $"{MainBicepName}.parameters.json"), - """ - { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", - "contentVersion": "1.0.0.0", - "parameters": { - "environmentName": { - "value": "${AZURE_ENV_NAME}" - }, - "location" : { - "value" : "${AZURE_LOCATION}" - }, - "principalId": { - "value": "${AZURE_PRINCIPAL_ID}" - } - } - } - """); - } - - internal static string ReadOrCreateCmid() - { - string appsettings = Path.Combine(".", "appsettings.json"); - - string? cmid; - if (!File.Exists(appsettings)) - { - cmid = GenerateCloudMachineId(); - - using FileStream file = File.OpenWrite(appsettings); - Utf8JsonWriter writer = new Utf8JsonWriter(file); - writer.WriteStartObject(); - writer.WritePropertyName("CloudMachine"u8); - writer.WriteStartObject(); - writer.WriteString("ID"u8, cmid); - writer.WriteEndObject(); - writer.WriteEndObject(); - writer.Flush(); - return cmid; - } - - using FileStream json = File.OpenRead(appsettings); - using JsonDocument jd = JsonDocument.Parse(json); - JsonElement je = jd.RootElement; - // attempt to read CM configuration from existing configuration file - if (je.TryGetProperty("CloudMachine"u8, out JsonElement cm)) - { - if (!cm.TryGetProperty("ID"u8, out JsonElement id)) - { - throw new NotImplementedException(); - } - cmid = id.GetString(); - if (cmid == null) - throw new NotImplementedException(); - return cmid; - } - else - { // add CM configuration to existing file - json.Seek(0, SeekOrigin.Begin); - JsonNode? root = JsonNode.Parse(json); - json.Close(); - if (root is null || root is not JsonObject obj) throw new InvalidOperationException("Existing appsettings.json is not a valid JSON object"); - - var cmProperties = new JsonObject(); - cmid = GenerateCloudMachineId(); - cmProperties.Add("ID", cmid); - obj.Add("CloudMachine", cmProperties); - - using FileStream file = File.OpenWrite(appsettings); - JsonWriterOptions writerOptions = new() - { - Indented = true, - }; - Utf8JsonWriter writer = new(file, writerOptions); - JsonSerializerOptions options = new() - { - WriteIndented = true, - }; - root.WriteTo(writer, options); - writer.Flush(); - } - - return cmid; - - 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; - } - } -} diff --git a/sdk/provisioning/Azure.Provisioning.CloudMachine/src/CDKLevel3/CloudMachineFeature.cs b/sdk/provisioning/Azure.Provisioning.CloudMachine/src/CDKLevel3/CloudMachineFeature.cs deleted file mode 100644 index ac23c3c3d5198..0000000000000 --- a/sdk/provisioning/Azure.Provisioning.CloudMachine/src/CDKLevel3/CloudMachineFeature.cs +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -using System; -using System.Collections.Generic; -using System.ComponentModel; -using Azure.CloudMachine; -using Azure.Provisioning.Primitives; - -namespace Azure.Provisioning.CloudMachine; - -public abstract class CloudMachineFeature -{ - [EditorBrowsable(EditorBrowsableState.Never)] - public virtual void AddTo(CloudMachineInfrastructure cm) => cm.Features.Add(this); - - [EditorBrowsable(EditorBrowsableState.Never)] - public void Emit(CloudMachineInfrastructure cm) - { - if (Emitted != null) - return; - ProvisionableResource provisionable = EmitCore(cm); - Emitted = provisionable; - } - - protected abstract ProvisionableResource EmitCore(CloudMachineInfrastructure cm); - - [EditorBrowsable(EditorBrowsableState.Never)] - public ProvisionableResource Emitted { get; protected set; } = default!; - - protected internal Dictionary RequiredSystemRoles { get; } = []; - - protected static T ValidateIsOfType(CloudMachineFeature resource) - { - if (resource.Emitted is T typed) - return typed; - throw new ArgumentException($"Expected resource of type {typeof(T).Name}, but got {resource.GetType().Name}"); - } -} diff --git a/sdk/provisioning/Azure.Provisioning.CloudMachine/src/CDKLevel3/CloudMachineInfrastructure.cs b/sdk/provisioning/Azure.Provisioning.CloudMachine/src/CDKLevel3/CloudMachineInfrastructure.cs deleted file mode 100644 index 0973d0106f44c..0000000000000 --- a/sdk/provisioning/Azure.Provisioning.CloudMachine/src/CDKLevel3/CloudMachineInfrastructure.cs +++ /dev/null @@ -1,160 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -using System; -using Azure.Provisioning.Authorization; -using Azure.Provisioning.Expressions; -using Azure.Provisioning.Roles; -using Azure.Provisioning.Primitives; -using System.Collections.Generic; -using Azure.Provisioning; -using Azure.Provisioning.CloudMachine; - -namespace Azure.CloudMachine; - -public class CloudMachineInfrastructure -{ - internal const string SB_PRIVATE_TOPIC = "cm_servicebus_topic_private"; - internal const string SB_PRIVATE_SUB = "cm_servicebus_subscription_private"; - private readonly string _cmId; - - private Infrastructure _infrastructure = new Infrastructure("cm"); - private List _resources = new(); - public FeatureCollection Features { get; } = new(); - internal List Endpoints { get; } = new(); - - public UserAssignedIdentity Identity { get; private set; } - public string Id => _cmId; - - /// - /// The common principalId parameter. - /// - public ProvisioningParameter PrincipalIdParameter => new ProvisioningParameter("principalId", typeof(string)); - - ///// - ///// The common principalType parameter. - ///// - //public ProvisioningParameter PrincipalTypeParameter => new BicepParameter("principalType", typeof(string)); - - ///// - ///// The common principalName parameter. - ///// - //public ProvisioningParameter PrincipalNameParameter => new BicepParameter("principalName", typeof(string)); - - public CloudMachineInfrastructure(string cmId) - { - _cmId = cmId; - - // setup CM identity - Identity = new UserAssignedIdentity("cm_identity"); - Identity.Name = _cmId; - _infrastructure.Add(new ProvisioningOutput($"cm_managed_identity_id", typeof(string)) { Value = Identity.Id }); - - // Add core features - var storage = new StorageFeature(_cmId); - Features.Add(storage); - var sbNamespace = new ServiceBusNamespaceFeature(_cmId); - Features.Add(sbNamespace); - var sbTopic_private = new ServiceBusTopicFeature("cm_servicebus_topic_private", sbNamespace); - Features.Add(sbTopic_private); - var sbTopic_default = new ServiceBusTopicFeature("cm_servicebus_default_topic", sbNamespace); - Features.Add(sbTopic_default); - Features.Add(new ServiceBusSubscriptionFeature("cm_servicebus_subscription_private", sbTopic_private)); - Features.Add(new ServiceBusSubscriptionFeature("cm_servicebus_subscription_default", sbTopic_default)); - var systemTopic = new EventGridSystemTopicFeature(_cmId, storage); - Features.Add(systemTopic); - Features.Add(new SystemTopicEventSubscriptionFeature("cm_eventgrid_subscription_blob", systemTopic, sbTopic_private, sbNamespace)); - } - - public void AddResource(NamedProvisionableConstruct resource) - { - _resources.Add(resource); - } - public void AddFeature(CloudMachineFeature feature) - { - feature.AddTo(this); - } - - public void AddEndpoints() - { - Type endpointsType = typeof(T); - if (!endpointsType.IsInterface) - throw new InvalidOperationException("Endpoints type must be an interface."); - Endpoints.Add(endpointsType); - } - - public ProvisioningPlan Build(ProvisioningBuildOptions? context = null) - { - if (context == null) - context = new ProvisioningBuildOptions(); - - Features.Emit(this); - - // This must occur after the features have been emitted. - context.InfrastructureResolvers.Add(new RoleResolver(Features.RoleAnnotations, [Identity], [PrincipalIdParameter])); - - // Always add a default location parameter. - // azd assumes there will be a location parameter for every module. - // The Infrastructure location resolver will resolve unset Location properties to this parameter. - _infrastructure.Add(new ProvisioningParameter("location", typeof(string)) - { - Description = "The location for the resource(s) to be deployed.", - Value = BicepFunction.GetResourceGroup().Location - }); - - _infrastructure.Add(new ProvisioningParameter("principalId", typeof(string)) - { - Description = "The objectId of the current user principal.", - }); - - _infrastructure.Add(Identity); - - // Add any add-on resources to the infrastructure. - foreach (Provisionable resource in _resources) - { - _infrastructure.Add(resource); - } - - return _infrastructure.Build(context); - } - - internal class RoleResolver(Dictionary annotations, IEnumerable managedIdentities, IEnumerable> userPrincipals) : InfrastructureResolver - { - public override IEnumerable ResolveResources(IEnumerable resources, ProvisioningBuildOptions options) - { - foreach (Provisionable provisionable in base.ResolveResources(resources, options)) - { - yield return provisionable; - if (annotations.TryGetValue(provisionable, out (string RoleName, string RoleId)[]? roles) && provisionable is ProvisionableResource resource && roles is not null) - { - foreach ((string RoleName, string RoleId) role in roles) - { - foreach (BicepValue userPrincipal in userPrincipals) - { - yield return new RoleAssignment($"{resource.BicepIdentifier}_{userPrincipal.Value.ToString().Replace('-', '_')}_{role.RoleName}") - { - Name = BicepFunction.CreateGuid(resource.BicepIdentifier, userPrincipal, BicepFunction.GetSubscriptionResourceId("Microsoft.Authorization/roleDefinitions", role.RoleId)), - Scope = new IdentifierExpression(resource.BicepIdentifier), - PrincipalType = RoleManagementPrincipalType.User, - RoleDefinitionId = BicepFunction.GetSubscriptionResourceId("Microsoft.Authorization/roleDefinitions", role.RoleId), - PrincipalId = userPrincipal - }; - } - - foreach (UserAssignedIdentity identity in managedIdentities) - { - yield return new RoleAssignment($"{resource.BicepIdentifier}_{identity.BicepIdentifier}_{role.RoleName}") - { - Name = BicepFunction.CreateGuid(resource.BicepIdentifier, identity.Id, BicepFunction.GetSubscriptionResourceId("Microsoft.Authorization/roleDefinitions", role.RoleId)), - Scope = new IdentifierExpression(resource.BicepIdentifier), - PrincipalType = RoleManagementPrincipalType.ServicePrincipal, - RoleDefinitionId = BicepFunction.GetSubscriptionResourceId("Microsoft.Authorization/roleDefinitions", role.RoleId), - PrincipalId = identity.PrincipalId - }; - } - } - } - } - } - } -} diff --git a/sdk/provisioning/Azure.Provisioning.CloudMachine/src/CDKLevel3/ServiceBusTopicFeature.cs b/sdk/provisioning/Azure.Provisioning.CloudMachine/src/CDKLevel3/ServiceBusTopicFeature.cs deleted file mode 100644 index 707b316d9389f..0000000000000 --- a/sdk/provisioning/Azure.Provisioning.CloudMachine/src/CDKLevel3/ServiceBusTopicFeature.cs +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -using System; -using Azure.Provisioning.CloudMachine; -using Azure.Provisioning.Primitives; -using Azure.Provisioning.ServiceBus; - -namespace Azure.CloudMachine; - -public class ServiceBusTopicFeature(string name, ServiceBusNamespaceFeature parent) : CloudMachineFeature -{ - protected override ProvisionableResource EmitCore(CloudMachineInfrastructure infrastructure) - { - var topic = new ServiceBusTopic(name, "2021-11-01") - { - Name = name, - Parent = ValidateIsOfType(parent), - MaxMessageSizeInKilobytes = 256, - DefaultMessageTimeToLive = TimeSpan.FromDays(14), - RequiresDuplicateDetection = false, - EnableBatchedOperations = true, - SupportOrdering = true, - Status = ServiceBusMessagingEntityStatus.Active - }; - - infrastructure.AddResource(topic); - Emitted = topic; - return topic; - } -} diff --git a/sdk/provisioning/Azure.Provisioning.CloudMachine/src/CDKLevel3/StorageFeature.cs b/sdk/provisioning/Azure.Provisioning.CloudMachine/src/CDKLevel3/StorageFeature.cs deleted file mode 100644 index b7f67141bc1f1..0000000000000 --- a/sdk/provisioning/Azure.Provisioning.CloudMachine/src/CDKLevel3/StorageFeature.cs +++ /dev/null @@ -1,79 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -using System.Collections.Generic; -using System.Linq; -using Azure.Provisioning; -using Azure.Provisioning.CloudMachine; -using Azure.Provisioning.Expressions; -using Azure.Provisioning.Primitives; -using Azure.Provisioning.Resources; -using Azure.Provisioning.Storage; - -namespace Azure.CloudMachine; - -public class StorageFeature : CloudMachineFeature -{ - private List _containerNames; - private StorageSkuName _skuName; - private string _name; - - public StorageFeature(string accountName, StorageSkuName sku = StorageSkuName.StandardLrs, IEnumerable? containerNames = null) - { - _skuName = sku; - _name = accountName; - if (containerNames != null) - _containerNames = containerNames.ToList(); - else - _containerNames = ["default"]; - } - - protected override ProvisionableResource EmitCore(CloudMachineInfrastructure infrastructure) - { - var _storage = - new StorageAccount("cm_storage", StorageAccount.ResourceVersions.V2023_01_01) - { - Name = _name, - Kind = StorageKind.StorageV2, - Sku = new StorageSku { Name = _skuName }, - IsHnsEnabled = true, - AllowBlobPublicAccess = false, - Identity = new() - { - ManagedServiceIdentityType = ManagedServiceIdentityType.UserAssigned, - UserAssignedIdentities = { { BicepFunction.Interpolate($"{infrastructure.Identity.Id}").Compile().ToString(), new UserAssignedIdentityDetails() } } - } - }; - infrastructure.AddResource(_storage); - - var _blobs = new BlobService("cm_storage_blobs") - { - Parent = _storage, - }; - infrastructure.AddResource(_blobs); - - foreach (var containerName in _containerNames) - { - infrastructure.AddResource( - new BlobContainer("cm_storage_blobs_container_" + containerName, "2023-01-01") - { - Parent = _blobs, - Name = containerName - } - ); - } - - RequiredSystemRoles.Add( - _storage, - [ - (StorageBuiltInRole.GetBuiltInRoleName(StorageBuiltInRole.StorageBlobDataContributor),StorageBuiltInRole.StorageBlobDataContributor.ToString()), - (StorageBuiltInRole.GetBuiltInRoleName(StorageBuiltInRole.StorageTableDataContributor), StorageBuiltInRole.StorageTableDataContributor.ToString()) - ]); - - // Placeholders for now. - infrastructure.AddResource(new ProvisioningOutput($"storage_name", typeof(string)) { Value = _storage.Name }); - - Emitted = _storage; - return _storage; - } -} diff --git a/sdk/provisioning/Azure.Provisioning.CloudMachine/src/Core/RestCLient/RestClient.cs b/sdk/provisioning/Azure.Provisioning.CloudMachine/src/Core/RestCLient/RestClient.cs deleted file mode 100644 index b56813f79dfc3..0000000000000 --- a/sdk/provisioning/Azure.Provisioning.CloudMachine/src/Core/RestCLient/RestClient.cs +++ /dev/null @@ -1,82 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -using System; -using System.ClientModel.Primitives; -using System.ClientModel; - -namespace Azure -{ - public class RestClient - { - private static readonly RestClient _shared = new RestClient(); - - private readonly ClientPipeline _pipeline; - - public static RestClient Shared => _shared; - - public RestClient() : this(default(RestClientOptions)) - { - } - - public RestClient(PipelinePolicy auth) : this(CreateOptions(auth)) - { - } - - private static RestClientOptions CreateOptions(PipelinePolicy auth) - { - RestClientOptions options = new RestClientOptions(); - options.AddPolicy(auth, PipelinePosition.PerTry); - return options; - } - - private RestClient(RestClientOptions? options = default) - { - if (options == null) - options = new RestClientOptions(); - _pipeline = ClientPipeline.Create(options); - } - - public PipelineResponse Get(string uri, RequestOptions? options = default) - { - PipelineMessage message = Create("GET", new Uri(uri)); - return Send(message, options); - } - - public PipelineResponse Post(string uri, BinaryContent content, RequestOptions? options = default) - { - PipelineMessage message = Create("POST", new Uri(uri)); - message.Request.Content = content; - return Send(message, options); - } - - public PipelineResponse Put(string uri, BinaryContent content, RequestOptions? options = default) - { - PipelineMessage message = Create("PUT", new Uri(uri)); - message.Request.Content = content; - return Send(message, options); - } - - public PipelineResponse Patch(string uri, BinaryContent content, RequestOptions? options = default) - { - PipelineMessage message = Create("PATCH", new Uri(uri)); - message.Request.Content = content; - return Send(message, options); - } - - public PipelineResponse Send(PipelineMessage message, RequestOptions? options = default) - { - if (options != default) message.Apply(options); - _pipeline.Send(message); - return message.Response!; - } - - public PipelineMessage Create(string method, Uri uri) - { - PipelineMessage message = _pipeline.CreateMessage(); - message.Request.Method = method; - message.Request.Uri = uri; - return message; - } - } -} diff --git a/sdk/provisioning/Azure.Provisioning.CloudMachine/tests/Azure.Provisioning.CloudMachine.Tests.csproj b/sdk/provisioning/Azure.Provisioning.CloudMachine/tests/Azure.Provisioning.CloudMachine.Tests.csproj deleted file mode 100644 index 0a891c2b442f0..0000000000000 --- a/sdk/provisioning/Azure.Provisioning.CloudMachine/tests/Azure.Provisioning.CloudMachine.Tests.csproj +++ /dev/null @@ -1,15 +0,0 @@ - - - 12 - - - - - - - - - - - - diff --git a/sdk/provisioning/Azure.Provisioning.CloudMachine/tests/CloudMachineTests.cs b/sdk/provisioning/Azure.Provisioning.CloudMachine/tests/CloudMachineTests.cs deleted file mode 100644 index be895b522e5c3..0000000000000 --- a/sdk/provisioning/Azure.Provisioning.CloudMachine/tests/CloudMachineTests.cs +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -#nullable enable - -using System; -using System.IO; -using System.Linq; -using Azure.CloudMachine.AppService; -using Azure.CloudMachine.KeyVault; -using Azure.CloudMachine.OpenAI; -using NUnit.Framework; - -namespace Azure.CloudMachine.Tests; - -public class CloudMachineTests -{ - [Test] - public void GenerateBicep() - { - CloudMachineCommands.Execute(["-bicep"], (CloudMachineInfrastructure infrastructure) => - { - infrastructure.AddFeature(new KeyVaultFeature()); - infrastructure.AddFeature(new OpenAIModel("gpt-35-turbo", "0125")); - infrastructure.AddFeature(new OpenAIModel("text-embedding-ada-002", "2", AIModelKind.Embedding)); - infrastructure.AddFeature(new AppServiceFeature()); - }, exitProcessIfHandled: false); - - CloudMachineInfrastructure infra = new("cm0c420d2f21084cd"); - infra.AddFeature(new KeyVaultFeature()); - infra.AddFeature(new OpenAIModel("gpt-35-turbo", "0125")); - infra.AddFeature(new OpenAIModel("text-embedding-ada-002", "2", AIModelKind.Embedding)); - infra.AddFeature(new AppServiceFeature()); - - string actualBicep = infra!.Build().Compile().FirstOrDefault().Value; - string expectedBicep = File.ReadAllText(Path.Combine(TestContext.CurrentContext.TestDirectory, "Data", "GenerateBicep.bicep")).Replace("\r\n", Environment.NewLine); - Assert.AreEqual(expectedBicep, actualBicep); - } - - [Ignore("no recordings yet")] - [Test] - public void ListModels() - { - CloudMachineCommands.Execute(["-ai", "chat"], exitProcessIfHandled: false); - } -} diff --git a/sdk/provisioning/Azure.Provisioning.sln b/sdk/provisioning/Azure.Provisioning.sln index b75af01e6b88c..e8220a9e9bee9 100644 --- a/sdk/provisioning/Azure.Provisioning.sln +++ b/sdk/provisioning/Azure.Provisioning.sln @@ -106,12 +106,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Azure.Provisioning.WebPubSu EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Azure.Provisioning.WebPubSub.Tests", "Azure.Provisioning.WebPubSub\tests\Azure.Provisioning.WebPubSub.Tests.csproj", "{015670AB-881C-464F-8545-E686C9A92C4F}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Azure.Provisioning.CloudMachine", "Azure.Provisioning.CloudMachine", "{DB2FC0CB-7103-4D3C-A737-8C6AB61168F8}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Azure.Provisioning.CloudMachine", "Azure.Provisioning.CloudMachine\src\Azure.Provisioning.CloudMachine.csproj", "{F54CA64F-3BB8-49D5-AE3B-3408AE324632}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Azure.Provisioning.CloudMachine.Tests", "Azure.Provisioning.CloudMachine\tests\Azure.Provisioning.CloudMachine.Tests.csproj", "{94642992-7CC0-4649-B58F-8E5E3F134C8A}" -EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Azure.Provisioning.Deployment", "Azure.Provisioning.Deployment\src\Azure.Provisioning.Deployment.csproj", "{0BC64290-A91B-4EF2-BAF2-01AE91F81AED}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Azure.Provisioning.Deployment.Tests", "Azure.Provisioning.Deployment\tests\Azure.Provisioning.Deployment.Tests.csproj", "{0D2C464F-C246-4069-90B2-33D637BE18BF}"