diff --git a/.gitignore b/.gitignore index 4dbf1c158517..1d7bdd04deda 100644 --- a/.gitignore +++ b/.gitignore @@ -211,4 +211,5 @@ FakesAssemblies/ /tools/*.dll *.GhostDoc.xml pingme.txt -groupMapping*.json \ No newline at end of file +groupMapping*.json +.vscode/ \ No newline at end of file diff --git a/setup/azurecmdfiles.wxi b/setup/azurecmdfiles.wxi index 328afceaf728..eb1f3f283574 100644 --- a/setup/azurecmdfiles.wxi +++ b/setup/azurecmdfiles.wxi @@ -271,6 +271,9 @@ + + + @@ -3148,6 +3151,7 @@ + diff --git a/src/Common/Commands.Common/AzureLongRunningJob.cs b/src/Common/Commands.Common/AzureLongRunningJob.cs index 1d1f61314cb0..0652e289a5f5 100644 --- a/src/Common/Commands.Common/AzureLongRunningJob.cs +++ b/src/Common/Commands.Common/AzureLongRunningJob.cs @@ -191,11 +191,13 @@ public static U CopyCmdlet(U cmdlet) where U : AzurePSCmdlet returnValue.MyInvocation.BoundParameters.Add(parameter.Key, parameter.Value); } - foreach (var field in returnType.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.DeclaredOnly)) + + foreach (var field in returnType.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)) { field.SafeCopyValue(source: cmdlet, target: returnValue); } + cmdlet.SafeCopyParameterSet(returnValue); return returnValue as U; } @@ -829,6 +831,9 @@ private bool InvokeShouldMethodAndWaitForResults(Func shouldMethod } } + /// + /// Stop job execution + /// public override void StopJob() { ShouldMethodStreamItem stream; diff --git a/src/Common/Commands.Common/Extensions/CmdletExtensions.cs b/src/Common/Commands.Common/Extensions/CmdletExtensions.cs index a93c4e0021f5..8d09a038932f 100644 --- a/src/Common/Commands.Common/Extensions/CmdletExtensions.cs +++ b/src/Common/Commands.Common/Extensions/CmdletExtensions.cs @@ -178,6 +178,30 @@ public static void SafeCopyValue(this FieldInfo field, T source, T target) } } + /// + /// Safely copy the selected parameter set from one cmdlet to another + /// + /// The cmdlet type + /// The cmdlet to copy the parameter set name from + /// The cmdlet to copy to + public static void SafeCopyParameterSet(this T source, T target) where T: AzurePSCmdlet + { + if (source != null && target != null) + { + if (!string.IsNullOrWhiteSpace(source.ParameterSetName)) + { + try + { + target.SetParameterSet(source.ParameterSetName); + } + catch + { + + } + } + } + } + public static string AsAbsoluteLocation(this string realtivePath) { return Path.GetFullPath(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, realtivePath)); diff --git a/src/ResourceManager/Common/Commands.Common.Strategies.UnitTest/Commands.Common.Strategies.UnitTest.csproj b/src/ResourceManager/Common/Commands.Common.Strategies.UnitTest/Commands.Common.Strategies.UnitTest.csproj new file mode 100644 index 000000000000..3e19b17f3158 --- /dev/null +++ b/src/ResourceManager/Common/Commands.Common.Strategies.UnitTest/Commands.Common.Strategies.UnitTest.csproj @@ -0,0 +1,89 @@ + + + + Debug + AnyCPU + {5C052681-DA3C-4FEA-8E02-7DFBB2A18B22} + Library + Properties + Microsoft.Azure.Commands.Common.Strategies.UnitTest + Microsoft.Azure.Commands.Common.Strategies.UnitTest + v4.5.2 + 512 + {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + 10.0 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) + $(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages + False + UnitTest + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + + + + + + + + + + + + + + + + + + {eea69772-d41b-482a-9252-2b4595c59e53} + Commands.Common.Strategies + + + + + + + False + + + False + + + False + + + False + + + + + + + + \ No newline at end of file diff --git a/src/ResourceManager/Common/Commands.Common.Strategies.UnitTest/Properties/AssemblyInfo.cs b/src/ResourceManager/Common/Commands.Common.Strategies.UnitTest/Properties/AssemblyInfo.cs new file mode 100644 index 000000000000..6d1737f580a8 --- /dev/null +++ b/src/ResourceManager/Common/Commands.Common.Strategies.UnitTest/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Commands.Common.Strategies.UnitTest")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("Commands.Common.Strategies.UnitTest")] +[assembly: AssemblyCopyright("Copyright © 2017")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("5c052681-da3c-4fea-8e02-7dfbb2a18b22")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/src/ResourceManager/Common/Commands.Common.Strategies.UnitTest/TimeSlotTest.cs b/src/ResourceManager/Common/Commands.Common.Strategies.UnitTest/TimeSlotTest.cs new file mode 100644 index 000000000000..8aca6597a420 --- /dev/null +++ b/src/ResourceManager/Common/Commands.Common.Strategies.UnitTest/TimeSlotTest.cs @@ -0,0 +1,72 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Microsoft.Azure.Commands.Common.Strategies.UnitTest +{ + [TestClass] + public class TimeSlotTest + { + [TestMethod] + public void AddTest() + { + var first = new TimeSlot(); + + Assert.AreEqual(0, first.Duration); + Assert.AreEqual(0, first.TaskCount); + Assert.AreEqual(null, first.Next); + + var next = first.AddTask(50); + + Assert.AreEqual(50, first.Duration); + Assert.AreEqual(1, first.TaskCount); + Assert.AreEqual(next, first.Next); + + Assert.AreEqual(10.0, first.GetTaskProgress(10)); + + Assert.AreEqual(0, next.Duration); + Assert.AreEqual(0, next.TaskCount); + Assert.AreEqual(null, next.Next); + + var next2 = first.AddTask(50); + + Assert.AreEqual(50, first.Duration); + Assert.AreEqual(2, first.TaskCount); + Assert.AreEqual(next2, first.Next); + Assert.AreEqual(next, first.Next); + + Assert.AreEqual(20.0, first.GetTaskProgress(40)); + + Assert.AreEqual(0, next2.Duration); + Assert.AreEqual(0, next2.TaskCount); + Assert.AreEqual(null, next2.Next); + + var next3 = first.AddTask(30); + Assert.AreEqual(30, first.Duration); + Assert.AreEqual(3, first.TaskCount); + Assert.AreEqual(next3, first.Next); + + Assert.AreEqual(3.0, first.GetTaskProgress(9)); + + Assert.AreEqual(20, next3.Duration); + Assert.AreEqual(2, next3.TaskCount); + Assert.AreEqual(next2, next3.Next); + Assert.AreEqual(next, next3.Next); + + Assert.AreEqual(10.0 + 5, first.GetTaskProgress(40)); + + Assert.AreEqual(0, next2.Duration); + Assert.AreEqual(0, next2.TaskCount); + Assert.AreEqual(null, next2.Next); + + var next4 = first.AddTask(75); + Assert.AreEqual(25, next2.Duration); + Assert.AreEqual(1, next2.TaskCount); + Assert.AreEqual(next4, next2.Next); + + Assert.AreEqual((30.0 / 4) + (20.0 / 3) + 20, first.GetTaskProgress(70)); + + Assert.AreEqual(0, next4.Duration); + Assert.AreEqual(0, next4.TaskCount); + Assert.AreEqual(null, next4.Next); + } + } +} diff --git a/src/ResourceManager/Common/Commands.Common.Strategies/Commands.Common.Strategies.csproj b/src/ResourceManager/Common/Commands.Common.Strategies/Commands.Common.Strategies.csproj new file mode 100644 index 000000000000..35b3c5524a97 --- /dev/null +++ b/src/ResourceManager/Common/Commands.Common.Strategies/Commands.Common.Strategies.csproj @@ -0,0 +1,104 @@ + + + + + Debug + AnyCPU + {EEA69772-D41B-482A-9252-2B4595C59E53} + Library + Properties + Microsoft.Azure.Commands.Common.Strategies + Microsoft.Azure.Commands.Common.Strategies + v4.5.2 + 512 + + + true + full + false + ..\..\..\Package\Debug\ResourceManager\AzureResourceManager\AzureRM.Compute\ + TRACE;DEBUG + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE;SIGN + prompt + 4 + true + true + MSSharedLibKey.snk + + + + ..\..\..\packages\Microsoft.Rest.ClientRuntime.2.3.10\lib\net452\Microsoft.Rest.ClientRuntime.dll + True + + + ..\..\..\packages\Microsoft.Rest.ClientRuntime.Azure.3.3.10\lib\net452\Microsoft.Rest.ClientRuntime.Azure.dll + True + + + ..\..\..\packages\Newtonsoft.Json.6.0.8\lib\net45\Newtonsoft.Json.dll + True + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/ResourceManager/Common/Commands.Common.Strategies/Common.Strategies.Netcore.csproj b/src/ResourceManager/Common/Commands.Common.Strategies/Common.Strategies.Netcore.csproj new file mode 100644 index 000000000000..562c9416f9bc --- /dev/null +++ b/src/ResourceManager/Common/Commands.Common.Strategies/Common.Strategies.Netcore.csproj @@ -0,0 +1,30 @@ + + + + + + netcoreapp2.0 + Microsoft.Azure.Commands.Common.Strategies + win10-x64 + Microsoft.Azure.Commands.Common.Strategies + false + + + + True + True + MSSharedLibKey.snk + TRACE;RELEASE;NETSTANDARD;SIGN + + + + TRACE;DEBUG;NETSTANDARD + false + + + + + + + + \ No newline at end of file diff --git a/src/ResourceManager/Common/Commands.Common.Strategies/CreateOrUpdateAsyncParams.cs b/src/ResourceManager/Common/Commands.Common.Strategies/CreateOrUpdateAsyncParams.cs new file mode 100644 index 000000000000..3c37cc1d1e01 --- /dev/null +++ b/src/ResourceManager/Common/Commands.Common.Strategies/CreateOrUpdateAsyncParams.cs @@ -0,0 +1,56 @@ +// ---------------------------------------------------------------------------------- +// +// 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.Threading; + +namespace Microsoft.Azure.Commands.Common.Strategies +{ + public static class CreateOrUpdateAsyncParams + { + public static CreateOrUpdateAsyncParams Create( + string resourceGroupName, + string name, + TModel model, + CancellationToken cancellationToken) + => new CreateOrUpdateAsyncParams( + resourceGroupName, name, model, cancellationToken); + } + + /// + /// Parameters for CreateOrUpdateAsync functions. + /// + /// + public sealed class CreateOrUpdateAsyncParams + { + public string ResourceGroupName { get; } + + public string Name { get; } + + public CancellationToken CancellationToken { get; } + + public TModel Model { get; } + + public CreateOrUpdateAsyncParams( + string resourceGroupName, + string name, + TModel model, + CancellationToken cancellationToken) + { + ResourceGroupName = resourceGroupName; + Name = name; + Model = model; + CancellationToken = cancellationToken; + } + } +} diff --git a/src/ResourceManager/Common/Commands.Common.Strategies/EntityConfigExtensions.cs b/src/ResourceManager/Common/Commands.Common.Strategies/EntityConfigExtensions.cs new file mode 100644 index 000000000000..85c5df027a9f --- /dev/null +++ b/src/ResourceManager/Common/Commands.Common.Strategies/EntityConfigExtensions.cs @@ -0,0 +1,27 @@ +// ---------------------------------------------------------------------------------- +// +// 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.Collections.Generic; + +namespace Microsoft.Azure.Commands.Common.Strategies +{ + public static class EntityConfigExtensions + { + public static string IdToString(this IEnumerable id) + => "/" + string.Join("/", id); + + public static string DefaultIdStr(this IEntityConfig config) + => config.GetId(string.Empty).IdToString(); + } +} diff --git a/src/ResourceManager/Common/Commands.Common.Strategies/Extensions.cs b/src/ResourceManager/Common/Commands.Common.Strategies/Extensions.cs new file mode 100644 index 000000000000..d4f9fb81a3e1 --- /dev/null +++ b/src/ResourceManager/Common/Commands.Common.Strategies/Extensions.cs @@ -0,0 +1,41 @@ +// ---------------------------------------------------------------------------------- +// +// 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.Concurrent; +using System.Collections.Generic; +using System.Linq; + +namespace Microsoft.Azure.Commands.Common.Strategies +{ + public static class Extensions + { + public static IEnumerable EmptyIfNull(this IEnumerable value) + => value ?? Enumerable.Empty(); + + public static TValue GetOrNull( + this IDictionary dictionary, TKey key) + where TValue : class + { + TValue result; + dictionary.TryGetValue(key, out result); + return result; + } + + public static T GetOrAddWithCast( + this ConcurrentDictionary dictionary, TKey key, Func add) + where T : TBase + => (T)dictionary.GetOrAdd(key, _ => add()); + } +} diff --git a/src/ResourceManager/Common/Commands.Common.Strategies/GetAsyncParams.cs b/src/ResourceManager/Common/Commands.Common.Strategies/GetAsyncParams.cs new file mode 100644 index 000000000000..d6f934bb8d23 --- /dev/null +++ b/src/ResourceManager/Common/Commands.Common.Strategies/GetAsyncParams.cs @@ -0,0 +1,40 @@ +// ---------------------------------------------------------------------------------- +// +// 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.Threading; + +namespace Microsoft.Azure.Commands.Common.Strategies +{ + /// + /// Parameters for GetAsync functions. + /// + public sealed class GetAsyncParams + { + public string ResourceGroupName { get; } + + public string Name { get; } + + public CancellationToken CancellationToken { get; } + + public GetAsyncParams( + string resourceGroupName, + string name, + CancellationToken cancellationToken) + { + ResourceGroupName = resourceGroupName; + Name = name; + CancellationToken = cancellationToken; + } + } +} diff --git a/src/ResourceManager/Common/Commands.Common.Strategies/GetStateExtensions.cs b/src/ResourceManager/Common/Commands.Common.Strategies/GetStateExtensions.cs new file mode 100644 index 000000000000..81689a49b7e2 --- /dev/null +++ b/src/ResourceManager/Common/Commands.Common.Strategies/GetStateExtensions.cs @@ -0,0 +1,72 @@ +// ---------------------------------------------------------------------------------- +// +// 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.Linq; +using System.Threading; +using System.Threading.Tasks; + +namespace Microsoft.Azure.Commands.Common.Strategies +{ + public static class GetStateExtensions + { + /// + /// Returns a current Azure state for the given resource (config). + /// + /// + /// + /// + /// + /// + public static async Task GetStateAsync( + this ResourceConfig config, + IClient client, + CancellationToken cancellationToken) + where TModel : class + { + var context = new StateOperationContext(client, cancellationToken); + await context.GetStateAsyncDispatch(config); + return context.Result; + } + + static Task GetStateAsyncDispatch(this StateOperationContext context, IResourceConfig config) + => config.Accept(new GetStateAsyncVisitor(), context); + + static async Task GetStateAsync( + this StateOperationContext context, ResourceConfig config) + where TModel : class + => await context.GetOrAdd( + config, + async () => + { + var info = await config.GetAsync(context.Client, context.CancellationToken); + // Get state of dependencies if the resource doesn't exist + if (info == null) + { + var tasks = config + .GetResourceDependencies() + .Select(context.GetStateAsyncDispatch); + await Task.WhenAll(tasks); + } + return info; + }); + + sealed class GetStateAsyncVisitor : IResourceConfigVisitor + { + public Task Visit( + ResourceConfig config, StateOperationContext context) + where TModel : class + => context.GetStateAsync(config); + } + } +} diff --git a/src/ResourceManager/Common/Commands.Common.Strategies/IClient.cs b/src/ResourceManager/Common/Commands.Common.Strategies/IClient.cs new file mode 100644 index 000000000000..7e3a58416526 --- /dev/null +++ b/src/ResourceManager/Common/Commands.Common.Strategies/IClient.cs @@ -0,0 +1,24 @@ +// ---------------------------------------------------------------------------------- +// +// 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 Microsoft.Rest; + +namespace Microsoft.Azure.Commands.Common.Strategies +{ + public interface IClient + { + T GetClient() + where T : ServiceClient; + } +} diff --git a/src/ResourceManager/Common/Commands.Common.Strategies/IEntityConfig.cs b/src/ResourceManager/Common/Commands.Common.Strategies/IEntityConfig.cs new file mode 100644 index 000000000000..7092a440ebc4 --- /dev/null +++ b/src/ResourceManager/Common/Commands.Common.Strategies/IEntityConfig.cs @@ -0,0 +1,42 @@ +// ---------------------------------------------------------------------------------- +// +// 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.Collections.Generic; + +namespace Microsoft.Azure.Commands.Common.Strategies +{ + /// + /// Base interface for ResourceConfig and NestedResourceConfig. + /// + public interface IEntityConfig + { + IEntityStrategy Strategy { get; } + + string Name { get; } + + IResourceConfig Resource { get; } + + TResult Accept( + IEntityConfigVisitor visitor, TContext context); + + IEnumerable GetId(string subscription); + } + + public interface IEntityConfig : IEntityConfig + where TModel : class + { + TResult Accept( + IEntityConfigVisitor visitor, TContext context); + } +} diff --git a/src/ResourceManager/Common/Commands.Common.Strategies/IEntityConfigVisitor.cs b/src/ResourceManager/Common/Commands.Common.Strategies/IEntityConfigVisitor.cs new file mode 100644 index 000000000000..a275ab01dd86 --- /dev/null +++ b/src/ResourceManager/Common/Commands.Common.Strategies/IEntityConfigVisitor.cs @@ -0,0 +1,36 @@ +// ---------------------------------------------------------------------------------- +// +// 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. +// ---------------------------------------------------------------------------------- + +namespace Microsoft.Azure.Commands.Common.Strategies +{ + public interface IEntityConfigVisitor + { + TResult Visit(ResourceConfig config, TContext context) + where TModel : class; + + TResult Visit( + NestedResourceConfig config, TContext context) + where TModel : class + where TParentModel : class; + } + + public interface IEntityConfigVisitor + where TModel : class + { + TResult Visit(ResourceConfig config, TContext context); + + TResult Visit(NestedResourceConfig config, TContext context) + where TParentModel : class; + } +} diff --git a/src/ResourceManager/Common/Commands.Common.Strategies/IEntityStrategy.cs b/src/ResourceManager/Common/Commands.Common.Strategies/IEntityStrategy.cs new file mode 100644 index 000000000000..84f1e199668d --- /dev/null +++ b/src/ResourceManager/Common/Commands.Common.Strategies/IEntityStrategy.cs @@ -0,0 +1,23 @@ +// ---------------------------------------------------------------------------------- +// +// 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. +// ---------------------------------------------------------------------------------- + +namespace Microsoft.Azure.Commands.Common.Strategies +{ + /// + /// Base interface for ResourceStrategy and NestedResourceStrategy classes. + /// + public interface IEntityStrategy + { + } +} diff --git a/src/ResourceManager/Common/Commands.Common.Strategies/IReportProgress.cs b/src/ResourceManager/Common/Commands.Common.Strategies/IReportProgress.cs new file mode 100644 index 000000000000..9090a5391843 --- /dev/null +++ b/src/ResourceManager/Common/Commands.Common.Strategies/IReportProgress.cs @@ -0,0 +1,25 @@ +// ---------------------------------------------------------------------------------- +// +// 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. +// ---------------------------------------------------------------------------------- + +namespace Microsoft.Azure.Commands.Common.Strategies +{ + public interface IProgressReport + { + void Start(ResourceConfig config) + where TModel : class; + + void Done(ResourceConfig config, double progress) + where TModel : class; + } +} diff --git a/src/ResourceManager/Common/Commands.Common.Strategies/IResourceConfig.cs b/src/ResourceManager/Common/Commands.Common.Strategies/IResourceConfig.cs new file mode 100644 index 000000000000..657d9069b413 --- /dev/null +++ b/src/ResourceManager/Common/Commands.Common.Strategies/IResourceConfig.cs @@ -0,0 +1,33 @@ +// ---------------------------------------------------------------------------------- +// +// 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.Collections.Generic; + +namespace Microsoft.Azure.Commands.Common.Strategies +{ + /// + /// Base interface for ResourceConfig[]. + /// + public interface IResourceConfig : IEntityConfig + { + new IResourceStrategy Strategy { get; } + + string ResourceGroupName { get; } + + IEnumerable Dependencies { get; } + + TResult Accept( + IResourceConfigVisitor visitor, TContext context); + } +} diff --git a/src/ResourceManager/Common/Commands.Common.Strategies/IResourceConfigVisitor.cs b/src/ResourceManager/Common/Commands.Common.Strategies/IResourceConfigVisitor.cs new file mode 100644 index 000000000000..a837efdd355b --- /dev/null +++ b/src/ResourceManager/Common/Commands.Common.Strategies/IResourceConfigVisitor.cs @@ -0,0 +1,22 @@ +// ---------------------------------------------------------------------------------- +// +// 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. +// ---------------------------------------------------------------------------------- + +namespace Microsoft.Azure.Commands.Common.Strategies +{ + public interface IResourceConfigVisitor + { + TResult Visit(ResourceConfig config, TContext context) + where TModel : class; + } +} diff --git a/src/ResourceManager/Common/Commands.Common.Strategies/IResourceStrategy.cs b/src/ResourceManager/Common/Commands.Common.Strategies/IResourceStrategy.cs new file mode 100644 index 000000000000..8898e119c6c6 --- /dev/null +++ b/src/ResourceManager/Common/Commands.Common.Strategies/IResourceStrategy.cs @@ -0,0 +1,10 @@ +namespace Microsoft.Azure.Commands.Common.Strategies +{ + /// + /// Base interface for ResourceStrategy[]. + /// + public interface IResourceStrategy : IEntityStrategy + { + string Type { get; } + } +} diff --git a/src/ResourceManager/Common/Commands.Common.Strategies/IShouldProcess.cs b/src/ResourceManager/Common/Commands.Common.Strategies/IShouldProcess.cs new file mode 100644 index 000000000000..423c1d22bc1a --- /dev/null +++ b/src/ResourceManager/Common/Commands.Common.Strategies/IShouldProcess.cs @@ -0,0 +1,24 @@ +// ---------------------------------------------------------------------------------- +// +// 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.Threading.Tasks; + +namespace Microsoft.Azure.Commands.Common.Strategies +{ + public interface IShouldProcess + { + Task ShouldCreate(ResourceConfig config, TModel model) + where TModel : class; + } +} diff --git a/src/ResourceManager/Common/Commands.Common.Strategies/IState.cs b/src/ResourceManager/Common/Commands.Common.Strategies/IState.cs new file mode 100644 index 000000000000..8179a8e7e5df --- /dev/null +++ b/src/ResourceManager/Common/Commands.Common.Strategies/IState.cs @@ -0,0 +1,27 @@ +// ---------------------------------------------------------------------------------- +// +// 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. +// ---------------------------------------------------------------------------------- + +namespace Microsoft.Azure.Commands.Common.Strategies +{ + /// + /// An Azure state which is a dictionary of models. + /// + public interface IState + { + TModel Get(ResourceConfig config) + where TModel : class; + + bool Contains(IResourceConfig config); + } +} diff --git a/src/ResourceManager/Common/Commands.Common.Strategies/LocationExtensions.cs b/src/ResourceManager/Common/Commands.Common.Strategies/LocationExtensions.cs new file mode 100644 index 000000000000..8397d327a3e0 --- /dev/null +++ b/src/ResourceManager/Common/Commands.Common.Strategies/LocationExtensions.cs @@ -0,0 +1,88 @@ +// ---------------------------------------------------------------------------------- +// +// 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.Linq; + +namespace Microsoft.Azure.Commands.Common.Strategies +{ + public static class LocationExtensions + { + /// + /// Get the best location for the given entity from the given state. + /// + /// + /// + /// + public static string GetLocation(this IState state, IResourceConfig config) + => state.GetDependencyLocationDispatch(config)?.Location; + + static DependencyLocation GetDependencyLocationDispatch(this IState state, IResourceConfig config) + => config.Accept(new GetDependencyLocationVisitor(), state); + + static DependencyLocation GetDependencyLocation( + this IState state, ResourceConfig config) + where TModel : class + { + var info = state.Get(config); + return info != null + ? new DependencyLocation( + config.Strategy.GetLocation(info), + config.Strategy.CompulsoryLocation) + : config + .GetResourceDependencies() + .Select(state.GetDependencyLocationDispatch) + .Aggregate(null as DependencyLocation, Merge); + } + + sealed class GetDependencyLocationVisitor : IResourceConfigVisitor + { + public DependencyLocation Visit(ResourceConfig config, IState state) + where TModel : class + => state.GetDependencyLocation(config); + } + + sealed class DependencyLocation + { + public string Location { get; } + + public bool IsCompulsory { get; } + + public DependencyLocation(string location, bool isCompulsory) + { + Location = location; + IsCompulsory = isCompulsory; + } + } + + static DependencyLocation Merge(this DependencyLocation a, DependencyLocation b) + { + if (a == null) + { + return b; + } + if (b == null) + { + return a; + } + + if (a.IsCompulsory != b.IsCompulsory) + { + return a.IsCompulsory ? b : a; + } + + // a.IsCompulsory == b.IsCompulsory + return a.Location == b.Location ? a : new DependencyLocation(null, a.IsCompulsory); + } + } +} diff --git a/src/ResourceManager/Common/Commands.Common.Strategies/MSSharedLibKey.snk b/src/ResourceManager/Common/Commands.Common.Strategies/MSSharedLibKey.snk new file mode 100644 index 000000000000..695f1b38774e Binary files /dev/null and b/src/ResourceManager/Common/Commands.Common.Strategies/MSSharedLibKey.snk differ diff --git a/src/ResourceManager/Common/Commands.Common.Strategies/NestedResourceConfig.cs b/src/ResourceManager/Common/Commands.Common.Strategies/NestedResourceConfig.cs new file mode 100644 index 000000000000..90819a81e97d --- /dev/null +++ b/src/ResourceManager/Common/Commands.Common.Strategies/NestedResourceConfig.cs @@ -0,0 +1,68 @@ +// ---------------------------------------------------------------------------------- +// +// 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.Generic; +using System.Linq; + +namespace Microsoft.Azure.Commands.Common.Strategies +{ + /// + /// Nested resource configuration. Fro example, Subnet. + /// + /// + /// + public sealed class NestedResourceConfig : IEntityConfig + where TModel : class + where TParenModel : class + { + public NestedResourceStrategy Strategy { get; } + + public string Name { get; } + + /// + /// Parent. For example, VirtualNetwork is a parent of Subnet. + /// + public IEntityConfig Parent { get; } + + public Func CreateModel { get; } + + public IResourceConfig Resource => Parent.Resource; + + IEntityStrategy IEntityConfig.Strategy => Strategy; + + public NestedResourceConfig( + NestedResourceStrategy strategy, + IEntityConfig parent, + string name, + Func createModel) + { + Strategy = strategy; + Name = name; + Parent = parent; + CreateModel = createModel; + } + + public IEnumerable GetId(string subscription) + => Parent.GetId(subscription).Concat(Strategy.GetId(Name)); + + TResult IEntityConfig.Accept( + IEntityConfigVisitor visitor, TContext context) + => visitor.Visit(this, context); + + TResult IEntityConfig.Accept( + IEntityConfigVisitor visitor, TContext context) + => visitor.Visit(this, context); + } +} diff --git a/src/ResourceManager/Common/Commands.Common.Strategies/NestedResourceConfigExtensions.cs b/src/ResourceManager/Common/Commands.Common.Strategies/NestedResourceConfigExtensions.cs new file mode 100644 index 000000000000..2c762667068a --- /dev/null +++ b/src/ResourceManager/Common/Commands.Common.Strategies/NestedResourceConfigExtensions.cs @@ -0,0 +1,30 @@ +// ---------------------------------------------------------------------------------- +// +// 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; + +namespace Microsoft.Azure.Commands.Common.Strategies +{ + public static class NestedResourceConfigExtensions + { + public static NestedResourceConfig CreateConfig( + this NestedResourceStrategy strategy, + IEntityConfig parent, + string name, + Func createModel) + where TModel : class + where TParenModel : class + => new NestedResourceConfig(strategy, parent, name, createModel); + } +} diff --git a/src/ResourceManager/Common/Commands.Common.Strategies/NestedResourceStrategy.cs b/src/ResourceManager/Common/Commands.Common.Strategies/NestedResourceStrategy.cs new file mode 100644 index 000000000000..8c559f2f547a --- /dev/null +++ b/src/ResourceManager/Common/Commands.Common.Strategies/NestedResourceStrategy.cs @@ -0,0 +1,52 @@ +// ---------------------------------------------------------------------------------- +// +// 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.Generic; + +namespace Microsoft.Azure.Commands.Common.Strategies +{ + public sealed class NestedResourceStrategy : IEntityStrategy + { + public Func> GetId { get; } + + public Func Get { get; } + + public Action CreateOrUpdate { get; } + + public NestedResourceStrategy( + Func> getId, + Func get, + Action createOrUpdate) + { + GetId = getId; + Get = get; + CreateOrUpdate = createOrUpdate; + } + } + + public static class NestedResourceStrategy + { + public static NestedResourceStrategy Create( + string header, + Func get, + Action createOrUpdate) + where TModel : class + where TParentModel : class + => new NestedResourceStrategy( + name => new[] { header, name}, + get, + createOrUpdate); + } +} diff --git a/src/ResourceManager/Common/Commands.Common.Strategies/ProgressMap.cs b/src/ResourceManager/Common/Commands.Common.Strategies/ProgressMap.cs new file mode 100644 index 000000000000..043f4e52c729 --- /dev/null +++ b/src/ResourceManager/Common/Commands.Common.Strategies/ProgressMap.cs @@ -0,0 +1,45 @@ +// ---------------------------------------------------------------------------------- +// +// 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.Generic; + +namespace Microsoft.Azure.Commands.Common.Strategies +{ + public class ProgressMap + { + readonly Dictionary> _Map + = new Dictionary>(); + + readonly int _Duration; + + public ProgressMap(Dictionary> map, int duration) + { + _Map = map; + _Duration = duration; + } + + /// + /// Returns a value of [0..1] range which is used to increment a progress bar when the + /// resource is created. + /// + /// a resource configuration. + /// + public double Get(IResourceConfig config) + { + var x = _Map.GetOrNull(config); + return x.Item1.GetTaskProgress(x.Item2) / _Duration; + } + } +} diff --git a/src/ResourceManager/Common/Commands.Common.Strategies/Properties/AssemblyInfo.cs b/src/ResourceManager/Common/Commands.Common.Strategies/Properties/AssemblyInfo.cs new file mode 100644 index 000000000000..338b456f0726 --- /dev/null +++ b/src/ResourceManager/Common/Commands.Common.Strategies/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Commands.Common.Strategies")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("Commands.Common.Strategies")] +[assembly: AssemblyCopyright("Copyright © 2017")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("eea69772-d41b-482a-9252-2b4595c59e53")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/src/ResourceManager/Common/Commands.Common.Strategies/ResourceConfig.cs b/src/ResourceManager/Common/Commands.Common.Strategies/ResourceConfig.cs new file mode 100644 index 000000000000..c2addb183ba1 --- /dev/null +++ b/src/ResourceManager/Common/Commands.Common.Strategies/ResourceConfig.cs @@ -0,0 +1,81 @@ +// ---------------------------------------------------------------------------------- +// +// 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.Generic; +using System.Linq; + +namespace Microsoft.Azure.Commands.Common.Strategies +{ + /// + /// Resource configuration. It contains information to create a resource, + /// including name, resource group name, dependencies, model creation function, etc. + /// + /// + public sealed class ResourceConfig : IEntityConfig, IResourceConfig + where TModel : class + { + public ResourceStrategy Strategy { get; } + + public string ResourceGroupName { get; } + + public string Name { get; } + + public Func CreateModel { get; } + + public IEnumerable Dependencies { get; } + + IEntityStrategy IEntityConfig.Strategy => Strategy; + + IResourceStrategy IResourceConfig.Strategy => Strategy; + + IResourceConfig IEntityConfig.Resource => this; + + public ResourceConfig( + ResourceStrategy strategy, + string resourceGroupName, + string name, + Func createModel, + IEnumerable dependencies) + { + Strategy = strategy; + ResourceGroupName = resourceGroupName; + Name = name; + CreateModel = createModel; + Dependencies = dependencies; + } + + public IEnumerable GetId(string subscription) + => new[] + { + "subscriptions", + subscription, + "resourceGroups", + ResourceGroupName + } + .Concat(Strategy.GetId(Name)); + + TResult IEntityConfig.Accept( + IEntityConfigVisitor visitor, TContext context) + => visitor.Visit(this, context); + + TResult IEntityConfig.Accept( + IEntityConfigVisitor visitor, TContext context) + => visitor.Visit(this, context); + + TResult IResourceConfig.Accept( + IResourceConfigVisitor visitor, TContext context) + => visitor.Visit(this, context); + } +} diff --git a/src/ResourceManager/Common/Commands.Common.Strategies/ResourceConfigExtensions.cs b/src/ResourceManager/Common/Commands.Common.Strategies/ResourceConfigExtensions.cs new file mode 100644 index 000000000000..de590730bde2 --- /dev/null +++ b/src/ResourceManager/Common/Commands.Common.Strategies/ResourceConfigExtensions.cs @@ -0,0 +1,78 @@ +// ---------------------------------------------------------------------------------- +// +// 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 Microsoft.Rest.Azure; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Threading; +using System.Threading.Tasks; + +namespace Microsoft.Azure.Commands.Common.Strategies +{ + public static class ResourceConfigExtensions + { + public static ResourceConfig CreateConfig( + this ResourceStrategy strategy, + string resourceGroupName, + string name, + Func createModel = null, + IEnumerable dependencies = null) + where TModel : class, new() + => new ResourceConfig( + strategy, + resourceGroupName, + name, + createModel ?? (_ => new TModel()), + dependencies.EmptyIfNull()); + + public static async Task GetAsync( + this ResourceConfig config, + IClient client, + CancellationToken cancellationToken) + where TModel : class + { + try + { + return await config.Strategy.GetAsync( + client, + new GetAsyncParams(config.ResourceGroupName, config.Name, cancellationToken)); + } + catch (CloudException e) + when (e.Response.StatusCode == HttpStatusCode.NotFound) + { + return null; + } + } + + public static Task CreateOrUpdateAsync( + this ResourceConfig config, + IClient client, + TModel model, + CancellationToken cancellationToken) + where TModel : class + => config.Strategy.CreateOrUpdateAsync( + client, + CreateOrUpdateAsyncParams.Create( + config.ResourceGroupName, + config.Name, + model, + cancellationToken)); + + public static IEnumerable GetResourceDependencies( + this IResourceConfig config) + => config.Dependencies.Select(d => d.Resource); + } +} diff --git a/src/ResourceManager/Common/Commands.Common.Strategies/ResourceStrategy.cs b/src/ResourceManager/Common/Commands.Common.Strategies/ResourceStrategy.cs new file mode 100644 index 000000000000..28768d51b15f --- /dev/null +++ b/src/ResourceManager/Common/Commands.Common.Strategies/ResourceStrategy.cs @@ -0,0 +1,111 @@ +// ---------------------------------------------------------------------------------- +// +// 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 Microsoft.Rest; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace Microsoft.Azure.Commands.Common.Strategies +{ + public sealed class ResourceStrategy : IResourceStrategy + { + public string Type { get; } + + public Func> GetId { get; } + + public Func> GetAsync { get; } + + public Func, Task> CreateOrUpdateAsync + { get; } + + public Func GetLocation { get; } + + public Action SetLocation { get; } + + public Func CreateTime { get; } + + public bool CompulsoryLocation { get; } + + public ResourceStrategy( + string type, + Func> getId, + Func> getAsync, + Func, Task> createOrUpdateAsync, + Func getLocation, + Action setLocation, + Func createTime, + bool compulsoryLocation) + { + Type = type; + GetId = getId; + GetAsync = getAsync; + CreateOrUpdateAsync = createOrUpdateAsync; + GetLocation = getLocation; + SetLocation = setLocation; + CreateTime = createTime; + CompulsoryLocation = compulsoryLocation; + } + } + + public static class ResourceStrategy + { + public static ResourceStrategy Create( + string type, + Func> getId, + Func getOperations, + Func> getAsync, + Func, Task> createOrUpdateAsync, + Func getLocation, + Action setLocation, + Func createTime, + bool compulsoryLocation) + where TClient : ServiceClient + { + Func toOperations = client => getOperations(client.GetClient()); + return new ResourceStrategy( + type, + getId, + (client, p) => getAsync(toOperations(client), p), + (client, p) => createOrUpdateAsync(toOperations(client), p), + getLocation, + setLocation, + createTime, + compulsoryLocation); + } + + public static ResourceStrategy Create( + string type, + IEnumerable headers, + Func getOperations, + Func> getAsync, + Func, Task> createOrUpdateAsync, + Func getLocation, + Action setLocation, + Func createTime, + bool compulsoryLocation) + where TClient : ServiceClient + => Create( + type, + name => new[] { "providers" }.Concat(headers).Concat(new[] { name }), + getOperations, + getAsync, + createOrUpdateAsync, + getLocation, + setLocation, + createTime, + compulsoryLocation); + } +} diff --git a/src/ResourceManager/Common/Commands.Common.Strategies/State.cs b/src/ResourceManager/Common/Commands.Common.Strategies/State.cs new file mode 100644 index 000000000000..744934980e0a --- /dev/null +++ b/src/ResourceManager/Common/Commands.Common.Strategies/State.cs @@ -0,0 +1,39 @@ +// ---------------------------------------------------------------------------------- +// +// 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.Concurrent; + +namespace Microsoft.Azure.Commands.Common.Strategies +{ + /// + /// Azure State. It contains information (models) of Azure resources. + /// + sealed class State : IState + { + readonly ConcurrentDictionary _Map + = new ConcurrentDictionary(); + + public TModel Get(ResourceConfig config) + where TModel : class + => _Map.GetOrNull(config.DefaultIdStr()) as TModel; + + public TModel GetOrAdd(ResourceConfig config, Func f) + where TModel : class + => _Map.GetOrAddWithCast(config.DefaultIdStr(), f); + + public bool Contains(IResourceConfig config) + => _Map.ContainsKey(config.DefaultIdStr()); + } +} diff --git a/src/ResourceManager/Common/Commands.Common.Strategies/StateExtensions.cs b/src/ResourceManager/Common/Commands.Common.Strategies/StateExtensions.cs new file mode 100644 index 000000000000..17d584040d7c --- /dev/null +++ b/src/ResourceManager/Common/Commands.Common.Strategies/StateExtensions.cs @@ -0,0 +1,82 @@ +// ---------------------------------------------------------------------------------- +// +// 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. +// ---------------------------------------------------------------------------------- + +namespace Microsoft.Azure.Commands.Common.Strategies +{ + public static class StateExtensions + { + /// + /// Get a model of the given nested resource config from the given state. + /// + /// + /// + /// + /// + /// + public static TModel Get( + this IState state, NestedResourceConfig config) + where TModel : class + where TParentModel : class + { + var parentModel = state.GetDispatch(config.Parent); + return parentModel == null ? null : config.Strategy.Get(parentModel, config.Name); + } + + /// + /// Get a model of the given entity model from the given state. + /// + /// + /// + /// + /// + public static TModel GetDispatch( + this IState state, IEntityConfig config) + where TModel : class + => config.Accept(new GetVisitor(), state); + + public static bool Contains( + this IState state, NestedResourceConfig config) + where TModel : class + where TParentModel : class + => state.Get(config) != null; + + public static bool ContainsDispatch(this IState state, IEntityConfig config) + => config.Accept(new ContainsDispatchVisitor(), state); + + sealed class GetVisitor : IEntityConfigVisitor + where TModel : class + { + public TModel Visit(ResourceConfig config, IState state) + => state.Get(config); + + public TModel Visit( + NestedResourceConfig config, IState state) + where TParentModel : class + => state.Get(config); + } + + sealed class ContainsDispatchVisitor : IEntityConfigVisitor + { + public bool Visit(ResourceConfig config, IState context) + where TModel : class + => context.Contains(config); + + public bool Visit( + NestedResourceConfig config, IState context) + where TModel : class + where TParentModel : class + => context.Contains(config); + } + } +} diff --git a/src/ResourceManager/Common/Commands.Common.Strategies/StateOperationContext.cs b/src/ResourceManager/Common/Commands.Common.Strategies/StateOperationContext.cs new file mode 100644 index 000000000000..d6d34a5b9418 --- /dev/null +++ b/src/ResourceManager/Common/Commands.Common.Strategies/StateOperationContext.cs @@ -0,0 +1,60 @@ +// ---------------------------------------------------------------------------------- +// +// 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.Concurrent; +using System.Threading; +using System.Threading.Tasks; + +namespace Microsoft.Azure.Commands.Common.Strategies +{ + /// + /// Context for asyncronous operations, such as GetAsync or CreateOrUpdateAsync. + /// + public sealed class StateOperationContext + { + public IClient Client { get; } + + public CancellationToken CancellationToken { get; } + + public IState Result => _Result; + + readonly State _Result = new State(); + + readonly ConcurrentDictionary _TaskMap + = new ConcurrentDictionary(); + + public StateOperationContext(IClient client, CancellationToken cancellationToken) + { + Client = client; + CancellationToken = cancellationToken; + } + + public async Task GetOrAdd( + ResourceConfig config, Func> operation) + where TModel : class + => await _TaskMap.GetOrAddWithCast( + config.DefaultIdStr(), + async () => + { + var model = await operation(); + if (model != null) + { + // add the operation result to a result. + _Result.GetOrAdd(config, () => model); + } + return model; + }); + } +} diff --git a/src/ResourceManager/Common/Commands.Common.Strategies/SyncTaskScheduler.cs b/src/ResourceManager/Common/Commands.Common.Strategies/SyncTaskScheduler.cs new file mode 100644 index 000000000000..67989355cef3 --- /dev/null +++ b/src/ResourceManager/Common/Commands.Common.Strategies/SyncTaskScheduler.cs @@ -0,0 +1,65 @@ +// ---------------------------------------------------------------------------------- +// +// 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.Concurrent; +using System.Threading; +using System.Threading.Tasks; + +namespace Microsoft.Azure.Commands.Common.Strategies +{ + public sealed class SyncTaskScheduler + { + readonly ConcurrentQueue _Tasks = new ConcurrentQueue(); + + public async Task Invoke(Func func) + { + var task = new Task(func); + _Tasks.Enqueue(task); + // note: don't use 'await' keyword for the 'task' because it may start the task in + // another thread. + while (!task.IsCompleted) + { + await Task.Yield(); + } + return task.Result; + } + + public void BeginInvoke(Action action) + => _Tasks.Enqueue(new Task(action)); + + public void Wait(Task task) + { + while (!task.IsCompleted) + { + HandleActions(); + Thread.Yield(); + } + HandleActions(); + if (task.IsFaulted) + { + throw task.Exception.InnerException; + } + } + + void HandleActions() + { + Task task; + while (_Tasks.TryDequeue(out task)) + { + task.RunSynchronously(); + } + } + } +} diff --git a/src/ResourceManager/Common/Commands.Common.Strategies/TargetStateExtensions.cs b/src/ResourceManager/Common/Commands.Common.Strategies/TargetStateExtensions.cs new file mode 100644 index 000000000000..1de0ab970582 --- /dev/null +++ b/src/ResourceManager/Common/Commands.Common.Strategies/TargetStateExtensions.cs @@ -0,0 +1,125 @@ +// ---------------------------------------------------------------------------------- +// +// 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.Collections.Generic; +using System.Linq; + +namespace Microsoft.Azure.Commands.Common.Strategies +{ + public static class TargetStateExtensions + { + public static IEnumerable GetTargetDependencies( + this IResourceConfig config, IState target) + => config.GetResourceDependencies().Where(target.Contains); + + public static IState GetTargetState( + this ResourceConfig config, + IState current, + string subscription, + string location) + where TModel : class + { + var context = new Context(current, subscription, location); + context.AddIfRequired(config); + return context.Target; + } + + sealed class Context + { + public State Target { get; } = new State(); + + public IState Current { get; } + + public string Subscription { get; } + + public string Location { get; } + + public Context(IState current, string subscription, string location) + { + Current = current; + Subscription = subscription; + Location = location; + } + + public void AddIfRequired(IEntityConfig config) + { + if (!Current.ContainsDispatch(config)) + { + config.Accept(new AddVisitor(), this); + } + } + + public TModel GetOrAdd(ResourceConfig config) + where TModel : class + => Target.GetOrAdd( + config, + () => + { + foreach (var dependency in config.Dependencies) + { + AddIfRequired(dependency); + } + var model = config.CreateModel(Subscription); + config.Strategy.SetLocation(model, Location); + return model; + }); + + public TModel GetOrAdd( + NestedResourceConfig config) + where TModel : class + where TParentModel : class + { + var parentModel = config.Parent.Accept(new GetOrAddVisitor(), this); + var model = config.Strategy.Get(parentModel, config.Name); + if (model == null) + { + model = config.CreateModel(); + config.Strategy.CreateOrUpdate(parentModel, config.Name, model); + } + return model; + } + } + + sealed class AddVisitor : IEntityConfigVisitor + { + public Void Visit(ResourceConfig config, Context context) + where TModel : class + { + context.GetOrAdd(config); + return new Void(); + } + + public Void Visit( + NestedResourceConfig config, Context context) + where TModel : class + where TParentModel : class + { + context.GetOrAdd(config); + return new Void(); + } + } + + sealed class GetOrAddVisitor : IEntityConfigVisitor + where TModel : class + { + public TModel Visit(ResourceConfig config, Context context) + => context.GetOrAdd(config); + + public TModel Visit( + NestedResourceConfig config, Context context) + where TParenModel : class + => context.GetOrAdd(config); + } + } +} diff --git a/src/ResourceManager/Common/Commands.Common.Strategies/TimeSlot.cs b/src/ResourceManager/Common/Commands.Common.Strategies/TimeSlot.cs new file mode 100644 index 000000000000..57310436f72a --- /dev/null +++ b/src/ResourceManager/Common/Commands.Common.Strategies/TimeSlot.cs @@ -0,0 +1,80 @@ +// ---------------------------------------------------------------------------------- +// +// 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. +// ---------------------------------------------------------------------------------- + +namespace Microsoft.Azure.Commands.Common.Strategies +{ + /// + /// TimeSlot is a node of a singly linked list of TimeSlots. + /// The last node of the list is always an empty time slot: + /// - Duration = 0. + /// - Next = null. + /// - TaskCount = 0. + /// + public sealed class TimeSlot + { + public int Duration { get; private set; } + + public int TaskCount { get; private set; } + + public TimeSlot Next { get; private set; } + + public bool IsLast => Next == null; + + public TimeSlot() : this(0, 0, null) { } + + TimeSlot(int duration, int taskCount, TimeSlot next) + { + Duration = duration; + TaskCount = taskCount; + Next = next; + } + + public TimeSlot AddTask(int duration) + { + if (duration <= 0) + { + return this; + } + else if (IsLast) + { + Next = new TimeSlot(); + Duration = duration; + TaskCount = 1; + return Next; + } + else if (duration < Duration) + { + Next = new TimeSlot(Duration - duration, TaskCount, Next); + Duration = duration; + TaskCount++; + return Next; + } + else // if (Duration <= duration) + { + TaskCount++; + return Next.AddTask(duration - Duration); + } + } + + public double GetTaskProgress(int duration) + => IsLast + ? duration + : duration <= Duration + ? GetTimeSlotProgress(duration) + : GetTimeSlotProgress(Duration) + Next.GetTaskProgress(duration - Duration); + + double GetTimeSlotProgress(double duration) + => duration / TaskCount; + } +} diff --git a/src/ResourceManager/Common/Commands.Common.Strategies/TimeSlotExtensions.cs b/src/ResourceManager/Common/Commands.Common.Strategies/TimeSlotExtensions.cs new file mode 100644 index 000000000000..f979cb98cd4a --- /dev/null +++ b/src/ResourceManager/Common/Commands.Common.Strategies/TimeSlotExtensions.cs @@ -0,0 +1,80 @@ +// ---------------------------------------------------------------------------------- +// +// 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.Concurrent; +using System.Collections.Generic; +using System.Linq; + +namespace Microsoft.Azure.Commands.Common.Strategies +{ + public static class TimeSlotExtensions + { + public static ProgressMap GetProgressMap( + this ResourceConfig config, IState state) + where TModel : class + { + var context = new Context(state); + var duration = context.GetTimeSlotAndDuration(config).Item2; + return new ProgressMap(context.BeginMap, duration); + } + + sealed class Context + { + public TimeSlot Begin { get; } = new TimeSlot(); + + public Dictionary> BeginMap { get; } + = new Dictionary>(); + + readonly IState _State; + + readonly ConcurrentDictionary> _Map + = new ConcurrentDictionary>(); + + public Context(IState state) + { + _State = state; + } + + public Tuple GetTimeSlotAndDuration( + ResourceConfig config) + where TModel : class + => _Map.GetOrAdd( + config, + _ => + { + var tupleBegin = config + .GetTargetDependencies(_State) + .Select(GetTimeSlotAndDurationDispatch) + .Aggregate(Tuple.Create(Begin, 0), (a, b) => a.Item2 > b.Item2 ? a : b); + var duration = config.Strategy.CreateTime(_State.Get(config)); + BeginMap.Add(config, Tuple.Create(tupleBegin.Item1, duration)); + return Tuple.Create( + tupleBegin.Item1.AddTask(duration), tupleBegin.Item2 + duration); + }); + + Tuple GetTimeSlotAndDurationDispatch(IResourceConfig config) + => config.Accept(new GetTimeSlotAndDurationVisitor(), this); + } + + sealed class GetTimeSlotAndDurationVisitor : + IResourceConfigVisitor> + { + public Tuple Visit( + ResourceConfig config, Context context) + where TModel : class + => context.GetTimeSlotAndDuration(config); + } + } +} diff --git a/src/ResourceManager/Common/Commands.Common.Strategies/UpdateStateExtensions.cs b/src/ResourceManager/Common/Commands.Common.Strategies/UpdateStateExtensions.cs new file mode 100644 index 000000000000..0828473d826e --- /dev/null +++ b/src/ResourceManager/Common/Commands.Common.Strategies/UpdateStateExtensions.cs @@ -0,0 +1,115 @@ +// ---------------------------------------------------------------------------------- +// +// 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.Linq; +using System.Threading; +using System.Threading.Tasks; + +namespace Microsoft.Azure.Commands.Common.Strategies +{ + public static class UpdateStateExtensions + { + public static async Task UpdateStateAsync( + this ResourceConfig config, + IClient client, + IState target, + CancellationToken cancellationToken, + IShouldProcess shouldProcess, + IProgressReport progressReport) + where TModel : class + { + var context = new Context( + new StateOperationContext(client, cancellationToken), + target, + shouldProcess, + progressReport, + config.GetProgressMap(target)); + await context.UpdateStateAsync(config); + return context.Result; + } + + sealed class Context + { + public IState Result => _OperationContext.Result; + + readonly StateOperationContext _OperationContext; + + readonly IState _Target; + + readonly IShouldProcess _ShouldProcess; + + readonly IProgressReport _ProgressReport; + + readonly ProgressMap _ProgressMap; + + public Context( + StateOperationContext operationContext, + IState target, + IShouldProcess shouldProcess, + IProgressReport progressReport, + ProgressMap progressMap) + { + _OperationContext = operationContext; + _Target = target; + _ShouldProcess = shouldProcess; + _ProgressReport = progressReport; + _ProgressMap = progressMap; + } + + public async Task UpdateStateAsync(ResourceConfig config) + where TModel : class + { + var model = _Target.Get(config); + if (model != null) + { + await _OperationContext.GetOrAdd( + config, + async () => + { + // wait for all dependencies + var tasks = config + .GetResourceDependencies() + .Select(UpdateStateAsyncDispatch); + await Task.WhenAll(tasks); + // call the CreateOrUpdateAsync function for the resource. + if (await _ShouldProcess.ShouldCreate(config, model)) + { + _ProgressReport.Start(config); + var result = await config.CreateOrUpdateAsync( + _OperationContext.Client, + model, + _OperationContext.CancellationToken); + _ProgressReport.Done(config, _ProgressMap.Get(config)); + return result; + } + else + { + return null; + } + }); + } + } + + public Task UpdateStateAsyncDispatch(IResourceConfig config) + => config.Accept(new UpdateStateAsyncVisitor(), this); + } + + sealed class UpdateStateAsyncVisitor : IResourceConfigVisitor + { + public Task Visit(ResourceConfig config, Context context) + where TModel : class + => context.UpdateStateAsync(config); + } + } +} diff --git a/src/ResourceManager/Common/Commands.Common.Strategies/Void.cs b/src/ResourceManager/Common/Commands.Common.Strategies/Void.cs new file mode 100644 index 000000000000..403652f72153 --- /dev/null +++ b/src/ResourceManager/Common/Commands.Common.Strategies/Void.cs @@ -0,0 +1,20 @@ +// ---------------------------------------------------------------------------------- +// +// 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. +// ---------------------------------------------------------------------------------- + +namespace Microsoft.Azure.Commands.Common.Strategies +{ + public struct Void + { + } +} diff --git a/src/ResourceManager/Common/Commands.Common.Strategies/packages.config b/src/ResourceManager/Common/Commands.Common.Strategies/packages.config new file mode 100644 index 000000000000..c658da2ce8bb --- /dev/null +++ b/src/ResourceManager/Common/Commands.Common.Strategies/packages.config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/src/ResourceManager/Compute/AzureRM.Compute.psd1 b/src/ResourceManager/Compute/AzureRM.Compute.psd1 index ee0d5d1ebe8e..f4c0c8aebedc 100644 --- a/src/ResourceManager/Compute/AzureRM.Compute.psd1 +++ b/src/ResourceManager/Compute/AzureRM.Compute.psd1 @@ -64,7 +64,8 @@ RequiredAssemblies = '.\AutoMapper.dll', '.\Microsoft.WindowsAzure.Commands.Sync.dll', '.\Microsoft.WindowsAzure.Commands.Tools.Vhd.dll', '.\Microsoft.WindowsAzure.Storage.dll', - '.\System.Spatial.dll' + '.\System.Spatial.dll', + '.\Microsoft.Azure.Commands.Common.Strategies.dll' # Script files (.ps1) that are run in the caller's environment prior to importing this module. # ScriptsToProcess = @() diff --git a/src/ResourceManager/Compute/Commands.Compute/Commands.Compute.Netcore.csproj b/src/ResourceManager/Compute/Commands.Compute/Commands.Compute.Netcore.csproj index bec57e10c5d8..6234e13a0cf3 100644 --- a/src/ResourceManager/Compute/Commands.Compute/Commands.Compute.Netcore.csproj +++ b/src/ResourceManager/Compute/Commands.Compute/Commands.Compute.Netcore.csproj @@ -140,6 +140,7 @@ + diff --git a/src/ResourceManager/Compute/Commands.Compute/Commands.Compute.csproj b/src/ResourceManager/Compute/Commands.Compute/Commands.Compute.csproj index bffe3c5fef00..acc1d845e369 100644 --- a/src/ResourceManager/Compute/Commands.Compute/Commands.Compute.csproj +++ b/src/ResourceManager/Compute/Commands.Compute/Commands.Compute.csproj @@ -64,6 +64,14 @@ ..\..\..\packages\Microsoft.Azure.Management.KeyVault.2.3.0-preview\lib\net452\Microsoft.Azure.Management.KeyVault.dll True + + ..\..\..\packages\Microsoft.Azure.Management.Network.15.1.0-preview\lib\net452\Microsoft.Azure.Management.Network.dll + True + + + ..\..\..\packages\Microsoft.Azure.Management.ResourceManager.1.6.0-preview\lib\net452\Microsoft.Azure.Management.ResourceManager.dll + True + ..\..\..\packages\Microsoft.Azure.Management.Storage.4.1.0-preview\lib\net45\Microsoft.Azure.Management.Storage.dll @@ -79,10 +87,22 @@ ..\..\..\packages\Microsoft.Data.Services.Client.5.8.2\lib\net40\Microsoft.Data.Services.Client.dll True + + ..\..\..\packages\Microsoft.Rest.ClientRuntime.2.3.10\lib\net452\Microsoft.Rest.ClientRuntime.dll + True + + + ..\..\..\packages\Microsoft.Rest.ClientRuntime.Azure.3.3.10\lib\net452\Microsoft.Rest.ClientRuntime.Azure.dll + True + ..\..\..\packages\WindowsAzure.Storage.8.1.1\lib\net45\Microsoft.WindowsAzure.Storage.dll True + + ..\..\..\packages\Newtonsoft.Json.6.0.8\lib\net45\Newtonsoft.Json.dll + True + @@ -270,6 +290,23 @@ + + + + + + + + + + + + + + + + + @@ -329,6 +366,10 @@ {5ee72c53-1720-4309-b54b-5fb79703195f} Commands.Common + + {eea69772-d41b-482a-9252-2b4595c59e53} + Commands.Common.Strategies + {3819d8a7-c62c-4c47-8ddd-0332d9ce1252} Commands.ResourceManager.Common diff --git a/src/ResourceManager/Compute/Commands.Compute/Common/ComputeClient.cs b/src/ResourceManager/Compute/Commands.Compute/Common/ComputeClient.cs index f3878d69a5f3..b55df4f718b5 100644 --- a/src/ResourceManager/Compute/Commands.Compute/Common/ComputeClient.cs +++ b/src/ResourceManager/Compute/Commands.Compute/Common/ComputeClient.cs @@ -29,7 +29,8 @@ public class ComputeClient public Action ErrorLogger { get; set; } public ComputeClient(IAzureContext context) - : this(AzureSession.Instance.ClientFactory.CreateArmClient(context, AzureEnvironment.Endpoint.ResourceManager)) + : this(AzureSession.Instance.ClientFactory.CreateArmClient( + context, AzureEnvironment.Endpoint.ResourceManager)) { } diff --git a/src/ResourceManager/Compute/Commands.Compute/Common/ComputeClientBaseCmdlet.cs b/src/ResourceManager/Compute/Commands.Compute/Common/ComputeClientBaseCmdlet.cs index bbecc9bf91f5..65bec91a94c3 100644 --- a/src/ResourceManager/Compute/Commands.Compute/Common/ComputeClientBaseCmdlet.cs +++ b/src/ResourceManager/Compute/Commands.Compute/Common/ComputeClientBaseCmdlet.cs @@ -22,10 +22,7 @@ public abstract class ComputeClientBaseCmdlet : AzureRMCmdlet { protected const string VirtualMachineExtensionType = "Microsoft.Compute/virtualMachines/extensions"; - protected override bool IsUsageMetricEnabled - { - get { return true; } - } + protected override bool IsUsageMetricEnabled => true; private ComputeClient computeClient; diff --git a/src/ResourceManager/Compute/Commands.Compute/Generated/VirtualMachineScaleSet/VirtualMachineScaleSetCreateOrUpdateMethod.cs b/src/ResourceManager/Compute/Commands.Compute/Generated/VirtualMachineScaleSet/VirtualMachineScaleSetCreateOrUpdateMethod.cs index 724ca7b598e2..fee6343c74cb 100644 --- a/src/ResourceManager/Compute/Commands.Compute/Generated/VirtualMachineScaleSet/VirtualMachineScaleSetCreateOrUpdateMethod.cs +++ b/src/ResourceManager/Compute/Commands.Compute/Generated/VirtualMachineScaleSet/VirtualMachineScaleSetCreateOrUpdateMethod.cs @@ -167,4 +167,4 @@ public override void ExecuteCmdlet() [AllowNull] public PSVirtualMachineScaleSet VirtualMachineScaleSet { get; set; } } -} +} \ No newline at end of file diff --git a/src/ResourceManager/Compute/Commands.Compute/Models/PSVirtualMachine.cs b/src/ResourceManager/Compute/Commands.Compute/Models/PSVirtualMachine.cs index 60ee62321a79..ca66e3d02f6e 100644 --- a/src/ResourceManager/Compute/Commands.Compute/Models/PSVirtualMachine.cs +++ b/src/ResourceManager/Compute/Commands.Compute/Models/PSVirtualMachine.cs @@ -98,5 +98,7 @@ public string ResourceGroupName // Gets or sets the virtual machine zones. public IList Zones { get; set; } + + public string Fqdn { get; set; } } } diff --git a/src/ResourceManager/Compute/Commands.Compute/Strategies/AsyncCmdletExtensions.cs b/src/ResourceManager/Compute/Commands.Compute/Strategies/AsyncCmdletExtensions.cs new file mode 100644 index 000000000000..3d7913045f60 --- /dev/null +++ b/src/ResourceManager/Compute/Commands.Compute/Strategies/AsyncCmdletExtensions.cs @@ -0,0 +1,59 @@ +// ---------------------------------------------------------------------------------- +// +// 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 Microsoft.Azure.Commands.Common.Strategies; +using System; +using System.Management.Automation; +using System.Threading.Tasks; + +namespace Microsoft.Azure.Commands.Compute.Strategies +{ + static class AsyncCmdletExtensions + { + /// + /// Note: the function must be called in the main PowerShell thread. + /// + /// + /// + public static void StartAndWait(this Cmdlet cmdlet, Func createAndStartTask) + { + var asyncCmdlet = new AsyncCmdlet(cmdlet); + asyncCmdlet.Scheduler.Wait(createAndStartTask(asyncCmdlet)); + } + + sealed class AsyncCmdlet : IAsyncCmdlet + { + public SyncTaskScheduler Scheduler { get; } = new SyncTaskScheduler(); + + readonly Cmdlet _Cmdlet; + + public AsyncCmdlet(Cmdlet cmdlet) + { + _Cmdlet = cmdlet; + } + + public void WriteVerbose(string message) + => Scheduler.BeginInvoke(() => _Cmdlet.WriteVerbose(message)); + + public Task ShouldProcessAsync(string target, string action) + => Scheduler.Invoke(() => _Cmdlet.ShouldProcess(target, action)); + + public void WriteObject(object value) + => Scheduler.BeginInvoke(() => _Cmdlet.WriteObject(value)); + + public void WriteProgress(ProgressRecord progressRecord) + => Scheduler.BeginInvoke(() => _Cmdlet.WriteProgress(progressRecord)); + } + } +} diff --git a/src/ResourceManager/Compute/Commands.Compute/Strategies/Client.cs b/src/ResourceManager/Compute/Commands.Compute/Strategies/Client.cs new file mode 100644 index 000000000000..fd9280bfa2fa --- /dev/null +++ b/src/ResourceManager/Compute/Commands.Compute/Strategies/Client.cs @@ -0,0 +1,39 @@ +// ---------------------------------------------------------------------------------- +// +// 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 Microsoft.Azure.Commands.Common.Authentication; +using Microsoft.Azure.Commands.Common.Authentication.Abstractions; +using Microsoft.Azure.Commands.Common.Strategies; +using Microsoft.Rest; + +namespace Microsoft.Azure.Commands.Compute.Strategies +{ + sealed class Client : IClient + { + public string SubscriptionId { get; } + + IAzureContext Context { get; } + + public Client(IAzureContext context) + { + Context = context; + SubscriptionId = Context.Subscription.Id; + } + + public T GetClient() + where T : ServiceClient + => AzureSession.Instance.ClientFactory.CreateArmClient( + Context, AzureEnvironment.Endpoint.ResourceManager); + } +} diff --git a/src/ResourceManager/Compute/Commands.Compute/Strategies/Compute/ComputeStrategy.cs b/src/ResourceManager/Compute/Commands.Compute/Strategies/Compute/ComputeStrategy.cs new file mode 100644 index 000000000000..738faaf904c6 --- /dev/null +++ b/src/ResourceManager/Compute/Commands.Compute/Strategies/Compute/ComputeStrategy.cs @@ -0,0 +1,43 @@ +// ---------------------------------------------------------------------------------- +// +// 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 Microsoft.Azure.Management.Compute; +using Microsoft.Azure.Management.Compute.Models; +using System; +using System.Threading.Tasks; + +namespace Microsoft.Azure.Commands.Common.Strategies.Compute +{ + static class ComputePolicy + { + public static ResourceStrategy Create( + string type, + string header, + Func getOperations, + Func> getAsync, + Func, Task> createOrUpdateAsync, + Func createTime) + where TModel : Resource + => ResourceStrategy.Create( + type, + new[] { "Microsoft.Compute", header }, + getOperations, + getAsync, + createOrUpdateAsync, + config => config.Location, + (config, location) => config.Location = location, + createTime, + true); + } +} diff --git a/src/ResourceManager/Compute/Commands.Compute/Strategies/Compute/Image.cs b/src/ResourceManager/Compute/Commands.Compute/Strategies/Compute/Image.cs new file mode 100644 index 000000000000..40501d8bd131 --- /dev/null +++ b/src/ResourceManager/Compute/Commands.Compute/Strategies/Compute/Image.cs @@ -0,0 +1,27 @@ +// ---------------------------------------------------------------------------------- +// +// 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. +// ---------------------------------------------------------------------------------- + +namespace Microsoft.Azure.Commands.Common.Strategies.Compute +{ + sealed class Image + { + public string publisher; + + public string offer; + + public string sku; + + public string version; + } +} diff --git a/src/ResourceManager/Compute/Commands.Compute/Strategies/Compute/Images.cs b/src/ResourceManager/Compute/Commands.Compute/Strategies/Compute/Images.cs new file mode 100644 index 000000000000..96c8ae38ce6c --- /dev/null +++ b/src/ResourceManager/Compute/Commands.Compute/Strategies/Compute/Images.cs @@ -0,0 +1,149 @@ +// ---------------------------------------------------------------------------------- +// +// 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.Collections.Generic; + +namespace Microsoft.Azure.Commands.Common.Strategies.Compute +{ + static class Images + { + public static Dictionary> Instance { get; } = + new Dictionary> + { + { + "Linux", + new Dictionary + { + { + "CentOS", + new Image + { + publisher = "OpenLogic", + offer = "CentOS", + sku = "7.3", + version = "latest", + } + }, + { + "CoreOS", + new Image + { + publisher = "CoreOS", + offer = "CoreOS", + sku = "Stable", + version = "latest", + + } + }, + { + "Debian", + new Image + { + publisher = "credativ", + offer = "Debian", + sku = "8", + version = "latest", + } + }, + { + "openSUSE-Leap", + new Image + { + publisher = "SUSE", + offer = "openSUSE-Leap", + sku = "42.2", + version = "latest", + } + }, + { + "RHEL", + new Image + { + publisher = "RedHat", + offer = "RHEL", + sku = "7.3", + version = "latest" + } + }, + { + "SLES", + new Image + { + publisher = "SUSE", + offer = "SLES", + sku = "12-SP2", + version = "latest", + } + }, + { + "UbuntuLTS", + new Image + { + publisher = "Canonical", + offer = "UbuntuServer", + sku = "16.04-LTS", + version = "latest", + } + } + } + }, + { + "Windows", + new Dictionary + { + { + "Win2016Datacenter", + new Image + { + publisher = "MicrosoftWindowsServer", + offer = "WindowsServer", + sku = "2016-Datacenter", + version = "latest", + } + }, + { + "Win2012R2Datacenter", + new Image + { + publisher = "MicrosoftWindowsServer", + offer = "WindowsServer", + sku = "2012-R2-Datacenter", + version = "latest", + } + }, + { + "Win2012Datacenter", + new Image + { + publisher = "MicrosoftWindowsServer", + offer = "WindowsServer", + sku = "2012-Datacenter", + version = "latest", + } + }, + { + "Win2008R2SP1", + new Image + { + publisher = "MicrosoftWindowsServer", + offer = "WindowsServer", + sku = "2008-R2-SP1", + version = "latest", + } + } + } + } + }; + } +} diff --git a/src/ResourceManager/Compute/Commands.Compute/Strategies/Compute/VirtualMachineStrategy.cs b/src/ResourceManager/Compute/Commands.Compute/Strategies/Compute/VirtualMachineStrategy.cs new file mode 100644 index 000000000000..5733b8932496 --- /dev/null +++ b/src/ResourceManager/Compute/Commands.Compute/Strategies/Compute/VirtualMachineStrategy.cs @@ -0,0 +1,85 @@ +// ---------------------------------------------------------------------------------- +// +// 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 Microsoft.Azure.Management.Compute; +using Microsoft.Azure.Management.Compute.Models; +using Microsoft.Azure.Management.Internal.Resources.Models; +using Microsoft.Azure.Commands.Compute.Strategies.ResourceManager; +using Microsoft.Azure.Management.Internal.Network.Version2017_10_01.Models; + +namespace Microsoft.Azure.Commands.Common.Strategies.Compute +{ + static class VirtualMachineStrategy + { + public static ResourceStrategy Strategy { get; } + = ComputePolicy.Create( + "virtual machine", + "virtualMachines", + client => client.VirtualMachines, + (o, p) => o.GetAsync( + p.ResourceGroupName, p.Name, null, p.CancellationToken), + (o, p) => o.CreateOrUpdateAsync( + p.ResourceGroupName, p.Name, p.Model, p.CancellationToken), + c => c.OsProfile.WindowsConfiguration != null ? 240 : 120); + + public static ResourceConfig CreateVirtualMachineConfig( + this ResourceConfig resourceGroup, + string name, + ResourceConfig networkInterface, + bool isWindows, + string adminUsername, + string adminPassword, + Image image, + string size) + => Strategy.CreateResourceConfig( + resourceGroup, + name, + subscription => new VirtualMachine + { + OsProfile = new OSProfile + { + ComputerName = name, + WindowsConfiguration = isWindows ? new WindowsConfiguration { } : null, + LinuxConfiguration = isWindows ? null : new LinuxConfiguration(), + AdminUsername = adminUsername, + AdminPassword = adminPassword, + }, + NetworkProfile = new NetworkProfile + { + NetworkInterfaces = new[] + { + new NetworkInterfaceReference + { + Id = networkInterface.GetId(subscription).IdToString() + } + } + }, + HardwareProfile = new HardwareProfile + { + VmSize = size + }, + StorageProfile = new StorageProfile + { + ImageReference = new ImageReference + { + Publisher = image.publisher, + Offer = image.offer, + Sku = image.sku, + Version = image.version + } + }, + }, + new[] { networkInterface }); + } +} diff --git a/src/ResourceManager/Compute/Commands.Compute/Strategies/IAsyncCmdlet.cs b/src/ResourceManager/Compute/Commands.Compute/Strategies/IAsyncCmdlet.cs new file mode 100644 index 000000000000..61d14ac156d6 --- /dev/null +++ b/src/ResourceManager/Compute/Commands.Compute/Strategies/IAsyncCmdlet.cs @@ -0,0 +1,30 @@ +// ---------------------------------------------------------------------------------- +// +// 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.Management.Automation; +using System.Threading.Tasks; + +namespace Microsoft.Azure.Commands.Compute.Strategies +{ + interface IAsyncCmdlet + { + void WriteVerbose(string message); + + Task ShouldProcessAsync(string target, string action); + + void WriteObject(object value); + + void WriteProgress(ProgressRecord progressRecord); + } +} diff --git a/src/ResourceManager/Compute/Commands.Compute/Strategies/Network/NetworkInterfaceStrategy.cs b/src/ResourceManager/Compute/Commands.Compute/Strategies/Network/NetworkInterfaceStrategy.cs new file mode 100644 index 000000000000..8b3e768469d2 --- /dev/null +++ b/src/ResourceManager/Compute/Commands.Compute/Strategies/Network/NetworkInterfaceStrategy.cs @@ -0,0 +1,67 @@ +// ---------------------------------------------------------------------------------- +// +// 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 Microsoft.Azure.Commands.Compute.Strategies.ResourceManager; +using Microsoft.Azure.Management.Internal.Network.Version2017_10_01; +using Microsoft.Azure.Management.Internal.Network.Version2017_10_01.Models; +using Microsoft.Azure.Management.Internal.Resources.Models; + +namespace Microsoft.Azure.Commands.Common.Strategies.Network +{ + static class NetworkInterfaceStrategy + { + public static ResourceStrategy Strategy { get; } + = NetworkStrategy.Create( + "network interface", + "networkInterfaces", + client => client.NetworkInterfaces, + (o, p) => o.GetAsync( + p.ResourceGroupName, p.Name, null, p.CancellationToken), + (o, p) => o.CreateOrUpdateAsync( + p.ResourceGroupName, p.Name, p.Model, p.CancellationToken), + _ => 5); + + public static ResourceConfig CreateNetworkInterfaceConfig( + this ResourceConfig resourceGroup, + string name, + NestedResourceConfig subnet, + ResourceConfig publicIPAddress, + ResourceConfig networkSecurityGroup = null) + => Strategy.CreateResourceConfig( + resourceGroup, + name, + subscription => new NetworkInterface + { + IpConfigurations = new [] + { + new NetworkInterfaceIPConfiguration + { + Name = name, + Subnet = new Subnet { Id = subnet.GetId(subscription).IdToString() }, + PublicIPAddress = new PublicIPAddress + { + Id = publicIPAddress.GetId(subscription).IdToString() + } + } + }, + NetworkSecurityGroup = networkSecurityGroup == null + ? null + : new NetworkSecurityGroup + { + Id = networkSecurityGroup.GetId(subscription).IdToString() + } + }, + new IEntityConfig[] { subnet, publicIPAddress, networkSecurityGroup }); + } +} diff --git a/src/ResourceManager/Compute/Commands.Compute/Strategies/Network/NetworkSecurityGroupPolicy.cs b/src/ResourceManager/Compute/Commands.Compute/Strategies/Network/NetworkSecurityGroupPolicy.cs new file mode 100644 index 000000000000..4841c737b82f --- /dev/null +++ b/src/ResourceManager/Compute/Commands.Compute/Strategies/Network/NetworkSecurityGroupPolicy.cs @@ -0,0 +1,59 @@ +// ---------------------------------------------------------------------------------- +// +// 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 Microsoft.Azure.Management.Internal.Resources.Models; +using Microsoft.Azure.Commands.Compute.Strategies.ResourceManager; +using System.Linq; +using Microsoft.Azure.Management.Internal.Network.Version2017_10_01.Models; +using Microsoft.Azure.Management.Internal.Network.Version2017_10_01; + +namespace Microsoft.Azure.Commands.Common.Strategies.Network +{ + static class NetworkSecurityGroupStrategy + { + public static ResourceStrategy Strategy { get; } + = NetworkStrategy.Create( + "network security group", + "networkSecurityGroups", + client => client.NetworkSecurityGroups, + (o, p) => o.GetAsync( + p.ResourceGroupName, p.Name, null, p.CancellationToken), + (o, p) => o.CreateOrUpdateAsync( + p.ResourceGroupName, p.Name, p.Model, p.CancellationToken), + _ => 15); + + public static ResourceConfig CreateNetworkSecurityGroupConfig( + this ResourceConfig resourceGroup, string name, int[] openPorts) + => Strategy.CreateResourceConfig( + resourceGroup, + name, + _ => new NetworkSecurityGroup + { + SecurityRules = openPorts + .Select((port, index) => new SecurityRule + { + Name = name + port, + Protocol = "Tcp", + Priority = index + 1000, + Access = "Allow", + Direction = "Inbound", + SourcePortRange = "*", + SourceAddressPrefix = "*", + DestinationPortRange = port.ToString(), + DestinationAddressPrefix = "*" + }) + .ToList() + }); + } +} diff --git a/src/ResourceManager/Compute/Commands.Compute/Strategies/Network/NetworkStrategy.cs b/src/ResourceManager/Compute/Commands.Compute/Strategies/Network/NetworkStrategy.cs new file mode 100644 index 000000000000..13b9e084979c --- /dev/null +++ b/src/ResourceManager/Compute/Commands.Compute/Strategies/Network/NetworkStrategy.cs @@ -0,0 +1,43 @@ +// ---------------------------------------------------------------------------------- +// +// 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 Microsoft.Azure.Management.Internal.Network.Version2017_10_01; +using Microsoft.Azure.Management.Internal.Network.Version2017_10_01.Models; +using System; +using System.Threading.Tasks; + +namespace Microsoft.Azure.Commands.Common.Strategies.Network +{ + static class NetworkStrategy + { + public static ResourceStrategy Create( + string type, + string header, + Func getOperations, + Func> getAsync, + Func, Task> createOrUpdateAsync, + Func createTime) + where TModel : Resource + => ResourceStrategy.Create( + type, + new [] { "Microsoft.Network", header }, + getOperations, + getAsync, + createOrUpdateAsync, + model => model.Location, + (model, location) => model.Location = location, + createTime, + true); + } +} diff --git a/src/ResourceManager/Compute/Commands.Compute/Strategies/Network/PublicIPAddressStrategy.cs b/src/ResourceManager/Compute/Commands.Compute/Strategies/Network/PublicIPAddressStrategy.cs new file mode 100644 index 000000000000..5e265979a636 --- /dev/null +++ b/src/ResourceManager/Compute/Commands.Compute/Strategies/Network/PublicIPAddressStrategy.cs @@ -0,0 +1,52 @@ +// ---------------------------------------------------------------------------------- +// +// 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 Microsoft.Azure.Commands.Compute.Strategies.ResourceManager; +using Microsoft.Azure.Management.Internal.Network.Version2017_10_01; +using Microsoft.Azure.Management.Internal.Network.Version2017_10_01.Models; +using Microsoft.Azure.Management.Internal.Resources.Models; + +namespace Microsoft.Azure.Commands.Common.Strategies.Network +{ + static class PublicIPAddressStrategy + { + public static ResourceStrategy Strategy { get; } + = NetworkStrategy.Create( + "public IP address", + "publicIPAddresses", + client => client.PublicIPAddresses, + (o, p) => o.GetAsync( + p.ResourceGroupName, p.Name, null, p.CancellationToken), + (o, p) => o.CreateOrUpdateAsync( + p.ResourceGroupName, p.Name, p.Model, p.CancellationToken), + _ => 15); + + public static ResourceConfig CreatePublicIPAddressConfig( + this ResourceConfig resourceGroup, + string name, + string domainNameLabel, + string allocationMethod) + => Strategy.CreateResourceConfig( + resourceGroup, + name, + _ => new PublicIPAddress + { + PublicIPAllocationMethod = allocationMethod, + DnsSettings = new PublicIPAddressDnsSettings + { + DomainNameLabel = domainNameLabel, + } + }); + } +} diff --git a/src/ResourceManager/Compute/Commands.Compute/Strategies/Network/SubnetStrategy.cs b/src/ResourceManager/Compute/Commands.Compute/Strategies/Network/SubnetStrategy.cs new file mode 100644 index 000000000000..93d90c4c5276 --- /dev/null +++ b/src/ResourceManager/Compute/Commands.Compute/Strategies/Network/SubnetStrategy.cs @@ -0,0 +1,54 @@ +// ---------------------------------------------------------------------------------- +// +// 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 Microsoft.Azure.Management.Internal.Network.Version2017_10_01.Models; +using System.Collections.Generic; +using System.Linq; + +namespace Microsoft.Azure.Commands.Common.Strategies.Network +{ + static class SubnetPolicy + { + public static NestedResourceStrategy Strategy { get; } + = NestedResourceStrategy.Create( + "subnets", + (vn, name) => vn.Subnets?.FirstOrDefault(s => s?.Name == name), + (vn, name, subnet) => + { + subnet.Name = name; + if (vn.Subnets == null) + { + vn.Subnets = new List { subnet }; + return; + } + var s = vn + .Subnets + .Select((sn, i) => new { sn, i }) + .FirstOrDefault(p => p.sn.Name == name); + if (s != null) + { + vn.Subnets[s.i] = subnet; + return; + } + vn.Subnets.Add(subnet); + }); + + public static NestedResourceConfig CreateSubnet( + this ResourceConfig virtualNetwork, string name, string addressPrefix) + => Strategy.CreateConfig( + virtualNetwork, + name, + () => new Subnet { Name = name, AddressPrefix = addressPrefix }); + } +} diff --git a/src/ResourceManager/Compute/Commands.Compute/Strategies/Network/VirtualNetworkStrategy.cs b/src/ResourceManager/Compute/Commands.Compute/Strategies/Network/VirtualNetworkStrategy.cs new file mode 100644 index 000000000000..55b1a93a497b --- /dev/null +++ b/src/ResourceManager/Compute/Commands.Compute/Strategies/Network/VirtualNetworkStrategy.cs @@ -0,0 +1,50 @@ +// ---------------------------------------------------------------------------------- +// +// 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 Microsoft.Azure.Commands.Compute.Strategies.ResourceManager; +using Microsoft.Azure.Management.Internal.Network.Version2017_10_01; +using Microsoft.Azure.Management.Internal.Network.Version2017_10_01.Models; +using Microsoft.Azure.Management.Internal.Resources.Models; + +namespace Microsoft.Azure.Commands.Common.Strategies.Network +{ + static class VirtualNetworkStrategy + { + public static ResourceStrategy Strategy { get; } + = NetworkStrategy.Create( + "virtual network", + "virtualNetworks", + client => client.VirtualNetworks, + (o, p) => o.GetAsync( + p.ResourceGroupName, p.Name, null, p.CancellationToken), + (o, p) => o.CreateOrUpdateAsync( + p.ResourceGroupName, p.Name, p.Model, p.CancellationToken), + _ => 15); + + public static ResourceConfig CreateVirtualNetworkConfig( + this ResourceConfig resourceGroup, + string name, + string addressPrefix) + => Strategy.CreateResourceConfig( + resourceGroup, + name, + _ => new VirtualNetwork + { + AddressSpace = new AddressSpace + { + AddressPrefixes = new[] { addressPrefix } + } + }); + } +} diff --git a/src/ResourceManager/Compute/Commands.Compute/Strategies/ProgressReport.cs b/src/ResourceManager/Compute/Commands.Compute/Strategies/ProgressReport.cs new file mode 100644 index 000000000000..ac522f1653cd --- /dev/null +++ b/src/ResourceManager/Compute/Commands.Compute/Strategies/ProgressReport.cs @@ -0,0 +1,67 @@ +// ---------------------------------------------------------------------------------- +// +// 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 Microsoft.Azure.Commands.Common.Strategies; +using System.Management.Automation; +using System.Collections.Concurrent; +using System.Linq; + +namespace Microsoft.Azure.Commands.Compute.Strategies +{ + sealed class ProgressReport : IProgressReport + { + readonly IAsyncCmdlet _Cmdlet; + + double _Completed = 0; + + ConcurrentDictionary _Set = + new ConcurrentDictionary(); + + public ProgressReport(IAsyncCmdlet cmdlet) + { + _Cmdlet = cmdlet; + } + + public void Done(ResourceConfig config, double progress) where TModel : class + { + _Completed += progress; + Void _; + _Set.TryRemove(config, out _); + _Cmdlet.WriteVerbose(config.Name + " " + config.Strategy.Type); + Update(); + } + + public void Update() + { + var x = string.Join(", ", _Set.Keys.Select(c => "'" + c.Name + "' " + c.Strategy.Type)); + var p = (int)(_Completed * 100.0); + _Cmdlet.WriteProgress( + new ProgressRecord( + 0, + "Creating Azure resources", + p + "%") + { + PercentComplete = p, + CurrentOperation = x == string.Empty ? null : "Creating " + x + "." + }); + } + + public void Start(ResourceConfig config) where TModel : class + { + _Set.TryAdd(config, new Void()); + _Cmdlet.WriteVerbose("Creating '" + config.Name + "' " + config.Strategy.Type + "..."); + Update(); + } + } +} diff --git a/src/ResourceManager/Compute/Commands.Compute/Strategies/ResourceManager/ResourceConfigExtensions.cs b/src/ResourceManager/Compute/Commands.Compute/Strategies/ResourceManager/ResourceConfigExtensions.cs new file mode 100644 index 000000000000..67eaea6aae50 --- /dev/null +++ b/src/ResourceManager/Compute/Commands.Compute/Strategies/ResourceManager/ResourceConfigExtensions.cs @@ -0,0 +1,24 @@ +using Microsoft.Azure.Commands.Common.Strategies; +using Microsoft.Azure.Management.Internal.Resources.Models; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Microsoft.Azure.Commands.Compute.Strategies.ResourceManager +{ + static class ResourceConfigExtensions + { + public static ResourceConfig CreateResourceConfig( + this ResourceStrategy strategy, + ResourceConfig resourceGroup, + string name, + Func createModel = null, + IEnumerable dependencies = null) + where TModel : class, new() + => strategy.CreateConfig( + resourceGroup.Name, + name, + createModel, + dependencies.EmptyIfNull().Where(d => d != null).Concat(new[] { resourceGroup })); + } +} diff --git a/src/ResourceManager/Compute/Commands.Compute/Strategies/ResourceManager/ResourceGroupStrategy.cs b/src/ResourceManager/Compute/Commands.Compute/Strategies/ResourceManager/ResourceGroupStrategy.cs new file mode 100644 index 000000000000..cc3e5e03bbc4 --- /dev/null +++ b/src/ResourceManager/Compute/Commands.Compute/Strategies/ResourceManager/ResourceGroupStrategy.cs @@ -0,0 +1,38 @@ +// ---------------------------------------------------------------------------------- +// +// 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 Microsoft.Azure.Management.Internal.Resources; +using Microsoft.Azure.Management.Internal.Resources.Models; +using System.Linq; + +namespace Microsoft.Azure.Commands.Common.Strategies.ResourceManager +{ + static class ResourceGroupStrategy + { + public static ResourceStrategy Strategy { get; } + = ResourceStrategy.Create( + "resource group", + _ => Enumerable.Empty(), + (ResourceManagementClient client) => client.ResourceGroups, + (o, p) => o.GetAsync(p.Name, p.CancellationToken), + (o, p) => o.CreateOrUpdateAsync(p.Name, p.Model, p.CancellationToken), + model => model.Location, + (model, location) => model.Location = location, + _ => 3, + false); + + public static ResourceConfig CreateResourceGroupConfig(string name) + => Strategy.CreateConfig(name, name); + } +} diff --git a/src/ResourceManager/Compute/Commands.Compute/Strategies/ShouldProcess.cs b/src/ResourceManager/Compute/Commands.Compute/Strategies/ShouldProcess.cs new file mode 100644 index 000000000000..3c798f5547b8 --- /dev/null +++ b/src/ResourceManager/Compute/Commands.Compute/Strategies/ShouldProcess.cs @@ -0,0 +1,35 @@ +// ---------------------------------------------------------------------------------- +// +// 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 Microsoft.Azure.Commands.Common.Strategies; +using System.Management.Automation; +using System.Threading.Tasks; + +namespace Microsoft.Azure.Commands.Compute.Strategies +{ + internal sealed class ShouldProcess : IShouldProcess + { + readonly IAsyncCmdlet _Cmdlet; + + public ShouldProcess(IAsyncCmdlet cmdlet) + { + _Cmdlet = cmdlet; + } + + public Task ShouldCreate(ResourceConfig config, TModel model) + where TModel : class + => _Cmdlet.ShouldProcessAsync( + config.Name, VerbsCommon.New + " " + config.Strategy.Type); + } +} diff --git a/src/ResourceManager/Compute/Commands.Compute/VirtualMachine/Operation/NewAzureVMCommand.cs b/src/ResourceManager/Compute/Commands.Compute/VirtualMachine/Operation/NewAzureVMCommand.cs index 4aa9a17f1066..b88d5adc4cac 100644 --- a/src/ResourceManager/Compute/Commands.Compute/VirtualMachine/Operation/NewAzureVMCommand.cs +++ b/src/ResourceManager/Compute/Commands.Compute/VirtualMachine/Operation/NewAzureVMCommand.cs @@ -15,45 +15,70 @@ using AutoMapper; using Microsoft.Azure.Commands.Common.Authentication; using Microsoft.Azure.Commands.Common.Authentication.Abstractions; -using Microsoft.Azure.Commands.Common.Authentication.Models; +using Microsoft.Azure.Commands.Common.Strategies; +using Microsoft.Azure.Commands.Common.Strategies.Compute; +using Microsoft.Azure.Commands.Common.Strategies.Network; +using Microsoft.Azure.Commands.Common.Strategies.ResourceManager; using Microsoft.Azure.Commands.Compute.Common; using Microsoft.Azure.Commands.Compute.Models; +using Microsoft.Azure.Commands.Compute.Strategies; using Microsoft.Azure.Commands.ResourceManager.Common.ArgumentCompleters; using Microsoft.Azure.Management.Compute; using Microsoft.Azure.Management.Compute.Models; +using Microsoft.Azure.Management.Network; +using Microsoft.Azure.Management.ResourceManager; using Microsoft.Azure.Management.Storage; using Microsoft.Azure.Management.Storage.Models; using System; using System.Collections; using System.Linq; using System.Management.Automation; +using System.Net; using System.Reflection; +using System.Threading; +using System.Threading.Tasks; using CM = Microsoft.Azure.Management.Compute.Models; namespace Microsoft.Azure.Commands.Compute { - [Cmdlet(VerbsCommon.New, ProfileNouns.VirtualMachine, SupportsShouldProcess = true)] - [OutputType(typeof(PSAzureOperationResponse))] + [Cmdlet( + VerbsCommon.New, + ProfileNouns.VirtualMachine, + SupportsShouldProcess = true, + DefaultParameterSetName = "DefaultParameterSet")] + [OutputType(typeof(PSAzureOperationResponse), typeof(PSVirtualMachine))] public class NewAzureVMCommand : VirtualMachineBaseCmdlet { + public const string DefaultParameterSet = "DefaultParameterSet"; + public const string SimpleParameterSet = "SimpleParameterSet"; + [Parameter( + ParameterSetName = DefaultParameterSet, Mandatory = true, Position = 0, ValueFromPipelineByPropertyName = true)] [ResourceGroupCompleter()] + [Parameter( + ParameterSetName = SimpleParameterSet, + Mandatory = false)] [ValidateNotNullOrEmpty] public string ResourceGroupName { get; set; } [Parameter( + ParameterSetName = DefaultParameterSet, Mandatory = true, Position = 1, ValueFromPipelineByPropertyName = true)] + [Parameter( + ParameterSetName = SimpleParameterSet, + Mandatory = false)] [LocationCompleter("Microsoft.Compute/virtualMachines")] [ValidateNotNullOrEmpty] public string Location { get; set; } [Alias("VMProfile")] [Parameter( + ParameterSetName = DefaultParameterSet, Mandatory = true, Position = 2, ValueFromPipeline = true, @@ -62,31 +87,187 @@ public class NewAzureVMCommand : VirtualMachineBaseCmdlet public PSVirtualMachine VM { get; set; } [Parameter( - Mandatory = false, - Position = 3, - ValueFromPipelineByPropertyName = true)] + ParameterSetName = DefaultParameterSet, + Mandatory = false, + Position = 3, + ValueFromPipelineByPropertyName = true)] [ValidateNotNullOrEmpty] public string[] Zone { get; set; } [Parameter( + ParameterSetName = DefaultParameterSet, + Position = 3, HelpMessage = "Disable BG Info Extension")] public SwitchParameter DisableBginfoExtension { get; set; } [Parameter( + ParameterSetName = DefaultParameterSet, Mandatory = false, ValueFromPipelineByPropertyName = true)] public Hashtable Tags { get; set; } [Parameter( + ParameterSetName = DefaultParameterSet, Mandatory = false, ValueFromPipelineByPropertyName = false)] [ValidateNotNullOrEmpty] public string LicenseType { get; set; } + [Parameter( + ParameterSetName = SimpleParameterSet, + Mandatory = true)] + [ValidateNotNullOrEmpty] + public string Name { get; set; } + + [Parameter(ParameterSetName = SimpleParameterSet, Mandatory = true)] + public PSCredential Credential { get; set; } + + [Parameter(ParameterSetName = SimpleParameterSet, Mandatory = false)] + public string VirtualNetworkName { get; set; } + + [Parameter(ParameterSetName = SimpleParameterSet, Mandatory = false)] + public string AddressPrefix { get; set; } = "192.168.0.0/16"; + + [Parameter(ParameterSetName = SimpleParameterSet, Mandatory = false)] + public string SubnetName { get; set; } + + [Parameter(ParameterSetName = SimpleParameterSet, Mandatory = false)] + public string SubnetAddressPrefix { get; set; } = "192.168.1.0/24"; + + [Parameter(ParameterSetName = SimpleParameterSet, Mandatory = false)] + public string PublicIpAddressName { get; set; } + + [Parameter(ParameterSetName = SimpleParameterSet, Mandatory = false)] + public string DomainNameLabel { get; set; } + + [Parameter(ParameterSetName = SimpleParameterSet, Mandatory = false)] + [ValidateSet("Static", "Dynamic")] + public string AllocationMethod { get; set; } = "Static"; + + [Parameter(ParameterSetName = SimpleParameterSet, Mandatory = false)] + public string SecurityGroupName { get; set; } + + [Parameter(ParameterSetName = SimpleParameterSet, Mandatory = false)] + public int[] OpenPorts { get; set; } + + [Parameter(ParameterSetName = SimpleParameterSet, Mandatory = false)] + [PSArgumentCompleter( + "CentOS", + "CoreOS", + "Debian", + "openSUSE-Leap", + "RHEL", + "SLES", + "UbuntuLTS", + "Win2016Datacenter", + "Win2012R2Datacenter", + "Win2012Datacenter", + "Win2008R2SP1")] + public string ImageName { get; set; } = "Win2016Datacenter"; + + [Parameter(ParameterSetName = SimpleParameterSet, Mandatory = false)] + public string Size { get; set; } = "Standard_DS1_v2"; + [Parameter(Mandatory = false, HelpMessage = "Run cmdlet in the background")] public SwitchParameter AsJob { get; set; } public override void ExecuteCmdlet() + { + switch (ParameterSetName) + { + case SimpleParameterSet: + this.StartAndWait(StrategyExecuteCmdletAsync); + break; + default: + DefaultExecuteCmdlet(); + break; + } + } + + async Task StrategyExecuteCmdletAsync(IAsyncCmdlet asyncCmdlet) + { + ResourceGroupName = ResourceGroupName ?? Name; + VirtualNetworkName = VirtualNetworkName ?? Name; + SubnetName = SubnetName ?? Name; + PublicIpAddressName = PublicIpAddressName ?? Name; + DomainNameLabel = DomainNameLabel ?? (Name + ResourceGroupName).ToLower(); + SecurityGroupName = SecurityGroupName ?? Name; + + // get image + var image = Images + .Instance + .SelectMany(osAndMap => osAndMap + .Value + .Where(nameAndImage => nameAndImage.Key.ToLower() == ImageName.ToLower()) + .Select(nameAndImage => new { OsType = osAndMap.Key, Image = nameAndImage.Value })) + .FirstOrDefault(); + + var isWindows = image.OsType == "Windows"; + OpenPorts = OpenPorts ?? (isWindows ? new[] { 3389, 5985 } : new[] { 22 }); + + var resourceGroup = ResourceGroupStrategy.CreateResourceGroupConfig(ResourceGroupName); + var virtualNetwork = resourceGroup.CreateVirtualNetworkConfig( + name: VirtualNetworkName, addressPrefix: AddressPrefix); + var subnet = virtualNetwork.CreateSubnet(SubnetName, SubnetAddressPrefix); + var publicIpAddress = resourceGroup.CreatePublicIPAddressConfig( + name: PublicIpAddressName, + domainNameLabel: DomainNameLabel, + allocationMethod: AllocationMethod); + var networkSecurityGroup = resourceGroup.CreateNetworkSecurityGroupConfig( + name: SecurityGroupName, + openPorts: OpenPorts); + var networkInterface = resourceGroup.CreateNetworkInterfaceConfig( + Name, subnet, publicIpAddress, networkSecurityGroup); + var virtualMachine = resourceGroup.CreateVirtualMachineConfig( + name: Name, + networkInterface: networkInterface, + isWindows: isWindows, + adminUsername: Credential.UserName, + adminPassword: new NetworkCredential(string.Empty, Credential.Password).Password, + image: image.Image, + size: Size); + + var client = new Client(DefaultProfile.DefaultContext); + + // get current Azure state + var current = await virtualMachine.GetStateAsync(client, new CancellationToken()); + + if (Location == null) + { + Location = current.GetLocation(virtualMachine); + if (Location == null) + { + Location = "eastus"; + } + } + + var fqdn = DomainNameLabel + "." + Location + ".cloudapp.azure.com"; + + // create target state + var target = virtualMachine.GetTargetState(current, client.SubscriptionId, Location); + + // apply target state + var newState = await virtualMachine + .UpdateStateAsync( + client, + target, + new CancellationToken(), + new ShouldProcess(asyncCmdlet), + new ProgressReport(asyncCmdlet)); + + var result = newState.Get(virtualMachine); + if (result != null) + { + var psResult = ComputeAutoMapperProfile.Mapper.Map(result); + psResult.Fqdn = fqdn; + asyncCmdlet.WriteVerbose(isWindows + ? "Use 'mstsc /v:" + fqdn + "' to connect to the VM." + : "Use 'ssh " + Credential.UserName + "@" + fqdn + "' to connect to the VM."); + asyncCmdlet.WriteObject(psResult); + } + } + + public void DefaultExecuteCmdlet() { base.ExecuteCmdlet(); @@ -150,7 +331,8 @@ public override void ExecuteCmdlet() AutoUpgradeMinorVersion = true, }; - typeof(CM.Resource).GetRuntimeProperty("Name").SetValue(extensionParameters, VirtualMachineBGInfoExtensionContext.ExtensionDefaultName); + typeof(CM.Resource).GetRuntimeProperty("Name") + .SetValue(extensionParameters, VirtualMachineBGInfoExtensionContext.ExtensionDefaultName); typeof(CM.Resource).GetRuntimeProperty("Type") .SetValue(extensionParameters, VirtualMachineExtensionType); diff --git a/src/ResourceManager/Compute/Commands.Compute/VirtualMachine/VirtualMachineBaseCmdlet.cs b/src/ResourceManager/Compute/Commands.Compute/VirtualMachine/VirtualMachineBaseCmdlet.cs index 4fd31f40451d..e6b0662c84e5 100644 --- a/src/ResourceManager/Compute/Commands.Compute/VirtualMachine/VirtualMachineBaseCmdlet.cs +++ b/src/ResourceManager/Compute/Commands.Compute/VirtualMachine/VirtualMachineBaseCmdlet.cs @@ -28,13 +28,8 @@ public abstract class VirtualMachineBaseCmdlet : ComputeClientBaseCmdlet { protected const InstanceViewTypes InstanceViewExpand = InstanceViewTypes.InstanceView; - public IVirtualMachinesOperations VirtualMachineClient - { - get - { - return ComputeClient.ComputeManagementClient.VirtualMachines; - } - } + public IVirtualMachinesOperations VirtualMachineClient + => ComputeClient.ComputeManagementClient.VirtualMachines; public static string FormatObject(Object obj) { diff --git a/src/ResourceManager/Compute/Commands.Compute/help/New-AzureRmVM.md b/src/ResourceManager/Compute/Commands.Compute/help/New-AzureRmVM.md index f683ff6da3f5..898ce4018efe 100644 --- a/src/ResourceManager/Compute/Commands.Compute/help/New-AzureRmVM.md +++ b/src/ResourceManager/Compute/Commands.Compute/help/New-AzureRmVM.md @@ -1,4 +1,4 @@ ---- +--- external help file: Microsoft.Azure.Commands.Compute.dll-Help.xml ms.assetid: 05E6155D-4F0E-406B-9312-77AD97EF66EE online version: https://docs.microsoft.com/en-us/powershell/module/azurerm.compute/new-azurermvm @@ -12,69 +12,35 @@ Creates a virtual machine. ## SYNTAX +### DefaultParameterSet (Default) ``` New-AzureRmVM [-ResourceGroupName] [-Location] [-VM] [[-Zone] ] [-DisableBginfoExtension] [-Tags ] [-LicenseType ] [-DefaultProfile ] [-WhatIf] [-Confirm] [] ``` +### StrategyParameterSet +``` +New-AzureRmVM [[-ResourceGroupName] ] [[-Location] ] -Name -Credential + [-VirtualNetworkName ] [-AddressPrefix ] [-SubnetName ] + [-SubnetAddressPrefix ] [-PublicIpAddressName ] [-DomainNameLabel ] + [-AllocationMethod ] [-SecurityGroupName ] [-OpenPorts ] [-ImageName ] + [-DefaultProfile ] [-WhatIf] [-Confirm] [] +``` + ## DESCRIPTION The **New-AzureRmVM** cmdlet creates a virtual machine in Azure. This cmdlet takes a virtual machine object as input. Use the New-AzureRmVMConfig cmdlet to create a virtual machine object. Other cmdlets can be used to configure the virtual machine, such as Set-AzureRmVMOperatingSystem, Set-AzureRmVMSourceImage, Add-AzureRmVMNetworkInterface, and Set-AzureRmVMOSDisk. +The `StrategyParameterSet` provides a convenient method to create a VM by making common VM creation arguments optional. + ## EXAMPLES ### Example 1: Create a virtual machine ``` -PS C:\> # Variables -## Global -$ResourceGroupName = "ResourceGroup11" -$Location = "WestEurope" - -## Storage -$StorageName = "generalstorage6cc" -$StorageType = "Standard_GRS" - -## Network -$InterfaceName = "ServerInterface06" -$Subnet1Name = "Subnet1" -$VNetName = "VNet09" -$VNetAddressPrefix = "10.0.0.0/16" -$VNetSubnetAddressPrefix = "10.0.0.0/24" - -## Compute -$VMName = "VirtualMachine12" -$ComputerName = "Server22" -$VMSize = "Standard_A2" -$OSDiskName = $VMName + "OSDisk" - -# Resource Group -New-AzureRmResourceGroup -Name $ResourceGroupName -Location $Location - -# Storage -$StorageAccount = New-AzureRmStorageAccount -ResourceGroupName $ResourceGroupName -Name $StorageName -Type $StorageType -Location $Location - -# Network -$PIp = New-AzureRmPublicIpAddress -Name $InterfaceName -ResourceGroupName $ResourceGroupName -Location $Location -AllocationMethod Dynamic -$SubnetConfig = New-AzureRmVirtualNetworkSubnetConfig -Name $Subnet1Name -AddressPrefix $VNetSubnetAddressPrefix -$VNet = New-AzureRmVirtualNetwork -Name $VNetName -ResourceGroupName $ResourceGroupName -Location $Location -AddressPrefix $VNetAddressPrefix -Subnet $SubnetConfig -$Interface = New-AzureRmNetworkInterface -Name $InterfaceName -ResourceGroupName $ResourceGroupName -Location $Location -SubnetId $VNet.Subnets[0].Id -PublicIpAddressId $PIp.Id - -# Compute - -## Setup local VM object -$Credential = Get-Credential -$VirtualMachine = New-AzureRmVMConfig -VMName $VMName -VMSize $VMSize -$VirtualMachine = Set-AzureRmVMOperatingSystem -VM $VirtualMachine -Windows -ComputerName $ComputerName -Credential $Credential -ProvisionVMAgent -EnableAutoUpdate -$VirtualMachine = Set-AzureRmVMSourceImage -VM $VirtualMachine -PublisherName MicrosoftWindowsServer -Offer WindowsServer -Skus 2012-R2-Datacenter -Version "latest" -$VirtualMachine = Add-AzureRmVMNetworkInterface -VM $VirtualMachine -Id $Interface.Id -$OSDiskUri = $StorageAccount.PrimaryEndpoints.Blob.ToString() + "vhds/" + $OSDiskName + ".vhd" -$VirtualMachine = Set-AzureRmVMOSDisk -VM $VirtualMachine -Name $OSDiskName -VhdUri $OSDiskUri -CreateOption FromImage - -## Create the VM in Azure -New-AzureRmVM -ResourceGroupName $ResourceGroupName -Location $Location -VM $VirtualMachine +PS C:\> New-AzureRmVM -Name MyVm ``` This example script shows how to create a virtual machine. @@ -138,6 +104,52 @@ You can confirm your login status by using the **Get-AzureSubscription** cmdlet. ## PARAMETERS +### -AddressPrefix +The address prefix for the virtual network which will be created for the VM. + +```yaml +Type: String +Parameter Sets: StrategyParameterSet +Aliases: + +Required: False +Position: Named +Default value: 192.168.0.0/16 +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -AllocationMethod +The IP allocation method for the public IP which will be created for the VM. + +```yaml +Type: String +Parameter Sets: StrategyParameterSet +Aliases: +Accepted values: Static, Dynamic + +Required: False +Position: Named +Default value: Static +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Credential +The administrator credentials for the VM. + +```yaml +Type: PSCredential +Parameter Sets: StrategyParameterSet +Aliases: + +Required: True +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + ### -DefaultProfile The credentials, account, tenant, and subscription used for communication with azure. @@ -158,7 +170,22 @@ Indicates that this cmdlet does not install the **BG Info** extension on the vir ```yaml Type: SwitchParameter -Parameter Sets: (All) +Parameter Sets: DefaultParameterSet +Aliases: + +Required: False +Position: 3 +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -DomainNameLabel +The subdomain label for the fully-qualified domain name (FQDN) of the VM. This will take the form `{domainNameLabel}.{location}.cloudapp.azure.com`. + +```yaml +Type: String +Parameter Sets: StrategyParameterSet Aliases: Required: False @@ -168,6 +195,21 @@ Accept pipeline input: False Accept wildcard characters: False ``` +### -ImageName +The friendly image name upon which the VM will be built. These include: Win2016Datacenter, Win2012R2Datacenter, Win2012Datacenter, Win2008R2SP1, UbuntuLTS, CentOS, CoreOS, Debian, openSUSE-Leap, RHEL, SLES. + +```yaml +Type: String +Parameter Sets: StrategyParameterSet +Aliases: + +Required: False +Position: Named +Default value: Win2016Datacenter +Accept pipeline input: False +Accept wildcard characters: False +``` + ### -LicenseType Specifies a license type, which indicates that the image or disk for the virtual machine was licensed on-premises. This value is used only for images that contain the Windows Server operating system. @@ -181,7 +223,7 @@ If you specify this parameter for an update, the value must match the initial va ```yaml Type: String -Parameter Sets: (All) +Parameter Sets: DefaultParameterSet Aliases: Required: False @@ -196,7 +238,7 @@ Specifies a location for the virtual machine. ```yaml Type: String -Parameter Sets: (All) +Parameter Sets: DefaultParameterSet Aliases: Required: True @@ -206,12 +248,69 @@ Accept pipeline input: True (ByPropertyName) Accept wildcard characters: False ``` +```yaml +Type: String +Parameter Sets: StrategyParameterSet +Aliases: + +Required: False +Position: 1 +Default value: None +Accept pipeline input: True (ByPropertyName) +Accept wildcard characters: False +``` + +### -Name +The name of the VM resource. + +```yaml +Type: String +Parameter Sets: StrategyParameterSet +Aliases: + +Required: True +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -OpenPorts +A list of ports to open on the network security group (NSG) for the created VM. The default value depends on the type of image chosen (i.e., Windows: 3389, 5985 and Linux: 22). + +```yaml +Type: Int32[] +Parameter Sets: StrategyParameterSet +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -PublicIpAddressName +The name of a new (or existing) public IP address for the created VM to use. If not specified, a name will be generated. + +```yaml +Type: String +Parameter Sets: StrategyParameterSet +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + ### -ResourceGroupName Specifies the name of a resource group. ```yaml Type: String -Parameter Sets: (All) +Parameter Sets: DefaultParameterSet Aliases: Required: True @@ -221,6 +320,63 @@ Accept pipeline input: True (ByPropertyName) Accept wildcard characters: False ``` +```yaml +Type: String +Parameter Sets: StrategyParameterSet +Aliases: + +Required: False +Position: 0 +Default value: None +Accept pipeline input: True (ByPropertyName) +Accept wildcard characters: False +``` + +### -SecurityGroupName +The name of a new (or existing) network security group (NSG) for the created VM to use. If not specified, a name will be generated. + +```yaml +Type: String +Parameter Sets: StrategyParameterSet +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -SubnetAddressPrefix +The address prefix for the subnet which will be created for the VM. + +```yaml +Type: String +Parameter Sets: StrategyParameterSet +Aliases: + +Required: False +Position: Named +Default value: 192.168.1.0/24 +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -SubnetName +The name of a new (or existing) subnet for the created VM to use. If not specified, a name will be generated. + +```yaml +Type: String +Parameter Sets: StrategyParameterSet +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + ### -Tags Specifies that resources and resource groups can be tagged with a set of name-value pairs. Adding tags to resources enables you to group resources together across resource groups and to create your own views. @@ -228,7 +384,7 @@ Each resource or resource group can have a maximum of 15 tags. ```yaml Type: Hashtable -Parameter Sets: (All) +Parameter Sets: DefaultParameterSet Aliases: Required: False @@ -238,6 +394,21 @@ Accept pipeline input: True (ByPropertyName) Accept wildcard characters: False ``` +### -VirtualNetworkName +The name of a new (or existing) virtual network for the created VM to use. If not specified, a name will be generated. + +```yaml +Type: String +Parameter Sets: StrategyParameterSet +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + ### -VM Specifies a local virtual machine to create. To obtain a virtual machine object, use the New-AzureRmVMConfig cmdlet. @@ -245,7 +416,7 @@ Other cmdlets can be used to configure the virtual machine, such as Set-AzureRmV ```yaml Type: PSVirtualMachine -Parameter Sets: (All) +Parameter Sets: DefaultParameterSet Aliases: VMProfile Required: True @@ -260,7 +431,7 @@ Specifies the zone list of the virtual machine. ```yaml Type: String[] -Parameter Sets: (All) +Parameter Sets: DefaultParameterSet Aliases: Required: False diff --git a/src/ResourceManager/Compute/Commands.Compute/packages.config b/src/ResourceManager/Compute/Commands.Compute/packages.config index 8b03e6aaf788..8a80bac82d2c 100644 --- a/src/ResourceManager/Compute/Commands.Compute/packages.config +++ b/src/ResourceManager/Compute/Commands.Compute/packages.config @@ -4,6 +4,11 @@ + + + + + \ No newline at end of file diff --git a/src/ResourceManager/Compute/Compute.sln b/src/ResourceManager/Compute/Compute.sln index 5a2bc6996c14..cb0cf21b9acc 100644 --- a/src/ResourceManager/Compute/Compute.sln +++ b/src/ResourceManager/Compute/Compute.sln @@ -38,9 +38,9 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Commands.Common.Authenticat EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Commands.Common.Authentication.ResourceManager", "..\Common\Commands.Common.Authentication.ResourceManager\Commands.Common.Authentication.ResourceManager.csproj", "{69C2EB6B-CD63-480A-89A0-C489706E9299}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Commands.Common.Authorization", "..\..\Common\Commands.Common.Authorization\Commands.Common.Authorization.csproj", "{24508E26-154D-47F1-80EE-439BF0710996}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Commands.Common.Strategies", "..\Common\Commands.Common.Strategies\Commands.Common.Strategies.csproj", "{EEA69772-D41B-482A-9252-2B4595C59E53}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Commands.Common.Graph.RBAC", "..\..\Common\Commands.Common.Graph.RBAC\Commands.Common.Graph.RBAC.csproj", "{269ACF73-0A34-42DC-AB9C-4B15931A489D}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Commands.Common.Strategies.UnitTest", "..\Common\Commands.Common.Strategies.UnitTest\Commands.Common.Strategies.UnitTest.csproj", "{5C052681-DA3C-4FEA-8E02-7DFBB2A18B22}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -116,14 +116,14 @@ Global {69C2EB6B-CD63-480A-89A0-C489706E9299}.Debug|Any CPU.Build.0 = Debug|Any CPU {69C2EB6B-CD63-480A-89A0-C489706E9299}.Release|Any CPU.ActiveCfg = Release|Any CPU {69C2EB6B-CD63-480A-89A0-C489706E9299}.Release|Any CPU.Build.0 = Release|Any CPU - {24508E26-154D-47F1-80EE-439BF0710996}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {24508E26-154D-47F1-80EE-439BF0710996}.Debug|Any CPU.Build.0 = Debug|Any CPU - {24508E26-154D-47F1-80EE-439BF0710996}.Release|Any CPU.ActiveCfg = Release|Any CPU - {24508E26-154D-47F1-80EE-439BF0710996}.Release|Any CPU.Build.0 = Release|Any CPU - {269ACF73-0A34-42DC-AB9C-4B15931A489D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {269ACF73-0A34-42DC-AB9C-4B15931A489D}.Debug|Any CPU.Build.0 = Debug|Any CPU - {269ACF73-0A34-42DC-AB9C-4B15931A489D}.Release|Any CPU.ActiveCfg = Release|Any CPU - {269ACF73-0A34-42DC-AB9C-4B15931A489D}.Release|Any CPU.Build.0 = Release|Any CPU + {EEA69772-D41B-482A-9252-2B4595C59E53}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {EEA69772-D41B-482A-9252-2B4595C59E53}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EEA69772-D41B-482A-9252-2B4595C59E53}.Release|Any CPU.ActiveCfg = Release|Any CPU + {EEA69772-D41B-482A-9252-2B4595C59E53}.Release|Any CPU.Build.0 = Release|Any CPU + {5C052681-DA3C-4FEA-8E02-7DFBB2A18B22}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5C052681-DA3C-4FEA-8E02-7DFBB2A18B22}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5C052681-DA3C-4FEA-8E02-7DFBB2A18B22}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5C052681-DA3C-4FEA-8E02-7DFBB2A18B22}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -131,5 +131,9 @@ Global GlobalSection(NestedProjects) = preSolution {37C44181-3F1B-4ABD-8089-26DFAB4B6BA8} = {95C16AED-FD57-42A0-86C3-2CF4300A4817} {3436A126-EDC9-4060-8952-9A1BE34CDD95} = {95C16AED-FD57-42A0-86C3-2CF4300A4817} + {5C052681-DA3C-4FEA-8E02-7DFBB2A18B22} = {95C16AED-FD57-42A0-86C3-2CF4300A4817} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {54E53EE0-76A1-4911-8FED-63555F09459F} EndGlobalSection EndGlobal diff --git a/src/ResourceManager/Network/Commands.Network/Commands.Network.csproj b/src/ResourceManager/Network/Commands.Network/Commands.Network.csproj index a3d5512ddfd2..10ea541892b8 100644 --- a/src/ResourceManager/Network/Commands.Network/Commands.Network.csproj +++ b/src/ResourceManager/Network/Commands.Network/Commands.Network.csproj @@ -554,6 +554,10 @@ {5ee72c53-1720-4309-b54b-5fb79703195f} Commands.Common + + {EEA69772-D41B-482A-9252-2B4595C59E53} + Commands.Common.Strategies + {3819d8a7-c62c-4c47-8ddd-0332d9ce1252} Commands.ResourceManager.Common diff --git a/src/ResourceManager/Network/Commands.Network/LoadBalancer/NewAzureLoadBalancerCommand.cs b/src/ResourceManager/Network/Commands.Network/LoadBalancer/NewAzureLoadBalancerCommand.cs index ef40cd3599f1..2a017d5c688a 100644 --- a/src/ResourceManager/Network/Commands.Network/LoadBalancer/NewAzureLoadBalancerCommand.cs +++ b/src/ResourceManager/Network/Commands.Network/LoadBalancer/NewAzureLoadBalancerCommand.cs @@ -190,4 +190,4 @@ private PSLoadBalancer CreateLoadBalancer() return getLoadBalancer; } } -} +} \ No newline at end of file diff --git a/src/ResourceManager/Profile/Commands.Profile/Models/SimpleAccessToken.cs b/src/ResourceManager/Profile/Commands.Profile/Models/SimpleAccessToken.cs index 17a6b7533b12..8736443defe6 100644 --- a/src/ResourceManager/Profile/Commands.Profile/Models/SimpleAccessToken.cs +++ b/src/ResourceManager/Profile/Commands.Profile/Models/SimpleAccessToken.cs @@ -50,7 +50,7 @@ public SimpleAccessToken(IAzureAccount account, string tenantId, string tokenTyp } this.UserId = account.Id; this._tokenType = tokenType; - this.AccessToken = account.GetProperty(AzureAccount.Property.AccessToken); + this.AccessToken = account.GetAccessToken(); this.TenantId = tenantId; } @@ -65,9 +65,7 @@ public SimpleAccessToken(IAzureAccount account, string tenantId, string tokenTyp /// /// The authorization token setter function. public void AuthorizeRequest(System.Action authTokenSetter) - { - authTokenSetter(_tokenType, AccessToken); - } + => authTokenSetter(_tokenType, AccessToken); /// /// The login type for this token diff --git a/tools/AzureRM/AzureRM.psm1 b/tools/AzureRM/AzureRM.psm1 index c02fe1adae19..170775ed2a15 100644 Binary files a/tools/AzureRM/AzureRM.psm1 and b/tools/AzureRM/AzureRM.psm1 differ diff --git a/tools/StaticAnalysis/BreakingChangeAnalyzer/BreakingChangeAnalyzer.cs b/tools/StaticAnalysis/BreakingChangeAnalyzer/BreakingChangeAnalyzer.cs index 67a27f05c636..b1d043475fe6 100644 --- a/tools/StaticAnalysis/BreakingChangeAnalyzer/BreakingChangeAnalyzer.cs +++ b/tools/StaticAnalysis/BreakingChangeAnalyzer/BreakingChangeAnalyzer.cs @@ -41,7 +41,7 @@ public BreakingChangeAnalyzer() } /// - /// Given a set of directory paths containing PowerShell module folders, + /// Given a set of directory paths containing PowerShell module folders, /// analyze the breaking changes in the modules and report any issues /// /// Set of directory paths containing PowerShell module folders to be checked for breaking changes. @@ -53,7 +53,7 @@ public void Analyze(IEnumerable cmdletProbingDirs) /// /// Given a set of directory paths containing PowerShell module folders, /// analyze the breaking changes in the modules and report any issues - /// + /// /// Filters can be added to find breaking changes for specific modules /// /// Set of directory paths containing PowerShell module folders to be checked for breaking changes. @@ -73,7 +73,7 @@ public void Analyze( cmdletProbingDirs = directoryFilter(cmdletProbingDirs); } - foreach (var baseDirectory in cmdletProbingDirs.Where(s => !s.Contains("ServiceManagement") && + foreach (var baseDirectory in cmdletProbingDirs.Where(s => !s.Contains("ServiceManagement") && !s.Contains("Stack") && Directory.Exists(Path.GetFullPath(s)))) { List probingDirectories = new List(); @@ -104,8 +104,8 @@ public void Analyze( var psd1FileName = Path.GetFileName(psd1); PowerShell powershell = PowerShell.Create(); - powershell.AddScript("Import-LocalizedData -BaseDirectory " + parentDirectory + - " -FileName " + psd1FileName + + powershell.AddScript("Import-LocalizedData -BaseDirectory " + parentDirectory + + " -FileName " + psd1FileName + " -BindingVariable ModuleMetadata; $ModuleMetadata.NestedModules"); var cmdletResult = powershell.Invoke(); @@ -116,17 +116,17 @@ public void Analyze( foreach (var cmdletFileName in cmdletFiles) { var cmdletFileFullPath = Path.Combine(directory, Path.GetFileName(cmdletFileName)); - + if (File.Exists(cmdletFileFullPath)) { issueLogger.Decorator.AddDecorator(a => a.AssemblyFileName = cmdletFileFullPath, "AssemblyFileName"); processedHelpFiles.Add(cmdletFileName); - var proxy = + var proxy = EnvironmentHelpers.CreateProxy(directory, out _appDomain); var newModuleMetadata = proxy.GetModuleMetadata(cmdletFileFullPath); string fileName = cmdletFileName + ".json"; - string executingPath = + string executingPath = Path.GetDirectoryName(new Uri(Assembly.GetExecutingAssembly().CodeBase).AbsolutePath); string filePath = executingPath + "\\SerializedCmdlets\\" + fileName; diff --git a/tools/StaticAnalysis/Exceptions/BreakingChangeIssues.csv b/tools/StaticAnalysis/Exceptions/BreakingChangeIssues.csv index d254632ea33e..40534946b7fe 100644 --- a/tools/StaticAnalysis/Exceptions/BreakingChangeIssues.csv +++ b/tools/StaticAnalysis/Exceptions/BreakingChangeIssues.csv @@ -766,11 +766,18 @@ "d:\workspace\powershell\src\Package\Debug\ResourceManager\AzureResourceManager\AzureRM.Sql\Microsoft.Azure.Commands.Sql.dll","Microsoft.Azure.Commands.Sql.Database.Cmdlet.SetAzureSqlDatabase","Set-AzureRmSqlDatabase","0","2100","The parameter 'ElasticPoolName' in cmdlet 'Set-AzureRmSqlDatabase' is no longer in the parameter set '__AllParameterSets'.","Add parameter 'ElasticPoolName' back to the parameter set '__AllParameterSets'." "d:\workspace\powershell\src\Package\Debug\ResourceManager\AzureResourceManager\AzureRM.Sql\Microsoft.Azure.Commands.Sql.dll","Microsoft.Azure.Commands.Sql.Database.Cmdlet.SetAzureSqlDatabase","Set-AzureRmSqlDatabase","0","2100","The parameter 'ReadScale' in cmdlet 'Set-AzureRmSqlDatabase' is no longer in the parameter set '__AllParameterSets'.","Add parameter 'ReadScale' back to the parameter set '__AllParameterSets'." "d:\workspace\powershell\src\Package\Debug\ResourceManager\AzureResourceManager\AzureRM.Sql\Microsoft.Azure.Commands.Sql.dll","Microsoft.Azure.Commands.Sql.Database.Cmdlet.SetAzureSqlDatabase","Set-AzureRmSqlDatabase","0","2100","The parameter 'Tags' in cmdlet 'Set-AzureRmSqlDatabase' is no longer in the parameter set '__AllParameterSets'.","Add parameter 'Tags' back to the parameter set '__AllParameterSets'." -"d:\workspace\powershell\src\Package\Debug\ResourceManager\AzureResourceManager\AzureRM.Compute\Microsoft.Azure.Commands.Compute.dll","Microsoft.Azure.Commands.Compute.SetAzureVMCommand","Set-AzureRmVM","0","2060","The position of parameter 'Generalized' has changed for parameter set 'GeneralizeResourceGroupNameParameterSetName' for cmdlet 'Set-AzureRmVM'.","Revert the position change made to parameter 'Generalized' for parameter set 'GeneralizeResourceGroupNameParameterSetName'." -"d:\workspace\powershell\src\Package\Debug\ResourceManager\AzureResourceManager\AzureRM.Compute\Microsoft.Azure.Commands.Compute.dll","Microsoft.Azure.Commands.Compute.SetAzureVMCommand","Set-AzureRmVM","0","2080","The parameter 'Generalized' in parameter set 'GeneralizeResourceGroupNameParameterSetName' for cmdlet 'Set-AzureRmVM' no longer has the attribute 'ValueFromPipelineByPropertyName'.","Add the attribute 'ValueFromPipelineByPropertyName' back to parameter 'Generalized' in parameter set 'GeneralizeResourceGroupNameParameterSetName'." -"d:\workspace\powershell\src\Package\Debug\ResourceManager\AzureResourceManager\AzureRM.Compute\Microsoft.Azure.Commands.Compute.dll","Microsoft.Azure.Commands.Compute.SetAzureVMCommand","Set-AzureRmVM","0","2060","The position of parameter 'Redeploy' has changed for parameter set 'RedeployResourceGroupNameParameterSetName' for cmdlet 'Set-AzureRmVM'.","Revert the position change made to parameter 'Redeploy' for parameter set 'RedeployResourceGroupNameParameterSetName'." -"d:\workspace\powershell\src\Package\Debug\ResourceManager\AzureResourceManager\AzureRM.Compute\Microsoft.Azure.Commands.Compute.dll","Microsoft.Azure.Commands.Compute.SetAzureVMCommand","Set-AzureRmVM","0","2080","The parameter 'Redeploy' in parameter set 'RedeployResourceGroupNameParameterSetName' for cmdlet 'Set-AzureRmVM' no longer has the attribute 'ValueFromPipelineByPropertyName'.","Add the attribute 'ValueFromPipelineByPropertyName' back to parameter 'Redeploy' in parameter set 'RedeployResourceGroupNameParameterSetName'." -"d:\workspace\powershell\src\Package\Debug\ResourceManager\AzureResourceManager\AzureRM.Compute\Microsoft.Azure.Commands.Compute.dll","Microsoft.Azure.Commands.Compute.SetAzureVMCommand","Set-AzureRmVM","0","2060","The position of parameter 'Generalized' has changed for parameter set 'GeneralizeIdParameterSetName' for cmdlet 'Set-AzureRmVM'.","Revert the position change made to parameter 'Generalized' for parameter set 'GeneralizeIdParameterSetName'." -"d:\workspace\powershell\src\Package\Debug\ResourceManager\AzureResourceManager\AzureRM.Compute\Microsoft.Azure.Commands.Compute.dll","Microsoft.Azure.Commands.Compute.SetAzureVMCommand","Set-AzureRmVM","0","2080","The parameter 'Generalized' in parameter set 'GeneralizeIdParameterSetName' for cmdlet 'Set-AzureRmVM' no longer has the attribute 'ValueFromPipelineByPropertyName'.","Add the attribute 'ValueFromPipelineByPropertyName' back to parameter 'Generalized' in parameter set 'GeneralizeIdParameterSetName'." -"d:\workspace\powershell\src\Package\Debug\ResourceManager\AzureResourceManager\AzureRM.Compute\Microsoft.Azure.Commands.Compute.dll","Microsoft.Azure.Commands.Compute.SetAzureVMCommand","Set-AzureRmVM","0","2060","The position of parameter 'Redeploy' has changed for parameter set 'RedeployIdParameterSetName' for cmdlet 'Set-AzureRmVM'.","Revert the position change made to parameter 'Redeploy' for parameter set 'RedeployIdParameterSetName'." -"d:\workspace\powershell\src\Package\Debug\ResourceManager\AzureResourceManager\AzureRM.Compute\Microsoft.Azure.Commands.Compute.dll","Microsoft.Azure.Commands.Compute.SetAzureVMCommand","Set-AzureRmVM","0","2080","The parameter 'Redeploy' in parameter set 'RedeployIdParameterSetName' for cmdlet 'Set-AzureRmVM' no longer has the attribute 'ValueFromPipelineByPropertyName'.","Add the attribute 'ValueFromPipelineByPropertyName' back to parameter 'Redeploy' in parameter set 'RedeployIdParameterSetName'." +"AzureRM.Compute\Microsoft.Azure.Commands.Compute.dll","Microsoft.Azure.Commands.Compute.NewAzureVMCommand","New-AzureRmVM","0","2100","The parameter 'ResourceGroupName' in cmdlet 'New-AzureRmVM' is no longer in the parameter set '__AllParameterSets'.","Add parameter 'ResourceGroupName' back to the parameter set '__AllParameterSets'." +"Microsoft.Azure.Commands.Compute.dll","Microsoft.Azure.Commands.Compute.NewAzureVMCommand","New-AzureRmVM","0","2100","The parameter 'Location' in cmdlet 'New-AzureRmVM' is no longer in the parameter set '__AllParameterSets'.","Add parameter 'Location' back to the parameter set '__AllParameterSets'." +"Microsoft.Azure.Commands.Compute.dll","Microsoft.Azure.Commands.Compute.NewAzureVMCommand","New-AzureRmVM","0","2100","The parameter 'VM' in cmdlet 'New-AzureRmVM' is no longer in the parameter set '__AllParameterSets'.","Add parameter 'VM' back to the parameter set '__AllParameterSets'." +"Microsoft.Azure.Commands.Compute.dll","Microsoft.Azure.Commands.Compute.NewAzureVMCommand","New-AzureRmVM","0","2100","The parameter 'Zone' in cmdlet 'New-AzureRmVM' is no longer in the parameter set '__AllParameterSets'.","Add parameter 'Zone' back to the parameter set '__AllParameterSets'." +"Microsoft.Azure.Commands.Compute.dll","Microsoft.Azure.Commands.Compute.NewAzureVMCommand","New-AzureRmVM","0","2100","The parameter 'DisableBginfoExtension' in cmdlet 'New-AzureRmVM' is no longer in the parameter set '__AllParameterSets'.","Add parameter 'DisableBginfoExtension' back to the parameter set '__AllParameterSets'." +"Microsoft.Azure.Commands.Compute.dll","Microsoft.Azure.Commands.Compute.NewAzureVMCommand","New-AzureRmVM","0","2100","The parameter 'Tags' in cmdlet 'New-AzureRmVM' is no longer in the parameter set '__AllParameterSets'.","Add parameter 'Tags' back to the parameter set '__AllParameterSets'." +"Microsoft.Azure.Commands.Compute.dll","Microsoft.Azure.Commands.Compute.NewAzureVMCommand","New-AzureRmVM","0","2100","The parameter 'LicenseType' in cmdlet 'New-AzureRmVM' is no longer in the parameter set '__AllParameterSets'.","Add parameter 'LicenseType' back to the parameter set '__AllParameterSets'." +"Microsoft.Azure.Commands.Compute.dll","Microsoft.Azure.Commands.Compute.SetAzureVMCommand","Set-AzureRmVM","0","2060","The position of parameter 'Generalized' has changed for parameter set 'GeneralizeResourceGroupNameParameterSetName' for cmdlet 'Set-AzureRmVM'.","Revert the position change made to parameter 'Generalized' for parameter set 'GeneralizeResourceGroupNameParameterSetName'." +"Microsoft.Azure.Commands.Compute.dll","Microsoft.Azure.Commands.Compute.SetAzureVMCommand","Set-AzureRmVM","0","2080","The parameter 'Generalized' in parameter set 'GeneralizeResourceGroupNameParameterSetName' for cmdlet 'Set-AzureRmVM' no longer has the attribute 'ValueFromPipelineByPropertyName'.","Add the attribute 'ValueFromPipelineByPropertyName' back to parameter 'Generalized' in parameter set 'GeneralizeResourceGroupNameParameterSetName'." +"Microsoft.Azure.Commands.Compute.dll","Microsoft.Azure.Commands.Compute.SetAzureVMCommand","Set-AzureRmVM","0","2060","The position of parameter 'Redeploy' has changed for parameter set 'RedeployResourceGroupNameParameterSetName' for cmdlet 'Set-AzureRmVM'.","Revert the position change made to parameter 'Redeploy' for parameter set 'RedeployResourceGroupNameParameterSetName'." +"Microsoft.Azure.Commands.Compute.dll","Microsoft.Azure.Commands.Compute.SetAzureVMCommand","Set-AzureRmVM","0","2080","The parameter 'Redeploy' in parameter set 'RedeployResourceGroupNameParameterSetName' for cmdlet 'Set-AzureRmVM' no longer has the attribute 'ValueFromPipelineByPropertyName'.","Add the attribute 'ValueFromPipelineByPropertyName' back to parameter 'Redeploy' in parameter set 'RedeployResourceGroupNameParameterSetName'." +"Microsoft.Azure.Commands.Compute.dll","Microsoft.Azure.Commands.Compute.SetAzureVMCommand","Set-AzureRmVM","0","2060","The position of parameter 'Generalized' has changed for parameter set 'GeneralizeIdParameterSetName' for cmdlet 'Set-AzureRmVM'.","Revert the position change made to parameter 'Generalized' for parameter set 'GeneralizeIdParameterSetName'." +"Microsoft.Azure.Commands.Compute.dll","Microsoft.Azure.Commands.Compute.SetAzureVMCommand","Set-AzureRmVM","0","2080","The parameter 'Generalized' in parameter set 'GeneralizeIdParameterSetName' for cmdlet 'Set-AzureRmVM' no longer has the attribute 'ValueFromPipelineByPropertyName'.","Add the attribute 'ValueFromPipelineByPropertyName' back to parameter 'Generalized' in parameter set 'GeneralizeIdParameterSetName'." +"Microsoft.Azure.Commands.Compute.dll","Microsoft.Azure.Commands.Compute.SetAzureVMCommand","Set-AzureRmVM","0","2060","The position of parameter 'Redeploy' has changed for parameter set 'RedeployIdParameterSetName' for cmdlet 'Set-AzureRmVM'.","Revert the position change made to parameter 'Redeploy' for parameter set 'RedeployIdParameterSetName'." +"Microsoft.Azure.Commands.Compute.dll","Microsoft.Azure.Commands.Compute.SetAzureVMCommand","Set-AzureRmVM","0","2080","The parameter 'Redeploy' in parameter set 'RedeployIdParameterSetName' for cmdlet 'Set-AzureRmVM' no longer has the attribute 'ValueFromPipelineByPropertyName'.","Add the attribute 'ValueFromPipelineByPropertyName' back to parameter 'Redeploy' in parameter set 'RedeployIdParameterSetName'." diff --git a/tools/StaticAnalysis/HelpAnalyzer/HelpAnalyzer.cs b/tools/StaticAnalysis/HelpAnalyzer/HelpAnalyzer.cs index a8702f60ee59..17e1129cbfbd 100644 --- a/tools/StaticAnalysis/HelpAnalyzer/HelpAnalyzer.cs +++ b/tools/StaticAnalysis/HelpAnalyzer/HelpAnalyzer.cs @@ -67,7 +67,7 @@ private static bool IsAssemblyFile(string path) return result; } /// - /// Given a set of directory paths containing PowerShell module folders, analyze the help + /// Given a set of directory paths containing PowerShell module folders, analyze the help /// in the module folders and report any issues /// /// @@ -155,7 +155,7 @@ private void AnalyzeMarkdownHelp( string savedDirectory) { var helpFolder = Directory.EnumerateDirectories(directory, "help").FirstOrDefault(); - var service = Path.GetFileName(directory); + var service = Path.GetFileName(directory); if (helpFolder == null) { helpLogger.LogRecord(new HelpIssue() @@ -226,7 +226,7 @@ private void AnalyzeMarkdownHelp( } } - private void ValidateHelpRecords(IList cmdlets, IList helpRecords, + private void ValidateHelpRecords(IList cmdlets, IList helpRecords, ReportLogger helpLogger) { foreach (var cmdlet in cmdlets) @@ -238,7 +238,7 @@ private void ValidateHelpRecords(IList cmdlets, IList