Improve logging in the UfwStatus procedure #3712
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Universal NRP Test | |
| on: | |
| workflow_dispatch: | |
| inputs: | |
| policy_packages: | |
| description: 'List of policy packages to test' | |
| required: true | |
| default: '[]' | |
| pull_request: | |
| schedule: | |
| - cron: '0 20 * * *' # Every day at 12pm PST (UTC-8) | |
| env: | |
| storageAccountName: 'osconfigstorage' | |
| storageContainerName: 'policypackages' | |
| defaultSelfHostedImage: 'ubuntu-22.04' | |
| vmDefaultMemory: '2048' # Default memory for local VMs in MB | |
| jobs: | |
| package: | |
| name: Package | |
| if: ${{ inputs.policy_packages == '[]' || inputs.policy_packages == '' }} | |
| uses: ./.github/workflows/package-build.yml | |
| needs: [setup-matrix] | |
| secrets: | |
| onedstenant: >- | |
| ${{ | |
| (github.ref == 'refs/heads/main' && secrets.ONEDS_TENANT_PROD) || | |
| (github.ref == 'refs/heads/dev' && secrets.ONEDS_TENANT_NONPROD) || | |
| (github.ref == 'refs/heads/ahbenmes/telemetry_staging' && secrets.ONEDS_TENANT_NONPROD) || | |
| '' | |
| }} | |
| with: | |
| target: ubuntu-14.04 | |
| arch: amd64 | |
| artifact: nrp-test | |
| package-type: DEB | |
| container-tag: "@sha256:d6f9c8072541d23779b0fbd4ff53d7b55dbdcf5d8998aa44a790546d9ec1e747" | |
| test: true | |
| machine-config: true | |
| release: ${{ github.event_name == 'pull_request' && false || true }} | |
| setup-matrix: | |
| name: Setup Matrix | |
| runs-on: ubuntu-latest | |
| outputs: | |
| targets: ${{ steps.matrix.outputs.targets }} | |
| custom_download: ${{ steps.matrix.outputs.custom_download }} | |
| policy_packages: ${{ steps.matrix.outputs.policy_packages }} | |
| default_self_hosted_image: ${{ steps.matrix.outputs.default_self_hosted_image }} | |
| steps: | |
| - name: Generate Matrix | |
| id: matrix | |
| env: | |
| DEFAULT_POLICYPACKAGES: | | |
| [ | |
| { "name": "LinuxSshServerSecurityBaseline", "short-name": "SSH", "resource-count": 20 }, | |
| { "name": "AzureLinuxBaseline", "short-name": "ASB", "resource-count": 169 } | |
| ] | |
| TARGETS: | | |
| [ | |
| { "os": "almalinux", "version": "9" }, | |
| { "os": "azurelinux", "version": "3" }, | |
| { "os": "debian", "version": "11" }, | |
| { "os": "debian", "version": "12" }, | |
| { "os": "oraclelinux", "version": "8" }, | |
| { "os": "rhel", "version": "8" }, | |
| { "os": "rhel", "version": "9" }, | |
| { "os": "sles", "version": "15" }, | |
| { "os": "ubuntu", "version": "20.04" }, | |
| { "os": "ubuntu", "version": "22.04" }, | |
| { "os": "ubuntu", "version": "24.04" } | |
| ] | |
| run: | | |
| # If no explicit packages defined, use the default packages | |
| if [[ '${{ inputs.policy_packages }}' == '[]' || '${{ inputs.policy_packages }}' == '' ]]; then | |
| custom_download="false" | |
| policy_packages="$DEFAULT_POLICYPACKAGES" | |
| else | |
| custom_download="true" | |
| policy_packages="${{ inputs.policy_packages }}" | |
| fi | |
| if policy_packages=$(echo $policy_packages | jq -r 'tostring'); then | |
| echo "Successfully processed JSON" | |
| else | |
| echo "Failed to process JSON, attempting to process as raw JSON" | |
| policy_packages=$(echo $policy_packages | jq -R -r 'tostring' | tr -d '\\') | |
| fi | |
| echo $custom_download | |
| echo $policy_packages | |
| echo targets=$(echo $TARGETS | jq -r 'tostring') >> $GITHUB_OUTPUT | |
| echo custom_download=$custom_download >> $GITHUB_OUTPUT | |
| echo policy_packages=$policy_packages >> $GITHUB_OUTPUT | |
| echo default_self_hosted_image="${{ env.defaultSelfHostedImage }}" >> $GITHUB_OUTPUT | |
| custom-download: | |
| name: Custom Download | |
| if: ${{ needs.setup-matrix.outputs.custom_download == 'true' }} | |
| needs: [setup-matrix] | |
| runs-on: [self-hosted, 1ES.Pool=ci-pool, '1ES.ImageOverride=${{ needs.setup-matrix.outputs.default_self_hosted_image }}'] | |
| steps: | |
| - name: Check and Install Az module | |
| shell: pwsh | |
| run: | | |
| Write-Host 'Checking the Az module...' | |
| try { | |
| Get-InstalledModule Az -AllVersions -ErrorAction Stop | |
| Write-Host 'Az module is already installed.' | |
| } catch { | |
| Write-Host 'Az module is not installed. Trying to install...' | |
| Install-Module -Name Az -Repository PSGallery -Force | |
| } | |
| Write-Host 'Done' | |
| - name: Azure login | |
| uses: azure/login@v2 | |
| with: | |
| auth-type: IDENTITY | |
| client-id: ${{ secrets.AZURE_CLIENT_ID }} | |
| tenant-id: ${{ secrets.AZURE_TENANT_ID }} | |
| subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} | |
| enable-AzPSSession: true | |
| - name: Azure PowerShell script | |
| uses: azure/powershell@v2 | |
| with: | |
| azPSVersion: latest | |
| inlineScript: | | |
| # Download the policy packages from Azure Storage or from a URL | |
| $jsonPolicyPackages = '${{ needs.setup-matrix.outputs.policy_packages }}' | |
| $policyPackages = $jsonPolicyPackages | ConvertFrom-Json | |
| foreach ($package in $policyPackages) { | |
| $policyPackagUrl=$package.'policy-package-url' | |
| $storageURIPrefix="storage://" | |
| if ($policyPackagUrl.StartsWith($storageURIPrefix)) { | |
| $storagePath=$policyPackagUrl.Substring($storageURIPrefix.Length) | |
| Write-Host "Downloading $storagePath from Azure Storage" | |
| $storageContext = New-AzStorageContext -StorageAccountName $env:storageAccountName -UseConnectedAccount | |
| Get-AzStorageBlobContent -Container $env:storageContainerName -Blob $storagePath -Context $storageContext -Destination $storagePath | |
| } else { | |
| Write-Host "Downloading from url \"$policyPackagUrl\"" | |
| Invoke-WebRequest -Uri $policyPackagUrl -OutFile PolicyPackage.zip | |
| } | |
| } | |
| - uses: actions/upload-artifact@v4 | |
| with: | |
| name: nrp-test | |
| path: '*.zip' | |
| mc-test: | |
| name: MC Test | |
| if: ${{ always() && needs.setup-matrix.result == 'success' && (needs.package.result == 'success' || needs.custom-download.result == 'success') }} | |
| needs: [setup-matrix, package, custom-download] | |
| runs-on: [self-hosted, 1ES.Pool=ci-pool, '1ES.ImageOverride=${{ matrix.target.os }}-${{ matrix.target.version }}'] | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| target: ${{ fromJSON(needs.setup-matrix.outputs.targets) }} | |
| arch: [amd64] | |
| mode: [Audit, Remediate] | |
| policy-package: ${{ fromJSON(needs.setup-matrix.outputs.policy_packages) }} | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| submodules: false | |
| clean: true | |
| - uses: actions/download-artifact@v4 | |
| id: download | |
| with: | |
| name: nrp-test | |
| - name: Normalize variables | |
| id: normalize | |
| run: | | |
| echo name="${{ matrix.target.os }}-${{ matrix.target.version }}_${{ matrix.policy-package.short-name }}-${{ matrix.mode }}" >> $GITHUB_OUTPUT | |
| echo dir="${{ steps.download.outputs.download-path }}" >> $GITHUB_OUTPUT | |
| echo path="${{ steps.download.outputs.download-path }}/${{ matrix.policy-package.name }}.zip" >> $GITHUB_OUTPUT | |
| - name: Fix policy package names | |
| if: ${{ needs.setup-matrix.outputs.custom_download == 'true' }} | |
| working-directory: ${{ steps.normalize.outputs.PolicyPackageDir }} | |
| shell: pwsh | |
| run: | | |
| $name="${{ matrix.policy-package.name }}" | |
| Get-ChildItem -Path $name*.zip -File | Select-Object -First 1 { | |
| Write-Host "Renaming $($_.Name) to $name.zip" | |
| Rename-Item -Path $_.Name -NewName "$name.zip" | |
| } | |
| - name: Check package size | |
| if: success() || failure() | |
| uses: ./.github/actions/check-size | |
| with: | |
| package: '${{ steps.normalize.outputs.path }}' | |
| limit: 5000 | |
| - name: Run Guest Configuration Test | |
| working-directory: ${{ steps.normalize.outputs.PolicyPackageDir }} | |
| run: | | |
| script="./universalNRPTest.ps1" | |
| cat >$script <<EOL | |
| Install-Module -Name GuestConfiguration -Force | |
| Install-Module Pester -Force -SkipPublisherCheck | |
| Import-Module Pester -Passthru | |
| \$params = @{ | |
| PolicyPackage = '${{ steps.normalize.outputs.path }}' | |
| SkipRemediation = if ('${{ matrix.mode }}' -eq 'Audit') { \$true } else { \$false } | |
| ResourceCount = ${{ matrix.policy-package.resource-count }} | |
| } | |
| \$container = New-PesterContainer -Path ./src/tests/universal-nrp-e2e/UniversalNRP.Tests.ps1 -Data \$params | |
| \$pesterConfig = [PesterConfiguration]@{ | |
| Run = @{ | |
| Exit = \$true | |
| Container = \$container | |
| } | |
| Output = @{ | |
| Verbosity = 'Detailed' | |
| } | |
| TestResult = @{ | |
| Enabled = \$true | |
| OutputFormat = 'JUnitXml' | |
| OutputPath = '${{ steps.normalize.outputs.name }}-testResults.xml' | |
| } | |
| Should = @{ | |
| ErrorAction = 'Continue' | |
| } | |
| }; | |
| Invoke-Pester -Configuration \$pesterConfig | |
| EOL | |
| sudo LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/opt/omi/lib/ pwsh -Command $script | |
| stat *testResults.xml | |
| - name: Check performance | |
| if: success() || failure() | |
| uses: ./.github/actions/check-perf | |
| with: | |
| perflog: /var/log/osconfig_asb_perf.log | |
| mark: "is longer than" | |
| - name: Stage OSConfig Logs | |
| if: success() || failure() | |
| run: | | |
| mkdir osconfig-logs | |
| stat /var/log/osconfig_nrp.log | |
| sudo chown -R $USER:$(id -gn) /var/log/osconfig* | |
| sudo chmod 644 /var/log/osconfig* | |
| cp -r /var/log/osconfig* osconfig-logs/ | |
| - uses: actions/upload-artifact@v4 | |
| if: success() || failure() | |
| with: | |
| name: ${{ steps.normalize.outputs.name }}_report | |
| path: '${{ steps.normalize.outputs.dir }}/*testResults.xml' | |
| - uses: actions/upload-artifact@v4 | |
| if: success() || failure() | |
| with: | |
| name: ${{ steps.normalize.outputs.name }}_logs | |
| path: osconfig-logs/osconfig* | |
| module-test: | |
| name: Module Test | |
| # Module test requires the package artifact as it also includes the modules to be tested in the artifact | |
| if: ${{ needs.setup-matrix.outputs.custom_download == 'false' }} | |
| needs: [setup-matrix, package] | |
| runs-on: [self-hosted, 1ES.Pool=ci-pool, '1ES.ImageOverride=${{ matrix.target.os }}-${{ matrix.target.version }}'] | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| target: ${{ fromJSON(needs.setup-matrix.outputs.targets) }} | |
| arch: [amd64] | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - run: ldd --version | |
| - uses: actions/download-artifact@v4 | |
| id: download | |
| with: | |
| name: nrp-test | |
| - name: Create osconfig.json | |
| run: | | |
| sudo mkdir -p /etc/osconfig | |
| sudo cp -r ${{ steps.download.outputs.download-path }}/modules/test/osconfig.json /etc/osconfig/osconfig.json | |
| - name: Defer test execution | |
| uses: ./.github/actions/defer-tests | |
| - name: Run moduletest | |
| working-directory: ${{ steps.download.outputs.download-path }}/modules/test | |
| run: | | |
| sudo chmod +x ./moduletest | |
| result=0 | |
| recipe="./recipes/SecurityBaselineTests.json" | |
| name=$(basename $recipe | tr '[:upper:]' '[:lower:]' | sed 's/\.[^.]*$//' | sed 's/\(test\|tests\)$//') | |
| echo -n "testing $name ... " | |
| if output=$(sudo ./moduletest $recipe --bin ../bin); then | |
| echo passed | |
| else | |
| echo failed | |
| result=1 | |
| echo "::warning file=$name.log::Error(s) in module-test for '$name'" | |
| fi | |
| echo "$output" | |
| echo "$output" > ../../$name.log | |
| exit $result | |
| - name: Check performance | |
| if: success() || failure() | |
| uses: ./.github/actions/check-perf | |
| with: | |
| perflog: /var/log/osconfig_asb_perf.log | |
| mark: "is longer than" | |
| - uses: actions/upload-artifact@v4 | |
| if: success() || failure() | |
| with: | |
| name: ${{ matrix.target.os }}-${{ matrix.target.version }}_logs | |
| path: '*.log' | |
| local-tests: | |
| name: Local Tests | |
| if: ${{ needs.setup-matrix.outputs.custom_download == 'false' }} | |
| needs: package | |
| runs-on: [self-hosted, 1ES.Pool=ci-pool, '1ES.ImageOverride=ubuntu-24.04'] | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: actions/download-artifact@v4 | |
| id: download | |
| with: | |
| name: nrp-test | |
| - uses: azure/login@v2 | |
| with: | |
| auth-type: IDENTITY | |
| client-id: ${{ secrets.AZURE_CLIENT_ID }} | |
| tenant-id: ${{ secrets.AZURE_TENANT_ID }} | |
| subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} | |
| - name: Run Local Tests | |
| working-directory: src/tests/universal-nrp-e2e | |
| run: | | |
| echo "Copying Azure CLI configuration/tokens to root" | |
| sudo mkdir -p /root/.azure | |
| sudo cp -r ~/.azure/* /root/.azure/ | |
| echo "Starting local tests" | |
| # Start tests using built packages, use 2048MB of memory per VM and disable the GUI | |
| # The -n flag disables the GUI, which is needed in the headless environment | |
| # The -p flag disables parallel image downloads | |
| sudo ./StartTests.sh -d ${{ steps.download.outputs.download-path }} -m ${{ env.vmDefaultMemory }} -n -p | |
| - name: Stage logs and reports | |
| if: success() || failure() | |
| working-directory: src/tests/universal-nrp-e2e | |
| run: | | |
| mkdir -p staging/reports | |
| mkdir -p staging/logs | |
| sudo find .cache -type f -name '*.tar.gz' | while read -r file; do | |
| parent_dir=$(dirname "$file") | |
| base_dir=$(basename "$parent_dir") | |
| dest_dir="staging/reports/$base_dir" | |
| mkdir -p "$dest_dir" | |
| sudo cp "$file" "$dest_dir/" | |
| pushd "$dest_dir" | |
| sudo find . -name '*.tar.gz' -exec tar -xzf {} \; -exec rm -f {} \; | |
| sudo find . -mindepth 2 -type f -exec mv -t . -- {} + | |
| sudo find . -mindepth 1 -type d -exec rmdir --ignore-fail-on-non-empty {} + | |
| sudo mkdir -p "../../logs/$base_dir" | |
| sudo find . \( -name '*.log' -o -name '*.bak' \) -exec mv -t ../../logs/$base_dir -- {} + | |
| popd | |
| done | |
| sudo chown -R $USER:$(id -gn) ./staging | |
| echo "Listing reports:" | |
| find ./staging/reports -type f -print | xargs ls -l | |
| echo "Listing logs:" | |
| find ./staging/logs -type f -print | xargs ls -l | |
| - name: Upload Logs Artifact | |
| uses: actions/upload-artifact@v4 | |
| if: success() || failure() | |
| with: | |
| name: local_tests_logs | |
| path: 'src/tests/universal-nrp-e2e/staging/logs/**/*' | |
| - name: Upload Test Reports Artifact | |
| uses: actions/upload-artifact@v4 | |
| if: success() || failure() | |
| with: | |
| name: local_tests_reports | |
| path: 'src/tests/universal-nrp-e2e/staging/reports/**/*testResults.xml' | |
| - name: Cleanup root azure/login | |
| if: always() | |
| run: sudo rm -rf /root/.azure | |
| # See for more details: https://github.com/marketplace/actions/publish-test-results | |
| report: | |
| name: Report | |
| needs: [mc-test, local-tests] | |
| runs-on: ubuntu-latest | |
| permissions: | |
| checks: write | |
| pull-requests: write | |
| if: always() | |
| steps: | |
| - name: Download CloudTest Report Artifacts | |
| uses: actions/download-artifact@v4 | |
| with: | |
| path: universal-nrp-test | |
| pattern: '*_report' | |
| merge-multiple: true | |
| - name: Download Local Test Report Artifacts | |
| uses: actions/download-artifact@v4 | |
| with: | |
| path: local_tests_reports | |
| - name: Publish CloudTest Test Results | |
| uses: EnricoMi/publish-unit-test-result-action@v2 | |
| with: | |
| files: 'universal-nrp-test/*testResults.xml' | |
| - name: Publish Local Test Results | |
| uses: EnricoMi/publish-unit-test-result-action@v2 | |
| with: | |
| files: 'local_tests_reports/**/*testResults.xml' |