Skip to content

Commit 8d70507

Browse files
authored
EnableDataCollection and DisplayBreakingChangeWarnings configs (#318)
* configs: data collection; survey; warning message * revert survey config * do not obsolete AzurePSDataCollectionProfile(bool enable) * remove s in DisplayBreakingChangeWarnings * Add a config scope: Environment * refactor breaking change / preview attribute * add scope to config filter
1 parent a346bb0 commit 8d70507

10 files changed

+132
-124
lines changed

src/Authentication.Abstractions/AzurePSDataCollectionProfile.cs

+28-4
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
// limitations under the License.
1313
// ----------------------------------------------------------------------------------
1414

15-
using Newtonsoft.Json;
15+
using Microsoft.Azure.PowerShell.Common.Config;
1616
using System;
1717

1818
namespace Microsoft.Azure.Commands.Common.Authentication.Abstractions
@@ -23,16 +23,40 @@ public class AzurePSDataCollectionProfile
2323
OldDefaultFileName = "AzureDataCollectionProfile.json",
2424
DefaultFileName = "AzurePSDataCollectionProfile.json";
2525

26+
private bool? _enableAzureDataCollection = null;
27+
28+
/// <summary>
29+
/// Creates a data collection profile who relies on config API to determine whether it is enabled.
30+
/// </summary>
2631
public AzurePSDataCollectionProfile()
2732
{
2833
}
2934

35+
/// <summary>
36+
/// Creates a data collection profile with a predefined state of whether it is enabled.
37+
/// </summary>
3038
public AzurePSDataCollectionProfile(bool enable)
3139
{
32-
EnableAzureDataCollection = enable;
40+
_enableAzureDataCollection = enable;
3341
}
3442

35-
[JsonProperty(PropertyName = "enableAzureDataCollection")]
36-
public bool? EnableAzureDataCollection { get; set; }
43+
public bool? EnableAzureDataCollection {
44+
get
45+
{
46+
if (_enableAzureDataCollection.HasValue)
47+
{
48+
return _enableAzureDataCollection.Value;
49+
}
50+
if (AzureSession.Instance.TryGetComponent<IConfigManager>(nameof(IConfigManager), out var configManager))
51+
{
52+
_enableAzureDataCollection = configManager.GetConfigValue<bool>(ConfigKeysForCommon.EnableDataCollection);
53+
}
54+
return _enableAzureDataCollection;
55+
}
56+
set
57+
{
58+
throw new NotSupportedException("Setting data collection directly is not supported. Use Config API instead.");
59+
}
60+
}
3761
}
3862
}

src/Authentication.Abstractions/DataCollectionController.cs

+4-69
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,8 @@
1212
// limitations under the License.
1313
// ----------------------------------------------------------------------------------
1414

15-
using Newtonsoft.Json;
15+
using Microsoft.Azure.PowerShell.Common.Config;
1616
using System;
17-
using System.IO;
1817

