Skip to content

Commit 1bc9277

Browse files
authored
Merge pull request Azure#5050 from markcowl/jobs
Add AsJob Implementation to Common Code and apply to Compute cmdlets
2 parents 6568dea + d8b4d4a commit 1bc9277

File tree

55 files changed

+2004
-110
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

55 files changed

+2004
-110
lines changed

src/Common/Commands.Common/AzureLongRunningJob.cs

Lines changed: 839 additions & 0 deletions
Large diffs are not rendered by default.

src/Common/Commands.Common/AzurePSCmdlet.cs

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ protected AzurePSDataCollectionProfile _dataCollectionProfile
4545
{
4646
get
4747
{
48-
lock(lockObject)
48+
lock (lockObject)
4949
{
5050
DataCollectionController controller;
5151
if (_cachedProfile == null && AzureSession.Instance.TryGetComponent(DataCollectionController.RegistryKey, out controller))
@@ -64,7 +64,7 @@ protected AzurePSDataCollectionProfile _dataCollectionProfile
6464

6565
set
6666
{
67-
lock(lockObject)
67+
lock (lockObject)
6868
{
6969
_cachedProfile = value;
7070
}
@@ -233,7 +233,7 @@ protected virtual void TearDownDebuggingTraces()
233233

234234
protected virtual void SetupHttpClientPipeline()
235235
{
236-
AzureSession.Instance.ClientFactory.AddUserAgent(ModuleName, string.Format("v{0}", ModuleVersion));
236+
AzureSession.Instance.ClientFactory.AddUserAgent(ModuleName, string.Format("v{0}", ModuleVersion));
237237
AzureSession.Instance.ClientFactory.AddUserAgent(PSVERSION, string.Format("v{0}", PSVersion));
238238

239239
AzureSession.Instance.ClientFactory.AddHandler(
@@ -636,14 +636,32 @@ protected override void ProcessRecord()
636636
try
637637
{
638638
base.ProcessRecord();
639-
ExecuteCmdlet();
639+
this.ExecuteSynchronouslyOrAsJob();
640640
}
641641
catch (Exception ex) when (!IsTerminatingError(ex))
642642
{
643643
WriteExceptionError(ex);
644644
}
645645
}
646646

647+
/// <summary>
648+
/// Job Name paroperty iof this cmdlet is run as a job
649+
/// </summary>
650+
public virtual string ImplementationBackgroundJobDescription
651+
{
652+
get
653+
{
654+
string name = "Long Running Azure Operation";
655+
string commandName = MyInvocation?.MyCommand?.Name;
656+
if (!string.IsNullOrWhiteSpace(commandName))
657+
{
658+
name = string.Format("Long Running Operation for '{0}'", commandName);
659+
}
660+
661+
return name;
662+
}
663+
}
664+
647665
protected virtual void Dispose(bool disposing)
648666
{
649667
try

src/Common/Commands.Common/Commands.Common.csproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,10 +122,14 @@
122122
<Reference Include="System.Xml" />
123123
</ItemGroup>
124124
<ItemGroup>
125+
<Compile Include="LongRunningJobCancelledException.cs" />
126+
<Compile Include="ShouldMethodInvoker.cs" />
127+
<Compile Include="AzureLongRunningJob.cs" />
125128
<Compile Include="Serialization\LegacyAzureAccount.cs" />
126129
<Compile Include="Serialization\LegacyAzureEnvironment.cs" />
127130
<Compile Include="Serialization\LegacyAzureSubscription.cs" />
128131
<Compile Include="Serialization\ModelConversionExtensions.cs" />
132+
<Compile Include="ShouldMethodStreamItem.cs" />
129133
<Compile Include="Utilities\INetworkHelper.cs" />
130134
<Compile Include="Utilities\JsonUtilities.cs" />
131135
<Compile Include="AzureDataCmdlet.cs" />

src/Common/Commands.Common/Extensions/CmdletExtensions.cs

Lines changed: 167 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,22 +12,172 @@
1212
// limitations under the License.
1313
// ----------------------------------------------------------------------------------
1414

15+
using Microsoft.Azure.Commands.Common;
1516
using Microsoft.Azure.Commands.Common.Authentication.Abstractions;
16-
using Microsoft.WindowsAzure.Commands.Common;
1717
using System;
1818
using System.Collections.Generic;
1919
using System.Collections.ObjectModel;
2020
using System.IO;
21-
using System.Linq;
2221
using System.Management.Automation;
23-
using System.Management.Automation.Runspaces;
2422
using System.Reflection;
2523
using System.Text;
24+
using System.Threading;
2625

2726
namespace Microsoft.WindowsAzure.Commands.Utilities.Common
2827
{
2928
public static class CmdletExtensions
3029
{
30+
/// <summary>
31+
/// Execute this cmdlet in the background and return a job that tracks the results
32+
/// </summary>
33+
/// <typeparam name="T">The cmdlet type</typeparam>
34+
/// <param name="cmdlet">The cmdlet to execute</param>
35+
/// <param name="jobName">The name of the job</param>
36+
/// <returns>The job tracking cmdlet execution</returns>
37+
public static Job ExecuteAsJob<T>(this T cmdlet, string jobName) where T : AzurePSCmdlet
38+
{
39+
if (cmdlet == null)
40+
{
41+
throw new ArgumentNullException(nameof(cmdlet));
42+
}
43+
44+
return ExecuteAsJob(cmdlet, jobName, cmd => cmd.ExecuteCmdlet());
45+
}
46+
47+
/// <summary>
48+
/// Execute this cmdlet in the background and return a job that tracks the results
49+
/// </summary>
50+
/// <typeparam name="T">The cmdlet type</typeparam>
51+
/// <param name="cmdlet">The cmdlet to execute</param>
52+
/// <param name="jobName">The name of the job</param>
53+
/// <param name="executor">The method to execute in the background job</param>
54+
/// <returns>The job tracking cmdlet execution</returns>
55+
public static Job ExecuteAsJob<T>(this T cmdlet, string jobName, Action<T> executor) where T : AzurePSCmdlet
56+
{
57+
if (cmdlet == null)
58+
{
59+
throw new ArgumentNullException(nameof(cmdlet));
60+
}
61+
62+
if (executor == null)
63+
{
64+
throw new ArgumentNullException(nameof(executor));
65+
}
66+
67+
var job = AzureLongRunningJob<T>.Create(cmdlet, cmdlet?.MyInvocation?.MyCommand?.Name, jobName, executor);
68+
cmdlet.SafeAddToJobRepository(job);
69+
ThreadPool.QueueUserWorkItem(job.RunJob, job);
70+
return job;
71+
}
72+
73+
/// <summary>
74+
/// Determine if AsJob is present
75+
/// </summary>
76+
/// <typeparam name="T">The cmdlet type</typeparam>
77+
/// <param name="cmdlet">The cmdlet</param>
78+
/// <returns>True if the cmdlet shoudl run as a Job, otherwise false</returns>
79+
public static bool AsJobPresent<T>(this T cmdlet) where T : AzurePSCmdlet
80+
{
81+
if (cmdlet == null)
82+
{
83+
throw new ArgumentNullException(nameof(cmdlet));
84+
}
85+
86+
return (cmdlet.MyInvocation?.BoundParameters != null
87+
&& cmdlet.MyInvocation.BoundParameters.ContainsKey("AsJob"));
88+
}
89+
90+
/// <summary>
91+
/// Execute the given cmdlet synchronously os as a job, based on input parameters
92+
/// </summary>
93+
/// <typeparam name="T"></typeparam>
94+
/// <param name="cmdlet"></param>
95+
public static void ExecuteSynchronouslyOrAsJob<T>(this T cmdlet) where T: AzurePSCmdlet
96+
{
97+
if (cmdlet == null)
98+
{
99+
throw new ArgumentNullException(nameof(cmdlet));
100+
}
101+
102+
cmdlet.ExecuteSynchronouslyOrAsJob(c => c.ExecuteCmdlet());
103+
}
104+
105+
/// <summary>
106+
/// Decide whether to execute this cmdlet as a job or synchronously, based on input parameters
107+
/// </summary>
108+
/// <typeparam name="T">The cmdlet type</typeparam>
109+
/// <param name="cmdlet">The cmdlet to execute</param>
110+
/// <param name="executor">The cmdlet method to execute</param>
111+
public static void ExecuteSynchronouslyOrAsJob<T>(this T cmdlet, Action<T> executor) where T : AzurePSCmdlet
112+
{
113+
if (cmdlet == null)
114+
{
115+
throw new ArgumentNullException(nameof(cmdlet));
116+
}
117+
118+
if (executor == null)
119+
{
120+
throw new ArgumentNullException(nameof(executor));
121+
}
122+
123+
if (cmdlet.AsJobPresent())
124+
{
125+
cmdlet.WriteObject(cmdlet.ExecuteAsJob(cmdlet.ImplementationBackgroundJobDescription, executor));
126+
}
127+
else
128+
{
129+
executor(cmdlet);
130+
}
131+
}
132+
133+
/// <summary>
134+
/// Safely Attempt to copy a property value from source to target
135+
/// </summary>
136+
/// <typeparam name="T">The type fo the source and target objects</typeparam>
137+
/// <param name="property">The property to copy</param>
138+
/// <param name="source">The source object to copy from</param>
139+
/// <param name="target">The target object to copy to</param>
140+
public static void SafeCopyValue<T>(this PropertyInfo property, T source, T target)
141+
{
142+
if (property == null)
143+
{
144+
throw new ArgumentNullException(nameof(property));
145+
}
146+
147+
try
148+
{
149+
property.SetValue(target, property.GetValue(source));
150+
}
151+
catch
152+
{
153+
// ignore get and set errors
154+
}
155+
}
156+
157+
/// <summary>
158+
/// Safely Attempt to copy a field value from source to target
159+
/// </summary>
160+
/// <typeparam name="T">The type of the source and target objects</typeparam>
161+
/// <param name="field">The field to copy</param>
162+
/// <param name="source">The source object to copy from</param>
163+
/// <param name="target">The target object to copy to</param>
164+
public static void SafeCopyValue<T>(this FieldInfo field, T source, T target)
165+
{
166+
if (field == null)
167+
{
168+
throw new ArgumentNullException(nameof(field));
169+
}
170+
171+
try
172+
{
173+
field.SetValue(target, field.GetValue(source));
174+
}
175+
catch
176+
{
177+
// ignore get and set errors
178+
}
179+
}
180+
31181
public static string AsAbsoluteLocation(this string realtivePath)
32182
{
33183
return Path.GetFullPath(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, realtivePath));
@@ -231,5 +381,19 @@ public static void DisableDataCollection(this AzurePSCmdlet cmdlt)
231381
}
232382

233383
#endregion
384+
385+
386+
static void SafeAddToJobRepository(this AzurePSCmdlet cmdlet, Job job)
387+
{
388+
try
389+
{
390+
cmdlet.JobRepository.Add(job);
391+
}
392+
catch
393+
{
394+
// ignore errors in adding the job to the repository
395+
}
396+
}
397+
234398
}
235399
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
// ----------------------------------------------------------------------------------
2+
//
3+
// Copyright Microsoft Corporation
4+
// Licensed under the Apache License, Version 2.0 (the "License");
5+
// you may not use this file except in compliance with the License.
6+
// You may obtain a copy of the License at
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
// Unless required by applicable law or agreed to in writing, software
9+
// distributed under the License is distributed on an "AS IS" BASIS,
10+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+
// See the License for the specific language governing permissions and
12+
// limitations under the License.
13+
// ----------------------------------------------------------------------------------
14+
15+
using System;
16+
17+
namespace Microsoft.WindowsAzure.Commands.Common
18+
{
19+
/// <summary>
20+
/// Exception used to terminate runtime functions when a job is stopping
21+
/// </summary>
22+
[Serializable]
23+
public class LongRunningJobCancelledException :InvalidOperationException
24+
{
25+
public LongRunningJobCancelledException() : base()
26+
{
27+
}
28+
29+
public LongRunningJobCancelledException(string message) : base(message)
30+
{
31+
}
32+
33+
public LongRunningJobCancelledException(string message, Exception innerException) : base(message, innerException)
34+
{
35+
}
36+
}
37+
}

0 commit comments

Comments
 (0)