Родитель
91dfa1834d
Коммит
a6e2112827
|
@ -2,17 +2,19 @@
|
|||
# CI pipeline for PSRule.Monitor
|
||||
|
||||
variables:
|
||||
version: '0.1.0'
|
||||
version: '0.2.0'
|
||||
buildConfiguration: 'Release'
|
||||
disable.coverage.autogenerate: 'true'
|
||||
imageName: 'ubuntu-18.04'
|
||||
|
||||
# Use build number format, i.e. 0.1.0-B1811001
|
||||
# Use build number format, i.e. 0.2.0-B1811001
|
||||
name: $(version)-B$(date:yyMM)$(rev:rrr)
|
||||
|
||||
trigger:
|
||||
branches:
|
||||
include:
|
||||
- 'main'
|
||||
- 'release/*'
|
||||
tags:
|
||||
include:
|
||||
- 'v0.*'
|
||||
|
@ -21,31 +23,19 @@ pr:
|
|||
branches:
|
||||
include:
|
||||
- 'main'
|
||||
- 'release/*'
|
||||
|
||||
stages:
|
||||
|
||||
# Build pipeline
|
||||
- stage: Build
|
||||
displayName: Build
|
||||
dependsOn: []
|
||||
jobs:
|
||||
- job:
|
||||
strategy:
|
||||
matrix:
|
||||
Linux:
|
||||
displayName: 'Linux'
|
||||
imageName: 'ubuntu-latest'
|
||||
MacOS:
|
||||
displayName: 'MacOS'
|
||||
imageName: 'macOS-latest'
|
||||
Windows:
|
||||
displayName: 'Windows'
|
||||
imageName: 'vs2017-win2016'
|
||||
publish: 'true'
|
||||
analysis: 'true'
|
||||
coverage: 'true'
|
||||
pool:
|
||||
vmImage: $(imageName)
|
||||
displayName: 'PowerShell'
|
||||
displayName: 'Module'
|
||||
steps:
|
||||
|
||||
# Install pipeline dependencies
|
||||
|
@ -56,43 +46,13 @@ stages:
|
|||
- powershell: Invoke-Build -Configuration $(buildConfiguration) -Build $(Build.BuildNumber)
|
||||
displayName: 'Build module'
|
||||
|
||||
# Run SonarCloud analysis
|
||||
- powershell: dotnet tool install --global dotnet-sonarscanner
|
||||
displayName: 'Install Sonar scanner'
|
||||
condition: and(ne(variables['Build.Reason'], 'PullRequest'), eq(variables['analysis'], 'true'))
|
||||
|
||||
- script: dotnet sonarscanner begin /k:"BernieWhite_PSRule_Monitor" /o:"berniewhite-github" /d:sonar.host.url="https://sonarcloud.io" /d:sonar.login=$(sonarQubeToken) /v:"$(Build.BuildNumber)" /d:sonar.cs.vscoveragexml.reportsPaths="reports/" /d:sonar.cs.xunit.reportsPaths="reports/"
|
||||
displayName: 'Prepare SonarCloud'
|
||||
condition: and(ne(variables['Build.Reason'], 'PullRequest'), eq(variables['analysis'], 'true'))
|
||||
|
||||
- script: dotnet build
|
||||
displayName: 'Build solution for analysis'
|
||||
condition: and(ne(variables['Build.Reason'], 'PullRequest'), eq(variables['analysis'], 'true'))
|
||||
|
||||
- script: dotnet sonarscanner end /d:sonar.login=$(sonarQubeToken)
|
||||
displayName: 'Complete SonarCloud'
|
||||
condition: and(ne(variables['Build.Reason'], 'PullRequest'), eq(variables['analysis'], 'true'))
|
||||
|
||||
# DotNet test results
|
||||
# - task: PublishTestResults@2
|
||||
# displayName: 'Publish unit test results'
|
||||
# inputs:
|
||||
# testRunTitle: 'DotNet on $(imageName)'
|
||||
# testRunner: VSTest
|
||||
# testResultsFiles: 'reports/*.trx'
|
||||
# mergeTestResults: true
|
||||
# platform: $(imageName)
|
||||
# configuration: $(buildConfiguration)
|
||||
# publishRunAttachments: true
|
||||
# condition: succeededOrFailed()
|
||||
|
||||
# Pester test results
|
||||
- task: PublishTestResults@2
|
||||
displayName: 'Publish Pester results'
|
||||
displayName: 'Publish unit test results'
|
||||
inputs:
|
||||
testRunTitle: 'Pester on $(imageName)'
|
||||
testRunner: NUnit
|
||||
testResultsFiles: 'reports/pester-unit.xml'
|
||||
testRunTitle: 'DotNet on $(imageName)'
|
||||
testRunner: VSTest
|
||||
testResultsFiles: 'reports/*.trx'
|
||||
mergeTestResults: true
|
||||
platform: $(imageName)
|
||||
configuration: $(buildConfiguration)
|
||||
|
@ -100,66 +60,141 @@ stages:
|
|||
condition: succeededOrFailed()
|
||||
|
||||
# PSRule results
|
||||
# - task: PublishTestResults@2
|
||||
# displayName: 'Publish PSRule results'
|
||||
# inputs:
|
||||
# testRunTitle: 'PSRule on $(imageName)'
|
||||
# testRunner: NUnit
|
||||
# testResultsFiles: 'reports/rule.report.xml'
|
||||
# mergeTestResults: true
|
||||
# platform: $(imageName)
|
||||
# configuration: $(buildConfiguration)
|
||||
# publishRunAttachments: true
|
||||
# condition: succeededOrFailed()
|
||||
|
||||
# Generate Code Coverage report
|
||||
- task: Palmmedia.reportgenerator.reportgenerator-build-release-task.reportgenerator@4
|
||||
displayName: 'Code coverage report generator'
|
||||
- task: PublishTestResults@2
|
||||
displayName: 'Publish PSRule results'
|
||||
inputs:
|
||||
reports: 'reports\pester-coverage.xml'
|
||||
targetdir: 'reports\coverage'
|
||||
sourcedirs: 'src\PSRule.Monitor'
|
||||
reporttypes: 'HtmlInline_AzurePipelines;Cobertura;Badges'
|
||||
tag: $(Build.BuildNumber)
|
||||
condition: eq(variables['coverage'], 'true')
|
||||
|
||||
# Publish Code Coverage report
|
||||
- task: PublishCodeCoverageResults@1
|
||||
displayName: 'Publish Pester code coverage'
|
||||
inputs:
|
||||
codeCoverageTool: 'Cobertura'
|
||||
summaryFileLocation: 'reports/coverage/Cobertura.xml'
|
||||
reportDirectory: 'reports/coverage'
|
||||
condition: eq(variables['coverage'], 'true')
|
||||
testRunTitle: 'PSRule on $(imageName)'
|
||||
testRunner: NUnit
|
||||
testResultsFiles: 'reports/ps-rule*.xml'
|
||||
mergeTestResults: true
|
||||
platform: $(imageName)
|
||||
configuration: $(buildConfiguration)
|
||||
publishRunAttachments: true
|
||||
condition: succeededOrFailed()
|
||||
|
||||
# Generate artifacts
|
||||
- task: PublishPipelineArtifact@0
|
||||
- publish: out/modules/PSRule.Monitor
|
||||
displayName: 'Publish module'
|
||||
artifact: PSRule.Monitor
|
||||
|
||||
# Analysis pipeline
|
||||
- stage: Analysis
|
||||
displayName: Analysis
|
||||
dependsOn: []
|
||||
variables:
|
||||
skipComponentGovernanceDetection: true
|
||||
jobs:
|
||||
- job:
|
||||
pool:
|
||||
vmImage: $(imageName)
|
||||
displayName: 'SonarCloud'
|
||||
condition: not(eq(variables['Build.Reason'], 'PullRequest'))
|
||||
steps:
|
||||
|
||||
- script: |
|
||||
echo "##vso[task.setvariable variable=JAVA_HOME]$(JAVA_HOME_11_X64)"
|
||||
echo "##vso[task.setvariable variable=PATH]$(JAVA_HOME_11_X64)\bin;$(PATH)"
|
||||
displayName: 'Set Java version'
|
||||
|
||||
# Run SonarCloud analysis
|
||||
- script: dotnet tool install --global dotnet-sonarscanner
|
||||
displayName: 'Install Sonar scanner'
|
||||
|
||||
- script: $HOME/.dotnet/tools/dotnet-sonarscanner begin /k:"BernieWhite_PSRule_Monitor" /o:"berniewhite-github" /d:sonar.host.url="https://sonarcloud.io" /d:sonar.login=$(sonarQubeToken) /v:"$(Build.BuildNumber)" /d:sonar.cs.vscoveragexml.reportsPaths="reports/" /d:sonar.cs.xunit.reportsPaths="reports/"
|
||||
displayName: 'Prepare SonarCloud'
|
||||
|
||||
- script: dotnet build
|
||||
displayName: 'Build solution for analysis'
|
||||
|
||||
- script: $HOME/.dotnet/tools/dotnet-sonarscanner end /d:sonar.login=$(sonarQubeToken)
|
||||
displayName: 'Complete SonarCloud'
|
||||
|
||||
- job: Secret_Scan
|
||||
pool: 'Hosted VS2017'
|
||||
displayName: Secret scan
|
||||
steps:
|
||||
- task: securedevelopmentteam.vss-secure-development-tools.build-task-credscan.CredScan@2
|
||||
displayName: 'Scan for secrets'
|
||||
inputs:
|
||||
artifactName: PSRule.Monitor
|
||||
targetPath: out/modules/PSRule.Monitor
|
||||
condition: and(succeeded(), eq(variables['publish'], 'true'))
|
||||
debugMode: false
|
||||
toolMajorVersion: V2
|
||||
|
||||
- task: securedevelopmentteam.vss-secure-development-tools.build-task-publishsecurityanalysislogs.PublishSecurityAnalysisLogs@2
|
||||
displayName: 'Publish scan logs'
|
||||
continueOnError: true
|
||||
|
||||
- task: securedevelopmentteam.vss-secure-development-tools.build-task-postanalysis.PostAnalysis@1
|
||||
displayName: 'Check for failures'
|
||||
inputs:
|
||||
CredScan: true
|
||||
ToolLogsNotFoundAction: Error
|
||||
|
||||
# Test pipeline
|
||||
- stage: Test
|
||||
dependsOn: Build
|
||||
jobs:
|
||||
|
||||
- template: jobs/test.yaml
|
||||
parameters:
|
||||
name: ubuntu_18_04_coverage
|
||||
imageName: 'ubuntu-18.04'
|
||||
displayName: 'PowerShell coverage'
|
||||
coverage: 'false'
|
||||
publishResults: 'false'
|
||||
|
||||
- template: jobs/test.yaml
|
||||
parameters:
|
||||
name: macOS_10_15
|
||||
displayName: 'PowerShell 7.1 - macOS-10.15'
|
||||
imageName: 'macOS-10.15'
|
||||
|
||||
- template: jobs/test.yaml
|
||||
parameters:
|
||||
name: windows
|
||||
displayName: 'PowerShell 5.1 - win2016'
|
||||
imageName: 'vs2017-win2016'
|
||||
|
||||
- template: jobs/testContainer.yaml
|
||||
parameters:
|
||||
name: ps_7_ubuntu_18_04
|
||||
displayName: 'PowerShell 7.0 - ubuntu-18.04'
|
||||
imageName: mcr.microsoft.com/powershell
|
||||
imageTag: 7.0.3-ubuntu-18.04
|
||||
|
||||
- template: jobs/testContainer.yaml
|
||||
parameters:
|
||||
name: ps_7_1_ubuntu_20_04
|
||||
displayName: 'PowerShell 7.1 - ubuntu-20.04'
|
||||
imageName: mcr.microsoft.com/powershell
|
||||
imageTag: 7.1.3-ubuntu-20.04
|
||||
|
||||
- template: jobs/testContainer.yaml
|
||||
parameters:
|
||||
name: ps_6_ubuntu_18_04
|
||||
displayName: 'PowerShell 6.2 - ubuntu-18.04'
|
||||
imageName: mcr.microsoft.com/powershell
|
||||
imageTag: 6.2.4-ubuntu-18.04
|
||||
|
||||
# Release pipeline
|
||||
- stage: Release
|
||||
displayName: Release
|
||||
dependsOn: Build
|
||||
dependsOn: [ 'Test', 'Analysis' ]
|
||||
condition: and(succeeded(), startsWith(variables['Build.SourceBranch'], 'refs/tags/v0.'))
|
||||
jobs:
|
||||
- job:
|
||||
displayName: Live
|
||||
pool:
|
||||
vmImage: 'ubuntu-latest'
|
||||
vmImage: $(imageName)
|
||||
variables:
|
||||
isPreRelease: $[contains(variables['Build.SourceBranchName'], '-B')]
|
||||
steps:
|
||||
|
||||
# Download module from build
|
||||
- task: DownloadPipelineArtifact@1
|
||||
- task: DownloadPipelineArtifact@2
|
||||
displayName: 'Download module'
|
||||
inputs:
|
||||
artifactName: PSRule.Monitor
|
||||
downloadPath: $(Build.SourcesDirectory)/out/modules/PSRule.Monitor
|
||||
artifact: PSRule.Monitor
|
||||
path: $(Build.SourcesDirectory)/out/modules/PSRule.Monitor
|
||||
|
||||
# Install pipeline dependencies
|
||||
- powershell: ./.azure-pipelines/pipeline-deps.ps1
|
||||
|
@ -170,15 +205,15 @@ stages:
|
|||
displayName: 'Publish module'
|
||||
|
||||
# Update GitHub release
|
||||
- task: GitHubRelease@0
|
||||
- task: GitHubRelease@1
|
||||
displayName: 'GitHub release'
|
||||
inputs:
|
||||
gitHubConnection: 'AzureDevOps-PSRule.Monitor'
|
||||
repositoryName: '$(Build.Repository.Name)'
|
||||
action: edit
|
||||
tag: '$(Build.SourceBranchName)'
|
||||
releaseNotesSource: input
|
||||
releaseNotes: 'See [change log](https://github.com/BernieWhite/PSRule.Monitor/blob/main/CHANGELOG.md)'
|
||||
releaseNotesSource: inline
|
||||
releaseNotesInline: 'See [change log](https://github.com/Microsoft/PSRule.Monitor/blob/main/CHANGELOG.md)'
|
||||
assetUploadMode: replace
|
||||
addChangeLog: false
|
||||
isPreRelease: $(isPreRelease)
|
||||
|
|
|
@ -0,0 +1,71 @@
|
|||
# Azure DevOps
|
||||
# CI job for running VM pipelines
|
||||
|
||||
parameters:
|
||||
name: ''
|
||||
displayName: ''
|
||||
buildConfiguration: 'Release'
|
||||
imageName: ''
|
||||
coverage: 'false'
|
||||
publishResults: 'true'
|
||||
|
||||
jobs:
|
||||
- job: ${{ parameters.name }}
|
||||
displayName: ${{ parameters.displayName }}
|
||||
pool:
|
||||
vmImage: ${{ parameters.imageName }}
|
||||
variables:
|
||||
COVERAGE: ${{ parameters.coverage }}
|
||||
PUBLISHRESULTS: ${{ parameters.publishResults }}
|
||||
skipComponentGovernanceDetection: true
|
||||
steps:
|
||||
|
||||
# Install pipeline dependencies
|
||||
- powershell: ./.azure-pipelines/pipeline-deps.ps1
|
||||
displayName: 'Install dependencies'
|
||||
|
||||
# Download module
|
||||
- task: DownloadPipelineArtifact@2
|
||||
displayName: 'Download module'
|
||||
inputs:
|
||||
artifact: PSRule.Monitor
|
||||
path: $(Build.SourcesDirectory)/out/modules/PSRule.Monitor
|
||||
|
||||
# Build module
|
||||
- powershell: Invoke-Build TestModule -Configuration ${{ parameters.buildConfiguration }} -Build $(Build.BuildNumber)
|
||||
env:
|
||||
COVERAGE: ${{ parameters.coverage }}
|
||||
displayName: 'Test module'
|
||||
|
||||
# Pester test results
|
||||
- task: PublishTestResults@2
|
||||
displayName: 'Publish Pester results'
|
||||
inputs:
|
||||
testRunTitle: 'Pester on ${{ parameters.imageName }}'
|
||||
testRunner: NUnit
|
||||
testResultsFiles: 'reports/pester-unit.xml'
|
||||
mergeTestResults: true
|
||||
platform: ${{ parameters.name }}
|
||||
configuration: ${{ parameters.buildConfiguration }}
|
||||
publishRunAttachments: true
|
||||
condition: and(succeededOrFailed(), eq(variables['PUBLISHRESULTS'], 'true'))
|
||||
|
||||
# Generate Code Coverage report
|
||||
- task: Palmmedia.reportgenerator.reportgenerator-build-release-task.reportgenerator@4
|
||||
displayName: 'Code coverage report generator'
|
||||
inputs:
|
||||
reports: 'reports/pester-coverage.xml'
|
||||
targetdir: 'reports/coverage'
|
||||
sourcedirs: 'src/PSRule.Monitor'
|
||||
reporttypes: 'HtmlInline_AzurePipelines;Cobertura;SonarQube;Badges'
|
||||
tag: $(Build.BuildNumber)
|
||||
condition: eq(variables['COVERAGE'], 'true')
|
||||
|
||||
# Publish Code Coverage report
|
||||
- task: PublishCodeCoverageResults@1
|
||||
displayName: 'Publish Pester code coverage'
|
||||
inputs:
|
||||
codeCoverageTool: 'Cobertura'
|
||||
summaryFileLocation: 'reports/coverage/Cobertura.xml'
|
||||
reportDirectory: 'reports/coverage'
|
||||
condition: eq(variables['COVERAGE'], 'true')
|
|
@ -0,0 +1,76 @@
|
|||
# Azure DevOps
|
||||
# CI job for running container pipelines
|
||||
|
||||
parameters:
|
||||
name: ''
|
||||
displayName: ''
|
||||
buildConfiguration: 'Release'
|
||||
vmImage: 'ubuntu-16.04'
|
||||
imageName: ''
|
||||
imageTag: ''
|
||||
coverage: 'false'
|
||||
publishResults: 'true'
|
||||
|
||||
jobs:
|
||||
- job: ${{ parameters.name }}
|
||||
displayName: ${{ parameters.displayName }}
|
||||
pool:
|
||||
vmImage: ${{ parameters.vmImage }}
|
||||
container:
|
||||
image: '${{ parameters.imageName }}:${{ parameters.imageTag }}'
|
||||
env:
|
||||
COVERAGE: ${{ parameters.coverage }}
|
||||
PUBLISHRESULTS: ${{ parameters.publishResults }}
|
||||
variables:
|
||||
COVERAGE: ${{ parameters.coverage }}
|
||||
PUBLISHRESULTS: ${{ parameters.publishResults }}
|
||||
skipComponentGovernanceDetection: true
|
||||
steps:
|
||||
|
||||
# Install pipeline dependencies
|
||||
- powershell: ./.azure-pipelines/pipeline-deps.ps1
|
||||
displayName: 'Install dependencies'
|
||||
|
||||
# Download module
|
||||
- task: DownloadPipelineArtifact@2
|
||||
displayName: 'Download module'
|
||||
inputs:
|
||||
artifact: PSRule.Monitor
|
||||
path: $(Build.SourcesDirectory)/out/modules/PSRule.Monitor
|
||||
|
||||
# Build module
|
||||
- powershell: Invoke-Build TestModule -Configuration ${{ parameters.buildConfiguration }} -Build $(Build.BuildNumber)
|
||||
displayName: 'Test module'
|
||||
|
||||
# Pester test results
|
||||
- task: PublishTestResults@2
|
||||
displayName: 'Publish Pester results'
|
||||
inputs:
|
||||
testRunTitle: 'Pester on ${{ parameters.imageTag }}'
|
||||
testRunner: NUnit
|
||||
testResultsFiles: 'reports/pester-unit.xml'
|
||||
mergeTestResults: true
|
||||
platform: ${{ parameters.imageTag }}
|
||||
configuration: ${{ parameters.buildConfiguration }}
|
||||
publishRunAttachments: true
|
||||
condition: and(succeededOrFailed(), eq(variables['PUBLISHRESULTS'], 'true'))
|
||||
|
||||
# Generate Code Coverage report
|
||||
- task: Palmmedia.reportgenerator.reportgenerator-build-release-task.reportgenerator@4
|
||||
displayName: 'Code coverage report generator'
|
||||
inputs:
|
||||
reports: 'reports\pester-coverage.xml'
|
||||
targetdir: 'reports\coverage'
|
||||
sourcedirs: 'src\PSRule.Monitor'
|
||||
reporttypes: 'HtmlInline_AzurePipelines;Cobertura;SonarQube;Badges'
|
||||
tag: $(Build.BuildNumber)
|
||||
condition: eq(variables['COVERAGE'], 'true')
|
||||
|
||||
# Publish Code Coverage report
|
||||
- task: PublishCodeCoverageResults@1
|
||||
displayName: 'Publish Pester code coverage'
|
||||
inputs:
|
||||
codeCoverageTool: 'Cobertura'
|
||||
summaryFileLocation: 'reports/coverage/Cobertura.xml'
|
||||
reportDirectory: 'reports/coverage'
|
||||
condition: eq(variables['COVERAGE'], 'true')
|
|
@ -2,6 +2,12 @@
|
|||
|
||||
## Unreleased
|
||||
|
||||
What's changed since v0.1.0:
|
||||
|
||||
- New features:
|
||||
- Added support for passing through data and field properties. [#31](https://github.com/microsoft/PSRule.Monitor/issues/31)
|
||||
- Added support for passing through rule module name. [#30](https://github.com/microsoft/PSRule.Monitor/issues/30)
|
||||
|
||||
## v0.1.0
|
||||
|
||||
What's changed since pre-release v0.1.0-B1912005:
|
||||
|
|
|
@ -3,7 +3,9 @@ Microsoft Visual Studio Solution File, Format Version 12.00
|
|||
# Visual Studio Version 16
|
||||
VisualStudioVersion = 16.0.29418.71
|
||||
MinimumVisualStudioVersion = 15.0.26124.0
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PSRule.Monitor", "src\PSRule.Monitor\PSRule.Monitor.csproj", "{E6BB91E3-5A15-48F8-9722-1F3AC50DDD75}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PSRule.Monitor", "src\PSRule.Monitor\PSRule.Monitor.csproj", "{BC87E736-4B87-48C2-AC35-3686C86C13A7}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PSRule.Monitor.Tests", "tests\PSRule.Monitor.Tests\PSRule.Monitor.Tests.csproj", "{5A666E79-F8B6-4613-8E4C-0F969CBA68E3}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
|
@ -11,10 +13,14 @@ Global
|
|||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{E6BB91E3-5A15-48F8-9722-1F3AC50DDD75}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{E6BB91E3-5A15-48F8-9722-1F3AC50DDD75}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{E6BB91E3-5A15-48F8-9722-1F3AC50DDD75}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{E6BB91E3-5A15-48F8-9722-1F3AC50DDD75}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{BC87E736-4B87-48C2-AC35-3686C86C13A7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{BC87E736-4B87-48C2-AC35-3686C86C13A7}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{BC87E736-4B87-48C2-AC35-3686C86C13A7}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{BC87E736-4B87-48C2-AC35-3686C86C13A7}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{5A666E79-F8B6-4613-8E4C-0F969CBA68E3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{5A666E79-F8B6-4613-8E4C-0F969CBA68E3}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{5A666E79-F8B6-4613-8E4C-0F969CBA68E3}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{5A666E79-F8B6-4613-8E4C-0F969CBA68E3}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<configuration>
|
||||
<packageSources>
|
||||
<add key="NuGet official package source" value="https://api.nuget.org/v3/index.json" />
|
||||
</packageSources>
|
||||
<disabledPackageSources>
|
||||
<add key="Microsoft Visual Studio Offline Packages" value="true" />
|
||||
</disabledPackageSources>
|
||||
</configuration>
|
|
@ -180,16 +180,16 @@ task PSScriptAnalyzer NuGet, {
|
|||
|
||||
# Synopsis: Install PSRule
|
||||
task PSRule NuGet, {
|
||||
if ($Null -eq (Get-InstalledModule -Name PSRule -MinimumVersion 0.18.0 -ErrorAction Ignore)) {
|
||||
Install-Module -Name PSRule -Repository PSGallery -MinimumVersion 0.18.0 -Scope CurrentUser -Force;
|
||||
if ($Null -eq (Get-InstalledModule -Name PSRule -MinimumVersion 1.2.0 -ErrorAction Ignore)) {
|
||||
Install-Module -Name PSRule -Repository PSGallery -MinimumVersion 1.2.0 -Scope CurrentUser -Force;
|
||||
}
|
||||
Import-Module -Name PSRule -Verbose:$False;
|
||||
}
|
||||
|
||||
# Synopsis: Install PSDocs
|
||||
task PSDocs NuGet, {
|
||||
if ($Null -eq (Get-InstalledModule -Name PSDocs -MinimumVersion 0.6.3 -ErrorAction Ignore)) {
|
||||
Install-Module -Name PSDocs -Repository PSGallery -MinimumVersion 0.6.3 -Scope CurrentUser -Force;
|
||||
if ($Null -eq (Get-InstalledModule -Name PSDocs -MinimumVersion 0.8.0 -ErrorAction Ignore)) {
|
||||
Install-Module -Name PSDocs -Repository PSGallery -MinimumVersion 0.8.0 -Scope CurrentUser -Force;
|
||||
}
|
||||
Import-Module -Name PSDocs -Verbose:$False;
|
||||
}
|
||||
|
@ -199,7 +199,6 @@ task platyPS {
|
|||
if ($Null -eq (Get-InstalledModule -Name PlatyPS -MinimumVersion 0.14.0 -ErrorAction Ignore)) {
|
||||
Install-Module -Name PlatyPS -Scope CurrentUser -MinimumVersion 0.14.0 -Force;
|
||||
}
|
||||
Import-Module -Name PlatyPS -Verbose:$False;
|
||||
}
|
||||
|
||||
# Synopsis: Install module dependencies
|
||||
|
@ -217,13 +216,13 @@ task TestDotNet {
|
|||
if ($CodeCoverage) {
|
||||
exec {
|
||||
# Test library
|
||||
# dotnet test --collect:"Code Coverage" --logger trx -r (Join-Path $PWD -ChildPath reports/) tests/PSRule.Monitor.Tests
|
||||
dotnet test --collect:"Code Coverage" --logger trx -r (Join-Path $PWD -ChildPath reports/) tests/PSRule.Monitor.Tests
|
||||
}
|
||||
}
|
||||
else {
|
||||
exec {
|
||||
# Test library
|
||||
# dotnet test --logger trx -r (Join-Path $PWD -ChildPath reports/) tests/PSRule.Monitor.Tests
|
||||
dotnet test --logger trx -r (Join-Path $PWD -ChildPath reports/) tests/PSRule.Monitor.Tests
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -238,13 +237,13 @@ task CopyModule {
|
|||
# Synopsis: Build modules only
|
||||
task BuildModule BuildDotNet, CopyModule
|
||||
|
||||
task TestRules TestDotNet, PSRule, Pester, PSScriptAnalyzer, {
|
||||
task TestModule ModuleDependencies, Pester, PSScriptAnalyzer, {
|
||||
# Run Pester tests
|
||||
$pesterParams = @{ Path = (Join-Path -Path $PWD -ChildPath tests/PSRule.Monitor.Tests); OutputFile = 'reports/pester-unit.xml'; OutputFormat = 'NUnitXml'; PesterOption = @{ IncludeVSCodeMarker = $True }; PassThru = $True; };
|
||||
|
||||
if ($CodeCoverage) {
|
||||
$pesterParams.Add('CodeCoverage', (Join-Path -Path $PWD -ChildPath 'out/modules/**/*.psm1'));
|
||||
$pesterParams.Add('CodeCoverageOutputFile', (Join-Path -Path $PWD -ChildPath reports/pester-coverage.xml));
|
||||
$pesterParams.Add('CodeCoverageOutputFile', (Join-Path -Path $PWD -ChildPath 'reports/pester-coverage.xml'));
|
||||
}
|
||||
|
||||
if (!(Test-Path -Path reports)) {
|
||||
|
@ -281,8 +280,6 @@ task Analyze Build, PSScriptAnalyzer, {
|
|||
|
||||
# Synopsis: Build help
|
||||
task BuildHelp BuildModule, PlatyPS, {
|
||||
# Generate MAML and about topics
|
||||
$Null = New-ExternalHelp -OutputPath out/docs/PSRule.Monitor -Path '.\docs\commands\PSRule.Monitor\en-US' -Force;
|
||||
|
||||
# Copy generated help into module out path
|
||||
if (!(Test-Path -Path out/modules/PSRule.Monitor/en-US/)) {
|
||||
|
@ -294,9 +291,25 @@ task BuildHelp BuildModule, PlatyPS, {
|
|||
if (!(Test-Path -Path out/modules/PSRule.Monitor/en-GB/)) {
|
||||
$Null = New-Item -Path out/modules/PSRule.Monitor/en-GB -Force -ItemType Directory;
|
||||
}
|
||||
$Null = Copy-Item -Path out/docs/PSRule.Monitor/* -Destination out/modules/PSRule.Monitor/en-US/ -Recurse;
|
||||
$Null = Copy-Item -Path out/docs/PSRule.Monitor/* -Destination out/modules/PSRule.Monitor/en-AU/ -Recurse;
|
||||
$Null = Copy-Item -Path out/docs/PSRule.Monitor/* -Destination out/modules/PSRule.Monitor/en-GB/ -Recurse;
|
||||
|
||||
# Avoid YamlDotNet issue in same app domain
|
||||
exec {
|
||||
$pwshPath = (Get-Process -Id $PID).Path;
|
||||
&$pwshPath -Command {
|
||||
# Generate MAML and about topics
|
||||
Import-Module -Name PlatyPS -Verbose:$False;
|
||||
$Null = New-ExternalHelp -OutputPath 'out/docs/PSRule.Monitor' -Path '.\docs\commands\PSRule.Monitor\en-US' -Force;
|
||||
|
||||
# Copy generated help into module out path
|
||||
$Null = Copy-Item -Path out/docs/PSRule.Monitor/* -Destination out/modules/PSRule.Monitor/en-US/ -Recurse;
|
||||
$Null = Copy-Item -Path out/docs/PSRule.Monitor/* -Destination out/modules/PSRule.Monitor/en-AU/ -Recurse;
|
||||
$Null = Copy-Item -Path out/docs/PSRule.Monitor/* -Destination out/modules/PSRule.Monitor/en-GB/ -Recurse;
|
||||
}
|
||||
}
|
||||
|
||||
if (!(Test-Path -Path 'out/docs/PSRule.Monitor/PSRule.Monitor-help.xml')) {
|
||||
throw 'Failed find generated cmdlet help.';
|
||||
}
|
||||
}
|
||||
|
||||
task ScaffoldHelp Build, {
|
||||
|
@ -318,8 +331,9 @@ task Clean {
|
|||
|
||||
task Build Clean, BuildModule, VersionModule, BuildHelp
|
||||
|
||||
task Test Build, TestRules
|
||||
task Test Build, Rules, TestDotNet, TestModule
|
||||
|
||||
task Release ReleaseModule, TagBuild
|
||||
|
||||
task . Build, Test
|
||||
# Synopsis: Build and test. Entry point for CI Build stage
|
||||
task . Build, Rules, TestDotNet
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.Collections;
|
||||
|
||||
namespace PSRule.Monitor
|
||||
{
|
||||
/// <summary>
|
||||
/// A JSON converter to convert an object into a flat string.
|
||||
/// </summary>
|
||||
internal sealed class StringifyMapConverter : JsonConverter
|
||||
{
|
||||
public override bool CanConvert(Type objectType)
|
||||
{
|
||||
return typeof(Hashtable).IsAssignableFrom(objectType);
|
||||
}
|
||||
|
||||
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
|
||||
{
|
||||
if (!(value is Hashtable map))
|
||||
return;
|
||||
|
||||
var v = JsonConvert.SerializeObject(map);
|
||||
writer.WriteValue(v);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
using System.Collections;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace PSRule.Monitor.Data
|
||||
|
@ -12,6 +13,10 @@ namespace PSRule.Monitor.Data
|
|||
{
|
||||
public string RuleName { get; set; }
|
||||
|
||||
public string DisplayName { get; set; }
|
||||
|
||||
public string ModuleName { get; set; }
|
||||
|
||||
public string TargetName { get; set; }
|
||||
|
||||
public string TargetType { get; set; }
|
||||
|
@ -20,5 +25,14 @@ namespace PSRule.Monitor.Data
|
|||
|
||||
[JsonIgnore]
|
||||
public string ResourceId { get; set; }
|
||||
|
||||
[JsonConverter(typeof(StringifyMapConverter))]
|
||||
public Hashtable Field { get; set; }
|
||||
|
||||
[JsonConverter(typeof(StringifyMapConverter))]
|
||||
public Hashtable Data { get; set; }
|
||||
|
||||
[JsonConverter(typeof(StringifyMapConverter))]
|
||||
public Hashtable Annotations { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
<RootNamespace>PSRule.Monitor</RootNamespace>
|
||||
<OutputType>Library</OutputType>
|
||||
<ProjectGuid>{bc87e736-4b87-48c2-ac35-3686c86c13a7}</ProjectGuid>
|
||||
<DebugType>portable</DebugType>
|
||||
<NeutralLanguage>en-US</NeutralLanguage>
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
|
@ -25,7 +26,7 @@ This project is open source and not a supported product.</Description>
|
|||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="3.3.2">
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.NetAnalyzers" Version="5.0.3">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
|
|
|
@ -5,9 +5,7 @@ using Newtonsoft.Json;
|
|||
using PSRule.Monitor.Data;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Globalization;
|
||||
using System.Management.Automation;
|
||||
using System.Net.Http;
|
||||
using System.Security;
|
||||
using System.Text;
|
||||
|
||||
|
@ -47,42 +45,28 @@ namespace PSRule.Monitor.Pipeline
|
|||
|
||||
public override IPipeline Build()
|
||||
{
|
||||
return new InjestPipeline(PrepareContext(), PrepareReader(), _WorkspaceId, _SharedKey, _LogName);
|
||||
var logClient = new LogClient(_WorkspaceId, _LogName);
|
||||
return new InjestPipeline(PrepareContext(), PrepareReader(), _WorkspaceId, _SharedKey, logClient);
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class InjestPipeline : PipelineBase
|
||||
{
|
||||
private const string CONTENTTYPE = "application/json";
|
||||
private const string TIMESTAMPFIELD = "";
|
||||
private const string APIVERSION = "2016-04-01";
|
||||
|
||||
private const string HEADER_ACCEPT = "Accept";
|
||||
private const string HEADER_AUTHORIZATION = "Authorization";
|
||||
private const string HEADER_LOGTYPE = "Log-Type";
|
||||
private const string HEADER_DATE = "x-ms-date";
|
||||
private const string HEADER_RESOURCEID = "x-ms-AzureResourceId";
|
||||
private const string HEADER_TIMEGENERATED = "time-generated-field";
|
||||
|
||||
private static readonly CultureInfo FormatCulture = new CultureInfo("en-US");
|
||||
|
||||
private readonly string _LogName;
|
||||
private readonly Uri _EndpointUri;
|
||||
private readonly CollectionHash _Hash;
|
||||
private readonly BatchQueue _SubmissionQueue;
|
||||
private readonly HttpClient _HttpClient;
|
||||
private readonly ILogClient _LogClient;
|
||||
|
||||
// Track whether Dispose has been called.
|
||||
private bool _Disposed;
|
||||
|
||||
internal InjestPipeline(PipelineContext context, PipelineReader reader, string workspaceId, SecureString sharedKey, string logName)
|
||||
internal InjestPipeline(PipelineContext context, PipelineReader reader, string workspaceId, SecureString sharedKey, ILogClient logClient)
|
||||
: base(context, reader)
|
||||
{
|
||||
_LogName = logName;
|
||||
_EndpointUri = new Uri(string.Concat("https://", workspaceId, ".ods.opinsights.azure.com/api/logs?api-version=", APIVERSION));
|
||||
_Hash = new CollectionHash(workspaceId, sharedKey);
|
||||
_SubmissionQueue = new BatchQueue();
|
||||
_HttpClient = GetClient();
|
||||
_LogClient = logClient;
|
||||
}
|
||||
|
||||
public override void Process(PSObject sourceObject)
|
||||
|
@ -113,15 +97,23 @@ namespace PSRule.Monitor.Pipeline
|
|||
var targetName = GetPropertyValue(sourceObject, "targetName");
|
||||
var targetType = GetPropertyValue(sourceObject, "targetType");
|
||||
var outcome = GetPropertyValue(sourceObject, "outcome");
|
||||
var data = GetProperty<object>(sourceObject, "data");
|
||||
var field = GetProperty<object>(sourceObject, "field");
|
||||
var resourceId = GetField(field, "resourceId");
|
||||
var info = GetProperty<object>(sourceObject, "info");
|
||||
var resourceId = GetField(data, "resourceId") ?? GetField(field, "resourceId");
|
||||
var displayName = GetField(info, "displayName") ?? ruleName;
|
||||
var moduleName = GetField(info, "moduleName");
|
||||
var record = new LogRecord
|
||||
{
|
||||
RuleName = ruleName,
|
||||
DisplayName = displayName,
|
||||
ModuleName = moduleName,
|
||||
TargetName = targetName,
|
||||
TargetType = targetType,
|
||||
Outcome = outcome,
|
||||
ResourceId = resourceId
|
||||
ResourceId = resourceId,
|
||||
Data = GetPropertyMap(data),
|
||||
Field = GetPropertyMap(field),
|
||||
};
|
||||
return record;
|
||||
}
|
||||
|
@ -136,12 +128,34 @@ namespace PSRule.Monitor.Pipeline
|
|||
return obj.Properties[propertyName] == null ? default(T) : (T)obj.Properties[propertyName].Value;
|
||||
}
|
||||
|
||||
private static string GetField(object obj, string propertyName)
|
||||
private static Hashtable GetPropertyMap(object o)
|
||||
{
|
||||
if (obj is IDictionary dictionary && TryDictionary(dictionary, propertyName, out object value) && value != null)
|
||||
if (o == null)
|
||||
return null;
|
||||
|
||||
var result = new Hashtable();
|
||||
if (o is IDictionary dictionary)
|
||||
{
|
||||
foreach (DictionaryEntry kv in dictionary)
|
||||
result[kv.Key] = kv.Value;
|
||||
}
|
||||
else if (o is PSObject pso)
|
||||
{
|
||||
foreach (var p in pso.Properties)
|
||||
{
|
||||
if (p.MemberType == PSMemberTypes.NoteProperty)
|
||||
result[p.Name] = p.Value;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static string GetField(object o, string propertyName)
|
||||
{
|
||||
if (o is IDictionary dictionary && TryDictionary(dictionary, propertyName, out object value) && value != null)
|
||||
return value.ToString();
|
||||
|
||||
if (obj is PSObject pso)
|
||||
if (o is PSObject pso)
|
||||
return GetPropertyValue(pso, propertyName);
|
||||
|
||||
return null;
|
||||
|
@ -182,31 +196,7 @@ namespace PSRule.Monitor.Pipeline
|
|||
/// </summary>
|
||||
private void PostData(string signature, DateTime date, string resourceId, string json)
|
||||
{
|
||||
using (var request = PrepareRequest(signature, date, resourceId, json))
|
||||
{
|
||||
var response = _HttpClient.SendAsync(request);
|
||||
response.Wait();
|
||||
var result = response.Result.Content.ReadAsStringAsync().Result;
|
||||
}
|
||||
}
|
||||
|
||||
private HttpClient GetClient()
|
||||
{
|
||||
var client = new HttpClient();
|
||||
client.DefaultRequestHeaders.Add(HEADER_ACCEPT, CONTENTTYPE);
|
||||
client.DefaultRequestHeaders.Add(HEADER_LOGTYPE, _LogName);
|
||||
return client;
|
||||
}
|
||||
|
||||
private HttpRequestMessage PrepareRequest(string signature, DateTime date, string resourceId, string json)
|
||||
{
|
||||
var request = new HttpRequestMessage(HttpMethod.Post, _EndpointUri);
|
||||
request.Headers.Add(HEADER_AUTHORIZATION, signature);
|
||||
request.Headers.Add(HEADER_DATE, date.ToString("r", FormatCulture));
|
||||
request.Headers.Add(HEADER_TIMEGENERATED, TIMESTAMPFIELD);
|
||||
request.Headers.Add(HEADER_RESOURCEID, resourceId);
|
||||
request.Content = new StringContent(json, Encoding.UTF8, CONTENTTYPE);
|
||||
return request;
|
||||
_LogClient.Post(signature, date, resourceId, json);
|
||||
}
|
||||
|
||||
#region IDisposable
|
||||
|
@ -218,7 +208,7 @@ namespace PSRule.Monitor.Pipeline
|
|||
if (disposing)
|
||||
{
|
||||
_Hash.Dispose();
|
||||
_HttpClient.Dispose();
|
||||
_LogClient.Dispose();
|
||||
}
|
||||
_Disposed = true;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,97 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Net.Http;
|
||||
using System.Text;
|
||||
|
||||
namespace PSRule.Monitor.Pipeline
|
||||
{
|
||||
internal interface ILogClient : IDisposable
|
||||
{
|
||||
void Post(string signature, DateTime date, string resourceId, string json);
|
||||
}
|
||||
|
||||
internal sealed class LogClient : ILogClient
|
||||
{
|
||||
private const string CONTENTTYPE = "application/json";
|
||||
private const string TIMESTAMPFIELD = "";
|
||||
private const string APIVERSION = "2016-04-01";
|
||||
|
||||
private const string HEADER_ACCEPT = "Accept";
|
||||
private const string HEADER_AUTHORIZATION = "Authorization";
|
||||
private const string HEADER_LOGTYPE = "Log-Type";
|
||||
private const string HEADER_DATE = "x-ms-date";
|
||||
private const string HEADER_RESOURCEID = "x-ms-AzureResourceId";
|
||||
private const string HEADER_TIMEGENERATED = "time-generated-field";
|
||||
|
||||
private static readonly CultureInfo FormatCulture = new CultureInfo("en-US");
|
||||
|
||||
private readonly HttpClient _HttpClient;
|
||||
private readonly Uri _EndpointUri;
|
||||
|
||||
// Track whether Dispose has been called.
|
||||
private bool _Disposed;
|
||||
|
||||
public LogClient(string workspaceId, string logName)
|
||||
{
|
||||
_EndpointUri = new Uri(string.Concat("https://", workspaceId, ".ods.opinsights.azure.com/api/logs?api-version=", APIVERSION));
|
||||
_HttpClient = GetClient(logName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Post log data to Azure Monitor endpoint.
|
||||
/// </summary>
|
||||
public void Post(string signature, DateTime date, string resourceId, string json)
|
||||
{
|
||||
using (var request = PrepareRequest(signature, date, resourceId, json))
|
||||
{
|
||||
var response = _HttpClient.SendAsync(request);
|
||||
response.Wait();
|
||||
var result = response.Result.Content.ReadAsStringAsync().Result;
|
||||
}
|
||||
}
|
||||
|
||||
private static HttpClient GetClient(string logName)
|
||||
{
|
||||
var client = new HttpClient();
|
||||
client.DefaultRequestHeaders.Add(HEADER_ACCEPT, CONTENTTYPE);
|
||||
client.DefaultRequestHeaders.Add(HEADER_LOGTYPE, logName);
|
||||
return client;
|
||||
}
|
||||
|
||||
private HttpRequestMessage PrepareRequest(string signature, DateTime date, string resourceId, string json)
|
||||
{
|
||||
var request = new HttpRequestMessage(HttpMethod.Post, _EndpointUri);
|
||||
request.Headers.Add(HEADER_AUTHORIZATION, signature);
|
||||
request.Headers.Add(HEADER_DATE, date.ToString("r", FormatCulture));
|
||||
request.Headers.Add(HEADER_TIMEGENERATED, TIMESTAMPFIELD);
|
||||
request.Headers.Add(HEADER_RESOURCEID, resourceId);
|
||||
request.Content = new StringContent(json, Encoding.UTF8, CONTENTTYPE);
|
||||
return request;
|
||||
}
|
||||
|
||||
#region IDisposable
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
private void Dispose(bool disposing)
|
||||
{
|
||||
if (!_Disposed)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
_HttpClient.Dispose();
|
||||
}
|
||||
_Disposed = true;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion IDisposable
|
||||
}
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
[assembly: InternalsVisibleTo("PSRule.Monitor.Tests")]
|
|
@ -0,0 +1,86 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using PSRule.Monitor.Configuration;
|
||||
using PSRule.Monitor.Pipeline;
|
||||
using System;
|
||||
using System.Management.Automation;
|
||||
using System.Security;
|
||||
using System.Text;
|
||||
using Xunit;
|
||||
|
||||
namespace PSRule.Monitor
|
||||
{
|
||||
public sealed class InjestPipelineTests
|
||||
{
|
||||
[Fact]
|
||||
public void InvokePipeline()
|
||||
{
|
||||
var pipeline = GetPipeline(out TestLogClient logClient);
|
||||
pipeline.Begin();
|
||||
var o = GetObject();
|
||||
pipeline.Process(o);
|
||||
pipeline.End();
|
||||
|
||||
Assert.Single(logClient.Output);
|
||||
var actual = logClient.Output[0];
|
||||
var actualJson = JsonConvert.DeserializeObject<JArray>(actual.Json);
|
||||
|
||||
Assert.Equal("test-resource-id", actual.ResourceId);
|
||||
Assert.NotEmpty(actual.Signature);
|
||||
|
||||
Assert.Equal(o.Properties["ruleName"].Value, actualJson[0]["RuleName"].Value<string>());
|
||||
Assert.Equal(o.Properties["targetName"].Value, actualJson[0]["TargetName"].Value<string>());
|
||||
Assert.Equal(o.Properties["targetType"].Value, actualJson[0]["TargetType"].Value<string>());
|
||||
Assert.Equal(o.Properties["outcome"].Value, actualJson[0]["Outcome"].Value<string>());
|
||||
Assert.Equal(o.Properties["ruleName"].Value, actualJson[0]["DisplayName"].Value<string>());
|
||||
Assert.Equal("test-module", actualJson[0]["ModuleName"].Value<string>());
|
||||
}
|
||||
|
||||
#region Helper methods
|
||||
|
||||
private const string _WorkspaceId = "00000000-0000-0000-0000-000000000000";
|
||||
|
||||
private static PSObject GetObject()
|
||||
{
|
||||
var info = new PSObject();
|
||||
info.Properties.Add(new PSNoteProperty("moduleName", "test-module"));
|
||||
|
||||
var data = new PSObject();
|
||||
data.Properties.Add(new PSNoteProperty("resourceId", "test-resource-id"));
|
||||
|
||||
var o = new PSObject();
|
||||
o.Properties.Add(new PSNoteProperty("ruleName", "test-rule"));
|
||||
o.Properties.Add(new PSNoteProperty("targetName", "test-name"));
|
||||
o.Properties.Add(new PSNoteProperty("targetType", "test-type"));
|
||||
o.Properties.Add(new PSNoteProperty("outcome", "Fail"));
|
||||
o.Properties.Add(new PSNoteProperty("info", info));
|
||||
o.Properties.Add(new PSNoteProperty("data", data));
|
||||
return o;
|
||||
}
|
||||
|
||||
private static IPipeline GetPipeline(out TestLogClient logClient)
|
||||
{
|
||||
var key = new SecureString();
|
||||
foreach (var c in Convert.ToBase64String(Encoding.UTF8.GetBytes(_WorkspaceId)))
|
||||
key.AppendChar(c);
|
||||
|
||||
logClient = new TestLogClient();
|
||||
return new InjestPipeline(GetContent(), GetReader(), _WorkspaceId, key, logClient);
|
||||
}
|
||||
|
||||
private static PipelineReader GetReader()
|
||||
{
|
||||
return new PipelineReader();
|
||||
}
|
||||
|
||||
private static PipelineContext GetContent()
|
||||
{
|
||||
return new PipelineContext(new PSRuleOption());
|
||||
}
|
||||
|
||||
#endregion Helper methods
|
||||
}
|
||||
}
|
|
@ -6,9 +6,7 @@
|
|||
#
|
||||
|
||||
[CmdletBinding()]
|
||||
param (
|
||||
|
||||
)
|
||||
param ()
|
||||
|
||||
# Setup error handling
|
||||
$ErrorActionPreference = 'Stop';
|
||||
|
@ -54,7 +52,7 @@ Describe 'PSRule.Monitor' -Tag 'PowerShellGallery' {
|
|||
Context 'Static analysis' {
|
||||
It 'Has no quality errors' {
|
||||
$modulePath = (Join-Path -Path $rootPath -ChildPath out/modules/PSRule.Monitor);
|
||||
$result = @(Invoke-ScriptAnalyzer -Path $modulePath -Verbose);
|
||||
$result = @(Invoke-ScriptAnalyzer -Path $modulePath);
|
||||
|
||||
$warningCount = ($result | Where-Object { $_.Severity -eq 'Warning' } | Measure-Object).Count;
|
||||
$errorCount = ($result | Where-Object { $_.Severity -eq 'Error' } | Measure-Object).Count;
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||
<ProjectGuid>{22bf9268-67e2-4c43-b254-1c43262cddcc}</ProjectGuid>
|
||||
<IsTestProject>true</IsTestProject>
|
||||
<IsPackable>false</IsPackable>
|
||||
<RootNamespace>PSRule.Monitor</RootNamespace>
|
||||
<DebugType>Full</DebugType>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.9.4" />
|
||||
<PackageReference Include="Microsoft.PowerShell.SDK" Version="7.0.6" />
|
||||
<PackageReference Include="xunit" Version="2.4.1" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3">
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="coverlet.collector" Version="3.0.3">
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\src\PSRule.Monitor\PSRule.Monitor.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
|
@ -0,0 +1,53 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
using PSRule.Monitor.Pipeline;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace PSRule.Monitor
|
||||
{
|
||||
internal sealed class TestLogClient : ILogClient
|
||||
{
|
||||
public TestLogClient()
|
||||
{
|
||||
Output = new List<LogEntry>();
|
||||
}
|
||||
|
||||
public List<LogEntry> Output { get; }
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
// Test class only
|
||||
}
|
||||
|
||||
public void Post(string signature, DateTime date, string resourceId, string json)
|
||||
{
|
||||
Output.Add(new LogEntry(
|
||||
signature,
|
||||
date,
|
||||
resourceId,
|
||||
json
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class LogEntry
|
||||
{
|
||||
public LogEntry(string signature, DateTime date, string resourceId, string json)
|
||||
{
|
||||
Signature = signature;
|
||||
Date = date;
|
||||
ResourceId = resourceId;
|
||||
Json = json;
|
||||
}
|
||||
|
||||
public string Signature { get; }
|
||||
|
||||
public DateTime Date { get; }
|
||||
|
||||
public string ResourceId { get; }
|
||||
|
||||
public string Json { get; }
|
||||
}
|
||||
}
|
Загрузка…
Ссылка в новой задаче