1918
namespace Microsoft.Azure.Commands.Common.Authentication.Abstractions
2019
{
@@ -25,60 +24,12 @@ public abstract class DataCollectionController
2524

2625
static AzurePSDataCollectionProfile Initialize(IAzureSession session)
2726
{
28-
AzurePSDataCollectionProfile result = new AzurePSDataCollectionProfile(true);
29-
try
30-
{
31-
var environmentValue = Environment.GetEnvironmentVariable(AzurePSDataCollectionProfile.EnvironmentVariableName);
32-
bool enabled = true;
33-
if (!string.IsNullOrWhiteSpace(environmentValue) && bool.TryParse(environmentValue, out enabled))
34-
{
35-
result.EnableAzureDataCollection = enabled;
36-
}
37-
else
38-
{
39-
var store = session.DataStore;
40-
string dataPath = Path.Combine(session.ProfileDirectory, AzurePSDataCollectionProfile.DefaultFileName);
41-
if (store.FileExists(dataPath))
42-
{
43-
string contents = store.ReadFileAsText(dataPath);
44-
var localResult = JsonConvert.DeserializeObject<AzurePSDataCollectionProfile>(contents);
45-
if (localResult != null && localResult.EnableAzureDataCollection.HasValue)
46-
{
47-
result = localResult;
48-
}
49-
}
50-
else
51-
{
52-
WritePSDataCollectionProfile(session, new AzurePSDataCollectionProfile(true));
53-
}
54-
}
55-
}
56-
catch
57-
{
58-
// do not throw for i/o or serialization errors
59-
}
60-
61-
return result;
27+
return new AzurePSDataCollectionProfile();
6228
}
6329

30+
[Obsolete("Use config API to update data collection settings.")]
6431
public static void WritePSDataCollectionProfile(IAzureSession session, AzurePSDataCollectionProfile profile)
6532
{
66-
try
67-
{
68-
var store = session.DataStore;
69-
string dataPath = Path.Combine(session.ProfileDirectory, AzurePSDataCollectionProfile.DefaultFileName);
70-
if (!store.DirectoryExists(session.ProfileDirectory))
71-
{
72-
store.CreateDirectory(session.ProfileDirectory);
73-
}
74-
75-
string contents = JsonConvert.SerializeObject(profile);
76-
store.WriteFile(dataPath, contents);
77-
}
78-
catch
79-
{
80-
// do not throw for i/o or serialization errors
81-
}
8233
}
8334

8435
public static DataCollectionController Create(IAzureSession session)
@@ -93,33 +44,17 @@ public static DataCollectionController Create(AzurePSDataCollectionProfile profi
9344

9445
class MemoryDataCollectionController : DataCollectionController
9546
{
96-
object _lock;
97-
bool? _enabled;
98-
9947
public MemoryDataCollectionController()
10048
{
101-
_lock = new object();
102-
_enabled = null;
10349
}
10450

10551
public MemoryDataCollectionController(AzurePSDataCollectionProfile enabled)
10652
{
107-
_lock = new object();
108-
_enabled = enabled?.EnableAzureDataCollection;
10953
}
11054

11155
public override AzurePSDataCollectionProfile GetProfile(Action warningWriter)
11256
{
113-
lock (_lock)
114-
{
115-
if (!_enabled.HasValue)
116-
{
117-
_enabled = true;
118-
warningWriter();
119-
}
120-
121-
return new AzurePSDataCollectionProfile(_enabled.Value);
122-
}
57+
return new AzurePSDataCollectionProfile();
12358
}
12459
}
12560
}

src/Authentication.Abstractions/Interfaces/IConfigManager.cs

-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212
// limitations under the License.
1313
// ----------------------------------------------------------------------------------
1414

15-
using System;
1615
using System.Collections.Generic;
1716

1817
namespace Microsoft.Azure.PowerShell.Common.Config

src/Authentication.Abstractions/Models/ConfigDefinition.cs

+29-1
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,35 @@ public abstract class ConfigDefinition
4343
/// <summary>
4444
/// Gets the name of the environment variable that can control this config.
4545
/// </summary>
46-
public virtual string EnvironmentVariableName { get; } = null;
46+
/// <remarks>
47+
/// For most cases, where the config is connected to only 1 environment variable,
48+
/// and the value can be parsed without extra logic, use this property,
49+
/// else override <see cref="ParseFromEnvironmentVariables(IReadOnlyDictionary{string, string})"/>.
50+
/// </remarks>
51+
protected virtual string EnvironmentVariableName { get; } = null;
52+
53+
/// <summary>
54+
/// Customizes how to parse config value from environment variables.
55+
/// </summary>
56+
/// <param name="environmentVariables">All the environment variables.</param>
57+
/// <returns>The parsed config value, in string. Returns null if the environment variable of this config is not set.</returns>
58+
/// <remarks>
59+
/// Note: use <see cref="EnvironmentVariableName"/> if there's no need for customized parsing logic. <br />
60+
/// The return type is string because it will be further parsed into other types (int, bool...) by the config provider.
61+
/// Make sure the return value matches <see cref="ValueType"/>.
62+
/// For example if <see cref="ValueType"/> is bool, the return value cannot be like "Disabled".
63+
/// </remarks>
64+
public virtual string ParseFromEnvironmentVariables(IReadOnlyDictionary<string, string> environmentVariables)
65+
{
66+
if (string.IsNullOrEmpty(EnvironmentVariableName))
67+
{
68+
return null;
69+
}
70+
else
71+
{
72+
return environmentVariables.TryGetValue(EnvironmentVariableName, out string value) ? value : null;
73+
}
74+
}
4775

4876
/// <summary>
4977
/// Gets how the config can be applied to.

src/Authentication.Abstractions/Models/ConfigFilter.cs

+6
Original file line numberDiff line numberDiff line change
@@ -42,5 +42,11 @@ public class ConfigFilter
4242
/// - Name of a cmdlet: the config applies to a certain cmdlet of Azure PowerShell. For example, "Get-AzKeyVault".
4343
/// </remarks>
4444
public string AppliesTo { get; set; } = null;
45+
46+
/// <summary>
47+
/// Scope of the configs to filter. By default all scopes are included.
48+
/// </summary>
49+
/// <value></value>
50+
public ConfigScope? Scope { get; set; } = null;
4551
}
4652
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// ----------------------------------------------------------------------------------
2+
//
3+
// Copyright Microsoft Corporation
4+
// Licensed under the Apache License, Version 2.0 (the "License");
5+
// you may not use this file except in compliance with the License.
6+
// You may obtain a copy of the License at
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
// Unless required by applicable law or agreed to in writing, software
9+
// distributed under the License is distributed on an "AS IS" BASIS,
10+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+
// See the License for the specific language governing permissions and
12+
// limitations under the License.
13+
// ----------------------------------------------------------------------------------
14+
15+
namespace Microsoft.Azure.PowerShell.Common.Config
16+
{
17+
/// <summary>
18+
/// This class stores keys of pre-defined configs.
19+
/// </summary>
20+
/// <remarks>
21+
/// All keys should be defined in ConfigKeys class in Azure/azure-powershell repo.
22+
/// If the key is used in common code, duplicate it here.
23+
/// Keys defined here should NEVER be removed or changed to prevent breaking change.
24+
/// </remarks>
25+
public static class ConfigKeysForCommon
26+
{
27+
public const string EnableInterceptSurvey = "EnableInterceptSurvey";
28+
public const string DisplayBreakingChangeWarning = "DisplayBreakingChangeWarning";
29+
public const string EnableDataCollection = "EnableDataCollection";
30+
}
31+
}

