Skip to content

Commit d9275d8

Browse files
Added core types needed by CloudMachine incubation to Azure.Core.Experimental (Azure#37787)
* Added core types needed by CloudMachine incubation * removed config as it's harder than I thought and we don't need it now * PR feedback * PR feedback * removed config package * more PR feedback fixes * PR feedback * fixed test bug * updated API files * updates APIs
1 parent c12f465 commit d9275d8

File tree

7 files changed

+330
-1
lines changed

7 files changed

+330
-1
lines changed

sdk/core/Azure.Core.Experimental/api/Azure.Core.Experimental.net461.cs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,19 @@
11
namespace Azure
22
{
3+
public partial class CloudMachine
4+
{
5+
public CloudMachine(System.IO.Stream configurationContent) { }
6+
public CloudMachine(string configurationFile = "cloudconfig.json") { }
7+
public string DisplayName { get { throw null; } set { } }
8+
public string Id { get { throw null; } }
9+
public string Region { get { throw null; } }
10+
public string SubscriptionId { get { throw null; } }
11+
public static Azure.CloudMachine Create(string subscriptionId, string region) { throw null; }
12+
[System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)]
13+
public void Save(System.IO.Stream stream) { }
14+
[System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)]
15+
public void Save(string filepath) { }
16+
}
317
[System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)]
418
public readonly partial struct Value
519
{
@@ -118,6 +132,12 @@ public void AddOrUpdate(TKey key, TValue? val, int length) { }
118132
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { throw null; }
119133
public bool TryGet(TKey key, out TValue? value) { throw null; }
120134
}
135+
[System.AttributeUsageAttribute(System.AttributeTargets.Assembly | System.AttributeTargets.Class, Inherited=false, AllowMultiple=true)]
136+
public partial class ProvisionableTemplateAttribute : System.Attribute
137+
{
138+
public ProvisionableTemplateAttribute(string resourceName) { }
139+
public string ResourceName { get { throw null; } }
140+
}
121141
public abstract partial class SchemaValidator
122142
{
123143
protected SchemaValidator() { }

sdk/core/Azure.Core.Experimental/api/Azure.Core.Experimental.net6.0.cs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,19 @@
11
namespace Azure
22
{
3+
public partial class CloudMachine
4+
{
5+
public CloudMachine(System.IO.Stream configurationContent) { }
6+
public CloudMachine(string configurationFile = "cloudconfig.json") { }
7+
public string DisplayName { get { throw null; } set { } }
8+
public string Id { get { throw null; } }
9+
public string Region { get { throw null; } }
10+
public string SubscriptionId { get { throw null; } }
11+
public static Azure.CloudMachine Create(string subscriptionId, string region) { throw null; }
12+
[System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)]
13+
public void Save(System.IO.Stream stream) { }
14+
[System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)]
15+
public void Save(string filepath) { }
16+
}
317
[System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)]
418
public readonly partial struct Value
519
{
@@ -118,6 +132,12 @@ public void AddOrUpdate(TKey key, TValue? val, int length) { }
118132
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { throw null; }
119133
public bool TryGet(TKey key, out TValue? value) { throw null; }
120134
}
135+
[System.AttributeUsageAttribute(System.AttributeTargets.Assembly | System.AttributeTargets.Class, Inherited=false, AllowMultiple=true)]
136+
public partial class ProvisionableTemplateAttribute : System.Attribute
137+
{
138+
public ProvisionableTemplateAttribute(string resourceName) { }
139+
public string ResourceName { get { throw null; } }
140+
}
121141
public abstract partial class SchemaValidator
122142
{
123143
protected SchemaValidator() { }

sdk/core/Azure.Core.Experimental/api/Azure.Core.Experimental.netstandard2.0.cs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,19 @@
11
namespace Azure
22
{
3+
public partial class CloudMachine
4+
{
5+
public CloudMachine(System.IO.Stream configurationContent) { }
6+
public CloudMachine(string configurationFile = "cloudconfig.json") { }
7+
public string DisplayName { get { throw null; } set { } }
8+
public string Id { get { throw null; } }
9+
public string Region { get { throw null; } }
10+
public string SubscriptionId { get { throw null; } }
11+
public static Azure.CloudMachine Create(string subscriptionId, string region) { throw null; }
12+
[System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)]
13+
public void Save(System.IO.Stream stream) { }
14+
[System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)]
15+
public void Save(string filepath) { }
16+
}
317
[System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)]
418
public readonly partial struct Value
519
{
@@ -118,6 +132,12 @@ public void AddOrUpdate(TKey key, TValue? val, int length) { }
118132
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { throw null; }
119133
public bool TryGet(TKey key, out TValue? value) { throw null; }
120134
}
135+
[System.AttributeUsageAttribute(System.AttributeTargets.Assembly | System.AttributeTargets.Class, Inherited=false, AllowMultiple=true)]
136+
public partial class ProvisionableTemplateAttribute : System.Attribute
137+
{
138+
public ProvisionableTemplateAttribute(string resourceName) { }
139+
public string ResourceName { get { throw null; } }
140+
}
121141
public abstract partial class SchemaValidator
122142
{
123143
protected SchemaValidator() { }

sdk/core/Azure.Core.Experimental/src/Azure.Core.Experimental.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<Project Sdk="Microsoft.NET.Sdk">
1+
<Project Sdk="Microsoft.NET.Sdk">
22
<PropertyGroup>
33
<Description>Experimental types that might eventually move to Azure.Core</Description>
44
<AssemblyTitle>Microsoft Azure Client Pipeline Experimental Extensions</AssemblyTitle>
Lines changed: 197 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,197 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License.
3+
4+
using System;
5+
using System.ComponentModel;
6+
using System.IO;
7+
using System.Text.Json;
8+
using Azure.Core;
9+
10+
namespace Azure
11+
{
12+
/// <summary>
13+
/// Stores configuration of a CloudMachine.
14+
/// </summary>
15+
public class CloudMachine
16+
{
17+
/// <summary>
18+
/// Unique identifier of a CloudMachine. It's the name of the resource group of all the CloudMachine resources.
19+
/// </summary>
20+
public string Id { get; }
21+
22+
/// <summary>
23+
/// Friendly name of CloudMachine. It's stored as a Tag of all Azure resources associated with the machine.
24+
/// </summary>
25+
public string DisplayName { get; set; }
26+
27+
/// <summary>
28+
/// Azure subscription ID.
29+
/// </summary>
30+
public string SubscriptionId { get; }
31+
32+
/// <summary>
33+
/// Azure region, e.g. westus2
34+
/// </summary>
35+
public string Region { get; }
36+
37+
private int Version { get; }
38+
39+
/// <summary>
40+
/// Creates a new CloudMachine
41+
/// </summary>
42+
/// <param name="subscriptionId"></param>
43+
/// <param name="region"></param>
44+
/// <returns></returns>
45+
/// <remarks>DisplayName is initialized to id. It can be changed by setting the DisplayName property.</remarks>
46+
public static CloudMachine Create(string subscriptionId, string region)
47+
{
48+
if (string.IsNullOrEmpty(subscriptionId)) throw new ArgumentNullException(nameof(subscriptionId));
49+
if (string.IsNullOrEmpty(region)) throw new ArgumentNullException(nameof(region));
50+
51+
var id = GenerateCloudMachineId();
52+
var defaultDisplayName = $"{id}@{DateTime.UtcNow:d}";
53+
return new CloudMachine(id, defaultDisplayName, subscriptionId, region, 1);
54+
}
55+
56+
/// <summary>
57+
/// Loads CloudMachine settings from configurationFile
58+
/// </summary>
59+
/// <param name="configurationFile"></param>
60+
/// <exception cref="InvalidCloudMachineConfigurationException"></exception>
61+
public CloudMachine(string configurationFile = "cloudconfig.json")
62+
{
63+
try
64+
{
65+
byte[] configurationContent = File.ReadAllBytes(configurationFile);
66+
var document = JsonDocument.Parse(configurationContent);
67+
JsonElement json = document.RootElement.GetProperty("CloudMachine");
68+
69+
Id = ReadString(json, "id", configurationFile);
70+
SubscriptionId = ReadString(json, "subscriptionId", configurationFile);
71+
Region = ReadString(json, "region", configurationFile);
72+
DisplayName = ReadString(json, "name", configurationFile);
73+
Version = ReadInt32(json, "version", configurationFile);
74+
}
75+
catch (Exception e) when (e is not InvalidCloudMachineConfigurationException)
76+
{
77+
throw new InvalidCloudMachineConfigurationException(configurationFile, setting: null, e);
78+
}
79+
}
80+
81+
/// <summary>
82+
/// Loads CloudMachine settings from stream.
83+
/// </summary>
84+
/// <param name="configurationContent"></param>
85+
/// <exception cref="InvalidCloudMachineConfigurationException"></exception>
86+
public CloudMachine(Stream configurationContent)
87+
{
88+
try
89+
{
90+
var document = JsonDocument.Parse(configurationContent);
91+
JsonElement json = document.RootElement.GetProperty("CloudMachine");
92+
93+
Id = ReadString(json, "id", nameof(configurationContent));
94+
SubscriptionId = ReadString(json, "subscriptionId", nameof(configurationContent));
95+
Region = ReadString(json, "region", nameof(configurationContent));
96+
DisplayName = ReadString(json, "name", nameof(configurationContent));
97+
Version = ReadInt32(json, "version", nameof(configurationContent));
98+
}
99+
catch (Exception e) when (e is not InvalidCloudMachineConfigurationException)
100+
{
101+
throw new InvalidCloudMachineConfigurationException(nameof(configurationContent), setting: null, e);
102+
}
103+
}
104+
105+
private CloudMachine(string id, string displayName, string subscriptionId, string region, int version)
106+
{
107+
Argument.AssertNotNullOrEmpty(id, nameof(id));
108+
Argument.AssertNotNullOrEmpty(displayName, nameof(displayName));
109+
Argument.AssertNotNullOrEmpty(subscriptionId, nameof(subscriptionId));
110+
Argument.AssertNotNullOrEmpty(region, nameof(region));
111+
Argument.AssertInRange(version, 0, int.MaxValue, nameof(version));
112+
113+
Id = id;
114+
DisplayName = displayName;
115+
SubscriptionId = subscriptionId;
116+
Region = region;
117+
Version = version;
118+
}
119+
120+
/// <summary>
121+
/// Save CloudMachine configuration to a stream.
122+
/// </summary>
123+
/// <param name="stream"></param>
124+
[EditorBrowsable(EditorBrowsableState.Never)]
125+
public void Save(Stream stream)
126+
{
127+
var options = new JsonWriterOptions() { Indented = true };
128+
using var json = new Utf8JsonWriter(stream, options);
129+
json.WriteStartObject();
130+
json.WriteStartObject("CloudMachine");
131+
json.WriteString("id", Id);
132+
json.WriteString("name", DisplayName);
133+
json.WriteString("subscriptionId", SubscriptionId);
134+
json.WriteString("region", Region);
135+
json.WriteNumber("version", Version);
136+
json.WriteEndObject();
137+
json.WriteEndObject();
138+
json.Flush();
139+
}
140+
141+
/// <summary>
142+
/// Save CloudMachine configuration to a file.
143+
/// </summary>
144+
/// <param name="filepath"></param>
145+
[EditorBrowsable(EditorBrowsableState.Never)]
146+
public void Save(string filepath)
147+
{
148+
using var stream = File.OpenWrite(filepath);
149+
Save(stream);
150+
}
151+
152+
private static string ReadString(JsonElement json, string key, string configurationStoreDisplayName)
153+
{
154+
try {
155+
var value = json.GetProperty(key).GetString()!;
156+
if (string.IsNullOrEmpty(value)) throw new ArgumentNullException(key);
157+
return value;
158+
}
159+
catch (Exception e) {
160+
throw new InvalidCloudMachineConfigurationException(configurationStoreDisplayName, key, e);
161+
}
162+
}
163+
private static int ReadInt32(JsonElement json, string key, string configurationStoreDisplayName)
164+
{
165+
try {
166+
var value = json.GetProperty(key).GetInt32();
167+
return value;
168+
}
169+
catch (Exception e) {
170+
throw new InvalidCloudMachineConfigurationException(configurationStoreDisplayName, key, e);
171+
}
172+
}
173+
174+
private static string GenerateCloudMachineId()
175+
{
176+
var guid = Guid.NewGuid();
177+
var guidString = guid.ToString("N");
178+
var cnId = "cm" + guidString.Substring(0, 15); // we can increase it to 20, but the template name cannot be that long
179+
return cnId;
180+
}
181+
182+
internal class InvalidCloudMachineConfigurationException : InvalidOperationException
183+
{
184+
public InvalidCloudMachineConfigurationException(string configurationStoreDisplayName, string? setting, Exception innerException) :
185+
base(CreateMessage(configurationStoreDisplayName, setting), innerException)
186+
{ }
187+
188+
public static string CreateMessage(string configurationStoreDisplayName, string? setting)
189+
{
190+
if (setting != null)
191+
return $"ERROR: Configuration setting {setting} not found in {configurationStoreDisplayName} or invalid format.";
192+
else
193+
return $"ERROR: Configuration store {configurationStoreDisplayName} not found or invalid format.";
194+
}
195+
}
196+
}
197+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License.
3+
4+
using System;
5+
6+
namespace Azure.Core
7+
{
8+
/// <summary>
9+
/// Attribute used to describe a deployment template.
10+
/// </summary>
11+
[AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Class, Inherited = false, AllowMultiple = true)]
12+
public class ProvisionableTemplateAttribute : Attribute
13+
{
14+
/// <summary>
15+
/// Deployment teplate stored in resources.
16+
/// </summary>
17+
/// <param name="resourceName"></param>
18+
public ProvisionableTemplateAttribute(string resourceName)
19+
=> ResourceName = resourceName;
20+
21+
/// <summary>
22+
/// Name of assembly resource file containing a deployment template.
23+
/// </summary>
24+
public string ResourceName { get; }
25+
}
26+
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License.
3+
4+
using System;
5+
using System.IO;
6+
using NUnit.Framework;
7+
8+
namespace Azure
9+
{
10+
public class CloudMachineTests
11+
{
12+
[Test]
13+
public void SerializationRoundTrip()
14+
{
15+
var cm = CloudMachine.Create(Guid.NewGuid().ToString(), "westus2");
16+
using var stream = new MemoryStream();
17+
cm.Save(stream);
18+
stream.Position = 0;
19+
20+
var deserialized = new CloudMachine(stream);
21+
Assert.AreEqual(cm.Id, deserialized.Id);
22+
Assert.AreEqual(cm.DisplayName, deserialized.DisplayName);
23+
Assert.AreEqual(cm.Region, deserialized.Region);
24+
Assert.AreEqual(cm.SubscriptionId, deserialized.SubscriptionId);
25+
}
26+
27+
[Test]
28+
public void DefaultCtor()
29+
{
30+
try
31+
{
32+
var cm = CloudMachine.Create(Guid.NewGuid().ToString(), "westus2");
33+
cm.Save("cloudconfig.json");
34+
var deserialized = new CloudMachine();
35+
Assert.AreEqual(cm.Id, deserialized.Id);
36+
Assert.AreEqual(cm.DisplayName, deserialized.DisplayName);
37+
Assert.AreEqual(cm.Region, deserialized.Region);
38+
Assert.AreEqual(cm.SubscriptionId, deserialized.SubscriptionId);
39+
}
40+
finally
41+
{
42+
File.Delete("cloudconfig.json");
43+
}
44+
}
45+
}
46+
}

0 commit comments

Comments
 (0)