Skip to content

Move generic code to strategies library. #5972

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

Merged
merged 24 commits into from
Apr 24, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
7708bb5
verbose parameter output.
sergey-shandar Apr 11, 2018
37f8700
ToPowerShellString function.
sergey-shandar Apr 11, 2018
fd9ca3a
changelog
sergey-shandar Apr 11, 2018
e38ff70
Merge branch 'preview' into sergey-whatif
sergey-shandar Apr 11, 2018
69516a3
Merge branch 'preview' into sergey-whatif
sergey-shandar Apr 13, 2018
c0804e0
Move generic code to strategies library.
sergey-shandar Apr 17, 2018
fe632ec
merge with verbose output
sergey-shandar Apr 18, 2018
67e5b30
Merge branch 'preview' into sergey-whatif
cormacpayne Apr 19, 2018
ef13db6
Fix for no parameterset.
sergey-shandar Apr 19, 2018
74df39c
Merge branch 'sergey-whatif' of https://github.com/sergey-shandar/azu…
sergey-shandar Apr 19, 2018
809d05f
merge from preview
sergey-shandar Apr 19, 2018
3ff7035
merge from 'what-if'
sergey-shandar Apr 19, 2018
692976d
Parameters fix.
sergey-shandar Apr 19, 2018
6f7366b
Unit Test for verbose output.
sergey-shandar Apr 20, 2018
7879aa2
Merge branch 'preview' into sergey-whatif
markcowl Apr 23, 2018
b300b19
Merge branch 'preview' into sergey-strategies-update2
markcowl Apr 23, 2018
64d55f6
Remove 'what-if'.
sergey-shandar Apr 23, 2018
2ef14aa
Merge branch 'sergey-whatif' of https://github.com/sergey-shandar/azu…
sergey-shandar Apr 23, 2018
1c75c6b
merge
sergey-shandar Apr 23, 2018
e60eb4b
Merge branch 'sergey-strategies-update2' of https://github.com/sergey…
sergey-shandar Apr 23, 2018
d11b0e0
address comments.
sergey-shandar Apr 23, 2018
9cef8d5
Remove CancellationToken.
sergey-shandar Apr 23, 2018
5522206
IParameters: DefaultLocation property.
sergey-shandar Apr 23, 2018
b459f26
Apply DefaultLocation
sergey-shandar Apr 23, 2018
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.Rest;
using Xunit;
using Microsoft.WindowsAzure.Commands.ScenarioTest;
using System.Threading;

