Skip to content

Commit 2cefac7

Browse files
committed
Update C# HelloWorld sample to track 2
1 parent 17fa81b commit 2cefac7

File tree

6 files changed

+169
-236
lines changed

6 files changed

+169
-236
lines changed

CSharp/GettingStarted/01_HelloWorld/HelloWorld.csproj

Lines changed: 5 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,25 +2,14 @@
22

33
<PropertyGroup>
44
<OutputType>Exe</OutputType>
5-
<TargetFramework>net462</TargetFramework>
6-
<RootNamespace>Microsoft.Azure.Batch.Samples.HelloWorld</RootNamespace>
5+
<TargetFramework>net8.0</TargetFramework>
6+
<RootNamespace>Azure.Compute.Batch.Samples.HelloWorld</RootNamespace>
77
</PropertyGroup>
88

99
<ItemGroup>
10-
<ProjectReference Include="..\..\Common\Microsoft.Azure.Batch.Samples.Common.csproj" />
11-
</ItemGroup>
12-
13-
<ItemGroup>
14-
<PackageReference Include="Microsoft.Azure.Batch" Version="14.0.0" />
15-
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="2.1.0" />
16-
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="2.1.0" />
17-
<PackageReference Include="WindowsAzure.Storage" Version="9.3.3" />
18-
</ItemGroup>
19-
20-
<ItemGroup>
21-
<None Update="settings.json">
22-
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
23-
</None>
10+
<PackageReference Include="Azure.Compute.Batch" Version="1.0.0-alpha.20240417.3" />
11+
<PackageReference Include="Azure.Identity" Version="1.11.1" />
12+
<PackageReference Include="Azure.ResourceManager.Batch" Version="1.4.0" />
2413
</ItemGroup>
2514

2615
</Project>
Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,10 @@
11