src/Authentication.Abstractions/Models/ConfigScope.cs

+8-2
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ namespace Microsoft.Azure.PowerShell.Common.Config
2020
public enum ConfigScope
2121
{
2222
/// <summary>
23-
/// Config will be persitent on the disk, available for all the PowerShell sessions initiated by the current user.
23+
/// Config will be persistent on the disk, available for all the PowerShell sessions initiated by the current user.
2424
/// </summary>
2525
CurrentUser,
2626

@@ -33,6 +33,12 @@ public enum ConfigScope
3333
/// Config is never set.
3434
/// </summary>
3535
/// <remarks>This option is not available when updating or clearing a config.</remarks>
36-
Default
36+
Default,
37+
38+
/// <summary>
39+
/// Config is set by environment variables.
40+
/// </summary>
41+
/// <remarks>This option is not available when updating or clearing a config.</remarks>
42+
Environment
3743
}
3844
}

src/Common/AzurePSCmdlet.cs

+19-5
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
using Microsoft.Azure.Commands.Common.Authentication;
1616
using Microsoft.Azure.Commands.Common.Authentication.Abstractions;
17+
using Microsoft.Azure.PowerShell.Common.Config;
1718
using Microsoft.Azure.PowerShell.Common.Share.Survey;
1819
using Microsoft.Azure.ServiceManagement.Common.Models;
1920
using Microsoft.WindowsAzure.Commands.Common;
@@ -65,7 +66,7 @@ protected AzurePSDataCollectionProfile _dataCollectionProfile
6566
}
6667
else if (_cachedProfile == null)
6768
{
68-
_cachedProfile = new AzurePSDataCollectionProfile(true);
69+
_cachedProfile = new AzurePSDataCollectionProfile();
6970
WriteWarning(DataCollectionWarning);
7071
}
7172

@@ -361,8 +362,17 @@ protected override void BeginProcessing()
361362

362363
//Now see if the cmdlet has any Breaking change attributes on it and process them if it does
363364
//This will print any breaking change attribute messages that are applied to the cmdlet
364-
BreakingChangeAttributeHelper.ProcessCustomAttributesAtRuntime(this.GetType(), this.MyInvocation, WriteWarning);
365-
PreviewAttributeHelper.ProcessCustomAttributesAtRuntime(this.GetType(), this.MyInvocation, WriteDebug);
365+
WriteBreakingChangeOrPreviewMessage();
366+
}
367+
368+
private void WriteBreakingChangeOrPreviewMessage()
369+
{
370+
if (AzureSession.Instance.TryGetComponent<IConfigManager>(nameof(IConfigManager), out var configManager)
371+
&& configManager.GetConfigValue<bool>(ConfigKeysForCommon.DisplayBreakingChangeWarning))
372+
{
373+
BreakingChangeAttributeHelper.ProcessCustomAttributesAtRuntime(this.GetType(), this.MyInvocation, WriteWarning);
374+
PreviewAttributeHelper.ProcessCustomAttributesAtRuntime(this.GetType(), this.MyInvocation, WriteDebug);
375+
}
366376
}
367377

368378
/// <summary>
@@ -451,7 +461,7 @@ protected void WriteSurvey()
451461
Message = "Open-AzSurveyLink",
452462
NoNewLine = true,
453463
};
454-
}
464+
}
455465
HostInformationMessage action = new HostInformationMessage()
456466
{
457467
Message = " to fill out a short Survey"
@@ -471,7 +481,11 @@ protected void WriteSurvey()
471481
_qosEvent.IsSuccess = false;
472482
}
473483
base.WriteError(errorRecord);
474-
PreviewAttributeHelper.ProcessCustomAttributesAtRuntime(this.GetType(), this.MyInvocation, WriteWarning);
484+
if (AzureSession.Instance.TryGetComponent<IConfigManager>(nameof(IConfigManager), out var configManager)
485+
&& configManager.GetConfigValue<bool>(ConfigKeysForCommon.DisplayBreakingChangeWarning))
486+
{
487+
PreviewAttributeHelper.ProcessCustomAttributesAtRuntime(this.GetType(), this.MyInvocation, WriteWarning);
488+
}
475489
}
476490

477491
protected new void ThrowTerminatingError(ErrorRecord errorRecord)

0 commit comments

Comments
 (0)