namespace Microsoft.Azure.Commands.Common.Strategies.UnitTest
{
public class AsyncCmdletExtensionsTest
{
class Client : IClient
{
public T GetClient<T>() where T : ServiceClient<T>
=> null;
}

class RG { }

class Parameters : IParameters<RG>
{
public string DefaultLocation => "eastus";

public string Location
{
get
{
return "somelocation";
}

set
{
}
}

public async Task<ResourceConfig<RG>> CreateConfigAsync()
=> new ResourceConfig<RG>(
new ResourceStrategy<RG>(
null,
async (c, p) => new RG(),
null,
null,
null,
false),
null,
null,
null,
null);
}

class AsyncCmdlet : IAsyncCmdlet
{
public CancellationToken CancellationToken { get; }
= new CancellationToken();

public IEnumerable<KeyValuePair<string, object>> Parameters
{
get
{
yield return new KeyValuePair<string, object>("Str", "str");
}
}

public string VerbsNew
{
get
{
throw new NotImplementedException();
}
}

public void ReportTaskProgress(ITaskProgress taskProgress)
{
throw new NotImplementedException();
}

public Task<bool> ShouldProcessAsync(string target, string action)
{
throw new NotImplementedException();
}

public void WriteObject(object value)
{
throw new NotImplementedException();
}

public void WriteVerbose(string message)
{
Assert.Equal("Str = \"str\"", message);
}
}

[Fact]
[Trait(Category.AcceptanceType, Category.CheckIn)]
public async Task TestVerboseStream()
{
var client = new Client();
var parameters = new Parameters();
var asyncCmdlet = new AsyncCmdlet();

await client.RunAsync("subscriptionId", parameters, asyncCmdlet);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="AsyncCmdletExtensionsTest.cs" />
<Compile Include="Compute\ImageVersionTest.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="TargetStateTest.cs" />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,206 @@
// ----------------------------------------------------------------------------------
//
// Copyright Microsoft Corporation
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ----------------------------------------------------------------------------------

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;

namespace Microsoft.Azure.Commands.Common.Strategies
{
public static class AsyncCmdletExtensions
{
/// <summary>
/// WriteVerbose function with formatting.
/// </summary>
/// <param name="cmdlet">Cmdlet</param>
/// <param name="message">message with formatting</param>
/// <param name="p">message parameters</param>
public static void WriteVerbose(this IAsyncCmdlet cmdlet, string message, params object[] p)
=> cmdlet.WriteVerbose(string.Format(message, p));

/// <summary>
/// The function read current Azure state and update it according to the `parameters`.
/// </summary>
/// <typeparam name="TModel">A resource model type.</typeparam>
/// <param name="client">Azure SDK client.</param>
/// <param name="subscriptionId">Subbscription Id.</param>
/// <param name="parameters">Cmdlet parameters.</param>
/// <param name="asyncCmdlet">Asynchronous cmdlet interface.</param>
/// <param name="cancellationToken">Cancellation token.</param>
/// <returns></returns>
public static async Task<TModel> RunAsync<TModel>(
this IClient client,
string subscriptionId,
IParameters<TModel> parameters,
IAsyncCmdlet asyncCmdlet)
where TModel : class
{
// create a DAG of configs.
var config = await parameters.CreateConfigAsync();
// read current Azure state.
var current = await config.GetStateAsync(client, asyncCmdlet.CancellationToken);
// update location.
parameters.Location =
parameters.Location ?? current.GetLocation(config) ?? parameters.DefaultLocation;
// update a DAG of configs.
config = await parameters.CreateConfigAsync();
// create a target.
var target = config.GetTargetState(
current, new SdkEngine(subscriptionId), parameters.Location);
// print paramaters to a verbose stream.
foreach (var p in asyncCmdlet.Parameters)
{
asyncCmdlet.WriteVerbose(p.Key + " = " + ToPowerShellString(p.Value));
}

// apply the target state
var newState = await config.UpdateStateAsync(
client,
target,
asyncCmdlet.CancellationToken,
new ShouldProcess(asyncCmdlet),
asyncCmdlet.ReportTaskProgress);
// return a resource model
return newState.Get(config) ?? current.Get(config);
}

static string ToPowerShellString(object value)
{
if (value == null)
{
return "$null";
}

var s = value as string;
if (s != null)
{
return "\"" + s + "\"";
}

var e = value as IEnumerable;
if (e != null)
{
return string.Join(",", e.Cast<object>().Select(ToPowerShellString));
}

return value.ToString();
}

sealed class ShouldProcess : IShouldProcess
{
readonly IAsyncCmdlet _Cmdlet;

public ShouldProcess(IAsyncCmdlet cmdlet)
{
_Cmdlet = cmdlet;
}

public Task<bool> ShouldCreate<TModel>(ResourceConfig<TModel> config, TModel model)
where TModel : class
=> _Cmdlet.ShouldProcessAsync(config.GetFullName(), _Cmdlet.VerbsNew);
}

/// <summary>
/// Note: the function must be called in the main PowerShell thread.
/// </summary>
/// <param name="cmdlet"></param>
/// <param name="createAndStartTask"></param>
public static void CmdletStartAndWait(
this ICmdlet cmdlet, Func<IAsyncCmdlet, Task> createAndStartTask)
{
var asyncCmdlet = new AsyncCmdlet(cmdlet);
string previousX = null;
string previousOperation = null;
asyncCmdlet.Scheduler.Wait(
createAndStartTask(asyncCmdlet),
() =>
{
if (asyncCmdlet.TaskProgressList.Any())
{
var progress = 0.0;
var activeTasks = new List<string>();
foreach (var taskProgress in asyncCmdlet.TaskProgressList)
{
if (!taskProgress.IsDone)
{
var config = taskProgress.Config;
activeTasks.Add(config.GetFullName());
}

progress += taskProgress.GetProgress();
}

var percent = (int)(progress * 100.0);
var r = new[] { "|", "/", "-", "\\" };
var x = r[DateTime.Now.Second % 4];
var operation = activeTasks.Count > 0
? "Creating " + string.Join(", ", activeTasks) + "."
: null;

// write progress only if it's changed.
if (x != previousX || operation != previousOperation)
{
asyncCmdlet.Cmdlet.WriteProgress(
activity: "Creating Azure resources",
statusDescription: percent + "% " + x,
currentOperation: operation,
percentComplete: percent);
previousX = x;
previousOperation = operation;
}
}
});
}

sealed class AsyncCmdlet : IAsyncCmdlet
{
public SyncTaskScheduler Scheduler { get; } = new SyncTaskScheduler();

public ICmdlet Cmdlet { get; }

public List<ITaskProgress> TaskProgressList { get; }
= new List<ITaskProgress>();

public string VerbsNew => Cmdlet.VerbsNew;

public IEnumerable<KeyValuePair<string, object>> Parameters
=> Cmdlet.Parameters;

public CancellationToken CancellationToken { get; }
= new CancellationToken();

public AsyncCmdlet(ICmdlet cmdlet)
{
Cmdlet = cmdlet;
}

public void WriteVerbose(string message)
=> Scheduler.BeginInvoke(() => Cmdlet.WriteVerbose(message));

public Task<bool> ShouldProcessAsync(string target, string action)
=> Scheduler.Invoke(() => Cmdlet.ShouldProcess(target, action));

public void WriteObject(object value)
=> Scheduler.BeginInvoke(() => Cmdlet.WriteObject(value));

public void ReportTaskProgress(ITaskProgress taskProgress)
=> Scheduler.BeginInvoke(() => TaskProgressList.Add(taskProgress));


}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -55,11 +55,15 @@
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="AsyncCmdletExtensions.cs" />
<Compile Include="Compute\ImageVersion.cs" />
<Compile Include="DependencyEngine.cs" />
<Compile Include="IAsyncCmdlet.cs" />
<Compile Include="ICmdlet.cs" />
<Compile Include="INestedResourceConfig.cs" />
<Compile Include="INestedResourceConfigVisitor.cs" />
<Compile Include="INestedResourceStrategy.cs" />
<Compile Include="IParameters.cs" />
<Compile Include="IResourceId.cs" />
<Compile Include="Property.cs" />
<Compile Include="ResourceId.cs" />
Expand Down
Loading