├── .flake8 ├── .fmf └── version ├── .github ├── dependabot.yml ├── pull_request_template.md └── workflows │ ├── codespell.yml │ ├── docstring_validation.yml │ ├── pytest.yml │ ├── stylish.yml │ └── testimony.yml ├── .gitleaks.toml ├── .packit.yaml ├── .sourcery.yaml ├── .testing-farm.yaml ├── LICENSE ├── README.md ├── TESTING.md ├── data ├── .exp.sed ├── cert-api.access.redhat.com.pem ├── insights-client.conf ├── insights-client.motd ├── logrotate.d │ ├── insights-client │ └── meson.build ├── meson.build ├── redhattools.pub.gpg ├── rpm.egg ├── rpm.egg.asc ├── systemd │ ├── 80-insights-register.preset │ ├── 80-insights.preset │ ├── insights-client-boot.service │ ├── insights-client-checkin.service.in │ ├── insights-client-checkin.timer │ ├── insights-client-results.path.in │ ├── insights-client-results.service.in │ ├── insights-client.service │ ├── insights-client.timer │ ├── insights-register.path.in │ ├── insights-register.service.in │ ├── insights-unregister.path.in │ ├── insights-unregister.service.in │ └── meson.build └── tmpfiles.d │ ├── insights-client.conf │ └── meson.build ├── docs ├── file-content-redaction.yaml.example ├── file-redaction.yaml.example ├── insights-client.8 ├── insights-client.conf.5 └── meson.build ├── insights-client.spec ├── integration-tests ├── README.md ├── __init__.py ├── conftest.py ├── constants.py ├── custom_betelgeuse_config.py ├── playbook_verifier │ ├── __init__.py │ ├── playbooks │ │ ├── README.md │ │ ├── bugs.yml │ │ ├── compliance_openscap_setup.yml │ │ └── insights_setup.yml │ └── test_verifier.py ├── requirements.txt ├── test_checkin.py ├── test_client.py ├── test_client_options.py ├── test_client_systemd.py ├── test_collection.py ├── test_common_specs.py ├── test_compliance.py ├── test_connection.py ├── test_display_name_option.py ├── test_e2e.py ├── test_file_workflow.py ├── test_manpage.py ├── test_motd.py ├── test_obfuscation.py ├── test_redaction.py ├── test_registration.py ├── test_status.py ├── test_tags.py ├── test_unregister.py ├── test_upload.py ├── test_version.py └── testimony.yml ├── meson.build ├── meson_options.txt ├── pyproject.toml ├── pytest.ini ├── requirements-dev.txt ├── scripts ├── 01-upgrade-egg.sh └── README.md ├── src ├── insights-client.in ├── insights_client │ ├── __init__.py │ ├── constants.py.in │ ├── meson.build │ ├── run.py │ ├── tests │ │ ├── conftest.py │ │ ├── meson.build │ │ ├── requirements-core.txt │ │ ├── requirements.txt │ │ ├── test_client.py │ │ ├── test_commands.py │ │ ├── test_motd.py │ │ └── test_sed.py │ └── utc.py ├── meson.build └── redhat-access-insights.in └── systemtest ├── copr-setup.sh ├── guest-setup.sh ├── insights-core-setup.sh ├── plans └── main.fmf └── tests └── integration ├── main.fmf └── test.sh /.flake8: -------------------------------------------------------------------------------- 1 | [flake8] 2 | filename = 3 | *.py, 4 | *.py.in, 5 | */src/insights-client.in, 6 | # same limit as black 7 | max-line-length = 88 8 | ignore = 9 | # E203 whitespace before ':' 10 | # result of black-formatted code 11 | E203, 12 | # W503: line break before binary operator 13 | W503 14 | extend-exclude = 15 | # default build directory 16 | build/, 17 | -------------------------------------------------------------------------------- /.fmf/version: -------------------------------------------------------------------------------- 1 | 1 2 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "github-actions" 4 | directory: "/" 5 | schedule: 6 | interval: "weekly" 7 | target-branch: "master" 8 | commit-message: 9 | prefix: "ci" 10 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | 4 | 11 | 12 | 15 | 16 | 20 | -------------------------------------------------------------------------------- /.github/workflows/codespell.yml: -------------------------------------------------------------------------------- 1 | name: codespell 2 | 3 | on: 4 | pull_request: 5 | workflow_dispatch: 6 | 7 | jobs: 8 | codespell: 9 | runs-on: "ubuntu-latest" 10 | 11 | steps: 12 | - uses: actions/checkout@v4 13 | 14 | - uses: codespell-project/actions-codespell@v2 15 | -------------------------------------------------------------------------------- /.github/workflows/docstring_validation.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Test Docstrings Validation 3 | 4 | on: 5 | pull_request: 6 | paths: 7 | - "integration-tests/**" 8 | 9 | jobs: 10 | betelgeuse: 11 | name: "betelgeuse dry-run" 12 | runs-on: ubuntu-latest 13 | container: 14 | image: fedora:latest 15 | 16 | steps: 17 | - uses: actions/checkout@v4 18 | 19 | - name: Base setup for Betelgeuse 20 | run: | 21 | dnf --setopt install_weak_deps=False install -y \ 22 | python3-pip 23 | python3 -m pip install betelgeuse 24 | 25 | - name: Run Betelgeuse 26 | run: | 27 | PYTHONPATH=integration-tests/ betelgeuse --config-module \ 28 | custom_betelgeuse_config test-case --dry-run \ 29 | integration-tests/ dryrun_project ./test_case.xml 30 | -------------------------------------------------------------------------------- /.github/workflows/pytest.yml: -------------------------------------------------------------------------------- 1 | name: pytest 2 | 3 | on: 4 | pull_request: 5 | paths: 6 | - "src/**" 7 | - ".github/workflows/pytest.yml" 8 | 9 | jobs: 10 | pytest: 11 | strategy: 12 | fail-fast: false 13 | matrix: 14 | include: 15 | - name: "Fedora Rawhide" 16 | image: "registry.fedoraproject.org/fedora:rawhide" 17 | - name: "Fedora Latest" 18 | image: "registry.fedoraproject.org/fedora:latest" 19 | - name: "CentOS Stream 10" 20 | image: "quay.io/centos/centos:stream10" 21 | 22 | runs-on: "ubuntu-latest" 23 | container: 24 | image: ${{ matrix.image }} 25 | 26 | steps: 27 | - name: "Checkout the repository" 28 | uses: actions/checkout@v4 29 | 30 | - name: "Install dependencies" 31 | run: | 32 | dnf --setopt install_weak_deps=False install -y git-core python3-pip gpg 33 | python3 -m pip install --upgrade pip wheel 34 | python3 -m pip install -r src/insights_client/tests/requirements.txt 35 | 36 | - name: "Run pytest" 37 | env: 38 | PYTEST_ADDOPTS: "--color=yes --code-highlight=yes --showlocals" 39 | run: | 40 | python3 -m pytest src/insights_client/tests 41 | -------------------------------------------------------------------------------- /.github/workflows/stylish.yml: -------------------------------------------------------------------------------- 1 | name: stylish 2 | 3 | on: 4 | pull_request: 5 | workflow_dispatch: 6 | 7 | jobs: 8 | stylish: 9 | name: "black & flake8" 10 | runs-on: ubuntu-latest 11 | container: 12 | image: fedora:latest 13 | 14 | steps: 15 | - name: Base setup 16 | run: | 17 | dnf --setopt install_weak_deps=False install -y \ 18 | git-core \ 19 | python3-flake8 \ 20 | python3-pip 21 | 22 | - uses: actions/checkout@v4 23 | 24 | - uses: psf/black@stable 25 | with: 26 | version: "24.3.0" 27 | 28 | - name: Setup flake8 annotations 29 | uses: rbialon/flake8-annotations@v1 30 | 31 | - name: Run flake8 32 | run: | 33 | flake8 34 | -------------------------------------------------------------------------------- /.github/workflows/testimony.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Testimony Validation 3 | 4 | on: 5 | pull_request: 6 | paths: 7 | - "integration-tests/**" 8 | 9 | jobs: 10 | testimony: 11 | name: testimony validate 12 | runs-on: ubuntu-latest 13 | container: 14 | image: fedora:latest 15 | 16 | steps: 17 | - name: Setup for Testimony 18 | run: | 19 | dnf --setopt install_weak_deps=False install -y \ 20 | python3-pip 21 | python3 -m pip install testimony 22 | - uses: actions/checkout@v4 23 | 24 | - name: Run Testimony 25 | run: | 26 | testimony validate --config \ 27 | integration-tests/testimony.yml integration-tests/test* 28 | -------------------------------------------------------------------------------- /.gitleaks.toml: -------------------------------------------------------------------------------- 1 | [extend] 2 | useDefault = true 3 | 4 | 5 | [allowlist] 6 | description = "Repository-specific configuration" 7 | 8 | paths = [ 9 | # Tests for insights-core contain public and private GPG keypair. They were 10 | # generated specifically for this usecase. 11 | "systemtest/insights-core-setup.sh", 12 | ] 13 | -------------------------------------------------------------------------------- /.packit.yaml: -------------------------------------------------------------------------------- 1 | upstream_package_name: insights-client 2 | downstream_package_name: insights-client 3 | specfile_path: out/insights-client.spec 4 | 5 | srpm_build_deps: 6 | - gawk 7 | - rpkg 8 | 9 | actions: 10 | post-upstream-clone: 11 | - mkdir out 12 | - rpkg srpm --outdir out 13 | get-current-version: 14 | - awk '/^Version:/ {print $2;}' out/insights-client.spec 15 | create-archive: 16 | - bash -c 'echo out/insights-client-*.tar.*' 17 | fix-spec-file: 18 | - echo 'nothing to fix' 19 | 20 | jobs: 21 | - job: copr_build 22 | trigger: pull_request 23 | targets: 24 | - centos-stream-10-x86_64 25 | - centos-stream-10-aarch64 26 | - centos-stream-10-s390x 27 | - centos-stream-10-ppc64le 28 | - fedora-all 29 | 30 | - job: copr_build 31 | trigger: commit 32 | branch: master 33 | owner: "@yggdrasil" 34 | project: latest 35 | targets: 36 | - centos-stream-10-x86_64 37 | - centos-stream-10-aarch64 38 | - centos-stream-10-s390x 39 | - centos-stream-10-ppc64le 40 | - fedora-all 41 | 42 | - job: tests 43 | trigger: pull_request 44 | identifier: "unit/centos-stream" 45 | targets: 46 | - centos-stream-10-x86_64 47 | labels: 48 | - unit 49 | tf_extra_params: 50 | environments: 51 | - artifacts: 52 | - type: repository-file 53 | id: https://copr.fedorainfracloud.org/coprs/g/yggdrasil/latest/repo/centos-stream-$releasever/group_yggdrasil-latest-centos-stream-$releasever.repo 54 | 55 | - job: tests 56 | trigger: pull_request 57 | identifier: "unit/fedora" 58 | targets: 59 | - fedora-all 60 | labels: 61 | - unit 62 | tf_extra_params: 63 | environments: 64 | - artifacts: 65 | - type: repository-file 66 | id: https://copr.fedorainfracloud.org/coprs/g/yggdrasil/latest/repo/fedora-$releasever/group_yggdrasil-latest-fedora-$releasever.repo 67 | 68 | - job: tests 69 | trigger: pull_request 70 | identifier: "unit/rhel" 71 | targets: 72 | centos-stream-10-x86_64: # TODO: Change back to RHEL 73 | distros: 74 | - RHEL-10-Nightly 75 | labels: 76 | - unit 77 | tf_extra_params: 78 | environments: 79 | - artifacts: 80 | - type: repository-file 81 | id: https://copr.fedorainfracloud.org/coprs/g/yggdrasil/latest/repo/rhel-$releasever/group_yggdrasil-latest-rhel-$releasever.repo 82 | settings: 83 | provisioning: 84 | tags: 85 | BusinessUnit: sst_csi_client_tools 86 | use_internal_tf: true 87 | -------------------------------------------------------------------------------- /.sourcery.yaml: -------------------------------------------------------------------------------- 1 | rule_settings: 2 | enable: [default] 3 | disable: 4 | - no-loop-in-tests 5 | - no-conditionals-in-tests 6 | -------------------------------------------------------------------------------- /.testing-farm.yaml: -------------------------------------------------------------------------------- 1 | version: 1 2 | 3 | environments: 4 | secrets: 5 | SETTINGS_URL: "ea2a89aa-6a78-40e0-906e-140f623c45b0,qdeC9UxsCJa8e4UA/873Bv41RgSaYlbmFWz97gNEkiFjgxBF5wQiZGD3KDgki110oOXmAb4Ty0haFAi7XB2SAZvA7ooXRxrVRP/U3dyxNir5uYmHRurBx+pqb05eOn2zmygqqyTthXNglRvg2kJnjuYB44h4W/u2VUl8Id+s4h67IaX15toY3VKr3vmdH2OxFMUI6U/atyd6Z96jqP0muxhu+KuYv40nYUrgiFqoOedom0DOCPCMQUubeYQ3mexcc0HF/L28bbyt1eFNWYYuSXb61/Gjg3s2Shw7e7RhWquqsvzWlCTa0JFalGD+uELgVxOjwHDbn56drnnjh3oVcpD20YTQQbsAGHCgPhqCuxkqCe9ayIvKKCsYKapdQ+D0NvjXFRyj2QfFrOdQHf4KUOD6MXBWAOswM/3C6x+zj6O5YZTSmMagq1oLgeG2wqhL56zNUGoxqgcRv9MYmgPzbiF6SPlAniMwWjjcDNsmWhMAoJDAI5q7+Tazjs7OvziUsEgMp1T4jZooIdHtAU+ily4K8Gf8lPiQHhxFD1ieC9E1xzBeVuh+tHhuB7E6WN+sISzxXdyUDTd5lyZicqgxJ/GmEjB2K+/MDDW1VI9Yyk6Ee3iUVGGBktvaF0eV4WqrNPv8AAJL/RMDU+T/grUTmf703DQFNWPyISAJDuw8h+Y=" 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (C) 2015 Red Hat Inc. 2 | 3 | This program is free software: you can redistribute it and/or modify 4 | it under the terms of the GNU General Public License as published by 5 | the Free Software Foundation, either version 3 of the License, or 6 | (at your option) any later version. 7 | 8 | This program is distributed in the hope that it will be useful, 9 | but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | GNU General Public License for more details. 12 | 13 | You should have received a copy of the GNU General Public License 14 | along with this program. If not, see . 15 | -------------------------------------------------------------------------------- /TESTING.md: -------------------------------------------------------------------------------- 1 | # Testing `insights-client` 2 | 3 | After installing the prerequisites, you can run the unit test suite using `pytest`: 4 | 5 | ```shell 6 | $ python3 -m pip install -r src/insights_client/tests/requirements.txt 7 | $ python3 -m pytest src/insights_client/tests 8 | ``` 9 | 10 | ## Specifying the egg 11 | 12 | By default, we are using the `rpm.egg` to run insights-client test suite. 13 | By pointing the `EGG` environment variable to a different path, you can test custom eggs (the upstream HEAD, for example). 14 | 15 | 16 | ## CI 17 | 18 | The unit tests are also run by GitHub Actions. 19 | -------------------------------------------------------------------------------- /data/.exp.sed: -------------------------------------------------------------------------------- 1 | s/(password[a-zA-Z0-9_]*)(\s*\:\s*\"*\s*|\s*\"*\s*=\s*\"\s*|\s*=+\s*|\s*--md5+\s*|\s*)([a-zA-Z0-9_!@#$%^&*()+=/-]+)/\1\2********/ 2 | s/(password[a-zA-Z0-9_]*)(\s*\*+\s+)(.+)/\1\2********/ 3 | -------------------------------------------------------------------------------- /data/cert-api.access.redhat.com.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIHZDCCBUygAwIBAgIJAOb+QiglyeZeMA0GCSqGSIb3DQEBBQUAMIGwMQswCQYD 3 | VQQGEwJVUzEXMBUGA1UECAwOTm9ydGggQ2Fyb2xpbmExEDAOBgNVBAcMB1JhbGVp 4 | Z2gxFjAUBgNVBAoMDVJlZCBIYXQsIEluYy4xGDAWBgNVBAsMD1JlZCBIYXQgTmV0 5 | d29yazEeMBwGA1UEAwwVRW50aXRsZW1lbnQgTWFzdGVyIENBMSQwIgYJKoZIhvcN 6 | AQkBFhVjYS1zdXBwb3J0QHJlZGhhdC5jb20wHhcNMTAwMzE3MTkwMDQ0WhcNMzAw 7 | MzEyMTkwMDQ0WjCBsDELMAkGA1UEBhMCVVMxFzAVBgNVBAgMDk5vcnRoIENhcm9s 8 | aW5hMRAwDgYDVQQHDAdSYWxlaWdoMRYwFAYDVQQKDA1SZWQgSGF0LCBJbmMuMRgw 9 | FgYDVQQLDA9SZWQgSGF0IE5ldHdvcmsxHjAcBgNVBAMMFUVudGl0bGVtZW50IE1h 10 | c3RlciBDQTEkMCIGCSqGSIb3DQEJARYVY2Etc3VwcG9ydEByZWRoYXQuY29tMIIC 11 | IjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA2Z+mW7OYcBcGxWS+RSKG2GJ2 12 | csMXiGGfEp36vKVsIvypmNS60SkicKENMYREalbdSjrgfXxPJygZWsVWJ5lHPfBV 13 | o3WkFrFHTIXd/R6LxnaHD1m8Cx3GwEeuSlE/ASjc1ePtMnsHH7xqZ9wdl85b1C8O 14 | scgO7fwuM192kvv/veI/BogIqUQugtG6szXpV8dp4ml029LXFoNIy2lfFoa2wKYw 15 | MiUHwtYgAz7TDY63e8qGhd5PoqTv9XKQogo2ze9sF9y/npZjliNy5qf6bFE+24oW 16 | E8pGsp3zqz8h5mvw4v+tfIx5uj7dwjDteFrrWD1tcT7UmNrBDWXjKMG81zchq3h4 17 | etgF0iwMHEuYuixiJWNzKrLNVQbDmcLGNOvyJfq60tM8AUAd72OUQzivBegnWMit 18 | CLcT5viCT1AIkYXt7l5zc/duQWLeAAR2FmpZFylSukknzzeiZpPclRziYTboDYHq 19 | revM97eER1xsfoSYp4mJkBHfdlqMnf3CWPcNgru8NbEPeUGMI6+C0YvknPlqDDtU 20 | ojfl4qNdf6nWL+YNXpR1YGKgWGWgTU6uaG8Sc6qGfAoLHh6oGwbuz102j84OgjAJ 21 | DGv/S86svmZWSqZ5UoJOIEqFYrONcOSgztZ5tU+gP4fwRIkTRbTEWSgudVREOXhs 22 | bfN1YGP7HYvS0OiBKZUCAwEAAaOCAX0wggF5MB0GA1UdDgQWBBSIS6ZFxEbsj9bP 23 | pvYazyY8kMx/FzCB5QYDVR0jBIHdMIHagBSIS6ZFxEbsj9bPpvYazyY8kMx/F6GB 24 | tqSBszCBsDELMAkGA1UEBhMCVVMxFzAVBgNVBAgMDk5vcnRoIENhcm9saW5hMRAw 25 | DgYDVQQHDAdSYWxlaWdoMRYwFAYDVQQKDA1SZWQgSGF0LCBJbmMuMRgwFgYDVQQL 26 | DA9SZWQgSGF0IE5ldHdvcmsxHjAcBgNVBAMMFUVudGl0bGVtZW50IE1hc3RlciBD 27 | QTEkMCIGCSqGSIb3DQEJARYVY2Etc3VwcG9ydEByZWRoYXQuY29tggkA5v5CKCXJ 28 | 5l4wDAYDVR0TBAUwAwEB/zALBgNVHQ8EBAMCAQYwEQYJYIZIAYb4QgEBBAQDAgEG 29 | MCAGA1UdEQQZMBeBFWNhLXN1cHBvcnRAcmVkaGF0LmNvbTAgBgNVHRIEGTAXgRVj 30 | YS1zdXBwb3J0QHJlZGhhdC5jb20wDQYJKoZIhvcNAQEFBQADggIBAJ1hEdNBDTRr 31 | 6kI6W6stoogSUwjuiWPDY8DptwGhdpyIfbCoxvBR7F52DlwyXOpCunogfKMRklnE 32 | gH1Wt66RYkgNuJcenKHAhR5xgSLoPCOVF9rDjMunyyBuxjIbctM21R7BswVpsEIE 33 | OpV5nlJ6wkHsrn0/E+Zk5UJdCzM+Fp4hqHtEn/c97nvRspQcpWeDg6oUvaJSZTGM 34 | 8yFpzR90X8ZO4rOgpoERukvYutUfJUzZuDyS3LLc6ysamemH93rZXr52zc4B+C9G 35 | Em8zemDgIPaH42ce3C3TdVysiq/yk+ir7pxW8toeavFv75l1UojFSjND+Q2AlNQn 36 | pYkmRznbD5TZ3yDuPFQG2xYKnMPACepGgKZPyErtOIljQKCdgcvb9EqNdZaJFz1+ 37 | /iWKYBL077Y0CKwb+HGIDeYdzrYxbEd95YuVU0aStnf2Yii2tLcpQtK9cC2+DXjL 38 | Yf3kQs4xzH4ZejhG9wzv8PGXOS8wHYnfVNA3+fclDEQ1mEBKWHHmenGI6QKZUP8f 39 | g0SQ3PNRnSZu8R+rhABOEuVFIBRlaYijg2Pxe0NgL9FlHsNyRfo6EUrB2QFRKACW 40 | 3Mo6pZyDjQt7O8J7l9B9IIURoJ1niwygf7VSJTMl2w3fFleNJlZTGgdXw0V+5g+9 41 | Kg6Ay0rrsi4nw1JHue2GvdjdfVOaWSWC 42 | -----END CERTIFICATE----- 43 | -----BEGIN CERTIFICATE----- 44 | MIIFfTCCA2WgAwIBAgIJAJGKz8qFAAADMA0GCSqGSIb3DQEBBQUAMIGwMQswCQYD 45 | VQQGEwJVUzEXMBUGA1UECAwOTm9ydGggQ2Fyb2xpbmExEDAOBgNVBAcMB1JhbGVp 46 | Z2gxFjAUBgNVBAoMDVJlZCBIYXQsIEluYy4xGDAWBgNVBAsMD1JlZCBIYXQgTmV0 47 | d29yazEeMBwGA1UEAwwVRW50aXRsZW1lbnQgTWFzdGVyIENBMSQwIgYJKoZIhvcN 48 | AQkBFhVjYS1zdXBwb3J0QHJlZGhhdC5jb20wHhcNMTUwNTA1MTMwMzQ4WhcNMjUw 49 | NTAyMTMwMzQ4WjCBiTELMAkGA1UEBhMCVVMxFzAVBgNVBAgTDk5PUlRIIENBUk9M 50 | SU5BMRAwDgYDVQQHEwdSYWxlaWdoMRAwDgYDVQQKEwdSZWQgSGF0MRgwFgYDVQQL 51 | Ew9SZWQgSGF0IE5ldHdvcmsxIzAhBgNVBAMTGmNlcnQtYXBpLmFjY2Vzcy5yZWRo 52 | YXQuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA9hTNMtZMa7Kg 53 | Jlux6pnuUinP0Rv0aiiPFr7qNHFore4loGrPlpzUvQbUByy3xm7lhf4R4qbINCls 54 | veWg6HDidvQr174RXb5YLMXuBrYAiPWQTrRRLNuvXFHKzREghRWTv48IXTIDEo0G 55 | fZJUO+myY2RfwqugZKic5dR6ZakHSSpQO70O6H5R0eHlKa13k4eEpG2fVY/xqFto 56 | WkfZyEmSacZpqxp7gIjZqreLc4MFwpiVjGFrK3Jk+Px1Z6J94LTLx2SxrYzWIeUs 57 | 5j+lceQOvpV4/pkClnRCW1pkCKTccjFKQkpNPGwdIusRXUGl9IYc20Fa/7g9iUQc 58 | 5fXu9EAzfwIDAQABo4G+MIG7MAkGA1UdEwQCMAAwEQYJYIZIAYb4QgEBBAQDAgZA 59 | MAsGA1UdDwQEAwIF4DATBgNVHSUEDDAKBggrBgEFBQcDATA5BglghkgBhvhCAQ0E 60 | LBYqTWFuYWdlZCBieSBSZWQgSGF0IChjYS1zdXBwb3J0QHJlZGhhdC5jb20pMB0G 61 | A1UdDgQWBBRfgCjd8aXf0U4VX8DKTVIn+paGBzAfBgNVHSMEGDAWgBSIS6ZFxEbs 62 | j9bPpvYazyY8kMx/FzANBgkqhkiG9w0BAQUFAAOCAgEAlC+r6UEEp5BUkI0Rj2T+ 63 | 1PH7oUCaGQeQoyVbGddz/WUcBk/lMMtyCEoxU+3tTwNWmCWWjYXtjoL9MlSAC/q+ 64 | NZfBi1iq0uuSus9JI/Uu8aRhoxTK56qGRed/JNixOHEmFn891cahIPpF0exWwtYD 65 | ThwXo7Z6PI7t8EMKdSrGTOowp58yho8xYFL/Z7JmjL55Pf85GIrdiniNZd4i178J 66 | 07R9zsiLvdXq9mT33iJwkm+uhO+FA9d8OE3ji21pBbGUAQSWOdkemvUCsy8zANW9 67 | fT+dBrMr5Buk7eaBBJ2PxECNiWLCRQJWmyff1O5zMT0daS2lBdEGUNhBZ0hnX13Q 68 | kabUp0bxRrNRq+WkomP7onZhfZS6SjKm0UmwoV6o3V1ED6y7muQNRmgDpA5PcbvO 69 | gl7OexNL4zcpyfMdAmTYf5yTRSvB42Yg5hVfuzPEnOIqupwES3mWkEHRlqbMUkHw 70 | qIQAxIwQqZd5PdPpElQ/6j/ZT9DwW/I6zgndX2rsS0oGYcwFTkSj0/rKKkC13hk7 71 | LchXMZu5ckdustM79U6OZIBairrJaL2OpR08un2nwIjgEGqhVFYc44UK1VpkE8mr 72 | qvqJS6OHVlTlKcEDnhVkPS3i5qjuS/PtSq0CwH8bzYKFJayLDY/z36Zv6PdttzmU 73 | Yb1NSDcJejHJ80pMINutyYQ= 74 | -----END CERTIFICATE----- 75 | -------------------------------------------------------------------------------- /data/insights-client.conf: -------------------------------------------------------------------------------- 1 | [insights-client] 2 | # Example options in this file are the defaults 3 | 4 | # Change log level, valid options DEBUG, INFO, WARNING, ERROR, CRITICAL. Default DEBUG 5 | #loglevel=DEBUG 6 | 7 | # Attempt to auto configure with Satellite server 8 | #auto_config=True 9 | 10 | # Base URL for the Insights API 11 | #base_url=cert-api.access.redhat.com:443/r/insights 12 | 13 | # URL for your proxy. Example: http://user:pass@192.168.100.50:8080 14 | #proxy= 15 | 16 | # Automatically update the dynamic configuration 17 | #auto_update=True 18 | 19 | # Obfuscate IP addresses 20 | #obfuscate=False 21 | 22 | # Obfuscate hostname. Requires obfuscate=True. 23 | #obfuscate_hostname=False 24 | 25 | # Display name for registration 26 | #display_name= 27 | 28 | # Ansible hostname for this system 29 | #ansible_host= 30 | 31 | # Timeout for commands run during collection, in seconds 32 | #cmd_timeout=120 33 | 34 | # Timeout for HTTP calls, in seconds 35 | #http_timeout=120 36 | 37 | # Location of the redaction file for commands, files, and components 38 | #redaction_file=/etc/insights-client/file-redaction.yaml 39 | 40 | # Location of the redaction file for patterns and keywords 41 | #content_redaction_file=/etc/insights-client/file-content-redaction.yaml 42 | 43 | # Location of the tags file for this system 44 | #tags_file=/etc/insights-client/tags.yaml 45 | -------------------------------------------------------------------------------- /data/insights-client.motd: -------------------------------------------------------------------------------- 1 | Register this system with Red Hat Insights: rhc connect 2 | 3 | Example: 4 | # rhc connect --activation-key --organization 5 | 6 | The rhc client and Red Hat Insights will enable analytics and additional 7 | management capabilities on your system. 8 | View your connected systems at https://console.redhat.com/insights 9 | 10 | You can learn more about how to register your system 11 | using rhc at https://red.ht/registration 12 | -------------------------------------------------------------------------------- /data/logrotate.d/insights-client: -------------------------------------------------------------------------------- 1 | /var/log/insights-client/*.log { 2 | rotate 4 3 | weekly 4 | missingok 5 | notifempty 6 | copytruncate 7 | } 8 | -------------------------------------------------------------------------------- /data/logrotate.d/meson.build: -------------------------------------------------------------------------------- 1 | install_data( 2 | ['insights-client'], 3 | install_dir: get_option('sysconfdir') / 'logrotate.d' 4 | ) 5 | -------------------------------------------------------------------------------- /data/meson.build: -------------------------------------------------------------------------------- 1 | sysconf_sources = [ 2 | '.exp.sed', 3 | 'cert-api.access.redhat.com.pem', 4 | 'insights-client.conf', 5 | 'insights-client.motd', 6 | 'redhattools.pub.gpg', 7 | 'rpm.egg', 8 | 'rpm.egg.asc' 9 | ] 10 | 11 | install_data( 12 | sysconf_sources, 13 | install_dir: get_option('sysconfdir') / 'insights-client' 14 | ) 15 | 16 | subdir('logrotate.d') 17 | subdir('systemd') 18 | subdir('tmpfiles.d') 19 | -------------------------------------------------------------------------------- /data/redhattools.pub.gpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RedHatInsights/insights-client/cfe9826192b6369d61b4d20f91321795c8adb93a/data/redhattools.pub.gpg -------------------------------------------------------------------------------- /data/rpm.egg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RedHatInsights/insights-client/cfe9826192b6369d61b4d20f91321795c8adb93a/data/rpm.egg -------------------------------------------------------------------------------- /data/rpm.egg.asc: -------------------------------------------------------------------------------- 1 | -----BEGIN PGP SIGNATURE----- 2 | Version: GnuPG v1 3 | 4 | iQIcBAABCAAGBQJoCfbwAAoJEHUU932DZrDZScwP/17zbBToA/d1Gzvq/v/QDp/c 5 | kNy9YMQwEAe1vF9FfB2j+OKulfQIqrIQoC9PADND1Ut0PaEW4As1sJEVot/kA6vk 6 | EU4+L6eEOAhI0SdozvKmGWxbtctYiwSRRnO/rqUR0NjFQpj+mVj7doRuL1OUUA3U 7 | 82o7bdZmvwsViS5bTvR8dZ5eTS4zLgZA3p7Bh4tjMdZZkzE2Ndq7+R6Y3z8glHVD 8 | auPkOR0/xXttDzT/7H9D+d5AFVdtOM42HGHi5x0wkiLp+Kq+DG7tXUCghvIXdY2c 9 | dQWV0yhecizSap9Ph6yF3/DK68xidMrIlXtzfpXyrzCYLUQiONDGsy+0JLwenHjt 10 | hjI5iRu4g3zok5dLbDQGam/ZbNe7ngvZT384tsjEVr6lx/TsH5H8C9EbDdxxCHnj 11 | rbOkoX0kEdd6SCUBRsczFOFIgglHaoF+PPwofuD/8GCoL4lRuQd21ouPq2YuLYbg 12 | HltcH5PCvvqKhZYHbCUIBuKf5Or+OnexBx/i17vC6SmSeXsmhPSxLeq9yu+XuyG+ 13 | DaAGVXeTHN1j9p/VxEpNQ0Kul2dymbCQNH6EMvg06mUGCgjCLA4YCposmx69PpJR 14 | F3PmmlNyFsd6R0D0ArfyleCvxwJeqGnsbRPPOtFYtHCweorYbVhbO/UTYA2hvltS 15 | Iasitn7L+jxs2B5+qz3R 16 | =mCaM 17 | -----END PGP SIGNATURE----- 18 | -------------------------------------------------------------------------------- /data/systemd/80-insights-register.preset: -------------------------------------------------------------------------------- 1 | enable insights-register.path 2 | enable insights-unregister.path 3 | -------------------------------------------------------------------------------- /data/systemd/80-insights.preset: -------------------------------------------------------------------------------- 1 | enable insights-client-boot.service 2 | -------------------------------------------------------------------------------- /data/systemd/insights-client-boot.service: -------------------------------------------------------------------------------- 1 | # This file is part of insights-client. 2 | # 3 | # Any changes made to this file will be overwritten during a software update. To 4 | # override a parameter in this file, create a drop-in file, typically located at 5 | # /etc/systemd/system/insights-client-boot.service.d/override.conf Put the desired 6 | # overrides in that file and reload systemd. 7 | # 8 | # For more information about systemd drop-in files, see systemd.unit(5). 9 | 10 | [Unit] 11 | Description=Run Insights Client at boot 12 | Documentation=man:insights-client(8) 13 | After=network-online.target 14 | ConditionPathExists=/etc/insights-client/.run_insights_client_next_boot 15 | 16 | [Service] 17 | Type=oneshot 18 | ExecStart=/usr/bin/insights-client --retry 3 19 | Restart=no 20 | WatchdogSec=900 21 | CPUQuota=30% 22 | MemoryHigh=1G 23 | MemoryMax=2G 24 | TasksMax=300 25 | BlockIOWeight=100 26 | ExecStartPre=/bin/rm -f /etc/insights-client/.run_insights_client_next_boot 27 | 28 | [Install] 29 | WantedBy=multi-user.target 30 | -------------------------------------------------------------------------------- /data/systemd/insights-client-checkin.service.in: -------------------------------------------------------------------------------- 1 | # This file is part of insights-client. 2 | # 3 | # Any changes made to this file will be overwritten during a software update. To 4 | # override a parameter in this file, create a drop-in file, typically located at 5 | # /etc/systemd/system/insights-client-results.service.d/override.conf. Put the 6 | # desired overrides in that file and reload systemd. The next time this service 7 | # is run (either manually or via another systemd unit), the overridden values 8 | # will be in effect. 9 | # 10 | # For more information about systemd drop-in files, see systemd.unit(5). 11 | 12 | [Unit] 13 | Description=Check-in with the platform 14 | Documentation=man:insights-client(8) 15 | After=network-online.target 16 | Wants=network-online.target 17 | 18 | [Service] 19 | Type=oneshot 20 | RemainAfterExit=no 21 | ExecStart=@bindir@/insights-client --checkin 22 | Restart=no 23 | -------------------------------------------------------------------------------- /data/systemd/insights-client-checkin.timer: -------------------------------------------------------------------------------- 1 | # This file is part of insights-client. 2 | # 3 | # Any changes made to this file will be overwritten during a software update. To 4 | # override a parameter in this file, create a drop-in file, typically located at 5 | # /etc/systemd/system/insights-client.timer.d/override.conf. Put the desired 6 | # overrides in that file, reload systemd and restart this timer. 7 | # 8 | # For more information about systemd drop-in files, see systemd.unit(5). 9 | 10 | [Unit] 11 | Description=Check-in with the platform timer 12 | Documentation=man:insights-client(8) 13 | After=network-online.target 14 | Wants=network-online.target 15 | 16 | [Timer] 17 | OnCalendar=hourly 18 | Persistent=true 19 | RandomizedDelaySec=600 20 | 21 | [Install] 22 | WantedBy=timers.target 23 | -------------------------------------------------------------------------------- /data/systemd/insights-client-results.path.in: -------------------------------------------------------------------------------- 1 | # This file is part of insights-client. 2 | # 3 | # Any changes made to this file will be overwritten during a software update. To 4 | # override a parameter in this file, create a drop-in file, typically located at 5 | # /etc/systemd/system/insights-client-results.path.d/override.conf. Put the 6 | # desired overrides in that file and reload systemd. 7 | # 8 | # For more information about systemd drop-in files, see systemd.unit(5). 9 | 10 | [Unit] 11 | Description=Monitor @pkgsysconfdir@/.lastupload for modifications 12 | Documentation=man:insights-client(8) 13 | PartOf=insights-client.timer 14 | 15 | [Path] 16 | PathModified=@pkgsysconfdir@/.lastupload 17 | 18 | [Install] 19 | WantedBy=insights-client.timer 20 | -------------------------------------------------------------------------------- /data/systemd/insights-client-results.service.in: -------------------------------------------------------------------------------- 1 | # This file is part of insights-client. 2 | # 3 | # Any changes made to this file will be overwritten during a software update. To 4 | # override a parameter in this file, create a drop-in file, typically located at 5 | # /etc/systemd/system/insights-client-results.service.d/override.conf. Put the 6 | # desired overrides in that file and reload systemd. The next time this service 7 | # is run (either manually or via another systemd unit), the overridden values 8 | # will be in effect. 9 | # 10 | # For more information about systemd drop-in files, see systemd.unit(5). 11 | 12 | [Unit] 13 | Description=Check for insights from Red Hat Cloud Services 14 | Documentation=man:insights-client(8) 15 | After=network-online.target 16 | 17 | [Service] 18 | Type=oneshot 19 | RemainAfterExit=no 20 | ExecStart=@bindir@/insights-client --check-results 21 | Restart=no 22 | -------------------------------------------------------------------------------- /data/systemd/insights-client.service: -------------------------------------------------------------------------------- 1 | # This file is part of insights-client. 2 | # 3 | # Any changes made to this file will be overwritten during a software update. To 4 | # override a parameter in this file, create a drop-in file, typically located at 5 | # /etc/systemd/system/insights-client.service.d/override.conf. Put the desired 6 | # overrides in that file and reload systemd. The next time this service is run 7 | # (either manually or via a systemd timer), the overridden values will be in 8 | # effect. 9 | # 10 | # For more information about systemd drop-in files, see systemd.unit(5). 11 | 12 | [Unit] 13 | Description=Insights Client 14 | Documentation=man:insights-client(8) 15 | After=network.target 16 | StartLimitIntervalSec=12h 17 | StartLimitBurst=6 18 | 19 | [Service] 20 | Type=exec 21 | ExecStart=/usr/bin/insights-client 22 | Restart=on-failure 23 | RestartSec=1h 24 | WatchdogSec=900 25 | CPUQuota=30% 26 | MemoryHigh=1G 27 | MemoryMax=2G 28 | TasksMax=300 29 | BlockIOWeight=100 30 | -------------------------------------------------------------------------------- /data/systemd/insights-client.timer: -------------------------------------------------------------------------------- 1 | # This file is part of insights-client. 2 | # 3 | # Any changes made to this file will be overwritten during a software update. To 4 | # override a parameter in this file, create a drop-in file, typically located at 5 | # /etc/systemd/system/insights-client.timer.d/override.conf. Put the desired 6 | # overrides in that file, reload systemd and restart this timer. 7 | # 8 | # For more information about systemd drop-in files, see systemd.unit(5). 9 | 10 | [Unit] 11 | Description=Insights Client Timer Task 12 | Documentation=man:insights-client(8) 13 | After=network-online.target 14 | Wants=network-online.target 15 | 16 | [Timer] 17 | OnCalendar=daily 18 | Persistent=true 19 | RandomizedDelaySec=14400 20 | 21 | [Install] 22 | WantedBy=timers.target 23 | Also=insights-client-results.path 24 | -------------------------------------------------------------------------------- /data/systemd/insights-register.path.in: -------------------------------------------------------------------------------- 1 | # This file is part of insights-client. 2 | # 3 | # Any changes made to this file will be overwritten during a software update. To 4 | # override a parameter in this file, create a drop-in file, typically located at 5 | # /etc/systemd/system/insights-register.path.d/override.conf. Put the desired 6 | # overrides in that file and reload systemd. 7 | # 8 | # For more information about systemd drop-in files, see systemd.unit(5). 9 | 10 | [Unit] 11 | Description=Automatically Register with Red Hat Insights Path Watch 12 | Documentation=man:insights-client(8) 13 | 14 | [Path] 15 | PathExists=@sysconfdir@/pki/consumer/cert.pem 16 | 17 | [Install] 18 | WantedBy=multi-user.target 19 | -------------------------------------------------------------------------------- /data/systemd/insights-register.service.in: -------------------------------------------------------------------------------- 1 | # This file is part of insights-client. 2 | # 3 | # Any changes made to this file will be overwritten during a software update. To 4 | # override a parameter in this file, create a drop-in file, typically located at 5 | # /etc/systemd/system/insights-register.service.d/override.conf. Put the desired 6 | # overrides in that file and reload systemd. The next time this service is run 7 | # (either manually or via a systemd timer), the overridden values will be in 8 | # effect. 9 | # 10 | # For more information about systemd drop-in files, see systemd.unit(5). 11 | 12 | [Unit] 13 | Description=Automatically Register with Red Hat Insights 14 | Documentation=man:insights-client(8) 15 | After=network-online.target 16 | 17 | [Service] 18 | Type=simple 19 | ExecStart=@bindir@/insights-client --register 20 | Restart=no 21 | WatchdogSec=900 22 | CPUQuota=30% 23 | MemoryHigh=1G 24 | MemoryMax=2G 25 | TasksMax=300 26 | BlockIOWeight=100 27 | ExecStopPost=systemctl mask --now insights-register.path 28 | -------------------------------------------------------------------------------- /data/systemd/insights-unregister.path.in: -------------------------------------------------------------------------------- 1 | # This file is part of insights-client. 2 | # 3 | # Any changes made to this file will be overwritten during a software update. To 4 | # override a parameter in this file, create a drop-in file, typically located at 5 | # /etc/systemd/system/insights-unregister.path.d/override.conf. Put the desired 6 | # overrides in that file and reload systemd. 7 | # 8 | # For more information about systemd drop-in files, see systemd.unit(5). 9 | 10 | [Unit] 11 | Description=Automatically Unregister from Red Hat Insights Path Watch 12 | Documentation=man:insights-client(8) 13 | 14 | [Path] 15 | PathChanged=@sysconfdir@/pki/consumer/cert.pem 16 | 17 | [Install] 18 | WantedBy=multi-user.target 19 | -------------------------------------------------------------------------------- /data/systemd/insights-unregister.service.in: -------------------------------------------------------------------------------- 1 | # This file is part of insights-client. 2 | # 3 | # Any changes made to this file will be overwritten during a software update. To 4 | # override a parameter in this file, create a drop-in file, typically located at 5 | # /etc/systemd/system/insights-unregister.service.d/override.conf. Put the 6 | # desired overrides in that file and reload systemd. The next time this service 7 | # is run (either manually or via a systemd timer), the overridden values will be 8 | # in effect. 9 | # 10 | # For more information about systemd drop-in files, see systemd.unit(5). 11 | 12 | [Unit] 13 | Description=Automatically Unregister from Red Hat Insights 14 | Documentation=man:insights-client(8) 15 | After=network-online.target 16 | ConditionPathExists=!@sysconfdir@/pki/consumer/cert.pem 17 | 18 | [Service] 19 | Type=simple 20 | ExecStart=@bindir@/insights-client --unregister --force 21 | ExecStopPost=systemctl unmask --now insights-register.path 22 | ExecStopPost=systemctl start insights-register.path 23 | Restart=no 24 | -------------------------------------------------------------------------------- /data/systemd/meson.build: -------------------------------------------------------------------------------- 1 | unit_sources = [ 2 | 'insights-client-boot.service', 3 | 'insights-client.service', 4 | 'insights-client.timer', 5 | ] 6 | 7 | preset_sources = [ 8 | '80-insights.preset' 9 | ] 10 | 11 | unit_inputs = [ 12 | 'insights-client-results.service.in', 13 | 'insights-client-results.path.in' 14 | ] 15 | 16 | if get_option('auto_registration').enabled() 17 | unit_inputs += [ 18 | 'insights-register.service.in', 19 | 'insights-register.path.in', 20 | 'insights-unregister.service.in', 21 | 'insights-unregister.path.in', 22 | ] 23 | preset_sources += '80-insights-register.preset' 24 | endif 25 | 26 | if get_option('checkin').enabled() 27 | unit_inputs += [ 28 | 'insights-client-checkin.service.in' 29 | ] 30 | unit_sources += 'insights-client-checkin.timer' 31 | endif 32 | 33 | foreach unit_input : unit_inputs 34 | unit_sources += configure_file( 35 | input: unit_input, 36 | output: '@BASENAME@', 37 | configuration: config_data, 38 | ) 39 | endforeach 40 | 41 | install_data( 42 | unit_sources, 43 | install_dir: systemd.get_pkgconfig_variable('systemdsystemunitdir') 44 | ) 45 | 46 | install_data( 47 | preset_sources, 48 | install_dir: systemd.get_pkgconfig_variable('systemdsystempresetdir') 49 | ) 50 | -------------------------------------------------------------------------------- /data/tmpfiles.d/insights-client.conf: -------------------------------------------------------------------------------- 1 | R /var/tmp/insights-client* - - - 24h 2 | -------------------------------------------------------------------------------- /data/tmpfiles.d/meson.build: -------------------------------------------------------------------------------- 1 | install_data( 2 | ['insights-client.conf'], 3 | install_dir: systemd.get_pkgconfig_variable('tmpfilesdir') 4 | ) 5 | -------------------------------------------------------------------------------- /docs/file-content-redaction.yaml.example: -------------------------------------------------------------------------------- 1 | --- 2 | # Omit lines from files and command output in the collection using parameters listed here. 3 | 4 | # Using YAML syntax, create lists of "patterns" and "keywords" to omit. 5 | 6 | # About the "patterns" section 7 | # ---------------------------- 8 | # Lines matching the parameters specified will be omitted 9 | # in the order that the parameters are given, e.g., 10 | 11 | # patterns: 12 | # - "example_string_1" 13 | # - "example_string_2" 14 | 15 | # Lines containing "example_string_1" or "example_string_2" will be 16 | # omitted from output. 17 | 18 | # Regular expression matching using egrep are available to use. 19 | # Wrap the list with "regex" like in the following example: 20 | 21 | # patterns: 22 | # regex: 23 | # - "abc.*" 24 | # - "localhost[[:digit:]]" 25 | 26 | # Lines matching these regular expressions will be omitted 27 | # from output. 28 | 29 | # NOTE: You cannot mix plain string matching and regular expression matching. 30 | 31 | # About the "keywords" section 32 | # ---------------------------- 33 | # Replace the specified keywords with generic identifiers by the soscleaner module. 34 | # NOTE: In order to use keyword replacement, the "obfuscate" option must be enabled in 35 | # the insights-client.conf file. 36 | 37 | # Refer to https://access.redhat.com/articles/4511681 for up-to-date configuration information. 38 | 39 | # An example configuration is provided below. 40 | 41 | patterns: 42 | regex: 43 | - "abc.*" 44 | 45 | keywords: 46 | - "1.1.1.1" 47 | - "keyword_example" 48 | 49 | # More examples 50 | # -------- 51 | # patterns: 52 | # regex: 53 | # # never send up lines with my host name suffix (example.com) 54 | # - "[a-zA-Z_.-]*example\.com" 55 | # 56 | # # never send up uncompressed IPv6 addresses 57 | # - "([0-9a-fA-F]{0,4}:){7}[0-9a-fA-F]{0,4}" 58 | -------------------------------------------------------------------------------- /docs/file-redaction.yaml.example: -------------------------------------------------------------------------------- 1 | --- 2 | # Omit entire files and commands from the collection using parameters listed here. 3 | 4 | # Using YAML syntax, create lists of "components", "commands", and "files" to omit. 5 | 6 | # For a full list of files and commands run by insights-client, 7 | # refer to /etc/insights-client/.fallback.json 8 | 9 | # This file is deprecated and provided for compatibility only. 10 | # It will be removed in a future version. 11 | 12 | # Commands and files are provided here presently for compatibility only. 13 | # The use of components is preferred over commands and files, and any commands 14 | # or files that match components will be converted to components during parsing. 15 | # These commands and files must match entries in the .fallback.json file. 16 | 17 | # For a full list of Insights Core components, refer to the following: 18 | # https://insights-core.readthedocs.io/en/latest/specs_catalog.html 19 | 20 | # Components must be prefixed with "insights.specs.default.DefaultSpecs." 21 | 22 | # An example configuration is provided below. 23 | 24 | components: 25 | - insights.specs.default.DefaultSpecs.httpd_V 26 | - insights.specs.default.DefaultSpecs.mysql_log 27 | - insights.specs.default.DefaultSpecs.ifconfig 28 | commands: 29 | - /bin/rpm -qa 30 | - /bin/ls 31 | - ethtool_i 32 | files: 33 | - /etc/audit/auditd.conf 34 | - cluster_conf 35 | -------------------------------------------------------------------------------- /docs/insights-client.8: -------------------------------------------------------------------------------- 1 | .\" insights-client - Red Hat Insights 2 | .TH "insights-client" "8" "" "Red Hat Insights" "" 3 | .SH "NAME" 4 | insights\-client \- Red Hat Insights Client tool 5 | 6 | .SH "SYNOPSIS" 7 | .B insights-client [options] 8 | .SH "DESCRIPTION" 9 | \fBinsights\-client\fP is designed to help customers proactively resolve issues affecting business operations in Red Hat Enterprise Linux and Red Hat Cloud Infrastructure environments. View alerts or learn more at https://console.redhat.com/insights/. Due to the dynamic nature of this application, some options may become available that are not described in the manual. Please refer to the output of --help for the most up-to-date options, or view https://access.redhat.com/articles/4099591 for more details. 10 | 11 | 12 | .SH "OPTIONS" 13 | .IP "-h, --help" 14 | Show this help message and exit. 15 | .IP "--register" 16 | Register host to the Red Hat Insights service. 17 | .IP "--unregister" 18 | Unregister host. 19 | .IP "--display-name=DISPLAYNAME" 20 | Display name to use for this host. May be used during registration. 21 | .IP "--group=GROUP" 22 | Add a tag name group with the specified value to this host. The tag will be saved to the tags file. 23 | .IP "--retry=RETRIES" 24 | Number of times to retry uploading. 180 seconds between tries. 25 | .IP "--validate" 26 | Validate the contents of the denylist configuration. 27 | .IP "--quiet" 28 | Only display error messages to stdout. 29 | .IP "--silent" 30 | Display no messages to stdout. 31 | .IP "--enable-schedule" 32 | Enable automatic scheduling. 33 | .IP "--disable-schedule" 34 | Disable automatic scheduling. 35 | .IP "--compressor" 36 | Specifies the compression algorithm to use. Choices are gz, bz2, xz, and none. Defaults to gz. 37 | .IP "--offline" 38 | Collect locally only, do not connect to Insights and do not upload. 39 | .IP "--payload=FILE" 40 | Skip collection and upload a specified archive. Requires --content-type. 41 | .IP "--content-type=TYPE" 42 | Content type of the archive specified with --payload. 43 | .IP "--diagnosis" 44 | Retrieve a diagnosis for this host. 45 | .IP "--compliance" 46 | Scan the system with OpenSCAP and upload the report. 47 | .IP "--check-results" 48 | Retrieve analysis results from the Red Hat Insights service. 49 | .IP "--show-results" 50 | Display analysis results from the Red Hat Insights service. 51 | .IP "--output-dir=DIR" 52 | Write the collection to a specified directory. Does not perform an upload. 53 | .IP "--output-file=FILE" 54 | Write the collection to a specified file. Does not perform an upload. 55 | .IP "--checkin" 56 | Perform a lightweight hourly check-in. 57 | .IP "--list-specs" 58 | Show insights-client collection specs. 59 | .IP "--compliance" 60 | Run a Compliance scan and upload. 61 | .IP "--module MODULE, -m MODULE" 62 | Run a module from within the insights.client package. Analogous to "python -m MODULE". Use only as directed. 63 | 64 | .SH "DEBUG OPTIONS" 65 | .IP "--version" 66 | Display version. 67 | .IP "--test-connection" 68 | Test connectivity to Red Hat. 69 | .IP "--verbose" 70 | DEBUG output to stdout 71 | .IP "--no-upload" 72 | Do not upload the archive. 73 | .IP "--keep-archive" 74 | Do not delete archive after upload. 75 | .IP "--support" 76 | Create a support logfile for Red Hat Insights. 77 | .IP "--status" 78 | Check this host's registration status by checking the presence of \fB/etc/insights-client/.registered\fP file. 79 | .IP "--net-debug" 80 | Log network activity to console. 81 | 82 | .SH "MOTD" 83 | A message will be displayed on login about \fBinsights\-client\fP if installed but has not yet been used at least once. 84 | 85 | To change this message, edit the \fB/etc/insights-client/insights-client.motd\fP file. 86 | 87 | To prevent this message to be shown on login, create the \fB/etc/motd.d/insights-client\fP symlink pointing to \fB/dev/null\fP. 88 | 89 | .SH "LOGGING" 90 | \fBinsights\-client\fP utilizes the 'logrotate' tool to rotate log files. To change log rotation options, edit the \fB/etc/logrotate.d/insights-client\fP file. Please refer to the 'logrotate' tool documentation to see all available configuration options. 91 | 92 | .SH "SEE ALSO" 93 | .BR insights-client.conf (5) 94 | 95 | \& 96 | -------------------------------------------------------------------------------- /docs/insights-client.conf.5: -------------------------------------------------------------------------------- 1 | .\" insights-client.conf - Red Hat Insights 2 | .TH "insights-client.conf" "5" "" "Red Hat Insights Configuration" "" 3 | .SH "NAME" 4 | insights\-client.conf \- Red Hat Insights client configuration 5 | 6 | .SH "DESCRIPTION" 7 | The \fBinsights\-client.conf\fP file contains configuration information for \fBinsights\-client\fP, the client for the Red Hat Insights service. This file is in an INI style format, option=value, default or example values are shown below. 8 | 9 | [insights-client]\& 10 | .IP "loglevel=DEBUG" 11 | Change log level, valid options DEBUG, INFO, WARNING, ERROR, CRITICAL. 12 | .IP "auto_config=True" 13 | Automatically attempt to configure connectivity to Red Hat Insights. If an RHSM or Satellite subscription is detected, CERT auth will be automatically selected. 14 | .IP "base_url=cert-api.access.redhat.com:443/r/insights" 15 | Base URL for API Interactions. 16 | .IP "proxy=http://user:pass@192.168.100.50:8080" 17 | URL for the proxy. 18 | .IP "auto_update=True" 19 | Automatically update the dynamic configuration. 20 | .IP "obfuscate=False" 21 | Obfuscate IP addresses. 22 | .IP "obfuscate_hostname=False" 23 | Obfuscate hostname. Requires obfuscate=True. 24 | .IP "display_name=" 25 | Display name for this system. 26 | .IP "ansible_host=" 27 | Ansible hostname for this system. 28 | .IP "cmd_timeout=120" 29 | Timeout for commands run during collection, in seconds. 30 | .IP "http_timeout=120" 31 | Timeout for HTTP calls, in seconds. 32 | .IP "core_collect=True" 33 | Use Insights Core to perform the data collection when True. This option is provided for compatibility only and will be deprecated in a future release. 34 | .IP "redaction_file=/etc/insights-client/file-redaction.yaml" 35 | This file can be used to omit files or commands from the collection. 36 | .br 37 | See /usr/share/doc/insights-client/file-redaction.yaml.example or https://access.redhat.com/articles/4511681 for information on how to use it. 38 | .IP "content_redaction_file=/etc/insights-client/file-content-redaction.yaml" 39 | This file can be used to omit lines or keywords from files and commands in the collection. 40 | .br 41 | See /usr/share/doc/insights-client/file-content-redaction.yaml.example or https://access.redhat.com/articles/4511681 for information on how to use it. 42 | .IP "tags_file=/etc/insights-client/tags.yaml" 43 | Location of the tags file for this system. 44 | .SH "SEE ALSO" 45 | .BR insights-client (8) 46 | \& 47 | -------------------------------------------------------------------------------- /docs/meson.build: -------------------------------------------------------------------------------- 1 | install_man( 2 | ['insights-client.8', 'insights-client.conf.5'] 3 | ) 4 | 5 | install_data( 6 | ['file-redaction.yaml.example', 'file-content-redaction.yaml.example'], 7 | install_dir: get_option('datadir') / 'doc' / meson.project_name() 8 | ) 9 | -------------------------------------------------------------------------------- /insights-client.spec: -------------------------------------------------------------------------------- 1 | %define _binaries_in_noarch_packages_terminate_build 0 2 | 3 | # This conditional build macro adds a "--with ros" commandline option to 4 | # rpmbuild. The default behavior is to build without it. 5 | %bcond_with ros 6 | 7 | Name: insights-client 8 | Summary: Uploads Insights information to Red Hat on a periodic basis 9 | Version: {{{ git_dir_version lead=3.2 }}} 10 | Release: 0%{?dist} 11 | Source: {{{ git_dir_pack }}} 12 | License: GPL-2.0-or-later 13 | URL: https://console.redhat.com/insights 14 | Group: Applications/System 15 | Vendor: Red Hat, Inc. 16 | 17 | BuildArch: noarch 18 | 19 | Requires: tar 20 | Requires: gpg 21 | Requires: pciutils 22 | 23 | %{?__python3:Requires: %{__python3}} 24 | %{?systemd_requires} 25 | Requires: python3-requests >= 2.6 26 | Requires: python3-PyYAML 27 | Requires: python3-six 28 | Requires: python3dist(setuptools) 29 | Requires: coreutils 30 | 31 | Requires: subscription-manager 32 | 33 | BuildRequires: wget 34 | BuildRequires: binutils 35 | BuildRequires: python3-devel 36 | BuildRequires: systemd 37 | BuildRequires: pam 38 | BuildRequires: meson 39 | BuildRequires: python3-pytest 40 | BuildRequires: systemd-rpm-macros 41 | 42 | 43 | %description 44 | Sends insightful information to Red Hat for automated analysis 45 | 46 | %if %{with ros} 47 | %package ros 48 | Requires: pcp-zeroconf 49 | Requires: insights-client 50 | 51 | Summary: The subpackage for Insights resource optimization service 52 | 53 | %description ros 54 | 55 | The ros subpackage add ros_collect configuration parameter to insights-client.conf file, 56 | the parameter is set to True by default. The system starts sending PCP archives to 57 | Resource Optimization service upon modifying ros_collect parameter to True. 58 | %endif 59 | 60 | %prep 61 | {{{ git_dir_setup_macro }}} 62 | 63 | 64 | %build 65 | %{meson} \ 66 | -Dpython=%{__python3} \ 67 | %if (0%{?rhel} && 0%{?rhel} < 10) 68 | -Dredhat_access_insights=true \ 69 | %endif 70 | %{nil} 71 | %{meson_build} 72 | 73 | 74 | %install 75 | %{meson_install} 76 | 77 | # Create different insights directories in /var 78 | mkdir -p %{buildroot}%{_localstatedir}/log/insights-client/ 79 | mkdir -p %{buildroot}%{_localstatedir}/lib/insights/ 80 | mkdir -p %{buildroot}%{_localstatedir}/cache/insights/ 81 | mkdir -p %{buildroot}%{_localstatedir}/cache/insights-client/ 82 | 83 | %post 84 | %systemd_post %{name}.timer 85 | %systemd_post %{name}-boot.service 86 | if [ -d %{_sysconfdir}/motd.d ]; then 87 | if [ ! -e %{_sysconfdir}/motd.d/insights-client -a ! -L %{_sysconfdir}/motd.d/insights-client ]; then 88 | if [ -e %{_localstatedir}/lib/insights/newest.egg ]; then 89 | ln -sn /dev/null %{_sysconfdir}/motd.d/insights-client 90 | else 91 | ln -sn %{_sysconfdir}/insights-client/insights-client.motd %{_sysconfdir}/motd.d/insights-client 92 | fi 93 | fi 94 | fi 95 | 96 | %if %{with ros} 97 | %post ros 98 | rm -f /var/lib/pcp/config/pmlogger/config.ros 99 | sed -i "/PCP_LOG_DIR\/pmlogger\/ros/d" /etc/pcp/pmlogger/control.d/local 100 | 101 | if ! grep -q "^ros_collect" %{_sysconfdir}/insights-client/insights-client.conf; then 102 | cat <> %{_sysconfdir}/insights-client/insights-client.conf 103 | ### Begin insights-client-ros ### 104 | ros_collect=True 105 | ### End insights-client-ros ### 106 | EOF 107 | fi 108 | %endif 109 | 110 | %preun 111 | %systemd_preun %{name}.timer 112 | %systemd_preun %{name}.service 113 | %systemd_preun %{name}-boot.service 114 | 115 | %postun 116 | %systemd_postun %{name}.timer 117 | %systemd_postun %{name}.service 118 | %systemd_postun %{name}-boot.service 119 | 120 | # Clean up files created by insights-client that are unowned by the RPM 121 | if [ $1 -eq 0 ]; then 122 | rm -f %{_sysconfdir}/cron.daily/insights-client 123 | rm -f %{_sysconfdir}/ansible/facts.d/insights.fact 124 | rm -f %{_sysconfdir}/ansible/facts.d/insights_machine_id.fact 125 | rm -f %{_sysconfdir}/motd.d/insights-client 126 | rm -rf %{_localstatedir}/lib/insights 127 | rm -rf %{_localstatedir}/log/insights-client 128 | rm -f %{_sysconfdir}/insights-client/.*.etag 129 | fi 130 | 131 | %if %{with ros} 132 | %postun ros 133 | sed -i '/### Begin insights-client-ros ###/,/### End insights-client-ros ###/d;/ros_collect=True/d' %{_sysconfdir}/insights-client/insights-client.conf 134 | %endif 135 | 136 | %files 137 | %config(noreplace) %{_sysconfdir}/insights-client/*.conf 138 | %{_sysconfdir}/insights-client/insights-client.motd 139 | %{_sysconfdir}/insights-client/.exp.sed 140 | %{_sysconfdir}/insights-client/rpm.egg* 141 | %{_bindir}/* 142 | %{_unitdir}/* 143 | %attr(444,root,root) %{_sysconfdir}/insights-client/*.pem 144 | %attr(444,root,root) %{_sysconfdir}/insights-client/redhattools.pub.gpg 145 | %{python3_sitelib}/insights_client/ 146 | %{_defaultdocdir}/%{name} 147 | %{_presetdir}/*.preset 148 | %attr(700,root,root) %dir %{_localstatedir}/log/insights-client/ 149 | %attr(700,root,root) %dir %{_localstatedir}/cache/insights-client/ 150 | %attr(750,root,root) %dir %{_localstatedir}/cache/insights/ 151 | %attr(750,root,root) %dir %{_localstatedir}/lib/insights/ 152 | %{_sysconfdir}/logrotate.d/insights-client 153 | %{_tmpfilesdir}/insights-client.conf 154 | 155 | %doc 156 | %defattr(-, root, root) 157 | %{_mandir}/man8/*.8.gz 158 | %{_mandir}/man5/*.5.gz 159 | 160 | %if %{with ros} 161 | %files ros 162 | %endif 163 | 164 | %changelog 165 | {{{ git_dir_changelog }}} 166 | -------------------------------------------------------------------------------- /integration-tests/README.md: -------------------------------------------------------------------------------- 1 | # Running Betelgeuse 2 | 3 | ### Docs: 4 | https://betelgeuse.readthedocs.io/en/stable/ 5 | https://betelgeuse.readthedocs.io/en/stable/config.html 6 | 7 | ## Test-case command 8 | Command generates an XML file suited to be imported by the **Test Case XML Importer**. It reads the Python test suite source code and generated XML file with all the information necessary. 9 | 10 | The `test-case` requires: 11 | 12 | - The path to the Python test suite source code 13 | - The Polarion project ID 14 | - The output XML file path (will be overwritten if exists) 15 | 16 | 17 | There should also be a custom config file specified for pythonpath for Betelgeuse to correctly read all the custom fields in the docstrings. The file is saved in integration-tests/custom_betelgeuse_config.py 18 | 19 | Example: 20 | 21 | ```console 22 | $ PYTHONPATH=integration-tests/ \ 23 | betelgeuse --config-module \ 24 | custom_betelgeuse_config test-case \ 25 | integration-tests/ PROJECT ./test_case.xml 26 | ``` 27 | 28 | This will create a test_case.xml file in integration-tests/ 29 | 30 | ## Test-run command 31 | Command generates an XML file suited to be imported by the **Test Run XML Importer**. 32 | 33 | It takes: 34 | 35 | - A valid xUnit XML file 36 | - A Python test suite where test case IDs can be found 37 | 38 | And generates a resulting XML file with all the information necessary. 39 | 40 | It requires: 41 | 42 | - The path to the xUnit XML file 43 | - The path to the Python test suite source code 44 | - The Polarion user ID 45 | - The Polarion project ID 46 | - The output XML file path (will be overwritten if exists) 47 | 48 | It is also highly recommended to use `--response-property` as it will then be easier to monitor the importer messages 49 | 50 | Example: 51 | 52 | ```console 53 | $ PYTHONPATH=integration-tests/ \ 54 | betelgeuse test-run \ 55 | --response-property property_key=property_value \ 56 | junit.xml \ 57 | insights-client/integration-tests \ 58 | testuser \ 59 | betelgeuse-test-run.xml 60 | ``` 61 | 62 | NOTE: 63 | 64 | `--dry-run` can be used with `test-run` command when testing the functionality. -------------------------------------------------------------------------------- /integration-tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RedHatInsights/insights-client/cfe9826192b6369d61b4d20f91321795c8adb93a/integration-tests/__init__.py -------------------------------------------------------------------------------- /integration-tests/conftest.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | import subprocess 3 | import time 4 | import logging 5 | 6 | logger = logging.getLogger(__name__) 7 | 8 | 9 | @pytest.fixture(scope="session") 10 | def install_katello_rpm(test_config): 11 | if "satellite" in test_config.environment: 12 | # install katello rpm before register system against Satellite 13 | satellite_hostname = test_config.get("candlepin", "host") 14 | cmd = [ 15 | "rpm", 16 | "-Uvh", 17 | "http://%s/pub/katello-ca-consumer-latest.noarch.rpm" % satellite_hostname, 18 | ] 19 | subprocess.check_call(cmd) 20 | yield 21 | if "satellite" in test_config.environment: 22 | cmd = "rpm -qa 'katello-ca-consumer*' | xargs rpm -e" 23 | subprocess.check_call(cmd, shell=True) 24 | 25 | 26 | @pytest.fixture(scope="session") 27 | def register_subman( 28 | external_candlepin, install_katello_rpm, subman_session, test_config 29 | ): 30 | if "satellite" in test_config.environment: 31 | subman_session.register( 32 | activationkey=test_config.get("candlepin", "activation_keys"), 33 | org=test_config.get("candlepin", "org"), 34 | ) 35 | else: 36 | subman_session.register( 37 | username=test_config.get("candlepin", "username"), 38 | password=test_config.get("candlepin", "password"), 39 | ) 40 | yield subman_session 41 | 42 | 43 | def loop_until(predicate, poll_sec=5, timeout_sec=120): 44 | """ 45 | An helper function to handle a time periond waiting for an external service 46 | to update its state. 47 | 48 | an example: 49 | 50 | assert loop_until(lambda: insights_client.is_registered) 51 | 52 | The loop function will retry to run predicate every 5secs 53 | until the total time exceeds timeout_sec. 54 | """ 55 | start = time.time() 56 | ok = False 57 | while (not ok) and (time.time() - start < timeout_sec): 58 | time.sleep(poll_sec) 59 | ok = predicate() 60 | return ok 61 | -------------------------------------------------------------------------------- /integration-tests/constants.py: -------------------------------------------------------------------------------- 1 | import pathlib 2 | 3 | HOST_DETAILS: str = "/var/lib/insights/host-details.json" 4 | REGISTERED_FILE: str = "/etc/insights-client/.registered" 5 | UNREGISTERED_FILE: str = "/etc/insights-client/.unregistered" 6 | MACHINE_ID_FILE: str = "/etc/insights-client/machine-id" 7 | TAGS_FILE = pathlib.Path("/etc/insights-client/tags.yaml") 8 | -------------------------------------------------------------------------------- /integration-tests/custom_betelgeuse_config.py: -------------------------------------------------------------------------------- 1 | from betelgeuse import default_config 2 | 3 | TESTCASE_CUSTOM_FIELDS = default_config.TESTCASE_CUSTOM_FIELDS + ( 4 | "casecomponent", 5 | "subsystemteam", 6 | "reference", 7 | "polarionincludeskipped", 8 | "polarionlookupmethod", 9 | "polarionprojectid", 10 | ) 11 | 12 | DEFAULT_CASECOMPONENT_VALUE = "" 13 | DEFAULT_SUBSYSTEMTEAM_VALUE = "" 14 | DEFAULT_REFERENCE_VALUE = "" 15 | POLARION_INCLUDE_SKIPED = "" 16 | POLARION_LOOKUP_METHOD = "" 17 | POLARION_PROJECT_ID = "" 18 | -------------------------------------------------------------------------------- /integration-tests/playbook_verifier/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RedHatInsights/insights-client/cfe9826192b6369d61b4d20f91321795c8adb93a/integration-tests/playbook_verifier/__init__.py -------------------------------------------------------------------------------- /integration-tests/playbook_verifier/playbooks/README.md: -------------------------------------------------------------------------------- 1 | # Test data for the Ansible playbook verifier 2 | 3 | To verify the functionality of the verifier, these playbooks have been downloaded from git repository of config-manager: 4 | - [insights_setup.yml](https://github.com/RedHatInsights/config-manager/blob/master/playbooks/insights_setup.yml) 5 | - [compliance_openscap_setup.yml](https://github.com/RedHatInsights/config-manager/blob/master/playbooks/compliance_openscap_setup.yml) 6 | 7 | Additionally, this playbook has been downloaded from git repository of insights-ansible-playbook-verifier: 8 | - [bugs.yml](https://github.com/RedHatInsights/insights-ansible-playbook-verifier/blob/main/data/playbooks/bugs.yml) 9 | 10 | The list of playbooks should be extended to improve coverage: 11 | - lines ending with Windows-style line endings (`\r\n`), 12 | - comments with weird indentation, 13 | - playbook with comments stripped before verifying, 14 | - playbooks containing non-ASCII UTF-8 characters. 15 | -------------------------------------------------------------------------------- /integration-tests/playbook_verifier/playbooks/bugs.yml: -------------------------------------------------------------------------------- 1 | # This testing file is used to identify, track, and fix issues. It allows adding 2 | # new issues as they are discovered and helps work through them step by step. 3 | - name: Set the LoginGraceTime with a regex and file mode to 0600 4 | hosts: localhost 5 | become: true 6 | vars: 7 | insights_signature_exclude: /hosts,/vars/insights_signature 8 | insights_signature: !!binary | 9 | TFMwdExTMUNSVWRKVGlCUVIxQWdVMGxIVGtGVVZWSkZMUzB0TFMwS1ZtVnljMmx2YmpvZ1IyNTFV 10 | RWNnZGpFS0NtbFJTV05DUVVGQ1EwRkJSMEpSU201b2NrOTNRVUZ2U2tWTmRuYzFPRVFyYWpWd1Rs 11 | SkJiMUF2TUhKVFowUmxWVU5DTDFoWFl6QlBkRW8xTTNabWJIb0tjWGhWWlRaNWRHcHZiVzkwZVhK 12 | alpHVjBhMmRyTjFseU9ITTJORVZKTnpsNVUxTndWVk40VTBncmIwWTJVMjkyU0RsSWFUQXJORWxK 13 | WlRWb1dsQjBRZ3BaUW05Q2FIUmpaRXRTWm1jMGVpOHJXRkYxT0ZOU1luVjZaak5FWTFRNWJVUjJk 14 | WGROTnprNGVHbFRjbTFZYTNGNVdUUmxkakZVYVZnM2RuRTNaREZGQ25GSGFtZGlOVTlDTUVJNE16 15 | bG5WM2xaYmtkV1NXNWtNV2xKUjB4UEwzSktaV3hqVHlzeWVFa3pVbGxQWnpRd1lVODRPR0pZZFZw 16 | cE1VeEllbXhJYm5FS2IydEZkWE5YU2l0VVEwbE9abUo2ZVN0QlIzZENWRXhCTWt4WVozSkZibmwz 17 | ZW5Fd2NteFNhMmhwTjI1c1luTTBkU3RoWlRGclNISnhPVTVLWkZGRFZncG1jRWxSU2k5eFdUSkxT 18 | MkYyWTJkUVEyWlNLMEZuZDJsalkwUmtlbEpDV2tWalEycHpWVzFDWlZNdlJrdFVSMFpHUlVneWJF 19 | SkRhRFpLWjNweFNsbHBDakZVU1c1cWF5dHlWamxuWkhkNk5uZGpWek5JZVhCdWVWZHFLMHBKTVda 20 | NWFUSnhaa0pKUVRoc1JWZHBiSFU1TTBWTVFXRXhhMHhpY1c5U2RWTmlUbThLWW1wM2RFOWpUSFZG 21 | VlZabllteHhlV2RpUldwRmNGRXJWMkpUZUdGMVptTnZVRzVWWTFGVWMzZFBZMmMwYVhCWlRUSTFN 22 | bWczZVZkSVRuaDBUa2hEYndwVVV6bE1NME5zVVVGcE9YTnRTMHRzWm1sVFNXc3JOMUJQZDJGT1dH 23 | SklObVZRTURobVltcFZWVkV6ZWpOQ1ZXa3lhMDFVZUdWQmVqWm9TbTE0UXk4ekNuQm1MMmRhYkVS 24 | eFprUk1jVXRHU0V4dVJra3lSa28yVGxGeVMwaFVOa2xVY21SelZ6WkxLM0ZIZFRWcmRWTmhlV04y 25 | YzBoeVMybEtPVmROWjNKSFRWSUtZVEpEZUdaQ1NHSllhbWhwZGtFNWVEbFZSMjE0ZGt0b1J5dG1j 26 | VVEyYmpCb1ozcElaM2swVWl0b01ESlRTbE40TkZwTldrNUZOVFZuVkhSdWExUnJNZ3BtT0Vsc1dH 27 | ZFZWbUZvV0VsS01HZ3pVa3QwYndvOVJXODBUd290TFMwdExVVk9SQ0JRUjFBZ1UwbEhUa0ZVVlZK 28 | RkxTMHRMUzBL 29 | tasks: 30 | - ticket: CCT-1065 31 | file: 32 | regexp: '(?i)^\s*LoginGraceTime' 33 | mode: 0600 34 | 35 | - name: Test string with quotes and special characters 36 | hosts: localhost 37 | become: true 38 | vars: 39 | insights_signature_exclude: /hosts,/vars/insights_signature 40 | insights_signature: !!binary | 41 | TFMwdExTMUNSVWRKVGlCUVIxQWdVMGxIVGtGVVZWSkZMUzB0TFMwS1ZtVnljMmx2YmpvZ1IyNTFV 42 | RWNnZGpFS0NtbFJTV05DUVVGQ1EwRkJSMEpSU201b2NsWlJRVUZ2U2tWTmRuYzFPRVFyYWpWd1Rp 43 | OW1iMUZCU25SWFZXRnFVRzlZTTFCQ1lubDJTMGxPSzFoV2EyNEtlQzlsUTJ0VWNHWlVWa2gwTTFa 44 | RVFubFVWVUZVUW01eU1sVTRNRVZxTHpSSUwxazJUemxXU25OVFZESmFTVUpxV1ZRNFdpOTVkSHBh 45 | TjBoSFMyWk9NZ3BzUzJ0TVJsSnpPRlIyVVdoaVFsRXdNbGt3TTNGa1lWWndaVU52ZGk5VGIwVXlh 46 | bnBoT0ZVMVkyZENNREZvTjJsYU4zWjNkVFZOUW5KcE9WTkhWRGQxQ25Od1VUZEpOQzlZUkRKRFQx 47 | Qk9WbVJ4WkNzNU5XUmlNSGxMV21kb2QySnphbnBLZURNM1JWbEhRa05XZUZOTmJuUnVTRzF6V0c1 48 | RVdsbHFlV0pIYmt3S1EwdFBkRkJCTDFoQ1NXOTVOMjF1VTI5aFVUUXdVVXg0WjJoU1RIaHRUMmxR 49 | WWxoRU1YUktRVGRGUzBaclZpODJPSGxIYW5FMWFHWnhNVVJEVDFjd013cFlOelJFWW14c1MwNU9T 50 | bEk1YUU4NGJEUk9TRVZDU210M2MwaHFUMHhhUjBwdVRYUjJSMmxRUmtFNE5HbExhV3g2T0RGbWVX 51 | cGFTbWd6UzJOTFRYVjRDbU41T0hZME9UWmxOaXRqV0ZWeGRGWkVPVWRPU2pacFFWWjBiSE4wTUc1 52 | VlpFSk1UM0pyVVhJMGVscDNNRnA1ZDJsblMyOWpSR2wyT1RocVptNTFWREVLYkhaNllYVXlZMnRO 53 | VTNSeFpYaEZaa1ZPU2psMWMyNXRhWGcwUVU1NVUxQlNlWGxvV0ZwdGVrWnZVMDV1YkhGbU5sRlhZ 54 | VVZ1YTIxaFJtNUtOVk5XS3dwTk1rSmlkSEV6ZHl0TlYyTmtaVWRvYVZkcGMyWXlNbXRwUkZKV01r 55 | ZFNSSEZ0UjBOTk1qUjBVRVZvVEhwNVNVeHhjbEpsZDJ4MVRVUm1Nbk0yUjA5TkNtMVNNRGxPT1Vs 56 | TFNYUm5ZMUpqUjBKaVdGTTJkMUp0TUdKa1UyaFNUMDV4TnpSUmMzZHJlRzAzYlcxd1JUaDZLMjVG 57 | UTNkWk1XNDBSbXRZYWxFM1lsVUtiVEE0VW1NMU1HczFRMWhCUTNkcVdqTlRXRzR3VWxaT1Qyc3JL 58 | M1pTU0VodlJFNTVkMUJpYUZkQlQzSndiak5CV1dweWFGWlpZbE4wWVU4clJDOTVjQXBvYUZsYWFV 59 | aHpOREZNWTBGSVNHZzBWRVowVVFvOVVqY3ZOQW90TFMwdExVVk9SQ0JRUjFBZ1UwbEhUa0ZVVlZK 60 | RkxTMHRMUzBL 61 | tasks: 62 | - ticket: CCT-1065 63 | value: > 64 | This_string_contains_special_characters, 65 | single'quote, double"quote, both'"quotes, 66 | backslashes\\need to be\\\\escaped!\\ 67 | Newlines?\nYep, they stay. 68 | Tabs\t too! 69 | ~End~ 70 | 71 | - name: Test a play with an empty map 72 | hosts: localhost 73 | become: true 74 | vars: 75 | insights_signature_exclude: /hosts,/vars/insights_signature 76 | insights_signature: !!binary | 77 | TFMwdExTMUNSVWRKVGlCUVIxQWdVMGxIVGtGVVZWSkZMUzB0TFMwS1ZtVnljMmx2YmpvZ1IyNTFV 78 | RWNnZGpFS0NtbFJTV05DUVVGQ1EwRkJSMEpSU201b2NsWldRVUZ2U2tWTmRuYzFPRVFyYWpWd1Ru 79 | cDJaMUZCU2xwR1p6VlBRVUZRU1haSU1XdG5jblJIWWtkM2VFb0taelJTWmk4NGVpODNhV1JOTHpJ 80 | MFREQTRla2RqVWxCMlVFeDBaRFYxYzI4NU5qaElTa3hEVlhoV2NVMTZaRUZEVUVkV2RqWlBWV0pC 81 | WWxCUFRsUlJjZ3B0WjNKUEwzTjRUblkzVUhKQ1IzWjRMMjAyYUhSVlprd3lVWFJCU1VaeUswOHpO 82 | Vk5CTVhaNGF5OUpNMlJJTlVGMU9XVXZNbFExWjBGWWVFdHZRWG96Q2tsd01GVkxOakZKV25WcGRu 83 | Z3JaV1oxVUhSR2JqQmlkQ3ROZDA5VlJXMVFkUzlsVTJWUVIycFZhSHBRTjJKU1JWaE9LMmR1TlRR 84 | elZHYzFRMEZqZFRRS09WbDBTV1UyVW5kTU5UaEpUR3hHWms5d01FUkJaMHRoYTFSSFNtYzBSWEIy 85 | YVRCVWVVbHViRTVYYTNoTVEyOWtMM0FyUVdOd01Wb3hUVTVhWlhWaEx3cEZZbVpsTUVwUU5qWTJi 86 | VU5SUjBWVFVHbHlUV2xTZW5GNU1sQXplVTh3ZG5ScGVteDNlRkp5UWtVNFIwOVZZMVpxYkhKUGMz 87 | bEdUM1ZqY0ZNM1IwZHRDbnBLUnpoRFRWbGFSM2hSYVc1VVFuUXZSRmRqU1VReGJrZGxhMGxFYkda 88 | VmNuSmxXV0ZzV2xCRlkwNDVlVkF6TTNWNEszbGtZVWg2VDNGSU9ITnNlbEFLZUUxRmRITnJVa3BM 89 | Um5WVFFYRkZiRGdyV0hGV1kyTnBaM2RGVm14VmEyWlFiR3RDUkZJNFdIRlJibHB6ZHpsSk1WcFdV 90 | MHg2T1V0UlFuVkJORTV6VHdwRVVXRlhNVXRTYTFsaWVHb3liRUp2YkZCeFJpOU5NRzVOVEVZeksx 91 | ZHdVRUpUUkRONVQzZzJLeko0VWpGd1JuSXdTVVpGTmxWMVUwbFhWMHMyVVVwQ0NqWTNhWE12TUNz 92 | eFZYcEpjMUY0U2tKbVZXTXdiVkprYzNSS1VFdG5WMHQzWjIxR1QyYzVWRXBFSzFkeVV6ZGtNV2M1 93 | U1RCWE1VSjRPV3hzVFhGbVVYY0tjekF6VW5RMGFsZHdaUzlPWTJ3NVNHZHJkMG95Y1M4MWVXaHpV 94 | bTk1ZFVOR1ZUaDZlbkJWVXpKaFYyUlRkR2h3WnpWMlZHUnRaeXRXYlZSNlpqVlNPUXBLYTJrek5I 95 | WlpTa1puZDIxbGNsZFphVlU0TlFvOWVteDFWd290TFMwdExVVk9SQ0JRUjFBZ1UwbEhUa0ZVVlZK 96 | RkxTMHRMUzBL 97 | tasks: 98 | - ticket: CCT-1101 99 | value: {} 100 | 101 | - name: Test a play with an empty value 102 | hosts: localhost 103 | become: true 104 | vars: 105 | insights_signature_exclude: /hosts,/vars/insights_signature 106 | insights_signature: !!binary | 107 | TFMwdExTMUNSVWRKVGlCUVIxQWdVMGxIVGtGVVZWSkZMUzB0TFMwS1ZtVnljMmx2YmpvZ1IyNTFV 108 | RWNnZGpFS0NtbFJTV05DUVVGQ1EwRkJSMEpSU201b2NsWmhRVUZ2U2tWTmRuYzFPRVFyYWpWd1Rs 109 | RnRhMUZCU1VGSFlqSmtSWE5NYkVKdlNrRm5Oa3hHUjB0VGRWWUtXRmhrSzBGcFJrYzBiSFJOV1cx 110 | RU5tc3dTRmxTUjJkUk9WRXpiREp2VUhoelRqRTFjVzl0TkV4SmJsazRZVkZxYjNGR1N6Vm9UV2N2 111 | WXpGcWQwdDFTZ3BsUkdFNGNYcFBURFZ4VjNFeVlXcGFlak0wU1d4aFRrMUVZM2xLTm00MVltbFhU 112 | bHBqZWs0eWEwcHNUaTlJWlV4cGJtSjFiSEJSWnpBMVpYSkxjWFJNQ2xWcFRGbDZkR0pGVGxsQ05r 113 | dFFWSGhJUjB0R1kzRkVka05hVjBaUWExRm1lVmhOZDNNeVFXeGpSbGRzYVhGcFdWWm9MelJRV1Vv 114 | NU1IaG1RbVpoWkdjS09EUlFiMFF5WmtkemRtNUdTVGgyTkhsWEwxbFRUV1p4VmpoMmVsUm9hV3RM 115 | VUdWb1kzcFZRazlNUVdKdVVWUmFObkZ4Y1dSU09YTlpNR2xtZHpSc1lRcGpjRWhrWTNnd1VsRkhM 116 | MDR3ZHpGUGVHbHphRkZKYXpSd1FYUk1NRXhZUkVwdk5IRklUWEptVTJsTmIwUjVNMVJxV1N0NlZX 117 | OTZPVUpHV1RWalJHZHJDblJtZVdSRFEyNVFRMnRVYjI1SGNFZDJla3BVTms5d1J6bHJkRTFaTjBj 118 | NFYxZHdOazh4ZGxwMldUUlRaVU5WVFdkNWJtWnlhbEo0VDBGcUsxZ3haV29LUzBsMlVXUXpaa2xM 119 | ZDJWSFNtWjNOamxsVTJWbWNGUkVlblIyUlhKM01IZEhNekUyUm1nMWNGRXdWSEJZYlhsV1ZVVmtN 120 | MlVyYTFGaE1tZzBPVWxvYkFwaWNXdGliV1JHYjFoSVIwSTNXWFE1VlVocVZrWmtabGxqV2k5MlpX 121 | TmFVR2wzV25aa1FYSnpTRTg0V0VweFNqUndUV2xpZEVsaE5GZEVOVnBKVldkMkNtWjFSbHAyZVRK 122 | NlJqWk5XWEJQY3pSRlpraGphbUo2TlRkWlVFVllUMGxpWkZVemQyeHdibEZxUVhVeVZXOXBVbFJP 123 | UnpabVdHMUxVbTlyVjFWMVJIZ0tjME52ZW05amVYbHJZMU5xTTJKNU9YWnhjVTVtUzNsVWRURjVR 124 | WFptYVdscWMzUnFia3hwV1RsM2JGYzRLMmRZWWxwdmNXeEtSRVEzWmsxeldFOHdOZ3BCYnpWV1ZF 125 | a3lNVWhhZUUxSVdtZHJLMUpyU1FvOWJHVk5RZ290TFMwdExVVk9SQ0JRUjFBZ1UwbEhUa0ZVVlZK 126 | RkxTMHRMUzBL 127 | tasks: 128 | - ticket: CCT-1102 129 | value: 130 | -------------------------------------------------------------------------------- /integration-tests/playbook_verifier/playbooks/compliance_openscap_setup.yml: -------------------------------------------------------------------------------- 1 | # This playbook will install the required OpenSCAP packages to get your systems ready to be used 2 | # with Insights Compliance service. 3 | # 4 | # After running this playbook, perform the following steps manually: 5 | # 1. Create a Compliance policy in cloud.redhat.com and associate the required systems. 6 | # 2. Run `insights-client --compliance` on your system to report Compliance to cloud.redhat.com 7 | 8 | - name: Compliance OpenSCAP Setup 9 | hosts: localhost 10 | become: yes 11 | vars: 12 | insights_signature_exclude: /hosts,/vars/insights_signature 13 | insights_signature: !!binary | 14 | TFMwdExTMUNSVWRKVGlCUVIxQWdVMGxIVGtGVVZWSkZMUzB0TFMwS1ZtVnljMmx2YmpvZ1IyNTFV 15 | RWNnZGpFS0NtbFJTVlpCZDFWQldUbE1WREpqZG5jMU9FUXJhalZ3VGtGUmFIaDFRa0ZCWjA1cE9D 16 | OXJTVFZQYW5reWMySjJOR3BXWlVKdEx6WnZiR3RpTlV4SVVrRUtiRzVtY2tOSFp6Sm5NVEZoYUU1 17 | TEx6RmpkVzVPUWsxTmVUazFXV3RKTlU1cWRtNXpOVVpHVTNSTFowMHdWV0Z3YjFoaVZWQktSV1pT 18 | U2xWRUwzUXhaUXBJVURaUlEwTldTa1l5VFVWWldIcHZVVzFsTUVSc01FeERaMUlyU1hvd2NWaFJa 19 | R2t2WVRSWWNEVnBWRkZTVW5OT1l5OXBWa2cxZFZZemVscHNNbWN4Q2xsRE1sQlFOVzkyZDBwMGFU 20 | RlFjemRyYmtabmJIcERiVWxPVUdGdllYVlRTamhoUkU5a00wSkxZbXRTVW10cmVXNDJORlJzVWtn 21 | eE9HSXdSbGh0Y1VrS2VHaE5hMVZRVEhOd1UwVkxZWGhqTDA5UVNUUXJPVEkwSzI0NVRHRnlUWFZO 22 | THpncmVURlNTa1pTT1hNMk5GaFVaMmh6VEdkQ2JtNXlPRE41VTFKWmJ3cHVWelpDY0c0eVowdzNR 23 | MHBCVHpabmJsZFhZbUphZURVNVdYRTRkV2RpYldWTFJFUXJSalZqVmpSaFpFaENXV3BZUmpkbFRY 24 | bEpUbTlvUzA5WVNtUkRDbkpzU1VSUlJUUktSM2RGU1dJMmRHSnNVWEJMYVhZeFFWZzNNbFF4WjFS 25 | WFNVUXdPVEZEVEU1Rk0wZERVa2M0VnpWeVl6SkRTVkJRYW5KNlNUWldTVWdLTWpVclltbEVPVTFp 26 | YW5sSFVXNVZRMkV3SzBGVlZsVlVibmxYVUhWRFEyeHhlbWhHYXpsVGRYQjRlRXh2VDBkTkwxTnlj 27 | elp2UlZFMFptUk1RV3BKYVFwd1ptUkdRVkI0VEdJMmNGVldSRkJGTVZFeU5GWXZXamd6WkZwTll5 28 | dHhORVJzZWtKV09EaHFaMDlwVWxOc1lYSktTQzl2YUZkSGNVSjFZM1oxYjBZMkNqQTRNamxLYVhW 29 | ak5rOHlUa1ZOYTNjNU1YUjVaSEJPZG1kbU5HNVhhbmRPYmt0Q1ZsTkViR3gyTW01QlQwcHdaRVZx 30 | WVhoRFl5c3hVVkJTU1cxWk5WTUtPRTFQUjBjMlRuRmxWMDV1VGtwaVYzRTBXRVp3Y2xOSlJFZHJV 31 | MFpFVmxkQllURlNka3RyYVV4bVMxQTFlQzh4V2pkT2RITTVWbGhVU0VwQ09YbHVad3BoYUdJeFFs 32 | aHhaM3B2VFQwS1BVTkphbWtLTFMwdExTMUZUa1FnVUVkUUlGTkpSMDVCVkZWU1JTMHRMUzB0Q2c9 33 | PQ== 34 | tasks: 35 | - name: Install OpenSCAP packages for Compliance service 36 | ansible.builtin.package: 37 | name: 38 | - openscap 39 | - openscap-scanner 40 | - scap-security-guide 41 | state: latest 42 | when: 43 | - ansible_facts.ansible_cmdline.ostree is undefined -------------------------------------------------------------------------------- /integration-tests/playbook_verifier/playbooks/insights_setup.yml: -------------------------------------------------------------------------------- 1 | # Steps required to get your system ready to use the Insights Client: 2 | # 1. Yum install the insights-client 3 | # 2. Register the insights-client 4 | # 3. Modify file permissions 5 | - name: Insights Setup 6 | hosts: localhost 7 | become: yes 8 | vars: 9 | insights_signature_exclude: /hosts,/vars/insights_signature 10 | insights_signature: !!binary | 11 | TFMwdExTMUNSVWRKVGlCUVIxQWdVMGxIVGtGVVZWSkZMUzB0TFMwS1ZtVnljMmx2YmpvZ1IyNTFV 12 | RWNnZGpFS0NtbFJTVlpCZDFWQldUbG1jbHBqZG5jMU9FUXJhalZ3VGtGUmFrWjFaeTh2WTJJMWMw 13 | MVJkWEpPZG5oM05rMTRSVXRtUW1OU1MxbzBNVzVPUzBkRlFYQUtaVTh2VkZka00ySlVZVVZQYjBK 14 | bUx6RXpiVlUyUVRGWlJYQlJaM3BFTWtwMWFqVlNWWEJVT0RSdWJHazNMMWRMVDFwMmJURjRWR28w 15 | YTNWRWVXSlROQXB6TUZBd1pWRnBXR1V3ZUVwQmFVeDRRVkJ2Y25KRVlsbFNWR0Y2YVRNemJucENS 16 | bkIyVVVoMmEybG9hRFpVYkM5TWNITjZURXREZDNSSFZVWk5kazVxQ21wVFRGbFJXV0o1YlZWVFZ6 17 | RnBXR0ZyTW5SSU9FbEtaVTU2VFRWSmJFTm5TM1UzYVRsdVVUYzFORWswV0hFeFFUbEpOMGRJVG1J 18 | MU1WaFpkRTVyWm5vS1EwdzNRVk0zVHpCcGEwaGhZblJxSzJWdGIyWlhjVkJ4VVdwamRsUlZNa1Ey 19 | YldaMk1YRnNXRWhITjBSaU4zTmxWRk5hUW1oS1JuUkhOVk51Um5oMU1ncHJiMFZrVjJoV1dYZFNN 20 | MDlZWmtoWVRXcEdjVTEzWlROT2NuaHhaR2d6VWpRMlJ6Tk5WVk5zZGtSVlIwdzFUVzFqYWsweFNt 21 | TnZVSHBWWkdnMmRFeFdDbXhWZEhKVlJuVmxkbkJNYmxad1NFczRUemhWU0VOdGVHNTRNSGhQWTFW 22 | Wk5XZHlVMmhOYTAwMVlUVkhRa0ZvVVdOYU9FNXVZME5ETmpGTVNsRnFhM2NLUW1Vck1sVlhlSFZ6 23 | TW1sclFsUjJWeTlDYUVsRlRFTmtORUZSYmxGdFYxRXJkVmhTVUdJd2RHTkNVMVZDVlVOTVNERmpS 24 | VTgyWjFoWGNrUlRUbWxUTWdwd2VHdDNURnBKYlRSbGN6TXdVbWd3V0ZCeFZUVm1iazE0YTFaTlFX 25 | VmhOVlV4V0ZwdFkwUnpNeTlaUVRCNmFFbEJaRVJET0doelMxTkVURkJJUmtKdUNtVmxNRVJXUkdk 26 | SksycFVaUzlSUkhkMk0yMWpiRk0yWVUxMmFYVlJaa05uWjJkV1ZtOXFURTVJU2paNGVtNVZjMFl4 27 | WVRoVVZ6YzNkMlJyYkdkcWNrc0tPVlV5V0VabFJISm9iWFpxZGxWcmFXY3JlbEJLYTBwYWNXUmtk 28 | MHBVZEV4UWMwbGlOMFpFYjB0UFIwSXJiR3BSY1RkMFlURTBUR2R2YURZck1IaGxVd292VFZsb056 29 | WlRUWEp4UlQwS1BXZ3JOMEVLTFMwdExTMUZUa1FnVUVkUUlGTkpSMDVCVkZWU1JTMHRMUzB0Q2c9 30 | PQ== 31 | tasks: 32 | - name: Install latest insights-client, rhc, rhc-worker-playbook 33 | ansible.builtin.package: 34 | name: 35 | - insights-client 36 | - rhc 37 | - rhc-worker-playbook 38 | state: latest 39 | when: 40 | - ansible_facts.ansible_cmdline.ostree is undefined 41 | 42 | # With legacy_upload=True: Insights API says this machine is NOT registered. 43 | # With legacy_upload=False: This host is unregistered. 44 | - name: Get insights-client's status 45 | command: insights-client --status 46 | changed_when: false 47 | failed_when: false 48 | register: result 49 | - name: Register insights-client 50 | command: insights-client --register 51 | when: '"NOT registered" in result.stdout or "unregistered" in result.stdout' 52 | 53 | # insights_remove.yml stops and disables insights-client.timer, but leaves host registered. 54 | - name: Schedule insights-client runs 55 | command: insights-client --enable-schedule -------------------------------------------------------------------------------- /integration-tests/playbook_verifier/test_verifier.py: -------------------------------------------------------------------------------- 1 | """ 2 | :casecomponent: insights-client 3 | :requirement: RHSS-291297 4 | :polarion-project-id: RHELSS 5 | :polarion-include-skipped: false 6 | :polarion-lookup-method: id 7 | :subsystemteam: rhel-sst-csi-client-tools 8 | :caseautomation: Automated 9 | :upstream: Yes 10 | """ 11 | 12 | import pathlib 13 | import subprocess 14 | import sys 15 | import pytest 16 | from pytest_client_tools.util import Version 17 | 18 | 19 | PLAYBOOK_DIRECTORY = pathlib.Path(__file__).parent.absolute() / "playbooks" 20 | 21 | 22 | @pytest.mark.parametrize( 23 | "filename", 24 | [ 25 | "insights_setup.yml", 26 | "compliance_openscap_setup.yml", 27 | "bugs.yml", 28 | ], 29 | ) 30 | @pytest.mark.tier1 31 | def test_official_playbook(insights_client, filename: str): 32 | """ 33 | :id: 3659e27f-3621-4591-b1c4-b5f0a277bb72 34 | :title: Test playbook verifier 35 | :parametrized: yes 36 | :description: 37 | This test verifies the official playbooks against the GPG key 38 | the application ships. 39 | :tags: Tier 1 40 | :steps: 41 | 1. Read playbook file content 42 | 2. Run insights-client verifier with playbook 43 | 3. Compare output to input 44 | :expectedresults: 45 | 1. File content is correctly read and loaded into memory 46 | 2. Subprocess executes successfully without errors 47 | 3. Verifier's output matches original playbook content 48 | """ 49 | if ( 50 | sys.version_info >= (3, 12) 51 | and insights_client.core_version < Version(3, 5, 2) 52 | and filename == "bugs.yml" 53 | ): 54 | pytest.xfail( 55 | f"Core {insights_client.core_version} suffers from " 56 | "CCT-1065, CCT-1101, CCT-1102." 57 | ) 58 | 59 | playbook_content: str = (PLAYBOOK_DIRECTORY / filename).read_text() 60 | 61 | result = subprocess.run( 62 | [ 63 | "insights-client", 64 | "-m", 65 | "insights.client.apps.ansible.playbook_verifier", 66 | "--quiet", 67 | "--payload", 68 | "noop", 69 | "--content-type", 70 | "noop", 71 | ], 72 | input=playbook_content, 73 | stdout=subprocess.PIPE, 74 | stderr=subprocess.PIPE, 75 | universal_newlines=True, 76 | check=True, 77 | ) 78 | 79 | # The playbooks may and may not include newline as EOF. 80 | assert result.stdout.strip() == playbook_content.strip() 81 | -------------------------------------------------------------------------------- /integration-tests/requirements.txt: -------------------------------------------------------------------------------- 1 | git+https://github.com/RedHatInsights/pytest-client-tools@main 2 | pyyaml 3 | pytest-subtests 4 | distro 5 | -------------------------------------------------------------------------------- /integration-tests/test_checkin.py: -------------------------------------------------------------------------------- 1 | """ 2 | :casecomponent: insights-client 3 | :requirement: RHSS-291297 4 | :polarion-project-id: RHELSS 5 | :polarion-include-skipped: false 6 | :polarion-lookup-method: id 7 | :subsystemteam: rhel-sst-csi-client-tools 8 | :caseautomation: Automated 9 | :upstream: Yes 10 | """ 11 | 12 | import contextlib 13 | import json 14 | import pytest 15 | from pytest_client_tools.util import Version 16 | 17 | from constants import HOST_DETAILS 18 | import conftest 19 | 20 | pytestmark = pytest.mark.usefixtures("register_subman") 21 | 22 | 23 | @pytest.mark.tier2 24 | def test_ultralight_checkin(insights_client, test_config): 25 | """ 26 | :id: c662fd5e-0751-45e4-8477-6b0d27f735ac 27 | :title: Test lightweight check-in updates staleness timestamps 28 | :description: 29 | This test verifies that performing an ultra-light check-in with the 30 | insights-client updates the host's 'stale_timestamps' and 'updated' 31 | fields on the server 32 | :tags: Tier 2 33 | :steps: 34 | 1. Register the insights-client 35 | 2. Run '--check-results' and record the 'stale_timestamp' and 'updated' 36 | timestamps before check-in 37 | 3. Perform an ultra-light check-in ny running '--checkin' 38 | 4. Run '--check-results' and record the 'stale_timestamp' and 'updated' 39 | timestamps again 40 | 5. Verify that timestamps were updated successfully 41 | :expectedresults: 42 | 1. Insights-client is registered 43 | 2. The initial timestamps were retrieved and recorded 44 | 3. The check-in completes without any errors 45 | 4. The updated timestamps were retrieved and recorded 46 | 5. Both updated timestamps will be greater than before check-in 47 | """ 48 | insights_client.register() 49 | assert conftest.loop_until(lambda: insights_client.is_registered) 50 | 51 | # Performing check-results operation provides latest host data in host-details.json 52 | insights_client.run("--check-results") 53 | with open(HOST_DETAILS, "r") as data_file: 54 | data = json.load(data_file) 55 | stale_ts_before_checkin = data["results"][0]["stale_timestamp"] 56 | updated_ts_before_checkin = data["results"][0]["updated"] 57 | 58 | # Performing an ultra light check-in 59 | insights_client.run("--checkin") 60 | insights_client.run("--check-results") 61 | 62 | with open(HOST_DETAILS, "r") as data_file: 63 | data = json.load(data_file) 64 | stale_ts_after_checkin = data["results"][0]["stale_timestamp"] 65 | updated_ts_after_checkin = data["results"][0]["updated"] 66 | 67 | assert stale_ts_after_checkin > stale_ts_before_checkin 68 | assert updated_ts_after_checkin > updated_ts_before_checkin 69 | 70 | 71 | @pytest.mark.tier1 72 | def test_client_checkin_unregistered(insights_client): 73 | """ 74 | :id: 91331995-20c2-4d44-8abe-74a3e7d28309 75 | :title: Test check-in fails for unregistered client 76 | :description: 77 | This test verifies that attempting to perform check-in while unregistered 78 | fails with appropriate error message 79 | :tags: Tier 1 80 | :steps: 81 | 1. Unregister the insights-client if registered 82 | 2. Attempt to perform a check-in by running '--checkin' 83 | :expectedresults: 84 | 1. Insights-client is unregistered successfully 85 | 2. The check-in fails with return code 1 and message 'Error: failed 86 | to find host with matching machine-id' 87 | """ 88 | with contextlib.suppress(Exception): 89 | insights_client.unregister() 90 | assert conftest.loop_until(lambda: not insights_client.is_registered) 91 | 92 | checkin_result = insights_client.run("--checkin", check=False) 93 | if insights_client.core_version >= Version(3, 4, 25): 94 | assert checkin_result.returncode > 0 95 | assert "This host is not registered" in checkin_result.stdout 96 | else: 97 | assert checkin_result.returncode == 1 98 | assert ( 99 | "Error: failed to find host with matching machine-id" 100 | in checkin_result.stdout 101 | ) 102 | -------------------------------------------------------------------------------- /integration-tests/test_client.py: -------------------------------------------------------------------------------- 1 | """ 2 | :casecomponent: insights-client 3 | :requirement: RHSS-291297 4 | :polarion-project-id: RHELSS 5 | :polarion-include-skipped: false 6 | :polarion-lookup-method: id 7 | :subsystemteam: rhel-sst-csi-client-tools 8 | :caseautomation: Automated 9 | :upstream: Yes 10 | """ 11 | 12 | import contextlib 13 | import glob 14 | import os 15 | import subprocess 16 | import pytest 17 | import conftest 18 | 19 | 20 | @pytest.mark.usefixtures("register_subman") 21 | @pytest.mark.tier1 22 | def test_client_files_permission(insights_client): 23 | """ 24 | :id: e793cf5e-1c25-4e31-93c9-6f0465f50cae 25 | :title: Verify client files permission 26 | :description: 27 | Verify that the permission for the last upload file 28 | /etc/insights-client/.lastupload is set to 0644 29 | :reference: https://bugzilla.redhat.com/show_bug.cgi?id=1924990 30 | :tags: Tier 1 31 | :steps: 32 | 1. Remove /etc/insights-client/.lastupload if it exists 33 | 2. Register insights-client 34 | 3. Verify the file permissions 35 | :expectedresults: 36 | 1. The file /etc/insights-client/.lastupload does not exist 37 | 2. The insights-client is registered 38 | 3. The permission of /etc/insights-client/.lastupload is set to 0644 39 | """ 40 | file_last_upload = "/etc/insights-client/.lastupload" 41 | with contextlib.suppress(FileNotFoundError): 42 | os.remove(file_last_upload) # performing a cleanup before test 43 | insights_client.register() 44 | assert conftest.loop_until(lambda: insights_client.is_registered) 45 | assert oct(os.stat(file_last_upload).st_mode & 0o777) == "0o644" 46 | 47 | 48 | @pytest.fixture() 49 | def rpm_ql_insights_client(): 50 | cmd = ["rpm", "-ql", "insights-client"] 51 | list_file = subprocess.check_output(cmd, universal_newlines=True) 52 | return list_file.split("\n") 53 | 54 | 55 | @pytest.mark.parametrize( 56 | "filename", 57 | [ 58 | "/etc/insights-client/insights-client.motd", 59 | "/etc/insights-client/insights-client.conf", 60 | "/usr/lib/systemd/system/insights-client-boot.service", 61 | "/usr/lib/systemd/system/insights-client-results.path", 62 | "/usr/lib/systemd/system/insights-client-results.service", 63 | "/usr/lib/systemd/system/insights-client.service", 64 | "/usr/lib/systemd/system/insights-client.timer", 65 | "/usr/share/doc/insights-client/file-content-redaction.yaml.example", 66 | "/usr/share/doc/insights-client/file-redaction.yaml.example", 67 | "/usr/share/man/man5/insights-client.conf.5.gz", 68 | "/usr/share/man/man8/insights-client.8.gz", 69 | "/etc/logrotate.d/insights-client", 70 | ], 71 | ) 72 | @pytest.mark.tier1 73 | def test_client_rpm_mandatory_files(filename, rpm_ql_insights_client): 74 | """ 75 | :id: c7d2edbe-ae78-47e0-9b3d-ae1634c0ac79 76 | :title: Verify mandatory files for RPM 77 | :parametrized: yes 78 | :description: Verify the existence of mandatory files for the insights-client RPM 79 | :tags: Tier 1 80 | :steps: 81 | 1. List all files in the insights-client RPM package 82 | 2. Check if each mandatory file exists in the package 83 | :expectedresults: 84 | 1. A list of files is generated 85 | 2. All of the mandatory files are present in the RPM 86 | """ 87 | assert ( 88 | filename in rpm_ql_insights_client 89 | ), f"{filename} is not in insights-client package" 90 | 91 | 92 | @pytest.mark.usefixtures("register_subman") 93 | @pytest.mark.tier1 94 | def test_client_logfiles_mask(insights_client): 95 | """ 96 | :id: 8f24500c-d0ff-4ab1-a2ae-3b99cbf68e36 97 | :title: Verify client logfiles permissions 98 | :description: 99 | Verify that the log files in 100 | /var/log/insights-client have the correct mode 0600 101 | :reference: https://bugzilla.redhat.com/show_bug.cgi?id=1955724 102 | :tags: Tier 1 103 | :steps: 104 | 1. Register insights-client 105 | 2. Check the file permission of each log file generated 106 | :expectedresults: 107 | 1. Insights-client is registered 108 | 2. The file permissions for all log files are 0600 109 | """ 110 | # It is necessary to perform some command using insights-client 111 | # to populate logs 112 | insights_client.register() 113 | logfiles = glob.glob("/var/log/insights-client/*.log*") 114 | for logfile in logfiles: 115 | assert oct(os.stat(logfile).st_mode & 0o777) == "0o600" 116 | 117 | 118 | @pytest.mark.tier1 119 | def test_client_logdir_permissions(): 120 | """ 121 | :id: 204b1d54-6d8f-4d87-9227-cf3924cc5bb8 122 | :title: Verify log directory permissions 123 | :description: 124 | Verify that the permissions on the directory 125 | /var/log/insights-client are set to 0700 126 | :tags: Tier 1 127 | :steps: Check the directory permissions of /var/log/insights-client 128 | :expectedresults: The directory permissions are set to 0700 129 | """ 130 | logdir_name = "/var/log/insights-client" 131 | assert oct(os.stat(logdir_name).st_mode & 0o777) == "0o700" 132 | 133 | 134 | @pytest.mark.usefixtures("register_subman") 135 | @pytest.mark.tier1 136 | def test_verify_logrotate_feature(insights_client): 137 | """ 138 | :id: 5442729f-c6bc-4322-aa17-facd538e9fc3 139 | :title: Verify Logrotate feature 140 | :description: Verify that the logrotate works properly for insights-client 141 | :reference: https://bugzilla.redhat.com/show_bug.cgi?id=1940267 142 | :tags: Tier 1 143 | :steps: 144 | 1. Ensure the logrotate configuration file exists 145 | 2. Register insights-client 146 | 3. Run the logrotate command 147 | 4. Verify that 2 new log files were created 148 | 5. Verify the size of insights-client.log 149 | 6. Verify the size of insights-client-payload.log 150 | :expectedresults: 151 | 1. The logrotate config file exists 152 | 2. The insights-client is registered 153 | 3. The logrotate command is executed successfully 154 | 4. Two new log files were created 155 | 5. The size of insights-client.log is 0B 156 | 6. The size of insights-client-payload.log is 0B 157 | """ 158 | 159 | logrotate_conf_file_path = "/etc/logrotate.d/insights-client" 160 | logdir = "/var/log/insights-client/" 161 | logfile_insights = f"{logdir}/insights-client.log" 162 | logfile_payload = f"{logdir}/insights-client-payload.log" 163 | 164 | assert os.path.exists(logrotate_conf_file_path), "logrotate is not configured" 165 | """ 166 | It is necessary to perform some command using insights-client 167 | to populate logs. 168 | Save the archive, to be used while register operation using --keep-archive. 169 | for example- 170 | [root@test ~]# insights-client --register --keep-archive 171 | Automatic scheduling for Insights has been enabled. 172 | Starting to collect Insights data for test 173 | Writing RHSM facts to /etc/rhsm/facts/insights-client.facts ... 174 | Uploading Insights data. 175 | Successfully uploaded report for test. 176 | View the Red Hat Insights console at https://console.redhat.com/insights/ 177 | Copying archive from /var/tmp/insights-client-qxl3vdqy/insights-test-date.tar.gz 178 | to /var/cache/insights-client/insights-test-date.tar.gz 179 | Insights archive retained in /var/cache/insights-client/insights-test-date.tar.gz 180 | """ 181 | reg_result = insights_client.run("--register", "--keep-archive") 182 | assert conftest.loop_until(lambda: insights_client.is_registered) 183 | 184 | archive_name = reg_result.stdout.split()[-1] 185 | insights_client.run( 186 | f"--payload={archive_name}", 187 | "--content-type=gz", 188 | ) 189 | number_of_log_files = len(os.listdir(logdir)) # count of files before rotation 190 | 191 | subprocess.check_call(["logrotate", "-vf", logrotate_conf_file_path]) 192 | assert os.path.getsize(logfile_insights) == 0 193 | assert os.path.getsize(logfile_payload) == 0 194 | number_of_files_after_logrotate = len(os.listdir(logdir)) 195 | assert number_of_files_after_logrotate == (number_of_log_files + 2) 196 | 197 | 198 | @pytest.mark.usefixtures("register_subman") 199 | @pytest.mark.tier1 200 | def test_insights_details_file_exists(insights_client): 201 | """ 202 | :id: 2ccc8e00-0e76-47fd-bdb2-27998c0094ab 203 | :title: Verify insights-client details file exists 204 | :description: Verify that the file /var/lib/insights/insights-client.json exists 205 | :tags: Tier 1 206 | :steps: 207 | 1. Register insights-client 208 | 2. Delete /var/lib/insights/insights-client.json if it exists 209 | 3. Run the --check-results command 210 | 4. Verify the existence of /var/lib/insights/insights-client.json 211 | :expectedresults: 212 | 1. Insights-client is registered 213 | 2. The file /var/lib/insights/insights-client.json does not exist 214 | 3. The --check-results command is executed successfully 215 | 4. The file /var/lib/insights/insights-client.json exists 216 | """ 217 | output_file = "/var/lib/insights/insights-details.json" 218 | insights_client.register() 219 | assert conftest.loop_until(lambda: insights_client.is_registered) 220 | 221 | # Deleting file manually 222 | with contextlib.suppress(FileNotFoundError): 223 | os.remove(output_file) 224 | insights_client.run("--check-results") 225 | # Verify that insights-details.json gets re generated 226 | assert os.path.isfile(output_file) 227 | 228 | 229 | @pytest.mark.usefixtures("register_subman") 230 | @pytest.mark.tier1 231 | def test_insights_directory_files(insights_client): 232 | """ 233 | :id: 02072b65-9905-4426-96dc-76af6a73e14f 234 | :title: Verify insights directory files 235 | :description: Verify that the /var/lib/insights directory has the expected content 236 | :tags: Tier 1 237 | :steps: 238 | 1. Register insights-client 239 | 2. Check the content of /var/lib/insights directory 240 | 3. Verify specific files exists 241 | :expectedresults: 242 | 1. Insights-client is registered 243 | 2. The list of contents of /var/lib/insights directory is created 244 | 3. All specified files are present 245 | """ 246 | directory = "/var/lib/insights" 247 | registered_contents = [ 248 | "last_stable.egg", 249 | "last_stable.egg.asc", 250 | ] 251 | 252 | insights_client.register() 253 | assert conftest.loop_until(lambda: insights_client.is_registered) 254 | 255 | dir_content_registered = [ 256 | entry.name for entry in os.scandir(directory) if entry.is_file() 257 | ] 258 | 259 | for item in registered_contents: 260 | assert item in dir_content_registered, f"File '{item}' not found in directory." 261 | -------------------------------------------------------------------------------- /integration-tests/test_client_systemd.py: -------------------------------------------------------------------------------- 1 | """ 2 | :casecomponent: insights-client 3 | :requirement: RHSS-291297 4 | :polarion-project-id: RHELSS 5 | :polarion-include-skipped: false 6 | :polarion-lookup-method: id 7 | :subsystemteam: rhel-sst-csi-client-tools 8 | :caseautomation: Automated 9 | :upstream: Yes 10 | """ 11 | 12 | import conftest 13 | from time import sleep 14 | import pytest 15 | import subprocess 16 | from pathlib import Path 17 | 18 | pytestmark = pytest.mark.usefixtures("register_subman") 19 | 20 | 21 | @pytest.mark.tier1 22 | def test_data_upload_systemd_timer(insights_client, external_inventory): 23 | """ 24 | :id: 6bbac679-cb37-47b1-9163-497f1a1758dd 25 | :title: Verify insights-client upload via systemd timer 26 | :description: 27 | Ensure that the insights-client data upload is triggered by the 28 | systemd timer and the last check-in time is updated accordingly 29 | :tags: Tier 1 30 | :steps: 31 | 1. Register the system 32 | 2. Note the last_check_in in host details from inventory 33 | 3. Edit insights-client timer to run every 3 minutes (24 hours is 34 | too long to wait for a test, 3 min appears be a decent wait) 35 | 4. Wait for upload to finish 36 | 5. Again note the last_check_in in host details from inventory 37 | 6. Verify the updated last_check_in time 38 | :expectedresults: 39 | 1. System is registered 40 | 2. The last_check_in time is recorded from the inventory 41 | 3. The timer is adjusted to a 3-minute interval and upload proceeds 42 | as expected. 43 | 4. Upload is finished successfully 44 | 5. The last_check_in time is recorded from the inventory 45 | 6. The last_check_in time is updated, reflecting a successful upload 46 | """ 47 | insights_client.register() 48 | assert conftest.loop_until(lambda: insights_client.is_registered) 49 | 50 | machine_id = insights_client.uuid 51 | # fetching host details to note last_check_in time 52 | response = external_inventory.get(path=f"hosts?insights_id={machine_id}") 53 | host_staleness = response.json()["results"][0]["per_reporter_staleness"] 54 | last_checkin_time = host_staleness["puptoo"]["last_check_in"] 55 | 56 | # override insights-client.timer to run every 3 min 57 | override_path = Path("/etc/systemd/system/insights-client.timer.d/override.conf") 58 | override_content = """ 59 | [Timer] 60 | OnCalendar=*:0/3 61 | Persistent=true 62 | RandomizedDelaySec=5 63 | """ 64 | try: 65 | override_path.parent.mkdir() 66 | override_path.write_text(override_content) 67 | 68 | subprocess.run(["systemctl", "daemon-reload"]) 69 | 70 | sleep(60 * 5) # sleep for the period when timer is triggered and upload happens 71 | 72 | # remove the override.conf for insights-client.timer to avoid multiple uploads 73 | subprocess.run(["systemctl", "revert", "insights-client.timer"]) 74 | 75 | # fetch the last_check_in time 76 | response = external_inventory.this_system() 77 | latest_checkin_time = response["per_reporter_staleness"]["puptoo"][ 78 | "last_check_in" 79 | ] 80 | assert last_checkin_time < latest_checkin_time 81 | 82 | finally: 83 | subprocess.run(["systemctl", "revert", "insights-client.timer"]) 84 | -------------------------------------------------------------------------------- /integration-tests/test_common_specs.py: -------------------------------------------------------------------------------- 1 | """ 2 | :casecomponent: insights-client 3 | :requirement: RHSS-291297 4 | :polarion-project-id: RHELSS 5 | :polarion-include-skipped: false 6 | :polarion-lookup-method: id 7 | :subsystemteam: rhel-sst-csi-client-tools 8 | :caseautomation: Automated 9 | :upstream: Yes 10 | """ 11 | 12 | import json 13 | import os 14 | import pytest 15 | import subprocess 16 | import logging 17 | 18 | pytestmark = pytest.mark.usefixtures("register_subman") 19 | 20 | 21 | @pytest.mark.tier1 22 | def test_common_specs(insights_client, tmp_path): 23 | """ 24 | :id: 9010f731-ca05-4abb-b119-de9730b055c1 25 | :title: Test common specs 26 | :description: 27 | This test verifies that the specified specs can be collected, 28 | parsed and contain valid data without errors 29 | :tags: Tier 1 30 | :steps: 31 | 1. Define the list of common specs to be tested 32 | 2. Run the insights-client to collect data and save it in the 33 | specified temporary directory 34 | 3. For each spec in the list check that it exists in the 35 | correct location 36 | 4. Verify the spec file contains no errors 37 | 5. Verify the spec file has valid results 38 | :expectedresults: 39 | 1. The list of specs is correctly defined 40 | 2. The client runs and the data is saved in the tmp_path directory 41 | 3. Each spec file is found in the meta_data directory and none is missing 42 | 4. Data contains no errors (data["errors"] is empty or does not exist) 43 | 5. Each spec contains valid results (data["results"] is not None) confirming 44 | the data was successfully collected 45 | """ 46 | common_specs = [ 47 | "insights.specs.Specs.date.json", 48 | "insights.specs.Specs.hosts.json", 49 | "insights.specs.Specs.installed_rpms.json", 50 | "insights.specs.Specs.ls_dev.json", 51 | "insights.specs.Specs.lscpu.json", 52 | "insights.specs.Specs.lspci.json", 53 | "insights.specs.Specs.meminfo.json", 54 | "insights.specs.Specs.mount.json", 55 | "insights.specs.Specs.mountinfo.json", 56 | "insights.specs.Specs.ps_auxcww.json", 57 | "insights.specs.Specs.redhat_release.json", 58 | "insights.specs.Specs.uname.json", 59 | "insights.specs.Specs.yum_repos_d.json", 60 | ] 61 | # The following specs can't be collected in containers 62 | privileged_specs = [ 63 | "insights.specs.Specs.dmidecode.json", 64 | "insights.specs.Specs.fstab.json", 65 | "insights.specs.Specs.hostname.json", 66 | "insights.specs.Specs.ip_addresses.json", 67 | ] 68 | 69 | # Running insights-client to collect data in tmp path 70 | insights_client.run(f"--output-dir={tmp_path}") 71 | 72 | in_container: bool = "container" in os.environ.keys() 73 | for spec in common_specs + privileged_specs: 74 | spec_filepath = tmp_path / "meta_data" / spec 75 | 76 | # assert that spec file exists 77 | assert os.path.exists(spec_filepath), f"spec file {spec} not found " 78 | 79 | with open(spec_filepath, "r") as specfile: 80 | data = json.load(specfile) 81 | 82 | # assert that a spec doesn't contain errors 83 | # (unless we are in container and the spec is known to not work in containers) 84 | if in_container and spec in privileged_specs: 85 | continue 86 | 87 | try: 88 | assert not data["errors"], f"'{spec}' contains errors: {data['errors']} " 89 | assert data["results"] is not None, f"'{spec}' does not contain results" 90 | except AssertionError as e: 91 | logging.error(f"Spec '{spec}' failed with an error '{str(e)}'") 92 | 93 | # Try to extract and re-run the command from the spec JSON 94 | cmd = data["results"]["object"]["cmd"] 95 | if cmd: 96 | logging.debug(f"Attempting to re-run the cmd from the spec: '{cmd}'") 97 | result = subprocess.run( 98 | cmd, shell=True, capture_output=True, text=True, timeout=10 99 | ) 100 | 101 | logging.debug(f"Command return code: {result.returncode}") 102 | logging.debug(f"Command STDOUT code: {result.stdout.strip()}") 103 | logging.debug(f"Command STDERR code: {result.stderr.strip()}") 104 | 105 | if result.returncode != 0: 106 | raise AssertionError( 107 | f"Command '{cmd}' failed with a return code \ 108 | of {result.returncode}" 109 | ) 110 | else: 111 | logging.debug(f"No command found in spec JSON for {spec}") 112 | raise 113 | -------------------------------------------------------------------------------- /integration-tests/test_compliance.py: -------------------------------------------------------------------------------- 1 | """ 2 | :casecomponent: insights-client 3 | :requirement: RHSS-291297 4 | :polarion-project-id: RHELSS 5 | :polarion-include-skipped: false 6 | :polarion-lookup-method: id 7 | :subsystemteam: rhel-sst-csi-client-tools 8 | :caseautomation: Automated 9 | :upstream: Yes 10 | """ 11 | 12 | import conftest 13 | import pytest 14 | import distro 15 | 16 | pytestmark = [ 17 | pytest.mark.usefixtures("register_subman"), 18 | pytest.mark.skipif( 19 | distro.id() != "rhel", 20 | reason="Skipping tests because OS ID is not RHEL", 21 | allow_module_level=True, 22 | ), 23 | ] 24 | 25 | 26 | @pytest.mark.tier1 27 | def test_compliance_option(insights_client): 28 | """ 29 | :id: caa8b3e1-9347-494c-a1f5-1fa670136834 30 | :title: Test compliance option 31 | :reference: https://issues.redhat.com/browse/CCT-1229 32 | :description: 33 | This test verifies that running the --compliance will not result in a failure 34 | :tags: Tier 1 35 | :steps: 36 | 1. Run insights-client with --compliance option 37 | 2. Register insights-client 38 | 3. Run insights-client with --compliance option 39 | :expectedresults: 40 | 1. Command will fail with instructions for user to register 41 | 2. System is successfully registered 42 | 3. The output of the command either informs user that system is not associated 43 | with any policies or the report will be successfully uploaded 44 | """ 45 | compliance_before_registration = insights_client.run("--compliance", check=False) 46 | assert compliance_before_registration.returncode == 1 47 | assert ( 48 | "This host is unregistered. Use --register to register this host" 49 | in compliance_before_registration.stdout 50 | or "This host has not been registered. Use --register to register this host" 51 | in compliance_before_registration.stdout 52 | ) 53 | 54 | insights_client.register() 55 | assert conftest.loop_until(lambda: insights_client.is_registered) 56 | 57 | compliance_after_registration = insights_client.run("--compliance", check=False) 58 | if compliance_after_registration.returncode == 1: 59 | assert ( 60 | "System is not associated with any policies." 61 | in compliance_after_registration.stdout 62 | ) 63 | else: 64 | assert "Successfully uploaded report" in compliance_after_registration.stdout 65 | 66 | 67 | @pytest.mark.tier1 68 | def test_compliance_policies_option(insights_client): 69 | """ 70 | :id: ad3a2073-3a2e-485e-bc7b-fede2111a06a 71 | :title: Test compliance-policies option 72 | :reference: https://issues.redhat.com/browse/CCT-1229 73 | :description: 74 | This test verifies that running the --compliance-policies 75 | will not result in a failure 76 | :tags: Tier 1 77 | :steps: 78 | 1. Register insights-client 79 | 2. Run insights-client with --compliance-policies option 80 | :expectedresults: 81 | 1. System is successfully registered 82 | 2. The output of the command either informs the user that system is not 83 | assignable to any policy or ID of available policy is found and 84 | displayed 85 | """ 86 | insights_client.register() 87 | assert conftest.loop_until(lambda: insights_client.is_registered) 88 | 89 | compliance_policies = insights_client.run("--compliance-policies", check=False) 90 | if compliance_policies.returncode == 1: 91 | assert "System is not assignable to any policy." in compliance_policies.stdout 92 | assert ( 93 | "Create supported policy using the Compliance web UI." 94 | in compliance_policies.stdout 95 | ) 96 | else: 97 | assert "ID" in compliance_policies.stdout 98 | -------------------------------------------------------------------------------- /integration-tests/test_connection.py: -------------------------------------------------------------------------------- 1 | """ 2 | :casecomponent: insights-client 3 | :requirement: RHSS-291297 4 | :polarion-project-id: RHELSS 5 | :polarion-include-skipped: false 6 | :polarion-lookup-method: id 7 | :subsystemteam: rhel-sst-csi-client-tools 8 | :caseautomation: Automated 9 | :upstream: Yes 10 | """ 11 | 12 | import configparser 13 | import contextlib 14 | import logging 15 | import os 16 | import typing 17 | 18 | import pytest 19 | 20 | if typing.TYPE_CHECKING: 21 | import pytest_client_tools.insights_client 22 | 23 | # the tests need a valid connection to insights so therefore the subman registration 24 | pytestmark = pytest.mark.usefixtures("register_subman") 25 | 26 | 27 | def _is_using_proxy( 28 | insights_config: "pytest_client_tools.insights_client.InsightsClientConfig", 29 | ) -> bool: 30 | for key, value in os.environ.items(): 31 | if key.lower() == "https_proxy": 32 | logging.debug(f"Proxy is set via environment variable: '{value}'.") 33 | return True 34 | 35 | with contextlib.suppress(KeyError): 36 | insights_proxy: str = insights_config.proxy 37 | if insights_proxy != "": 38 | logging.debug(f"Proxy is set via insights-client: '{insights_proxy}'.") 39 | return True 40 | 41 | # sub-man fixture doesn't currently support reading the configuration file, 42 | # let's parse it ourselves. 43 | with contextlib.suppress(Exception): 44 | rhsm_config = configparser.ConfigParser() 45 | rhsm_config.read("/etc/rhsm/rhsm.conf") 46 | rhsm_proxy: str = rhsm_config.get("server", "proxy_hostname", fallback="") 47 | if rhsm_proxy != "": 48 | logging.debug(f"Proxy is set via subscription-manager: '{rhsm_proxy}'.") 49 | return True 50 | 51 | logging.debug("Proxy is not set.") 52 | return False 53 | 54 | 55 | @pytest.mark.tier1 56 | def test_connection_ok(insights_client): 57 | """ 58 | :id: ff674d37-0ccc-481c-9f04-91237b8c50d0 59 | :title: Test connection 60 | :description: 61 | This test verifies that the --test-connection option works 62 | properly, confirming successful connection 63 | :tags: Tier 1 64 | :steps: 65 | 1. Run insights-client with --test-connection option 66 | 2. Verify that the connection to the upload URl is successful 67 | 3. Verify that the connection to the API URL is successful 68 | :expectedresults: 69 | 1. The command executes successfully 70 | 2. The output contains the expected output about upload connection 71 | 3. The output contains expected message about API URL connection 72 | """ 73 | url_test = "End Upload URL Connection Test: SUCCESS" 74 | api_test = "End API URL Connection Test: SUCCESS" 75 | 76 | test_connection = insights_client.run("--test-connection") 77 | assert url_test in test_connection.stdout 78 | assert api_test in test_connection.stdout 79 | 80 | 81 | @pytest.mark.tier1 82 | def test_http_timeout(insights_client): 83 | """ 84 | :id: 46c5fe2a-1553-4f2e-802d-fa10080c72df 85 | :title: Test HTTP timeout configuration 86 | :description: 87 | Verifies that setting a very low http_timeout value causes 88 | the connection to time out 89 | :tags: Tier 1 90 | :steps: 91 | 1. Set the http_timeout option to a very low value and save 92 | 2. Run insights-client with the --test-conection option 93 | 3. Verify that the command fails due to the low timeout setting 94 | 4. Check that the timeout value is in the output 95 | :expectedresults: 96 | 1. The http_timeout configuration is set and saved 97 | 2. The insights-client command is run 98 | 3. The command fails with a return code of 1 99 | 4. The output mentions timeout value 100 | """ 101 | insights_client.config.http_timeout = 0.001 102 | insights_client.config.save() 103 | 104 | output = insights_client.run("--test-connection", check=False) 105 | assert output.returncode == 1 106 | 107 | if _is_using_proxy(insights_client.config): 108 | assert "timeout('timed out')" in output.stdout 109 | else: 110 | assert "Read timed out. (read timeout=0.001)" 111 | assert "Traceback" not in output.stdout 112 | 113 | 114 | @pytest.mark.tier1 115 | def test_noauth_proxy_connection(insights_client, test_config): 116 | """ 117 | :id: a4bcb7e6-c04f-49d2-8362-525124dc61d9 118 | :title: Test no-auth proxy connection 119 | :description: 120 | Verifies that the insights-client can successfully connect 121 | through a no-auth proxy when using the --test-connection option 122 | :tags: Tier 1 123 | :steps: 124 | 1. Configure insights-client to use no-auth proxy and save 125 | 2. Run insights-client with the --test-connection option 126 | 3. Verify the connection to the upload URL is successful 127 | 4. Verify that the connection to the API URL is successful 128 | :expectedresults: 129 | 1. Insights-client is configured to use no-auth proxy 130 | 2. The command is executed successfully 131 | 3. The output mentions that the upload URL connection was suuccessful 132 | 4. The output mentions that the API URL connection test was successful 133 | """ 134 | url_test = "End Upload URL Connection Test: SUCCESS" 135 | api_test = "End API URL Connection Test: SUCCESS" 136 | 137 | no_auth_proxy: str = ( 138 | "http://" 139 | + test_config.get("noauth_proxy", "host") 140 | + ":" 141 | + str(test_config.get("noauth_proxy", "port")) 142 | ) 143 | insights_client.config.proxy = no_auth_proxy 144 | insights_client.config.save() 145 | 146 | test_connection = insights_client.run("--test-connection") 147 | assert url_test in test_connection.stdout 148 | assert api_test in test_connection.stdout 149 | 150 | 151 | @pytest.mark.tier1 152 | def test_auth_proxy_connection(insights_client, test_config): 153 | """ 154 | :id: 0b3e91d6-3b8b-42c7-8f3b-f7ee1728c311 155 | :title: Test auth-proxy connection 156 | :description: 157 | Verifies that the insights-client can successfully connect 158 | through an authenticated proxy 159 | :tags: Tier 1 160 | :steps: 161 | 1. Configure insights-client to use auth proxy and save 162 | 2. Run the insights-client with --test-connection option 163 | 3. Verify the connection to the upload URL is successful 164 | 4. Verify that the connection to the API URL is successful 165 | :expectedresults: 166 | 1. Insights-client is configured to use auth proxy 167 | 2. The command is executed successfully 168 | 3. The output mentions that the upload URL connection was suuccessful 169 | 4. The output mentions that the API URL connection test was successful 170 | """ 171 | url_test = "End Upload URL Connection Test: SUCCESS" 172 | api_test = "End API URL Connection Test: SUCCESS" 173 | 174 | auth_proxy: str = ( 175 | "http://" 176 | + test_config.get("auth_proxy", "username") 177 | + ":" 178 | + test_config.get("auth_proxy", "password") 179 | + "@" 180 | + test_config.get("auth_proxy", "host") 181 | + ":" 182 | + str(test_config.get("auth_proxy", "port")) 183 | ) 184 | insights_client.config.proxy = auth_proxy 185 | insights_client.config.save() 186 | test_connection = insights_client.run("--test-connection") 187 | assert url_test in test_connection.stdout 188 | assert api_test in test_connection.stdout 189 | 190 | 191 | @pytest.mark.tier1 192 | def test_wrong_url_connection(insights_client): 193 | """ 194 | :id: aa411b34-9af2-4759-ae05-756e9019c85e 195 | :title: Test wrong URL connection 196 | :description: 197 | Verifies that the insights-client fails to connect when an 198 | incorrect URL is configured 199 | :tags: Tier 1 200 | :steps: 201 | 1. Disable auto-configuration and auto-updates 202 | 2. Set an incorrect base URL in the configuration 203 | 3. Run insights-client with the --test-connection option 204 | 4. Verify the command fails due to the incorrect URL 205 | 5. Check that the output contains the appropriate failure message 206 | :expectedresults: 207 | 1. Auto-configuration and auto-updates are disabled 208 | 2. Incorrect base URL is set 209 | 3. Insights-client command is executed successfully 210 | 4. The command failed with a return code of 1 211 | 5. The message includes expected mentions of the failure 212 | """ 213 | insights_client.config.auto_config = False 214 | insights_client.config.auto_update = False 215 | insights_client.config.base_url = "no-such-insights-url.example.com/something" 216 | insights_client.config.authmethod = "CERT" 217 | insights_client.config.save() 218 | 219 | test_connection = insights_client.run("--test-connection", check=False) 220 | assert test_connection.returncode == 1 221 | 222 | if _is_using_proxy(insights_client.config): 223 | assert "Cannot connect to proxy." in test_connection.stdout 224 | else: 225 | assert "Failed to establish a new connection" in test_connection.stdout 226 | assert "Connectivity test failed!" in test_connection.stdout 227 | -------------------------------------------------------------------------------- /integration-tests/test_display_name_option.py: -------------------------------------------------------------------------------- 1 | """ 2 | :casecomponent: insights-client 3 | :requirement: RHSS-291297 4 | :polarion-project-id: RHELSS 5 | :polarion-include-skipped: false 6 | :polarion-lookup-method: id 7 | :subsystemteam: rhel-sst-csi-client-tools 8 | :caseautomation: Automated 9 | :upstream: Yes 10 | """ 11 | 12 | import string 13 | import pytest 14 | import uuid 15 | import logging 16 | import json 17 | import random 18 | import conftest 19 | import subprocess 20 | 21 | from constants import HOST_DETAILS 22 | 23 | logger = logging.getLogger(__name__) 24 | 25 | pytestmark = pytest.mark.usefixtures("register_subman") 26 | 27 | 28 | def read_host_details(): 29 | with open(HOST_DETAILS, "r") as data_file: 30 | return json.load(data_file) 31 | 32 | 33 | def generate_unique_hostname(): 34 | return f"test-qa.{uuid.uuid4()}.csi-client-tools.example.com" 35 | 36 | 37 | def create_random_string(n: int): 38 | return "".join(random.choices(string.ascii_letters, k=n)) 39 | 40 | 41 | @pytest.mark.tier1 42 | def test_display_name(insights_client): 43 | """ 44 | :id: 4758cb21-03b4-4334-852c-791b7c82b50a 45 | :title: Test updating display name via '--display-name' 46 | :description: 47 | This test verifies that a registered host's display name can be 48 | updated using the --display-name option. 49 | :tags: Tier 1 50 | :steps: 51 | 1. Generate a unique hostname and register the insights-client 52 | 2. Update the display name using --display-name 53 | 3. Verify the display name has been updated in the host details 54 | :expectedresults: 55 | 1. A unique hostname is generated and insights-client is registered 56 | 2. The command outputs 'Display name updated to ' 57 | 3. The display_name in host detailes matches the new hostname 58 | """ 59 | new_hostname = generate_unique_hostname() 60 | insights_client.run("--register") 61 | assert conftest.loop_until(lambda: insights_client.is_registered) 62 | 63 | response = insights_client.run("--display-name", new_hostname) 64 | logger.debug(f"response from console {response}") 65 | 66 | assert f"Display name updated to {new_hostname}" in response.stdout 67 | 68 | def display_name_changed(): 69 | insights_client.run("--check-results") 70 | host_details = read_host_details() 71 | logger.debug(f"host details {host_details}") 72 | record = host_details["results"][0] 73 | return new_hostname == record["display_name"] 74 | 75 | assert conftest.loop_until(display_name_changed) 76 | 77 | 78 | @pytest.mark.tier1 79 | def test_register_with_display_name(insights_client): 80 | """ 81 | :id: d127b2bf-2f6d-4b02-bb8e-99036bfc4291 82 | :title: Test registration with custom display name 83 | :description: 84 | This test ensures that registering the insights-client with a custom 85 | display name sets the display name correctly in host details 86 | :tags: Tier 1 87 | :steps: 88 | 1. Generate a unique hostname 89 | 2. Register the insights-client using '--register --display-name' 90 | 3. Verify the display_name in host details 91 | :expectedresults: 92 | 1. Unique hostname is generated 93 | 2. The client registers and successfully outputs the unique hostname 94 | 3. The display_name in host details matches the unique hostname 95 | """ 96 | unique_hostname = generate_unique_hostname() 97 | 98 | status = insights_client.run("--register", "--display-name", unique_hostname) 99 | assert conftest.loop_until(lambda: insights_client.is_registered) 100 | assert unique_hostname in status.stdout 101 | insights_client.run("--check-results") 102 | host_details = read_host_details() 103 | logger.debug(f"content of host-details.json {host_details}") 104 | 105 | record = host_details["results"][0] 106 | assert "display_name" in record.keys() 107 | assert unique_hostname == record["display_name"] 108 | 109 | 110 | @pytest.mark.tier1 111 | def test_register_twice_with_different_display_name( 112 | insights_client, test_config, subtests 113 | ): 114 | """ 115 | :id: 3d28562d-16c4-4fb1-b9b1-f39044e05ef5 116 | :title: Test re-registration with different display names 117 | :description: 118 | This test checks that registering the insights-client twice with different 119 | display names does not change the insights_id and display_name is updated 120 | :tags: Tier 1 121 | :steps: 122 | 1. Generate a unique hostname 123 | 2. Register the insights-client using '--register --display-name' 124 | 3. Record the machine ID and display_name 125 | 4. Generate another unique hostname 126 | 5. Register the insights-client using '--register --display_name' 127 | 6. Compare the old display_name and insights_id with those updated 128 | :expectedresults: 129 | 1. Unique hostname is generated 130 | 2. The client registers and successfully outputs the unique hostname 131 | 3. Information are successfully retrieved and stored 132 | 4. Unique hostname is generated 133 | 5. The output indicates that the host is already registered but updates the 134 | display_name to the new one 135 | 6. Insights_id stayed unchanged while display_name changed to the latest one 136 | """ 137 | insights_id = None 138 | unique_hostname = generate_unique_hostname() 139 | unique_hostname_02 = generate_unique_hostname() 140 | 141 | with subtests.test(msg="the first registration"): 142 | status = insights_client.run("--register", "--display-name", unique_hostname) 143 | assert unique_hostname in status.stdout 144 | 145 | assert conftest.loop_until(lambda: insights_client.is_registered) 146 | insights_client.run("--check-results") 147 | host_details = read_host_details() 148 | record = host_details["results"][0] 149 | assert "display_name" in record.keys() 150 | assert unique_hostname == record["display_name"] 151 | insights_id = record["insights_id"] 152 | 153 | (status, host_details, record) = (None, None, None) 154 | with subtests.test(msg="The second registration"): 155 | status = insights_client.run("--register", "--display-name", unique_hostname_02) 156 | registration_message = "This host has already been registered" 157 | assert registration_message in status.stdout 158 | 159 | assert conftest.loop_until(lambda: insights_client.is_registered) 160 | insights_client.run("--check-results") 161 | host_details = read_host_details() 162 | logger.debug(f"content of host-details.json: {host_details}") 163 | record = host_details["results"][0] 164 | assert "display_name" in record.keys() 165 | assert unique_hostname_02 == record["display_name"] 166 | assert ( 167 | insights_id == record["insights_id"] 168 | ), "machine-id should remain the same even display-name has been changed" 169 | 170 | 171 | @pytest.mark.parametrize("invalid_display_name", [create_random_string(201), ""]) 172 | @pytest.mark.tier1 173 | def test_invalid_display_name(invalid_display_name, insights_client): 174 | """ 175 | :id: 9cbdd1a6-9ee3-4799-baaf-15c3894ca55b 176 | :title: Test handling of invalid display names 177 | :parametrized: yes 178 | :description: 179 | This test verifies that attempting to set an invalid display_name is rejected 180 | and does not alter the current display_name value 181 | :tags: Tier 1 182 | :steps: 183 | 1. Register the insights-client 184 | 2. Record the original display_name value 185 | 3. Attempt to update the display_name using an invalid value 186 | 4. Verify that display_name stayed unchanged 187 | :expectedresults: 188 | 1. Insights-client is registered 189 | 2. Original display_name value is saved 190 | 3. The command fails with an error code 1 and a message 191 | 'Could not update display name' 192 | 4. The display_name in host details matches the saved original 193 | """ 194 | insights_client.run("--register") 195 | assert conftest.loop_until(lambda: insights_client.is_registered) 196 | 197 | insights_client.run("--check-results") 198 | host_details = read_host_details() 199 | origin_display_name = host_details["results"][0]["display_name"] 200 | 201 | response = insights_client.run("--display-name", invalid_display_name, check=False) 202 | assert response.returncode == 1 203 | assert "Could not update display name" in response.stdout 204 | 205 | insights_client.run("--check-results") 206 | host_details = read_host_details() 207 | logger.debug(f"content of host-details.json {host_details}") 208 | 209 | record = host_details["results"][0] 210 | assert ( 211 | origin_display_name == record["display_name"] 212 | ), "display-name should remain unchanged when new display-name is rejected" 213 | 214 | 215 | @pytest.mark.tier2 216 | def test_display_name_disable_autoconfig_and_autoupdate(insights_client, test_config): 217 | """ 218 | :id: 8cdbc0ff-42ba-41e8-bd3f-31550ccac081 219 | :title: Test registration with display_name when auto-config and auto-update 220 | are disabled 221 | :description: 222 | This test verifies that the insights-client can be registered with a 223 | display_name name even when auto_config and auto_update are set to 224 | False and host appears on cloud with the correct display name 225 | :reference: https://issues.redhat.com/browse/RHEL-19435 226 | :tags: Tier 2 227 | :steps: 228 | 1. Configure insights-client.conf with auto_config and auto_update set to False 229 | and display_name set 230 | 2. Register the insights-client 231 | 3. Verify the host is visible in cloud.redhat.com with the correct display_name 232 | set in the configuration file 233 | :expectedresults: 234 | 1. Configuration is set and successfully saved 235 | 2. Insights-client is registered 236 | 3. Host appears in inventory with the display name matching the one that was set 237 | """ 238 | # configuration on insights-client.conf 239 | insights_client.config.legacy_upload = False 240 | insights_client.config.cert_verify = True 241 | insights_client.config.auto_update = False 242 | insights_client.config.auto_config = False 243 | insights_client.config.authmethod = "CERT" 244 | unique_hostname = generate_unique_hostname() 245 | insights_client.config.display_name = unique_hostname 246 | if "satellite" in test_config.environment: 247 | satellite_hostname = test_config.get("candlepin", "host") 248 | satellite_port = test_config.get("candlepin", "port") 249 | insights_client.config.base_url = ( 250 | satellite_hostname + ":" + str(satellite_port) + "/redhat_access/r/insights" 251 | ) 252 | insights_client.config.cert_verify = "/etc/rhsm/ca/katello-server-ca.pem" 253 | elif "prod" in test_config.environment: 254 | insights_client.config.base_url = "cert.cloud.redhat.com/api" 255 | elif "stage" in test_config.environment: 256 | insights_client.config.base_url = "cert.cloud.stage.redhat.com/api" 257 | insights_client.config.save() 258 | 259 | # register insights 260 | try: 261 | status = insights_client.run("--register") 262 | except subprocess.CalledProcessError as e: 263 | if ( 264 | "certificate verify failed" in e.stdout.lower() 265 | or "certificate verify failed" in str(e) 266 | ): 267 | pytest.skip("Skipping test due to SSL certificate verification failure") 268 | raise 269 | assert conftest.loop_until(lambda: insights_client.is_registered) 270 | assert unique_hostname in status.stdout 271 | 272 | # check the display name on CRC 273 | insights_client.run("--check-results") 274 | host_details = read_host_details() 275 | logger.debug(f"content of host-details.json {host_details}") 276 | record = host_details["results"][0] 277 | assert unique_hostname == record["display_name"] 278 | -------------------------------------------------------------------------------- /integration-tests/test_e2e.py: -------------------------------------------------------------------------------- 1 | """ 2 | :casecomponent: insights-client 3 | :requirement: RHSS-291297 4 | :polarion-project-id: RHELSS 5 | :polarion-include-skipped: false 6 | :polarion-lookup-method: id 7 | :subsystemteam: rhel-sst-csi-client-tools 8 | :caseautomation: Automated 9 | :upstream: Yes 10 | """ 11 | 12 | import pytest 13 | import conftest 14 | from pytest_client_tools.util import Version 15 | 16 | pytestmark = pytest.mark.usefixtures("register_subman") 17 | 18 | 19 | @pytest.mark.tier1 20 | def test_insights_client_version_in_inventory(insights_client, external_inventory): 21 | """ 22 | :id: 1d5d101e-94ad-4404-900f-f86a26450c3f 23 | :title: Verify insights-client version in Inventory 24 | :description: 25 | Ensure that running insights-client creates a new host entry in the 26 | Inventory and includes both the insights-client and egg version information 27 | :tags: Tier 1 28 | :steps: 29 | 1. Register the system with insights-client 30 | 2. Confirm registration status 31 | 3. Retrieve the system profile from the Inventory 32 | :expectedresults: 33 | 1. Insights-client is registered 34 | 2. The system profile is retrieved from the Inventory 35 | 3. The system profile includes insights-client and egg version information 36 | """ 37 | insights_client.register() 38 | assert conftest.loop_until(lambda: insights_client.is_registered) 39 | 40 | system_profile = external_inventory.this_system_profile() 41 | 42 | assert insights_client.version == Version(system_profile["insights_client_version"]) 43 | assert insights_client.core_version == Version( 44 | (system_profile["insights_egg_version"].split("-"))[0] 45 | ) 46 | -------------------------------------------------------------------------------- /integration-tests/test_file_workflow.py: -------------------------------------------------------------------------------- 1 | """ 2 | :casecomponent: insights-client 3 | :requirement: RHSS-291297 4 | :polarion-project-id: RHELSS 5 | :polarion-include-skipped: false 6 | :polarion-lookup-method: id 7 | :subsystemteam: rhel-sst-csi-client-tools 8 | :caseautomation: Automated 9 | :upstream: Yes 10 | """ 11 | 12 | import json 13 | import os 14 | import random 15 | import string 16 | import subprocess 17 | import tarfile 18 | from time import sleep 19 | import pytest 20 | from constants import HOST_DETAILS 21 | from constants import MACHINE_ID_FILE 22 | import conftest 23 | import logging 24 | 25 | 26 | pytestmark = pytest.mark.usefixtures("register_subman") 27 | 28 | 29 | @pytest.mark.tier1 30 | def test_file_workflow_with_an_archive_with_only_one_canonical_fact( 31 | insights_client, tmp_path 32 | ): 33 | """ 34 | :id: 816e341e-2477-4f24-8e37-dacf308413f5 35 | :title: Test File Workflow with an Archive with only One Canonical Fact 36 | :description: 37 | Verify uploading an Insights Archive with just one 38 | canonical fact creates a new host on Inventory 39 | :tags: Tier 1 40 | :steps: 41 | 1. Remove canonical facts files from the pre-collected archive 42 | except the file that has the insights-id 43 | 2. Upload the pre-collected archive 44 | 3. Retrieve the system from Inventory 45 | :expectedresults: 46 | 1. The facts are removed 47 | 2. The pre-collected archive is successfully uploaded 48 | 3. The host FQDN returned in host data retrieved from Inventory on step 3 49 | matches the hostname set in the archive 50 | """ 51 | archive_name = tmp_path / "archive.tar.gz" 52 | modified_archive = tmp_path / "archive_modified.tar.gz" 53 | 54 | insights_client.run(f"--output-file={archive_name}") 55 | # Sending archive only with Insights ID 56 | files_to_remove = [ 57 | "/data/etc/machine-id", 58 | "/data/insights_commands/hostname", 59 | "/data/insights_commands/hostname_-f", 60 | "/data/insights_commands/subscription-manager_identity", 61 | ] 62 | machine_id = open(MACHINE_ID_FILE, "r").read() 63 | remove_files_from_archive(archive_name, files_to_remove, modified_archive) 64 | upload_result = insights_client.run( 65 | f"--payload={modified_archive}", "--content-type=gz", check=False 66 | ) 67 | assert "Successfully uploaded report" in upload_result.stdout 68 | 69 | # once archive is uploaded system status changes to register 70 | assert conftest.loop_until(lambda: insights_client.is_registered) 71 | 72 | insights_client.run("--check-results") # to get host details from inventory 73 | with open(HOST_DETAILS, "r") as data_file: 74 | file_content = json.load(data_file) 75 | 76 | assert "count" in file_content.keys() 77 | assert file_content["count"] == 1 78 | assert "results" in file_content.keys() 79 | host_data = file_content["results"][0] 80 | 81 | assert host_data.get("insights_id") == machine_id 82 | 83 | 84 | @pytest.mark.tier1 85 | def test_file_workflow_with_an_archive_without_canonical_facts( 86 | insights_client, tmp_path 87 | ): 88 | """ 89 | :id: 46428b70-7803-4fb6-b694-66c88a0236e3 90 | :title: Test File Workflow with an Archive without Canonical Facts 91 | :description: 92 | Verify uploading an Insights Archive without canonical facts 93 | does NOT create a new host on Inventory 94 | :tags: Tier 1 95 | :steps: 96 | 1. Remove all canonical facts files from the pre-collected archive 97 | 2. Upload the pre-collected archive 98 | :expectedresults: 99 | 1. The facts are removed 100 | 2. The pre-collected archive is successfully uploaded 101 | """ 102 | archive_name = tmp_path / "archive.tar.gz" 103 | modified_archive = tmp_path / "archive_modified.tar.gz" 104 | 105 | insights_client.run(f"--output-file={archive_name}") 106 | files_to_remove = [ 107 | "/data/etc/insights-client/machine-id", 108 | "/data/etc/machine-id", 109 | "/data/etc/redhat-release", 110 | "/data/insights_commands/hostname", 111 | "/data/insights_commands/hostname_-f", 112 | "/data/insights_commands/subscription-manager_identity", 113 | "/data/insights_commands/dmidecode_-s_system-uuid", 114 | "/data/insights_commands/ip_addr", 115 | "/data/insights_commands/hostname_-I", 116 | "/data/sys/class/net/eth0/address", 117 | "/data/sys/class/net/lo/address", 118 | ] 119 | 120 | remove_files_from_archive(archive_name, files_to_remove, modified_archive) 121 | upload_result = insights_client.run( 122 | f"--payload={modified_archive}", "--content-type=gz", check=False 123 | ) 124 | assert "Successfully uploaded report" in upload_result.stdout 125 | 126 | 127 | @pytest.mark.skipif( 128 | "container" in os.environ.keys(), 129 | reason="Containers cannot change hostnames", 130 | ) 131 | @pytest.mark.tier1 132 | def test_file_workflow_archive_update_host_info(insights_client, external_inventory): 133 | """ 134 | :id: 336abff9-4263-4f1d-9448-2cd05d40a371 135 | :title: Verify Insights Archive Updates Host Information 136 | :description: 137 | Ensure that updating files within an Insights Archive reflects 138 | the correct host information, such as hostname, in the Inventory 139 | :tags: Tier 1 140 | :steps: 141 | 1. Register the system with insights-client and confirm data upload 142 | 2. Change the system hostname 143 | 3. Collect and upload the new archive 144 | 4. Retrieve host data from Inventory and verify fqdn 145 | :expectedresults: 146 | 1. The system is registered and the archive is uploaded successfully 147 | 2. The system hostname is updated successfully 148 | 3. A new archive is collected and uploaded successfully 149 | 4. The fqdn in the Inventory matches the updated hostname 150 | """ 151 | insights_client.register() 152 | assert conftest.loop_until(lambda: insights_client.is_registered) 153 | current_hostname = subprocess.check_output("hostname", shell=True).decode().strip() 154 | 155 | # Set a new hostname 156 | new_hostname = set_hostname() 157 | fqdn = subprocess.check_output("hostname -f", shell=True).decode().strip() 158 | logging.debug(f"Assigned hostname: {new_hostname}, FQDN: {fqdn}") 159 | 160 | insights_client.run() 161 | sleep(30) # Wait for data to get reflected in inventory 162 | host_data = external_inventory.this_system() 163 | logging.debug(f"Host data from inventory: {host_data}") 164 | 165 | # New hostname matches the one in Inventory 166 | assert host_data.get("fqdn") == fqdn 167 | 168 | # Reset to the original hostname 169 | set_hostname(current_hostname) 170 | 171 | 172 | def remove_files_from_archive(original_archive, files_to_remove, modified_archive): 173 | with tarfile.open(original_archive, "r:gz") as tar: 174 | file_list = tar.getnames() 175 | dir_name = tar.getnames()[0] 176 | # append dirname to create absolute path names 177 | files_to_remove = [dir_name + item for item in files_to_remove] 178 | # Remove the specified files from the list 179 | files_to_keep = [f for f in file_list if f not in files_to_remove] 180 | # Create a new tar archive 181 | with tarfile.open(modified_archive, "w:gz") as new_tar: 182 | for member in tar.getmembers(): 183 | if member.name in files_to_keep: 184 | new_tar.addfile(member, tar.extractfile(member)) 185 | 186 | 187 | def set_hostname(hostname=None): 188 | if hostname is None: 189 | hostname = ( 190 | "".join(random.choices(string.ascii_lowercase + string.digits, k=10)) 191 | + "-new-hostname.example.com" 192 | ) 193 | 194 | subprocess.run(["hostnamectl", "set-hostname", hostname], check=True) 195 | return hostname 196 | -------------------------------------------------------------------------------- /integration-tests/test_manpage.py: -------------------------------------------------------------------------------- 1 | """ 2 | :casecomponent: insights-client 3 | :requirement: RHSS-291297 4 | :polarion-project-id: RHELSS 5 | :polarion-include-skipped: false 6 | :polarion-lookup-method: id 7 | :subsystemteam: rhel-sst-csi-client-tools 8 | :caseautomation: Automated 9 | :upstream: Yes 10 | """ 11 | 12 | import gzip 13 | import pytest 14 | 15 | 16 | @pytest.mark.parametrize( 17 | "option", 18 | [ 19 | "--checkin", 20 | "--compliance", 21 | "--content-type", 22 | "--diagnosis", 23 | "--disable-schedule", 24 | "--display-name", 25 | "--enable-schedule", 26 | "--group", 27 | "--keep-archive", 28 | "--list-specs", 29 | "--net-debug", 30 | "--no-upload", 31 | "--offline", 32 | "--output-dir", 33 | "--output-file", 34 | "--payload", 35 | "--quiet", 36 | "--register", 37 | "--retry", 38 | "--show-results", 39 | "--silent", 40 | "--status", 41 | "--test-connection", 42 | "--unregister", 43 | "--validate", 44 | "--verbose", 45 | "--version", 46 | ], 47 | ) 48 | @pytest.mark.tier1 49 | def test_manpage(option): 50 | """ 51 | :id: bd8dbda3-930e-4081-b318-1e88b25e26ef 52 | :title: Test manual page entries for insights-client 53 | :parametrized: yes 54 | :description: 55 | This test verifies that the insights-client manual page includes 56 | all the specified options. 57 | :tags: Tier 1 58 | :steps: 59 | 1. Open the manual page 60 | 2. Verify that the specified options are present 61 | :expectedresults: 62 | 1. Manual page is opened successfully 63 | 2. All od the options are found in the manual page 64 | """ 65 | file = "/usr/share/man/man8/insights-client.8.gz" 66 | opened_file = gzip.open(file, "rt") 67 | content = opened_file.read() 68 | assert option in content, f"Option {option} is not present" 69 | -------------------------------------------------------------------------------- /integration-tests/test_motd.py: -------------------------------------------------------------------------------- 1 | """ 2 | :casecomponent: insights-client 3 | :requirement: RHSS-291297 4 | :polarion-project-id: RHELSS 5 | :polarion-include-skipped: false 6 | :polarion-lookup-method: id 7 | :subsystemteam: rhel-sst-csi-client-tools 8 | :caseautomation: Automated 9 | :upstream: Yes 10 | """ 11 | 12 | import contextlib 13 | import conftest 14 | import os 15 | import subprocess 16 | import pytest 17 | 18 | 19 | DOT_REGISTERED_PATH = "/etc/insights-client/.registered" 20 | DOT_UNREGISTERED_PATH = "/etc/insights-client/.unregistered" 21 | 22 | MOTD_PATH = "/etc/motd.d/insights-client" 23 | MOTD_SRC = "/etc/insights-client/insights-client.motd" 24 | 25 | 26 | @pytest.fixture 27 | def delete_special_files(): 28 | """Clean special files before and after test. 29 | 30 | Since .registered and .unregistered files are usually ignored and 31 | do not affect the behavior of insights-client, the MOTD case is 32 | special and depends on them. 33 | 34 | To ensure the tests here are not affected by them, we remove them 35 | """ 36 | 37 | def delete_files(): 38 | with contextlib.suppress(FileNotFoundError): 39 | os.remove(DOT_REGISTERED_PATH) 40 | with contextlib.suppress(FileNotFoundError): 41 | os.remove(DOT_UNREGISTERED_PATH) 42 | with contextlib.suppress(FileNotFoundError): 43 | os.remove(MOTD_PATH) 44 | 45 | delete_files() 46 | yield 47 | delete_files() 48 | 49 | 50 | @pytest.mark.usefixtures("register_subman") 51 | @pytest.mark.usefixtures("delete_special_files") 52 | @pytest.mark.tier1 53 | def test_motd(insights_client): 54 | """ 55 | :id: a66a93bb-bbd2-4db0-a2aa-2bb184b11187 56 | :title: Test MOTD file presence based on registration status 57 | :description: 58 | This test verifies that the MOTD file exists on an unregistered 59 | system and that it is appropriately removed or not recreated upon 60 | registration and unregistration 61 | :tags: Tier 1 62 | :steps: 63 | 1. Verify that MOTD file is present on an unregistered system 64 | 2. Register the insights-client 65 | 3. Verify the MOTD file does not exists after registration 66 | 4. Unregister the insights-client 67 | 5. Verify the MOTD file still does not exist after unregistration 68 | :expectedresults: 69 | 1. The MOTD file is present 70 | 2. The client registers successfully 71 | 3. The MOTD file is removed 72 | 4. The client unregisters successfully 73 | 5. The MOTD file is still not present 74 | """ 75 | # If the system is not registered, the file should be present. 76 | insights_client.run("--status", check=False) 77 | assert os.path.exists(MOTD_PATH) 78 | 79 | # After registration, the file should not exist. 80 | insights_client.register() 81 | assert conftest.loop_until(lambda: insights_client.is_registered) 82 | assert not os.path.exists(MOTD_PATH) 83 | 84 | # The system was unregistered. Because .unregistered file exists, 85 | # the file should not be present. 86 | insights_client.unregister() 87 | assert not os.path.exists(MOTD_PATH) 88 | 89 | 90 | @pytest.mark.usefixtures("register_subman") 91 | @pytest.mark.usefixtures("delete_special_files") 92 | @pytest.mark.tier1 93 | def test_motd_dev_null(insights_client): 94 | """ 95 | :id: 7d48df16-e1af-4158-8a33-1d2cbb9ed22d 96 | :title: Test MOTD remains untouched when symlinked to /dev/null 97 | :description: 98 | This tst ensures that of the MOTD file is a symbolic link to 99 | /dev/null, it is not modified or removed during the client's registration 100 | and unregistration processes 101 | :tags: Tier 1 102 | :steps: 103 | 1. Create a symlink from MOTD_PATH to /dev/null 104 | 2. Run insights-client with --status option 105 | 3. Register the insights-client 106 | 4. Unregister the insights-client 107 | :expectedresults: 108 | 1. The symlink is created successfully 109 | 2. Command runs successfully and MOTD remains a symlink to /dev/null 110 | 3. The client is registered and MOTD stayed unchanged 111 | 4. the client is unregistered and MOTD stayed unchanged 112 | """ 113 | with contextlib.ExitStack() as stack: 114 | os.symlink(os.devnull, MOTD_PATH) 115 | stack.callback(os.unlink, MOTD_PATH) 116 | 117 | insights_client.run("--status", check=False) 118 | assert os.path.samefile(os.devnull, MOTD_PATH) 119 | 120 | insights_client.register() 121 | assert conftest.loop_until(lambda: insights_client.is_registered) 122 | assert os.path.samefile(os.devnull, MOTD_PATH) 123 | 124 | insights_client.unregister() 125 | assert os.path.samefile(os.devnull, MOTD_PATH) 126 | 127 | 128 | @pytest.mark.usefixtures("delete_special_files") 129 | @pytest.mark.tier1 130 | def test_motd_message(): 131 | """ 132 | :id: 56d12383-f7bb-4dbe-899c-a1cbd2172a30 133 | :title: Test MOTD message content for unregistered systems 134 | :description: 135 | This test ensures that on unregistered system, the MOTD provides users 136 | with complete instructions on how to register 137 | :reference: https://issues.redhat.com/browse/CCT-264 138 | :tags: Tier 1 139 | :steps: 140 | 1. Ensure the host is unregistered 141 | 2. Read the content of the MOTD file and verify that content matches 142 | the expected message 143 | :expectedresults: 144 | 1. The system is unregistered 145 | 2. The MOTD provides correct registration instructions 146 | """ 147 | cmd = ["cat", MOTD_SRC] 148 | output = subprocess.check_output(cmd, universal_newlines=True) 149 | motd_msg = "Register this system with Red Hat Insights: rhc connect\n\n\ 150 | Example:\n\ 151 | # rhc connect --activation-key --organization \n\n\ 152 | The rhc client and Red Hat Insights will enable analytics and additional\n\ 153 | management capabilities on your system.\n\ 154 | View your connected systems at https://console.redhat.com/insights\n\n\ 155 | You can learn more about how to register your system \n\ 156 | using rhc at https://red.ht/registration\n" 157 | assert output == motd_msg 158 | -------------------------------------------------------------------------------- /integration-tests/test_obfuscation.py: -------------------------------------------------------------------------------- 1 | """ 2 | :casecomponent: insights-client 3 | :requirement: RHSS-291297 4 | :polarion-project-id: RHELSS 5 | :polarion-include-skipped: false 6 | :polarion-lookup-method: id 7 | :subsystemteam: rhel-sst-csi-client-tools 8 | :caseautomation: Automated 9 | :upstream: Yes 10 | """ 11 | 12 | import pytest 13 | import tarfile 14 | import logging 15 | import uuid 16 | import socket 17 | 18 | pytestmark = pytest.mark.usefixtures("register_subman") 19 | 20 | 21 | @pytest.mark.tier1 22 | def test_ip_obfuscation(insights_client, tmp_path): 23 | """ 24 | :id: 59c073f7-d207-42ae-85ee-a7a5d6fc6f8d 25 | :title: Test IP obfuscation functionality 26 | :description: 27 | This test verifies that when IP obfuscation is enabled in the 28 | insights-client configuration, the system's IP address is not 29 | present in the collected archive 30 | :tags: Tier 1 31 | :steps: 32 | 1. Record the system's IP address 33 | 2. Disable obfuscation in the configuration 34 | 3. Run the insights-client and check archive for an IP address 35 | 4. Enable obfuscation in the configuration 36 | 5. Run the insights-client and check archive for an IP address 37 | :expectedresults: 38 | 1. The system's IP address is recorded 39 | 2. Configuration is updated and saved 40 | 3. The IP address is found in the collected archive 41 | 4. Configuration is updated and saved 42 | 5. The IP address is not found in the collected archive 43 | """ 44 | # Record the system ip 45 | hostname = socket.gethostname() 46 | system_ip = socket.gethostbyname(hostname) 47 | 48 | # Check the ip in archive before obfuscating ip 49 | insights_client.config.obfuscate = False 50 | insights_client.config.save() 51 | assert check_obfuscated_info(insights_client, tmp_path, system_ip) 52 | 53 | # Obfuscate hostname: obfuscate=True 54 | insights_client.config.obfuscate = True 55 | insights_client.config.save() 56 | 57 | # Check the ip in archive after obfuscating ip 58 | assert not check_obfuscated_info(insights_client, tmp_path, system_ip) 59 | 60 | 61 | @pytest.mark.tier1 62 | def test_hostname_obfuscation(insights_client, tmp_path): 63 | """ 64 | :id: 68196220-f633-4a40-8040-6924d0e71b46 65 | :title: Test hostname obfuscation functionality 66 | :description: 67 | This test verifies that when hostname obfuscation is enabled in the 68 | insights-client configuration, the system's hostname is not present 69 | in the collected archive 70 | :tags: Tier 1 71 | :steps: 72 | 1. Record the system's hostname 73 | 2. Disable hostname obfuscation in the configuration 74 | 3. Run the insights-client and check archive for a hostname 75 | 4. Enable hostname obfuscation in the configuration 76 | 5. Run the insights-client and check archive for a hostname 77 | :expectedresults: 78 | 1. The system's hostname is recorded 79 | 2. Configuration is updated and saved 80 | 3. The hostname is found in the collected archive 81 | 4. Configuration is updated and saved 82 | 5. The hostname is not found in the collected archive 83 | """ 84 | # Record the system hostname 85 | system_hostname = socket.gethostname() 86 | 87 | # Check the hostname in archive before obfuscating hostname 88 | insights_client.config.obfuscate = False 89 | insights_client.config.obfuscate_hostname = False 90 | insights_client.config.save() 91 | assert check_obfuscated_info(insights_client, tmp_path, system_hostname) 92 | 93 | # Check the hostname in archive after obfuscating hostname 94 | insights_client.config.obfuscate = True 95 | insights_client.config.obfuscate_hostname = True 96 | insights_client.config.save() 97 | assert not check_obfuscated_info(insights_client, tmp_path, system_hostname) 98 | 99 | 100 | @pytest.mark.parametrize("password_file", ["/etc/redhat-release", "/etc/hosts"]) 101 | @pytest.mark.tier1 102 | def test_password_obfuscation(insights_client, tmp_path, password_file): 103 | """ 104 | :id: ad3f22b2-8792-45fb-abdd-d29d58db5c41 105 | :title: Test password obfuscation in collected files 106 | :parametrized: yes 107 | :description: 108 | This test ensures that sensitive information such as passwords is obfuscated 109 | in collected files, regardless of the obfuscation setting in the configuration 110 | file 111 | :tags: Tier 1 112 | :steps: 113 | 1. Backup the original content of the test file 114 | 2. Append a password string to the test file 115 | 3. Make sure obfuscation is disabled in the configuration file 116 | 4. Run the insights-client and check for the password in the archive 117 | 5. Enable obfuscation 118 | 6. Run the insights-client and check for the password in the archive 119 | 7. Restore the original content of the test file 120 | :expectedresults: 121 | 1. Original content is backed up 122 | 2. Password string is added without errors 123 | 3. Configuration is updated and saved 124 | 4. Password is not found in the test file 125 | 5. Configuration is updated and saved 126 | 6. Password is still not present in the test file 127 | 7. Original content is restored 128 | """ 129 | # backup the original content of tested files 130 | with open(password_file, "r+") as f: 131 | original_content = f.read() 132 | try: 133 | # Add a line about password in a tested file 134 | password_string = "my-super-secret-password" 135 | with open(password_file, "a") as file: 136 | file.write(f"\npassword: {password_string}") 137 | 138 | # Make sure obfuscate=False 139 | insights_client.config.obfuscate = False 140 | insights_client.config.save() 141 | 142 | # the password value is not visible although obfuscate=False 143 | assert not check_obfuscated_info(insights_client, tmp_path, password_string) 144 | 145 | # the password value is not visible after Obfuscating IP 146 | insights_client.config.obfuscate = True 147 | insights_client.config.save() 148 | assert not check_obfuscated_info(insights_client, tmp_path, password_string) 149 | finally: 150 | with open(password_file, "w+") as f: 151 | f.write(original_content) 152 | 153 | 154 | @pytest.mark.parametrize( 155 | "package_info_file", 156 | [ 157 | "data/insights_commands/rpm_-qa_--qf", 158 | "data/insights_commands/yum_-C_--noplugins_list_available", 159 | "data/insights_commands/yum_updates_list", 160 | "data/etc/dnf/modules.d/", 161 | ], 162 | ) 163 | @pytest.mark.tier1 164 | def test_no_obfuscation_on_package_version( 165 | insights_client, tmp_path, package_info_file 166 | ): 167 | """ 168 | :id: aa2eb4cf-9fed-4fe9-8423-87bbf2f2dd95 169 | :title: Test package versions are not obfuscated when obfuscation is enabled 170 | :parametrized: yes 171 | :description: 172 | This test ensures that version strings in package information files 173 | are not incorrectly obfuscated as IP addresses when obfuscation is 174 | enabled 175 | :reference: https://issues.redhat.com/browse/ESSNTL-444 176 | :tags: Tier 1 177 | :steps: 178 | 1. Enable obfuscation in the configuration file 179 | 2. Run the insights-client and collect the archive 180 | 3. Check the specified package information files in the archive 181 | :expectedresults: 182 | 1. Configuration is updated and saved 183 | 2. Archive is collected 184 | 3. Version strings are not obfuscated and are present 185 | """ 186 | # Obfuscate IP 187 | insights_client.config.obfuscate = True 188 | insights_client.config.save() 189 | 190 | # Check if the package version is obfuscated by IP obfuscation 191 | archive_name = "test_archive_" + str(uuid.uuid4()) + ".tar.gz" 192 | archive_location = tmp_path / archive_name 193 | insights_client.run("--register", "--output-file=%s" % archive_location) 194 | with tarfile.open(archive_location, "r") as tar: 195 | for w_file in tar.getmembers(): 196 | if w_file.name.find(package_info_file) != -1: 197 | file_content = tar.extractfile(w_file).read() 198 | assert "10.230.230" not in file_content.decode() 199 | 200 | 201 | @pytest.mark.tier1 202 | def test_no_obfuscation_on_display_name(insights_client, tmp_path): 203 | """ 204 | :id: a5b73cba-928d-4e78-9792-6a667e5c4c2b 205 | :title: Test display_name is not obfuscated when obfuscation is enabled 206 | :description: 207 | This test ensures that display_name in package information files 208 | are not incorrectly obfuscated as IP addresses when obfuscation is 209 | enabled 210 | :tags: Tier 1 211 | :steps: 212 | 1. Enable obfuscation in the configuration file 213 | 2. Run the insights-client and collect the archive 214 | 3. Check the display_name information in the archive 215 | :expectedresults: 216 | 1. Configuration is updated and saved 217 | 2. Archive is collected 218 | 3. Display_name is not obfuscated and is present 219 | """ 220 | # Set IP obfuscation and set display_name in insights-client.conf 221 | display_name_setting = "test_display_name" + str(uuid.uuid4()) 222 | insights_client.config.obfuscate = True 223 | insights_client.config.display_name = display_name_setting 224 | insights_client.config.save() 225 | 226 | # Check the display_name in the archive 227 | archive_name = "test_archive_" + str(uuid.uuid4()) + ".tar.gz" 228 | archive_location = tmp_path / archive_name 229 | insights_client.run("--register", "--output-file=%s" % archive_location) 230 | with tarfile.open(archive_location, "r") as tar: 231 | for w_file in tar.getmembers(): 232 | if w_file.name.endswith("display_name"): 233 | file_content = tar.extractfile(w_file).read().decode() 234 | assert display_name_setting in file_content 235 | 236 | 237 | def check_obfuscated_info(insights_client, tmp_path, info_to_search): 238 | archive_name = "test_archive_" + str(uuid.uuid4()) + ".tar.gz" 239 | archive_location = tmp_path / archive_name 240 | logging.info(archive_location) 241 | insights_client.run("--output-file=%s" % archive_location) 242 | info_exist = [] 243 | with tarfile.open(archive_location, "r") as tar: 244 | for w_file in tar.getmembers(): 245 | extracted_file = tar.extractfile(w_file) 246 | if ( 247 | extracted_file is not None 248 | and info_to_search in extracted_file.read().decode() 249 | ): 250 | info_exist += [w_file.name] 251 | logging.info(info_exist) 252 | return info_exist 253 | -------------------------------------------------------------------------------- /integration-tests/test_redaction.py: -------------------------------------------------------------------------------- 1 | """ 2 | :casecomponent: insights-client 3 | :requirement: RHSS-291297 4 | :polarion-project-id: RHELSS 5 | :polarion-include-skipped: false 6 | :polarion-lookup-method: id 7 | :subsystemteam: rhel-sst-csi-client-tools 8 | :caseautomation: Automated 9 | :upstream: Yes 10 | """ 11 | 12 | import pytest 13 | import subprocess 14 | import tarfile 15 | import os 16 | import yaml 17 | import uuid 18 | from enum import Enum 19 | 20 | pytestmark = pytest.mark.usefixtures("register_subman") 21 | TEST_CMD = ["/usr/bin/uptime", "/sbin/lsmod", "/bin/ls", "/sbin/ethtool"] 22 | TEST_FILE = ["/proc/cpuinfo", "/proc/meminfo", "/etc/os-release"] 23 | FILE_REDACTION_FILE = "/etc/insights-client/file-redaction.yaml" 24 | CONTENT_REDACTION_FILE = "/etc/insights-client/file-content-redaction.yaml" 25 | 26 | 27 | @pytest.mark.parametrize("not_removed_command", TEST_CMD) 28 | def test_redaction_not_on_cmd(insights_client, tmp_path, not_removed_command): 29 | """ 30 | :id: 264d1d8f-47a5-49ce-800c-d349aaacdb01 31 | :title: Test commands are collected when redaction not configured 32 | :parametrized: yes 33 | :description: 34 | This test verifies that when no commands are configured for redaction in 35 | `/etc/insights-client/file-redaction.yaml`, the outputs of the related 36 | commands are included in the collection archive 37 | :tags: Tier 1 38 | :steps: 39 | 1. Ensure no command redaction is configured 40 | 2. Run the insights client to collect data 41 | 3. Check the archive for the specified command output 42 | :expectedresults: 43 | 1. No commands are specified for redaction in the configuration file 44 | 2. Data collection completes successfully 45 | 3. The output of the command is present in the archive 46 | """ 47 | assert check_archive(insights_client, tmp_path, not_removed_command) 48 | 49 | 50 | @pytest.mark.parametrize("removed_command", TEST_CMD) 51 | def test_redaction_on_cmd(insights_client, tmp_path, removed_command): 52 | """ 53 | :id: a2d7b71c-205d-4545-8a6b-b0be9ff57611 54 | :title: Test commands are redacted when configured 55 | :parametrized: yes 56 | :description: 57 | This test verifies that when commands are configured for redaction in 58 | `/etc/insights-client/file-redaction.yaml`, the outputs of the related 59 | commands are excluded from the collection archive 60 | :tags: Tier 1 61 | :steps: 62 | 1. Configure command redaction for the specified command 63 | 2. Run the insights client to collect data 64 | 3. Check the archive for the specified command output 65 | 4. Clean up by removing the redaction configuration file 66 | :expectedresults: 67 | 1. The command is added to the redaction configuration successfully 68 | 2. Data collection completes successfully 69 | 3. The output of the command is not present in the archive 70 | 4. Configuration file is removed successfully 71 | """ 72 | try: 73 | configure_file_redaction("Command", removed_command) 74 | assert not check_archive(insights_client, tmp_path, removed_command) 75 | finally: 76 | os.remove(FILE_REDACTION_FILE) 77 | 78 | 79 | @pytest.mark.parametrize("not_removed_file", TEST_FILE) 80 | def test_redaction_not_on_file(insights_client, tmp_path, not_removed_file): 81 | """ 82 | :id: cb2ee8b8-fd82-48ad-bebc-3e044f277c55 83 | :title: Test files are collected when redaction not configured 84 | :parametrized: yes 85 | :description: 86 | This test verifies that when no files are configured for redaction in 87 | `/etc/insights-client/file-redaction.yaml`, the related files are 88 | included in the collection archive 89 | :tags: Tier 1 90 | :steps: 91 | 1. Ensure no file redaction is configured 92 | 2. Run the insights client to collect data 93 | 3. Check the archive for the specified file 94 | :expectedresults: 95 | 1. No files are specified for redaction in the configuration file 96 | 2. Data collection completes successfully 97 | 3. The file is present in the archive 98 | """ 99 | assert check_archive(insights_client, tmp_path, not_removed_file) 100 | 101 | 102 | @pytest.mark.parametrize("removed_file", TEST_FILE) 103 | @pytest.mark.tier1 104 | def test_redaction_on_file(insights_client, tmp_path, removed_file): 105 | """ 106 | :id: 849cc4ac-d45e-44b8-b307-797935085eda 107 | :title: Test files are redacted when configured 108 | :parametrized: yes 109 | :description: 110 | This test verifies that when files are configured for redaction in 111 | `/etc/insights-client/file-redaction.yaml`, the related files are 112 | excluded from the collection archive 113 | :tags: Tier 1 114 | :steps: 115 | 1. Configure file redaction for the specified file 116 | 2. Run the insights client to collect data 117 | 3. Check the archive for the specified file 118 | 4. Clean up by removing the redaction configuration file 119 | :expectedresults: 120 | 1. The file is added to the redaction configuration successfully 121 | 2. Data collection completes successfully 122 | 3. The file is not present in the archive 123 | 4. Configuration file is removed successfully 124 | """ 125 | try: 126 | configure_file_redaction("File", removed_file) 127 | assert not check_archive(insights_client, tmp_path, removed_file) 128 | finally: 129 | os.remove(FILE_REDACTION_FILE) 130 | 131 | 132 | @pytest.mark.skipif( 133 | "container" in os.environ.keys(), 134 | reason="Containers cannot change hostnames", 135 | ) 136 | @pytest.mark.tier1 137 | def test_redaction_on_pattern_hostname(insights_client, tmp_path): 138 | """ 139 | :id: 641edf11-ace1-4a98-9fb4-198cf9e5e4d0 140 | :title: Test hostname is redacted when pattern is configured 141 | :description: 142 | This test verifies that when a pattern matching the system's 143 | hostname is configured for content redaction in 144 | `/etc/insights-client/content-redaction.yaml`, the hostname is 145 | obfuscated in the collected data 146 | :reference: https://issues.redhat.com/browse/RHEL-2471 147 | :tags: Tier 1 148 | :steps: 149 | 1. Record the current system hostname 150 | 2. Set a new hostname for testing 151 | 3. Configure content redaction for the test hostname 152 | 4. Run the insights client and collect data 153 | 5. Check the archive to verify the hostname is redacted 154 | 6. Restore the original hostname and clean up 155 | :expectedresults: 156 | 1. The current hostname is recorded successfully 157 | 2. The system hostname is updated to the test hostname 158 | 3. The test hostname is added to the content redaction configuration 159 | 4. Data collection completes successfully 160 | 5. The test hostname is not present in the collected data 161 | 6. Original hostname is restored, and configuration files are removed 162 | """ 163 | # Record the current hostname 164 | with open("/etc/hostname", "r") as f: 165 | previous_hostname = f.read().strip() 166 | 167 | try: 168 | # Set a new hostname 169 | hostname_to_test = "insights-client-host" 170 | set_hostname(hostname_to_test) 171 | cmd = ["hostnamectl", "status"] 172 | output = subprocess.check_output(cmd, universal_newlines=True) 173 | assert hostname_to_test in output 174 | 175 | # redaction on hostname: 176 | configure_content_redaction(hostname_to_test) 177 | 178 | # check the collection, if redaction pattern is filtered out 179 | archive_name = "test_archive_" + str(uuid.uuid4()) + ".tar.gz" 180 | archive_location = tmp_path / archive_name 181 | insights_client.run("--register", "--output-file=%s" % archive_location) 182 | with tarfile.open(archive_location, "r") as tar: 183 | for w_file in tar.getmembers(): 184 | extracted_file = tar.extractfile(w_file) 185 | if extracted_file is not None: 186 | file_content = extracted_file.read().decode() 187 | assert hostname_to_test not in file_content 188 | 189 | finally: 190 | set_hostname(previous_hostname) 191 | os.remove(CONTENT_REDACTION_FILE) 192 | 193 | 194 | def check_archive(insights_client, tmp_path, tested): 195 | archive_location = tmp_path / "test_archive.tar.gz" 196 | insights_client.run("--output-file=%s" % archive_location) 197 | check_file = "/" + tested.split("/")[-1] 198 | if check_file == "/ls": 199 | check_file = "/ls_" 200 | with tarfile.open(archive_location, "r") as tar: 201 | check_tested = [ 202 | file_name for file_name in tar.getnames() if check_file in file_name 203 | ] 204 | return check_tested 205 | 206 | 207 | def configure_file_redaction(type, redaction_string): 208 | redaction_type = Enum("redaction_type", ["Command", "File"]) 209 | if type == redaction_type.Command.name: 210 | file_content = {"commands": [redaction_string]} 211 | elif type == redaction_type.File.name: 212 | file_content = {"files": [redaction_string]} 213 | with open(FILE_REDACTION_FILE, "w") as f: 214 | yaml.dump(file_content, f, default_flow_style=False) 215 | os.chmod(FILE_REDACTION_FILE, 0o600) 216 | 217 | 218 | def configure_content_redaction(pattern_string): 219 | file_content = {"patterns": {"regex": [pattern_string]}} 220 | with open(CONTENT_REDACTION_FILE, "w") as f: 221 | yaml.dump(file_content, f, default_flow_style=False) 222 | os.chmod(CONTENT_REDACTION_FILE, 0o600) 223 | 224 | 225 | def set_hostname(hostname_to_set): 226 | cmd = ["hostnamectl", "set-hostname", hostname_to_set] 227 | subprocess.check_call(cmd) 228 | -------------------------------------------------------------------------------- /integration-tests/test_status.py: -------------------------------------------------------------------------------- 1 | """ 2 | :casecomponent: insights-client 3 | :requirement: RHSS-291297 4 | :polarion-project-id: RHELSS 5 | :polarion-include-skipped: false 6 | :polarion-lookup-method: id 7 | :subsystemteam: rhel-sst-csi-client-tools 8 | :caseautomation: Automated 9 | :upstream: Yes 10 | """ 11 | 12 | import conftest 13 | from constants import REGISTERED_FILE, UNREGISTERED_FILE, MACHINE_ID_FILE 14 | import contextlib 15 | import os 16 | import pytest 17 | from pytest_client_tools.util import Version 18 | from time import sleep 19 | 20 | pytestmark = pytest.mark.usefixtures("register_subman") 21 | 22 | 23 | @pytest.mark.tier1 24 | def test_status_registered(external_candlepin, insights_client): 25 | """ 26 | :id: 624b01fc-e841-4c26-afd8-bb28eaf7fe75 27 | :title: Test insights-client --status when registered 28 | :description: 29 | This test verifies that when the insights client is registered, the 30 | `insights-client --status` command outputs the correct registration status 31 | :tags: Tier 1 32 | :steps: 33 | 1. Register the insights-client 34 | 2. Wait briefly to ensure inventory is up-to-date 35 | 3. Run `insights-client --status` command 36 | :expectedresults: 37 | 1. The client registers successfully 38 | 2. Wait time completes without issues 39 | 3. If 'legacy_upload' is True, output contains "Insights API confirms 40 | registration." If 'legacy_upload' is False, output is "This host 41 | is registered." 42 | """ 43 | insights_client.register() 44 | assert conftest.loop_until(lambda: insights_client.is_registered) 45 | # Adding a small wait to ensure inventory is up-to-date 46 | sleep(5) 47 | registration_status = insights_client.run("--status") 48 | if insights_client.config.legacy_upload: 49 | assert "Insights API confirms registration." in registration_status.stdout 50 | else: 51 | assert "This host is registered.\n" == registration_status.stdout 52 | 53 | 54 | @pytest.mark.tier1 55 | def test_status_registered_only_locally( 56 | external_candlepin, insights_client, external_inventory 57 | ): 58 | """ 59 | :id: 2ca3be87-8322-47b8-b451-9ea7fa3dbeef 60 | :title: Test insights-client --status when registered only locally 61 | :description: 62 | This test verifies that when the insights client is registered only 63 | locally, the `insights-client --status` command outputs the correct 64 | registration status 65 | :tags: Tier 1 66 | :steps: 67 | 1. Set the legacy_upload to False 68 | 2. Register the insights-client 69 | 3. Delete the host from the Inventory 70 | 4. Run `insights-client --status` command 71 | :expectedresults: 72 | 1. The client registers successfully 73 | 2. Wait time completes without issues 74 | 3. The host is deleted from the Inventory 75 | 4. On systems with version 3.5.7 and higher, output is "This host is 76 | registered.", the registered file exists, the unregistered file 77 | does not exist, and the machine ID file exists. Otherwise, output 78 | is "This host is unregistered." 79 | """ 80 | insights_client.config.legacy_upload = False 81 | insights_client.register() 82 | assert conftest.loop_until(lambda: insights_client.is_registered) 83 | machine_id = insights_client.uuid 84 | external_inventory.delete(path=f"hosts/{external_inventory.this_system()['id']}") 85 | response = external_inventory.get(path=f"hosts?insights_id={machine_id}") 86 | assert response.json()["total"] == 0 87 | 88 | registration_status = insights_client.run("--status", check=False) 89 | if insights_client.core_version >= Version(3, 5, 7): 90 | assert "This host is registered.\n" == registration_status.stdout 91 | assert os.path.exists(REGISTERED_FILE) 92 | assert not os.path.exists(UNREGISTERED_FILE) 93 | assert os.path.exists(MACHINE_ID_FILE) 94 | else: 95 | assert "This host is unregistered.\n" == registration_status.stdout 96 | 97 | 98 | @pytest.mark.tier1 99 | def test_status_unregistered(external_candlepin, insights_client): 100 | """ 101 | :id: aa37831a-a581-44db-a7c9-de8161767c7e 102 | :title: Test insights-client --status when unregistered 103 | :description: 104 | This test verifies that when the insights client is unregistered, the 105 | `insights-client --status` command outputs the correct unregistration 106 | status 107 | :tags: Tier 1 108 | :steps: 109 | 1. Unregister the insights client to ensure it's unregistered 110 | 2. Run `insights-client --status` command 111 | :expectedresults: 112 | 1. The client unregisters successfully 113 | 2. If 'legacy_upload' is True, return code is 1 and output contains 114 | "Insights API says this machine is NOT registered." 115 | If 'legacy_upload' is False, on systems with version 3.5.3 and 116 | higher, return code is 1 and output contains "This host is 117 | unregistered.". Otherwise return code is 0 and output contains 118 | "This host is unregistered." 119 | """ 120 | # running unregistration to ensure system is unregistered 121 | with contextlib.suppress(Exception): 122 | insights_client.unregister() 123 | assert conftest.loop_until(lambda: not insights_client.is_registered) 124 | 125 | registration_status = insights_client.run("--status", check=False) 126 | if insights_client.config.legacy_upload: 127 | assert registration_status.returncode == 1 128 | assert ( 129 | "Insights API says this machine is NOT registered." 130 | in registration_status.stdout 131 | ) 132 | else: 133 | if insights_client.core_version >= Version(3, 5, 3): 134 | assert registration_status.returncode == 1 135 | else: 136 | assert registration_status.returncode == 0 137 | assert "This host is unregistered.\n" == registration_status.stdout 138 | -------------------------------------------------------------------------------- /integration-tests/test_tags.py: -------------------------------------------------------------------------------- 1 | """ 2 | :casecomponent: insights-client 3 | :requirement: RHSS-291297 4 | :polarion-project-id: RHELSS 5 | :polarion-include-skipped: false 6 | :polarion-lookup-method: id 7 | :subsystemteam: rhel-sst-csi-client-tools 8 | :caseautomation: Automated 9 | :upstream: Yes 10 | """ 11 | 12 | import pytest 13 | import conftest 14 | import contextlib 15 | import os 16 | import yaml 17 | from constants import TAGS_FILE 18 | from time import sleep 19 | 20 | pytestmark = pytest.mark.usefixtures("register_subman") 21 | 22 | 23 | @pytest.mark.tier1 24 | def test_tags(insights_client, external_inventory, test_config): 25 | """ 26 | :id: 3e9d5b76-7065-4ade-8397-5854a8fef83b 27 | :title: Test tags 28 | :description: 29 | Test how the tags generated, and check the tags on inventory 30 | :tags: Tier 1 31 | :steps: 32 | 1. Register insights-client, and check satellite related tags 33 | on the inventory if the test env is satellte. 34 | 2. Run insights-client with the --group 35 | 3. Add a new tag in tags.yaml, and run insights-client, 36 | then check the inventory 37 | :expectedresults: 38 | 1. System is registered to insights, and there will be 39 | satellite related tags supported by branch_info with 40 | satellite env. 41 | 2. tags.yaml will be created with group option, and new tag 42 | generated by tags.yaml 43 | 3. The new tag shows on inventory by modifying tags.yaml 44 | """ 45 | # Remove the tags.yaml if it exists 46 | with contextlib.suppress(FileNotFoundError): 47 | TAGS_FILE.unlink() 48 | 49 | # Register insights 50 | insights_client.register() 51 | assert conftest.loop_until(lambda: insights_client.is_registered) 52 | 53 | # When test env is satellite, check the tags from branch_info 54 | # the tags from satellite are not generated by tags.yaml 55 | if "satellite" in test_config.environment: 56 | insights_client.run() 57 | system_tags = external_inventory.this_system_tags() 58 | for tag in system_tags: 59 | assert tag["namespace"] == "satellite" 60 | assert not TAGS_FILE.exists() 61 | 62 | with contextlib.ExitStack() as stack: 63 | # Run insights-client --group 64 | insights_client.run("--group=first_tag") 65 | stack.callback(os.remove, TAGS_FILE) 66 | assert TAGS_FILE.exists() 67 | with TAGS_FILE.open("r") as tags_yaml: 68 | data_loaded = yaml.safe_load(tags_yaml) 69 | assert data_loaded["group"] == "first_tag" 70 | 71 | # Check new tag from inventory 72 | sleep(30) 73 | system_tags = external_inventory.this_system_tags() 74 | assert { 75 | "namespace": "insights-client", 76 | "key": "group", 77 | "value": "first_tag", 78 | } in system_tags 79 | 80 | # Add new tag in tags.yaml file and check on inventory 81 | with TAGS_FILE.open("r") as tags_yaml: 82 | data_loaded = yaml.safe_load(tags_yaml) 83 | data_loaded["add_by_file"] = "second_tag" 84 | with TAGS_FILE.open("w") as tags_yaml: 85 | yaml.dump(data_loaded, tags_yaml, default_flow_style=False) 86 | insights_client.run() 87 | sleep(60) 88 | system_tags = external_inventory.this_system_tags() 89 | assert { 90 | "namespace": "insights-client", 91 | "key": "add_by_file", 92 | "value": "second_tag", 93 | } in system_tags 94 | -------------------------------------------------------------------------------- /integration-tests/test_unregister.py: -------------------------------------------------------------------------------- 1 | """ 2 | :casecomponent: insights-client 3 | :requirement: RHSS-291297 4 | :polarion-project-id: RHELSS 5 | :polarion-include-skipped: false 6 | :polarion-lookup-method: id 7 | :subsystemteam: rhel-sst-csi-client-tools 8 | :caseautomation: Automated 9 | :upstream: Yes 10 | """ 11 | 12 | import pytest 13 | from pytest_client_tools.util import Version 14 | import conftest 15 | 16 | pytestmark = pytest.mark.usefixtures("register_subman") 17 | 18 | 19 | @pytest.mark.tier1 20 | def test_unregister(insights_client): 21 | """ 22 | :id: ecaeeddc-4c8b-4f17-8d69-1c81d2c7c744 23 | :title: Test unregister 24 | :description: 25 | This test verifies that the insights-client can be unregistered 26 | successfully after being registered. 27 | :tags: Tier 1 28 | :steps: 29 | 1. Register the insights-client if not registered 30 | 2. Run `insights-client --unregister` command 31 | 3. Confirm the client is unregistered 32 | :expectedresults: 33 | 1. The client registers successfully 34 | 2. On systems with Insights Core version >= 3.5.11, the command outputs 35 | "Successfully unregistered this host." Otherwise, the command 36 | outputs "Successfully unregistered from the Red Hat Insights Service" 37 | 3. Client unregistration is confirmed 38 | """ 39 | insights_client.register() 40 | assert conftest.loop_until(lambda: insights_client.is_registered) 41 | 42 | unregistration_status = insights_client.run("--unregister") 43 | if insights_client.core_version >= Version(3, 5, 11): 44 | assert "Successfully unregistered this host." in unregistration_status.stdout 45 | else: 46 | assert ( 47 | "Successfully unregistered from the Red Hat Insights Service" 48 | in unregistration_status.stdout 49 | ) 50 | assert conftest.loop_until(lambda: not insights_client.is_registered) 51 | 52 | 53 | @pytest.mark.tier1 54 | def test_unregister_twice(insights_client): 55 | """ 56 | :id: bfff1b33-5f19-42d2-a6ff-4598975873e5 57 | :title: Test unregister already unregistered system 58 | :description: 59 | This test verifies that attempting to unregister the insights client 60 | when it is already unregistered behaves as expected. It checks that 61 | the first unregistration succeeds and that subsequent unregistration 62 | attempts produce the appropriate error message and return code 63 | :tags: Tier 1 64 | :steps: 65 | 1. Register the insights-client 66 | 2. Unregister the client for the first time 67 | 3. Attempt to unregister the client a second time 68 | :expectedresults: 69 | 1. The client registers successfully 70 | 2. On systems with Insights Core version >= 3.5.11, the command outputs 71 | "Successfully unregistered this host." Otherwise, the command 72 | outputs "Successfully unregistered from the Red Hat Insights Service" 73 | 3. Command returns exit code 1 and outputs "This host is not registered, 74 | unregistration is not applicable." 75 | """ 76 | insights_client.register() 77 | assert conftest.loop_until(lambda: insights_client.is_registered) 78 | 79 | # unregister once 80 | unregistration_status = insights_client.run("--unregister") 81 | assert conftest.loop_until(lambda: not insights_client.is_registered) 82 | if insights_client.core_version >= Version(3, 5, 11): 83 | assert "Successfully unregistered this host." in unregistration_status.stdout 84 | else: 85 | assert ( 86 | "Successfully unregistered from the Red Hat Insights Service" 87 | in unregistration_status.stdout 88 | ) 89 | 90 | # unregister twice 91 | unregistration_status = insights_client.run("--unregister", check=False) 92 | assert conftest.loop_until(lambda: not insights_client.is_registered) 93 | assert unregistration_status.returncode == 1 94 | assert ( 95 | "This host is not registered, unregistration is not applicable." 96 | in unregistration_status.stdout 97 | ) 98 | -------------------------------------------------------------------------------- /integration-tests/test_upload.py: -------------------------------------------------------------------------------- 1 | """ 2 | :casecomponent: insights-client 3 | :requirement: RHSS-291297 4 | :polarion-project-id: RHELSS 5 | :polarion-include-skipped: false 6 | :polarion-lookup-method: id 7 | :subsystemteam: rhel-sst-csi-client-tools 8 | :caseautomation: Automated 9 | :upstream: Yes 10 | """ 11 | 12 | import os 13 | import pytest 14 | import conftest 15 | 16 | pytestmark = pytest.mark.usefixtures("register_subman") 17 | 18 | 19 | @pytest.mark.tier1 20 | def test_upload_pre_collected_archive(insights_client, tmp_path): 21 | """ 22 | :id: 9eba5a67-d013-4d43-98c7-c41ed38bcede 23 | :title: Test Upload of Pre-Collected Archive 24 | :description: 25 | This test verifies that a pre-collect insights-archive 26 | can be uploaded using --payload operation. 27 | :tags: Tier 1 28 | :steps: 29 | 1. Register insights-client 30 | 2. Run insights-client in an offline mode to generate an archive 31 | and save it 32 | 3. Run the insights-client with the --payload option and valid --content-type 33 | 4. Verify the successful upload of the archive 34 | :expectedresults: 35 | 1. Insights-client is registered 36 | 2. The archive is successfully generated and saved 37 | 3. The upload process starts and the output message is as expected 38 | 4. The upload completes successfully with the message as expected 39 | """ 40 | archive_name = "archive.tar.gz" 41 | archive_location = tmp_path / archive_name 42 | 43 | # Registering the client because upload can happen on registered system 44 | insights_client.register() 45 | assert conftest.loop_until(lambda: insights_client.is_registered) 46 | 47 | # Running insights-client in offline mode to generate archive and save at tmp dir 48 | insights_client.run(f"--output-file={archive_location}") 49 | 50 | # Running insights-client --payload with --content-type to upload archive 51 | # collected in previous step 52 | upload_result = insights_client.run( 53 | f"--payload={archive_location}", "--content-type=gz" 54 | ) 55 | assert "Uploading Insights data." in upload_result.stdout 56 | assert "Successfully uploaded report" in upload_result.stdout 57 | 58 | 59 | @pytest.mark.tier1 60 | def test_upload_wrong_content_type(insights_client, tmp_path): 61 | """ 62 | :id: bb9ee84a-d262-4c42-ae16-9b45bf5a385c 63 | :title: Test Upload with Wrong Content Type 64 | :description: 65 | This test verifies that uploading an archive with wrong content 66 | type throws appropriate error message. Generate an archive and upload using 67 | --payload but wrong --content-type 68 | :tags: Tier 1 69 | :steps: 70 | 1. Register insights-client 71 | 2. Run the insights-client in offline mode to generate an archive and save it 72 | 3. Run the insights-client with --payload option and invalid --content-type 73 | 4. Run the insigts-client with a valid --content-type but different from 74 | compressor used 75 | :expectedresults: 76 | 1. Insights-client is registered 77 | 2. The archive is generated and saved 78 | 3. The upload process fails with the appropriate message 79 | 4. The upload process fails with the appropriate message 80 | """ 81 | archive_name = "archive.tar.gz" 82 | archive_location = tmp_path / archive_name 83 | 84 | # Registering the client because upload can happen on registered system 85 | insights_client.register() 86 | assert conftest.loop_until(lambda: insights_client.is_registered) 87 | 88 | # Running insights-client in offline mode to generate archive and save at tmp dir 89 | insights_client.run(f"--output-file={archive_location}") 90 | 91 | # Running insights-client --payload with invalid --content-type to upload archive 92 | # collected in previous step 93 | upload_result = insights_client.run( 94 | f"--payload={archive_location}", "--content-type=bzip", check=False 95 | ) 96 | assert "Invalid content-type." in upload_result.stdout 97 | 98 | # trying to upload with a valid content type but different from compressor 99 | upload_result = insights_client.run( 100 | f"--payload={archive_location}", "--content-type=xz", check=False 101 | ) 102 | assert "Content type different from compression" in upload_result.stdout 103 | 104 | 105 | @pytest.mark.tier1 106 | def test_upload_too_large_archive(insights_client, tmp_path): 107 | """ 108 | :id: bb9ee84a-d262-4c42-ae16-9b45bf5a385c 109 | :title: Test Upload of Too Large Archive 110 | :description: 111 | This test verifies that an attempt to upload too large archive 112 | results in failure 113 | :tags: Tier 1 114 | :steps: 115 | 1. Register insights-client 116 | 2. Create a large archive file in the temporary directory 117 | 3. Run insights-client with --payload option and --content-type 118 | pointing to the archive 119 | :expectedresults: 120 | 1. Insights-client is registered 121 | 2. A large archive is created in the temporary directory 122 | 3. The upload process fails with an appropriate message 123 | """ 124 | insights_client.register() 125 | assert conftest.loop_until(lambda: insights_client.is_registered) 126 | 127 | file_path = tmp_path / "large_file.tar.gz" 128 | file_size = 100 * 1024 * 1024 # 100mb 129 | with open(file_path, "wb") as f: 130 | f.seek(int(file_size) + 1) 131 | f.write(b"\0") 132 | 133 | upload_result = insights_client.run( 134 | f"--payload={file_path}", "--content-type=gz", check=False 135 | ) 136 | 137 | assert "Archive is too large to upload" in upload_result.stdout 138 | assert "Upload failed." in upload_result.stdout 139 | 140 | 141 | @pytest.mark.parametrize( 142 | "compressor,expected_extension", 143 | [ 144 | ("gz", ".gz"), 145 | ("bz2", ".bz2"), 146 | ("xz", ".xz"), 147 | ], 148 | ) 149 | @pytest.mark.tier1 150 | def test_upload_compressor_options( 151 | insights_client, 152 | compressor, 153 | expected_extension, 154 | ): 155 | """ 156 | :id: 69a06826-6093-46de-a7a6-9726ae141820 157 | :title: Test upload with Different Compressor Options 158 | :parametrized: yes 159 | :description: 160 | This test verifies that valid compression types can be used 161 | with --compressor to create archives and upload data using --payload 162 | :tags: Tier 1 163 | :steps: 164 | 1. Register insights-client 165 | 2. Run insights-client with --compressor option to generate an archive 166 | with specified type 167 | 3. Verify the archive has the correct file extension based on the compression 168 | type 169 | :expectedresults: 170 | 1. Insights-client is registered 171 | 2. The archive is successfully generated 172 | 3. The file has expected file extension 173 | """ 174 | insights_client.register() 175 | assert conftest.loop_until(lambda: insights_client.is_registered) 176 | 177 | # using --compressor option to generate and save archive 178 | command_result = insights_client.run(f"--compressor={compressor}", "--no-upload") 179 | archive_name = command_result.stdout.split()[-1] 180 | 181 | # Verifying that archive is created with expected extension 182 | assert os.path.splitext(archive_name)[1] == expected_extension 183 | assert (os.path.splitext(archive_name)[0]).endswith(".tar") 184 | 185 | # Now try to upload the pre-collected archive 186 | upload_result = insights_client.run( 187 | f"--payload={archive_name}", f"--content-type={compressor}" 188 | ) 189 | assert "Uploading Insights data." in upload_result.stdout 190 | assert "Successfully uploaded report" in upload_result.stdout 191 | 192 | 193 | @pytest.mark.tier1 194 | def test_retries(insights_client): 195 | """ 196 | :id: dafeb86e-463e-42fd-88e5-4551f1ba8f66 197 | :title: Test Retries on Upload Failure 198 | :description: 199 | This test verifies that client tries to upload archive if upload 200 | fails. Setting retries to 2 only because between each attempt the wait time is 201 | 180 sec. Set wrong base_url in insights-client.config to fail upload operation 202 | :tags: Tier 1 203 | :steps: 204 | 1. Register insights-client 205 | 2. Save the archive 206 | 3. Modify the configuration to use a non-existent base URL 207 | 4. Run insights-client with --payload option specifying --retry=2 208 | to attempt upload twice 209 | 5. verify the retry mechanism 210 | 6. verify the final failure message 211 | :expectedresults: 212 | 1. Insights-client is registered 213 | 2. Archive is saved 214 | 3. The configuration is modified and saved 215 | 4. The command is run 216 | 5. Each of the retry failed with expected error message 217 | 6. the final error message is as expected 218 | """ 219 | reg_result = insights_client.run("--register", "--keep-archive") 220 | assert conftest.loop_until(lambda: insights_client.is_registered) 221 | 222 | # Save the archive, to be used while upload operation 223 | archive_name = reg_result.stdout.split()[-1] 224 | 225 | # Modifying config to break connection 226 | insights_client.config.auto_config = False 227 | insights_client.config.base_url = "non-existent-url.redhat.com" 228 | insights_client.config.save() 229 | 230 | # Now try to upload the pre-collected archive with retry=2 , default content=type gz 231 | upload_result = insights_client.run( 232 | f"--payload={archive_name}", "--content-type={gz}", "--retry=2", check=False 233 | ) 234 | 235 | assert "Upload attempt 1 of 2 failed" in upload_result.stdout 236 | assert "Upload attempt 2 of 2 failed" in upload_result.stdout 237 | assert "Waiting 180 seconds then retrying" in upload_result.stdout 238 | assert "All attempts to upload have failed!" in upload_result.stdout 239 | 240 | 241 | @pytest.mark.tier1 242 | def test_retries_not_happening_on_unrecoverable_errors(insights_client): 243 | """ 244 | :id: 1d740d1c-e98b-4571-86ac-10a233ff65ce 245 | :title: Test No Retries on Uncoverable Errors 246 | :description: 247 | This test verifies that client retries won't happen during 248 | unrecoverable errors. The client should try to upload just once and then fail. 249 | :tags: Tier 1 250 | :steps: 251 | 1. Register insights-client 252 | 2. Save the archive 253 | 3. Run insights-client with --payload option specifying invalid --content-type 254 | 4. Verify the output of the command 255 | 5. Verify no-retries occur 256 | :expectedresults: 257 | 1. Insights-client is registered 258 | 2. Archive is saved 259 | 3. The command is run 260 | 4. The process fails with an appropriate message 261 | 5. No retries occurred 262 | """ 263 | reg_result = insights_client.run("--register", "--keep-archive") 264 | assert conftest.loop_until(lambda: insights_client.is_registered) 265 | 266 | # Save the archive, to be used while upload operation 267 | archive_name = reg_result.stdout.split()[-1] 268 | 269 | # Pass invalid content type to mock unrecoverable errors 270 | upload_result = insights_client.run( 271 | f"--payload={archive_name}", 272 | "--content-type=invalid-type", 273 | "--retry=2", 274 | check=False, 275 | ) 276 | assert "Upload failed." in upload_result.stdout 277 | assert "Upload attempt 1 of 2 failed" not in upload_result.stdout 278 | -------------------------------------------------------------------------------- /integration-tests/test_version.py: -------------------------------------------------------------------------------- 1 | """ 2 | :casecomponent: insights-client 3 | :requirement: RHSS-291297 4 | :polarion-project-id: RHELSS 5 | :polarion-include-skipped: false 6 | :polarion-lookup-method: id 7 | :subsystemteam: rhel-sst-csi-client-tools 8 | :caseautomation: Automated 9 | :upstream: Yes 10 | """ 11 | 12 | import pytest 13 | 14 | 15 | @pytest.mark.tier1 16 | def test_version(insights_client): 17 | """ 18 | :id: 7ec671cb-39ae-4cda-b279-f05d7c835d5d 19 | :title: Test --version outputs client and core versions 20 | :description: 21 | This test verifies that running `insights-client --version` outputs 22 | both the client and core version information 23 | :tags: Tier 1 24 | :steps: 25 | 1. Run `insights-client --version` 26 | 2. Check the output for "Client: " and "Core: " 27 | :expectedresults: 28 | 1. Command executes without errors 29 | 2. Both "Client: " and "Core: " are present in the output 30 | """ 31 | proc = insights_client.run("--version") 32 | assert "Client: " in proc.stdout 33 | assert "Core: " in proc.stdout 34 | -------------------------------------------------------------------------------- /integration-tests/testimony.yml: -------------------------------------------------------------------------------- 1 | --- 2 | Id: 3 | casesensitive: false 4 | required: true 5 | type: string 6 | Polarion-Project-Id: 7 | casesensitive: false 8 | required: true 9 | type: choice 10 | choices: 11 | - RHELSS 12 | Polarion-Include-Skipped: 13 | casesensitive: false 14 | required: true 15 | type: string 16 | Polarion-Lookup-Method: 17 | casesensitive: false 18 | required: true 19 | type: string 20 | Reference: 21 | casesensitive: false 22 | required: false 23 | type: string 24 | CaseComponent: 25 | casesensitive: false 26 | required: true 27 | type: string 28 | Requirement: 29 | casesensitive: false 30 | required: true 31 | type: choice 32 | choices: 33 | - RHSS-291297 34 | SubSystemTeam: 35 | casesensitive: false 36 | required: true 37 | type: choice 38 | choices: 39 | - rhel-sst-csi-client-tools 40 | Parametrized: 41 | casesensitive: false 42 | required: false 43 | type: string 44 | CaseAutomation: 45 | casesensitive: false 46 | required: true 47 | type: string 48 | Upstream: 49 | casesensitive: false 50 | type: string 51 | Title: 52 | casesensitive: false 53 | type: string 54 | Description: 55 | casesensitive: false 56 | required: true 57 | type: string 58 | Tags: 59 | casesensitive: true 60 | required: true 61 | type: choice 62 | choices: 63 | - Tier 1 64 | - Tier 2 65 | - Tier 3 66 | Steps: {} 67 | ExpectedResults: {} 68 | -------------------------------------------------------------------------------- /meson.build: -------------------------------------------------------------------------------- 1 | project('insights-client', 2 | version: '3.10.1', 3 | meson_version: '>=0.49' 4 | ) 5 | 6 | python = import('python') 7 | 8 | python_installation = python.find_installation(get_option('python')) 9 | 10 | python_exec = '/usr/bin/python3' 11 | 12 | systemd = dependency('systemd', version: '>=231') 13 | 14 | config_data = configuration_data({ 15 | 'bindir': get_option('prefix') / get_option('bindir'), 16 | 'BINDIR': get_option('prefix') / get_option('bindir'), 17 | 'DATADIR': get_option('prefix') / get_option('datadir'), 18 | 'DATAROOTDIR':get_option('prefix') / get_option('datadir'), 19 | 'DOCDIR': get_option('prefix') / get_option('datadir') / 'doc' / meson.project_name(), 20 | 'LIBEXECDIR': get_option('prefix') / get_option('libexecdir'), 21 | 'LOCALSTATEDIR': get_option('localstatedir'), 22 | 'PACKAGE': meson.project_name(), 23 | 'PACKAGE_VERSION': meson.project_version(), 24 | 'pkgsysconfdir': '/' / get_option('sysconfdir') / meson.project_name(), 25 | 'PREFIX': get_option('prefix'), 26 | 'PYTHON': python_exec, 27 | 'pythondir': python_installation.get_install_dir(), 28 | 'SBINDIR': get_option('prefix') / get_option('sbindir'), 29 | 'SYSCONFDIR': '/' / get_option('sysconfdir'), 30 | 'sysconfdir': '/' / get_option('sysconfdir'), 31 | 'top_srcdir': meson.source_root(), 32 | 'CORE_SELINUX_POLICY': get_option('core_selinux_policy'), 33 | }) 34 | 35 | run_target('update-egg', command: 'scripts/01-upgrade-egg.sh') 36 | 37 | subdir('data') 38 | subdir('docs') 39 | subdir('src') 40 | 41 | configuration = '**Configuration**\n' 42 | configuration += '\tpython\t\t\t: ' + get_option('python') + '\n' 43 | if get_option('checkin').enabled() 44 | configuration += '\tcheckin\t: ' + 'enabled' + '\n' 45 | else 46 | configuration += '\tcheckin\t: ' + 'disabled' + '\n' 47 | endif 48 | if get_option('auto_registration').enabled() 49 | configuration += '\tauto_registration\t: ' + 'enabled' + '\n' 50 | else 51 | configuration += '\tauto_registration\t: ' + 'disabled' + '\n' 52 | endif 53 | if get_option('core_selinux_policy') != '' 54 | configuration += '\tSELinux policy for insights-core\t: ' + get_option('core_selinux_policy') + '\n' 55 | else 56 | configuration += '\tSELinux policy for insights-core\t: ' + 'disabled' + '\n' 57 | endif 58 | message(configuration) 59 | -------------------------------------------------------------------------------- /meson_options.txt: -------------------------------------------------------------------------------- 1 | option('python', type: 'string', value: 'python3', description: 'python interpreter to use when finding python installation') 2 | option('auto_registration', type: 'feature', value: 'disabled', description: 'enable automatic registration') 3 | option('checkin', type: 'feature', value: 'disabled', description: 'enable hourly check-in') 4 | option('redhat_access_insights', type: 'boolean', value: false, description: 'enable deprecated redhat-access-insights executable') 5 | option('core_selinux_policy', type: 'string', value: '', description: 'SELinux policy for insights-core (empty: none used)') 6 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.black] 2 | include = '(\.py(\.in)?|src/insights-client.in)$' 3 | -------------------------------------------------------------------------------- /pytest.ini: -------------------------------------------------------------------------------- 1 | [pytest] 2 | markers = 3 | tier1: Mark a test to be Tier 1 4 | tier2: Mark a test to be Tier 2 5 | tier3: Mark a test to be Tier 3 6 | -------------------------------------------------------------------------------- /requirements-dev.txt: -------------------------------------------------------------------------------- 1 | # the version of black is specified also in the stylish.yml github workflow; 2 | # please update the version there in case it is bumped here 3 | black==24.3.0 4 | flake8 5 | -------------------------------------------------------------------------------- /scripts/01-upgrade-egg.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -xe 4 | 5 | cd "${MESON_SOURCE_ROOT}" 6 | 7 | declare -a PROGRAMS 8 | PROGRAMS+=( 9 | curl 10 | ) 11 | 12 | for PROGRAM in "${PROGRAMS[@]}"; do 13 | if ! which "${PROGRAM}"; then 14 | echo "missing required program: ${PROGRAM}" 15 | exit 1 16 | fi 17 | done 18 | 19 | TAG=$(cat EGG_VERSION) 20 | 21 | curl --fail -L https://console.redhat.com/api/v1/static/release/insights-core.el10.egg --output data/rpm.egg 22 | curl --fail -L https://console.redhat.com/api/v1/static/release/insights-core.el10.egg.asc --output data/rpm.egg.asc 23 | -------------------------------------------------------------------------------- /scripts/README.md: -------------------------------------------------------------------------------- 1 | ## 01-upgrade-egg.sh 2 | 3 | This script downloads the egg and the uploader.json (and the ones that are signed) that is defined in `EGG_VERSION` file. -------------------------------------------------------------------------------- /src/insights-client.in: -------------------------------------------------------------------------------- 1 | #!@PYTHON@ 2 | 3 | import sys 4 | 5 | from insights_client import _main 6 | 7 | sys.path.insert(1, "@pythondir@") 8 | 9 | 10 | if __name__ == "__main__": 11 | _main() 12 | -------------------------------------------------------------------------------- /src/insights_client/constants.py.in: -------------------------------------------------------------------------------- 1 | """ 2 | Constants 3 | """ 4 | 5 | APP_NAME = "@PACKAGE@" 6 | VERSION = "@PACKAGE_VERSION@" 7 | PREFIX = "@PREFIX@" 8 | BINDIR = "@BINDIR@" 9 | SBINDIR = "@SBINDIR@" 10 | LIBEXECDIR = "@LIBEXECDIR@" 11 | DATAROOTDIR = "@DATAROOTDIR@" 12 | DATADIR = "@DATADIR@" 13 | SYSCONFDIR = "@SYSCONFDIR@" 14 | LOCALSTATEDIR = "@LOCALSTATEDIR@" 15 | DOCDIR = "@DOCDIR@" 16 | CORE_SELINUX_POLICY = "@CORE_SELINUX_POLICY@" 17 | 18 | 19 | class InsightsConstants(object): 20 | app_name = APP_NAME 21 | version = VERSION 22 | -------------------------------------------------------------------------------- /src/insights_client/meson.build: -------------------------------------------------------------------------------- 1 | insights_client_sources = [ 2 | '__init__.py', 3 | 'run.py', 4 | 'utc.py' 5 | ] 6 | 7 | insights_client_sources += configure_file( 8 | input: 'constants.py.in', 9 | output: '@BASENAME@', 10 | configuration: config_data 11 | ) 12 | 13 | python_installation.install_sources(insights_client_sources, subdir: 'insights_client') 14 | 15 | subdir('tests') 16 | -------------------------------------------------------------------------------- /src/insights_client/run.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | 4 | import logging 5 | 6 | logger = logging.getLogger(__name__) 7 | 8 | try: 9 | try: 10 | from insights.client.phase import v1 as client 11 | except ImportError as e: 12 | sys.exit( 13 | "Error importing insights.client for %s as %s: %s" 14 | % (os.environ["INSIGHTS_PHASE"], os.environ["PYTHONPATH"], e) 15 | ) 16 | 17 | phase = getattr(client, os.environ["INSIGHTS_PHASE"]) 18 | sys.exit(phase()) 19 | except KeyboardInterrupt: 20 | sys.exit(1) 21 | except Exception as e: 22 | print("Fatal: {0}".format(e)) 23 | sys.exit(1) 24 | -------------------------------------------------------------------------------- /src/insights_client/tests/conftest.py: -------------------------------------------------------------------------------- 1 | import os 2 | import pathlib 3 | import sys 4 | 5 | 6 | def pytest_configure(config): 7 | repo_root = pathlib.Path(__file__).parents[3] 8 | 9 | # Hijack sys.path, so we don't have to use 'PYTHONPATH=src/' 10 | sources: pathlib.Path = repo_root / "src" 11 | sys.path.insert(0, str(sources)) 12 | 13 | # Point us to the RPM egg, if it hasn't been specified outside 14 | if "EGG" not in os.environ: 15 | os.environ["EGG"] = str((repo_root / "data" / "rpm.egg").resolve()) 16 | 17 | # We have to push the EGG into PATH immediately. 18 | # It would have been done by the client on its own, but we need to mock out 19 | # some of the internal functions (see `test_client.py`), and for that we need 20 | # the egg to already be present. 21 | sys.path.insert(0, os.environ["EGG"]) 22 | 23 | # Move the temporary directories outside /var/lib/insights/. 24 | import insights_client 25 | 26 | insights_client.TEMPORARY_GPG_HOME_PARENT_DIRECTORY = "/tmp/" 27 | # Point to the actual key we ship to be able to actually verify the bundled egg. 28 | insights_client.GPG_KEY = str( 29 | (repo_root / "data" / "redhattools.pub.gpg").resolve() 30 | ) 31 | -------------------------------------------------------------------------------- /src/insights_client/tests/meson.build: -------------------------------------------------------------------------------- 1 | pytest = find_program('pytest', 'pytest-3') 2 | 3 | env = environment() 4 | env.append('PYTHONPATH', meson.source_root() / 'src', meson.source_root() / 'data' / 'rpm.egg') 5 | 6 | test_sources = files('test_client.py', 'test_commands.py', 'test_sed.py') 7 | 8 | test('test', 9 | pytest, 10 | args: test_sources, 11 | env: env 12 | ) 13 | -------------------------------------------------------------------------------- /src/insights_client/tests/requirements-core.txt: -------------------------------------------------------------------------------- 1 | pyyaml 2 | requests 3 | setuptools 4 | six 5 | -------------------------------------------------------------------------------- /src/insights_client/tests/requirements.txt: -------------------------------------------------------------------------------- 1 | pytest 2 | # Setuptools is required because we still rely on 'distutils' which have been dropped in Python 3.12. 3 | # setuptools package ships it vendorized as a top-level package, so for a time being we can rely on it. 4 | setuptools 5 | -r requirements-core.txt 6 | -------------------------------------------------------------------------------- /src/insights_client/tests/test_client.py: -------------------------------------------------------------------------------- 1 | from unittest import mock 2 | import insights_client 3 | import pytest 4 | 5 | 6 | # Test config load error 7 | @mock.patch("os.getuid", return_value=0) 8 | @mock.patch( 9 | "insights.client.InsightsConfig.load_all", side_effect=ValueError("mocked error") 10 | ) 11 | def test_load_config_error(os_uid, insightsConfig): 12 | with pytest.raises(SystemExit) as sys_exit: 13 | insights_client._main() 14 | assert sys_exit.value.code != 0 15 | 16 | 17 | # test keyboardinterrupt handler 18 | @mock.patch("os.getuid", return_value=0) 19 | @mock.patch("insights.client.InsightsConfig.load_all", side_effect=KeyboardInterrupt) 20 | def test_keyboard_interrupt(os_uid, client): 21 | with pytest.raises(SystemExit) as sys_exit: 22 | insights_client._main() 23 | assert sys_exit.value.code != 0 24 | 25 | 26 | # check run phase error 100 handler 27 | @mock.patch("os.getuid", return_value=0) 28 | @mock.patch("insights.client.phase.v1.get_phases") 29 | @mock.patch("insights.client.InsightsClient") 30 | @mock.patch("insights_client.subprocess.Popen") 31 | def test_phase_error_100(mock_subprocess, client, phase, _os_getuid): 32 | client.get_conf = mock.Mock(return_value={"gpg": False}) 33 | 34 | with pytest.raises(SystemExit) as sys_exit: 35 | mock_subprocess.return_value.returncode = 100 36 | mock_subprocess.return_value.communicate.return_value = ("output", "error") 37 | insights_client.run_phase(phase, client, validated_eggs=[]) 38 | assert sys_exit.value.code == 0 39 | -------------------------------------------------------------------------------- /src/insights_client/tests/test_commands.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import insights_client 4 | from unittest.mock import patch 5 | from pytest import raises 6 | 7 | 8 | @patch("insights_client.sys.argv", ["insights-client", "--version"]) 9 | @patch("insights_client._main") 10 | def test_version_command(capsys): 11 | with patch("os.getuid", return_value=0): 12 | insights_client._main() 13 | captured = capsys.readouterr() 14 | output_sudo = captured.out 15 | with patch("os.getuid", return_value=1): 16 | insights_client._main() 17 | captured = capsys.readouterr() 18 | output_normal = captured.out 19 | 20 | assert output_sudo == output_normal 21 | 22 | 23 | @patch("insights_client.sys.argv", ["insights-client", "--help"]) 24 | @patch("insights_client._main") 25 | def test_help_command(capsys): 26 | with patch("os.getuid", return_value=0): 27 | insights_client._main() 28 | captured = capsys.readouterr() 29 | output_sudo = captured.out 30 | with patch("os.getuid", return_value=1): 31 | insights_client._main() 32 | captured = capsys.readouterr() 33 | output_normal = captured.out 34 | 35 | assert output_sudo == output_normal 36 | 37 | 38 | @patch("insights_client.sys.argv", ["insights-client"]) 39 | def test_exit_when_run_phases_no_sudo(): 40 | with raises(SystemExit) as pytest_wrapped_e: 41 | with patch("os.getuid", return_value=1): 42 | insights_client._main() 43 | assert pytest_wrapped_e.type == SystemExit 44 | assert pytest_wrapped_e.value.args[0] == "Insights client must be run as root." 45 | -------------------------------------------------------------------------------- /src/insights_client/tests/test_motd.py: -------------------------------------------------------------------------------- 1 | import os 2 | import pathlib 3 | import tempfile 4 | import typing 5 | import unittest 6 | import unittest.mock 7 | 8 | import pytest 9 | 10 | import insights_client 11 | 12 | 13 | class MockFS: 14 | """Mock filesystem. 15 | 16 | Ensures that the files required for the MOTD manipulation are created 17 | in a temporary directory instead of working with actual system files. 18 | """ 19 | 20 | _dir: tempfile.TemporaryDirectory 21 | 22 | @property 23 | def _chroot(self) -> pathlib.Path: 24 | return pathlib.Path(self._dir.name) 25 | 26 | def touch(self, path: str): 27 | (self._chroot / path).open("w").close() 28 | 29 | def exists(self, path: str) -> bool: 30 | return (self._chroot / path).exists() 31 | 32 | def symlink_to(self, path: str, point_to: str): 33 | (self._chroot / path).symlink_to(point_to) 34 | 35 | def samefile(self, first: str, second: str) -> bool: 36 | return (self._chroot / first).samefile(self._chroot / second) 37 | 38 | def __init__(self): 39 | self._dir = tempfile.TemporaryDirectory(prefix="test-chroot-") 40 | 41 | paths: typing.Dict[str, str] = { 42 | "insights_client.MOTD_SRC": "etc/insights-client/insights-client.motd", 43 | "insights_client.MOTD_FILE": "etc/motd.d/insights-client", 44 | "insights_client.REGISTERED_FILE": "etc/insights-client/.registered", 45 | "insights_client.UNREGISTERED_FILE": "etc/insights-client/.unregistered", 46 | } 47 | 48 | self.patches: typing.List[unittest.mock.patch] = [] 49 | for target, path in paths.items(): 50 | mocked_path = self._chroot / path 51 | mocked_path.parent.mkdir(parents=True, exist_ok=True) 52 | patch = unittest.mock.patch(target, f"{mocked_path!s}") 53 | self.patches.append(patch) 54 | patch.start() 55 | 56 | self.touch("etc/insights-client/insights-client.motd") 57 | 58 | def __del__(self): 59 | for patch in self.patches: 60 | patch.stop() 61 | self._dir.cleanup() 62 | 63 | 64 | @pytest.fixture(scope="function") 65 | def mock_fs(): 66 | fs = MockFS() 67 | yield fs 68 | del fs 69 | 70 | 71 | def test_present(mock_fs): 72 | assert not mock_fs.exists("etc/motd.d/insights-client") 73 | 74 | # The file gets created/symlinked when .registered & .unregistered do not exist 75 | insights_client.update_motd_message() 76 | assert mock_fs.exists("etc/motd.d/insights-client") 77 | assert mock_fs.samefile( 78 | "etc/motd.d/insights-client", "etc/insights-client/insights-client.motd" 79 | ) 80 | 81 | 82 | @pytest.mark.parametrize( 83 | "filename", 84 | [".registered", ".unregistered"], 85 | ) 86 | def test_absent_on_dot(mock_fs, filename): 87 | # The MOTD gets created by default... 88 | insights_client.update_motd_message() 89 | assert mock_fs.exists("etc/motd.d/insights-client") 90 | 91 | # ...and gets removed when .registered/.unregistered exists. 92 | mock_fs.touch(f"etc/insights-client/{filename}") 93 | 94 | insights_client.update_motd_message() 95 | assert not mock_fs.exists("etc/motd.d/insights-client") 96 | 97 | # ...and it stays absent when run multiple times. 98 | insights_client.update_motd_message() 99 | assert not mock_fs.exists("etc/motd.d/insights-client") 100 | 101 | 102 | def test_ignored_on_dev_null(mock_fs): 103 | # When the /etc/motd.d/insights-client is a symbolic link to /dev/null... 104 | mock_fs.symlink_to("etc/motd.d/insights-client", os.devnull) 105 | 106 | # ...it should not be overwritten... 107 | insights_client.update_motd_message() 108 | assert mock_fs.samefile("etc/motd.d/insights-client", os.devnull) 109 | 110 | # ...whether the MOTD file should be present or not. 111 | mock_fs.touch("etc/insights-client/.registered") 112 | insights_client.update_motd_message() 113 | assert mock_fs.samefile("etc/motd.d/insights-client", os.devnull) 114 | -------------------------------------------------------------------------------- /src/insights_client/tests/test_sed.py: -------------------------------------------------------------------------------- 1 | import pathlib 2 | import subprocess 3 | 4 | import pytest 5 | 6 | 7 | _REPO_ROOT = pathlib.Path(__file__).parents[3] 8 | SED_FILE: pathlib.Path = _REPO_ROOT / "data" / ".exp.sed" 9 | 10 | 11 | def run_sed(stdin: str) -> str: 12 | """Obfuscate input with `sed` script file.""" 13 | process = subprocess.Popen( 14 | ["sed", "-rf", str(SED_FILE)], 15 | stdin=subprocess.PIPE, 16 | stdout=subprocess.PIPE, 17 | stderr=subprocess.PIPE, 18 | env={"LC_ALL": "C.UTF-8"}, 19 | universal_newlines=True, 20 | ) 21 | stdout, _ = process.communicate(input=stdin) 22 | return stdout 23 | 24 | 25 | @pytest.mark.parametrize( 26 | ["stdin", "obfuscated"], 27 | [ 28 | ["password=root", "password=********"], 29 | ["password=p4ssw0rd", "password=********"], 30 | ["password=I!m_strong1S7arQ4ST$2bu/QurvRCjrTGWYajIMx/", "password=********"], 31 | ["password=!p@4#$$w%O^r&d*p(a)s-s_w+o=r/d", "password=********"], 32 | ["password_verification=root", "password_verification=********"], 33 | ["password == root", "password == ********"], 34 | ["password root", "password ********"], 35 | ["password --md555 4facade5cafe", "password --md555 ********"], 36 | ["password--md5 4facade5cafe", "password--md5 ********"], 37 | ["password--sha1", "password********"], 38 | [" (abc=def&password=root&key=value )", " (abc=def&password=******** )"], 39 | ["password: root", "password: ********"], 40 | ['{auth: {password: "root"}}', '{auth: {password: "********"}}'], 41 | ['', ''], 42 | ], 43 | ) 44 | def test_fully_obfuscate(stdin, obfuscated): 45 | stdout = run_sed(stdin) 46 | assert stdout == obfuscated 47 | 48 | 49 | @pytest.mark.parametrize( 50 | ["stdin", "obfuscated"], 51 | [ 52 | ["password=pass word", "password=******** word"], 53 | ["password=pass,word", "password=********,word"], 54 | ["password=pass.word", "password=********.word"], 55 | ["password=pass[word]", "password=********[word]"], 56 | ["password=pass{word}", "password=********{word}"], 57 | ["password=pass", "password=********"], 58 | ["password=pas/sw\\ord", "password=********\\ord"], 59 | ["password=passwörd", "password=********örd"], 60 | ["password=passw٥rd", "password=********٥rd"], 61 | ], 62 | ) 63 | def test_partially_obfuscate(stdin, obfuscated): 64 | stdout = run_sed(stdin) 65 | assert stdout == obfuscated 66 | 67 | 68 | @pytest.mark.parametrize( 69 | ["stdin", "obfuscated"], 70 | [ 71 | ["password ******** root", "password ******** ********"], 72 | ["password******** root", "password******** ********"], 73 | ], 74 | ) 75 | def test_obfuscate_rest(stdin, obfuscated): 76 | stdout = run_sed(stdin) 77 | assert stdout == obfuscated 78 | 79 | 80 | @pytest.mark.parametrize( 81 | ["stdin", "obfuscated"], 82 | [ 83 | ["password * root", "password ******** ********"], 84 | ["password*** root", "password******** ********"], 85 | ["password --sha1 4facade5cafe", "password ******** ********"], 86 | ["password--sha1 4facade5cafe", "password******** ********"], 87 | ], 88 | ) 89 | def test_joint_obfuscation(stdin, obfuscated): 90 | stdout = run_sed(stdin) 91 | assert stdout == obfuscated 92 | 93 | 94 | @pytest.mark.parametrize(["stdin"], [["PermitRootLogin without-password"]]) 95 | def test_no_obfuscation(stdin): 96 | stdout = run_sed(stdin) 97 | assert stdout == stdin 98 | -------------------------------------------------------------------------------- /src/insights_client/utc.py: -------------------------------------------------------------------------------- 1 | import datetime 2 | 3 | 4 | class UTC(datetime.tzinfo): 5 | """ 6 | UTC is a concrete subclass of datetime.tzinfo representing the UTC 7 | time zone. 8 | """ 9 | 10 | def utcoffset(self, dt): 11 | return datetime.timedelta(0) 12 | 13 | def tzname(self, dt): 14 | return "UTC" 15 | 16 | def dst(self, dt): 17 | return datetime.timedelta(0) 18 | 19 | 20 | def make_utc_datetime_rfc3339(): 21 | return ( 22 | datetime.datetime.utcnow().replace(microsecond=0, tzinfo=UTC()).isoformat("T") 23 | ) 24 | -------------------------------------------------------------------------------- /src/meson.build: -------------------------------------------------------------------------------- 1 | insights_client_sources = [ 2 | 'insights-client.in', 3 | ] 4 | 5 | if get_option('redhat_access_insights') 6 | insights_client_sources += 'redhat-access-insights.in' 7 | endif 8 | 9 | foreach source : insights_client_sources 10 | configure_file( 11 | input: source, 12 | output: '@BASENAME@', 13 | configuration: config_data, 14 | install_dir: get_option('bindir'), 15 | install_mode: 'rwxr-xr-x' 16 | ) 17 | endforeach 18 | 19 | subdir('insights_client') 20 | -------------------------------------------------------------------------------- /src/redhat-access-insights.in: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | >&2 echo "WARNING: $(basename "$0") is deprecated and will be removed in a future release; use 'insights-client' instead." 4 | sleep 3 5 | exec @bindir@/insights-client "$@" 6 | -------------------------------------------------------------------------------- /systemtest/copr-setup.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/bash -eux 2 | dnf install -y dnf-plugins-core 3 | 4 | #Determine the repo needed from copr 5 | #Available repositories: 'centos-stream-8-x86_64', 'rhel-8-x86_64', 6 | # 'centos-stream-9-x86_64', 'rhel-9-x86_64', 'fedora-40-x86_64', 7 | # 'fedora-39-x86_64', 'fedora-rawhide-x86_64' 8 | source /etc/os-release 9 | if [ "$ID" == "centos" ]; then 10 | ID='centos-stream' 11 | fi 12 | VERSION_MAJOR=$(echo ${VERSION_ID} | cut -d '.' -f 1) 13 | COPR_REPO="${ID}-${VERSION_MAJOR}-$(uname -m)" 14 | 15 | #get yggdrasil 16 | dnf copr -y enable @yggdrasil/latest ${COPR_REPO} 17 | dnf install -y yggdrasil yggdrasil-worker-package-manager --disablerepo=* --enablerepo=*yggdrasil* 18 | 19 | # These PR packit builds have an older version number for some reason than the released... 20 | dnf remove -y --noautoremove insights-client 21 | dnf copr -y enable packit/RedHatInsights-insights-client-${ghprbPullId} ${COPR_REPO} 22 | dnf install -y insights-client --disablerepo=* --enablerepo=*insights-client* 23 | -------------------------------------------------------------------------------- /systemtest/guest-setup.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/bash 2 | # this is a general use version of the guest setup that happens in testing farm 3 | 4 | # check for insights-client from existing repos 5 | if ! dnf info insights-client &>/dev/null; then 6 | source <(cat /etc/os-release | grep ^ID) 7 | 8 | # convert os-release to copr name 9 | if [ "$ID" == "centos" ]; then 10 | DISTRO='centos-stream' 11 | else 12 | DISTRO="$ID" 13 | fi 14 | 15 | # have to pull from dnf as os-release does not follow the same format on rhel 16 | RELEASEVER=$(python3 -c 'import dnf, json; db = dnf.dnf.Base(); print(db.conf.substitutions["releasever"])') 17 | 18 | curl https://copr.fedorainfracloud.org/coprs/g/yggdrasil/latest/repo/$DISTRO-$RELEASEVER/group_yggdrasil-latest-$DISTRO-$RELEASEVER.repo \ 19 | -o /etc/yum.repos.d/yggdrasil.repo 20 | fi 21 | 22 | dnf -y install insights-client 23 | -------------------------------------------------------------------------------- /systemtest/insights-core-setup.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/bash -ux 2 | 3 | # Stolen from 4 | # https://github.com/RedHatInsights/insights-core/blob/master/insights/tests/client/test_crypto.py 5 | # https://gitlab.cee.redhat.com/mhorky/tasks/-/blob/main/CCT-131/steps.md#prepare-the-vm 6 | 7 | cat >public.armor.key << EOF 8 | -----BEGIN PGP PUBLIC KEY BLOCK----- 9 | 10 | mDMEZpbJQRYJKwYBBAHaRw8BAQdA3HBHO0ADUcKgqaTbj6BUU82ZbJ5ojhTdju7b 11 | 9+NGtEm0GkNDVCBRRSA8Y2N0LXFlQHJlZGhhdC5jb20+iJMEExYKADsWIQQbHWyM 12 | LBEKAqrK4N73Fp/8VshPuwUCZpbJQQIbAwULCQgHAgIiAgYVCgkICwIEFgIDAQIe 13 | BwIXgAAKCRD3Fp/8VshPu3qhAQDWOV+kGpIGdLBRqIuQZCFAH5rS/6HW7J16CuS5 14 | qub0ZgEAvSiIanOj1TaGHnYOyHXi0694Amfvr3Txzligo62OwwW4OARmlslBEgor 15 | BgEEAZdVAQUBAQdAbCvp9o2XYSmtQweCmRBKcYUUPfzkoENeB+AehOxiMl0DAQgH 16 | iHgEGBYKACAWIQQbHWyMLBEKAqrK4N73Fp/8VshPuwUCZpbJQQIbDAAKCRD3Fp/8 17 | VshPu+vWAQCW0w/EIsPMPnlecizvNf3pXydze5XotIr4XFrJaB465AD8CVn5JDN2 18 | 5lg1O5u16Ww3WAaT01FCealgq+NnG/03IAI= 19 | =xnEk 20 | -----END PGP PUBLIC KEY BLOCK----- 21 | EOF 22 | 23 | cat >private.armor.key << EOF 24 | -----BEGIN PGP PRIVATE KEY BLOCK----- 25 | 26 | lFgEZpbJQRYJKwYBBAHaRw8BAQdA3HBHO0ADUcKgqaTbj6BUU82ZbJ5ojhTdju7b 27 | 9+NGtEkAAQDKnXHYfXWHpRQiNTPU8mqVcOg3M7VlZPxMgcEJH4bi2BBktBpDQ1Qg 28 | UUUgPGNjdC1xZUByZWRoYXQuY29tPoiTBBMWCgA7FiEEGx1sjCwRCgKqyuDe9xaf 29 | /FbIT7sFAmaWyUECGwMFCwkIBwICIgIGFQoJCAsCBBYCAwECHgcCF4AACgkQ9xaf 30 | /FbIT7t6oQEA1jlfpBqSBnSwUaiLkGQhQB+a0v+h1uydegrkuarm9GYBAL0oiGpz 31 | o9U2hh52Dsh14tOveAJn76908c5YoKOtjsMFnF0EZpbJQRIKKwYBBAGXVQEFAQEH 32 | QGwr6faNl2EprUMHgpkQSnGFFD385KBDXgfgHoTsYjJdAwEIBwAA/0lTtr1yPIFe 33 | +3xHwOEaA9K3Iss8unb7v8jAPTIUnRoYEI2IeAQYFgoAIBYhBBsdbIwsEQoCqsrg 34 | 3vcWn/xWyE+7BQJmlslBAhsMAAoJEPcWn/xWyE+769YBAJbTD8Qiw8w+eV5yLO81 35 | /elfJ3N7lei0ivhcWsloHjrkAPwJWfkkM3bmWDU7m7XpbDdYBpPTUUJ5qWCr42cb 36 | /TcgAg== 37 | =YrcZ 38 | -----END PGP PRIVATE KEY BLOCK----- 39 | EOF 40 | 41 | gpg --import public.armor.key 42 | gpg --import private.armor.key 43 | KEYID=$(gpg --list-secret-keys --keyid-format long cct-qe \ 44 | | grep sec | awk '{ split($2, a, "/"); print a[2] }') 45 | gpg --output public.gpg --export $KEYID 46 | 47 | mv /etc/insights-client/redhattools.pub.gpg{,.original} 48 | mv ./public.gpg /etc/insights-client/redhattools.pub.gpg.dev 49 | mkdir temp && chmod 700 temp 50 | gpg --homedir temp --import /etc/insights-client/redhattools.pub.gpg.* 51 | gpg --homedir temp --export > /etc/insights-client/redhattools.pub.gpg 52 | rm -rf temp/ 53 | 54 | mv /etc/insights-client/rpm.egg{,.original} 55 | mv /etc/insights-client/rpm.egg.asc{,.original} 56 | 57 | currDir=$PWD 58 | cd ~/ 59 | git clone https://github.com/RedHatInsights/insights-core.git 60 | cd insights-core 61 | git switch $insightsCoreBranch 62 | 63 | # Overwrite version and release of Core, to ensure we're always newer than released versions 64 | sed -i "s/3.0.8/9.99.999/" insights/VERSION 65 | sed -i "s/dev/0/" insights/RELEASE 66 | 67 | ./build_client_egg.sh 68 | cp insights.zip last_stable.egg 69 | gpg --detach-sign -u $KEYID --armor last_stable.egg 70 | cp last_stable.egg last_stable.egg.asc /var/lib/insights/ 71 | 72 | sed -ie 's/#auto_update=True/auto_update=False/g' \ 73 | /etc/insights-client/insights-client.conf 74 | 75 | cd $currDir 76 | -------------------------------------------------------------------------------- /systemtest/plans/main.fmf: -------------------------------------------------------------------------------- 1 | summary: insights-client test suite 2 | discover: 3 | how: fmf 4 | 5 | execute: 6 | how: tmt 7 | -------------------------------------------------------------------------------- /systemtest/tests/integration/main.fmf: -------------------------------------------------------------------------------- 1 | summary: Runs tmt tests 2 | test: ./test.sh 3 | duration: 3h 4 | -------------------------------------------------------------------------------- /systemtest/tests/integration/test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -u 3 | set -x 4 | 5 | # get to project root 6 | cd ../../../ 7 | 8 | # Check for bootc/image-mode deployments which should not run dnf 9 | if ! command -v bootc >/dev/null || bootc status | grep -q 'type: null'; then 10 | # Check for GitHub pull request ID and install build if needed. 11 | # This is for the downstream PR jobs. 12 | [ -z "${ghprbPullId+x}" ] || ./systemtest/copr-setup.sh 13 | 14 | # Simulate the packit setup on downstream builds. 15 | # This is for ad-hoc and compose testing. 16 | rpm -q insights-client || ./systemtest/guest-setup.sh 17 | 18 | dnf --setopt install_weak_deps=False install -y \ 19 | podman git-core python3-pip python3-pytest logrotate bzip2 zip \ 20 | scap-security-guide openscap-scanner openscap bzip2-devel 21 | fi 22 | 23 | # If this is an insightsCore PR build and sign the new egg. 24 | [ -z "${insightsCoreBranch+x}" ] || ./systemtest/insights-core-setup.sh 25 | 26 | # Override settings if provided and available. 27 | if [ -n "${SETTINGS_URL+x}" ] && curl -I "$SETTINGS_URL" > /dev/null 2>&1; then 28 | [ -f ./settings.toml ] && mv ./settings.toml.bak 29 | curl "$SETTINGS_URL" -o ./settings.toml 30 | fi 31 | 32 | python3 -m venv venv 33 | # shellcheck disable=SC1091 34 | . venv/bin/activate 35 | 36 | pip install -r integration-tests/requirements.txt 37 | 38 | pytest --log-level debug --junit-xml=./junit.xml -v integration-tests 39 | retval=$? 40 | 41 | if [ -d "$TMT_PLAN_DATA" ]; then 42 | cp ./junit.xml "$TMT_PLAN_DATA/junit.xml" 43 | cp -r ./artifacts "$TMT_PLAN_DATA/" 44 | fi 45 | 46 | exit $retval 47 | --------------------------------------------------------------------------------