Skip to content

Commit facb9c5

Browse files
StateMap
1 parent 6b2b72f commit facb9c5

File tree

5 files changed

+188
-9
lines changed

5 files changed

+188
-9
lines changed

experiments/Azure.Experiments/Azure.Experiments/ChildResourceConfig.cs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,15 @@
11
namespace Microsoft.Azure.Experiments
22
{
3+
public interface IChildResourceConfigVisitor<Result>
4+
{
5+
Result Visit<Info, ParentInfo>(ChildResourceConfig<Info, ParentInfo> config)
6+
where Info : class
7+
where ParentInfo : class;
8+
}
9+
310
public interface IChildResourceConfig
411
{
12+
Result Apply<Result>(IChildResourceConfigVisitor<Result> config);
513
}
614

715
public interface IChildResourceConfig<Info> : IChildResourceConfig
@@ -38,5 +46,8 @@ public ChildResourceConfig(
3846
Parent = parent;
3947
Name = name;
4048
}
49+
50+
public Result Apply<Result>(IChildResourceConfigVisitor<Result> config)
51+
=> config.Visit(this);
4152
}
4253
}
Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,13 @@
1-
namespace Microsoft.Azure.Experiments
1+
using Microsoft.Azure.Management.ResourceManager.Models;
2+
3+
namespace Microsoft.Azure.Experiments
24
{
35
public interface IState
46
{
5-
T Get<T>(IResourceConfig<T> resourceConfig);
6-
T Get<T>(IChildResourceConfig<T> childResourceConfig);
7+
ResourceGroup GetResourceGroup(string name);
8+
T Get<T>(ResourceConfig<T> resourceConfig)
9+
where T : class;
10+
T Get<T>(IChildResourceConfig<T> childResourceConfig)
11+
where T : class;
712
}
813
}

experiments/Azure.Experiments/Azure.Experiments/ResourceConfig.cs

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,15 @@
33

44
namespace Microsoft.Azure.Experiments
55
{
6-
public interface IResourceConfig
6+
public interface IResourceConfigVisitor<Result>
77
{
8+
Result Visit<T>(ResourceConfig<T> config)
9+
where T : class;
810
}
911

10-
public interface IResourceConfig<Info> : IResourceConfig
12+
public interface IResourceConfig
1113
{
14+
Result Apply<Result>(IResourceConfigVisitor<Result> config);
1215
}
1316

1417
public static class ResourceConfig
@@ -28,7 +31,7 @@ public static ResourceConfig<Info> CreateConfig<Info>(
2831
childResources.EmptyIfNull());
2932
}
3033

31-
public sealed class ResourceConfig<Info> : IResourceConfig<Info>
34+
public sealed class ResourceConfig<Info> : IResourceConfig
3235
where Info : class
3336
{
3437
public ResourcePolicy<Info> Policy { get; }
@@ -54,5 +57,8 @@ public ResourceConfig(
5457
Resources = resources;
5558
ChildResources = childResources;
5659
}
60+
61+
public Result Apply<Result>(IResourceConfigVisitor<Result> config)
62+
=> config.Visit(this);
5763
}
5864
}

