Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] Draft of C# Track 2 conversion #324

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 5 additions & 16 deletions CSharp/GettingStarted/01_HelloWorld/HelloWorld.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -2,25 +2,14 @@

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net462</TargetFramework>
<RootNamespace>Microsoft.Azure.Batch.Samples.HelloWorld</RootNamespace>
<TargetFramework>net8.0</TargetFramework>
<RootNamespace>Azure.Compute.Batch.Samples.HelloWorld</RootNamespace>
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="..\..\Common\Microsoft.Azure.Batch.Samples.Common.csproj" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Azure.Batch" Version="14.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="2.1.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="2.1.0" />
<PackageReference Include="WindowsAzure.Storage" Version="9.3.3" />
</ItemGroup>

<ItemGroup>
<None Update="settings.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<PackageReference Include="Azure.Compute.Batch" Version="1.0.0-alpha.20240417.3" />
<PackageReference Include="Azure.Identity" Version="1.11.1" />
<PackageReference Include="Azure.ResourceManager.Batch" Version="1.4.0" />
</ItemGroup>

</Project>
13 changes: 5 additions & 8 deletions CSharp/GettingStarted/01_HelloWorld/HelloWorld.sln
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.26430.12
# Visual Studio Version 17
VisualStudioVersion = 17.9.34728.123
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HelloWorld", "HelloWorld.csproj", "{FE62D509-B9A9-4592-81BB-779306C22F95}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Azure.Batch.Samples.Common", "..\..\Common\Microsoft.Azure.Batch.Samples.Common.csproj", "{612B170A-1697-4C40-BD57-26A6C8AC6534}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand All @@ -17,12 +15,11 @@ Global
{FE62D509-B9A9-4592-81BB-779306C22F95}.Debug|Any CPU.Build.0 = Debug|Any CPU
{FE62D509-B9A9-4592-81BB-779306C22F95}.Release|Any CPU.ActiveCfg = Release|Any CPU
{FE62D509-B9A9-4592-81BB-779306C22F95}.Release|Any CPU.Build.0 = Release|Any CPU
{612B170A-1697-4C40-BD57-26A6C8AC6534}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{612B170A-1697-4C40-BD57-26A6C8AC6534}.Debug|Any CPU.Build.0 = Debug|Any CPU
{612B170A-1697-4C40-BD57-26A6C8AC6534}.Release|Any CPU.ActiveCfg = Release|Any CPU
{612B170A-1697-4C40-BD57-26A6C8AC6534}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {33B77A65-5D1C-46B7-918E-D922919D6884}
EndGlobalSection
EndGlobal
150 changes: 150 additions & 0 deletions CSharp/GettingStarted/01_HelloWorld/HelloWorldSample.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
// Copyright (c) Microsoft Corporation. All rights reserved.

using Azure.Core;
using Azure.Identity;
using Azure.ResourceManager;
using Azure.ResourceManager.Batch;
using Azure.ResourceManager.Batch.Models;
using System;
using System.IO;
using System.Linq;
using System.Threading.Tasks;

