Skip to content
6 changes: 3 additions & 3 deletions application/Gamezure.VmPoolManager/AddPool.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,13 @@ namespace Gamezure.VmPoolManager
public class AddPool
{
private readonly PoolRepository poolRepository;
private readonly VmRepository vmRepository;
private readonly PoolManager poolManager;

public AddPool(PoolRepository poolRepository, PoolManager poolManager)
public AddPool(PoolRepository poolRepository, VmRepository vmRepository, PoolManager poolManager)
{
this.poolRepository = poolRepository;
this.vmRepository = vmRepository;
this.poolManager = poolManager;
}

Expand Down Expand Up @@ -49,8 +51,6 @@ public async Task<IActionResult> RunAsync(
return new ObjectResult(e) {StatusCode = (int)e.StatusCode};
}

pool.InitializeVmList();

var tags = new Dictionary<string, string>
{
{ "gamezure-pool-id", pool.Id }
Expand Down
18 changes: 12 additions & 6 deletions application/Gamezure.VmPoolManager/CreateVmOrchestrator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using Gamezure.VmPoolManager.Exceptions;
using Gamezure.VmPoolManager.Repository;
using Microsoft.Azure.Cosmos;
using Microsoft.Azure.WebJobs;
Expand All @@ -25,24 +26,29 @@ public CreateVmOrchestrator(PoolRepository poolRepository, PoolManager poolManag
}

[FunctionName("CreateVmOrchestrator")]
public async Task<List<string>> RunOrchestrator(
[OrchestrationTrigger] IDurableOrchestrationContext context)
public async Task<List<string>> RunOrchestrator([OrchestrationTrigger] IDurableOrchestrationContext context)
{
var outputs = new List<string>();

try
{
var poolId = context.GetInput<string>();
Pool pool = await context.CallActivityAsync<Pool>("CreateVmOrchestrator_GetPool", poolId);
if (pool is null)
{
throw new PoolNotFoundException(poolId);
}
outputs.Add(JsonConvert.SerializeObject(pool));

var tags = new Dictionary<string, string>
{
{ "gamezure-pool-id", pool.Id }
};

var tasks = new List<Task>();
foreach (var vm in pool.Vms)
context.NewGuid();
var vms = this.poolManager.InitializeVmList(pool, () => context.NewGuid().ToString());
var tasks = new List<Task<Vm>>();
foreach (var vm in vms)
{
var vmResultTask = VmResultTask(context, vm, pool, tags, outputs);
tasks.Add(vmResultTask);
Expand All @@ -60,7 +66,7 @@ public async Task<List<string>> RunOrchestrator(

private async Task<Vm> VmResultTask(IDurableOrchestrationContext context, Vm vm, Pool pool, IDictionary<string, string> tags, List<string> outputs)
{
var vmCreateParams = new VmCreateParams(vm.Name, vm.PoolId, "gamezure", vm.Password, pool.ResourceGroupName, pool.Location, tags, pool.Net);
var vmCreateParams = new VmCreateParams(vm.Id, vm.PoolId, "gamezure", vm.UserPass, pool.ResourceGroupName, pool.Location, tags, pool.Net);
Vm vmResult = await context.CallActivityAsync<Vm>("CreateVmOrchestrator_CreateWindowsVm", vmCreateParams);
outputs.Add($"Finished creation of {vmResult}");
return vmResult;
Expand Down Expand Up @@ -111,7 +117,7 @@ public async Task<Pool> GetPool([ActivityTrigger] string poolId, ILogger log)
[FunctionName("CreateVmOrchestrator_CreateWindowsVm")]
public async Task<Vm> CreateWindowsVm([ActivityTrigger] VmCreateParams vmCreateParams, ILogger log)
{
log.LogInformation($"Creating Virtual Machine");
log.LogInformation("Creating Virtual Machine");

return await poolManager.CreateVm(vmCreateParams);
}
Expand Down
10 changes: 5 additions & 5 deletions application/Gamezure.VmPoolManager/EnsureVMs.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ public EnsureVMs(PoolRepository poolRepository, PoolManager poolManager)
}

[FunctionName("EnsureVMs")]
[Obsolete("Use durable function instead!")]
public async Task<IActionResult> RunAsync(
[HttpTrigger(AuthorizationLevel.Function, "put", Route = null)]
HttpRequest req, ILogger log)
Expand Down Expand Up @@ -74,19 +75,18 @@ public async Task<IActionResult> RunAsync(

private List<Vm> CreateVirtualMachines(Pool pool, ILogger log = null)
{
int vmCount = pool.Vms.Count;
var vms = new List<Vm>(vmCount);
var tasks = new List<Task<Vm>>(vmCount);
var vms = this.poolManager.InitializeVmList(pool, () => Guid.NewGuid().ToString());
var tasks = new List<Task<Vm>>(vms.Count);

var tags = new Dictionary<string, string>
{
{ "gamezure-pool-id", pool.Id }
};

foreach (var vm in pool.Vms)
foreach (var vm in vms)
{
var vmCreateParams = new VmCreateParams(
vm.Name,
vm.Id,
vm.PoolId,
"gamezure-user",
Guid.NewGuid().ToString(), // TODO: Move credentials to KeyVault
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
namespace Gamezure.VmPoolManager.Exceptions
{
public class PoolNotFoundException : System.Exception
{
public string PoolId { get; }

public PoolNotFoundException(string poolId)
{
this.PoolId = poolId;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
<PackageReference Include="Azure.ResourceManager.Compute" Version="1.0.0-preview.2" />
<PackageReference Include="Azure.ResourceManager.Network" Version="1.0.0-preview.2" />
<PackageReference Include="Azure.ResourceManager.Resources" Version="1.0.0-preview.2" />
<PackageReference Include="IEvangelist.Azure.CosmosRepository" Version="2.5.0" />
<PackageReference Include="Microsoft.Azure.Cosmos" Version="3.21.0" />
<PackageReference Include="Microsoft.Azure.Functions.Extensions" Version="1.1.0" />
<PackageReference Include="Microsoft.Azure.Management.Fluent" Version="1.38.0" />
Expand Down
22 changes: 1 addition & 21 deletions application/Gamezure.VmPoolManager/Pool.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
using System;
using System.Collections.Generic;
using System.Text.Json;
using System.Text.Json;
using System.Text.Json.Serialization;

namespace Gamezure.VmPoolManager
Expand All @@ -12,26 +10,8 @@ public class Pool
public string ResourceGroupName { get; set; }
public string Location { get; set; }
public int DesiredVmCount { get; set; }

public Networking Net { get; set; }

public List<Vm> Vms { get; private set; } = new List<Vm>();

public void InitializeVmList()
{

for (int i = 0; i < this.DesiredVmCount; i++)
{
var vm = new Vm
{
Name = $"{this.Id}-vm-{i}",
PoolId = this.Id,
Password = Guid.NewGuid().ToString()
};
this.Vms.Add(vm);
}
}

public override string ToString()
{
return JsonSerializer.Serialize(this);
Expand Down
83 changes: 45 additions & 38 deletions application/Gamezure.VmPoolManager/PoolManager.cs
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Azure;
using Azure.Core;
using Azure.Identity;
using Azure.ResourceManager.Compute.Models;
using Azure.ResourceManager.Resources;
using Gamezure.VmPoolManager.Repository;
using Microsoft.Azure.Management.Compute.Fluent;
using Microsoft.Azure.Management.Fluent;
using Microsoft.Azure.Management.Network.Fluent;
Expand All @@ -17,25 +15,13 @@ public class PoolManager
{
private const string SUBNET_NAME_PUBLIC = "public";
private const string SUBNET_NAME_GAME = "game";
private readonly string subscriptionId;
private readonly TokenCredential credential;
private readonly IAzure azure;
private readonly ResourcesManagementClient resourceClient;
private readonly ResourceGroupsOperations resourceGroupsClient;
private readonly VmRepository vmRepository;

public PoolManager(string subscriptionId, TokenCredential credential, IAzure azure)
public PoolManager(IAzure azure, VmRepository vmRepository)
{
this.subscriptionId = subscriptionId;
this.credential = credential;
this.azure = azure;

resourceClient = new ResourcesManagementClient(this.subscriptionId, this.credential);

resourceGroupsClient = resourceClient.ResourceGroups;
}

public PoolManager(string subscriptionId, IAzure azure) : this(subscriptionId, new DefaultAzureCredential(), azure)
{
this.vmRepository = vmRepository;
}

public async Task<Vm> CreateVm(VmCreateParams vmCreateParams)
Expand All @@ -51,50 +37,71 @@ public async Task<Vm> CreateVm(VmCreateParams vmCreateParams)
tasks.Add(taskNsgGame);
Task.WaitAll(tasks.ToArray());

var taskPip = await this.FluentCreatePublicIp(vmCreateParams);
var publicIp = await this.FluentCreatePublicIp(vmCreateParams);

var vmTasks = new List<Task>(2);
var taskPublicNic = this.FluentCreatePublicNetworkConnection(
var taskNicPublic = this.FluentCreatePublicNetworkConnection(
vmCreateParams.Name,
taskVirtualNetwork.Result,
taskNsgPublic.Result,
taskPip,
publicIp,
vmCreateParams.Tags);

var taskGameNic = this.FluentCreateGameNetworkConnection(
var taskNicGame = this.FluentCreateGameNetworkConnection(
vmCreateParams.Name,
taskVirtualNetwork.Result,
taskNsgGame.Result,
vmCreateParams.Tags);

vmTasks.Add(taskPublicNic);
vmTasks.Add(taskGameNic);
vmTasks.Add(taskNicPublic);
vmTasks.Add(taskNicGame);

Task.WaitAll(vmTasks.ToArray());

var vm = await FluentCreateWindowsVm(vmCreateParams, taskPublicNic.Result, taskGameNic.Result);
INetworkInterface nicPublic = taskNicPublic.Result;
var nicGame = taskNicGame.Result;

var vm = await FluentCreateWindowsVm(vmCreateParams, nicPublic, nicGame);
var vmResult = new Vm
{
Name = vm.Name,
Id = vm.Name,
PoolId = vmCreateParams.PoolId,
PublicIp = vm.GetPrimaryPublicIPAddress().IPAddress,
ResourceId = vm.Id
ResourceId = vm.Id,
PublicIp = vm.GetPrimaryPublicIPAddress().IPAddress, // same as publicIp.IPAddress
PublicIpId = publicIp.Id,
PublicNicId = nicPublic.Id,
GameNicId = nicGame.Id,
UserPass = vmCreateParams.UserPassword
};

await this.vmRepository.Save(vmResult);

return vmResult;
}

public async Task<bool> GuardResourceGroup(string name)
public List<Vm> InitializeVmList(Pool pool, Func<string> passwordFunction)
{
bool exists = false;
Response rgExists = await resourceGroupsClient.CheckExistenceAsync(name);

if (rgExists.Status == 204) // 204 - No Content
var vms = new List<Vm>(pool.DesiredVmCount);
for (int i = 0; i < pool.DesiredVmCount; i++)
{
exists = true;
var vm = new Vm
{
Id = $"{pool.Id}-vm-{i}",
PoolId = pool.Id,
ResourceGroupName = pool.ResourceGroupName,
Location = pool.Location,
UserPass = passwordFunction(),
};
vm.NextProvisioningState();
vms.Add(vm);
}

return exists;
return vms;
}

public async Task<bool> GuardResourceGroup(string poolResourceGroupName)
{
return await this.azure.ResourceGroups.ContainAsync(poolResourceGroupName);
}

public INetwork FluentCreateVnet(
Expand Down
14 changes: 3 additions & 11 deletions application/Gamezure.VmPoolManager/Repository/PoolRepository.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,10 @@ public class PoolRepository

private readonly Container container;

public PoolRepository(string connectionString)
public PoolRepository(CosmosClient client)
{
var clientOptions = new CosmosClientOptions
{
SerializerOptions = new CosmosSerializationOptions
{
PropertyNamingPolicy = CosmosPropertyNamingPolicy.CamelCase
}
};
this.client = new CosmosClient(connectionString, clientOptions);

this.container = client.GetContainer("gamezure-db", "vmpool");
this.client = client;
this.container = client.GetContainer("gamezure-db", "pool");
}

public Task<ItemResponse<Pool>> Save(Pool pool)
Expand Down
Loading