experiments/Azure.Experiments/Azure.Experiments/ResourcePolicy.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,18 +19,18 @@ public static ResourcePolicy<Info> CreateResourcePolicy<Client, Info>(
1919
public sealed class ResourcePolicy<Info>
2020
where Info : class
2121
{
22-
public OperationsPolicy<IClient, Info> OperationsPolicy { get; }
22+
public OperationsPolicy<IClient, Info> Operations { get; }
2323

2424
public Func<Info, string> GetLocation { get; }
2525

2626
public Action<Info, string> SetLocation { get; }
2727

2828
public ResourcePolicy(
29-
OperationsPolicy<IClient, Info> operationsPolicy,
29+
OperationsPolicy<IClient, Info> operations,
3030
Func<Info, string> getLocation,
3131
Action<Info, string> setLocation)
3232
{
33-
OperationsPolicy = operationsPolicy;
33+
Operations = operations;
3434
GetLocation = getLocation;
3535
SetLocation = setLocation;
3636
}
Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
using Microsoft.Azure.Management.ResourceManager;
2+
using Microsoft.Azure.Management.ResourceManager.Models;
3+
using Microsoft.Rest.Azure;
4+
using System;
5+
using System.Collections.Concurrent;
6+
using System.Linq;
7+
using System.Net;
8+
using System.Threading.Tasks;
9+
10+
namespace Microsoft.Azure.Experiments
11+
{
12+
public sealed class StateMap : IState
13+
{
14+
public T Get<T>(ResourceConfig<T> resourceConfig)
15+
where T : class
16+
=> Resources.TryGetValue(resourceConfig, out var result) ? (T)result : null;
17+
18+
public T Get<T>(IChildResourceConfig<T> childResourceConfig)
19+
where T : class
20+
=> ChildResources.TryGetValue(childResourceConfig, out var result) ? (T)result : null;
21+
22+
public ResourceGroup GetResourceGroup(string name)
23+
=> ResourceGroups.TryGetValue(name, out var result) ? result : null;
24+
25+
public Task GetAsync<T>(IClient client, ResourceConfig<T> config)
26+
where T : class
27+
=> new GetAsyncVisitor(this, client).Visit(config);
28+
29+
static async Task<T> HandleNotFoundException<T>(Func<Task<T>> f)
30+
where T : class
31+
{
32+
try
33+
{
34+
return await f();
35+
}
36+
catch (CloudException e)
37+
when (e.Response.StatusCode == HttpStatusCode.NotFound)
38+
{
39+
return null;
40+
}
41+
}
42+
43+
sealed class GetAsyncVisitor :
44+
IResourceConfigVisitor<Task>,
45+
IChildResourceConfigVisitor<Task>
46+
{
47+
public GetAsyncVisitor(StateMap map, IClient client)
48+
{
49+
State = map;
50+
Client = client;
51+
}
52+
53+
public async Task GetResourceGroupAsync(string name)
54+
=> await ResourceGroupTasks.GetOrAdd(
55+
name,
56+
async _ =>
57+
{
58+
var result = await HandleNotFoundException(
59+
() => Client
60+
.GetClient<IResourceManagementClient>()
61+
.ResourceGroups
62+
.GetAsync(name));
63+
if (result != null)
64+
{
65+
State.ResourceGroups.GetOrAdd(name, result);
66+
}
67+
return result;
68+
});
69+
70+
public async Task<Info> GetResourceAsync<Info>(ResourceConfig<Info> config)
71+
where Info : class
72+
{
73+
var result = await ResourcesTasks.GetOrAdd(
74+
config,
75+
async _ =>
76+
{
77+
var i = await HandleNotFoundException(
78+
() => config.Policy.Operations.GetAsync(Client, config.Name));
79+
if (i != null)
80+
{
81+
State.Resources.GetOrAdd(config, i);
82+
}
83+
else
84+
{
85+
var resourceGroupTask = GetResourceGroupAsync(config.Name.ResourceGroupName);
86+
var resourceTasks = config.Resources.Select(c => c.Apply(this));
87+
var childResourceTasks = config.ChildResources.Select(c => c.Apply(this));
88+
var tasks = resourceTasks
89+
.Concat(childResourceTasks)
90+
.Concat(new[] { resourceGroupTask });
91+
// wait for all dependencies
92+
// (resource group, resources, and child resources).
93+
await Task.WhenAll(tasks);
94+
}
95+
return i;
96+
});
97+
return result as Info;
98+
}
99+
100+
public async Task Visit<Info>(ResourceConfig<Info> config)
101+
where Info : class
102+
=> await GetResourceAsync(config);
103+
104+
/// <summary>
105+
/// Get infromation about a child resource.
106+
/// </summary>
107+
/// <typeparam name="Info"></typeparam>
108+
/// <typeparam name="ParentInfo"></typeparam>
109+
/// <param name="config"></param>
110+
/// <returns></returns>
111+
public async Task Visit<Info, ParentInfo>(ChildResourceConfig<Info, ParentInfo> config)
112+
where Info : class
113+
where ParentInfo : class
114+
=> await ChildResourcesTasks.GetOrAdd(
115+
config,
116+
async _ =>
117+
{
118+
var parent = await GetResourceAsync(config.Parent);
119+
if (parent != null)
120+
{
121+
var result = config.Policy.Get(parent, config.Name);
122+
if (result != null)
123+
{
124+
State.ChildResources.GetOrAdd(config, result);
125+
}
126+
return result;
127+
}
128+
else
129+
{
130+
return null;
131+
}
132+
});
133+
134+
StateMap State { get; }
135+
136+
IClient Client { get; }
137+
138+
ConcurrentDictionary<string, Task<ResourceGroup>> ResourceGroupTasks { get; }
139+
= new ConcurrentDictionary<string, Task<ResourceGroup>>();
140+
141+
ConcurrentDictionary<IResourceConfig, Task<object>> ResourcesTasks { get; }
142+
= new ConcurrentDictionary<IResourceConfig, Task<object>>();
143+
144+
ConcurrentDictionary<IChildResourceConfig, Task<object>> ChildResourcesTasks { get; }
145+
= new ConcurrentDictionary<IChildResourceConfig, Task<object>>();
146+
}
147+
148+
ConcurrentDictionary<string, ResourceGroup> ResourceGroups { get; }
149+
= new ConcurrentDictionary<string, ResourceGroup>();
150+
151+
ConcurrentDictionary<IResourceConfig, object> Resources { get; }
152+
= new ConcurrentDictionary<IResourceConfig, object>();
153+
154+
ConcurrentDictionary<IChildResourceConfig, object> ChildResources { get; }
155+
= new ConcurrentDictionary<IChildResourceConfig, object>();
156+
}
157+
}

0 commit comments

Comments
 (0)