namespace Azure.Compute.Batch.Samples.HelloWorld
{
public class HelloWorldSample
{
private ArmClient _armClient;
private BatchClient _batchClient;

/// <summary>
/// Creates a pool with a configurable number of nodes, then submits tasks which print a 'Hello world' message.
/// The resulting stdout.txt or stderr.txt (depending on each task's exit code) is then printed to the console.
///
/// After running, the job will be terminated and the pool will be deleted.
/// </summary>
/// <param name="batchAccountResourceId">The ARM resource ID of the Batch account.</param>
/// <returns>A task which completes when the sample has finished running.</returns>
public async Task Run(string batchAccountResourceId)
{
var batchAccountIdentifier = ResourceIdentifier.Parse(batchAccountResourceId);

var credential = new DefaultAzureCredential();
_armClient = new ArmClient(credential);

BatchAccountResource batchAccount = await _armClient.GetBatchAccountResource(batchAccountIdentifier).GetAsync();

_batchClient = new BatchClient(new Uri($"https://{batchAccount.Data.AccountEndpoint}"), credential);

var poolName = GenerateUniqueName("HelloWorldPool");
var imageReference = new BatchImageReference()
{
Publisher = "canonical",
Offer = "0001-com-ubuntu-server-jammy",
Sku = "22_04-lts",
Version = "latest"
};
string nodeAgentSku = "batch.node.ubuntu 22.04";

BatchAccountPoolResource pool = (await batchAccount.GetBatchAccountPools().CreateOrUpdateAsync(WaitUntil.Completed, poolName, new BatchAccountPoolData()
{
VmSize = "Standard_DS1_v2",
DeploymentConfiguration = new BatchDeploymentConfiguration()
{
VmConfiguration = new BatchVmConfiguration(imageReference, nodeAgentSku)
},
ScaleSettings = new BatchAccountPoolScaleSettings()
{
FixedScale = new BatchAccountFixedScaleSettings()
{
TargetDedicatedNodes = 1
}
}
})).Value;

string jobId = GenerateUniqueName("HelloWorldJob");

try
{
await _batchClient.CreateJobAsync(new BatchJobCreateContent(jobId, new BatchPoolInfo() { PoolId = poolName }));

for (int i = 0; i < 5; i++)
{
string taskId = $"task-{i}";
Console.WriteLine("Submitting {0}", taskId);
await _batchClient.CreateTaskAsync(jobId, new BatchTaskCreateContent(taskId, $"echo Hello world from {taskId}"));
}

Console.WriteLine("Waiting for all tasks to complete on job: {0} ...", jobId);
await waitForTasksToComplete(jobId);

var completedTasks = _batchClient.GetTasksAsync(jobId, filter: "state eq 'completed'");
await foreach (BatchTask t in completedTasks)
{
var outputFileName = t.ExecutionInfo.ExitCode == 0 ? "stdout.txt" : "stderr.txt";

Console.WriteLine("Task {0} exited with code {1}. Output ({2}):",
t.Id, t.ExecutionInfo.ExitCode, outputFileName);

BinaryData fileContents = await _batchClient.GetTaskFileAsync(jobId, t.Id, outputFileName);
using (var reader = new StreamReader(fileContents.ToStream()))
{
Console.WriteLine(await reader.ReadLineAsync());
}
}
}
finally
{
Console.WriteLine("Terminating job {0} and deleting pool {1}", jobId, poolName);
await Task.WhenAll([
_batchClient.TerminateJobAsync(jobId),
pool.DeleteAsync(WaitUntil.Completed)]);
}
}

/// <summary>
/// Poll all the tasks in the given job and wait for them to reach the completed state.
/// </summary>
/// <param name="jobId">The ID of the job to poll</param>
/// <returns>A task that will complete when all Batch tasks have completed.</returns>
/// <exception cref="TimeoutException">Thrown if all tasks haven't reached the completed state after a certain period of time</exception>
private async Task waitForTasksToComplete(String jobId)
{
// Note that this timeout should take into account the time it takes for the pool to scale up
var timeoutAfter = DateTime.Now.AddMinutes(10);
while (DateTime.Now < timeoutAfter)
{
var allComplete = true;
var tasks = _batchClient.GetTasksAsync(jobId, select: ["id", "state"]);
await foreach (BatchTask task in tasks)
{
if (task.State != BatchTaskState.Completed)
{
allComplete = false;
break;
}
}

if (allComplete)
{
return;
}

await Task.Delay(10000);
}

throw new TimeoutException("Task(s) did not complete within the specified time");
}

/// <summary>
/// Generate a unique name with the given prefix using the current user name and a timestamp.
/// </summary>
/// <param name="prefix">The name's prefix.</param>
/// <returns>The generated name.</returns>
private static string GenerateUniqueName(string prefix)
{
string currentUser = new string(Environment.UserName.Where(char.IsLetterOrDigit).ToArray());
return string.Format("{0}-{1}-{2}", prefix, currentUser, DateTime.Now.ToString("yyyyMMdd-HHmmss"));
}
}
}
171 changes: 9 additions & 162 deletions CSharp/GettingStarted/01_HelloWorld/Program.cs
Original file line number Diff line number Diff line change
@@ -1,176 +1,23 @@
//Copyright (c) Microsoft Corporation
// Copyright (c) Microsoft Corporation. All rights reserved.