22
Microsoft Visual Studio Solution File, Format Version 12.00
3-
# Visual Studio 15
4-
VisualStudioVersion = 15.0.26430.12
3+
# Visual Studio Version 17
4+
VisualStudioVersion = 17.9.34728.123
55
MinimumVisualStudioVersion = 10.0.40219.1
66
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HelloWorld", "HelloWorld.csproj", "{FE62D509-B9A9-4592-81BB-779306C22F95}"
77
EndProject
8-
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Azure.Batch.Samples.Common", "..\..\Common\Microsoft.Azure.Batch.Samples.Common.csproj", "{612B170A-1697-4C40-BD57-26A6C8AC6534}"
9-
EndProject
108
Global
119
GlobalSection(SolutionConfigurationPlatforms) = preSolution
1210
Debug|Any CPU = Debug|Any CPU
@@ -17,12 +15,11 @@ Global
1715
{FE62D509-B9A9-4592-81BB-779306C22F95}.Debug|Any CPU.Build.0 = Debug|Any CPU
1816
{FE62D509-B9A9-4592-81BB-779306C22F95}.Release|Any CPU.ActiveCfg = Release|Any CPU
1917
{FE62D509-B9A9-4592-81BB-779306C22F95}.Release|Any CPU.Build.0 = Release|Any CPU
20-
{612B170A-1697-4C40-BD57-26A6C8AC6534}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
21-
{612B170A-1697-4C40-BD57-26A6C8AC6534}.Debug|Any CPU.Build.0 = Debug|Any CPU
22-
{612B170A-1697-4C40-BD57-26A6C8AC6534}.Release|Any CPU.ActiveCfg = Release|Any CPU
23-
{612B170A-1697-4C40-BD57-26A6C8AC6534}.Release|Any CPU.Build.0 = Release|Any CPU
2418
EndGlobalSection
2519
GlobalSection(SolutionProperties) = preSolution
2620
HideSolutionNode = FALSE
2721
EndGlobalSection
22+
GlobalSection(ExtensibilityGlobals) = postSolution
23+
SolutionGuid = {33B77A65-5D1C-46B7-918E-D922919D6884}
24+
EndGlobalSection
2825
EndGlobal
Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
3+
using Azure.Core;
4+
using Azure.Identity;
5+
using Azure.ResourceManager;
6+
using Azure.ResourceManager.Batch;
7+
using Azure.ResourceManager.Batch.Models;
8+
using System;
9+
using System.IO;
10+
using System.Linq;
11+
using System.Threading.Tasks;
12+
13+
namespace Azure.Compute.Batch.Samples.HelloWorld
14+
{
15+
public class HelloWorldSample
16+
{
17+
private ArmClient _armClient;
18+
private BatchClient _batchClient;
19+
20+
/// <summary>
21+
/// Creates a pool with a configurable number of nodes, then submits tasks which print a 'Hello world' message.
22+
/// The resulting stdout.txt or stderr.txt (depending on each task's exit code) is then printed to the console.
23+
///
24+
/// After running, the job will be terminated and the pool will be deleted.
25+
/// </summary>
26+
/// <param name="batchAccountResourceId">The ARM resource ID of the Batch account.</param>
27+
/// <returns>A task which completes when the sample has finished running.</returns>
28+
public async Task Run(string batchAccountResourceId)
29+
{
30+
var batchAccountIdentifier = ResourceIdentifier.Parse(batchAccountResourceId);
31+
32+
var credential = new DefaultAzureCredential();
33+
_armClient = new ArmClient(credential);
34+
35+
BatchAccountResource batchAccount = await _armClient.GetBatchAccountResource(batchAccountIdentifier).GetAsync();
36+
37+
_batchClient = new BatchClient(new Uri($"https://{batchAccount.Data.AccountEndpoint}"), credential);
38+
39+
var poolName = GenerateUniqueName("HelloWorldPool");
40+
var imageReference = new BatchImageReference()
41+
{
42+
Publisher = "canonical",
43+
Offer = "0001-com-ubuntu-server-jammy",
44+
Sku = "22_04-lts",
45+
Version = "latest"
46+
};
47+
string nodeAgentSku = "batch.node.ubuntu 22.04";
48+
49+
BatchAccountPoolResource pool = (await batchAccount.GetBatchAccountPools().CreateOrUpdateAsync(WaitUntil.Completed, poolName, new BatchAccountPoolData()
50+
{
51+
VmSize = "Standard_DS1_v2",
52+
DeploymentConfiguration = new BatchDeploymentConfiguration()
53+
{
54+
VmConfiguration = new BatchVmConfiguration(imageReference, nodeAgentSku)
55+
},
56+
ScaleSettings = new BatchAccountPoolScaleSettings()
57+
{
58+
FixedScale = new BatchAccountFixedScaleSettings()
59+
{
60+
TargetDedicatedNodes = 1
61+
}
62+
}
63+
})).Value;
64+
65+
string jobId = GenerateUniqueName("HelloWorldJob");
66+
67+
try
68+
{
69+
await _batchClient.CreateJobAsync(new BatchJobCreateContent(jobId, new BatchPoolInfo() { PoolId = poolName }));
70+
71+
for (int i = 0; i < 5; i++)
72+
{
73+
string taskId = $"task-{i}";
74+
Console.WriteLine("Submitting {0}", taskId);
75+
await _batchClient.CreateTaskAsync(jobId, new BatchTaskCreateContent(taskId, $"echo Hello world from {taskId}"));
76+
}
77+
78+
Console.WriteLine("Waiting for all tasks to complete on job: {0} ...", jobId);
79+
await waitForTasksToComplete(jobId);
80+
81+
var completedTasks = _batchClient.GetTasksAsync(jobId, filter: "state eq 'completed'");
82+
await foreach (BatchTask t in completedTasks)
83+
{
84+
var outputFileName = t.ExecutionInfo.ExitCode == 0 ? "stdout.txt" : "stderr.txt";
85+
86+
Console.WriteLine("Task {0} exited with code {1}. Output ({2}):",
87+
t.Id, t.ExecutionInfo.ExitCode, outputFileName);
88+
89+
BinaryData fileContents = await _batchClient.GetTaskFileAsync(jobId, t.Id, outputFileName);
90+
using (var reader = new StreamReader(fileContents.ToStream()))
91+
{
92+
Console.WriteLine(await reader.ReadLineAsync());
93+
}
94+
}
95+
}
96+
finally
97+
{
98+
Console.WriteLine("Terminating job {0} and deleting pool {1}", jobId, poolName);
99+
await Task.WhenAll([
100+
_batchClient.TerminateJobAsync(jobId),
101+
pool.DeleteAsync(WaitUntil.Completed)]);
102+
}
103+
}
104+
105+
/// <summary>
106+
/// Poll all the tasks in the given job and wait for them to reach the completed state.
107+
/// </summary>
108+
/// <param name="jobId">The ID of the job to poll</param>
109+
/// <returns>A task that will complete when all Batch tasks have completed.</returns>
110+
/// <exception cref="TimeoutException">Thrown if all tasks haven't reached the completed state after a certain period of time</exception>
111+
private async Task waitForTasksToComplete(String jobId)
112+
{
113+
// Note that this timeout should take into account the time it takes for the pool to scale up
114+
var timeoutAfter = DateTime.Now.AddMinutes(10);
115+
while (DateTime.Now < timeoutAfter)
116+
{
117+
var allComplete = true;
118+
var tasks = _batchClient.GetTasksAsync(jobId, select: ["id", "state"]);
119+
await foreach (BatchTask task in tasks)
120+
{
121+
if (task.State != BatchTaskState.Completed)
122+
{
123+
allComplete = false;
124+
break;
125+
}
126+
}
127+
128+
if (allComplete)
129+
{
130+
return;
131+
}
132+
133+
await Task.Delay(10000);
134+
}
135+
136+
throw new TimeoutException("Task(s) did not complete within the specified time");
137+
}
138+
139+
/// <summary>
140+
/// Generate a unique name with the given prefix using the current user name and a timestamp.
141+
/// </summary>
142+
/// <param name="prefix">The name's prefix.</param>
143+
/// <returns>The generated name.</returns>
144+
private static string GenerateUniqueName(string prefix)
145+
{
146+
string currentUser = new string(Environment.UserName.Where(char.IsLetterOrDigit).ToArray());
147+
return string.Format("{0}-{1}-{2}", prefix, currentUser, DateTime.Now.ToString("yyyyMMdd-HHmmss"));
148+
}
149+
}
150+
}
Lines changed: 9 additions & 162 deletions
Original file line numberDiff line numberDiff line change
@@ -1,176 +1,23 @@
1-
//Copyright (c) Microsoft Corporation
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
22

