Blacklist → denylist, whitelist → allowlist (#20)
Since this is an incompatible change, also bump up version to 2.0.0 (major version change, to signal incompatible API).
This commit is contained in:
Родитель
1a2e1f3261
Коммит
0742b2035b
|
@ -2,7 +2,7 @@
|
||||||
<package>
|
<package>
|
||||||
<metadata>
|
<metadata>
|
||||||
<id>FeatureFlags.PowerShell</id>
|
<id>FeatureFlags.PowerShell</id>
|
||||||
<version>1.0.1</version>
|
<version>2.0.0</version>
|
||||||
<title>PowerShell Feature Flags</title>
|
<title>PowerShell Feature Flags</title>
|
||||||
<authors>Andrea Spadaccini,Nick Hara</authors>
|
<authors>Andrea Spadaccini,Nick Hara</authors>
|
||||||
<owners>Andrea Spadaccini</owners>
|
<owners>Andrea Spadaccini</owners>
|
||||||
|
@ -10,7 +10,7 @@
|
||||||
<projectUrl>https://github.com/microsoft/PowerShell-FeatureFlags/</projectUrl>
|
<projectUrl>https://github.com/microsoft/PowerShell-FeatureFlags/</projectUrl>
|
||||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||||
<description>PowerShell module containing a Feature Flags implementation based on a local config file.</description>
|
<description>PowerShell module containing a Feature Flags implementation based on a local config file.</description>
|
||||||
<releaseNotes>Fixed bug with the "probability" condition (issue #18).</releaseNotes>
|
<releaseNotes>Renamed blacklist to denylist, whitelist to allowlist. Incompatible with 1.0.x.</releaseNotes>
|
||||||
<copyright>Copyright 2020 Microsoft</copyright>
|
<copyright>Copyright 2020 Microsoft</copyright>
|
||||||
<repository type="git" url="https://github.com/microsoft/PowerShell-FeatureFlags.git" branch="master" />
|
<repository type="git" url="https://github.com/microsoft/PowerShell-FeatureFlags.git" branch="master" />
|
||||||
</metadata>
|
</metadata>
|
||||||
|
|
Двоичные данные
FeatureFlags.psd1
Двоичные данные
FeatureFlags.psd1
Двоичный файл не отображается.
|
@ -219,24 +219,24 @@ function Test-FeatureConditions
|
||||||
)
|
)
|
||||||
# Conditions are evaluated in the order they are presented in the configuration file.
|
# Conditions are evaluated in the order they are presented in the configuration file.
|
||||||
foreach ($condition in $conditions) {
|
foreach ($condition in $conditions) {
|
||||||
# Each condition object can have only one of the whitelist, blacklist or probability
|
# Each condition object can have only one of the allowlist, denylist or probability
|
||||||
# attributes set. This invariant is enforced by the JSON schema, which uses the "oneof"
|
# attributes set. This invariant is enforced by the JSON schema, which uses the "oneof"
|
||||||
# strategy to choose between whitelist, blacklist or probability and, for each of these
|
# strategy to choose between allowlist, denylist or probability and, for each of these
|
||||||
# condition types, only allows the homonym attribute to be set.
|
# condition types, only allows the homonym attribute to be set.
|
||||||
if ($null -ne $condition.whitelist) {
|
if ($null -ne $condition.allowlist) {
|
||||||
Write-Verbose "Checking the whitelist condition"
|
Write-Verbose "Checking the allowlist condition"
|
||||||
# The predicate must match any of the regexes in the whitelist in order to
|
# The predicate must match any of the regexes in the allowlist in order to
|
||||||
# consider the whitelist condition satisfied.
|
# consider the allowlist condition satisfied.
|
||||||
$matchesWhitelist = Test-RegexList $predicate @($condition.whitelist)
|
$matchesallowlist = Test-RegexList $predicate @($condition.allowlist)
|
||||||
if (-not $matchesWhitelist) {
|
if (-not $matchesallowlist) {
|
||||||
return $false
|
return $false
|
||||||
}
|
}
|
||||||
} elseif ($null -ne $condition.blacklist) {
|
} elseif ($null -ne $condition.denylist) {
|
||||||
Write-Verbose "Checking the blacklist condition"
|
Write-Verbose "Checking the denylist condition"
|
||||||
# The predicate must not match all of the regexes in the blacklist in order to
|
# The predicate must not match all of the regexes in the denylist in order to
|
||||||
# consider the blacklist condition satisfied.
|
# consider the denylist condition satisfied.
|
||||||
$matchesBlacklist = Test-RegexList $predicate @($condition.blacklist)
|
$matchesdenylist = Test-RegexList $predicate @($condition.denylist)
|
||||||
if ($matchesBlacklist) {
|
if ($matchesdenylist) {
|
||||||
return $false
|
return $false
|
||||||
}
|
}
|
||||||
} elseif ($null -ne $condition.probability) {
|
} elseif ($null -ne $condition.probability) {
|
||||||
|
@ -250,7 +250,7 @@ function Test-FeatureConditions
|
||||||
return $false
|
return $false
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
throw "${condition} is not a supported condition type (blacklist, whitelist or probability)."
|
throw "${condition} is not a supported condition type (denylist, allowlist or probability)."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return $true
|
return $true
|
||||||
|
|
46
README.md
46
README.md
|
@ -49,14 +49,14 @@ Imagine to have a feature flag configuration file called `features.json`:
|
||||||
{
|
{
|
||||||
"stages": {
|
"stages": {
|
||||||
"test": [
|
"test": [
|
||||||
{"whitelist": ["test.*", "dev.*"]}
|
{"allowlist": ["test.*", "dev.*"]}
|
||||||
],
|
],
|
||||||
"canary": [
|
"canary": [
|
||||||
{"whitelist": ["prod-canary"]}
|
{"allowlist": ["prod-canary"]}
|
||||||
],
|
],
|
||||||
"prod": [
|
"prod": [
|
||||||
{"whitelist": ["prod.*"]},
|
{"allowlist": ["prod.*"]},
|
||||||
{"blacklist": ["prod-canary"]}
|
{"denylist": ["prod-canary"]}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"features": {
|
"features": {
|
||||||
|
@ -122,13 +122,13 @@ An example lifecycle of a feature flag might be the following:
|
||||||
|
|
||||||
Here is how these example stages could be implemented:
|
Here is how these example stages could be implemented:
|
||||||
|
|
||||||
* Stage 1 can be implemented with a `blacklist` condition with value `.*`.
|
* Stage 1 can be implemented with a `denylist` condition with value `.*`.
|
||||||
* Stages 2 and 3 can be implemented with `whitelist` conditions.
|
* Stages 2 and 3 can be implemented with `allowlist` conditions.
|
||||||
* Stages 4 and 5 can be implemented with `probability` conditions.
|
* Stages 4 and 5 can be implemented with `probability` conditions.
|
||||||
|
|
||||||
## Conditions
|
## Conditions
|
||||||
|
|
||||||
There are two types of conditions: *deterministic* (whitelist and blacklist,
|
There are two types of conditions: *deterministic* (allowlist and denylist,
|
||||||
regex-based) and *probabilistic* (probability, expressed as a number between
|
regex-based) and *probabilistic* (probability, expressed as a number between
|
||||||
0 and 1). Conditions can be repeated if multiple instances are required.
|
0 and 1). Conditions can be repeated if multiple instances are required.
|
||||||
|
|
||||||
|
@ -138,9 +138,9 @@ in the configuration file, for the feature to be considered enabled.
|
||||||
If any condition is not met, evaluation of conditions stops and the feature
|
If any condition is not met, evaluation of conditions stops and the feature
|
||||||
is considered disabled.
|
is considered disabled.
|
||||||
|
|
||||||
### Whitelist
|
### Allow list
|
||||||
|
|
||||||
The `whitelist` condition allows to specify a list of regular expressions; if the
|
The `allowlist` condition allows to specify a list of regular expressions; if the
|
||||||
predicate matches any of the expressions, then the condition is met and the evaluation
|
predicate matches any of the expressions, then the condition is met and the evaluation
|
||||||
moves to the next condition, if there is any.
|
moves to the next condition, if there is any.
|
||||||
|
|
||||||
|
@ -150,9 +150,9 @@ unintended matches, it's recommended to always anchor the regex.
|
||||||
|
|
||||||
So, for example, `"^storage$"` will only match `"storage"` and not `"storage1"`.
|
So, for example, `"^storage$"` will only match `"storage"` and not `"storage1"`.
|
||||||
|
|
||||||
### Blacklist
|
### Deny list
|
||||||
|
|
||||||
The `blacklist` condition is analogous to the whitelist condition, except that if
|
The `denylist` condition is analogous to the allowlist condition, except that if
|
||||||
the predicate matches any of the expressions the condition is considered not met
|
the predicate matches any of the expressions the condition is considered not met
|
||||||
and the evaluation stops.
|
and the evaluation stops.
|
||||||
|
|
||||||
|
@ -172,25 +172,25 @@ the following example:
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"stages": {
|
"stages": {
|
||||||
"whitelist-first": [
|
"allowlist-first": [
|
||||||
{"whitelist": ["storage.*"]},
|
{"allowlist": ["storage.*"]},
|
||||||
{"probability": 0.1}
|
{"probability": 0.1}
|
||||||
],
|
],
|
||||||
"probability-first": [
|
"probability-first": [
|
||||||
{"probability": 0.1}
|
{"probability": 0.1}
|
||||||
{"whitelist": ["storage.*"]},
|
{"allowlist": ["storage.*"]},
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
The first stage definition, `whitelist-first`, will evaluate the `probability` condition
|
The first stage definition, `allowlist-first`, will evaluate the `probability` condition
|
||||||
only if the predicate first passes the whitelist.
|
only if the predicate first passes the allowlist.
|
||||||
|
|
||||||
The second stage definition, `probability-first`, will instead first evaluate
|
The second stage definition, `probability-first`, will instead first evaluate
|
||||||
the `probability` condition, and then apply the whitelist.
|
the `probability` condition, and then apply the allowlist.
|
||||||
|
|
||||||
Assuming there are predicates that do not match the whitelist, the second stage definition
|
Assuming there are predicates that do not match the allowlist, the second stage definition
|
||||||
is more restrictive than the first one, leading to fewer positive evaluations of the
|
is more restrictive than the first one, leading to fewer positive evaluations of the
|
||||||
feature flag.
|
feature flag.
|
||||||
|
|
||||||
|
@ -231,19 +231,19 @@ for comments. Don't add comments to your feature flag configuration file.
|
||||||
],
|
],
|
||||||
// Examples of deterministic stages.
|
// Examples of deterministic stages.
|
||||||
"all-storage": [
|
"all-storage": [
|
||||||
{"whitelist": [".*Storage.*"]},
|
{"allowlist": [".*Storage.*"]},
|
||||||
],
|
],
|
||||||
"storage-except-important": [
|
"storage-except-important": [
|
||||||
{"whitelist": [".*Storage.*"]},
|
{"allowlist": [".*Storage.*"]},
|
||||||
{"blacklist": [".*StorageImportant.*"]},
|
{"denylist": [".*StorageImportant.*"]},
|
||||||
],
|
],
|
||||||
// Example of mixed roll-out stage.
|
// Example of mixed roll-out stage.
|
||||||
// This stage will match on predicates containing the word "Storage"
|
// This stage will match on predicates containing the word "Storage"
|
||||||
// but not the word "StorageImportant", and then will consider the feature
|
// but not the word "StorageImportant", and then will consider the feature
|
||||||
// enabled in 50% of the cases.
|
// enabled in 50% of the cases.
|
||||||
"50-percent-storage-except-StorageImportant": [
|
"50-percent-storage-except-StorageImportant": [
|
||||||
{"whitelist": [".*Storage.*"]},
|
{"allowlist": [".*Storage.*"]},
|
||||||
{"blacklist": ["StorageImportant"]},
|
{"denylist": ["StorageImportant"]},
|
||||||
{"probability": 0.5},
|
{"probability": 0.5},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
{
|
{
|
||||||
"stages": {
|
"stages": {
|
||||||
"test": [
|
"test": [
|
||||||
{"whitelist": ["test.*", "dev.*"]}
|
{"allowlist": ["test.*", "dev.*"]}
|
||||||
],
|
],
|
||||||
"canary": [
|
"canary": [
|
||||||
{"whitelist": ["prod-canary"]}
|
{"allowlist": ["prod-canary"]}
|
||||||
],
|
],
|
||||||
"prod": [
|
"prod": [
|
||||||
{"whitelist": ["prod.*"]},
|
{"allowlist": ["prod.*"]},
|
||||||
{"blacklist": ["prod-canary"]}
|
{"denylist": ["prod-canary"]}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"features": {
|
"features": {
|
||||||
|
|
|
@ -29,30 +29,30 @@
|
||||||
"required": ["stages"],
|
"required": ["stages"],
|
||||||
"additionalProperties": false,
|
"additionalProperties": false,
|
||||||
"definitions": {
|
"definitions": {
|
||||||
"whitelist": {
|
"allowlist": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
"whitelist": {
|
"allowlist": {
|
||||||
"type": "array",
|
"type": "array",
|
||||||
"items": {
|
"items": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"required": ["whitelist"],
|
"required": ["allowlist"],
|
||||||
"additionalProperties": false
|
"additionalProperties": false
|
||||||
},
|
},
|
||||||
"blacklist": {
|
"denylist": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
"blacklist": {
|
"denylist": {
|
||||||
"type": "array",
|
"type": "array",
|
||||||
"items": {
|
"items": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"required": ["blacklist"],
|
"required": ["denylist"],
|
||||||
"additionalProperties": false
|
"additionalProperties": false
|
||||||
},
|
},
|
||||||
"probability": {
|
"probability": {
|
||||||
|
@ -70,11 +70,12 @@
|
||||||
"conditions": {
|
"conditions": {
|
||||||
"type": "array",
|
"type": "array",
|
||||||
"items": {
|
"items": {
|
||||||
"oneOf": [{
|
"oneOf": [
|
||||||
"$ref": "#/definitions/whitelist"
|
{
|
||||||
|
"$ref": "#/definitions/allowlist"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"$ref": "#/definitions/blacklist"
|
"$ref": "#/definitions/denylist"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"$ref": "#/definitions/probability"
|
"$ref": "#/definitions/probability"
|
||||||
|
|
|
@ -75,6 +75,19 @@ Describe 'Confirm-FeatureFlagConfig' {
|
||||||
$cfg = @"
|
$cfg = @"
|
||||||
{
|
{
|
||||||
"stags": {
|
"stags": {
|
||||||
|
"storage": [
|
||||||
|
{"allowlist": [".*storage.*"]}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"@
|
||||||
|
Confirm-FeatureFlagConfig -EA 0 $cfg | Should -Be $false
|
||||||
|
}
|
||||||
|
|
||||||
|
It 'Fails if a deprecated condition is used (whitelist)' {
|
||||||
|
$cfg = @"
|
||||||
|
{
|
||||||
|
"stages": {
|
||||||
"storage": [
|
"storage": [
|
||||||
{"whitelist": [".*storage.*"]}
|
{"whitelist": [".*storage.*"]}
|
||||||
]
|
]
|
||||||
|
@ -84,12 +97,12 @@ Describe 'Confirm-FeatureFlagConfig' {
|
||||||
Confirm-FeatureFlagConfig -EA 0 $cfg | Should -Be $false
|
Confirm-FeatureFlagConfig -EA 0 $cfg | Should -Be $false
|
||||||
}
|
}
|
||||||
|
|
||||||
It 'Fails if a condition name contains a typo (whtelist)' {
|
It 'Fails if a condition name contains a typo (allwlist)' {
|
||||||
$cfg = @"
|
$cfg = @"
|
||||||
{
|
{
|
||||||
"stages": {
|
"stages": {
|
||||||
"storage": [
|
"storage": [
|
||||||
{"whtelist": [".*storage.*"]}
|
{"allwlist": [".*storage.*"]}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -102,7 +115,7 @@ Describe 'Confirm-FeatureFlagConfig' {
|
||||||
{
|
{
|
||||||
"stages": {
|
"stages": {
|
||||||
"storage": [
|
"storage": [
|
||||||
{"whitelist": [".*storage.*"]}
|
{"allowlist": [".*storage.*"]}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"featurs": {
|
"featurs": {
|
||||||
|
@ -118,7 +131,7 @@ Describe 'Confirm-FeatureFlagConfig' {
|
||||||
{
|
{
|
||||||
"stages": {
|
"stages": {
|
||||||
"": [
|
"": [
|
||||||
{"whitelist": [".*storage.*"]}
|
{"allowlist": [".*storage.*"]}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -131,7 +144,7 @@ Describe 'Confirm-FeatureFlagConfig' {
|
||||||
{
|
{
|
||||||
"stages": {
|
"stages": {
|
||||||
" ": [
|
" ": [
|
||||||
{"whitelist": [".*storage.*"]}
|
{"allowlist": [".*storage.*"]}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -141,12 +154,12 @@ Describe 'Confirm-FeatureFlagConfig' {
|
||||||
}
|
}
|
||||||
|
|
||||||
Context 'Successful validation of simple stages' {
|
Context 'Successful validation of simple stages' {
|
||||||
It 'Succeeds with a simple stage with two whitelists' {
|
It 'Succeeds with a simple stage with two allowlists' {
|
||||||
$cfg = @"
|
$cfg = @"
|
||||||
{
|
{
|
||||||
"stages": {
|
"stages": {
|
||||||
"storage": [
|
"storage": [
|
||||||
{"whitelist": [".*storage.*", ".*compute.*"]}
|
{"allowlist": [".*storage.*", ".*compute.*"]}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -154,13 +167,13 @@ Describe 'Confirm-FeatureFlagConfig' {
|
||||||
Confirm-FeatureFlagConfig $cfg | Should -Be $true
|
Confirm-FeatureFlagConfig $cfg | Should -Be $true
|
||||||
}
|
}
|
||||||
|
|
||||||
It 'Succeeds with a simple stage with a whitelist and a blacklist' {
|
It 'Succeeds with a simple stage with a allowlist and a denylist' {
|
||||||
$cfg = @"
|
$cfg = @"
|
||||||
{
|
{
|
||||||
"stages": {
|
"stages": {
|
||||||
"storage": [
|
"storage": [
|
||||||
{"whitelist": [".*storage.*"]},
|
{"allowlist": [".*storage.*"]},
|
||||||
{"blacklist": ["ImportantStorage"]}
|
{"denylist": ["ImportantStorage"]}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -187,7 +200,7 @@ Describe 'Confirm-FeatureFlagConfig' {
|
||||||
{
|
{
|
||||||
"stages": {
|
"stages": {
|
||||||
"storage": [
|
"storage": [
|
||||||
{"whitelist": [".*storage.*"]}
|
{"allowlist": [".*storage.*"]}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"features": {
|
"features": {
|
||||||
|
@ -205,10 +218,10 @@ Describe 'Confirm-FeatureFlagConfig' {
|
||||||
{
|
{
|
||||||
"stages": {
|
"stages": {
|
||||||
"all": [
|
"all": [
|
||||||
{"whitelist": [".*"]}
|
{"allowlist": [".*"]}
|
||||||
],
|
],
|
||||||
"storage": [
|
"storage": [
|
||||||
{"whitelist": [".*storage.*"]}
|
{"allowlist": [".*storage.*"]}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"features": {
|
"features": {
|
||||||
|
@ -231,7 +244,7 @@ Describe 'Confirm-FeatureFlagConfig' {
|
||||||
{
|
{
|
||||||
"stages": {
|
"stages": {
|
||||||
"storage": [
|
"storage": [
|
||||||
{"whitelist": [".*storage.*"]}
|
{"allowlist": [".*storage.*"]}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"features": {
|
"features": {
|
||||||
|
@ -255,13 +268,13 @@ Describe 'Get-FeatureFlagConfigFromFile' {
|
||||||
}
|
}
|
||||||
|
|
||||||
Describe 'Test-FeatureFlag' {
|
Describe 'Test-FeatureFlag' {
|
||||||
Context 'Whitelist condition' {
|
Context 'allowlist condition' {
|
||||||
Context 'Simple whitelist configuration' {
|
Context 'Simple allowlist configuration' {
|
||||||
$serializedConfig = @"
|
$serializedConfig = @"
|
||||||
{
|
{
|
||||||
"stages": {
|
"stages": {
|
||||||
"all": [
|
"all": [
|
||||||
{"whitelist": [".*"]}
|
{"allowlist": [".*"]}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"features": {
|
"features": {
|
||||||
|
@ -283,12 +296,12 @@ Describe 'Test-FeatureFlag' {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Context 'Chained whitelist configuration' {
|
Context 'Chained allowlist configuration' {
|
||||||
$serializedConfig = @"
|
$serializedConfig = @"
|
||||||
{
|
{
|
||||||
"stages": {
|
"stages": {
|
||||||
"test-repo-and-branch": [
|
"test-repo-and-branch": [
|
||||||
{"whitelist": [
|
{"allowlist": [
|
||||||
"storage1/.*",
|
"storage1/.*",
|
||||||
"storage2/dev-branch"
|
"storage2/dev-branch"
|
||||||
]}
|
]}
|
||||||
|
@ -315,13 +328,13 @@ Describe 'Test-FeatureFlag' {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Context 'Blacklist condition' {
|
Context 'denylist condition' {
|
||||||
Context 'Reject-all configuration' {
|
Context 'Reject-all configuration' {
|
||||||
$serializedConfig = @"
|
$serializedConfig = @"
|
||||||
{
|
{
|
||||||
"stages": {
|
"stages": {
|
||||||
"none": [
|
"none": [
|
||||||
{"blacklist": [".*"]}
|
{"denylist": [".*"]}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"features": {
|
"features": {
|
||||||
|
@ -346,7 +359,7 @@ Describe 'Test-FeatureFlag' {
|
||||||
{
|
{
|
||||||
"stages": {
|
"stages": {
|
||||||
"all-except-important": [
|
"all-except-important": [
|
||||||
{"blacklist": ["^important$"]}
|
{"denylist": ["^important$"]}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"features": {
|
"features": {
|
||||||
|
@ -360,7 +373,7 @@ Describe 'Test-FeatureFlag' {
|
||||||
Confirm-FeatureFlagConfig $serializedConfig
|
Confirm-FeatureFlagConfig $serializedConfig
|
||||||
$config = ConvertFrom-Json $serializedConfig
|
$config = ConvertFrom-Json $serializedConfig
|
||||||
|
|
||||||
# Given that the regex is ^important$, only the exact string "important" will match the blacklist.
|
# Given that the regex is ^important$, only the exact string "important" will match the denylist.
|
||||||
It 'Allows the flag if the predicate does not match exactly' {
|
It 'Allows the flag if the predicate does not match exactly' {
|
||||||
Test-FeatureFlag "some-feature" "Storage/master" $config | Should -Be $true
|
Test-FeatureFlag "some-feature" "Storage/master" $config | Should -Be $true
|
||||||
Test-FeatureFlag "some-feature" "foo" $config | Should -Be $true
|
Test-FeatureFlag "some-feature" "foo" $config | Should -Be $true
|
||||||
|
@ -378,7 +391,7 @@ Describe 'Test-FeatureFlag' {
|
||||||
{
|
{
|
||||||
"stages": {
|
"stages": {
|
||||||
"all-except-important": [
|
"all-except-important": [
|
||||||
{"blacklist": ["storage-important/master", "storage-important2/master"]}
|
{"denylist": ["storage-important/master", "storage-important2/master"]}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"features": {
|
"features": {
|
||||||
|
@ -391,7 +404,7 @@ Describe 'Test-FeatureFlag' {
|
||||||
Confirm-FeatureFlagConfig $serializedConfig
|
Confirm-FeatureFlagConfig $serializedConfig
|
||||||
$config = ConvertFrom-Json $serializedConfig
|
$config = ConvertFrom-Json $serializedConfig
|
||||||
|
|
||||||
It 'Allows predicates not matching the blacklist' {
|
It 'Allows predicates not matching the denylist' {
|
||||||
Test-FeatureFlag "some-feature" "storage1/master" $config | Should -Be $true
|
Test-FeatureFlag "some-feature" "storage1/master" $config | Should -Be $true
|
||||||
Test-FeatureFlag "some-feature" "storage2/master" $config | Should -Be $true
|
Test-FeatureFlag "some-feature" "storage2/master" $config | Should -Be $true
|
||||||
Test-FeatureFlag "some-feature" "storage-important/dev" $config | Should -Be $true
|
Test-FeatureFlag "some-feature" "storage-important/dev" $config | Should -Be $true
|
||||||
|
@ -404,13 +417,13 @@ Describe 'Test-FeatureFlag' {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Context 'Mixed whitelist/blacklist configuration' {
|
Context 'Mixed allowlist/denylist configuration' {
|
||||||
$serializedConfig = @"
|
$serializedConfig = @"
|
||||||
{
|
{
|
||||||
"stages": {
|
"stages": {
|
||||||
"all-storage-important": [
|
"all-storage-important": [
|
||||||
{"whitelist": ["storage.*"]},
|
{"allowlist": ["storage.*"]},
|
||||||
{"blacklist": ["storage-important/master", "storage-important2/master"]}
|
{"denylist": ["storage-important/master", "storage-important2/master"]}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"features": {
|
"features": {
|
||||||
|
@ -509,13 +522,13 @@ Describe 'Test-FeatureFlag' {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Context 'Complex whitelist + blacklist + probability configuration' {
|
Context 'Complex allowlist + denylist + probability configuration' {
|
||||||
$serializedConfig = @"
|
$serializedConfig = @"
|
||||||
{
|
{
|
||||||
"stages": {
|
"stages": {
|
||||||
"all-storage-important-50pc": [
|
"all-storage-important-50pc": [
|
||||||
{"whitelist": ["storage.*"]},
|
{"allowlist": ["storage.*"]},
|
||||||
{"blacklist": ["storage-important/master", "storage-important2/master"]},
|
{"denylist": ["storage-important/master", "storage-important2/master"]},
|
||||||
{"probability": 0.5}
|
{"probability": 0.5}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
@ -589,7 +602,7 @@ Describe 'Out-EvaluatedFeaturesFiles' -Tag Features {
|
||||||
Mock -ModuleName $ModuleName Add-Content { ${global:featuresIniContent}.Add($Value) } -ParameterFilter { $Path.EndsWith("features.ini") }
|
Mock -ModuleName $ModuleName Add-Content { ${global:featuresIniContent}.Add($Value) } -ParameterFilter { $Path.EndsWith("features.ini") }
|
||||||
Mock -ModuleName $ModuleName Add-Content { ${global:featuresEnvConfigContent}.Add($Value) } -ParameterFilter { $Path.EndsWith("features.env.config") }
|
Mock -ModuleName $ModuleName Add-Content { ${global:featuresEnvConfigContent}.Add($Value) } -ParameterFilter { $Path.EndsWith("features.env.config") }
|
||||||
|
|
||||||
It 'Honors blacklist' {
|
It 'Honors denylist' {
|
||||||
$features = Get-EvaluatedFeatureFlags -predicate "important" -config $config
|
$features = Get-EvaluatedFeatureFlags -predicate "important" -config $config
|
||||||
$expectedFeaturesIniContent = @("filetracker`tfalse", "newestfeature`tfalse", "testfeature`tfalse")
|
$expectedFeaturesIniContent = @("filetracker`tfalse", "newestfeature`tfalse", "testfeature`tfalse")
|
||||||
$expectedFeaturesEnvConfigContent = @()
|
$expectedFeaturesEnvConfigContent = @()
|
||||||
|
|
|
@ -2,28 +2,28 @@
|
||||||
"stages": {
|
"stages": {
|
||||||
"all-except-important": [
|
"all-except-important": [
|
||||||
{
|
{
|
||||||
"blacklist": [
|
"denylist": [
|
||||||
"^important$"
|
"^important$"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"poc": [
|
"poc": [
|
||||||
{
|
{
|
||||||
"whitelist": [
|
"allowlist": [
|
||||||
"demo/.*demofeature.*"
|
"demo/.*demofeature.*"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"dev": [
|
"dev": [
|
||||||
{
|
{
|
||||||
"whitelist": [
|
"allowlist": [
|
||||||
"Dev.*"
|
"Dev.*"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"test": [
|
"test": [
|
||||||
{
|
{
|
||||||
"whitelist": [
|
"allowlist": [
|
||||||
"demo/.*test.*/.*Official.*",
|
"demo/.*test.*/.*Official.*",
|
||||||
"demo2/.*test2.*"
|
"demo2/.*test2.*"
|
||||||
]
|
]
|
||||||
|
@ -31,7 +31,7 @@
|
||||||
],
|
],
|
||||||
"prod": [
|
"prod": [
|
||||||
{
|
{
|
||||||
"whitelist": [
|
"allowlist": [
|
||||||
"production/.*"
|
"production/.*"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
{
|
{
|
||||||
"stages": {
|
"stages": {
|
||||||
"all-storage": [
|
"all-storage": [
|
||||||
{"whitelist": [".*Storage.*"]}
|
{"allowlist": [".*Storage.*"]}
|
||||||
],
|
],
|
||||||
"specific-storage-repos": [
|
"specific-storage-repos": [
|
||||||
{"whitelist": ["Storage-repo1", "storage-repo2"]}
|
{"allowlist": ["Storage-repo1", "storage-repo2"]}
|
||||||
],
|
],
|
||||||
"all-storage-except-important": [
|
"all-storage-except-important": [
|
||||||
{"whitelist": [".*Storage.*"]},
|
{"allowlist": [".*Storage.*"]},
|
||||||
{"blacklist": ["Storage-important"]}
|
{"denylist": ["Storage-important"]}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"stages": {
|
"stages": {
|
||||||
"all-storage": [
|
"all-storage": [
|
||||||
{"whitelist": [".*Storage.*"]}
|
{"allowlist": [".*Storage.*"]}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Загрузка…
Ссылка в новой задаче