namespace Microsoft.Azure.Batch.Samples.HelloWorld
{
using System;
using System.IO;
using System.Collections.Generic;
using System.Threading.Tasks;
using Auth;
using Batch.Common;
using Common;
using Microsoft.Extensions.Configuration;
using System;
using System.Threading.Tasks;

namespace Azure.Compute.Batch.Samples.HelloWorld
{
/// <summary>
/// The main program of the HelloWorld sample
/// </summary>
public static class Program
{
public static void Main(string[] args)
{
try
{
AccountSettings accountSettings = SampleHelpers.LoadAccountSettings();
Settings helloWorldSettings = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("settings.json")
.Build()
.Get<Settings>();

HelloWorldAsync(accountSettings, helloWorldSettings).Wait();
}
catch (AggregateException aggregateException)
{
// Go through all exceptions and dump useful information
foreach (Exception exception in aggregateException.InnerExceptions)
{
Console.WriteLine(exception.ToString());
Console.WriteLine();
}
private static string batchAccountResourceId = "your-batch-account-resource-id";

throw;
}
public async static Task Main(string[] args)
{
await new HelloWorldSample().Run(batchAccountResourceId);

Console.WriteLine("Press return to exit...");
Console.ReadLine();
}

/// <summary>
/// Submits a job to the Azure Batch service, and waits for it to complete
/// </summary>
private static async Task HelloWorldAsync(
AccountSettings accountSettings,
Settings helloWorldConfigurationSettings)
{
Console.WriteLine("Running with the following settings: ");
Console.WriteLine("-------------------------------------");
Console.WriteLine(helloWorldConfigurationSettings.ToString());
Console.WriteLine(accountSettings.ToString());

// Set up the Batch Service credentials used to authenticate with the Batch Service.
BatchSharedKeyCredentials credentials = new BatchSharedKeyCredentials(
accountSettings.BatchServiceUrl,
accountSettings.BatchAccountName,
accountSettings.BatchAccountKey);

// Get an instance of the BatchClient for a given Azure Batch account.
using (BatchClient batchClient = BatchClient.Open(credentials))
{
// add a retry policy. The built-in policies are No Retry (default), Linear Retry, and Exponential Retry
batchClient.CustomBehaviors.Add(RetryPolicyProvider.ExponentialRetryProvider(TimeSpan.FromSeconds(5), 3));

string jobId = GettingStartedCommon.CreateJobId("HelloWorldJob");

try
{
// Submit the job
await SubmitJobAsync(batchClient, helloWorldConfigurationSettings, jobId);

// Wait for the job to complete
await WaitForJobAndPrintOutputAsync(batchClient, jobId);
}
finally
{
// Delete the job to ensure the tasks are cleaned up
if (!string.IsNullOrEmpty(jobId) && helloWorldConfigurationSettings.ShouldDeleteJob)
{
Console.WriteLine("Deleting job: {0}", jobId);
await batchClient.JobOperations.DeleteJobAsync(jobId);
}
}
}
}

/// <summary>
/// Creates a job and adds a task to it.
/// </summary>
/// <param name="batchClient">The BatchClient to use when interacting with the Batch service.</param>
/// <param name="configurationSettings">The configuration settings</param>
/// <param name="jobId">The ID of the job.</param>
/// <returns>An asynchronous <see cref="Task"/> representing the operation.</returns>
private static async Task SubmitJobAsync(
BatchClient batchClient,
Settings configurationSettings,
string jobId)
{
// create an empty unbound Job
CloudJob unboundJob = batchClient.JobOperations.CreateJob();
unboundJob.Id = jobId;

// For this job, ask the Batch service to automatically create a pool of VMs when the job is submitted.
unboundJob.PoolInformation = new PoolInformation()
{
AutoPoolSpecification = new AutoPoolSpecification()
{
AutoPoolIdPrefix = "HelloWorld",
PoolSpecification = new PoolSpecification()
{
TargetDedicatedComputeNodes = configurationSettings.PoolTargetNodeCount,
VirtualMachineSize = configurationSettings.PoolNodeVirtualMachineSize,
VirtualMachineConfiguration = new VirtualMachineConfiguration(
imageReference: new ImageReference(
publisher: configurationSettings.ImagePublisher,
offer: configurationSettings.ImageOffer,
sku: configurationSettings.ImageSku,
version: configurationSettings.ImageVersion
),
nodeAgentSkuId: configurationSettings.NodeAgentSkuId),
},
KeepAlive = false,
PoolLifetimeOption = PoolLifetimeOption.Job
}
};

// Commit Job to create it in the service
await unboundJob.CommitAsync();

// create a simple task. Each task within a job must have a unique ID
await batchClient.JobOperations.AddTaskAsync(jobId, new CloudTask("task1", "cmd /c echo Hello world from the Batch Hello world sample!"));
}

/// <summary>
/// Waits for all tasks under the specified job to complete and then prints each task's output to the console.
/// </summary>
/// <param name="batchClient">The BatchClient to use when interacting with the Batch service.</param>
/// <param name="jobId">The ID of the job.</param>
/// <returns>An asynchronous <see cref="Task"/> representing the operation.</returns>
private static async Task WaitForJobAndPrintOutputAsync(BatchClient batchClient, string jobId)
{
Console.WriteLine("Waiting for all tasks to complete on job: {0} ...", jobId);

// We use the task state monitor to monitor the state of our tasks -- in this case we will wait for them all to complete.
TaskStateMonitor taskStateMonitor = batchClient.Utilities.CreateTaskStateMonitor();

List<CloudTask> ourTasks = await batchClient.JobOperations.ListTasks(jobId).ToListAsync();

// Wait for all tasks to reach the completed state.
// If the pool is being resized then enough time is needed for the nodes to reach the idle state in order
// for tasks to run on them.
await taskStateMonitor.WhenAll(ourTasks, TaskState.Completed, TimeSpan.FromMinutes(10));

// dump task output
foreach (CloudTask t in ourTasks)
{
Console.WriteLine("Task {0}", t.Id);

//Read the standard out of the task
NodeFile standardOutFile = await t.GetNodeFileAsync(Constants.StandardOutFileName);
string standardOutText = await standardOutFile.ReadAsStringAsync();
Console.WriteLine("Standard out:");
Console.WriteLine(standardOutText);

Console.WriteLine();
}
}
}
}
Loading