3-
namespace Microsoft.Azure.Batch.Samples.HelloWorld
4-
{
5-
using System;
6-
using System.IO;
7-
using System.Collections.Generic;
8-
using System.Threading.Tasks;
9-
using Auth;
10-
using Batch.Common;
11-
using Common;
12-
using Microsoft.Extensions.Configuration;
3+
using System;
4+
using System.Threading.Tasks;
135

6+
namespace Azure.Compute.Batch.Samples.HelloWorld
7+
{
148
/// <summary>
159
/// The main program of the HelloWorld sample
1610
/// </summary>
1711
public static class Program
1812
{
19-
public static void Main(string[] args)
20-
{
21-
try
22-
{
23-
AccountSettings accountSettings = SampleHelpers.LoadAccountSettings();
24-
Settings helloWorldSettings = new ConfigurationBuilder()
25-
.SetBasePath(Directory.GetCurrentDirectory())
26-
.AddJsonFile("settings.json")
27-
.Build()
28-
.Get<Settings>();
29-
30-
HelloWorldAsync(accountSettings, helloWorldSettings).Wait();
31-
}
32-
catch (AggregateException aggregateException)
33-
{
34-
// Go through all exceptions and dump useful information
35-
foreach (Exception exception in aggregateException.InnerExceptions)
36-
{
37-
Console.WriteLine(exception.ToString());
38-
Console.WriteLine();
39-
}
13+
private static string batchAccountResourceId = "your-batch-account-resource-id";
4014

41-
throw;
42-
}
15+
public async static Task Main(string[] args)
16+
{
17+
await new HelloWorldSample().Run(batchAccountResourceId);
4318

4419
Console.WriteLine("Press return to exit...");
4520
Console.ReadLine();
4621
}
47-
48-
/// <summary>
49-
/// Submits a job to the Azure Batch service, and waits for it to complete
50-
/// </summary>
51-
private static async Task HelloWorldAsync(
52-
AccountSettings accountSettings,
53-
Settings helloWorldConfigurationSettings)
54-
{
55-
Console.WriteLine("Running with the following settings: ");
56-
Console.WriteLine("-------------------------------------");
57-
Console.WriteLine(helloWorldConfigurationSettings.ToString());
58-
Console.WriteLine(accountSettings.ToString());
59-
60-
// Set up the Batch Service credentials used to authenticate with the Batch Service.
61-
BatchSharedKeyCredentials credentials = new BatchSharedKeyCredentials(
62-
accountSettings.BatchServiceUrl,
63-
accountSettings.BatchAccountName,
64-
accountSettings.BatchAccountKey);
65-
66-
// Get an instance of the BatchClient for a given Azure Batch account.
67-
using (BatchClient batchClient = BatchClient.Open(credentials))
68-
{
69-
// add a retry policy. The built-in policies are No Retry (default), Linear Retry, and Exponential Retry
70-
batchClient.CustomBehaviors.Add(RetryPolicyProvider.ExponentialRetryProvider(TimeSpan.FromSeconds(5), 3));
71-
72-
string jobId = GettingStartedCommon.CreateJobId("HelloWorldJob");
73-
74-
try
75-
{
76-
// Submit the job
77-
await SubmitJobAsync(batchClient, helloWorldConfigurationSettings, jobId);
78-
79-
// Wait for the job to complete
80-
await WaitForJobAndPrintOutputAsync(batchClient, jobId);
81-
}
82-
finally
83-
{
84-
// Delete the job to ensure the tasks are cleaned up
85-
if (!string.IsNullOrEmpty(jobId) && helloWorldConfigurationSettings.ShouldDeleteJob)
86-
{
87-
Console.WriteLine("Deleting job: {0}", jobId);
88-
await batchClient.JobOperations.DeleteJobAsync(jobId);
89-
}
90-
}
91-
}
92-
}
93-
94-
/// <summary>
95-
/// Creates a job and adds a task to it.
96-
/// </summary>
97-
/// <param name="batchClient">The BatchClient to use when interacting with the Batch service.</param>
98-
/// <param name="configurationSettings">The configuration settings</param>
99-
/// <param name="jobId">The ID of the job.</param>
100-
/// <returns>An asynchronous <see cref="Task"/> representing the operation.</returns>
101-
private static async Task SubmitJobAsync(
102-
BatchClient batchClient,
103-
Settings configurationSettings,
104-
string jobId)
105-
{
106-
// create an empty unbound Job
107-
CloudJob unboundJob = batchClient.JobOperations.CreateJob();
108-
unboundJob.Id = jobId;
109-
110-
// For this job, ask the Batch service to automatically create a pool of VMs when the job is submitted.
111-
unboundJob.PoolInformation = new PoolInformation()
112-
{
113-
AutoPoolSpecification = new AutoPoolSpecification()
114-
{
115-
AutoPoolIdPrefix = "HelloWorld",
116-
PoolSpecification = new PoolSpecification()
117-
{
118-
TargetDedicatedComputeNodes = configurationSettings.PoolTargetNodeCount,
119-
VirtualMachineSize = configurationSettings.PoolNodeVirtualMachineSize,
120-
VirtualMachineConfiguration = new VirtualMachineConfiguration(
121-
imageReference: new ImageReference(
122-
publisher: configurationSettings.ImagePublisher,
123-
offer: configurationSettings.ImageOffer,
124-
sku: configurationSettings.ImageSku,
125-
version: configurationSettings.ImageVersion
126-
),
127-
nodeAgentSkuId: configurationSettings.NodeAgentSkuId),
128-
},
129-
KeepAlive = false,
130-
PoolLifetimeOption = PoolLifetimeOption.Job
131-
}
132-
};
133-
134-
// Commit Job to create it in the service
135-
await unboundJob.CommitAsync();
136-
137-
// create a simple task. Each task within a job must have a unique ID
138-
await batchClient.JobOperations.AddTaskAsync(jobId, new CloudTask("task1", "cmd /c echo Hello world from the Batch Hello world sample!"));
139-
}
140-
141-
/// <summary>
142-
/// Waits for all tasks under the specified job to complete and then prints each task's output to the console.
143-
/// </summary>
144-
/// <param name="batchClient">The BatchClient to use when interacting with the Batch service.</param>
145-
/// <param name="jobId">The ID of the job.</param>
146-
/// <returns>An asynchronous <see cref="Task"/> representing the operation.</returns>
147-
private static async Task WaitForJobAndPrintOutputAsync(BatchClient batchClient, string jobId)
148-
{
149-
Console.WriteLine("Waiting for all tasks to complete on job: {0} ...", jobId);
150-
151-
// We use the task state monitor to monitor the state of our tasks -- in this case we will wait for them all to complete.
152-
TaskStateMonitor taskStateMonitor = batchClient.Utilities.CreateTaskStateMonitor();
153-
154-
List<CloudTask> ourTasks = await batchClient.JobOperations.ListTasks(jobId).ToListAsync();
155-
156-
// Wait for all tasks to reach the completed state.
157-
// If the pool is being resized then enough time is needed for the nodes to reach the idle state in order
158-
// for tasks to run on them.
159-
await taskStateMonitor.WhenAll(ourTasks, TaskState.Completed, TimeSpan.FromMinutes(10));
160-
161-
// dump task output
162-
foreach (CloudTask t in ourTasks)
163-
{
164-
Console.WriteLine("Task {0}", t.Id);
165-
166-
//Read the standard out of the task
167-
NodeFile standardOutFile = await t.GetNodeFileAsync(Constants.StandardOutFileName);
168-
string standardOutText = await standardOutFile.ReadAsStringAsync();
169-
Console.WriteLine("Standard out:");
170-
Console.WriteLine(standardOutText);
171-
172-
Console.WriteLine();
173-
}
174-
}
17522
}
17623
}

0 commit comments

Comments
 (0)