├── .dmtlint.yaml ├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.yml │ ├── config.yml │ └── feature_request.yml ├── PULL_REQUEST_TEMPLATE.md ├── check_previous_channel_release.sh └── workflows │ ├── build_dev.yml │ ├── build_prod.yml │ ├── checks.yaml │ ├── deploy_dev.yml │ ├── deploy_prod.yml │ ├── dev_registry-cleanup.yml │ ├── go_checks.yaml │ └── trivy_image_check.yaml ├── .gitignore ├── .golangci.yaml ├── .helmignore ├── .pre-commit-config.yaml ├── .werf ├── base-images.yaml ├── bundle.yaml ├── choose-edition.yaml ├── consts.yaml ├── images-digests.yaml ├── images.yaml ├── python-deps.yaml └── release.yaml ├── Chart.yaml ├── LICENSE ├── Makefile ├── api ├── LICENSE ├── README.md ├── go.mod ├── go.sum ├── tools.go └── v1alpha1 │ ├── block_device.go │ ├── const.go │ ├── lvm_logical_volume.go │ ├── lvm_logical_volume_snapshot.go │ ├── lvm_volume_group.go │ ├── lvm_volume_group_set.go │ ├── register.go │ ├── zz_generated.deepcopy.go │ └── zz_generated_lvm_logical_volume_snapshot.deepcopy.go ├── charts └── deckhouse_lib_helm-1.41.0.tgz ├── crds ├── blockdevices.yaml ├── doc-ru-blockdevices.yaml ├── doc-ru-lvmlogicalvolume.yaml ├── doc-ru-lvmvolumegroup.yaml ├── doc-ru-lvmvolumegroupbackup.yaml ├── doc-ru-lvmvolumegroupset.yaml ├── lvmlogicalvolume.yaml ├── lvmlogicalvolumesnapshot.yaml ├── lvmvolumegroup.yaml ├── lvmvolumegroupbackup.yaml └── lvmvolumegroupset.yaml ├── cspell.config.yaml ├── docs ├── CONFIGURATION.md ├── CONFIGURATION.ru.md ├── CR.md ├── CR.ru.md ├── FAQ.md ├── FAQ.ru.md ├── LAYOUTS.md ├── LAYOUTS.ru.md ├── README.md ├── README.ru.md ├── USAGE.md ├── USAGE.ru.md └── images │ ├── sds-node-configurator-scenaries.png │ ├── sds-node-configurator-scenaries.puml │ ├── sds-node-configurator-scenaries.ru.png │ └── sds-node-configurator-scenaries.ru.puml ├── hack ├── boilerplate.txt ├── cspell │ ├── companies.txt │ └── terms.txt ├── for-each-mod ├── generate_code.sh ├── increase_semver.sh └── readme.md ├── hooks └── go │ ├── 020-webhook-certs │ └── webhook-certs.go │ ├── consts │ └── consts.go │ ├── go.mod │ ├── go.sum │ └── main.go ├── images ├── agent │ ├── LICENSE │ ├── README.md │ ├── cmd │ │ ├── llvs_ce.go │ │ ├── llvs_ee.go │ │ └── main.go │ ├── go.mod │ ├── go.sum │ ├── internal │ │ ├── cache │ │ │ ├── cache.go │ │ │ └── cache_test.go │ │ ├── config │ │ │ ├── config.go │ │ │ └── config_test.go │ │ ├── const.go │ │ ├── controller │ │ │ ├── bd │ │ │ │ ├── discoverer.go │ │ │ │ ├── discoverer_suite_test.go │ │ │ │ ├── discoverer_test.go │ │ │ │ └── testdata │ │ │ │ │ └── lsblk_output.json │ │ │ ├── controller.go │ │ │ ├── llv │ │ │ │ ├── llvs_ce.go │ │ │ │ ├── llvs_ee.go │ │ │ │ ├── reconciler.go │ │ │ │ ├── reconciler_ce.go │ │ │ │ ├── reconciler_ee.go │ │ │ │ └── reconciler_test.go │ │ │ ├── llv_extender │ │ │ │ └── reconciler.go │ │ │ ├── llvs │ │ │ │ └── reconciler_ee.go │ │ │ └── lvg │ │ │ │ ├── discoverer.go │ │ │ │ ├── discoverer_test.go │ │ │ │ ├── reconciler.go │ │ │ │ ├── reconciler_test.go │ │ │ │ └── utils.go │ │ ├── kubutils │ │ │ └── kubernetes.go │ │ ├── logger │ │ │ └── logger.go │ │ ├── mock_utils │ │ │ ├── block_device.go │ │ │ ├── commands.go │ │ │ └── syscall.go │ │ ├── monitoring │ │ │ └── monitoring.go │ │ ├── scanner │ │ │ └── scanner.go │ │ ├── test_utils │ │ │ └── fake_client.go │ │ ├── throttler │ │ │ └── throttler.go │ │ ├── type.go │ │ └── utils │ │ │ ├── block_device.go │ │ │ ├── block_device_test.go │ │ │ ├── client_bd.go │ │ │ ├── client_llv.go │ │ │ ├── client_lvg.go │ │ │ ├── commands.go │ │ │ ├── commands_ee.go │ │ │ ├── commands_test.go │ │ │ ├── range_cover.go │ │ │ ├── range_cover_test.go │ │ │ ├── syscall.go │ │ │ ├── thin_dump_ee.go │ │ │ ├── thin_dump_ee_test.go │ │ │ ├── units.go │ │ │ ├── utils.go │ │ │ ├── utils_test.go │ │ │ ├── volume_cleanup_ee.go │ │ │ └── volume_cleanup_ee_test.go │ ├── tools.go │ └── werf.inc.yaml ├── go-hooks │ └── werf.inc.yaml ├── sds-health-watcher-controller │ ├── LICENSE │ ├── api │ │ ├── module_config.go │ │ ├── register.go │ │ ├── zz_generated.deepcopy.go │ │ └── zz_generated.defaults.go │ ├── cmd │ │ └── main.go │ ├── config │ │ ├── config.go │ │ └── config_test.go │ ├── go.mod │ ├── go.sum │ ├── internal │ │ └── const.go │ ├── pkg │ │ ├── controller │ │ │ ├── block_device_labels_watcher.go │ │ │ ├── block_device_labels_watcher_test.go │ │ │ ├── lvg_conditions_watcher.go │ │ │ ├── lvg_conditions_watcher_funcs.go │ │ │ ├── lvg_conditions_watcher_test.go │ │ │ ├── lvg_status_watcher.go │ │ │ ├── lvm_volume_group_set_watcher.go │ │ │ ├── mc_watcher.go │ │ │ ├── sds_infra_watcher.go │ │ │ ├── sds_infra_watcher_funcs.go │ │ │ └── sds_infra_watcher_test.go │ │ ├── kubutils │ │ │ └── kubernetes.go │ │ ├── logger │ │ │ └── logger.go │ │ └── monitoring │ │ │ └── monitoring.go │ └── werf.inc.yaml ├── sds-utils-installer │ ├── LICENSE │ ├── cmd │ │ └── main.go │ ├── go.mod │ ├── go.sum │ └── werf.inc.yaml └── webhooks │ ├── LICENSE │ ├── cmd │ └── main.go │ ├── go.mod │ ├── go.sum │ ├── handlers │ ├── func.go │ └── llvsValidator.go │ └── werf.inc.yaml ├── lib ├── go │ └── common │ │ ├── LICENSE │ │ ├── go.mod │ │ ├── go.sum │ │ └── pkg │ │ └── feature │ │ ├── const_ce.go │ │ ├── const_csepro.go │ │ ├── const_ee.go │ │ ├── const_se.go │ │ ├── const_seplus.go │ │ └── feature.go └── python │ └── requirements.txt ├── module.yaml ├── openapi ├── config-values.yaml ├── doc-ru-config-values.yaml ├── openapi-case-tests.yaml ├── values_ce.yaml └── values_ee.yaml ├── release.yaml ├── templates ├── agent │ ├── daemonset.yaml │ ├── nodegroupconfiguration-blacklist-loop-devices.yaml │ ├── podmonitor.yaml │ └── rbac-for-us.yaml ├── namespace.yaml ├── registry-secret.yaml ├── sds-health-watcher-controller │ ├── deployment.yaml │ ├── rbac-for-us.yaml │ └── servicemonitor.yaml └── webhooks │ ├── deployment.yaml │ ├── rbac-for-us.yaml │ ├── secret.yaml │ ├── service.yaml │ └── webhook.yaml ├── testing ├── .markdownlintignore └── markdownlint.yaml ├── tools └── dev_images │ └── additional_tools │ └── binary_replace.sh ├── werf-giterminism.yaml ├── werf.yaml └── werf_cleanup.yaml /.dmtlint.yaml: -------------------------------------------------------------------------------- 1 | linters-settings: 2 | module: 3 | oss: 4 | disable: true 5 | openapi: 6 | exclude-rules: 7 | enum: 8 | - spec.versions[0].schema.openAPIV3Schema.properties.status.properties.fsType 9 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.yml: -------------------------------------------------------------------------------- 1 | name: 🎯 Bug report 2 | description: Report a bug to help us improve Deckhouse 3 | labels: 4 | - 'type/bug' 5 | - 'status/needs-triage' 6 | body: 7 | - type: markdown 8 | attributes: 9 | value: | 10 | Thank you for submitting a bug report! 11 | 12 | Please fill out the template below to make it easier to debug your problem. 13 | - type: checkboxes 14 | attributes: 15 | label: Preflight Checklist 16 | description: Please ensure you've completed all of the following. 17 | options: 18 | - label: I agree to follow the [Code of Conduct](https://github.com/deckhouse/deckhouse/blob/main/CODE_OF_CONDUCT.md) that this project adheres to. 19 | required: true 20 | - label: I have searched the [issue tracker](https://github.com/deckhouse/deckhouse/issues) for an issue that matches the one I want to file, without success. 21 | required: true 22 | - type: input 23 | attributes: 24 | label: Version 25 | description: | 26 | What version of Deckhouse are you running? 27 | placeholder: v1.0.0 28 | validations: 29 | required: true 30 | - type: textarea 31 | attributes: 32 | label: Expected Behavior 33 | description: A clear and concise description of what you expected to happen. 34 | validations: 35 | required: true 36 | - type: textarea 37 | attributes: 38 | label: Actual Behavior 39 | description: A clear description of what actually happens. 40 | validations: 41 | required: true 42 | - type: textarea 43 | attributes: 44 | label: Steps To Reproduce 45 | description: Steps to reproduce the behavior if it is not self-explanatory. 46 | placeholder: | 47 | 1. In this environment... 48 | 2. With this config... 49 | 3. Run '...' 50 | 4. See error... 51 | - type: textarea 52 | attributes: 53 | label: Additional Information 54 | description: Links? References? Anything that will give us more context about the issue that you are encountering! 55 | - type: textarea 56 | attributes: 57 | label: Logs 58 | description: Deckhouse application logs (if relevant). 59 | render: shell 60 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | contact_links: 3 | - name: 📚 Documentation 4 | url: https://deckhouse.io/documentation/ 5 | about: Check the documentation for help 6 | 7 | - name: 🤔 Ask a question 8 | url: https://github.com/deckhouse/deckhouse/discussions/new?category=q-a 9 | about: Ask and discuss questions with other community members 10 | 11 | - name: 💬 Telegram channel [EN] 12 | url: https://t.me/deckhouse 13 | about: Please ask and answer questions here 14 | 15 | - name: 🇷🇺 Telegram channel [RU] 16 | url: https://t.me/deckhouse_ru 17 | about: Please ask and answer questions here 18 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.yml: -------------------------------------------------------------------------------- 1 | name: 💡 Feature request 2 | description: Suggest an idea for Deckhouse 3 | labels: 4 | - 'type/enhancement' 5 | - 'status/needs-triage' 6 | body: 7 | - type: markdown 8 | attributes: 9 | value: | 10 | Thank you for submitting a feature request! 11 | 12 | Please describe what you would like to change/add and why in detail by filling out the template below. 13 | - type: checkboxes 14 | attributes: 15 | label: Preflight Checklist 16 | description: Please ensure you've completed all of the following. 17 | options: 18 | - label: I agree to follow the [Code of Conduct](https://github.com/deckhouse/deckhouse/blob/main/CODE_OF_CONDUCT.md) that this project adheres to. 19 | required: true 20 | - label: I have searched the [issue tracker](https://github.com/deckhouse/deckhouse/issues) for an issue that matches the one I want to file, without success. 21 | required: true 22 | - type: textarea 23 | attributes: 24 | label: Use case. Why is this important? 25 | description: A clear and concise description of the problem you are seeking to solve with this feature request. 26 | validations: 27 | required: true 28 | - type: textarea 29 | attributes: 30 | label: Proposed Solution 31 | description: A clear and concise description of what would you like to happen. 32 | validations: 33 | required: true 34 | - type: textarea 35 | attributes: 36 | label: Additional Information 37 | description: Add any other context about the problem here. 38 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ## Description 2 | 8 | 9 | ## Why do we need it, and what problem does it solve? 10 | 18 | 19 | ## What is the expected result? 20 | 25 | 26 | ## Checklist 27 | - [ ] The code is covered by unit tests. 28 | - [ ] e2e tests passed. 29 | - [ ] Documentation updated according to the changes. 30 | - [ ] Changes were tested in the Kubernetes cluster manually. 31 | -------------------------------------------------------------------------------- /.github/check_previous_channel_release.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | crane="/usr/local/bin/crane" 4 | repositoryName=$1 5 | edition=$2 6 | channel=$3 7 | version=$4 8 | user=$5 9 | password=$6 10 | 11 | echo "Module $repositoryName, edition $edition, channel $channel, version $version" 12 | 13 | if [[ "$channel" == "alpha" ]]; then 14 | echo "Deploying $version to alpha channel" 15 | exit 0 16 | elif [[ "$channel" == "beta" ]]; then 17 | previousChannel="alpha" 18 | elif [[ "$channel" == "early-access" ]]; then 19 | previousChannel="beta" 20 | elif [[ "$channel" == "stable" ]]; then 21 | previousChannel="early-access" 22 | elif [[ "$channel" == "rock-solid" ]]; then 23 | previousChannel="stable" 24 | else 25 | echo "Unknown channel" 26 | exit 1 27 | fi 28 | 29 | echo "Checking previous channel $previousChannel" 30 | $crane auth login -u $user -p $password registry.deckhouse.io 31 | previousChannelVersion=$($crane export registry.deckhouse.io/deckhouse/$edition/modules/$repositoryName/release:$previousChannel | grep -aoE '\{"version":".*"\}' | jq -r .version) 32 | if [[ "$version" == "$previousChannelVersion" ]]; then 33 | echo "Previous channel $previousChannel version $previousChannelVersion is equal desired version $version, processing" 34 | exit 0 35 | else 36 | echo "Previous channel $previousChannel version $previousChannelVersion is not equal desired version $version, rejecting" 37 | exit 1 38 | fi 39 | -------------------------------------------------------------------------------- /.github/workflows/checks.yaml: -------------------------------------------------------------------------------- 1 | name: PR Checks 2 | 3 | on: 4 | pull_request: 5 | types: [opened, labeled, unlabeled, synchronize] 6 | 7 | jobs: 8 | release-label: 9 | name: Release note label 10 | runs-on: [self-hosted, regular] 11 | 12 | steps: 13 | - name: Check minimum labels 14 | uses: mheap/github-action-required-labels@v5 15 | with: 16 | mode: minimum 17 | count: 1 18 | labels: "release-note/dependencies, dependencies, release-note/deprecation, release-note/breaking-change, release-note/bug, bug, release-note/enhancement, enhancement, release-note/documentation, documentation, release-note/new-feature, release-note/ignore" 19 | -------------------------------------------------------------------------------- /.github/workflows/deploy_dev.yml: -------------------------------------------------------------------------------- 1 | name: Deploy Dev 2 | 3 | env: 4 | MODULES_REGISTRY: ${{ vars.DEV_REGISTRY }} 5 | CI_COMMIT_REF_NAME: ${{ github.event.inputs.tag }} 6 | MODULES_MODULE_NAME: ${{ vars.MODULE_NAME }} 7 | MODULES_MODULE_SOURCE: ${{ vars.DEV_MODULE_SOURCE }} 8 | MODULES_REGISTRY_LOGIN: ${{ vars.DEV_MODULES_REGISTRY_LOGIN }} 9 | MODULES_REGISTRY_PASSWORD: ${{ secrets.DEV_MODULES_REGISTRY_PASSWORD }} 10 | RELEASE_CHANNEL: ${{ github.event.inputs.channel }} 11 | MODULES_MODULE_TAG: ${{ github.event.inputs.tag }} 12 | GOLANG_VERSION: ${{ vars.GOLANG_VERSION }} 13 | GOPROXY: ${{ secrets.GOPROXY }} 14 | SOURCE_REPO: ${{ secrets.SOURCE_REPO }} 15 | 16 | on: 17 | workflow_dispatch: 18 | inputs: 19 | channel: 20 | description: "Select release channel" 21 | type: choice 22 | default: alpha 23 | options: 24 | - "alpha" 25 | - "beta" 26 | - "early-access" 27 | - "stable" 28 | - "rock-solid" 29 | 30 | tag: 31 | description: "The module's tag, which must include the -dev1 postfix. For example: v1.21.1-dev1" 32 | type: string 33 | required: true 34 | 35 | enableBuild: 36 | type: boolean 37 | default: true 38 | description: "Set to true if build is required" 39 | 40 | jobs: 41 | deploy-dev: 42 | runs-on: [self-hosted, large] 43 | name: Deploy dev 44 | steps: 45 | - name: PRINT VARS 46 | run: | 47 | echo MODULES_REGISTRY=$MODULES_REGISTRY 48 | echo MODULES_MODULE_SOURCE=$MODULES_MODULE_SOURCE 49 | echo CI_COMMIT_REF_NAME=$CI_COMMIT_REF_NAME 50 | echo MODULES_MODULE_NAME=$MODULES_MODULE_NAME 51 | echo RELEASE_CHANNEL=$RELEASE_CHANNEL 52 | echo MODULES_MODULE_TAG=$MODULES_MODULE_TAG 53 | shell: bash 54 | - name: Validation for tag 55 | run: | 56 | echo ${{ github.event.inputs.tag }} | grep -P '^v\d+\.\d+\.\d+-dev1$' 57 | shell: bash 58 | 59 | - uses: actions/checkout@v4 60 | - uses: deckhouse/modules-actions/setup@v2 61 | with: 62 | registry: ${{ vars.DEV_REGISTRY }} 63 | registry_login: ${{ vars.DEV_MODULES_REGISTRY_LOGIN }} 64 | registry_password: ${{ secrets.DEV_MODULES_REGISTRY_PASSWORD }} 65 | - if: ${{ github.event.inputs.enableBuild == 'true' }} 66 | uses: deckhouse/modules-actions/build@v2 67 | with: 68 | module_source: "${{ vars.DEV_MODULE_SOURCE }}" 69 | module_name: ${{ vars.MODULE_NAME }} 70 | module_tag: ${{ github.event.inputs.tag }} 71 | source_repo: ${{ secrets.SOURCE_REPO }} 72 | source_repo_ssh_key: ${{ secrets.SOURCE_REPO_SSH_KEY }} 73 | - uses: deckhouse/modules-actions/deploy@v2 -------------------------------------------------------------------------------- /.github/workflows/dev_registry-cleanup.yml: -------------------------------------------------------------------------------- 1 | # Copyright 2024 Flant JSC 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | name: Cleanup dev registries 16 | 17 | env: 18 | MODULES_REGISTRY: ${{ vars.DEV_REGISTRY }} 19 | CI_COMMIT_REF_NAME: ${{ github.ref_name }} 20 | MODULES_MODULE_NAME: ${{ vars.MODULE_NAME }} 21 | MODULES_MODULE_SOURCE: ${{ vars.DEV_MODULE_SOURCE }} 22 | MODULES_REGISTRY_LOGIN: ${{ vars.DEV_MODULES_REGISTRY_LOGIN }} 23 | MODULES_REGISTRY_PASSWORD: ${{ secrets.DEV_MODULES_REGISTRY_PASSWORD }} 24 | WERF_DRY_RUN: "false" 25 | 26 | on: 27 | workflow_dispatch: 28 | schedule: 29 | - cron: "12 0 * * 6" 30 | 31 | defaults: 32 | run: 33 | shell: bash 34 | 35 | jobs: 36 | lint: 37 | runs-on: [self-hosted, large] 38 | name: Run cleanup 39 | steps: 40 | - uses: actions/checkout@v4 41 | - uses: deckhouse/modules-actions/setup@v2 42 | with: 43 | registry: ${{ vars.DEV_REGISTRY }} 44 | registry_login: ${{ vars.DEV_MODULES_REGISTRY_LOGIN }} 45 | registry_password: ${{ secrets.DEV_MODULES_REGISTRY_PASSWORD }} 46 | - name: Cleanup 47 | run: | 48 | werf cleanup \ 49 | --repo ${MODULES_MODULE_SOURCE}/${MODULES_MODULE_NAME} \ 50 | --without-kube=true --config werf_cleanup.yaml 51 | -------------------------------------------------------------------------------- /.github/workflows/go_checks.yaml: -------------------------------------------------------------------------------- 1 | name: Go checks for images 2 | 3 | env: 4 | GO_BUILD_TAGS: "ce ee se seplus csepro" 5 | 6 | on: 7 | pull_request: 8 | push: 9 | branches: 10 | - main 11 | 12 | jobs: 13 | go_linter: 14 | name: Go linter for images 15 | runs-on: [self-hosted, regular] 16 | 17 | steps: 18 | - name: Checkout repository 19 | uses: actions/checkout@v2 20 | 21 | - name: Run Go lint 22 | uses: deckhouse/modules-actions/go_linter@v2 23 | 24 | go_tests: 25 | name: Go tests for images 26 | runs-on: [self-hosted, regular] 27 | 28 | steps: 29 | - name: Checkout repository 30 | uses: actions/checkout@v2 31 | 32 | - name: Run Go tests 33 | uses: deckhouse/modules-actions/go_tests@v2 34 | 35 | go_test_coverage: 36 | name: Go test coverage for images 37 | runs-on: [self-hosted, regular] 38 | 39 | steps: 40 | - name: Checkout repository 41 | uses: actions/checkout@v2 42 | 43 | - name: Run Go test coverage count 44 | uses: deckhouse/modules-actions/go_test_coverage@v2 45 | 46 | go_modules_check: 47 | name: Go modules version 48 | runs-on: [self-hosted, regular] 49 | 50 | steps: 51 | - name: Checkout repository 52 | uses: actions/checkout@v2 53 | 54 | - name: Run Go modules version check 55 | uses: deckhouse/modules-actions/go_modules_check@v2 56 | -------------------------------------------------------------------------------- /.github/workflows/trivy_image_check.yaml: -------------------------------------------------------------------------------- 1 | name: Build and checks 2 | 3 | on: 4 | schedule: 5 | - cron: "0 01 * * 0,3" 6 | pull_request: 7 | types: [opened, reopened, labeled, synchronize] 8 | push: 9 | branches: 10 | - main 11 | workflow_dispatch: 12 | inputs: 13 | release_branch: 14 | description: "Optional. Set minor version of release you want to scan. e.g.: 1.23" 15 | required: false 16 | scan_several_lastest_releases: 17 | description: "Optional. Whether to scan last several releases or not. true/false. For scheduled pipelines it is always true. Default is: false." 18 | required: false 19 | latest_releases_amount: 20 | description: "Optional. Number of latest releases to scan. Default is: 3" 21 | required: false 22 | severity: 23 | description: "Optional. Vulnerabilities severity to scan. Default is: UNKNOWN,LOW,MEDIUM,HIGH,CRITICAL" 24 | required: false 25 | 26 | jobs: 27 | build_dev: 28 | if: github.event_name == 'pull_request' 29 | uses: ./.github/workflows/build_dev.yml 30 | secrets: inherit 31 | cve_scan_on_pr: 32 | if: github.event_name == 'pull_request' 33 | name: CVE scan for PR 34 | runs-on: [self-hosted, regular] 35 | needs: [build_dev] 36 | steps: 37 | - uses: actions/checkout@v4 38 | - uses: deckhouse/modules-actions/cve_scan@v4 39 | with: 40 | tag: pr${{ github.event.number }} 41 | module_name: ${{ vars.MODULE_NAME }} 42 | dd_url: ${{ secrets.DEFECTDOJO_HOST }} 43 | dd_token: ${{ secrets.DEFECTDOJO_API_TOKEN }} 44 | prod_registry: "registry.deckhouse.io" 45 | prod_registry_user: "license-token" 46 | prod_registry_password: ${{ secrets.PROD_MODULES_READ_REGISTRY_PASSWORD }} 47 | dev_registry: ${{ vars.DEV_REGISTRY }} 48 | dev_registry_user: ${{ vars.DEV_MODULES_REGISTRY_LOGIN }} 49 | dev_registry_password: ${{ secrets.DEV_MODULES_REGISTRY_PASSWORD }} 50 | deckhouse_private_repo: ${{ secrets.DECKHOUSE_PRIVATE_REPO }} 51 | severity: "HIGH,CRITICAL" 52 | cve_scan: 53 | if: github.event_name != 'pull_request' 54 | name: Regular CVE scan 55 | runs-on: [self-hosted, regular] 56 | steps: 57 | - uses: actions/checkout@v4 58 | - uses: deckhouse/modules-actions/cve_scan@v4 59 | with: 60 | tag: ${{ github.event.inputs.release_branch || github.event.repository.default_branch }} 61 | module_name: ${{ vars.MODULE_NAME }} 62 | dd_url: ${{ secrets.DEFECTDOJO_HOST }} 63 | dd_token: ${{ secrets.DEFECTDOJO_API_TOKEN }} 64 | prod_registry: "registry.deckhouse.io" 65 | prod_registry_user: "license-token" 66 | prod_registry_password: ${{ secrets.PROD_MODULES_READ_REGISTRY_PASSWORD }} 67 | dev_registry: ${{ vars.DEV_REGISTRY }} 68 | dev_registry_user: ${{ vars.DEV_MODULES_REGISTRY_LOGIN }} 69 | dev_registry_password: ${{ secrets.DEV_MODULES_REGISTRY_PASSWORD }} 70 | deckhouse_private_repo: ${{ secrets.DECKHOUSE_PRIVATE_REPO }} 71 | scan_several_lastest_releases: ${{ github.event.inputs.scan_several_lastest_releases }} 72 | latest_releases_amount: ${{ github.event.inputs.latest_releases_amount || '3' }} 73 | severity: ${{ github.event.inputs.severity }} 74 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.dll 4 | *.so 5 | *.dylib 6 | 7 | # Test binary, build with `go test -c` 8 | *.test 9 | 10 | # Output of the go coverage tool, specifically when used with LiteIDE 11 | *.out 12 | 13 | # Project-local glide cache, RE: https://github.com/Masterminds/glide/issues/736 14 | .glide/ 15 | 16 | # vim 17 | *.swp 18 | 19 | # IDE 20 | .history/ 21 | .idea 22 | *.suo 23 | *.ntvs* 24 | *.njsproj 25 | *.sln 26 | *.sw? 27 | .project 28 | .settings 29 | .idea/ 30 | venv/ 31 | *.code-workspace 32 | .vscode 33 | *.code-workspace 34 | 35 | # macOS Finder files 36 | *.DS_Store 37 | ._* 38 | 39 | # Python 40 | __pycache__/ 41 | *.py[cod] 42 | *$py.class 43 | .pytest_cache/ 44 | /lib/python/dist/ 45 | 46 | # dev 47 | images/agent/dev 48 | images/agent/Makefile 49 | local_build.sh 50 | -------------------------------------------------------------------------------- /.golangci.yaml: -------------------------------------------------------------------------------- 1 | run: 2 | timeout: 10m 3 | 4 | issues: 5 | exclude: 6 | - ST1005.* 7 | - "should not use dot imports" 8 | - "don't use an underscore in package name" 9 | - "exported: .*" 10 | - "could not import" 11 | 12 | linters-settings: 13 | gci: 14 | sections: 15 | - standard 16 | - default 17 | - prefix(github.com/deckhouse) 18 | 19 | linters: 20 | disable-all: true 21 | enable: 22 | - dogsled 23 | - errcheck 24 | - gci 25 | - gocritic 26 | - gofmt 27 | # - goimports 28 | - gosimple 29 | - govet 30 | - ineffassign 31 | - misspell 32 | - revive 33 | - staticcheck 34 | # - structcheck 35 | - typecheck 36 | - unconvert 37 | - unparam 38 | - whitespace 39 | - copyloopvar 40 | -------------------------------------------------------------------------------- /.helmignore: -------------------------------------------------------------------------------- 1 | *.md 2 | crds 3 | # charts 4 | docs 5 | enabled 6 | hooks 7 | images 8 | images_digests.json 9 | lib 10 | Makefile 11 | monitoring_test 12 | openapi 13 | release.yaml 14 | scripts 15 | template_tests 16 | werf*.yaml 17 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | repos: 2 | - repo: local 3 | hooks: 4 | - id: go-mod-tidy 5 | name: Run `go mod tidy` fo all the modules 6 | language: system 7 | entry: make go-mod-tidy 8 | pass_filenames: false 9 | types: 10 | - file 11 | files: ^.*/go\.mod$ 12 | - id: go-generate 13 | name: Run `go generate ./...` fo all the modules 14 | language: system 15 | entry: make go-generate 16 | pass_filenames: false 17 | types: [go] 18 | - id: go-test-ce 19 | name: Run `go test -tags=ee ./...` for all the modules 20 | pass_filenames: false 21 | language: system 22 | entry: make go-test-ee 23 | types: [go] 24 | - id: go-test-ee 25 | name: Run `go test -tags=ce ./...` for all the modules 26 | pass_filenames: false 27 | language: system 28 | entry: make go-test-ce 29 | types: [go] 30 | - repo: https://github.com/golangci/golangci-lint 31 | rev: v1.64.5 32 | hooks: 33 | # This doesn't work in case `pre-commit run --all-files` 34 | # - id: golangci-lint 35 | # name: Run golangci-lint with tag `ee` on change only 36 | # entry: ./hack/for-each-mod golangci-lint run --new-from-rev HEAD --build-tags ee --fix 37 | # - id: golangci-lint 38 | # name: Run golangci-lint with tag `ce` on change only 39 | # entry: ./hack/for-each-mod golangci-lint run --new-from-rev HEAD --build-tags ce --fix 40 | - id: golangci-lint-full 41 | name: Run golangci-lint with tag `ee` 42 | entry: ./hack/for-each-mod golangci-lint run --build-tags ee --fix 43 | - id: golangci-lint-full 44 | name: Run golangci-lint with tag `ce` 45 | entry: ./hack/for-each-mod golangci-lint run --build-tags ce --fix 46 | -------------------------------------------------------------------------------- /.werf/base-images.yaml: -------------------------------------------------------------------------------- 1 | # Base Images 2 | {{- $baseImages := .Files.Get "base_images.yml" | fromYaml }} 3 | {{- range $k, $v := $baseImages }} 4 | {{ $baseImagePath := (printf "%s@%s" $baseImages.REGISTRY_PATH (trimSuffix "/" $v)) }} 5 | {{- if ne $k "REGISTRY_PATH" }} 6 | {{- $_ := set $baseImages $k $baseImagePath }} 7 | {{- end }} 8 | {{- end }} 9 | {{- $_ := unset $baseImages "REGISTRY_PATH" }} 10 | 11 | {{- $_ := set . "Images" $baseImages }} 12 | # base images artifacts 13 | {{- range $k, $v := .Images }} 14 | --- 15 | image: {{ $k }} 16 | from: {{ $v }} 17 | final: false 18 | {{- end }} 19 | 20 | -------------------------------------------------------------------------------- /.werf/bundle.yaml: -------------------------------------------------------------------------------- 1 | # Bundle image, stored in your.registry.io/modules/: 2 | --- 3 | image: bundle 4 | fromImage: builder/scratch 5 | 6 | import: 7 | # Rendering .werf/images-digests.yaml is required! 8 | - image: images-digests 9 | add: /images_digests.json 10 | to: /images_digests.json 11 | after: setup 12 | # Rendering .werf/python-deps.yaml is required! 13 | - image: python-dependencies 14 | add: /lib/python/dist 15 | to: /lib/python/dist 16 | after: setup 17 | # Rendering .werf/go-hooks.yaml is required! 18 | - image: go-hooks-artifact 19 | add: /usr/local/bin/go-hooks 20 | to: /hooks/go-hooks 21 | after: setup 22 | # Rendering .werf/choose-edition.yaml is required! 23 | - image: choose-edition 24 | add: /openapi 25 | to: /openapi 26 | after: setup 27 | git: 28 | - add: / 29 | to: / 30 | excludePaths: 31 | - hooks/go 32 | includePaths: 33 | - .helmignore 34 | - charts 35 | - crds 36 | - docs 37 | - hooks 38 | - monitoring 39 | - templates 40 | - Chart.yaml 41 | - module.yaml 42 | -------------------------------------------------------------------------------- /.werf/choose-edition.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | image: choose-edition 3 | fromImage: builder/alt 4 | fromCacheVersion: {{ div .Commit.Date.Unix (mul 60 60 24 30) }} 5 | 6 | git: 7 | - add: / 8 | to: / 9 | includePaths: 10 | - openapi 11 | stageDependencies: 12 | setup: 13 | - openapi/values_*.yaml 14 | shell: 15 | setup: 16 | - cd /openapi 17 | - if [[ {{ .MODULE_EDITION }} == "ce" ]]; then cp -v values_ce.yaml values.yaml; else cp -v values_ee.yaml values.yaml; fi 18 | - rm -rf values_*.yaml 19 | -------------------------------------------------------------------------------- /.werf/consts.yaml: -------------------------------------------------------------------------------- 1 | # Edition module settings, default ce 2 | {{- $_ := set . "MODULE_EDITION" (env "MODULE_EDITION" "ce") }} 3 | 4 | # component versions 5 | {{- $versions := dict }} 6 | {{- $_ := set $versions "UTIL_LINUX" "v2.39.3" }} 7 | {{- $_ := set $versions "LVM2" "d786a8f820d54ce87a919e6af5426c333c173b11" }} 8 | 9 | {{- $_ := set $ "VERSIONS" $versions }} 10 | 11 | # custom constants 12 | {{- $_ := set $ "DECKHOUSE_UID_GID" "64535" }} 13 | {{- $_ := set $ "ALT_CLEANUP_CMD" "rm -rf /var/lib/apt/lists/* /var/cache/apt/* && mkdir -p /var/lib/apt/lists/partial /var/cache/apt/archives/partial" }} 14 | -------------------------------------------------------------------------------- /.werf/images-digests.yaml: -------------------------------------------------------------------------------- 1 | {{- /* Find all images digests and save use them to compose a structure for the module values. */ -}} 2 | {{- $ImagesIDList := list }} 3 | {{- $Images := tpl (.Files.Get ".werf/images.yaml") . }} 4 | 5 | 6 | {{- range $ImageManifest := regexSplit "\n?---[ \t]*\n" $Images -1 }} 7 | {{- $ImageManifest := $ImageManifest | fromYaml }} 8 | {{- if and $ImageManifest.image (ne ($ImageManifest.final | toJson) "false") }} 9 | {{- $ImagesIDList = append $ImagesIDList $ImageManifest.image }} 10 | {{- end }} 11 | {{- end }} 12 | 13 | # Images Digest: a files with all image digests to be able to use them in helm templates of a module 14 | --- 15 | image: images-digests 16 | fromImage: builder/alpine 17 | dependencies: 18 | {{- range $ImageID := $ImagesIDList }} 19 | {{- $ImageNameCamel := $ImageID | splitList "/" | last | camelcase | untitle }} 20 | - image: {{ $ImageID }} 21 | before: setup 22 | imports: 23 | - type: ImageDigest 24 | targetEnv: MODULE_IMAGE_DIGEST_{{ $ImageNameCamel }} 25 | {{- end }} 26 | shell: 27 | beforeInstall: 28 | - apk add --no-cache jq 29 | setup: 30 | - | 31 | env | grep MODULE_IMAGE_DIGEST | jq -Rn ' 32 | reduce inputs as $i ( 33 | {}; 34 | . * ( 35 | $i | ltrimstr("MODULE_IMAGE_DIGEST_") | sub("=";"_") | 36 | split("_") as [$imageName, $digest] | 37 | {($imageName): $digest} 38 | ) 39 | ) 40 | ' > /images_digests.json 41 | -------------------------------------------------------------------------------- /.werf/images.yaml: -------------------------------------------------------------------------------- 1 | {{- $ImagesBuildFiles := .Files.Glob "images/*/{Dockerfile,werf.inc.yaml}" }} 2 | 3 | {{- range $path, $content := $ImagesBuildFiles }} 4 | {{ $ctx := (dict "ImageName" ($path | split "/")._1 "Root" $ "Versions" $.VERSIONS) }} 5 | --- 6 | {{- /* For Dockerfile just render it from the folder. */ -}} 7 | {{- if not (regexMatch "/werf.inc.yaml$" $path) }} 8 | image: images/{{ $ctx.ImageName }} 9 | context: images/{{ $ctx.ImageName }} 10 | dockerfile: Dockerfile 11 | args: 12 | SOURCE_REPO: {{ env "SOURCE_REPO" | default "https://github.com" }} 13 | 14 | {{- /* For werf.inc.yaml render content by providing the ImageName param. */ -}} 15 | {{- else }} 16 | {{ tpl $content $ctx }} 17 | 18 | {{- end }} 19 | {{- end }} 20 | -------------------------------------------------------------------------------- /.werf/python-deps.yaml: -------------------------------------------------------------------------------- 1 | # Python deps image, required to download dependencies and put it to the final module image (bundle) 2 | --- 3 | image: python-dependencies 4 | fromImage: builder/alpine 5 | git: 6 | - add: / 7 | to: / 8 | includePaths: 9 | - lib/python 10 | shell: 11 | beforeInstall: 12 | - apk add --no-cache python3 py3-pip 13 | setup: 14 | - pip3 install -r /lib/python/requirements.txt -t /lib/python/dist 15 | -------------------------------------------------------------------------------- /.werf/release.yaml: -------------------------------------------------------------------------------- 1 | # Release image, stored in your.registry.io/modules//release: 2 | --- 3 | image: release-channel-version-artifact 4 | fromImage: builder/alpine 5 | shell: 6 | beforeInstall: 7 | - apk add --no-cache curl 8 | - curl -sfL https://github.com/mikefarah/yq/releases/download/2.4.1/yq_linux_amd64 --output /usr/local/bin/yq 9 | - chmod +x /usr/local/bin/yq 10 | install: 11 | - | 12 | yq n version "{{ env "CI_COMMIT_REF_NAME" }}" | yq r - -j > version.json 13 | --- 14 | image: release-channel-version 15 | fromImage: builder/scratch 16 | import: 17 | - image: release-channel-version-artifact 18 | add: / 19 | to: / 20 | after: install 21 | includePaths: 22 | - version.json 23 | -------------------------------------------------------------------------------- /Chart.yaml: -------------------------------------------------------------------------------- 1 | name: sds-node-configurator 2 | version: 0.0.1 3 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | SHELL := /bin/bash 2 | MDLINTER_IMAGE = ghcr.io/igorshubovych/markdownlint-cli@sha256:2e22b4979347f70e0768e3fef1a459578b75d7966e4b1a6500712b05c5139476 3 | 4 | .PHONY: --lint-markdown-header lint-markdown lint-markdown-fix 5 | --lint-markdown-header: 6 | @docker pull -q ${MDLINTER_IMAGE} 7 | @echo "\n######################################################################################################################" 8 | @echo '###' 9 | @echo "### Markdown linter report (powered by https://github.com/DavidAnson/markdownlint/)\n" 10 | 11 | lint-markdown: --lint-markdown-header ## Run markdown linter. 12 | @bash -c \ 13 | "if docker run --rm -v ${PWD}:/workdir ${MDLINTER_IMAGE} --config testing/markdownlint.yaml -p testing/.markdownlintignore '**/*.md' ; then \ 14 | echo; echo 'All checks passed.'; \ 15 | else \ 16 | echo; \ 17 | echo 'To run linter locally and fix common problems run: make lint-markdown-fix'; \ 18 | echo; \ 19 | exit 1; \ 20 | fi" 21 | 22 | lint-markdown-fix: ## Run markdown linter and fix problems automatically. 23 | @docker run --rm -v ${PWD}:/workdir ${MDLINTER_IMAGE} \ 24 | --config testing/markdownlint.yaml -p testing/.markdownlintignore "**/*.md" --fix && (echo 'Fixed successfully.') 25 | 26 | .PHONY: update-dev 27 | update-dev: check-yq 28 | make bump-dev version=$$(./hack/increase_semver.sh -d $$(yq .version Chart.yaml)) 29 | 30 | .PHONY: update-patch 31 | update-patch: check-yq 32 | make bump version=$$(./hack/increase_semver.sh -p $$(yq .version Chart.yaml)) 33 | 34 | .PHONY: update-minor 35 | update-minor: check-yq 36 | make bump version=$$(./hack/increase_semver.sh -m $$(yq .version Chart.yaml)) 37 | 38 | .PHONY: update-major 39 | update-major: check-yq 40 | make bump version=$$(./hack/increase_semver.sh -M $$(yq .version Chart.yaml)) 41 | 42 | .PHONY: bump-dev 43 | bump-dev: current-version 44 | yq -i '.version="$(version)"' Chart.yaml 45 | yq -i '.version="v$(version)"' release.yaml 46 | 47 | git commit -a -s -m "bump version $(version)" 48 | 49 | .PHONY: bump 50 | bump: current-version 51 | yq -i '.version="$(version)"' Chart.yaml 52 | yq -i '.version="v$(version)"' release.yaml 53 | 54 | git commit -a -s -m "bump version $(version)" 55 | git tag "v$(version)" 56 | 57 | .PHONY: push 58 | push: 59 | git push -u origin HEAD && git push --tags 60 | 61 | .PHONY: current-version 62 | current-version: check-yq 63 | @echo "Current version: $$(yq .version Chart.yaml)" 64 | 65 | .PHONY: check-yq 66 | check-yq: 67 | @which yq >/dev/null || (echo "yq not found. Install it to change Chart.yaml"; exit 1) 68 | 69 | .PHONY: check-jq 70 | check-jq: 71 | @which jq >/dev/null || (echo "jq not found. Install it to change package.json"; exit 1) 72 | 73 | ##@ Helm lib 74 | 75 | .PHONY: helm-update-subcharts 76 | helm-update-subcharts: ## Download subcharts into charts directory. Please, set desired versions in Chart.yaml before download. 77 | @which helm || (echo "Helm not found. Please, install helm to update helm_lib."; exit 1) 78 | helm repo add deckhouse https://deckhouse.github.io/lib-helm && \ 79 | helm repo update && \ 80 | helm dep update 81 | 82 | .PHONY: helm-bump-helm-lib 83 | helm-bump-helm-lib: ## Update helm lib in charts directory to specified version. 84 | ##~ Options: version= 85 | @which yq || (echo "yq not found. Install it to change Chart.yaml"; exit 1) 86 | yq -i '.dependencies[] |= select(.name == "deckhouse_lib_helm").version = "$(version)"' Chart.yaml 87 | git rm charts/*.tgz || true 88 | mkdir -p charts 89 | $(MAKE) helm-update-subcharts 90 | @echo "Helm lib updated to $(version)" 91 | ls -la charts 92 | 93 | .PHONY: go-generate 94 | go-generate: ## Run go generate 95 | hack/for-each-mod go generate ./... 96 | 97 | .PHONY: go-test-ce 98 | go-test-ce: ## Run go generate 99 | hack/for-each-mod go test -tags=ce ./... 100 | 101 | .PHONY: go-test-ee 102 | go-test-ee: ## Run go generate 103 | hack/for-each-mod go test -tags=ee ./... 104 | 105 | .PHONY: go-mod-tidy 106 | go-mod-tidy: ## Run go generate 107 | hack/for-each-mod go mod tidy 108 | -------------------------------------------------------------------------------- /api/README.md: -------------------------------------------------------------------------------- 1 | To use this module, execute 2 | ``` 3 | go get github.com/deckhouse/MODULE/api@main. 4 | ``` 5 | 6 | The pseudo-tag will be generated automatically. 7 | 8 | Please note that model changes will NOT BE APPLIED in dependent modules until you rerun go get (the pseudo-tag points to a specific commit). 9 | Therefore, it is important to remember to apply go get in all external modules that use this models. 10 | 11 | !Also, DO NOT FORGET to update the models when the CRD are changed! -------------------------------------------------------------------------------- /api/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/deckhouse/sds-node-configurator/api 2 | 3 | go 1.24.2 4 | 5 | require ( 6 | k8s.io/apimachinery v0.32.3 7 | k8s.io/code-generator v0.32.3 8 | ) 9 | 10 | require ( 11 | github.com/emicklei/go-restful/v3 v3.11.0 // indirect 12 | github.com/fxamacker/cbor/v2 v2.7.0 // indirect 13 | github.com/go-logr/logr v1.4.2 // indirect 14 | github.com/go-openapi/jsonpointer v0.21.0 // indirect 15 | github.com/go-openapi/jsonreference v0.20.2 // indirect 16 | github.com/go-openapi/swag v0.23.0 // indirect 17 | github.com/gogo/protobuf v1.3.2 // indirect 18 | github.com/golang/protobuf v1.5.4 // indirect 19 | github.com/google/gnostic-models v0.6.8 // indirect 20 | github.com/google/gofuzz v1.2.0 // indirect 21 | github.com/josharian/intern v1.0.0 // indirect 22 | github.com/json-iterator/go v1.1.12 // indirect 23 | github.com/mailru/easyjson v0.7.7 // indirect 24 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect 25 | github.com/modern-go/reflect2 v1.0.2 // indirect 26 | github.com/spf13/pflag v1.0.5 // indirect 27 | github.com/x448/float16 v0.8.4 // indirect 28 | golang.org/x/mod v0.21.0 // indirect 29 | golang.org/x/net v0.36.0 // indirect 30 | golang.org/x/sync v0.11.0 // indirect 31 | golang.org/x/text v0.22.0 // indirect 32 | golang.org/x/tools v0.26.0 // indirect 33 | google.golang.org/protobuf v1.35.1 // indirect 34 | gopkg.in/inf.v0 v0.9.1 // indirect 35 | gopkg.in/yaml.v3 v3.0.1 // indirect 36 | k8s.io/gengo/v2 v2.0.0-20240911193312-2b36238f13e9 // indirect 37 | k8s.io/klog/v2 v2.130.1 // indirect 38 | k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f // indirect 39 | k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 // indirect 40 | sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 // indirect 41 | sigs.k8s.io/structured-merge-diff/v4 v4.4.2 // indirect 42 | sigs.k8s.io/yaml v1.4.0 // indirect 43 | ) 44 | -------------------------------------------------------------------------------- /api/tools.go: -------------------------------------------------------------------------------- 1 | //go:build tools 2 | // +build tools 3 | 4 | /* 5 | Copyright 2025 Flant JSC 6 | 7 | Licensed under the Apache License, Version 2.0 (the "License"); 8 | you may not use this file except in compliance with the License. 9 | You may obtain a copy of the License at 10 | 11 | http://www.apache.org/licenses/LICENSE-2.0 12 | 13 | Unless required by applicable law or agreed to in writing, software 14 | distributed under the License is distributed on an "AS IS" BASIS, 15 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | See the License for the specific language governing permissions and 17 | limitations under the License. 18 | */ 19 | 20 | package tools 21 | 22 | import _ "k8s.io/code-generator" 23 | -------------------------------------------------------------------------------- /api/v1alpha1/block_device.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2025 Flant JSC 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package v1alpha1 18 | 19 | import ( 20 | "k8s.io/apimachinery/pkg/api/resource" 21 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 22 | ) 23 | 24 | // BlockDevice empty block device 25 | type BlockDevice struct { 26 | metav1.TypeMeta `json:",inline"` 27 | metav1.ObjectMeta `json:"metadata,omitempty"` 28 | 29 | Status BlockDeviceStatus `json:"status"` 30 | } 31 | 32 | // BlockDeviceList contains a list of empty block device 33 | type BlockDeviceList struct { 34 | metav1.TypeMeta `json:",inline"` 35 | metav1.ListMeta `json:"metadata"` 36 | Items []BlockDevice `json:"items"` 37 | } 38 | 39 | type BlockDeviceStatus struct { 40 | Type string `json:"type"` 41 | FsType string `json:"fsType"` 42 | NodeName string `json:"nodeName"` 43 | Consumable bool `json:"consumable"` 44 | PVUuid string `json:"pvUUID"` 45 | VGUuid string `json:"vgUUID"` 46 | PartUUID string `json:"partUUID"` 47 | LVMVolumeGroupName string `json:"lvmVolumeGroupName"` 48 | ActualVGNameOnTheNode string `json:"actualVGNameOnTheNode"` 49 | Wwn string `json:"wwn"` 50 | Serial string `json:"serial"` 51 | Path string `json:"path"` 52 | Size resource.Quantity `json:"size"` 53 | Model string `json:"model"` 54 | Rota bool `json:"rota"` 55 | HotPlug bool `json:"hotPlug"` 56 | MachineID string `json:"machineId"` 57 | } 58 | -------------------------------------------------------------------------------- /api/v1alpha1/const.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2025 Flant JSC 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package v1alpha1 18 | 19 | const ( 20 | PhaseCreated = "Created" 21 | PhasePending = "Pending" 22 | PhaseCleaning = "Cleaning" 23 | PhaseResizing = "Resizing" 24 | PhaseFailed = "Failed" 25 | PhaseNotReady = "NotReady" 26 | PhaseReady = "Ready" 27 | PhaseTerminating = "Terminating" 28 | 29 | LLVSNameTag = "storage.deckhouse.io/lvmLogicalVolumeSnapshotName" 30 | ) 31 | -------------------------------------------------------------------------------- /api/v1alpha1/lvm_logical_volume.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2025 Flant JSC 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package v1alpha1 18 | 19 | import ( 20 | "k8s.io/apimachinery/pkg/api/resource" 21 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 22 | ) 23 | 24 | type LVMLogicalVolumeList struct { 25 | metav1.TypeMeta `json:",inline"` 26 | metav1.ListMeta `json:"metadata"` 27 | 28 | Items []LVMLogicalVolume `json:"items"` 29 | } 30 | 31 | type LVMLogicalVolume struct { 32 | metav1.TypeMeta `json:",inline"` 33 | metav1.ObjectMeta `json:"metadata,omitempty"` 34 | 35 | Spec LVMLogicalVolumeSpec `json:"spec"` 36 | Status *LVMLogicalVolumeStatus `json:"status,omitempty"` 37 | } 38 | 39 | const ( 40 | VolumeCleanupDiscard = "Discard" 41 | VolumeCleanupRandomFillSinglePass = "RandomFillSinglePass" 42 | VolumeCleanupRandomFillThreePass = "RandomFillThreePass" 43 | ) 44 | 45 | type LVMLogicalVolumeSpec struct { 46 | ActualLVNameOnTheNode string `json:"actualLVNameOnTheNode"` 47 | Type string `json:"type"` 48 | Size string `json:"size"` 49 | LVMVolumeGroupName string `json:"lvmVolumeGroupName"` 50 | Source *LVMLogicalVolumeSource `json:"source"` 51 | Thin *LVMLogicalVolumeThinSpec `json:"thin"` 52 | Thick *LVMLogicalVolumeThickSpec `json:"thick"` 53 | VolumeCleanup *string `json:"volumeCleanup,omitempty"` 54 | } 55 | 56 | type LVMLogicalVolumeThinSpec struct { 57 | PoolName string `json:"poolName"` 58 | } 59 | 60 | type LVMLogicalVolumeThickSpec struct { 61 | Contiguous *bool `json:"contiguous,omitempty"` 62 | } 63 | type LVMLogicalVolumeStatus struct { 64 | Phase string `json:"phase"` 65 | Reason string `json:"reason"` 66 | ActualSize resource.Quantity `json:"actualSize"` 67 | Contiguous *bool `json:"contiguous"` 68 | } 69 | 70 | type LVMLogicalVolumeSource struct { 71 | // Either LVMLogicalVolume or LVMLogicalVolumeSnapshot 72 | Kind string `json:"kind"` 73 | Name string `json:"name"` 74 | } 75 | -------------------------------------------------------------------------------- /api/v1alpha1/lvm_logical_volume_snapshot.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2025 Flant JSC 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package v1alpha1 18 | 19 | import ( 20 | "k8s.io/apimachinery/pkg/api/resource" 21 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 22 | ) 23 | 24 | // +k8s:deepcopy-gen=true 25 | // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object 26 | type LVMLogicalVolumeSnapshotList struct { 27 | metav1.TypeMeta `json:",inline"` 28 | metav1.ListMeta `json:"metadata"` 29 | 30 | Items []LVMLogicalVolumeSnapshot `json:"items"` 31 | } 32 | 33 | // +k8s:deepcopy-gen=true 34 | // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object 35 | type LVMLogicalVolumeSnapshot struct { 36 | metav1.TypeMeta `json:",inline"` 37 | metav1.ObjectMeta `json:"metadata,omitempty"` 38 | 39 | Spec LVMLogicalVolumeSnapshotSpec `json:"spec"` 40 | Status *LVMLogicalVolumeSnapshotStatus `json:"status,omitempty"` 41 | } 42 | 43 | func (llvs *LVMLogicalVolumeSnapshot) ActualSnapshotNameOnTheNode() string { 44 | if llvs.Spec.ActualSnapshotNameOnTheNode != "" { 45 | return llvs.Spec.ActualSnapshotNameOnTheNode 46 | } 47 | return llvs.Name 48 | } 49 | 50 | // +k8s:deepcopy-gen=true 51 | type LVMLogicalVolumeSnapshotSpec struct { 52 | ActualSnapshotNameOnTheNode string `json:"actualSnapshotNameOnTheNode"` 53 | LVMLogicalVolumeName string `json:"lvmLogicalVolumeName"` 54 | } 55 | 56 | // +k8s:deepcopy-gen=true 57 | type LVMLogicalVolumeSnapshotStatus struct { 58 | NodeName string `json:"nodeName"` 59 | ActualVGNameOnTheNode string `json:"actualVGNameOnTheNode"` 60 | ActualLVNameOnTheNode string `json:"actualLVNameOnTheNode"` 61 | Phase string `json:"phase"` 62 | Reason string `json:"reason"` 63 | Size resource.Quantity `json:"size"` 64 | UsedSize resource.Quantity `json:"usedSize"` 65 | } 66 | -------------------------------------------------------------------------------- /api/v1alpha1/lvm_volume_group.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2025 Flant JSC 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package v1alpha1 18 | 19 | import ( 20 | "k8s.io/apimachinery/pkg/api/resource" 21 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 22 | ) 23 | 24 | type LVMVolumeGroupList struct { 25 | metav1.TypeMeta `json:",inline"` 26 | metav1.ListMeta `json:"metadata"` 27 | 28 | Items []LVMVolumeGroup `json:"items"` 29 | } 30 | 31 | type LVMVolumeGroup struct { 32 | metav1.TypeMeta `json:",inline"` 33 | metav1.ObjectMeta `json:"metadata,omitempty"` 34 | 35 | Spec LVMVolumeGroupSpec `json:"spec"` 36 | Status LVMVolumeGroupStatus `json:"status,omitempty"` 37 | } 38 | 39 | type LVMVolumeGroupSpec struct { 40 | ActualVGNameOnTheNode string `json:"actualVGNameOnTheNode"` 41 | BlockDeviceSelector *metav1.LabelSelector `json:"blockDeviceSelector"` 42 | ThinPools []LVMVolumeGroupThinPoolSpec `json:"thinPools"` 43 | Type string `json:"type"` 44 | Local LVMVolumeGroupLocalSpec `json:"local"` 45 | } 46 | 47 | type LVMVolumeGroupStatus struct { 48 | AllocatedSize resource.Quantity `json:"allocatedSize"` 49 | Nodes []LVMVolumeGroupNode `json:"nodes"` 50 | ThinPools []LVMVolumeGroupThinPoolStatus `json:"thinPools"` 51 | VGSize resource.Quantity `json:"vgSize"` 52 | VGUuid string `json:"vgUUID"` 53 | Phase string `json:"phase"` 54 | Conditions []metav1.Condition `json:"conditions"` 55 | ThinPoolReady string `json:"thinPoolReady"` 56 | ConfigurationApplied string `json:"configurationApplied"` 57 | VGFree resource.Quantity `json:"vgFree"` 58 | } 59 | 60 | type LVMVolumeGroupDevice struct { 61 | BlockDevice string `json:"blockDevice"` 62 | DevSize resource.Quantity `json:"devSize"` 63 | PVSize resource.Quantity `json:"pvSize"` 64 | PVUuid string `json:"pvUUID"` 65 | Path string `json:"path"` 66 | } 67 | 68 | type LVMVolumeGroupNode struct { 69 | Devices []LVMVolumeGroupDevice `json:"devices"` 70 | Name string `json:"name"` 71 | } 72 | 73 | type LVMVolumeGroupThinPoolStatus struct { 74 | Name string `json:"name"` 75 | ActualSize resource.Quantity `json:"actualSize"` 76 | UsedSize resource.Quantity `json:"usedSize"` 77 | AllocatedSize resource.Quantity `json:"allocatedSize"` 78 | AvailableSpace resource.Quantity `json:"availableSpace"` 79 | AllocationLimit string `json:"allocationLimit"` 80 | Ready bool `json:"ready"` 81 | Message string `json:"message"` 82 | } 83 | 84 | type LVMVolumeGroupThinPoolSpec struct { 85 | Name string `json:"name"` 86 | Size string `json:"size"` 87 | AllocationLimit string `json:"allocationLimit"` 88 | } 89 | 90 | type LVMVolumeGroupLocalSpec struct { 91 | NodeName string `json:"nodeName"` 92 | } 93 | -------------------------------------------------------------------------------- /api/v1alpha1/lvm_volume_group_set.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2025 Flant JSC 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package v1alpha1 18 | 19 | import ( 20 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 21 | ) 22 | 23 | type LVMVolumeGroupSetList struct { 24 | metav1.TypeMeta `json:",inline"` 25 | metav1.ListMeta `json:"metadata"` 26 | 27 | Items []LVMVolumeGroupSet `json:"items"` 28 | } 29 | 30 | type LVMVolumeGroupSet struct { 31 | metav1.TypeMeta `json:",inline"` 32 | metav1.ObjectMeta `json:"metadata,omitempty"` 33 | 34 | Spec LVMVolumeGroupSetSpec `json:"spec"` 35 | Status LVMVolumeGroupSetStatus `json:"status,omitempty"` 36 | } 37 | 38 | type LVMVolumeGroupSetSpec struct { 39 | NodeSelector *metav1.LabelSelector `json:"nodeSelector"` 40 | LVGTemplate LVMVolumeGroupTemplate `json:"lvmVolumeGroupTemplate"` 41 | Strategy string `json:"strategy"` 42 | } 43 | type LVMVolumeGroupTemplate struct { 44 | Metadata LVMVolumeGroupTemplateMeta `json:"metadata"` 45 | BlockDeviceSelector *metav1.LabelSelector `json:"blockDeviceSelector"` 46 | ActualVGNameOnTheNode string `json:"actualVGNameOnTheNode"` 47 | ThinPools []LVMVolumeGroupThinPoolSpec `json:"thinPools"` 48 | Type string `json:"type"` 49 | } 50 | 51 | type LVMVolumeGroupTemplateMeta struct { 52 | Labels map[string]string `json:"labels"` 53 | } 54 | 55 | type LVMVolumeGroupSetStatus struct { 56 | CreatedLVGs []LVMVolumeGroupSetStatusLVG `json:"createdLVMVolumeGroups"` 57 | CurrentLVMVolumeGroupsCount int `json:"currentLVMVolumeGroupsCount"` 58 | DesiredLVMVolumeGroupsCount int `json:"desiredLVMVolumeGroupsCount"` 59 | Phase string `json:"phase"` 60 | Reason string `json:"reason"` 61 | } 62 | 63 | type LVMVolumeGroupSetStatusLVG struct { 64 | LVMVolumeGroupName string `json:"lvmVolumeGroupName"` 65 | NodeName string `json:"nodeName"` 66 | } 67 | -------------------------------------------------------------------------------- /api/v1alpha1/register.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2025 Flant JSC 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package v1alpha1 18 | 19 | import ( 20 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 21 | "k8s.io/apimachinery/pkg/runtime" 22 | "k8s.io/apimachinery/pkg/runtime/schema" 23 | ) 24 | 25 | const ( 26 | APIGroup = "storage.deckhouse.io" 27 | APIVersion = "v1alpha1" 28 | ) 29 | 30 | // SchemeGroupVersion is group version used to register these objects 31 | var ( 32 | SchemeGroupVersion = schema.GroupVersion{ 33 | Group: APIGroup, 34 | Version: APIVersion, 35 | } 36 | SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes) 37 | AddToScheme = SchemeBuilder.AddToScheme 38 | ) 39 | 40 | var knownTypes = []runtime.Object{ 41 | &BlockDevice{}, 42 | &BlockDeviceList{}, 43 | &LVMVolumeGroup{}, 44 | &LVMVolumeGroupList{}, 45 | &LVMLogicalVolume{}, 46 | &LVMLogicalVolumeList{}, 47 | &LVMVolumeGroupSet{}, 48 | &LVMVolumeGroupSetList{}, 49 | &LVMLogicalVolumeSnapshot{}, 50 | &LVMLogicalVolumeSnapshotList{}, 51 | } 52 | 53 | // Adds the list of known types to Scheme. 54 | func addKnownTypes(scheme *runtime.Scheme) error { 55 | scheme.AddKnownTypes(SchemeGroupVersion, knownTypes...) 56 | metav1.AddToGroupVersion(scheme, SchemeGroupVersion) 57 | return nil 58 | } 59 | -------------------------------------------------------------------------------- /charts/deckhouse_lib_helm-1.41.0.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/deckhouse/sds-node-configurator/1f31870512024aef8129327c923b5580c5ef243b/charts/deckhouse_lib_helm-1.41.0.tgz -------------------------------------------------------------------------------- /crds/doc-ru-blockdevices.yaml: -------------------------------------------------------------------------------- 1 | spec: 2 | versions: 3 | - name: v1alpha1 4 | schema: 5 | openAPIV3Schema: 6 | description: | 7 | Ресурс отображает актуальную информацию о доступных блочных устройствах на узлах, которые могут быть использованы контроллером sds-node-configurator для операций в LVM. 8 | 9 | > Ресурс создается и обслуживается только контроллером. Ручное изменение ресурса запрещено. 10 | properties: 11 | status: 12 | properties: 13 | type: 14 | description: | 15 | Тип девайса (диск, партиция, RAID и другие). 16 | fsType: 17 | description: | 18 | Тип файловой системы. 19 | nodeName: 20 | description: | 21 | Имя Kubernetes-узла, на котором находится девайс. 22 | consumable: 23 | description: | 24 | Флаг, определяющий, может ли данный девайс быть использован контроллером для создания LVM Physical Volume. 25 | pvUUID: 26 | description: | 27 | LVM Physical Volume UUID. 28 | 29 | > Опциально: присутствует, только если девайс используется как LVM Physical Volume. 30 | vgUUID: 31 | description: | 32 | LVM Volume Group UUID, которой принадлежит данный девайс в качестве Physical Volume. 33 | 34 | > Опциально: присутствует, только если девайс используется как Physical Volume и включен в Volume Group. 35 | partUUID: 36 | description: | 37 | UUID раздела. 38 | 39 | > Опциально: присутствует, только если девайс является разделом. 40 | lvmVolumeGroupName: 41 | description: | 42 | Имя ресурса LVMVolumeGroup. 43 | 44 | > Опциально: присутствует, только если девайс используется как Physical Volume, включен в Volume Group и указан в LVMVolumeGroup-ресурсе. 45 | actualVGNameOnTheNode: 46 | description: | 47 | Фактическое имя LVM Volume Group на узле, в которую включен девайс. 48 | 49 | > Опциально: присутствует, только если девайс используется как Physical Volume и включен в Volume Group. 50 | wwn: 51 | description: | 52 | Уникальный идентификатор девайса WWN (World Wide Name). 53 | 54 | > Опционально: может отсутствовать, если WWN для данного типа дисков не поддерживается производителем. 55 | serial: 56 | description: | 57 | Уникальный серийный номер девайса. 58 | path: 59 | description: | 60 | Путь девайса на узле (/dev/sda). 61 | size: 62 | description: | 63 | Размер девайса. 64 | model: 65 | description: | 66 | Модель девайса. 67 | 68 | > Опционально: может отсутствовать, если для данного типа дисков не поддерживается производителем. 69 | rota: 70 | description: | 71 | Медиатип девайса. Может быть: 72 | - true (для HHD) 73 | - false (для SSD) 74 | hotPlug: 75 | description: | 76 | Тип подключения девайса. Может быть: 77 | - true (если девайс был подключен как съемный) 78 | - false (в иных случаях) 79 | machineId: 80 | description: | 81 | Уникальный идентификатор узла, на котором располагается девайс (обычно хранится в /etc/machine-id). 82 | -------------------------------------------------------------------------------- /crds/doc-ru-lvmvolumegroupbackup.yaml: -------------------------------------------------------------------------------- 1 | spec: 2 | versions: 3 | - name: v1alpha1 4 | schema: 5 | openAPIV3Schema: 6 | description: | 7 | Ресурс, сохраняющий состояние lvmvolumegroups ресурсов для совершения миграции kind с LvmVolumeGroup до LVMVolumeGroup. -------------------------------------------------------------------------------- /cspell.config.yaml: -------------------------------------------------------------------------------- 1 | $schema: https://raw.githubusercontent.com/streetsidesoftware/cspell/main/cspell.schema.json 2 | version: "0.2" 3 | dictionaries: 4 | - en 5 | - terms 6 | - companies 7 | dictionaryDefinitions: 8 | - name: terms 9 | description: Special terms we use 10 | path: hack/cspell/terms.txt 11 | - name: companies 12 | description: Company names mentioned 13 | path: hack/cspell/companies.txt 14 | ignorePaths: 15 | - hack/cspell/ 16 | allowCompoundWords: true 17 | ignoreRegExpList: 18 | - '["\t^]--?\w+["\t$]' 19 | - '[a-zA-Z0-9]{6}-[a-zA-Z0-9]{4}-[a-zA-Z0-9]{4}-[a-zA-Z0-9]{4}-[a-zA-Z0-9]{4}-[a-zA-Z0-9]{4}-[a-zA-Z0-9]{6}' 20 | -------------------------------------------------------------------------------- /docs/CONFIGURATION.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "The sds-node-configurator module: settings" 3 | description: "Settings of the sds-node-configurator module. Deckhouse Kubernetes Platform." 4 | --- 5 | 6 | {{< alert level="warning" >}} 7 | The module is guaranteed to work only with stock kernels that are shipped with the [supported distributions](https://deckhouse.io/documentation/v1/supported_versions.html#linux). 8 | 9 | The module may work with other kernels or distributions, but its stable operation and availability of all features is not guaranteed. 10 | {{< /alert >}} 11 | -------------------------------------------------------------------------------- /docs/CONFIGURATION.ru.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Модуль sds-node-configurator: настройки" 3 | description: "Настройки модуля sds-node-configurator, Deckhouse Kubernetes Platform." 4 | --- 5 | 6 | {{< alert level="warning" >}} 7 | Работоспособность модуля гарантируется только при использовании стоковых ядер, поставляемых вместе с [поддерживаемыми дистрибутивами](https://deckhouse.ru/documentation/v1/supported_versions.html#linux). 8 | 9 | Работоспособность модуля при использовании других ядер или дистрибутивов возможна, но не гарантируется. 10 | {{< /alert >}} 11 | -------------------------------------------------------------------------------- /docs/CR.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "The sds-node-configurator module: Custom Resources" 3 | description: "BlockDevice and LVMVolumeGroup custom resources. The sds-node-configurator module of Deckhouse Kubernetes Platform." 4 | --- 5 | 6 | {{< alert level="warning" >}} 7 | The module is guaranteed to work only with stock kernels that are shipped with the [supported distributions](https://deckhouse.io/documentation/v1/supported_versions.html#linux). 8 | 9 | The module may work with other kernels or distributions, but its stable operation and availability of all features is not guaranteed. 10 | {{< /alert >}} 11 | -------------------------------------------------------------------------------- /docs/CR.ru.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Модуль sds-node-configurator: Custom Resources" 3 | description: "Ресурсы BlockDevice и LVMVolumeGroup. Модуль sds-node-configurator, Deckhouse Kubernetes Platform." 4 | --- 5 | 6 | {{< alert level="warning" >}} 7 | Работоспособность модуля гарантируется только при использовании стоковых ядер, поставляемых вместе с [поддерживаемыми дистрибутивами](https://deckhouse.ru/documentation/v1/supported_versions.html#linux). 8 | 9 | Работоспособность модуля при использовании других ядер или дистрибутивов возможна, но не гарантируется. 10 | {{< /alert >}} 11 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "The sds-node-configurator module" 3 | description: "General Concepts and Principles of the sds-node-configurator module. Deckhouse Kubernetes Platform." 4 | --- 5 | 6 | {{< alert level="warning" >}} 7 | The module is guaranteed to work only with stock kernels that are shipped with the [supported distributions](https://deckhouse.io/documentation/v1/supported_versions.html#linux). 8 | 9 | The module may work with other kernels or distributions, but its stable operation and availability of all features is not guaranteed. 10 | {{< /alert >}} 11 | 12 | The module manages `LVM` on cluster nodes through [Kubernetes custom resources](./cr.html) by performing the following operations: 13 | 14 | - Discovering block devices and creating/updating/deleting their corresponding [BlockDevice resources](./cr.html#blockdevice). 15 | 16 | > **Caution!** Manual creation and modification of the `BlockDevice` resource is prohibited. 17 | 18 | - Discovering `LVM Volume Groups` on the nodes with the `storage.deckhouse.io/enabled=true` LVM tag attached and `Thin-pools` running on them as well as managing the corresponding [LVMVolumeGroup resources](./cr.html#lvmvolumegroup). The module automatically creates an `LVMVolumeGroup` resource if it does not yet exist for a discovered `LVM Volume Group`. 19 | 20 | - Scanning `LVM Physical Volumes` on the nodes that are part of managed `LVM Volume Groups`. In case the size of underlying block device expands, the corresponding `LVM Physical Volumes` will be automatically expanded as well (`pvresize` will occur). 21 | 22 | > **Caution!** Downsizing a block device is not supported. 23 | 24 | - Creating/expanding/deleting `LVM Volume Groups` on the node according to the changes the user has made to the `LVMVolumeGroup` resources. [Usage examples](./usage.html#lvmvolumegroup-resources) 25 | -------------------------------------------------------------------------------- /docs/README.ru.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Модуль sds-node-configurator" 3 | description: "Концепция и принцип работы модуля sds-node-configurator, Deckhouse Kubernetes Platform." 4 | --- 5 | {{< alert level="warning" >}} 6 | Работоспособность модуля гарантируется только при использовании стоковых ядер, поставляемых вместе с [поддерживаемыми дистрибутивами](https://deckhouse.ru/documentation/v1/supported_versions.html#linux). 7 | 8 | Работоспособность модуля при использовании других ядер или дистрибутивов возможна, но не гарантируется. 9 | {{< /alert >}} 10 | 11 | Модуль управляет `LVM` на узлах кластера через [пользовательские ресурсы Kubernetes](./cr.html), выполняя следующие операции: 12 | 13 | - Обнаружение блочных устройств и создание/обновление/удаление соответствующих им [ресурсов BlockDevice](./cr.html#blockdevice). 14 | 15 | > **Внимание!** Ручное создание и изменение ресурса `BlockDevice` запрещено. 16 | 17 | - Обнаружение на узлах `LVM Volume Group` с LVM тегом `storage.deckhouse.io/enabled=true` и `Thin-pool` на них, а также управление соответствующими [ресурсами LVMVolumeGroup](./cr.html#lvmvolumegroup). Модуль автоматически создает ресурс `LVMVolumeGroup`, если его еще не существует для обнаруженной `LVM Volume Group`. 18 | 19 | - Сканирование на узлах `LVM Physical Volumes`, которые входят в управляемые `LVM Volume Group`. В случае расширения размеров нижестоящих блочных устройств, соотвующие `LVM Physical Volumes` будут автоматически расширены (произойдёт `pvresize`). 20 | 21 | > **Внимание!** Уменьшение размеров блочного устройства не поддерживается. 22 | 23 | - Создание/расширение/удаление `LVM Volume Group` на узле в соответствии с пользовательскими изменениями в ресурсах `LVMVolumeGroup`. [Примеры использования](./usage.html#работа-с-ресурсами-lvmvolumegroup) 24 | -------------------------------------------------------------------------------- /docs/images/sds-node-configurator-scenaries.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/deckhouse/sds-node-configurator/1f31870512024aef8129327c923b5580c5ef243b/docs/images/sds-node-configurator-scenaries.png -------------------------------------------------------------------------------- /docs/images/sds-node-configurator-scenaries.puml: -------------------------------------------------------------------------------- 1 | @startuml 2 | 3 | !theme bluegray 4 | skinparam DefaultFontSize 16 5 | skinparam handwritten false 6 | skinparam ArrowFontStyle italic 7 | skinparam DefaultTextAlignment center 8 | skinparam PartitionFontColor grey 9 | skinparam backgroundColor transparent 10 | 11 | start 12 | 13 | :Selecting a scenario; 14 | 15 | if (Is the "Full mirror" scenario selected (recommended)?) then (Yes) 16 | 17 | group "Full mirror" 18 | :Creating a mirror 19 | of entire disks; 20 | :Creating a VG on the mirror; 21 | :Creating an LV in the VG; 22 | :Installing the operating system on the LV; 23 | :Assigning the storage.deckhouse.io/enabled=true tag 24 | to the VG; 25 | end group 26 | 27 | else (No) 28 | 29 | group "Partial mirror" 30 | :Creating two partitions 31 | on each disk; 32 | :Creating a mirror 33 | of the first partitions; 34 | :Creating a VG on the mirror; 35 | :Creating an LV in the VG on the mirror; 36 | :Installing the operating system on the LV in the VG on the mirror; 37 | :Assigning the storage.deckhouse.io/enabled=true tag 38 | to the VG on the mirror; 39 | :Creating a VG of the second partitions on each disk; 40 | :Assigning the storage.deckhouse.io/enabled=true tag 41 | to the VG of the second partitions; 42 | end group 43 | 44 | endif 45 | 46 | :Adding a node to the Deckhouse cluster; 47 | :Configuring the SDS modules; 48 | 49 | stop 50 | 51 | @enduml 52 | -------------------------------------------------------------------------------- /docs/images/sds-node-configurator-scenaries.ru.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/deckhouse/sds-node-configurator/1f31870512024aef8129327c923b5580c5ef243b/docs/images/sds-node-configurator-scenaries.ru.png -------------------------------------------------------------------------------- /docs/images/sds-node-configurator-scenaries.ru.puml: -------------------------------------------------------------------------------- 1 | @startuml 2 | 3 | !theme bluegray 4 | skinparam DefaultFontSize 16 5 | skinparam handwritten false 6 | skinparam ArrowFontStyle italic 7 | skinparam DefaultTextAlignment center 8 | skinparam PartitionFontColor grey 9 | skinparam backgroundColor transparent 10 | 11 | start 12 | 13 | :Выбор сценария; 14 | 15 | if (Выбран сценарий «Полное зеркало» (рекомендованный)?) then (да) 16 | 17 | group "Сценарий Полное зеркало" 18 | :Формирование зеркала 19 | из дисков целиком; 20 | :Создание VG на зеркале; 21 | :Создание LV в VG; 22 | :Установка ОС на LV; 23 | :Установка тега storage.deckhouse.io/enabled=true 24 | на VG; 25 | end group 26 | 27 | else (нет) 28 | 29 | group "Сценарий Частичное зеркало" 30 | :Создание двух разделов 31 | на каждом диске; 32 | :Формирование зеркала 33 | из первых разделов дисков; 34 | :Создание VG на зеркале; 35 | :Создание LV в VG на зеркале; 36 | :Установка ОС на LV в VG на зеркале; 37 | :Установка тега storage.deckhouse.io/enabled=true 38 | на VG на зеркале; 39 | :Создание VG из вторых разделов каждого диска; 40 | :Установка тега storage.deckhouse.io/enabled=true 41 | на VG из вторых разделов; 42 | end group 43 | 44 | endif 45 | 46 | :Добавление узла в кластер Deckhouse; 47 | :Настройка модулей SDS; 48 | 49 | stop 50 | 51 | @enduml 52 | -------------------------------------------------------------------------------- /hack/boilerplate.txt: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright YEAR Flant JSC 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ -------------------------------------------------------------------------------- /hack/cspell/companies.txt: -------------------------------------------------------------------------------- 1 | Flant 2 | -------------------------------------------------------------------------------- /hack/cspell/terms.txt: -------------------------------------------------------------------------------- 1 | actualvgnameonthenode 2 | APILVM 3 | apimachinery 4 | apiserver 5 | Bldr 6 | CDROM 7 | clientgoscheme 8 | configurator 9 | dmsetup 10 | extv 11 | finalizers 12 | fstype 13 | golangci 14 | golog 15 | goruntime 16 | healthz 17 | k8serr 18 | KNAME 19 | kubutils 20 | LLVS 21 | Loglevel 22 | lvchange 23 | lvextend 24 | lvgs 25 | LVMFS 26 | LVMVG 27 | lvmvolumegroup 28 | lvmvolumegroupname 29 | lvmvolumegroups 30 | lvremove 31 | machineid 32 | metav 33 | mpath 34 | mpatha 35 | NSENTER 36 | nsentrer 37 | nvme 38 | partuuid 39 | PKNAME 40 | pvremove 41 | pvresize 42 | PVUUID 43 | Readyz 44 | stretchr 45 | tdata 46 | tmeta 47 | vgchange 48 | vgextend 49 | vgremove 50 | vgtest 51 | VGUID 52 | VGUUID 53 | -------------------------------------------------------------------------------- /hack/for-each-mod: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Copyright 2025 Flant JSC 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | # Runs command in each folder with go.mod file 18 | # 19 | # Examples: 20 | # Tidy all the modules: 21 | # `for-each-mod go mod tody` 22 | # Generate all the modules: 23 | # `for-each-mod go generate ./...` 24 | 25 | find -type f -name go.mod -execdir sh -c "$*" {} + 26 | -------------------------------------------------------------------------------- /hack/generate_code.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Copyright 2025 Flant JSC 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | echo "Running go generate" 18 | 19 | make go-generate -------------------------------------------------------------------------------- /hack/increase_semver.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # BORROWED FROM https://github.com/fmahnke/shell-semver 4 | # 5 | # ============================================================================== 6 | # The MIT License (MIT) 7 | # 8 | # Copyright (c) 2014 Fritz Mahnke 9 | # 10 | # Permission is hereby granted, free of charge, to any person obtaining a copy 11 | # of this software and associated documentation files (the "Software"), to deal 12 | # in the Software without restriction, including without limitation the rights 13 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | # copies of the Software, and to permit persons to whom the Software is 15 | # furnished to do so, subject to the following conditions: 16 | # 17 | # The above copyright notice and this permission notice shall be included in all 18 | # copies or substantial portions of the Software. 19 | # 20 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 26 | # SOFTWARE. 27 | # 28 | # ============================================================================== 29 | # 30 | # Increment a version string using Semantic Versioning (SemVer) terminology. 31 | # 32 | # $ ./increment_version.sh 33 | # usage: increment_version.sh [-Mmp] major.minor.patch 34 | # 35 | # $ ./increment_version.sh -p 0.0.0 36 | # 0.0.1 37 | # 38 | # $ ./increment_version.sh -m 0.0.3 39 | # 0.1.0 40 | # 41 | # $ ./increment_version.sh -M 1.1.15 42 | # 2.0.0 43 | # 44 | # $ ./increment_version.sh -Mmp 2.3.4 45 | # 3.1.1 46 | # 47 | # $ ./increment_version.sh -d 2.3.4 48 | # 2.3.4-dev.1691718412 49 | 50 | # Parse command line options. 51 | 52 | while getopts ":Mmpd" Option 53 | do 54 | case $Option in 55 | M ) major=true;; 56 | m ) minor=true;; 57 | p ) patch=true;; 58 | d ) dev=true;; 59 | esac 60 | done 61 | 62 | shift $(($OPTIND - 1)) 63 | 64 | # remove dev version 1.2.3-dev.321 -> 1.2.3 65 | version=$(sed -E 's/-.*//' <<< $1) 66 | 67 | # Build array from version string. 68 | 69 | a=( ${version//./ } ) 70 | 71 | dev_version="" 72 | 73 | # If version string is missing or has the wrong number of members, show usage message. 74 | 75 | if [ ${#a[@]} -ne 3 ] 76 | then 77 | echo "usage: $(basename $0) [-Mmp] major.minor.patch" 78 | exit 1 79 | fi 80 | 81 | # Increment version numbers as requested. 82 | 83 | if [ ! -z $major ] 84 | then 85 | ((a[0]++)) 86 | a[1]=0 87 | a[2]=0 88 | fi 89 | 90 | if [ ! -z $minor ] 91 | then 92 | ((a[1]++)) 93 | a[2]=0 94 | fi 95 | 96 | if [ ! -z $patch ] 97 | then 98 | ((a[2]++)) 99 | fi 100 | 101 | if [ ! -z $dev ] 102 | then 103 | dev_version="-dev.$(date +%s)" 104 | fi 105 | 106 | echo "${a[0]}.${a[1]}.${a[2]}${dev_version}" 107 | -------------------------------------------------------------------------------- /hack/readme.md: -------------------------------------------------------------------------------- 1 | # Development tools 2 | 3 | ## `pre-commit` tool 4 | 5 | [pre-commit](https://pre-commit.com/) is a tool to run linters or any other scripts automatically on `git commit` or by hands. See [.pre-commit-config.yaml](/.pre-commit-config.yaml) 6 | 7 | ### Installing pre-commit hook 8 | 9 | To install hooks 10 | 11 | ```sh 12 | pre-commit install 13 | ``` 14 | 15 | Remove hooks 16 | 17 | ```sh 18 | pre-commit unistall 19 | ``` 20 | 21 | To commit without hooks git has `--no-verify` argument 22 | 23 | ```sh 24 | git commit --no-verify 25 | ``` 26 | 27 | To skip some checks `SKIP` environment variable is respected by `pre-commit` tool 28 | 29 | ```sh 30 | SKIP=go-mod-tidy git commit <...> 31 | ``` 32 | 33 | ### Running hooks manually 34 | 35 | To run pre-commit all hooks on staged files 36 | 37 | ```sh 38 | pre-commit run 39 | ``` 40 | 41 | To select tools to run 42 | 43 | ```sh 44 | pre-commit run go-mod-tidy 45 | ``` 46 | 47 | To run on all the files regardles if they changed or not there is `--all-files` argument 48 | 49 | ```sh 50 | pre-commit run [check_to_run] --all-files 51 | ``` 52 | 53 | ## `for-each-mod` tool 54 | 55 | We have multiple go packages in our repo. To run command in all of them at once we have a [tool](/hack/for-each-mod) to run the same command in all the folder with go.mod file. 56 | 57 | For example: 58 | 59 | ```sh 60 | ./hack/for-each-mod go mod tidy 61 | ``` 62 | 63 | ## Code generation 64 | 65 | To generate code we are using `go generate` 66 | 67 | ### Specifying generation command 68 | 69 | To add generation command in `.go` file add a comment 70 | 71 | ```go 72 | //go:generate 73 | ``` 74 | 75 | For example to generate mock file: 76 | 77 | ```go 78 | //go:generate go run go.uber.org/mock/mockgen -copyright_file ../../../../hack/boilerplate.txt -write_source_comment -destination=../mock_utils/$GOFILE -source=$GOFILE 79 | ``` 80 | 81 | To make tool available 82 | 83 | 1. Add it to go.mod: 84 | 85 | ```sh 86 | go get go.uber.org/mock/mockgen 87 | ``` 88 | 89 | 2. Make a fake usage adding `tools.go`. Without this usage `go mod tidy` will remove it from `go.mod` 90 | 91 | ```go 92 | //go:build tools 93 | // +build tools 94 | 95 | /* LICENSE HERE */ 96 | 97 | package tools 98 | 99 | import _ "go.uber.org/mock/mockgen" 100 | ``` 101 | 102 | ### Running generation command 103 | 104 | To run in single module 105 | 106 | ```sh 107 | go generate ./... 108 | ``` 109 | 110 | To run for all the modules 111 | 112 | ```sh 113 | ./hack/for-each-mod go generate ./... 114 | ``` 115 | 116 | ## CSpell 117 | 118 | We added very simple [CSpell](https://cspell.org/) configuration [file](/cspell.config.yaml). So now we can keep dictionaries in the repository 119 | 120 | To make use of then there are VSCode plugins: 121 | 122 | - [Code Spell Checker](https://marketplace.visualstudio.com/items?itemName=streetsidesoftware.code-spell-checker) 123 | - [Russian - Code Spell Checker](https://marketplace.visualstudio.com/items?itemName=streetsidesoftware.code-spell-checker-russian) 124 | 125 | To run from console the [cspell-cli](https://github.com/streetsidesoftware/cspell-cli) tool available 126 | -------------------------------------------------------------------------------- /hooks/go/020-webhook-certs/webhook-certs.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2025 Flant JSC 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package hooks_common 18 | 19 | import ( 20 | "fmt" 21 | 22 | tlscertificate "github.com/deckhouse/module-sdk/common-hooks/tls-certificate" 23 | consts "github.com/deckhouse/sds-node-configurator/hooks/go/consts" 24 | ) 25 | 26 | var _ = tlscertificate.RegisterInternalTLSHookEM(tlscertificate.GenSelfSignedTLSHookConf{ 27 | CommonCACanonicalName: fmt.Sprintf("%s-%s", consts.ModulePluralName, consts.WebhookCertCn), 28 | CN: consts.WebhookCertCn, 29 | TLSSecretName: fmt.Sprintf("%s-https-certs", consts.WebhookCertCn), 30 | Namespace: consts.ModuleNamespace, 31 | SANs: tlscertificate.DefaultSANs([]string{ 32 | consts.WebhookCertCn, 33 | fmt.Sprintf("%s.%s", consts.WebhookCertCn, consts.ModuleNamespace), 34 | fmt.Sprintf("%s.%s.svc", consts.WebhookCertCn, consts.ModuleNamespace), 35 | // %CLUSTER_DOMAIN%:// is a special value to generate SAN like 'svc_name.svc_namespace.svc.cluster.local' 36 | fmt.Sprintf("%%CLUSTER_DOMAIN%%://%s.%s.svc", consts.WebhookCertCn, consts.ModuleNamespace), 37 | }), 38 | FullValuesPathPrefix: fmt.Sprintf("%s.internal.customWebhookCert", consts.ModuleName), 39 | }) 40 | -------------------------------------------------------------------------------- /hooks/go/consts/consts.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2025 Flant JSC 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package consts 18 | 19 | const ( 20 | ModuleName string = "sdsNodeConfigurator" 21 | ModuleNamespace string = "d8-sds-node-configurator" 22 | ModulePluralName string = "sds-node-configurator" 23 | WebhookCertCn string = "webhooks" 24 | ) 25 | -------------------------------------------------------------------------------- /hooks/go/main.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2025 Flant JSC 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package main 18 | 19 | import ( 20 | "github.com/deckhouse/module-sdk/pkg/app" 21 | _ "github.com/deckhouse/sds-node-configurator/hooks/go/020-webhook-certs" 22 | ) 23 | 24 | func main() { 25 | app.Run() 26 | } 27 | -------------------------------------------------------------------------------- /images/agent/README.md: -------------------------------------------------------------------------------- 1 | # sds-node-configurator 2 | 3 | #### local run 4 | ``make run`` 5 | 6 | #### run go test 7 | ``make test`` 8 | 9 | #### run go tests with total coverage calculating 10 | ``make test-cover`` 11 | 12 | #### run go linter 13 | ``make lint`` 14 | 15 | #### docker image build 16 | ``make build`` 17 | 18 | #### remove docker image 19 | ``make remove`` 20 | 21 | #### ENV 22 | 23 | `NODE_NAME` 24 | 25 | `METRICS_PORT` - default : 4202 26 | 27 | 28 | #### Metrics 29 | ``` cs_available_block_device ``` 30 | -------------------------------------------------------------------------------- /images/agent/cmd/llvs_ce.go: -------------------------------------------------------------------------------- 1 | //go:build ce 2 | 3 | /* 4 | Copyright 2025 Flant JSC 5 | 6 | Licensed under the Apache License, Version 2.0 (the "License"); 7 | you may not use this file except in compliance with the License. 8 | You may obtain a copy of the License at 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, software 13 | distributed under the License is distributed on an "AS IS" BASIS, 14 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | See the License for the specific language governing permissions and 16 | limitations under the License. 17 | */ 18 | 19 | package main 20 | 21 | func addLLVSReconciler(_ ...any) { 22 | // noop 23 | } 24 | -------------------------------------------------------------------------------- /images/agent/cmd/llvs_ee.go: -------------------------------------------------------------------------------- 1 | //go:build !ce 2 | 3 | /* 4 | Copyright 2025 Flant JSC 5 | 6 | Licensed under the Deckhouse Platform Enterprise Edition (EE) license. 7 | you may not use this file except in compliance with the License. 8 | You may obtain a copy of the License at 9 | 10 | https://github.com/deckhouse/deckhouse/blob/main/ee/LICENSE 11 | */ 12 | 13 | package main 14 | 15 | import ( 16 | "os" 17 | 18 | "sigs.k8s.io/controller-runtime/pkg/manager" 19 | 20 | "github.com/deckhouse/sds-node-configurator/images/agent/internal/cache" 21 | "github.com/deckhouse/sds-node-configurator/images/agent/internal/config" 22 | "github.com/deckhouse/sds-node-configurator/images/agent/internal/controller" 23 | "github.com/deckhouse/sds-node-configurator/images/agent/internal/controller/llvs" 24 | "github.com/deckhouse/sds-node-configurator/images/agent/internal/logger" 25 | "github.com/deckhouse/sds-node-configurator/images/agent/internal/monitoring" 26 | "github.com/deckhouse/sds-node-configurator/images/agent/internal/utils" 27 | "github.com/deckhouse/sds-node-configurator/lib/go/common/pkg/feature" 28 | ) 29 | 30 | func addLLVSReconciler( 31 | mgr manager.Manager, 32 | log logger.Logger, 33 | metrics monitoring.Metrics, 34 | sdsCache *cache.Cache, 35 | commands utils.Commands, 36 | cfgParams *config.Config, 37 | ) { 38 | if !feature.SnapshotsEnabled() { 39 | log.Info("[addLLVSReconciler] Snapshot feature is disabled") 40 | return 41 | } 42 | 43 | log.Info("[addLLVSReconciler] Snapshot feature is enabled. Adding LLVS reconciler") 44 | 45 | err := controller.AddReconciler( 46 | mgr, 47 | log, 48 | llvs.NewReconciler( 49 | mgr.GetClient(), 50 | log, 51 | metrics, 52 | sdsCache, 53 | commands, 54 | llvs.ReconcilerConfig{ 55 | NodeName: cfgParams.NodeName, 56 | LLVRequeueInterval: cfgParams.LLVRequeueInterval, 57 | VolumeGroupScanInterval: cfgParams.VolumeGroupScanInterval, 58 | LLVSRequeueInterval: cfgParams.LLVSRequeueInterval, 59 | }, 60 | ), 61 | ) 62 | if err != nil { 63 | log.Error(err, "[main] unable to start llvs.NewReconciler") 64 | os.Exit(1) 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /images/agent/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/deckhouse/sds-node-configurator/images/agent 2 | 3 | go 1.24.2 4 | 5 | require ( 6 | github.com/deckhouse/sds-node-configurator/api v0.0.0-20250130211935-b68366dfd0f8 7 | github.com/deckhouse/sds-node-configurator/lib/go/common v0.0.0-00010101000000-000000000000 8 | github.com/go-logr/logr v1.4.2 9 | github.com/google/go-cmp v0.6.0 10 | github.com/gosimple/slug v1.14.0 11 | github.com/onsi/ginkgo/v2 v2.21.0 12 | github.com/onsi/gomega v1.35.1 13 | github.com/pilebones/go-udev v0.9.0 14 | github.com/prometheus/client_golang v1.19.1 15 | github.com/stretchr/testify v1.9.0 16 | go.uber.org/mock v0.5.0 17 | golang.org/x/sys v0.30.0 18 | k8s.io/api v0.32.1 19 | k8s.io/apiextensions-apiserver v0.32.0 20 | k8s.io/apimachinery v0.32.3 21 | k8s.io/client-go v0.32.1 22 | k8s.io/klog/v2 v2.130.1 23 | k8s.io/utils v0.0.0-20241210054802-24370beab758 24 | sigs.k8s.io/controller-runtime v0.20.1 25 | ) 26 | 27 | replace github.com/deckhouse/sds-node-configurator/api => ../../api 28 | 29 | replace github.com/deckhouse/sds-node-configurator/lib/go/common => ../../lib/go/common 30 | 31 | require ( 32 | github.com/beorn7/perks v1.0.1 // indirect 33 | github.com/cespare/xxhash/v2 v2.3.0 // indirect 34 | github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect 35 | github.com/emicklei/go-restful/v3 v3.12.1 // indirect 36 | github.com/evanphx/json-patch v5.6.0+incompatible // indirect 37 | github.com/evanphx/json-patch/v5 v5.9.11 // indirect 38 | github.com/fsnotify/fsnotify v1.7.0 // indirect 39 | github.com/fxamacker/cbor/v2 v2.7.0 // indirect 40 | github.com/go-openapi/jsonpointer v0.21.0 // indirect 41 | github.com/go-openapi/jsonreference v0.21.0 // indirect 42 | github.com/go-openapi/swag v0.23.0 // indirect 43 | github.com/go-task/slim-sprig/v3 v3.0.0 // indirect 44 | github.com/gogo/protobuf v1.3.2 // indirect 45 | github.com/golang/protobuf v1.5.4 // indirect 46 | github.com/google/btree v1.1.3 // indirect 47 | github.com/google/gnostic-models v0.6.9 // indirect 48 | github.com/google/gofuzz v1.2.0 // indirect 49 | github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db // indirect 50 | github.com/google/uuid v1.6.0 // indirect 51 | github.com/gosimple/unidecode v1.0.1 // indirect 52 | github.com/josharian/intern v1.0.0 // indirect 53 | github.com/json-iterator/go v1.1.12 // indirect 54 | github.com/mailru/easyjson v0.9.0 // indirect 55 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect 56 | github.com/modern-go/reflect2 v1.0.2 // indirect 57 | github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect 58 | github.com/pkg/errors v0.9.1 // indirect 59 | github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect 60 | github.com/prometheus/client_model v0.6.1 // indirect 61 | github.com/prometheus/common v0.55.0 // indirect 62 | github.com/prometheus/procfs v0.15.1 // indirect 63 | github.com/spf13/pflag v1.0.5 // indirect 64 | github.com/x448/float16 v0.8.4 // indirect 65 | golang.org/x/mod v0.21.0 // indirect 66 | golang.org/x/net v0.36.0 // indirect 67 | golang.org/x/oauth2 v0.26.0 // indirect 68 | golang.org/x/sync v0.11.0 // indirect 69 | golang.org/x/term v0.29.0 // indirect 70 | golang.org/x/text v0.22.0 // indirect 71 | golang.org/x/time v0.10.0 // indirect 72 | golang.org/x/tools v0.26.0 // indirect 73 | gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect 74 | google.golang.org/protobuf v1.36.5 // indirect 75 | gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect 76 | gopkg.in/inf.v0 v0.9.1 // indirect 77 | gopkg.in/yaml.v3 v3.0.1 // indirect 78 | k8s.io/kube-openapi v0.0.0-20241212222426-2c72e554b1e7 // indirect 79 | sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 // indirect 80 | sigs.k8s.io/structured-merge-diff/v4 v4.5.0 // indirect 81 | sigs.k8s.io/yaml v1.4.0 // indirect 82 | ) 83 | -------------------------------------------------------------------------------- /images/agent/internal/config/config_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2025 Flant JSC 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package config 18 | 19 | import ( 20 | "fmt" 21 | "os" 22 | "testing" 23 | 24 | "github.com/stretchr/testify/assert" 25 | ) 26 | 27 | func TestNewConfig(t *testing.T) { 28 | t.Run("AllValuesSet_ReturnsNoError", func(t *testing.T) { 29 | expNodeName := "test-node" 30 | expMetricsPort := ":0000" 31 | expMachineID := "test-id" 32 | 33 | err := os.Setenv(NodeName, expNodeName) 34 | if err != nil { 35 | t.Error(err) 36 | } 37 | err = os.Setenv(MetricsPort, expMetricsPort) 38 | if err != nil { 39 | t.Error(err) 40 | } 41 | err = os.Setenv(MachineID, expMachineID) 42 | if err != nil { 43 | t.Error(err) 44 | } 45 | defer os.Clearenv() 46 | 47 | opts, err := NewConfig() 48 | 49 | if assert.NoError(t, err) { 50 | assert.Equal(t, expNodeName, opts.NodeName) 51 | assert.Equal(t, expMetricsPort, opts.MetricsPort) 52 | assert.Equal(t, expMachineID, opts.MachineID) 53 | } 54 | }) 55 | 56 | t.Run("NodeNameNotSet_ReturnsError", func(t *testing.T) { 57 | machineIDFile := "./host-root/etc/machine-id" 58 | expMetricsPort := ":0000" 59 | expErrorMsg := fmt.Sprintf("[NewConfig] required %s env variable is not specified", NodeName) 60 | 61 | err := os.Setenv(MetricsPort, expMetricsPort) 62 | if err != nil { 63 | t.Error(err) 64 | } 65 | defer os.Clearenv() 66 | 67 | err = os.MkdirAll("./host-root/etc", 0750) 68 | if err != nil { 69 | t.Error(err) 70 | } 71 | 72 | file, err := os.Create(machineIDFile) 73 | if err != nil { 74 | t.Error(err) 75 | } 76 | defer func() { 77 | err = file.Close() 78 | if err != nil { 79 | t.Error(err) 80 | } 81 | 82 | err = os.RemoveAll("./host-root") 83 | if err != nil { 84 | t.Error(err) 85 | } 86 | }() 87 | 88 | _, err = NewConfig() 89 | assert.EqualError(t, err, expErrorMsg) 90 | }) 91 | 92 | t.Run("MachineIDNotSet_ReturnsError", func(t *testing.T) { 93 | expMetricsPort := ":0000" 94 | expNodeName := "test-node" 95 | expErrorMsg := fmt.Sprintf("[NewConfig] unable to get %s, error: %s", 96 | MachineID, "fork/exec /opt/deckhouse/sds/bin/nsenter.static: no such file or directory") 97 | 98 | err := os.Setenv(MetricsPort, expMetricsPort) 99 | if err != nil { 100 | t.Error(err) 101 | } 102 | err = os.Setenv(NodeName, expNodeName) 103 | if err != nil { 104 | t.Error(err) 105 | } 106 | defer os.Clearenv() 107 | 108 | _, err = NewConfig() 109 | assert.EqualError(t, err, expErrorMsg) 110 | }) 111 | 112 | t.Run("MetricsPortNotSet_ReturnsDefaultPort", func(t *testing.T) { 113 | expNodeName := "test-node" 114 | expMetricsPort := ":4202" 115 | expMachineID := "test-id" 116 | 117 | err := os.Setenv(NodeName, expNodeName) 118 | if err != nil { 119 | t.Error(err) 120 | } 121 | err = os.Setenv(MachineID, expMachineID) 122 | if err != nil { 123 | t.Error(err) 124 | } 125 | 126 | defer os.Clearenv() 127 | 128 | opts, err := NewConfig() 129 | 130 | if assert.NoError(t, err) { 131 | assert.Equal(t, expNodeName, opts.NodeName) 132 | assert.Equal(t, expMetricsPort, opts.MetricsPort) 133 | assert.Equal(t, expMachineID, opts.MachineID) 134 | } 135 | }) 136 | } 137 | -------------------------------------------------------------------------------- /images/agent/internal/controller/llv/llvs_ce.go: -------------------------------------------------------------------------------- 1 | //go:build ce 2 | 3 | /* 4 | Copyright 2025 Flant JSC 5 | 6 | Licensed under the Apache License, Version 2.0 (the "License"); 7 | you may not use this file except in compliance with the License. 8 | You may obtain a copy of the License at 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, software 13 | distributed under the License is distributed on an "AS IS" BASIS, 14 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | See the License for the specific language governing permissions and 16 | limitations under the License. 17 | */ 18 | 19 | package llv 20 | 21 | import ( 22 | "context" 23 | "errors" 24 | 25 | "github.com/deckhouse/sds-node-configurator/api/v1alpha1" 26 | ) 27 | 28 | func (r *Reconciler) handleLLVSSource(_ context.Context, _ *v1alpha1.LVMLogicalVolume, _ *v1alpha1.LVMVolumeGroup) (string, bool, error) { 29 | return "", false, errors.New("LVMLocalVolumeSnapshot as a source is not supported in your edition") 30 | } 31 | -------------------------------------------------------------------------------- /images/agent/internal/controller/llv/llvs_ee.go: -------------------------------------------------------------------------------- 1 | //go:build !ce 2 | 3 | /* 4 | Copyright 2025 Flant JSC 5 | 6 | Licensed under the Deckhouse Platform Enterprise Edition (EE) license. 7 | you may not use this file except in compliance with the License. 8 | You may obtain a copy of the License at 9 | 10 | https://github.com/deckhouse/deckhouse/blob/main/ee/LICENSE 11 | */ 12 | 13 | package llv 14 | 15 | import ( 16 | "context" 17 | "errors" 18 | "fmt" 19 | 20 | "k8s.io/apimachinery/pkg/types" 21 | 22 | "github.com/deckhouse/sds-node-configurator/api/v1alpha1" 23 | "github.com/deckhouse/sds-node-configurator/lib/go/common/pkg/feature" 24 | ) 25 | 26 | func (r *Reconciler) handleLLVSSource(ctx context.Context, llv *v1alpha1.LVMLogicalVolume, lvg *v1alpha1.LVMVolumeGroup) (string, bool, error) { 27 | if !feature.SnapshotsEnabled() { 28 | return "", false, errors.New("LVMLocalVolumeSnapshot as a source is not supported: snapshot feature is disabled") 29 | } 30 | 31 | sourceLLVS := &v1alpha1.LVMLogicalVolumeSnapshot{} 32 | if err := r.cl.Get(ctx, types.NamespacedName{Name: llv.Spec.Source.Name}, sourceLLVS); err != nil { 33 | r.log.Error(err, fmt.Sprintf("[reconcileLLVCreateFunc] unable to get source LVMLogicalVolumeSnapshot %s for the LVMLogicalVolume %s", llv.Spec.Source.Name, llv.Name)) 34 | return "", true, err 35 | } 36 | 37 | if sourceLLVS.Status.ActualVGNameOnTheNode != lvg.Spec.ActualVGNameOnTheNode || sourceLLVS.Status.NodeName != lvg.Spec.Local.NodeName { 38 | return "", false, errors.New("restored volume should be in the same volume group as the origin volume") 39 | } 40 | 41 | cmd, err := r.commands.CreateThinLogicalVolumeFromSource(llv.Spec.ActualLVNameOnTheNode, sourceLLVS.Status.ActualVGNameOnTheNode, sourceLLVS.Spec.ActualSnapshotNameOnTheNode) 42 | 43 | return cmd, err != nil, err 44 | } 45 | -------------------------------------------------------------------------------- /images/agent/internal/controller/llv/reconciler_ce.go: -------------------------------------------------------------------------------- 1 | //go:build ce 2 | 3 | /* 4 | Copyright 2025 Flant JSC 5 | 6 | Licensed under the Apache License, Version 2.0 (the "License"); 7 | you may not use this file except in compliance with the License. 8 | You may obtain a copy of the License at 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, software 13 | distributed under the License is distributed on an "AS IS" BASIS, 14 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | See the License for the specific language governing permissions and 16 | limitations under the License. 17 | */ 18 | 19 | package llv 20 | 21 | import ( 22 | "fmt" 23 | ) 24 | 25 | func (r *Reconciler) cleanupVolume(...any) (shouldRequeue bool, err error) { 26 | return false, fmt.Errorf("volume cleanup is not supported in your edition") 27 | } 28 | -------------------------------------------------------------------------------- /images/agent/internal/controller/llv/reconciler_ee.go: -------------------------------------------------------------------------------- 1 | //go:build !ce 2 | 3 | /* 4 | Copyright 2025 Flant JSC 5 | 6 | Licensed under the Deckhouse Platform Enterprise Edition (EE) license. 7 | you may not use this file except in compliance with the License. 8 | You may obtain a copy of the License at 9 | 10 | https://github.com/deckhouse/deckhouse/blob/main/ee/LICENSE 11 | */ 12 | 13 | package llv 14 | 15 | import ( 16 | "context" 17 | "errors" 18 | "fmt" 19 | 20 | "github.com/deckhouse/sds-node-configurator/api/v1alpha1" 21 | "github.com/deckhouse/sds-node-configurator/images/agent/internal/cache" 22 | "github.com/deckhouse/sds-node-configurator/images/agent/internal/utils" 23 | "github.com/deckhouse/sds-node-configurator/lib/go/common/pkg/feature" 24 | ) 25 | 26 | func (r *Reconciler) cleanupVolume(ctx context.Context, llv *v1alpha1.LVMLogicalVolume, lv *cache.LVData, vgName string, cleanupMethod string) (shouldRequeue bool, err error) { 27 | if !feature.VolumeCleanupEnabled() { 28 | return false, fmt.Errorf("volume cleanup is not supported in your edition") 29 | } 30 | 31 | if cleanupMethod == v1alpha1.VolumeCleanupDiscard && lv.Data.PoolName != "" { 32 | err := errors.New("Discard cleanup method is disabled for thin volumes") 33 | r.log.Error(err, "[deleteLVIfNeeded] Discard cleanup method is not allowed for thin volumes") 34 | return false, err 35 | } 36 | 37 | lvName := llv.Spec.ActualLVNameOnTheNode 38 | started, prevFailedMethod := r.startCleanupRunning(vgName, lvName) 39 | if !started { 40 | r.log.Debug(fmt.Sprintf("[deleteLVIfNeeded] cleanup already running for LV %s in VG %s", lvName, vgName)) 41 | return false, errAlreadyRunning 42 | } 43 | r.log.Trace(fmt.Sprintf("[deleteLVIfNeeded] starting cleaning up for LV %s in VG %s with method %s", lvName, vgName, cleanupMethod)) 44 | defer func() { 45 | r.log.Trace(fmt.Sprintf("[deleteLVIfNeeded] stopping cleaning up for LV %s in VG %s with method %s", lvName, vgName, cleanupMethod)) 46 | err := r.stopCleanupRunning(vgName, lvName, prevFailedMethod) 47 | if err != nil { 48 | r.log.Error(err, fmt.Sprintf("[deleteLVIfNeeded] can't unregister running cleanup for LV %s in VG %s", lvName, vgName)) 49 | } 50 | }() 51 | 52 | // prevent doing cleanup with previously failed method 53 | if prevFailedMethod != nil && *prevFailedMethod == cleanupMethod { 54 | r.log.Debug(fmt.Sprintf("[deleteLVIfNeeded] was already failed with method %s for LV %s in VG %s", *prevFailedMethod, lvName, vgName)) 55 | return false, errCleanupSameAsPreviouslyFailed 56 | } 57 | 58 | if err := r.llvCl.UpdatePhaseIfNeeded( 59 | ctx, 60 | llv, 61 | v1alpha1.PhaseCleaning, 62 | fmt.Sprintf("Cleaning up volume %s in %s group using %s", lvName, vgName, cleanupMethod), 63 | ); err != nil { 64 | r.log.Error(err, "[deleteLVIfNeeded] changing phase to Cleaning") 65 | return true, fmt.Errorf("changing phase to Cleaning :%w", err) 66 | } 67 | 68 | prevFailedMethod = &cleanupMethod 69 | r.log.Debug(fmt.Sprintf("[deleteLVIfNeeded] running cleanup for LV %s in VG %s with method %s", lvName, vgName, cleanupMethod)) 70 | if shouldRetry, err := utils.VolumeCleanup(ctx, r.log, r.sdsCache, lv, cleanupMethod); err != nil { 71 | r.log.Error(err, fmt.Sprintf("[deleteLVIfNeeded] unable to clean up LV %s in VG %s with method %s", lvName, vgName, cleanupMethod)) 72 | if shouldRetry { 73 | prevFailedMethod = nil 74 | } 75 | return shouldRetry, err 76 | } 77 | prevFailedMethod = nil 78 | return false, nil 79 | } 80 | -------------------------------------------------------------------------------- /images/agent/internal/controller/lvg/utils.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2025 Flant JSC 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package lvg 18 | 19 | import ( 20 | "k8s.io/apimachinery/pkg/api/resource" 21 | v1 "k8s.io/apimachinery/pkg/apis/meta/v1" 22 | 23 | "github.com/deckhouse/sds-node-configurator/api/v1alpha1" 24 | "github.com/deckhouse/sds-node-configurator/images/agent/internal" 25 | ) 26 | 27 | func isApplied(lvg *v1alpha1.LVMVolumeGroup) bool { 28 | for _, c := range lvg.Status.Conditions { 29 | if c.Type == internal.TypeVGConfigurationApplied && c.Status == v1.ConditionTrue { 30 | return true 31 | } 32 | } 33 | 34 | return false 35 | } 36 | 37 | func isThinPool(lv internal.LVData) bool { 38 | return string(lv.LVAttr[0]) == "t" 39 | } 40 | 41 | func getVGAllocatedSize(vg internal.VGData) resource.Quantity { 42 | allocatedSize := vg.VGSize 43 | allocatedSize.Sub(vg.VGFree) 44 | return allocatedSize 45 | } 46 | -------------------------------------------------------------------------------- /images/agent/internal/kubutils/kubernetes.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2025 Flant JSC 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package kubutils 18 | 19 | import ( 20 | "fmt" 21 | 22 | "k8s.io/client-go/rest" 23 | "k8s.io/client-go/tools/clientcmd" 24 | ) 25 | 26 | func KubernetesDefaultConfigCreate() (*rest.Config, error) { 27 | clientConfig := clientcmd.NewNonInteractiveDeferredLoadingClientConfig( 28 | clientcmd.NewDefaultClientConfigLoadingRules(), 29 | &clientcmd.ConfigOverrides{}, 30 | ) 31 | 32 | // Get a config to talk to API server 33 | config, err := clientConfig.ClientConfig() 34 | if err != nil { 35 | return nil, fmt.Errorf("config kubernetes error %w", err) 36 | } 37 | return config, nil 38 | } 39 | -------------------------------------------------------------------------------- /images/agent/internal/logger/logger.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2025 Flant JSC 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package logger 18 | 19 | import ( 20 | "fmt" 21 | "strconv" 22 | 23 | "github.com/go-logr/logr" 24 | "k8s.io/klog/v2/textlogger" 25 | ) 26 | 27 | const ( 28 | ErrorLevel Verbosity = "0" 29 | WarningLevel Verbosity = "1" 30 | InfoLevel Verbosity = "2" 31 | DebugLevel Verbosity = "3" 32 | TraceLevel Verbosity = "4" 33 | ) 34 | 35 | const ( 36 | warnLvl = iota + 1 37 | infoLvl 38 | debugLvl 39 | traceLvl 40 | ) 41 | 42 | type ( 43 | Verbosity string 44 | ) 45 | 46 | type Logger struct { 47 | log logr.Logger 48 | } 49 | 50 | func NewLogger(level Verbosity) (Logger, error) { 51 | v, err := strconv.Atoi(string(level)) 52 | if err != nil { 53 | return Logger{}, err 54 | } 55 | 56 | log := textlogger.NewLogger(textlogger.NewConfig(textlogger.Verbosity(v))).WithCallDepth(1) 57 | 58 | return Logger{log: log}, nil 59 | } 60 | 61 | func NewLoggerWrap(log logr.Logger) Logger { 62 | return Logger{log: log} 63 | } 64 | 65 | func (l Logger) GetLogger() logr.Logger { 66 | return l.log 67 | } 68 | 69 | func (l Logger) Error(err error, message string, keysAndValues ...interface{}) { 70 | l.log.Error(err, fmt.Sprintf("ERROR %s", message), keysAndValues...) 71 | } 72 | 73 | func (l Logger) Warning(message string, keysAndValues ...interface{}) { 74 | l.log.V(warnLvl).Info(fmt.Sprintf("WARNING %s", message), keysAndValues...) 75 | } 76 | 77 | func (l Logger) Info(message string, keysAndValues ...interface{}) { 78 | l.log.V(infoLvl).Info(fmt.Sprintf("INFO %s", message), keysAndValues...) 79 | } 80 | 81 | func (l Logger) Debug(message string, keysAndValues ...interface{}) { 82 | l.log.V(debugLvl).Info(fmt.Sprintf("DEBUG %s", message), keysAndValues...) 83 | } 84 | 85 | func (l Logger) Trace(message string, keysAndValues ...interface{}) { 86 | l.log.V(traceLvl).Info(fmt.Sprintf("TRACE %s", message), keysAndValues...) 87 | } 88 | -------------------------------------------------------------------------------- /images/agent/internal/mock_utils/syscall.go: -------------------------------------------------------------------------------- 1 | // Code generated by MockGen. DO NOT EDIT. 2 | // Source: syscall.go 3 | // 4 | // Generated by this command: 5 | // 6 | // mockgen -write_source_comment -destination=../mock_utils/syscall.go -source=syscall.go 7 | // 8 | 9 | // Package mock_utils is a generated GoMock package. 10 | package mock_utils 11 | 12 | import ( 13 | reflect "reflect" 14 | 15 | utils "github.com/deckhouse/sds-node-configurator/images/agent/internal/utils" 16 | gomock "go.uber.org/mock/gomock" 17 | ) 18 | 19 | // MockSysCall is a mock of SysCall interface. 20 | type MockSysCall struct { 21 | ctrl *gomock.Controller 22 | recorder *MockSysCallMockRecorder 23 | isgomock struct{} 24 | } 25 | 26 | // MockSysCallMockRecorder is the mock recorder for MockSysCall. 27 | type MockSysCallMockRecorder struct { 28 | mock *MockSysCall 29 | } 30 | 31 | // NewMockSysCall creates a new mock instance. 32 | func NewMockSysCall(ctrl *gomock.Controller) *MockSysCall { 33 | mock := &MockSysCall{ctrl: ctrl} 34 | mock.recorder = &MockSysCallMockRecorder{mock} 35 | return mock 36 | } 37 | 38 | // EXPECT returns an object that allows the caller to indicate expected use. 39 | func (m *MockSysCall) EXPECT() *MockSysCallMockRecorder { 40 | return m.recorder 41 | } 42 | 43 | // Blkdiscard mocks base method. 44 | func (m *MockSysCall) Blkdiscard(fd uintptr, start, count uint64) error { 45 | m.ctrl.T.Helper() 46 | ret := m.ctrl.Call(m, "Blkdiscard", fd, start, count) 47 | ret0, _ := ret[0].(error) 48 | return ret0 49 | } 50 | 51 | // Blkdiscard indicates an expected call of Blkdiscard. 52 | func (mr *MockSysCallMockRecorder) Blkdiscard(fd, start, count any) *gomock.Call { 53 | mr.mock.ctrl.T.Helper() 54 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Blkdiscard", reflect.TypeOf((*MockSysCall)(nil).Blkdiscard), fd, start, count) 55 | } 56 | 57 | // Fstat mocks base method. 58 | func (m *MockSysCall) Fstat(fd int, stat *utils.Stat_t) error { 59 | m.ctrl.T.Helper() 60 | ret := m.ctrl.Call(m, "Fstat", fd, stat) 61 | ret0, _ := ret[0].(error) 62 | return ret0 63 | } 64 | 65 | // Fstat indicates an expected call of Fstat. 66 | func (mr *MockSysCallMockRecorder) Fstat(fd, stat any) *gomock.Call { 67 | mr.mock.ctrl.T.Helper() 68 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Fstat", reflect.TypeOf((*MockSysCall)(nil).Fstat), fd, stat) 69 | } 70 | 71 | // Syscall mocks base method. 72 | func (m *MockSysCall) Syscall(trap, a1, a2, a3 uintptr) (uintptr, uintptr, utils.Errno) { 73 | m.ctrl.T.Helper() 74 | ret := m.ctrl.Call(m, "Syscall", trap, a1, a2, a3) 75 | ret0, _ := ret[0].(uintptr) 76 | ret1, _ := ret[1].(uintptr) 77 | ret2, _ := ret[2].(utils.Errno) 78 | return ret0, ret1, ret2 79 | } 80 | 81 | // Syscall indicates an expected call of Syscall. 82 | func (mr *MockSysCallMockRecorder) Syscall(trap, a1, a2, a3 any) *gomock.Call { 83 | mr.mock.ctrl.T.Helper() 84 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Syscall", reflect.TypeOf((*MockSysCall)(nil).Syscall), trap, a1, a2, a3) 85 | } 86 | -------------------------------------------------------------------------------- /images/agent/internal/test_utils/fake_client.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2025 Flant JSC 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package test_utils 18 | 19 | import ( 20 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 21 | "k8s.io/client-go/kubernetes/scheme" 22 | "sigs.k8s.io/controller-runtime/pkg/client" 23 | "sigs.k8s.io/controller-runtime/pkg/client/fake" 24 | 25 | "github.com/deckhouse/sds-node-configurator/api/v1alpha1" 26 | ) 27 | 28 | func NewFakeClient(statusSubresources ...client.Object) client.WithWatch { 29 | s := scheme.Scheme 30 | _ = metav1.AddMetaToScheme(s) 31 | _ = v1alpha1.AddToScheme(s) 32 | 33 | return fake. 34 | NewClientBuilder(). 35 | WithScheme(s). 36 | WithStatusSubresource(statusSubresources...). 37 | Build() 38 | } 39 | -------------------------------------------------------------------------------- /images/agent/internal/throttler/throttler.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2025 Flant JSC 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package throttler 18 | 19 | import ( 20 | "sync" 21 | "time" 22 | ) 23 | 24 | type Throttler interface { 25 | Do(f func()) 26 | } 27 | 28 | type throttle struct { 29 | duration time.Duration 30 | once sync.Once 31 | m sync.Mutex 32 | } 33 | 34 | func (t *throttle) Do(f func()) { 35 | t.m.Lock() 36 | defer t.m.Unlock() 37 | t.once.Do(func() { 38 | go func() { 39 | time.Sleep(t.duration) 40 | t.m.Lock() 41 | defer t.m.Unlock() 42 | t.once = sync.Once{} 43 | }() 44 | f() 45 | }) 46 | } 47 | 48 | func New(duration time.Duration) Throttler { 49 | return &throttle{ 50 | duration: duration, 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /images/agent/internal/utils/block_device_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2025 Flant JSC 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package utils_test 18 | 19 | import ( 20 | "errors" 21 | "os" 22 | "unsafe" 23 | 24 | . "github.com/onsi/ginkgo/v2" 25 | . "github.com/onsi/gomega" 26 | "go.uber.org/mock/gomock" 27 | "golang.org/x/sys/unix" 28 | 29 | . "github.com/deckhouse/sds-node-configurator/images/agent/internal/mock_utils" 30 | . "github.com/deckhouse/sds-node-configurator/images/agent/internal/utils" 31 | ) 32 | 33 | var _ = Describe("BlockDevice", func() { 34 | var ctrl *gomock.Controller 35 | var sysCall *MockSysCall 36 | var fileOpener *MockFileOpener 37 | var blockDeviceOpener BlockDeviceOpener 38 | var file *MockFile 39 | var err error 40 | BeforeEach(func() { 41 | ctrl = gomock.NewController(GinkgoT()) 42 | sysCall = NewMockSysCall(ctrl) 43 | fileOpener = NewMockFileOpener(ctrl) 44 | blockDeviceOpener = NewBlockDeviceOpener(fileOpener, sysCall) 45 | file = NewMockFile(ctrl) 46 | }) 47 | 48 | fileName := "fileName" 49 | flag := int(0) 50 | size := int64(1024) 51 | fd := uintptr(1234) 52 | 53 | var device BlockDevice 54 | 55 | When("device properly opened", func() { 56 | BeforeEach(func() { 57 | file.EXPECT().Fd().AnyTimes().Return(fd) 58 | fileOpener.EXPECT().Open(fileName, flag, os.ModeDevice).Return(file, nil) 59 | }) 60 | JustBeforeEach(func() { 61 | device, err = blockDeviceOpener.Open(fileName, 0) 62 | Expect(err).NotTo(HaveOccurred()) 63 | Expect(device).NotTo(BeNil()) 64 | }) 65 | 66 | It("finds out size", func() { 67 | sysCall.EXPECT().Fstat(int(fd), gomock.Any()).DoAndReturn(func(_ int, stat *Stat_t) error { 68 | stat.Mode = S_IFBLK 69 | return nil 70 | }) 71 | sysCall.EXPECT().Syscall(uintptr(unix.SYS_IOCTL), fd, uintptr(unix.BLKGETSIZE64), gomock.Any()).DoAndReturn(func(_, _, _, a3 uintptr) (uintptr, uintptr, Errno) { 72 | *(*uint64)(unsafe.Pointer(a3)) = uint64(size) 73 | return 0, 0, 0 74 | }) 75 | 76 | got, err := device.Size() 77 | Expect(err).NotTo(HaveOccurred()) 78 | Expect(got).To(BeEquivalentTo(size)) 79 | }) 80 | 81 | It("issues blkdiscard", func() { 82 | start := uint64(256) 83 | count := uint64(512) 84 | sysCall.EXPECT().Blkdiscard(fd, start, count).Return(nil) 85 | 86 | err = device.Discard(start, count) 87 | Expect(err).NotTo(HaveOccurred()) 88 | }) 89 | 90 | It("closes underlying file", func() { 91 | closingError := errors.New("Closing error") 92 | file.EXPECT().Close().Return(closingError) 93 | 94 | err = device.Close() 95 | 96 | Expect(err).To(MatchError(closingError)) 97 | }) 98 | }) 99 | 100 | When("underlying open error occurred", func() { 101 | openError := errors.New("Open file error") 102 | BeforeEach(func() { 103 | fileOpener.EXPECT().Open(fileName, flag, os.ModeDevice).Return(nil, openError) 104 | }) 105 | JustBeforeEach(func() { 106 | device, err = blockDeviceOpener.Open(fileName, 0) 107 | Expect(err).To(MatchError(openError)) 108 | }) 109 | 110 | It("returns nil device", func() { 111 | Expect(device).To(BeNil()) 112 | }) 113 | }) 114 | }) 115 | -------------------------------------------------------------------------------- /images/agent/internal/utils/client_bd.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2025 Flant JSC 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package utils 18 | 19 | import ( 20 | "context" 21 | "time" 22 | 23 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 24 | "k8s.io/apimachinery/pkg/labels" 25 | "sigs.k8s.io/controller-runtime/pkg/client" 26 | 27 | "github.com/deckhouse/sds-node-configurator/api/v1alpha1" 28 | "github.com/deckhouse/sds-node-configurator/images/agent/internal/monitoring" 29 | ) 30 | 31 | type BDClient struct { 32 | cl client.Client 33 | metrics monitoring.Metrics 34 | } 35 | 36 | func NewBDClient(cl client.Client, metrics monitoring.Metrics) *BDClient { 37 | return &BDClient{ 38 | cl: cl, 39 | metrics: metrics, 40 | } 41 | } 42 | 43 | // GetAPIBlockDevices returns map of BlockDevice resources with BlockDevice as a key. You might specify a selector to get a subset or 44 | // leave it as nil to get all the resources. 45 | func (bdCl *BDClient) GetAPIBlockDevices( 46 | ctx context.Context, 47 | controllerName string, 48 | selector *metav1.LabelSelector, 49 | ) (map[string]v1alpha1.BlockDevice, error) { 50 | list := &v1alpha1.BlockDeviceList{} 51 | s, err := metav1.LabelSelectorAsSelector(selector) 52 | if err != nil { 53 | return nil, err 54 | } 55 | if s == labels.Nothing() { 56 | s = nil 57 | } 58 | start := time.Now() 59 | err = bdCl.cl.List(ctx, list, &client.ListOptions{LabelSelector: s}) 60 | bdCl.metrics.APIMethodsDuration(controllerName, "list").Observe(bdCl.metrics.GetEstimatedTimeInSeconds(start)) 61 | bdCl.metrics.APIMethodsExecutionCount(controllerName, "list").Inc() 62 | if err != nil { 63 | bdCl.metrics.APIMethodsErrors(controllerName, "list").Inc() 64 | return nil, err 65 | } 66 | 67 | result := make(map[string]v1alpha1.BlockDevice, len(list.Items)) 68 | for _, item := range list.Items { 69 | result[item.Name] = item 70 | } 71 | 72 | return result, nil 73 | } 74 | -------------------------------------------------------------------------------- /images/agent/internal/utils/client_llv.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2025 Flant JSC 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package utils 18 | 19 | import ( 20 | "context" 21 | "fmt" 22 | 23 | "k8s.io/apimachinery/pkg/api/resource" 24 | "sigs.k8s.io/controller-runtime/pkg/client" 25 | 26 | "github.com/deckhouse/sds-node-configurator/api/v1alpha1" 27 | "github.com/deckhouse/sds-node-configurator/images/agent/internal/logger" 28 | ) 29 | 30 | type LLVClient struct { 31 | cl client.Client 32 | log logger.Logger 33 | } 34 | 35 | func NewLLVClient( 36 | cl client.Client, 37 | log logger.Logger, 38 | ) *LLVClient { 39 | return &LLVClient{ 40 | cl: cl, 41 | log: log, 42 | } 43 | } 44 | 45 | func (llvCl *LLVClient) UpdatePhaseIfNeeded( 46 | ctx context.Context, 47 | llv *v1alpha1.LVMLogicalVolume, 48 | phase string, 49 | reason string, 50 | ) error { 51 | if llv.Status != nil && 52 | llv.Status.Phase == phase && 53 | llv.Status.Reason == reason { 54 | llvCl.log.Debug(fmt.Sprintf("[updateLVMLogicalVolumePhaseIfNeeded] no need to update the LVMLogicalVolume %s phase and reason", llv.Name)) 55 | return nil 56 | } 57 | 58 | if llv.Status == nil { 59 | llv.Status = new(v1alpha1.LVMLogicalVolumeStatus) 60 | } 61 | 62 | llv.Status.Phase = phase 63 | llv.Status.Reason = reason 64 | 65 | llvCl.log.Debug(fmt.Sprintf("[updateLVMLogicalVolumePhaseIfNeeded] tries to update the LVMLogicalVolume %s status with phase: %s, reason: %s", llv.Name, phase, reason)) 66 | err := llvCl.cl.Status().Update(ctx, llv) 67 | if err != nil { 68 | return err 69 | } 70 | 71 | llvCl.log.Debug(fmt.Sprintf("[updateLVMLogicalVolumePhaseIfNeeded] updated LVMLogicalVolume %s status.phase to %s and reason to %s", llv.Name, phase, reason)) 72 | return nil 73 | } 74 | 75 | func (llvCl *LLVClient) UpdatePhaseToCreatedIfNeeded( 76 | ctx context.Context, 77 | llv *v1alpha1.LVMLogicalVolume, 78 | actualSize resource.Quantity, 79 | ) error { 80 | var contiguous *bool 81 | if llv.Spec.Thick != nil && llv.Spec.Thick.Contiguous != nil { 82 | if *llv.Spec.Thick.Contiguous { 83 | contiguous = llv.Spec.Thick.Contiguous 84 | } 85 | } 86 | 87 | updateNeeded := llv.Status.Phase != v1alpha1.PhaseCreated || 88 | llv.Status.ActualSize.Value() != actualSize.Value() || 89 | llv.Status.Reason != "" || 90 | llv.Status.Contiguous != contiguous 91 | 92 | if !updateNeeded { 93 | llvCl.log.Info(fmt.Sprintf("[UpdatePhaseToCreatedIfNeeded] no need to update the LVMLogicalVolume %s", llv.Name)) 94 | return nil 95 | } 96 | 97 | llv.Status.Phase = v1alpha1.PhaseCreated 98 | llv.Status.Reason = "" 99 | llv.Status.ActualSize = actualSize 100 | llv.Status.Contiguous = contiguous 101 | err := llvCl.cl.Status().Update(ctx, llv) 102 | if err != nil { 103 | llvCl.log.Error(err, fmt.Sprintf("[UpdatePhaseToCreatedIfNeeded] unable to update the LVMLogicalVolume %s", llv.Name)) 104 | return err 105 | } 106 | 107 | llvCl.log.Info(fmt.Sprintf("[UpdatePhaseToCreatedIfNeeded] the LVMLogicalVolume %s was successfully updated", llv.Name)) 108 | return nil 109 | } 110 | -------------------------------------------------------------------------------- /images/agent/internal/utils/commands_ee.go: -------------------------------------------------------------------------------- 1 | //go:build !ce 2 | 3 | /* 4 | Copyright 2025 Flant JSC 5 | 6 | Licensed under the Deckhouse Platform Enterprise Edition (EE) license. 7 | you may not use this file except in compliance with the License. 8 | You may obtain a copy of the License at 9 | 10 | https://github.com/deckhouse/deckhouse/blob/main/ee/LICENSE 11 | */ 12 | 13 | package utils 14 | 15 | import ( 16 | "bytes" 17 | "context" 18 | "errors" 19 | "fmt" 20 | "os/exec" 21 | 22 | "github.com/deckhouse/sds-node-configurator/images/agent/internal" 23 | "github.com/deckhouse/sds-node-configurator/images/agent/internal/logger" 24 | ) 25 | 26 | func ThinDumpRaw(ctx context.Context, log logger.Logger, tpool, tmeta, devID string) (out []byte, err error) { 27 | log.Trace(fmt.Sprintf("[ThinDumpRaw] calling for tpool %s tmeta %s devID %s", tpool, tmeta, devID)) 28 | cmd := exec.CommandContext( 29 | ctx, 30 | internal.NSENTERCmd, 31 | nsentrerExpendedArgs(internal.DMSetupCmd, "message", tpool, "0", "reserve_metadata_snap")...) 32 | log.Debug(fmt.Sprintf("[ThinDumpRaw] running %v", cmd)) 33 | if err = cmd.Run(); err != nil { 34 | log.Error(err, fmt.Sprintf("[ThinDumpRaw] can't reserve metadata snapshot for %s", tpool)) 35 | err = fmt.Errorf("reserving metadata snapshot: %w", err) 36 | return 37 | } 38 | defer func() { 39 | cmd := exec.CommandContext( 40 | ctx, 41 | internal.NSENTERCmd, 42 | nsentrerExpendedArgs(internal.DMSetupCmd, "message", tpool, "0", "release_metadata_snap")...) 43 | 44 | log.Debug(fmt.Sprintf("[ThinDumpRaw] running %v", cmd)) 45 | if errRelease := cmd.Run(); errRelease != nil { 46 | log.Error(errRelease, fmt.Sprintf("[ThinDumpRaw] can't release metadata snapshot for %s", tpool)) 47 | err = errors.Join(err, errRelease) 48 | } 49 | }() 50 | 51 | args := []string{tmeta, "-m", "-f", "xml"} 52 | if devID != "" { 53 | args = append(args, "--dev-id", devID) 54 | } 55 | cmd = exec.CommandContext(ctx, 56 | internal.NSENTERCmd, 57 | nsentrerExpendedArgs(internal.ThinDumpCmd, args...)...) 58 | 59 | var output bytes.Buffer 60 | cmd.Stdout = &output 61 | 62 | log.Debug(fmt.Sprintf("[ThinDumpRaw] running %v", cmd)) 63 | if err = cmd.Run(); err != nil { 64 | log.Error(err, fmt.Sprintf("[ThinDumpRaw] can't get metadata %s", tmeta)) 65 | err = fmt.Errorf("dumping metadata: %w", err) 66 | return 67 | } 68 | log.Trace(fmt.Sprintf("[ThinDumpRaw] device map is: %s", output.Bytes())) 69 | return output.Bytes(), nil 70 | } 71 | -------------------------------------------------------------------------------- /images/agent/internal/utils/range_cover.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2025 Flant JSC 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package utils 18 | 19 | import ( 20 | "fmt" 21 | "sort" 22 | ) 23 | 24 | type Range struct { 25 | Start, Count int64 26 | } 27 | 28 | type RangeCover []Range 29 | 30 | func (cover RangeCover) Merged() (RangeCover, error) { 31 | rcLen := len(cover) 32 | if rcLen <= 1 { 33 | return cover, nil 34 | } 35 | 36 | sort.Slice(cover, func(i, j int) bool { 37 | return cover[i].Start < cover[j].Start 38 | }) 39 | 40 | for i, d := range cover[0 : rcLen-1] { 41 | if d.Start+d.Count > cover[i+1].Start { 42 | return nil, fmt.Errorf("self overlapped range") 43 | } 44 | } 45 | 46 | last := Range{Start: 0, Count: 0} 47 | reduced := make(RangeCover, 0, len(cover)) 48 | for _, item := range cover { 49 | switch last.Count { 50 | case 0: // Special case for first range 51 | last = item 52 | case item.Start - last.Start: // Touching, merge to one range 53 | last.Count += item.Count 54 | default: // Add previously merged ranges to cover 55 | reduced = append(reduced, last) 56 | last = item 57 | } 58 | } 59 | 60 | if last.Count > 0 { 61 | reduced = append(reduced, last) 62 | } 63 | 64 | return reduced, nil 65 | } 66 | 67 | func (cover Range) Multiplied(multiplier int64) Range { 68 | return Range{Start: cover.Start * multiplier, Count: cover.Count * multiplier} 69 | } 70 | 71 | func (cover RangeCover) Multiplied(multiplier int64) RangeCover { 72 | multiplied := make([]Range, len(cover)) 73 | for i, value := range cover { 74 | multiplied[i] = value.Multiplied(multiplier) 75 | } 76 | return multiplied 77 | } 78 | -------------------------------------------------------------------------------- /images/agent/internal/utils/range_cover_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2025 Flant JSC 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package utils 18 | 19 | import ( 20 | "slices" 21 | "testing" 22 | ) 23 | 24 | func TestMergeEmpty(t *testing.T) { 25 | result, err := RangeCover{}.Merged() 26 | if err != nil { 27 | t.Errorf("unexpected error: %v", err) 28 | } 29 | 30 | if len(result) != 0 { 31 | t.Errorf("expected empty range, got %v", result) 32 | } 33 | } 34 | 35 | func TestMergeOverlapped(t *testing.T) { 36 | _, err := RangeCover{ 37 | Range{Start: 0, Count: 2}, 38 | Range{Start: 1, Count: 1}, 39 | }.Merged() 40 | if err == nil { 41 | t.Errorf("unexpected success: %v", err) 42 | } 43 | 44 | if err.Error() != "self overlapped range" { 45 | t.Errorf("expected error %v", err) 46 | } 47 | } 48 | 49 | func TestMergeNoMerge(t *testing.T) { 50 | original := RangeCover{ 51 | Range{Start: 0, Count: 2}, 52 | Range{Start: 7, Count: 1}, 53 | } 54 | result, err := original.Merged() 55 | if err != nil { 56 | t.Errorf("unexpected error: %v", err) 57 | } 58 | 59 | if !slices.Equal(original, result) { 60 | t.Errorf("expected %v, got %v", original, result) 61 | } 62 | } 63 | 64 | func TestMergeMerge(t *testing.T) { 65 | original := RangeCover{ 66 | Range{Start: 0, Count: 2}, 67 | Range{Start: 2, Count: 1}, 68 | } 69 | expected := RangeCover{ 70 | Range{Start: 0, Count: 3}, 71 | } 72 | result, err := original.Merged() 73 | if err != nil { 74 | t.Errorf("unexpected error: %v", err) 75 | } 76 | 77 | if !slices.Equal(expected, result) { 78 | t.Errorf("expected %v, got %v", expected, result) 79 | } 80 | } 81 | 82 | func TestMultiply(t *testing.T) { 83 | multiplier := int64(4) 84 | original := RangeCover{ 85 | Range{Start: 0, Count: 2}, 86 | Range{Start: 5, Count: 1}, 87 | } 88 | expected := RangeCover{ 89 | Range{Start: 0, Count: 8}, 90 | Range{Start: 20, Count: 4}, 91 | } 92 | 93 | result := original.Multiplied(multiplier) 94 | 95 | if !slices.Equal(result, expected) { 96 | t.Errorf("expected %v, got %v", expected, result) 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /images/agent/internal/utils/syscall.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2025 Flant JSC 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | //go:generate go run go.uber.org/mock/mockgen -write_source_comment -destination=../mock_utils/$GOFILE -source=$GOFILE 18 | package utils 19 | 20 | import ( 21 | "errors" 22 | "fmt" 23 | "unsafe" 24 | 25 | "golang.org/x/sys/unix" 26 | ) 27 | 28 | //nolint:revive 29 | type Stat_t = unix.Stat_t 30 | type Errno = unix.Errno 31 | 32 | type SysCall interface { 33 | Fstat(fd int, stat *Stat_t) (err error) 34 | Syscall(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err Errno) 35 | Blkdiscard(fd uintptr, start, count uint64) error 36 | } 37 | 38 | type osSyscall struct { 39 | } 40 | 41 | var theSysCall = osSyscall{} 42 | 43 | //nolint:revive 44 | func OsSysCall() osSyscall { 45 | return theSysCall 46 | } 47 | 48 | func (osSyscall) Fstat(fd int, stat *Stat_t) (err error) { 49 | return unix.Fstat(fd, stat) 50 | } 51 | 52 | func (osSyscall) Syscall(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err Errno) { 53 | return unix.Syscall(trap, a1, a2, a3) 54 | } 55 | 56 | func (osSyscall) Blkdiscard(fd uintptr, start, count uint64) error { 57 | rng := struct { 58 | start, count uint64 59 | }{ 60 | start: start, 61 | count: count, 62 | } 63 | _, _, errno := unix.Syscall( 64 | unix.SYS_IOCTL, 65 | fd, 66 | BLKDISCARD, 67 | uintptr(unsafe.Pointer(&rng))) 68 | 69 | if errno != 0 { 70 | err := errors.New(errno.Error()) 71 | return fmt.Errorf("calling ioctl BLKDISCARD: %w", err) 72 | } 73 | return nil 74 | } 75 | 76 | /* To find these constant missing from unix module: 77 | gcc -o test -x c - < 79 | #include 80 | #include 81 | #include 82 | 83 | #define PRINT_CONSTANT(name, fmt) printf(#name " = " fmt "\n", name) 84 | 85 | int main() { 86 | PRINT_CONSTANT(BLKDISCARD, "0x%x"); 87 | return 0; 88 | } 89 | EOF 90 | */ 91 | 92 | // TODO: It will be nice to figure them out during compilation or maybe runtime? 93 | // 94 | //nolint:revive 95 | const ( 96 | BLKDISCARD = uintptr(0x1277) 97 | 98 | S_IFMT = unix.S_IFMT /* type of file mask */ 99 | S_IFBLK = unix.S_IFBLK /* block special */ 100 | ) 101 | -------------------------------------------------------------------------------- /images/agent/internal/utils/thin_dump_ee.go: -------------------------------------------------------------------------------- 1 | //go:build !ce 2 | 3 | /* 4 | Copyright 2025 Flant JSC 5 | 6 | Licensed under the Deckhouse Platform Enterprise Edition (EE) license. 7 | you may not use this file except in compliance with the License. 8 | You may obtain a copy of the License at 9 | 10 | https://github.com/deckhouse/deckhouse/blob/main/ee/LICENSE 11 | */ 12 | 13 | package utils 14 | 15 | import ( 16 | "context" 17 | "encoding/xml" 18 | "errors" 19 | "fmt" 20 | 21 | "github.com/deckhouse/sds-node-configurator/images/agent/internal/logger" 22 | ) 23 | 24 | type ( 25 | LVMTime = int64 26 | LVMTransaction = int64 27 | LVMThinDeviceID = int64 28 | ) 29 | 30 | type Superblock struct { 31 | XMLName xml.Name `xml:"superblock"` 32 | UUID string `xml:"uuid,attr"` 33 | Time LVMTime `xml:"time,attr"` 34 | Transaction LVMTransaction `xml:"transaction,attr"` 35 | Flags int64 `xml:"flags,attr"` 36 | Version int32 `xml:"version,attr"` 37 | DataBlockSize int64 `xml:"data_block_size,attr"` 38 | NrDataBlocks int64 `xml:"nr_data_blocks,attr"` 39 | Devices []Device `xml:"device"` 40 | } 41 | 42 | type Device struct { 43 | XMLName xml.Name `xml:"device"` 44 | DevID LVMThinDeviceID `xml:"dev_id,attr"` 45 | MappedBlocks int64 `xml:"mapped_blocks,attr"` 46 | Transaction LVMTransaction `xml:"transaction,attr"` 47 | CreationTime LVMTime `xml:"creation_time,attr"` 48 | SnapTime LVMTime `xml:"snap_time,attr"` 49 | RangeMappings []RangeMapping `xml:"range_mapping"` 50 | SingleMappings []SingleMapping `xml:"single_mapping"` 51 | } 52 | 53 | type RangeMapping struct { 54 | XMLName xml.Name `xml:"range_mapping"` 55 | OriginBegin int64 `xml:"origin_begin,attr"` 56 | DataBegin int64 `xml:"data_begin,attr"` 57 | Length int64 `xml:"length,attr"` 58 | Time LVMTime `xml:"time,attr"` 59 | } 60 | 61 | type SingleMapping struct { 62 | XMLName xml.Name `xml:"single_mapping"` 63 | OriginBlock int64 `xml:"origin_block,attr"` 64 | DataBlock int64 `xml:"data_block,attr"` 65 | Time LVMTime `xml:"time,attr"` 66 | } 67 | 68 | func ThinDump(ctx context.Context, log logger.Logger, tpool, tmeta, devID string) (superblock Superblock, err error) { 69 | log.Trace(fmt.Sprintf("[ThinDump] calling for tpool %s tmeta %s devID %s", tpool, tmeta, devID)) 70 | 71 | var rawOut []byte 72 | rawOut, err = ThinDumpRaw(ctx, log, tpool, tmeta, devID) 73 | if err != nil { 74 | return 75 | } 76 | 77 | log.Debug("[ThinDump] unmarshaling") 78 | if err = xml.Unmarshal(rawOut, &superblock); err != nil { 79 | log.Error(err, "[ThinDump] unmarshaling error") 80 | err = fmt.Errorf("parsing metadata: %w", err) 81 | return 82 | } 83 | 84 | log.Trace(fmt.Sprintf("[ThinDump] unmarshaled: %v", superblock)) 85 | 86 | return superblock, nil 87 | } 88 | 89 | func ThinVolumeUsedRanges(_ context.Context, log logger.Logger, superblock Superblock, deviceID LVMThinDeviceID) (blockRanges RangeCover, err error) { 90 | log.Trace(fmt.Sprintf("[ThinVolumeUsedRanges] calling for deviceId %d", deviceID)) 91 | for _, device := range superblock.Devices { 92 | if device.DevID != deviceID { 93 | continue 94 | } 95 | 96 | blockRanges = make(RangeCover, 0, len(device.RangeMappings)+len(device.SingleMappings)) 97 | 98 | for _, mapping := range device.RangeMappings { 99 | blockRanges = append(blockRanges, Range{Start: mapping.OriginBegin, Count: mapping.Length}) 100 | } 101 | 102 | for _, mapping := range device.SingleMappings { 103 | blockRanges = append(blockRanges, Range{Start: mapping.OriginBlock, Count: 1}) 104 | } 105 | 106 | blockRanges, err = blockRanges.Merged() 107 | if err != nil { 108 | err = fmt.Errorf("finding used ranges: %w", err) 109 | return 110 | } 111 | 112 | return blockRanges.Multiplied(superblock.DataBlockSize), nil 113 | } 114 | return blockRanges, errors.New("device not found") 115 | } 116 | -------------------------------------------------------------------------------- /images/agent/internal/utils/units.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2025 Flant JSC 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package utils 18 | 19 | import ( 20 | "math" 21 | 22 | "k8s.io/apimachinery/pkg/api/resource" 23 | ) 24 | 25 | func AreSizesEqualWithinDelta(leftSize, rightSize, allowedDelta resource.Quantity) bool { 26 | leftSizeFloat := float64(leftSize.Value()) 27 | rightSizeFloat := float64(rightSize.Value()) 28 | 29 | return math.Abs(leftSizeFloat-rightSizeFloat) < float64(allowedDelta.Value()) 30 | } 31 | -------------------------------------------------------------------------------- /images/agent/internal/utils/utils_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2025 Flant JSC 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package utils_test 18 | 19 | import ( 20 | "testing" 21 | 22 | . "github.com/onsi/ginkgo/v2" 23 | . "github.com/onsi/gomega" 24 | ) 25 | 26 | func TestUtils(t *testing.T) { 27 | RegisterFailHandler(Fail) 28 | RunSpecs(t, "utils Suite") 29 | } 30 | -------------------------------------------------------------------------------- /images/agent/tools.go: -------------------------------------------------------------------------------- 1 | //go:build tools 2 | // +build tools 3 | 4 | /* 5 | Copyright 2025 Flant JSC 6 | 7 | Licensed under the Apache License, Version 2.0 (the "License"); 8 | you may not use this file except in compliance with the License. 9 | You may obtain a copy of the License at 10 | 11 | http://www.apache.org/licenses/LICENSE-2.0 12 | 13 | Unless required by applicable law or agreed to in writing, software 14 | distributed under the License is distributed on an "AS IS" BASIS, 15 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | See the License for the specific language governing permissions and 17 | limitations under the License. 18 | */ 19 | 20 | package tools 21 | 22 | import _ "go.uber.org/mock/mockgen" 23 | -------------------------------------------------------------------------------- /images/agent/werf.inc.yaml: -------------------------------------------------------------------------------- 1 | {{ $binaries := "/opt/deckhouse/sds/lib/libblkid.so.1 /opt/deckhouse/sds/lib/libmount.so.1 /opt/deckhouse/sds/lib/libsmartcols.so.1 /opt/deckhouse/sds/bin/nsenter.static /opt/deckhouse/sds/lib/x86_64-linux-gnu/libudev.so.1 /opt/deckhouse/sds/lib/x86_64-linux-gnu/libcap.so.2 /opt/deckhouse/sds/bin/lsblk.dynamic /usr/lib/x86_64-linux-gnu/sys-root/lib64/ld-linux-x86-64.so.2" }} 2 | 3 | # Do not remove. It's used in external tests. 4 | --- 5 | image: {{ $.ImageName }}-src-artifact 6 | fromImage: builder/src 7 | final: false 8 | 9 | git: 10 | - add: / 11 | to: /src 12 | includePaths: 13 | - api 14 | - lib/go 15 | - images/{{ $.ImageName }} 16 | stageDependencies: 17 | install: 18 | - '**/*' 19 | excludePaths: 20 | - images/{{ $.ImageName }}/werf.yaml 21 | 22 | shell: 23 | install: 24 | - apt-get update 25 | - apt-get -y install git 26 | - git config --global advice.detachedHead false 27 | - git clone --depth 1 --branch {{ $.Versions.UTIL_LINUX }} {{ env "SOURCE_REPO" }}/util-linux/util-linux.git /src/util-linux 28 | - rm -rf /src/util-linux/.git 29 | - rm -rf /src/.git 30 | 31 | --- 32 | image: {{ $.ImageName }}-binaries-artifact 33 | fromImage: builder/alt 34 | final: false 35 | 36 | import: 37 | - image: {{ $.ImageName }}-src-artifact 38 | add: /src 39 | to: /src 40 | before: install 41 | 42 | git: 43 | - add: /tools/dev_images/additional_tools/binary_replace.sh 44 | to: /binary_replace.sh 45 | stageDependencies: 46 | install: 47 | - "**/*" 48 | 49 | shell: 50 | install: 51 | - apt-get update 52 | - | 53 | apt-get install -y \ 54 | build-essential \ 55 | pkg-config \ 56 | gettext \ 57 | autoconf \ 58 | bison \ 59 | libtool \ 60 | libudev-devel \ 61 | libblkid-devel-static \ 62 | libsmartcols-devel-static \ 63 | libmount-devel-static \ 64 | automake \ 65 | gettext \ 66 | flex \ 67 | glibc-core \ 68 | cross-glibc-x86_64 69 | - cd /src/util-linux 70 | - ./autogen.sh 71 | - ./configure LDFLAGS="-static" --enable-static-programs -disable-all-programs --enable-nsenter 72 | - make install-strip 73 | - ./configure --prefix /opt/deckhouse/sds --with-udev 74 | - make install-strip 75 | - mkdir -p /opt/deckhouse/sds/lib/x86_64-linux-gnu/ 76 | - cp /src/util-linux/nsenter.static /opt/deckhouse/sds/bin/nsenter.static 77 | - cp /lib64/libudev.so.1 /opt/deckhouse/sds/lib/x86_64-linux-gnu/libudev.so.1 78 | - cp /lib64/libc.so.6 /opt/deckhouse/sds/lib/x86_64-linux-gnu/libc.so.6 79 | - cp /lib64/libcap.so.2 /opt/deckhouse/sds/lib/x86_64-linux-gnu/libcap.so.2 80 | # There is no more such file in P11 with glibc-core that it was a part of. Now it's /usr/lib/x86_64-linux-gnu/sys-root/lib64/ld-linux-x86-64.so.2 81 | #- cp /lib64/ld-2.32.so /opt/deckhouse/sds/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2 82 | - cp /usr/lib/x86_64-linux-gnu/sys-root/lib64/ld-linux-x86-64.so.2 /opt/deckhouse/sds/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2 83 | - cp /opt/deckhouse/sds/bin/lsblk /opt/deckhouse/sds/bin/lsblk.dynamic 84 | - chmod +x /binary_replace.sh 85 | - /binary_replace.sh -i "{{ $binaries }}" -o /relocate 86 | 87 | --- 88 | image: {{ $.ImageName }}-golang-artifact 89 | fromImage: builder/golang-alpine 90 | final: false 91 | 92 | import: 93 | - image: {{ $.ImageName }}-src-artifact 94 | add: /src 95 | to: /src 96 | before: install 97 | 98 | mount: 99 | - fromPath: ~/go-pkg-cache 100 | to: /go/pkg 101 | 102 | shell: 103 | setup: 104 | - cd /src/images/{{ $.ImageName }}/cmd 105 | - GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build -ldflags="-s -w" -tags {{ $.Root.MODULE_EDITION }} -o /{{ $.ImageName }} 106 | - chmod +x /{{ $.ImageName }} 107 | 108 | --- 109 | image: {{ $.ImageName }} 110 | fromImage: base/distroless 111 | import: 112 | - image: {{ $.ImageName }}-binaries-artifact 113 | add: /relocate 114 | to: / 115 | before: setup 116 | - image: {{ $.ImageName }}-golang-artifact 117 | add: /{{ $.ImageName }} 118 | to: /{{ $.ImageName }} 119 | before: setup 120 | 121 | docker: 122 | ENTRYPOINT: ["/{{ $.ImageName }}"] 123 | USER: deckhouse:deckhouse 124 | -------------------------------------------------------------------------------- /images/go-hooks/werf.inc.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | image: {{ $.ImageName }}-artifact 3 | fromImage: builder/golang-alpine 4 | final: false 5 | 6 | git: 7 | - add: /hooks/go 8 | to: /usr/src/app/hooks/go 9 | stageDependencies: 10 | install: 11 | - '**/go.mod' 12 | - '**/go.sum' 13 | beforeSetup: 14 | - '**/*.go' 15 | - add: /api 16 | to: /usr/src/app/api 17 | stageDependencies: 18 | install: 19 | - '**/go.mod' 20 | - '**/go.sum' 21 | beforeSetup: 22 | - '**/*.go' 23 | 24 | mount: 25 | - fromPath: ~/go-pkg-cache 26 | to: /go/pkg 27 | 28 | shell: 29 | install: 30 | - cd /usr/src/app/hooks/go 31 | - go mod download 32 | beforeSetup: 33 | - | 34 | cd /usr/src/app/hooks/go; 35 | CGO_ENABLED=0 go build -a -gcflags=all="-l -B" -ldflags="-w -s" -o /usr/local/bin/go-hooks *.go; 36 | -------------------------------------------------------------------------------- /images/sds-health-watcher-controller/api/module_config.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2025 Flant JSC 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package v1alpha1 18 | 19 | import ( 20 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 21 | "k8s.io/apimachinery/pkg/runtime" 22 | "k8s.io/apimachinery/pkg/runtime/schema" 23 | ) 24 | 25 | var ( 26 | // ModuleConfigGVR GroupVersionResource 27 | ModuleConfigGVR = schema.GroupVersionResource{ 28 | Group: SchemeGroupVersion.Group, 29 | Version: SchemeGroupVersion.Version, 30 | Resource: "moduleconfigs", 31 | } 32 | ModuleConfigGVK = schema.GroupVersionKind{ 33 | Group: SchemeGroupVersion.Group, 34 | Version: SchemeGroupVersion.Version, 35 | Kind: "ModuleConfig", 36 | } 37 | ) 38 | 39 | var _ runtime.Object = (*ModuleConfig)(nil) 40 | 41 | // +genclient 42 | // +genclient:nonNamespaced 43 | // +k8s:deepcopy-gen=true 44 | // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object 45 | 46 | // ModuleConfig is a configuration for module or for global config values. 47 | type ModuleConfig struct { 48 | metav1.TypeMeta `json:",inline"` 49 | // Standard object's metadata. 50 | // More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata 51 | // +optional 52 | metav1.ObjectMeta `json:"metadata,omitempty"` 53 | 54 | Spec ModuleConfigSpec `json:"spec"` 55 | 56 | Status ModuleConfigStatus `json:"status,omitempty"` 57 | } 58 | 59 | // SettingsValues empty interface in needed to handle DeepCopy generation. DeepCopy does not work with unnamed empty interfaces 60 | type SettingsValues map[string]interface{} 61 | 62 | func (v *SettingsValues) DeepCopy() *SettingsValues { 63 | nmap := make(map[string]interface{}, len(*v)) 64 | 65 | for key, value := range *v { 66 | nmap[key] = value 67 | } 68 | 69 | vv := SettingsValues(nmap) 70 | 71 | return &vv 72 | } 73 | 74 | func (v SettingsValues) DeepCopyInto(out *SettingsValues) { 75 | { 76 | v := &v 77 | clone := v.DeepCopy() 78 | *out = *clone 79 | return 80 | } 81 | } 82 | 83 | type ModuleConfigSpec struct { 84 | Version int `json:"version,omitempty"` 85 | Settings SettingsValues `json:"settings,omitempty"` 86 | Enabled *bool `json:"enabled,omitempty"` 87 | } 88 | 89 | type ModuleConfigStatus struct { 90 | Version string `json:"version"` 91 | Message string `json:"message"` 92 | } 93 | 94 | // +k8s:deepcopy-gen=true 95 | // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object 96 | 97 | // ModuleConfigList is a list of ModuleConfig resources 98 | type ModuleConfigList struct { 99 | metav1.TypeMeta `json:",inline"` 100 | metav1.ListMeta `json:"metadata"` 101 | 102 | Items []ModuleConfig `json:"items"` 103 | } 104 | 105 | type moduleConfigKind struct{} 106 | 107 | func (in *ModuleConfigStatus) GetObjectKind() schema.ObjectKind { 108 | return &moduleConfigKind{} 109 | } 110 | 111 | func (f *moduleConfigKind) SetGroupVersionKind(_ schema.GroupVersionKind) {} 112 | func (f *moduleConfigKind) GroupVersionKind() schema.GroupVersionKind { 113 | return ModuleConfigGVK 114 | } 115 | -------------------------------------------------------------------------------- /images/sds-health-watcher-controller/api/register.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2025 Flant JSC 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Copyright 2021 Flant JSC 18 | // 19 | // Licensed under the Apache License, Version 2.0 (the "License"); 20 | // you may not use this file except in compliance with the License. 21 | // You may obtain a copy of the License at 22 | // 23 | // http://www.apache.org/licenses/LICENSE-2.0 24 | // 25 | // Unless required by applicable law or agreed to in writing, software 26 | // distributed under the License is distributed on an "AS IS" BASIS, 27 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 28 | // See the License for the specific language governing permissions and 29 | // limitations under the License. 30 | 31 | package v1alpha1 32 | 33 | import ( 34 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 35 | "k8s.io/apimachinery/pkg/runtime" 36 | "k8s.io/apimachinery/pkg/runtime/schema" 37 | ) 38 | 39 | // SchemeGroupVersion is group version used to register these objects 40 | var SchemeGroupVersion = schema.GroupVersion{Group: "deckhouse.io", Version: "v1alpha1"} 41 | 42 | // Resource takes an unqualified resource and returns a Group qualified GroupResource 43 | func Resource(resource string) schema.GroupResource { 44 | return SchemeGroupVersion.WithResource(resource).GroupResource() 45 | } 46 | 47 | var ( 48 | // localSchemeBuilder and AddToScheme will stay in k8s.io/kubernetes. 49 | SchemeBuilder runtime.SchemeBuilder 50 | localSchemeBuilder = &SchemeBuilder 51 | AddToScheme = localSchemeBuilder.AddToScheme 52 | ) 53 | 54 | func init() { 55 | // We only register manually written functions here. The registration of the 56 | // generated functions takes place in the generated files. The separation 57 | // makes the code compile even when the generated files are missing. 58 | localSchemeBuilder.Register(addKnownTypes) 59 | } 60 | 61 | // Adds the list of known types to api.Scheme. 62 | func addKnownTypes(scheme *runtime.Scheme) error { 63 | scheme.AddKnownTypes(SchemeGroupVersion, 64 | &ModuleConfig{}, 65 | &ModuleConfigList{}, 66 | ) 67 | 68 | metav1.AddToGroupVersion(scheme, SchemeGroupVersion) 69 | return nil 70 | } 71 | -------------------------------------------------------------------------------- /images/sds-health-watcher-controller/api/zz_generated.deepcopy.go: -------------------------------------------------------------------------------- 1 | //go:build !ignore_autogenerated 2 | // +build !ignore_autogenerated 3 | 4 | /* 5 | Copyright The Kubernetes Authors. 6 | 7 | Licensed under the Apache License, Version 2.0 (the "License"); 8 | you may not use this file except in compliance with the License. 9 | You may obtain a copy of the License at 10 | 11 | http://www.apache.org/licenses/LICENSE-2.0 12 | 13 | Unless required by applicable law or agreed to in writing, software 14 | distributed under the License is distributed on an "AS IS" BASIS, 15 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | See the License for the specific language governing permissions and 17 | limitations under the License. 18 | */ 19 | 20 | // Code generated by deepcopy-gen. DO NOT EDIT. 21 | 22 | package v1alpha1 23 | 24 | import ( 25 | runtime "k8s.io/apimachinery/pkg/runtime" 26 | ) 27 | 28 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. 29 | func (in *ModuleConfig) DeepCopyInto(out *ModuleConfig) { 30 | *out = *in 31 | out.TypeMeta = in.TypeMeta 32 | in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) 33 | in.Spec.DeepCopyInto(&out.Spec) 34 | out.Status = in.Status 35 | return 36 | } 37 | 38 | // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ModuleConfig. 39 | func (in *ModuleConfig) DeepCopy() *ModuleConfig { 40 | if in == nil { 41 | return nil 42 | } 43 | out := new(ModuleConfig) 44 | in.DeepCopyInto(out) 45 | return out 46 | } 47 | 48 | // DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. 49 | func (in *ModuleConfig) DeepCopyObject() runtime.Object { 50 | if c := in.DeepCopy(); c != nil { 51 | return c 52 | } 53 | return nil 54 | } 55 | 56 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. 57 | func (in *ModuleConfigList) DeepCopyInto(out *ModuleConfigList) { 58 | *out = *in 59 | out.TypeMeta = in.TypeMeta 60 | in.ListMeta.DeepCopyInto(&out.ListMeta) 61 | if in.Items != nil { 62 | in, out := &in.Items, &out.Items 63 | *out = make([]ModuleConfig, len(*in)) 64 | for i := range *in { 65 | (*in)[i].DeepCopyInto(&(*out)[i]) 66 | } 67 | } 68 | return 69 | } 70 | 71 | // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ModuleConfigList. 72 | func (in *ModuleConfigList) DeepCopy() *ModuleConfigList { 73 | if in == nil { 74 | return nil 75 | } 76 | out := new(ModuleConfigList) 77 | in.DeepCopyInto(out) 78 | return out 79 | } 80 | 81 | // DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. 82 | func (in *ModuleConfigList) DeepCopyObject() runtime.Object { 83 | if c := in.DeepCopy(); c != nil { 84 | return c 85 | } 86 | return nil 87 | } 88 | 89 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. 90 | func (in *ModuleConfigSpec) DeepCopyInto(out *ModuleConfigSpec) { 91 | *out = *in 92 | in.Settings.DeepCopyInto(&out.Settings) 93 | if in.Enabled != nil { 94 | in, out := &in.Enabled, &out.Enabled 95 | *out = new(bool) 96 | **out = **in 97 | } 98 | return 99 | } 100 | 101 | // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ModuleConfigSpec. 102 | func (in *ModuleConfigSpec) DeepCopy() *ModuleConfigSpec { 103 | if in == nil { 104 | return nil 105 | } 106 | out := new(ModuleConfigSpec) 107 | in.DeepCopyInto(out) 108 | return out 109 | } 110 | 111 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. 112 | func (in *ModuleConfigStatus) DeepCopyInto(out *ModuleConfigStatus) { 113 | *out = *in 114 | return 115 | } 116 | 117 | // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ModuleConfigStatus. 118 | func (in *ModuleConfigStatus) DeepCopy() *ModuleConfigStatus { 119 | if in == nil { 120 | return nil 121 | } 122 | out := new(ModuleConfigStatus) 123 | in.DeepCopyInto(out) 124 | return out 125 | } 126 | -------------------------------------------------------------------------------- /images/sds-health-watcher-controller/api/zz_generated.defaults.go: -------------------------------------------------------------------------------- 1 | //go:build !ignore_autogenerated 2 | // +build !ignore_autogenerated 3 | 4 | /* 5 | Copyright The Kubernetes Authors. 6 | 7 | Licensed under the Apache License, Version 2.0 (the "License"); 8 | you may not use this file except in compliance with the License. 9 | You may obtain a copy of the License at 10 | 11 | http://www.apache.org/licenses/LICENSE-2.0 12 | 13 | Unless required by applicable law or agreed to in writing, software 14 | distributed under the License is distributed on an "AS IS" BASIS, 15 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | See the License for the specific language governing permissions and 17 | limitations under the License. 18 | */ 19 | 20 | // Code generated by defaulter-gen. DO NOT EDIT. 21 | 22 | package v1alpha1 23 | 24 | import ( 25 | runtime "k8s.io/apimachinery/pkg/runtime" 26 | ) 27 | 28 | // RegisterDefaults adds defaulters functions to the given scheme. 29 | // Public to allow building arbitrary schemes. 30 | // All generated defaulters are covering - they call all nested defaulters. 31 | func RegisterDefaults(scheme *runtime.Scheme) error { 32 | return nil 33 | } 34 | -------------------------------------------------------------------------------- /images/sds-health-watcher-controller/config/config.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2025 Flant JSC 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package config 18 | 19 | import ( 20 | "fmt" 21 | "os" 22 | "strconv" 23 | "time" 24 | 25 | "github.com/deckhouse/sds-node-configurator/images/sds-health-watcher-controller/pkg/logger" 26 | ) 27 | 28 | const ( 29 | ScanInterval = "SCAN_INTERVAL" 30 | LogLevel = "LOG_LEVEL" 31 | MetricsPort = "METRICS_PORT" 32 | DefaultHealthProbeBindAddressEnvName = "HEALTH_PROBE_BIND_ADDRESS" 33 | DefaultHealthProbeBindAddress = ":8081" 34 | ) 35 | 36 | type Options struct { 37 | Loglevel logger.Verbosity 38 | MetricsPort string 39 | ScanIntervalSec time.Duration 40 | NodeName string 41 | HealthProbeBindAddress string 42 | } 43 | 44 | func NewConfig() (*Options, error) { 45 | var opts Options 46 | 47 | loglevel := os.Getenv(LogLevel) 48 | if loglevel == "" { 49 | opts.Loglevel = logger.DebugLevel 50 | } else { 51 | opts.Loglevel = logger.Verbosity(loglevel) 52 | } 53 | 54 | opts.MetricsPort = os.Getenv(MetricsPort) 55 | if opts.MetricsPort == "" { 56 | opts.MetricsPort = ":8080" 57 | } 58 | 59 | opts.HealthProbeBindAddress = os.Getenv(DefaultHealthProbeBindAddressEnvName) 60 | if opts.HealthProbeBindAddress == "" { 61 | opts.HealthProbeBindAddress = DefaultHealthProbeBindAddress 62 | } 63 | 64 | scanInt := os.Getenv(ScanInterval) 65 | if scanInt == "" { 66 | opts.ScanIntervalSec = 5 * time.Second 67 | } else { 68 | interval, err := strconv.Atoi(scanInt) 69 | if err != nil { 70 | return nil, fmt.Errorf("[NewConfig] unable to get %s, error: %w", ScanInterval, err) 71 | } 72 | opts.ScanIntervalSec = time.Duration(interval) * time.Second 73 | } 74 | 75 | return &opts, nil 76 | } 77 | -------------------------------------------------------------------------------- /images/sds-health-watcher-controller/config/config_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2025 Flant JSC 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package config 18 | 19 | import ( 20 | "os" 21 | "testing" 22 | 23 | "github.com/stretchr/testify/assert" 24 | ) 25 | 26 | func TestNewConfig(t *testing.T) { 27 | t.Run("AllValuesSet_ReturnsNoError", func(t *testing.T) { 28 | expMetricsPort := ":0000" 29 | 30 | err := os.Setenv(MetricsPort, expMetricsPort) 31 | if err != nil { 32 | t.Error(err) 33 | } 34 | defer os.Clearenv() 35 | 36 | opts, err := NewConfig() 37 | 38 | if assert.NoError(t, err) { 39 | assert.Equal(t, expMetricsPort, opts.MetricsPort) 40 | } 41 | }) 42 | 43 | t.Run("MetricsPortNotSet_ReturnsDefaultPort", func(t *testing.T) { 44 | expMetricsPort := ":8080" 45 | 46 | defer os.Clearenv() 47 | 48 | opts, err := NewConfig() 49 | 50 | if assert.NoError(t, err) { 51 | assert.Equal(t, expMetricsPort, opts.MetricsPort) 52 | } 53 | }) 54 | } 55 | -------------------------------------------------------------------------------- /images/sds-health-watcher-controller/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/deckhouse/sds-node-configurator/images/sds-health-watcher-controller 2 | 3 | go 1.24.2 4 | 5 | require ( 6 | github.com/cloudflare/cfssl v1.5.0 7 | github.com/deckhouse/sds-node-configurator/api v0.0.0-20250114161813-c1a8b09cd47d 8 | github.com/go-logr/logr v1.4.2 9 | github.com/prometheus/client_golang v1.19.1 10 | github.com/stretchr/testify v1.9.0 11 | gopkg.in/yaml.v3 v3.0.1 12 | k8s.io/api v0.31.0 13 | k8s.io/apiextensions-apiserver v0.31.0 14 | k8s.io/apimachinery v0.32.3 15 | k8s.io/client-go v0.31.0 16 | k8s.io/klog/v2 v2.130.1 17 | k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 18 | sigs.k8s.io/controller-runtime v0.19.0 19 | ) 20 | 21 | replace github.com/deckhouse/sds-node-configurator/api => ../../api 22 | 23 | require ( 24 | github.com/beorn7/perks v1.0.1 // indirect 25 | github.com/cespare/xxhash/v2 v2.3.0 // indirect 26 | github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect 27 | github.com/emicklei/go-restful/v3 v3.11.0 // indirect 28 | github.com/evanphx/json-patch/v5 v5.9.0 // indirect 29 | github.com/fsnotify/fsnotify v1.7.0 // indirect 30 | github.com/fxamacker/cbor/v2 v2.7.0 // indirect 31 | github.com/go-openapi/jsonpointer v0.21.0 // indirect 32 | github.com/go-openapi/jsonreference v0.20.2 // indirect 33 | github.com/go-openapi/swag v0.23.0 // indirect 34 | github.com/gogo/protobuf v1.3.2 // indirect 35 | github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect 36 | github.com/golang/protobuf v1.5.4 // indirect 37 | github.com/google/gnostic-models v0.6.8 // indirect 38 | github.com/google/go-cmp v0.6.0 // indirect 39 | github.com/google/gofuzz v1.2.0 // indirect 40 | github.com/google/uuid v1.6.0 // indirect 41 | github.com/imdario/mergo v0.3.15 // indirect 42 | github.com/josharian/intern v1.0.0 // indirect 43 | github.com/json-iterator/go v1.1.12 // indirect 44 | github.com/mailru/easyjson v0.7.7 // indirect 45 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect 46 | github.com/modern-go/reflect2 v1.0.2 // indirect 47 | github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect 48 | github.com/pkg/errors v0.9.1 // indirect 49 | github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect 50 | github.com/prometheus/client_model v0.6.1 // indirect 51 | github.com/prometheus/common v0.55.0 // indirect 52 | github.com/prometheus/procfs v0.15.1 // indirect 53 | github.com/spf13/pflag v1.0.5 // indirect 54 | github.com/x448/float16 v0.8.4 // indirect 55 | golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 // indirect 56 | golang.org/x/net v0.36.0 // indirect 57 | golang.org/x/oauth2 v0.21.0 // indirect 58 | golang.org/x/sys v0.30.0 // indirect 59 | golang.org/x/term v0.29.0 // indirect 60 | golang.org/x/text v0.22.0 // indirect 61 | golang.org/x/time v0.7.0 // indirect 62 | gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect 63 | google.golang.org/protobuf v1.35.1 // indirect 64 | gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect 65 | gopkg.in/inf.v0 v0.9.1 // indirect 66 | k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f // indirect 67 | sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 // indirect 68 | sigs.k8s.io/structured-merge-diff/v4 v4.4.2 // indirect 69 | sigs.k8s.io/yaml v1.4.0 // indirect 70 | ) 71 | -------------------------------------------------------------------------------- /images/sds-health-watcher-controller/internal/const.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2025 Flant JSC 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package internal 18 | 19 | const ( 20 | SdsNodeConfiguratorNamespace = "d8-sds-node-configurator" 21 | 22 | ReasonPending = "Pending" 23 | ReasonUpdating = "Updating" 24 | ReasonCreating = "Creating" 25 | ReasonTerminating = "Terminating" 26 | ReasonValidationFailed = "ValidationFailed" 27 | 28 | TypeReady = "Ready" 29 | TypeVGConfigurationApplied = "VGConfigurationApplied" 30 | ) 31 | -------------------------------------------------------------------------------- /images/sds-health-watcher-controller/pkg/controller/lvg_conditions_watcher_funcs.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2025 Flant JSC 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package controller 18 | 19 | import ( 20 | "context" 21 | 22 | "gopkg.in/yaml.v3" 23 | v1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" 24 | "sigs.k8s.io/controller-runtime/pkg/client" 25 | 26 | "github.com/deckhouse/sds-node-configurator/api/v1alpha1" 27 | ) 28 | 29 | func getTargetConditionsCount(lvgCrd *v1.CustomResourceDefinition) (int, error) { 30 | type item struct { 31 | Type string `json:"type"` 32 | Properties struct { 33 | LastTransitionTime struct { 34 | Type string `json:"type"` 35 | } `json:"lastTransitionTime"` 36 | Message struct { 37 | Type string `json:"type"` 38 | } `json:"message"` 39 | ObservedGeneration struct { 40 | Type string `json:"type"` 41 | } `json:"observedGeneration"` 42 | Reason struct { 43 | Type string `json:"type"` 44 | } `json:"reason"` 45 | Status struct { 46 | Type string `json:"type"` 47 | } `json:"status"` 48 | Type struct { 49 | Type string `json:"type"` 50 | Enum []string `json:"enum"` 51 | } `json:"type"` 52 | } `json:"properties"` 53 | } 54 | i := item{} 55 | json, err := lvgCrd.Spec.Versions[0].Schema.OpenAPIV3Schema.Properties["status"].Properties["conditions"].Items.MarshalJSON() 56 | if err != nil { 57 | return 0, err 58 | } 59 | 60 | err = yaml.Unmarshal(json, &i) 61 | if err != nil { 62 | return 0, err 63 | } 64 | 65 | return len(i.Properties.Type.Enum), nil 66 | } 67 | 68 | func getCRD(ctx context.Context, cl client.Client, crdName string) (*v1.CustomResourceDefinition, error) { 69 | crd := &v1.CustomResourceDefinition{} 70 | err := cl.Get(ctx, client.ObjectKey{ 71 | Name: crdName, 72 | }, crd) 73 | 74 | return crd, err 75 | } 76 | 77 | func updateLVMVolumeGroupPhaseIfNeeded(ctx context.Context, cl client.Client, lvg *v1alpha1.LVMVolumeGroup, phase string) error { 78 | if lvg.Status.Phase == phase { 79 | return nil 80 | } 81 | 82 | lvg.Status.Phase = phase 83 | 84 | return cl.Status().Update(ctx, lvg) 85 | } 86 | -------------------------------------------------------------------------------- /images/sds-health-watcher-controller/pkg/controller/lvg_conditions_watcher_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2025 Flant JSC 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package controller 18 | 19 | import ( 20 | "context" 21 | "encoding/json" 22 | "testing" 23 | 24 | "github.com/stretchr/testify/assert" 25 | v1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" 26 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 27 | ) 28 | 29 | func TestLVGConditionsWatcher(t *testing.T) { 30 | cl := NewFakeClient() 31 | ctx := context.Background() 32 | 33 | t.Run("getCRD", func(t *testing.T) { 34 | targetName := "target" 35 | crds := []v1.CustomResourceDefinition{ 36 | { 37 | ObjectMeta: metav1.ObjectMeta{ 38 | Name: targetName, 39 | }, 40 | }, 41 | { 42 | ObjectMeta: metav1.ObjectMeta{ 43 | Name: "other-name", 44 | }, 45 | }, 46 | } 47 | 48 | for _, crd := range crds { 49 | err := cl.Create(ctx, &crd) 50 | if err != nil { 51 | t.Error(err) 52 | } 53 | } 54 | 55 | crd, err := getCRD(ctx, cl, targetName) 56 | if err != nil { 57 | t.Error(err) 58 | } 59 | 60 | assert.Equal(t, targetName, crd.Name) 61 | }) 62 | 63 | t.Run("getTargetConditionsCount", func(t *testing.T) { 64 | first, err := json.Marshal("first") 65 | if err != nil { 66 | t.Error(err) 67 | } 68 | second, err := json.Marshal("second") 69 | if err != nil { 70 | t.Error(err) 71 | } 72 | third, err := json.Marshal("third") 73 | if err != nil { 74 | t.Error(err) 75 | } 76 | crd := &v1.CustomResourceDefinition{ 77 | Spec: v1.CustomResourceDefinitionSpec{ 78 | Versions: []v1.CustomResourceDefinitionVersion{ 79 | { 80 | Schema: &v1.CustomResourceValidation{ 81 | OpenAPIV3Schema: &v1.JSONSchemaProps{ 82 | Properties: map[string]v1.JSONSchemaProps{ 83 | "status": { 84 | Properties: map[string]v1.JSONSchemaProps{ 85 | "conditions": { 86 | Items: &v1.JSONSchemaPropsOrArray{ 87 | Schema: &v1.JSONSchemaProps{ 88 | Properties: map[string]v1.JSONSchemaProps{ 89 | "type": { 90 | Enum: []v1.JSON{ 91 | { 92 | Raw: first, 93 | }, 94 | { 95 | Raw: second, 96 | }, 97 | { 98 | Raw: third, 99 | }, 100 | }, 101 | }, 102 | }, 103 | }, 104 | }, 105 | }, 106 | }, 107 | }, 108 | }, 109 | }, 110 | }, 111 | }, 112 | }, 113 | }, 114 | } 115 | 116 | count, err := getTargetConditionsCount(crd) 117 | if err != nil { 118 | t.Error(err) 119 | } 120 | 121 | assert.Equal(t, 3, count) 122 | }) 123 | } 124 | -------------------------------------------------------------------------------- /images/sds-health-watcher-controller/pkg/kubutils/kubernetes.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2025 Flant JSC 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package kubutils 18 | 19 | import ( 20 | "fmt" 21 | 22 | "k8s.io/client-go/rest" 23 | "k8s.io/client-go/tools/clientcmd" 24 | ) 25 | 26 | func KubernetesDefaultConfigCreate() (*rest.Config, error) { 27 | // err is either nil and we return config or its value doesn't matter for us here 28 | config, err := rest.InClusterConfig() 29 | if err == nil { 30 | return config, nil 31 | } 32 | 33 | clientConfig := clientcmd.NewNonInteractiveDeferredLoadingClientConfig( 34 | clientcmd.NewDefaultClientConfigLoadingRules(), 35 | &clientcmd.ConfigOverrides{}, 36 | ) 37 | 38 | // Get a config to talk to API server 39 | config, err = clientConfig.ClientConfig() 40 | 41 | if err != nil { 42 | return nil, fmt.Errorf("config kubernetes error %w", err) 43 | } 44 | return config, nil 45 | } 46 | -------------------------------------------------------------------------------- /images/sds-health-watcher-controller/pkg/logger/logger.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2025 Flant JSC 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package logger 18 | 19 | import ( 20 | "fmt" 21 | "strconv" 22 | 23 | "github.com/go-logr/logr" 24 | "k8s.io/klog/v2/textlogger" 25 | ) 26 | 27 | const ( 28 | ErrorLevel Verbosity = "0" 29 | WarningLevel Verbosity = "1" 30 | InfoLevel Verbosity = "2" 31 | DebugLevel Verbosity = "3" 32 | TraceLevel Verbosity = "4" 33 | CacheLevel Verbosity = "5" 34 | ) 35 | 36 | const ( 37 | warnLvl = iota + 1 38 | infoLvl 39 | debugLvl 40 | traceLvl 41 | cacheLvl 42 | ) 43 | 44 | type ( 45 | Verbosity string 46 | ) 47 | 48 | type Logger struct { 49 | log logr.Logger 50 | } 51 | 52 | func NewLogger(level Verbosity) (*Logger, error) { 53 | v, err := strconv.Atoi(string(level)) 54 | if err != nil { 55 | return nil, err 56 | } 57 | 58 | log := textlogger.NewLogger(textlogger.NewConfig(textlogger.Verbosity(v))).WithCallDepth(1) 59 | 60 | return &Logger{log: log}, nil 61 | } 62 | 63 | func (l Logger) GetLogger() logr.Logger { 64 | return l.log 65 | } 66 | 67 | func (l Logger) Error(err error, message string, keysAndValues ...interface{}) { 68 | l.log.Error(err, fmt.Sprintf("ERROR %s", message), keysAndValues...) 69 | } 70 | 71 | func (l Logger) Warning(message string, keysAndValues ...interface{}) { 72 | l.log.V(warnLvl).Info(fmt.Sprintf("WARNING %s", message), keysAndValues...) 73 | } 74 | 75 | func (l Logger) Info(message string, keysAndValues ...interface{}) { 76 | l.log.V(infoLvl).Info(fmt.Sprintf("INFO %s", message), keysAndValues...) 77 | } 78 | 79 | func (l Logger) Debug(message string, keysAndValues ...interface{}) { 80 | l.log.V(debugLvl).Info(fmt.Sprintf("DEBUG %s", message), keysAndValues...) 81 | } 82 | 83 | func (l Logger) Trace(message string, keysAndValues ...interface{}) { 84 | l.log.V(traceLvl).Info(fmt.Sprintf("TRACE %s", message), keysAndValues...) 85 | } 86 | 87 | func (l Logger) Cache(message string, keysAndValues ...interface{}) { 88 | l.log.V(cacheLvl).Info(fmt.Sprintf("CACHE %s", message), keysAndValues...) 89 | } 90 | -------------------------------------------------------------------------------- /images/sds-health-watcher-controller/werf.inc.yaml: -------------------------------------------------------------------------------- 1 | {{ $binaries := "/sds-utils/bin/lvm.static" }} 2 | 3 | # Do not remove. It's used in external tests. 4 | --- 5 | image: {{ $.ImageName }}-src-artifact 6 | fromImage: builder/src 7 | final: false 8 | 9 | git: 10 | - add: / 11 | to: /src 12 | includePaths: 13 | - api 14 | - lib/go 15 | - images/{{ $.ImageName }} 16 | stageDependencies: 17 | install: 18 | - '**/*' 19 | excludePaths: 20 | - images/{{ $.ImageName }}/werf.yaml 21 | 22 | shell: 23 | install: 24 | - echo "src artifact" 25 | 26 | --- 27 | image: {{ $.ImageName }}-golang-artifact 28 | fromImage: builder/golang-alpine 29 | final: false 30 | 31 | import: 32 | - image: {{ $.ImageName }}-src-artifact 33 | add: /src 34 | to: /src 35 | before: install 36 | 37 | mount: 38 | - fromPath: ~/go-pkg-cache 39 | to: /go/pkg 40 | 41 | shell: 42 | setup: 43 | - cd /src/images/{{ $.ImageName }}/cmd 44 | - GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build -ldflags="-s -w" -o /{{ $.ImageName }} 45 | - chmod +x /{{ $.ImageName }} 46 | 47 | --- 48 | image: {{ $.ImageName }} 49 | fromImage: base/distroless 50 | 51 | import: 52 | - image: {{ $.ImageName }}-golang-artifact 53 | add: /{{ $.ImageName }} 54 | to: /{{ $.ImageName }} 55 | before: setup 56 | 57 | docker: 58 | ENTRYPOINT: ["/{{ $.ImageName }}"] 59 | USER: deckhouse:deckhouse 60 | -------------------------------------------------------------------------------- /images/sds-utils-installer/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/deckhouse/sds-node-configurator/images/sds-utils-installer 2 | 3 | go 1.24.2 4 | -------------------------------------------------------------------------------- /images/sds-utils-installer/go.sum: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/deckhouse/sds-node-configurator/1f31870512024aef8129327c923b5580c5ef243b/images/sds-utils-installer/go.sum -------------------------------------------------------------------------------- /images/sds-utils-installer/werf.inc.yaml: -------------------------------------------------------------------------------- 1 | {{ $binaries := "/sds-utils/bin/lvm.static /sds-utils/bin/dmsetup.static /sds-utils/lib/libudev.so.1 /sds-utils/lib/libgcc_s.so.1 /sds-utils/lib/libm.so.6 /sds-utils/lib/libc.so.6 /sds-utils/lib/ld-linux-x86-64.so.2 /sds-utils/bin/thin_dump /sds-utils/bin/pdata_tools" }} 2 | 3 | # Do not remove. It's used in external tests. 4 | --- 5 | image: {{ $.ImageName }}-src-artifact 6 | fromImage: builder/src 7 | final: false 8 | 9 | git: 10 | - add: / 11 | to: /src 12 | includePaths: 13 | - api 14 | - lib/go 15 | - images/{{ $.ImageName }} 16 | stageDependencies: 17 | install: 18 | - '**/*' 19 | excludePaths: 20 | - images/{{ $.ImageName }}/werf.yaml 21 | 22 | shell: 23 | install: 24 | - apt-get update 25 | - apt-get -y install git 26 | - git config --global advice.detachedHead false 27 | - git clone --depth 1 {{ env "SOURCE_REPO" }}/lvmteam/lvm2.git /src/lvm2 28 | - cd /src/lvm2 29 | - git fetch --depth 1 origin {{ $.Versions.LVM2 }} 30 | - rm -rf /src/lvm2/.git 31 | 32 | --- 33 | image: {{ $.ImageName }}-binaries-artifact 34 | fromImage: builder/alt 35 | final: false 36 | 37 | import: 38 | - image: {{ $.ImageName }}-src-artifact 39 | add: /src 40 | to: /src 41 | before: install 42 | 43 | git: 44 | - add: /tools/dev_images/additional_tools/binary_replace.sh 45 | to: /binary_replace.sh 46 | stageDependencies: 47 | install: 48 | - "**/*" 49 | 50 | shell: 51 | install: 52 | - apt-get update 53 | - | 54 | apt-get install -y \ 55 | build-essential \ 56 | autoconf \ 57 | automake \ 58 | libtool \ 59 | pkg-config \ 60 | libdevmapper-devel \ 61 | libaio-devel-static \ 62 | libblkid-devel-static \ 63 | thin-provisioning-tools \ 64 | glibc-core \ 65 | cross-glibc-x86_64 66 | - cd /src/lvm2 67 | - ./configure --enable-static_link --disable-silent-rules --disable-readline --enable-blkid_wiping --build=x86_64-linux-gnu 68 | - make 69 | - mkdir -p /sds-utils/bin/ 70 | - mv /src/lvm2/tools/lvm.static /sds-utils/bin/lvm.static 71 | - mv /src/lvm2/libdm/dm-tools/dmsetup.static /sds-utils/bin/dmsetup.static 72 | - cp /usr/sbin/{thin_dump,pdata_tools} /sds-utils/bin/ 73 | - mkdir -p /sds-utils/lib/ 74 | - cp /lib64/{libudev.so.1,libgcc_s.so.1,libm.so.6,libc.so.6} /sds-utils/lib/ 75 | - cp /usr/lib/x86_64-linux-gnu/sys-root/lib64/ld-linux-x86-64.so.2 /sds-utils/lib/ 76 | - chmod +x /binary_replace.sh 77 | - /binary_replace.sh -i "{{ $binaries }}" -o /relocate 78 | 79 | --- 80 | image: {{ $.ImageName }}-golang-artifact 81 | fromImage: builder/golang-alpine 82 | final: false 83 | 84 | import: 85 | - image: {{ $.ImageName }}-src-artifact 86 | add: /src 87 | to: /src 88 | before: install 89 | 90 | mount: 91 | - fromPath: ~/go-pkg-cache 92 | to: /go/pkg 93 | 94 | shell: 95 | setup: 96 | - cd /src/images/{{ $.ImageName }}/cmd 97 | - GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build -ldflags="-s -w" -o /{{ $.ImageName }} 98 | - chmod +x /{{ $.ImageName }} 99 | 100 | --- 101 | image: {{ $.ImageName }} 102 | fromImage: base/distroless 103 | import: 104 | - image: {{ $.ImageName }}-binaries-artifact 105 | add: /relocate 106 | to: / 107 | before: setup 108 | - image: {{ $.ImageName }}-golang-artifact 109 | add: /{{ $.ImageName }} 110 | to: /{{ $.ImageName }} 111 | before: setup 112 | 113 | docker: 114 | ENTRYPOINT: ["/{{ $.ImageName }}"] 115 | CMD: ["/sds-utils", "/opt/deckhouse/sds"] 116 | USER: deckhouse:deckhouse 117 | -------------------------------------------------------------------------------- /images/webhooks/cmd/main.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2025 Flant JSC 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package main 18 | 19 | import ( 20 | "flag" 21 | "fmt" 22 | "net/http" 23 | "os" 24 | 25 | "github.com/sirupsen/logrus" 26 | kwhlogrus "github.com/slok/kubewebhook/v2/pkg/log/logrus" 27 | 28 | cn "github.com/deckhouse/sds-node-configurator/api/v1alpha1" 29 | "github.com/deckhouse/sds-node-configurator/images/webhooks/handlers" 30 | ) 31 | 32 | type config struct { 33 | certFile string 34 | keyFile string 35 | } 36 | 37 | func httpHandlerHealthz(w http.ResponseWriter, _ *http.Request) { 38 | fmt.Fprint(w, "Ok.") 39 | } 40 | 41 | func initFlags() config { 42 | cfg := config{} 43 | 44 | fl := flag.NewFlagSet(os.Args[0], flag.ExitOnError) 45 | fl.StringVar(&cfg.certFile, "tls-cert-file", "", "TLS certificate file") 46 | fl.StringVar(&cfg.keyFile, "tls-key-file", "", "TLS key file") 47 | 48 | if err := fl.Parse(os.Args[1:]); err != nil { 49 | fmt.Fprintf(os.Stderr, "error parsing os.Args: %s", err) 50 | os.Exit(1) 51 | } 52 | return cfg 53 | } 54 | 55 | const ( 56 | port = ":8443" 57 | llvsValidatorID = "LLVSValidator" 58 | ) 59 | 60 | func main() { 61 | logrusLogEntry := logrus.NewEntry(logrus.New()) 62 | logrusLogEntry.Logger.SetLevel(logrus.DebugLevel) 63 | logger := kwhlogrus.NewLogrus(logrusLogEntry) 64 | 65 | cfg := initFlags() 66 | 67 | llvsValidatingWebhookHandler, err := handlers.GetValidatingWebhookHandler(handlers.LLVSValidate, llvsValidatorID, &cn.LVMLogicalVolumeSnapshot{}, logger) 68 | if err != nil { 69 | fmt.Fprintf(os.Stderr, "error creating llvsValidatingWebhookHandler: %s", err) 70 | os.Exit(1) 71 | } 72 | 73 | mux := http.NewServeMux() 74 | mux.Handle("/llvs-validate", llvsValidatingWebhookHandler) 75 | mux.HandleFunc("/healthz", httpHandlerHealthz) 76 | 77 | logger.Infof("Listening on %s", port) 78 | err = http.ListenAndServeTLS(port, cfg.certFile, cfg.keyFile, mux) 79 | if err != nil { 80 | fmt.Fprintf(os.Stderr, "error serving webhook: %s", err) 81 | os.Exit(1) 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /images/webhooks/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/deckhouse/sds-node-configurator/images/webhooks 2 | 3 | go 1.24.2 4 | 5 | require ( 6 | github.com/deckhouse/sds-node-configurator/api v0.0.0-20250206203415-a9ffd855f5a3 7 | github.com/deckhouse/sds-node-configurator/lib/go/common v0.0.0-00010101000000-000000000000 8 | github.com/sirupsen/logrus v1.9.3 9 | github.com/slok/kubewebhook/v2 v2.7.0 10 | k8s.io/apimachinery v0.32.3 11 | ) 12 | 13 | replace github.com/deckhouse/sds-node-configurator/api => ../../api 14 | 15 | replace github.com/deckhouse/sds-node-configurator/lib/go/common => ../../lib/go/common 16 | 17 | require ( 18 | github.com/fxamacker/cbor/v2 v2.7.0 // indirect 19 | github.com/go-logr/logr v1.4.2 // indirect 20 | github.com/gogo/protobuf v1.3.2 // indirect 21 | github.com/google/gofuzz v1.2.0 // indirect 22 | github.com/json-iterator/go v1.1.12 // indirect 23 | github.com/kr/text v0.2.0 // indirect 24 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect 25 | github.com/modern-go/reflect2 v1.0.2 // indirect 26 | github.com/spf13/pflag v1.0.6 // indirect 27 | github.com/stretchr/testify v1.10.0 // indirect 28 | github.com/x448/float16 v0.8.4 // indirect 29 | golang.org/x/net v0.36.0 // indirect 30 | golang.org/x/sys v0.30.0 // indirect 31 | golang.org/x/text v0.22.0 // indirect 32 | gopkg.in/inf.v0 v0.9.1 // indirect 33 | k8s.io/api v0.32.1 // indirect 34 | k8s.io/client-go v0.32.1 // indirect 35 | k8s.io/klog/v2 v2.130.1 // indirect 36 | k8s.io/utils v0.0.0-20241210054802-24370beab758 // indirect 37 | sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 // indirect 38 | sigs.k8s.io/structured-merge-diff/v4 v4.5.0 // indirect 39 | sigs.k8s.io/yaml v1.4.0 // indirect 40 | ) 41 | -------------------------------------------------------------------------------- /images/webhooks/handlers/func.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2025 Flant JSC 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package handlers 18 | 19 | import ( 20 | "context" 21 | "net/http" 22 | 23 | kwhhttp "github.com/slok/kubewebhook/v2/pkg/http" 24 | "github.com/slok/kubewebhook/v2/pkg/log" 25 | "github.com/slok/kubewebhook/v2/pkg/model" 26 | kwhvalidating "github.com/slok/kubewebhook/v2/pkg/webhook/validating" 27 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 28 | ) 29 | 30 | func GetValidatingWebhookHandler(validationFunc func(ctx context.Context, _ *model.AdmissionReview, obj metav1.Object) (*kwhvalidating.ValidatorResult, error), validatorID string, obj metav1.Object, logger log.Logger) (http.Handler, error) { 31 | validatorFunc := kwhvalidating.ValidatorFunc(validationFunc) 32 | 33 | validatingWebhookConfig := kwhvalidating.WebhookConfig{ 34 | ID: validatorID, 35 | Obj: obj, 36 | Validator: validatorFunc, 37 | Logger: logger, 38 | } 39 | 40 | validatingWebhook, err := kwhvalidating.NewWebhook(validatingWebhookConfig) 41 | if err != nil { 42 | return nil, err 43 | } 44 | 45 | validatingWebhookHandler, err := kwhhttp.HandlerFor(kwhhttp.HandlerConfig{Webhook: validatingWebhook, Logger: logger}) 46 | 47 | return validatingWebhookHandler, err 48 | } 49 | -------------------------------------------------------------------------------- /images/webhooks/handlers/llvsValidator.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2025 Flant JSC 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package handlers 18 | 19 | import ( 20 | "context" 21 | 22 | "github.com/slok/kubewebhook/v2/pkg/model" 23 | kwhvalidating "github.com/slok/kubewebhook/v2/pkg/webhook/validating" 24 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 25 | 26 | "github.com/deckhouse/sds-node-configurator/lib/go/common/pkg/feature" 27 | ) 28 | 29 | func LLVSValidate(_ context.Context, _ *model.AdmissionReview, _ metav1.Object) (*kwhvalidating.ValidatorResult, error) { 30 | if !feature.SnapshotsEnabled() { 31 | return &kwhvalidating.ValidatorResult{ 32 | Valid: false, 33 | Message: "LVMLogicalVolumeSnapshot is not available in this Deckhouse edition.", 34 | }, nil 35 | } 36 | return &kwhvalidating.ValidatorResult{ 37 | Valid: true, 38 | }, nil 39 | } 40 | -------------------------------------------------------------------------------- /images/webhooks/werf.inc.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | # do not remove this image: used in external audits (DKP CSE) 3 | image: {{ $.ImageName }}-src-artifact 4 | fromImage: builder/src 5 | final: false 6 | git: 7 | - add: / 8 | to: /src 9 | includePaths: 10 | - api 11 | - lib/go 12 | - images/{{ $.ImageName }} 13 | stageDependencies: 14 | install: 15 | - '**/*' 16 | excludePaths: 17 | - images/{{ $.ImageName }}/werf.yaml 18 | 19 | shell: 20 | install: 21 | - rm -rf /src/.git 22 | 23 | --- 24 | image: {{ $.ImageName }}-golang-artifact 25 | fromImage: builder/golang-alpine 26 | final: false 27 | import: 28 | - image: {{ $.ImageName }}-src-artifact 29 | add: /src 30 | to: /src 31 | before: setup 32 | mount: 33 | - fromPath: ~/go-pkg-cache 34 | to: /go/pkg 35 | shell: 36 | setup: 37 | - cd /src/images/{{ $.ImageName }}/cmd 38 | - export CGO_ENABLED=0 GOOS=linux GOARCH=amd64 39 | - go build -ldflags="-s -w" -tags {{ $.Root.MODULE_EDITION }} -o /{{ $.ImageName }} 40 | - chmod +x /{{ $.ImageName }} 41 | 42 | --- 43 | image: {{ $.ImageName }} 44 | fromImage: base/distroless 45 | import: 46 | - image: {{ $.ImageName }}-golang-artifact 47 | add: /webhooks 48 | to: /webhooks 49 | before: setup 50 | docker: 51 | ENTRYPOINT: ["/{{ $.ImageName }}"] 52 | USER: deckhouse:deckhouse 53 | -------------------------------------------------------------------------------- /lib/go/common/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/deckhouse/sds-node-configurator/lib/go/common 2 | 3 | go 1.24.2 4 | -------------------------------------------------------------------------------- /lib/go/common/go.sum: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/deckhouse/sds-node-configurator/1f31870512024aef8129327c923b5580c5ef243b/lib/go/common/go.sum -------------------------------------------------------------------------------- /lib/go/common/pkg/feature/const_ce.go: -------------------------------------------------------------------------------- 1 | //go:build ce 2 | 3 | /* 4 | Copyright 2025 Flant JSC 5 | 6 | Licensed under the Apache License, Version 2.0 (the "License"); 7 | you may not use this file except in compliance with the License. 8 | You may obtain a copy of the License at 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, software 13 | distributed under the License is distributed on an "AS IS" BASIS, 14 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | See the License for the specific language governing permissions and 16 | limitations under the License. 17 | */ 18 | 19 | package feature 20 | 21 | const snapshotsEnabled = false 22 | const volumeCleanupEnabled = false 23 | -------------------------------------------------------------------------------- /lib/go/common/pkg/feature/const_csepro.go: -------------------------------------------------------------------------------- 1 | //go:build csepro 2 | 3 | /* 4 | Copyright 2025 Flant JSC 5 | 6 | Licensed under the Deckhouse Platform Enterprise Edition (EE) license. 7 | you may not use this file except in compliance with the License. 8 | You may obtain a copy of the License at 9 | 10 | https://github.com/deckhouse/deckhouse/blob/main/ee/LICENSE 11 | */ 12 | 13 | package feature 14 | 15 | const snapshotsEnabled = true 16 | const volumeCleanupEnabled = true 17 | -------------------------------------------------------------------------------- /lib/go/common/pkg/feature/const_ee.go: -------------------------------------------------------------------------------- 1 | //go:build ee 2 | 3 | /* 4 | Copyright 2025 Flant JSC 5 | 6 | Licensed under the Deckhouse Platform Enterprise Edition (EE) license. 7 | you may not use this file except in compliance with the License. 8 | You may obtain a copy of the License at 9 | 10 | https://github.com/deckhouse/deckhouse/blob/main/ee/LICENSE 11 | */ 12 | 13 | package feature 14 | 15 | const snapshotsEnabled = true 16 | const volumeCleanupEnabled = true 17 | -------------------------------------------------------------------------------- /lib/go/common/pkg/feature/const_se.go: -------------------------------------------------------------------------------- 1 | //go:build se 2 | 3 | /* 4 | Copyright 2025 Flant JSC 5 | 6 | Licensed under the Deckhouse Platform Enterprise Edition (EE) license. 7 | you may not use this file except in compliance with the License. 8 | You may obtain a copy of the License at 9 | 10 | https://github.com/deckhouse/deckhouse/blob/main/ee/LICENSE 11 | */ 12 | 13 | package feature 14 | 15 | const snapshotsEnabled = true 16 | const volumeCleanupEnabled = false 17 | -------------------------------------------------------------------------------- /lib/go/common/pkg/feature/const_seplus.go: -------------------------------------------------------------------------------- 1 | //go:build seplus 2 | 3 | /* 4 | Copyright 2025 Flant JSC 5 | 6 | Licensed under the Deckhouse Platform Enterprise Edition (EE) license. 7 | you may not use this file except in compliance with the License. 8 | You may obtain a copy of the License at 9 | 10 | https://github.com/deckhouse/deckhouse/blob/main/ee/LICENSE 11 | */ 12 | 13 | package feature 14 | 15 | const snapshotsEnabled = true 16 | const volumeCleanupEnabled = false 17 | -------------------------------------------------------------------------------- /lib/go/common/pkg/feature/feature.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2025 Flant JSC 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package feature 18 | 19 | func SnapshotsEnabled() bool { 20 | return snapshotsEnabled 21 | } 22 | 23 | func VolumeCleanupEnabled() bool { 24 | return volumeCleanupEnabled 25 | } 26 | -------------------------------------------------------------------------------- /lib/python/requirements.txt: -------------------------------------------------------------------------------- 1 | deckhouse==0.4.9 2 | dotmap==1.3.30 3 | PyYAML==6.0.1 4 | kubernetes==28.1.0 5 | -------------------------------------------------------------------------------- /module.yaml: -------------------------------------------------------------------------------- 1 | name: sds-node-configurator 2 | weight: 900 3 | descriptions: 4 | en: "sds node configurator module" 5 | requirements: 6 | bootstrapped: true 7 | deckhouse: ">= 1.67" 8 | namespace: "d8-sds-node-configurator" 9 | -------------------------------------------------------------------------------- /openapi/config-values.yaml: -------------------------------------------------------------------------------- 1 | type: object 2 | required: [] 3 | properties: 4 | disableDs: 5 | type: boolean 6 | default: false 7 | description: Disable sds-node-configurator daemonset 8 | logLevel: 9 | type: string 10 | enum: 11 | - ERROR 12 | - WARN 13 | - INFO 14 | - DEBUG 15 | - TRACE 16 | description: Module log level 17 | default: INFO 18 | enableThinProvisioning: 19 | type: boolean 20 | default: false 21 | description: Allow thin LVM volumes usage 22 | -------------------------------------------------------------------------------- /openapi/doc-ru-config-values.yaml: -------------------------------------------------------------------------------- 1 | type: object 2 | properties: 3 | disableDs: 4 | description: Выключить sds-node-configurator daemonset 5 | logLevel: 6 | description: Уровень логирования для приложений модуля 7 | enableThinProvisioning: 8 | description: Включить поддержку thin LVM томов 9 | -------------------------------------------------------------------------------- /openapi/openapi-case-tests.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | -------------------------------------------------------------------------------- /openapi/values_ce.yaml: -------------------------------------------------------------------------------- 1 | x-extend: 2 | schema: config-values.yaml 3 | type: object 4 | properties: 5 | internal: 6 | type: object 7 | default: {} 8 | properties: 9 | pythonVersions: 10 | type: array 11 | default: [] 12 | items: 13 | type: string 14 | customWebhookCert: 15 | type: object 16 | default: {} 17 | x-required-for-helm: 18 | - crt 19 | - key 20 | - ca 21 | properties: 22 | crt: 23 | type: string 24 | x-examples: ["YjY0ZW5jX3N0cmluZwo="] 25 | key: 26 | type: string 27 | x-examples: ["YjY0ZW5jX3N0cmluZwo="] 28 | ca: 29 | type: string 30 | x-examples: ["YjY0ZW5jX3N0cmluZwo="] 31 | registry: 32 | type: object 33 | description: "System field, overwritten by Deckhouse. Don't use" 34 | -------------------------------------------------------------------------------- /openapi/values_ee.yaml: -------------------------------------------------------------------------------- 1 | x-extend: 2 | schema: config-values.yaml 3 | type: object 4 | properties: 5 | internal: 6 | type: object 7 | default: {} 8 | properties: 9 | pythonVersions: 10 | type: array 11 | default: [] 12 | items: 13 | type: string 14 | customWebhookCert: 15 | type: object 16 | default: {} 17 | x-required-for-helm: 18 | - crt 19 | - key 20 | - ca 21 | properties: 22 | crt: 23 | type: string 24 | x-examples: ["YjY0ZW5jX3N0cmluZwo="] 25 | key: 26 | type: string 27 | x-examples: ["YjY0ZW5jX3N0cmluZwo="] 28 | ca: 29 | type: string 30 | x-examples: ["YjY0ZW5jX3N0cmluZwo="] 31 | registry: 32 | type: object 33 | description: "System field, overwritten by Deckhouse. Don't use" 34 | -------------------------------------------------------------------------------- /release.yaml: -------------------------------------------------------------------------------- 1 | # Module version 2 | version: v0.0.25-dev.1701781997 3 | -------------------------------------------------------------------------------- /templates/agent/nodegroupconfiguration-blacklist-loop-devices.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: deckhouse.io/v1alpha1 2 | kind: NodeGroupConfiguration 3 | metadata: 4 | name: sds-node-configurator-add-loop-devices-to-blacklist.sh 5 | {{- include "helm_lib_module_labels" (list .) | nindent 2 }} 6 | spec: 7 | weight: 100 8 | nodeGroups: ["*"] 9 | bundles: ["*"] 10 | content: | 11 | # Copyright 2024 Flant JSC 12 | # 13 | # Licensed under the Apache License, Version 2.0 (the "License"); 14 | # you may not use this file except in compliance with the License. 15 | # You may obtain a copy of the License at 16 | # 17 | # http://www.apache.org/licenses/LICENSE-2.0 18 | # 19 | # Unless required by applicable law or agreed to in writing, software 20 | # distributed under the License is distributed on an "AS IS" BASIS, 21 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 22 | # See the License for the specific language governing permissions and 23 | # limitations under the License. 24 | 25 | # Loop devices should not be queried by the LVM and multipath commands. 26 | # So we add loop devices into blacklist for multipath and configure 27 | # global_filter in lvm.conf for them 28 | 29 | bb-event-on 'bb-sync-file-changed' '_on_multipath_config_changed' 30 | _on_multipath_config_changed() { 31 | if systemctl is-enabled --quiet multipathd 2>/dev/null; then 32 | systemctl reload multipathd 33 | fi 34 | } 35 | 36 | configure_lvm() { 37 | command -V lvmconfig >/dev/null 2>&1 || return 0 38 | test -f /etc/lvm/lvm.conf || return 0 39 | current_global_filter=$(lvmconfig devices/global_filter 2>/dev/null || true) 40 | 41 | case "${current_global_filter}" in 42 | '' ) new_global_filter='["r|^/dev/loop[0-9]+|"]' ;; 43 | */dev/loop*) return 0 ;; 44 | 'global_filter="'*) new_global_filter='["r|^/dev/loop[0-9]+|",'${current_global_filter#*=}] ;; 45 | 'global_filter=['*) new_global_filter='["r|^/dev/loop[0-9]+|",'${current_global_filter#*[} ;; 46 | *) echo error parsing global_filter >&2; return 1 ;; 47 | esac 48 | 49 | lvmconfig --config "devices/global_filter=$new_global_filter" --withcomments --merge > /etc/lvm/lvm.conf.$$ 50 | mv /etc/lvm/lvm.conf.$$ /etc/lvm/lvm.conf 51 | } 52 | 53 | configure_multipath() { 54 | mkdir -p /etc/multipath/conf.d 55 | bb-sync-file /etc/multipath/conf.d/loop-blacklist.conf - </dev/null 2>&1; then 27 | echo "$tool is not installed." 28 | exit 1 29 | fi 30 | done 31 | 32 | function Help() { 33 | # Display Help 34 | echo "Copy binaries and their libraries to a folder" 35 | echo "Only one input parameter allowed (-f or -i) !!!" 36 | echo 37 | echo "Syntax: scriptTemplate [-h|f|i|o]" 38 | echo "options:" 39 | echo "f Files with paths to binaries; Support mask like /sbin/m*" 40 | echo "i Paths to binaries separated by space; Support mask like /sbin/m*; Example: /bin/chmod /bin/mount /sbin/m*" 41 | echo ' List of binaries should be in double quotes, -i "/bin/chmod /bin/mount" ' 42 | echo "o Output directory (Default value: '/relocate')" 43 | echo "h Print this help" 44 | echo 45 | echo 46 | } 47 | 48 | while getopts ":h:i:f:o:" option; do 49 | case $option in 50 | h) # display Help 51 | Help 52 | exit;; 53 | f) 54 | FILE_TEMPLATE_BINS=$OPTARG 55 | ;; 56 | i) 57 | TEMPLATE_BINS=$OPTARG 58 | ;; 59 | o) 60 | RDIR=$OPTARG 61 | ;; 62 | \?) 63 | echo "Error: Invalid option" 64 | exit;; 65 | esac 66 | done 67 | 68 | if [[ -z $RDIR ]];then 69 | RDIR="/relocate" 70 | fi 71 | mkdir -p "${RDIR}" 72 | 73 | function relocate() { 74 | local binary=$1 75 | relocate_item ${binary} 76 | 77 | for lib in $(ldd ${binary} 2>/dev/null | awk '/statically linked/ {next} {if ($2=="=>") print $3; else print $1}'); do 78 | # don't try to relocate linux-vdso.so lib due to this lib is virtual 79 | if [[ "${lib}" =~ "linux-vdso" ]]; then 80 | continue 81 | fi 82 | relocate_item ${lib} 83 | done 84 | } 85 | 86 | function relocate_item() { 87 | local file=$1 88 | local new_place="${RDIR}$(dirname ${file})" 89 | 90 | mkdir -p ${new_place} 91 | cp -a ${file} ${new_place} 92 | 93 | # if symlink, copy original file too 94 | local orig_file="$(readlink -f ${file})" 95 | if [[ "${file}" != "${orig_file}" ]]; then 96 | cp -a ${orig_file} ${new_place} 97 | fi 98 | } 99 | 100 | function get_binary_path () { 101 | local bin 102 | BINARY_LIST=() 103 | 104 | for bin in "$@"; do 105 | if [[ ! -f $bin ]] || [ "${bin}" == "${RDIR}" ]; then 106 | echo "Not found $bin" 107 | exit 1 108 | fi 109 | BINARY_LIST+=$(ls -la $bin 2>/dev/null | awk '{print $9}')" " 110 | done 111 | 112 | if [[ -z $BINARY_LIST ]]; then echo "No binaryes for replace"; exit 1; fi; 113 | } 114 | 115 | # if get file with binaryes (-f) 116 | if [[ -n $FILE_TEMPLATE_BINS ]] && [[ -f $FILE_TEMPLATE_BINS ]] && [[ -z $TEMPLATE_BINS ]]; then 117 | BIN_TEMPLATE=$(cat $FILE_TEMPLATE_BINS) 118 | get_binary_path ${BIN_TEMPLATE} 119 | # Or get paths to bin via raw input (-i) 120 | elif [[ -n $TEMPLATE_BINS ]] && [[ -z $FILE_TEMPLATE_BINS ]]; then 121 | get_binary_path ${TEMPLATE_BINS} 122 | else 123 | Help 124 | exit 125 | fi 126 | 127 | for binary in ${BINARY_LIST[@]}; do 128 | relocate ${binary} 129 | done 130 | -------------------------------------------------------------------------------- /werf-giterminism.yaml: -------------------------------------------------------------------------------- 1 | giterminismConfigVersion: 1 2 | config: 3 | goTemplateRendering: # The rules for the Go-template functions 4 | allowEnvVariables: 5 | - /CI_.+/ 6 | - MODULES_MODULE_TAG 7 | - WERF_DISABLE_META_TAGS 8 | - GOLANG_VERSION 9 | - GOPROXY 10 | - SOURCE_REPO 11 | - MODULE_EDITION 12 | - MODULES_REGISTRY 13 | - MODULES_REGISTRY_LOGIN 14 | - MODULES_REGISTRY_PASSWORD 15 | - MODULES_MODULE_SOURCE 16 | - MODULES_MODULE_NAME 17 | - DEV_MODULES_REGISTRY 18 | - DEV_MODULES_REGISTRY_LOGIN 19 | - DEV_MODULES_REGISTRY_PASSWORD 20 | - DEV_MODULES_MODULE_SOURCE 21 | allowUncommittedFiles: 22 | - "base_images.yml" 23 | stapel: 24 | mount: 25 | allowFromPaths: 26 | - ~/go-pkg-cache 27 | -------------------------------------------------------------------------------- /werf.yaml: -------------------------------------------------------------------------------- 1 | project: sds-replicated-volume 2 | configVersion: 1 3 | build: 4 | imageSpec: 5 | author: "Deckhouse Kubernetes Platform " 6 | clearHistory: true 7 | config: 8 | clearWerfLabels: true 9 | removeLabels: 10 | - /.*/ 11 | --- 12 | {{ tpl (.Files.Get ".werf/base-images.yaml") $ }} 13 | {{ tpl (.Files.Get ".werf/consts.yaml") $ }} 14 | {{ tpl (.Files.Get ".werf/choose-edition.yaml") $ }} 15 | {{ tpl (.Files.Get ".werf/images.yaml") $ }} 16 | {{ tpl (.Files.Get ".werf/images-digests.yaml") $ }} 17 | {{ tpl (.Files.Get ".werf/python-deps.yaml") $ }} 18 | {{ tpl (.Files.Get ".werf/bundle.yaml") $ }} 19 | {{ tpl (.Files.Get ".werf/release.yaml") $ }} 20 | -------------------------------------------------------------------------------- /werf_cleanup.yaml: -------------------------------------------------------------------------------- 1 | project: sds-node-configurator 2 | configVersion: 1 3 | cleanup: 4 | keepPolicies: 5 | - references: 6 | branch: /.*/ 7 | limit: 8 | in: 168h # keep dev images build during last week which not main|pre-alpha 9 | - references: 10 | branch: /main|pre-alpha/ 11 | imagesPerReference: 12 | last: 5 # keep 5 images for branches main|pre-alpha 13 | --------------------------------------------------------------------------------