├── .dockerignore ├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ ├── failing-test.yaml │ ├── feature_request.md │ └── flaking-test.yaml ├── PULL_REQUEST_TEMPLATE.md ├── dependabot.yml └── workflows │ ├── build-images-action.yml │ ├── dependabot.yml │ ├── e2e-fixture-test.yml │ ├── e2e-test-optional-periodic.yml │ ├── e2e-test-periodic-main.yml │ ├── e2e-test-periodic-release-0.10.yml │ ├── e2e-test-periodic-release-0.8.yml │ ├── e2e-test-periodic-release-0.9.yml │ ├── e2e-test.yml │ ├── golangci-lint.yml │ ├── lint-docs.yaml │ ├── osv-scanner-scan.yml │ ├── pipeline.yml │ ├── pr-gh-workflow-approve.yaml │ ├── pr-verifier.yaml │ ├── release.yaml │ └── unit.yml ├── .gitignore ├── .golangci.yaml ├── .markdownlinkcheck.json ├── .markdownlint-cli2.yaml ├── CONTRIBUTING.md ├── DCO ├── Dockerfile ├── LICENSE ├── Makefile ├── OWNERS ├── OWNERS_ALIASES ├── PROJECT ├── README.md ├── SECURITY_CONTACTS ├── Tiltfile ├── apis ├── go.mod ├── go.sum └── metal3.io │ └── v1alpha1 │ ├── baremetalhost_types.go │ ├── baremetalhost_types_test.go │ ├── baremetalhost_validation.go │ ├── baremetalhost_validation_test.go │ ├── baremetalhost_webhook.go │ ├── baremetalhost_webhook_test.go │ ├── bmceventsubscription_types.go │ ├── bmceventsubscription_validation.go │ ├── bmceventsubscription_validation_test.go │ ├── bmceventsubscription_webhook.go │ ├── bmceventsubscription_webhook_test.go │ ├── dataimage_types.go │ ├── doc.go │ ├── firmwareschema_types.go │ ├── groupversion_info.go │ ├── hardwaredata_types.go │ ├── hostfirmwarecomponents_types.go │ ├── hostfirmwarecomponents_types_test.go │ ├── hostfirmwaresettings_types.go │ ├── hostfirmwaresettings_types_test.go │ ├── hostupdatepolicy_types.go │ ├── preprovisioningimage_types.go │ ├── profile │ └── profile.go │ └── zz_generated.deepcopy.go ├── clean_bmcs.sh ├── cmd ├── get-hardware-details │ └── main.go ├── make-bm-worker │ ├── main.go │ └── templates │ │ ├── templates.go │ │ └── templates_test.go └── make-virt-host │ └── main.go ├── config ├── README.md ├── base │ ├── certmanager │ │ ├── certificate.yaml │ │ ├── kustomization.yaml │ │ └── kustomizeconfig.yaml │ ├── crds │ │ ├── bases │ │ │ ├── metal3.io_baremetalhosts.yaml │ │ │ ├── metal3.io_bmceventsubscriptions.yaml │ │ │ ├── metal3.io_dataimages.yaml │ │ │ ├── metal3.io_firmwareschemas.yaml │ │ │ ├── metal3.io_hardwaredata.yaml │ │ │ ├── metal3.io_hostfirmwarecomponents.yaml │ │ │ ├── metal3.io_hostfirmwaresettings.yaml │ │ │ ├── metal3.io_hostupdatepolicies.yaml │ │ │ └── metal3.io_preprovisioningimages.yaml │ │ ├── kustomization.yaml │ │ ├── kustomizeconfig.yaml │ │ └── patches │ │ │ ├── cainjection_in_baremetalhosts.yaml │ │ │ ├── cainjection_in_bmceventsubscriptions.yaml │ │ │ ├── cainjection_in_dataimages.yaml │ │ │ ├── cainjection_in_firmwareschemas.yaml │ │ │ ├── cainjection_in_hardwaredata.yaml │ │ │ ├── cainjection_in_hostfirmwarecomponents.yaml │ │ │ ├── cainjection_in_hostfirmwaresettings.yaml │ │ │ ├── cainjection_in_preprovisioningimages.yaml │ │ │ ├── webhook_in_baremetalhosts.yaml │ │ │ ├── webhook_in_bmceventsubscriptions.yaml │ │ │ ├── webhook_in_dataimages.yaml │ │ │ ├── webhook_in_firmwareschemas.yaml │ │ │ ├── webhook_in_hardwaredata.yaml │ │ │ ├── webhook_in_hostfirmwarecomponents.yaml │ │ │ ├── webhook_in_hostfirmwaresettings.yaml │ │ │ └── webhook_in_preprovisioningimages.yaml │ ├── kustomization.yaml │ ├── manager.yaml │ ├── manager_webhook_patch.yaml │ ├── prometheus │ │ ├── kustomization.yaml │ │ └── monitor.yaml │ ├── rbac │ │ ├── baremetalhost_editor_role.yaml │ │ ├── baremetalhost_viewer_role.yaml │ │ ├── bmceventsubscription_editor_role.yaml │ │ ├── bmceventsubscription_viewer_role.yaml │ │ ├── dataimage_editor_role.yaml │ │ ├── dataimage_viewer_role.yaml │ │ ├── firmwareschema_editor_role.yaml │ │ ├── firmwareschema_viewer_role.yaml │ │ ├── hardwaredata_editor_role.yaml │ │ ├── hardwaredata_viewer_role.yaml │ │ ├── hostfirmwarecomponents_editor_role.yaml │ │ ├── hostfirmwarecomponents_viewer_role.yaml │ │ ├── hostfirmwaresettings_editor_role.yaml │ │ ├── hostfirmwaresettings_viewer_role.yaml │ │ ├── kustomization.yaml │ │ ├── leader_election_role.yaml │ │ ├── leader_election_role_binding.yaml │ │ ├── metrics_auth_role.yaml │ │ ├── metrics_auth_role_binding.yaml │ │ ├── metrics_reader_role.yaml │ │ ├── metrics_service.yaml │ │ ├── preprovisioningimage_editor_role.yaml │ │ ├── preprovisioningimage_viewer_role.yaml │ │ ├── role.yaml │ │ ├── role_binding.yaml │ │ └── service_account.yaml │ ├── webhook │ │ ├── kustomization.yaml │ │ ├── kustomizeconfig.yaml │ │ ├── manifests.yaml │ │ └── service_patch.yaml │ └── webhookcainjection_patch.yaml ├── components │ ├── basic-auth │ │ ├── credentials_patch.yaml │ │ └── kustomization.yaml │ └── tls │ │ ├── kustomization.yaml │ │ └── tls_ca_patch.yaml ├── default │ ├── ironic.env │ └── kustomization.yaml ├── kustomization.yaml ├── namespace │ ├── kustomization.yaml │ └── namespace.yaml ├── overlays │ ├── basic-auth_tls │ │ └── kustomization.yaml │ ├── e2e-release-0.10 │ │ ├── ironic.env │ │ └── kustomization.yaml │ ├── e2e-release-0.8 │ │ ├── ironic.env │ │ └── kustomization.yaml │ ├── e2e-release-0.9 │ │ ├── ironic.env │ │ └── kustomization.yaml │ ├── e2e │ │ ├── ironic.env │ │ └── kustomization.yaml │ ├── fixture-release-0.10 │ │ └── kustomization.yaml │ ├── fixture-release-0.8 │ │ └── kustomization.yaml │ ├── fixture-release-0.9 │ │ └── kustomization.yaml │ └── fixture │ │ └── kustomization.yaml ├── render │ └── capm3.yaml └── samples │ ├── metal3.io_v1alpha1_baremetalhost.yaml │ ├── metal3.io_v1alpha1_dataimage.yaml │ ├── metal3.io_v1alpha1_firmwareschema.yaml │ ├── metal3.io_v1alpha1_hostfirmwarecomponents.yaml │ ├── metal3.io_v1alpha1_hostfirmwaresettings.yaml │ └── metal3.io_v1alpha1_preprovisioningimage.yaml ├── docs ├── BaremetalHost_ProvisioningState.dot ├── BaremetalHost_ProvisioningState.png ├── api.md ├── baremetalhost-states.md ├── configuration.md ├── dev-setup.md ├── inspectAnnotation.md ├── ironic-authentication.md ├── ironic-endpoint-keepalived-configuration.md ├── releasing.md ├── statusAnnotation.md └── testing.md ├── examples ├── demo-hosts.yaml ├── example-host-bad-credentials.yaml ├── example-host.yaml └── worker-0.yaml ├── go.mod ├── go.sum ├── hack ├── boilerplate.go.txt ├── ci-e2e.sh ├── clean-e2e.sh ├── e2e │ ├── ensure_go.sh │ ├── ensure_htpasswd.sh │ ├── ensure_kubectl.sh │ ├── ensure_yq.sh │ └── net.xml ├── generate.sh ├── gomod.sh ├── ironic_ci.env ├── kind_with_registry.sh ├── manifestlint.sh ├── markdownlint.sh ├── shellcheck.sh ├── tools │ ├── deploy-cli │ │ ├── .gitignore │ │ ├── deploy-cli.go │ │ ├── main.go │ │ └── templates │ │ │ ├── bmo-kustomize-bmopath.tpl │ │ │ ├── bmo-kustomize.tpl │ │ │ ├── ironic-kustomize-bmopath.tpl │ │ │ ├── ironic-kustomize.tpl │ │ │ ├── ironic.env.tpl │ │ │ └── ironic_bmo_configmap_env.tpl │ ├── go.mod │ ├── go.sum │ ├── release │ │ └── notes.go │ └── tools.go └── verify-release.sh ├── internal ├── controller │ └── metal3.io │ │ ├── action_result.go │ │ ├── action_result_test.go │ │ ├── baremetalhost_controller.go │ │ ├── baremetalhost_controller_test.go │ │ ├── bmceventsubscription_controller.go │ │ ├── bmceventsubscription_controller_test.go │ │ ├── controller_test.go │ │ ├── dataimage_controller.go │ │ ├── demo_test.go │ │ ├── errors.go │ │ ├── host_config_data.go │ │ ├── host_config_data_test.go │ │ ├── host_state_machine.go │ │ ├── host_state_machine_test.go │ │ ├── hostfirmwarecomponents_controller.go │ │ ├── hostfirmwarecomponents_test.go │ │ ├── hostfirmwaresettings_controller.go │ │ ├── hostfirmwaresettings_test.go │ │ ├── metrics.go │ │ ├── preprovisioningimage_controller.go │ │ └── preprovisioningimage_controller_test.go └── webhooks │ └── metal3.io │ └── v1alpha1 │ ├── baremetalhost_validation.go │ ├── baremetalhost_validation_test.go │ ├── baremetalhost_webhook.go │ ├── baremetalhost_webhook_test.go │ ├── bmceventsubscription_validation.go │ ├── bmceventsubscription_validation_test.go │ ├── bmceventsubscription_webhook.go │ ├── bmceventsubscription_webhook_test.go │ └── doc.go ├── ironic-deployment ├── README.md ├── base │ ├── ironic.yaml │ └── kustomization.yaml ├── components │ ├── basic-auth │ │ ├── auth.yaml │ │ ├── ironic-auth-config-tpl │ │ └── kustomization.yaml │ ├── ipxe-tls │ │ ├── certificate.yaml │ │ ├── kustomization.yaml │ │ ├── kustomizeconfig.yaml │ │ └── tls.yaml │ ├── keepalived │ │ ├── ironic_bmo_configmap.env │ │ ├── keepalived_patch.yaml │ │ └── kustomization.yaml │ ├── mariadb │ │ ├── certificate.yaml │ │ ├── kustomization.yaml │ │ └── mariadb_patch.yaml │ └── tls │ │ ├── certificate.yaml │ │ ├── kustomization.yaml │ │ ├── kustomizeconfig.yaml │ │ └── tls.yaml ├── default │ ├── ironic_bmo_configmap.env │ └── kustomization.yaml └── overlays │ ├── basic-auth_tls │ ├── basic-auth_tls.yaml │ └── kustomization.yaml │ ├── basic-auth_tls_keepalived │ └── kustomization.yaml │ ├── basic-auth_tls_keepalived_mariadb │ └── kustomization.yaml │ ├── e2e-local-ironic │ ├── ironic-patch.yaml │ └── kustomization.yaml │ ├── e2e-release-26.0 │ ├── ironic-patch.yaml │ ├── ironic_bmo_configmap.env │ └── kustomization.yaml │ ├── e2e-release-27.0 │ ├── ironic-patch.yaml │ ├── ironic_bmo_configmap.env │ └── kustomization.yaml │ ├── e2e-release-28.0 │ ├── ironic-patch.yaml │ ├── ironic_bmo_configmap.env │ └── kustomization.yaml │ ├── e2e-release-29.0 │ ├── ironic-patch.yaml │ ├── ironic_bmo_configmap.env │ └── kustomization.yaml │ ├── e2e │ ├── ironic-patch.yaml │ ├── ironic_bmo_configmap.env │ └── kustomization.yaml │ ├── with-ipxe-builder-tls │ ├── ipxe-configmap.env │ └── kustomization.yaml │ └── with-ipxe-builder │ ├── ipxe-builder-patch.yaml │ ├── ipxe-configmap.env │ └── kustomization.yaml ├── main.go ├── main_test.go ├── pkg ├── hardwareutils │ ├── LICENSE │ ├── bmc │ │ ├── access.go │ │ ├── access_test.go │ │ ├── credentials.go │ │ ├── credentials_test.go │ │ ├── errors.go │ │ ├── idrac_virtualmedia.go │ │ ├── ilo4.go │ │ ├── ilo5.go │ │ ├── ipmi.go │ │ ├── irmc.go │ │ ├── redfish.go │ │ ├── redfish_https.go │ │ └── redfish_virtualmedia.go │ ├── go.mod │ └── go.sum ├── imageprovider │ ├── default.go │ ├── imageprovider.go │ └── invalid.go ├── provisioner │ ├── demo │ │ └── demo.go │ ├── fixture │ │ └── fixture.go │ ├── ironic │ │ ├── adopt_test.go │ │ ├── bios_test.go │ │ ├── capabilities.go │ │ ├── capabilities_test.go │ │ ├── clients │ │ │ ├── auth.go │ │ │ ├── auth_test.go │ │ │ ├── client.go │ │ │ ├── client_test.go │ │ │ ├── features.go │ │ │ ├── features_test.go │ │ │ ├── updateopts.go │ │ │ └── updateopts_test.go │ │ ├── configdrive_test.go │ │ ├── delete_test.go │ │ ├── dependencies.go │ │ ├── devicehints │ │ │ ├── devicehints.go │ │ │ └── devicehints_test.go │ │ ├── factory.go │ │ ├── factory_test.go │ │ ├── findhost_test.go │ │ ├── hardwaredetails │ │ │ ├── hardwaredetails.go │ │ │ └── hardwaredetails_test.go │ │ ├── inspecthardware.go │ │ ├── inspecthardware_test.go │ │ ├── ironic.go │ │ ├── ironic_test.go │ │ ├── power_test.go │ │ ├── prepare_test.go │ │ ├── provision_test.go │ │ ├── provisioncapacity_test.go │ │ ├── raid.go │ │ ├── raid_test.go │ │ ├── ready_test.go │ │ ├── register.go │ │ ├── register_test.go │ │ ├── result.go │ │ ├── servicing.go │ │ ├── servicing_test.go │ │ ├── testbmc │ │ │ └── testbmc.go │ │ ├── testserver │ │ │ ├── ironic.go │ │ │ ├── ironic_test.go │ │ │ └── server.go │ │ └── updatehardwarestate_test.go │ └── provisioner.go ├── secretutils │ ├── label.go │ └── secret_manager.go ├── utils │ ├── stringlist.go │ └── stringlist_test.go └── version │ └── version.go ├── releasenotes ├── v0.10.0-beta.0.md ├── v0.10.0.md ├── v0.10.1.md ├── v0.8.1.md └── v0.9.1.md ├── template └── sarif.tpl ├── test ├── e2e │ ├── README.md │ ├── basic_ops_test.go │ ├── bmc.go │ ├── cert_manager.go │ ├── common.go │ ├── config │ │ ├── bmcs-fixture.yaml │ │ ├── bmcs-ipmi.yaml │ │ ├── bmcs-redfish-virtualmedia.yaml │ │ ├── bmcs-redfish.yaml │ │ ├── fixture.yaml │ │ └── ironic.yaml │ ├── e2e_config.go │ ├── e2e_suite_test.go │ ├── external_inspection_test.go │ ├── externally_provisioned_test.go │ ├── inspection_test.go │ ├── live_iso_test.go │ ├── provisioning_and_annotation_test.go │ ├── re_inspection_test.go │ ├── sushy-tools │ │ └── sushy-emulator.conf │ └── upgrade_test.go ├── go.mod ├── go.sum └── vbmctl │ ├── main.go │ └── templates │ ├── VM.xml.tpl │ ├── pool.xml.tpl │ └── volume.xml.tpl ├── tilt-provider.json └── tools ├── bmh_test ├── clean_local_bmh_test_setup.sh ├── create_bmh.sh ├── create_vm.sh ├── run_local_bmh_test_setup.sh └── vm2vbmc.sh ├── clean_demo_hosts.sh ├── clean_host.sh ├── deploy.sh ├── remove_local_ironic.sh └── run_local_ironic.sh /.dockerignore: -------------------------------------------------------------------------------- 1 | # More info: https://docs.docker.com/engine/reference/builder/#dockerignore-file 2 | # Ignore all files which are not go type 3 | !**/*.go 4 | !**/*.mod 5 | !**/*.sum 6 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Tell us about a problem you are experiencing 4 | 5 | --- 6 | 7 | **What steps did you take and what happened:** 8 | [A clear and concise description on how to REPRODUCE the bug.] 9 | 10 | 11 | **What did you expect to happen:** 12 | 13 | 14 | **Anything else you would like to add:** 15 | [Miscellaneous information that will assist in solving the issue.] 16 | 17 | 18 | **Environment:** 19 | 20 | - Baremetal Operator version: 21 | - Environment (metal3-dev-env or other): 22 | 23 | /kind bug 24 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/failing-test.yaml: -------------------------------------------------------------------------------- 1 | name: Failing Test 2 | description: Report continuously failing tests or jobs in Metal3 CI 3 | body: 4 | - type: textarea 5 | id: jobs 6 | attributes: 7 | label: Which jobs are failing? 8 | placeholder: | 9 | Please only use this template for submitting reports about continuously failing tests or jobs in Metal3 CI. 10 | validations: 11 | required: true 12 | 13 | - type: textarea 14 | id: tests 15 | attributes: 16 | label: Which tests are failing? 17 | validations: 18 | required: true 19 | 20 | - type: textarea 21 | id: since 22 | attributes: 23 | label: Since when has it been failing? 24 | validations: 25 | required: true 26 | 27 | - type: input 28 | id: Jenkins 29 | attributes: 30 | label: Jenkins link 31 | 32 | - type: textarea 33 | id: reason 34 | attributes: 35 | label: Reason for failure (if possible) 36 | 37 | - type: textarea 38 | id: additional 39 | attributes: 40 | label: Anything else we need to know? 41 | 42 | - type: textarea 43 | id: templateLabel 44 | attributes: 45 | label: Label(s) to be applied 46 | value: | 47 | /kind failing-test 48 | One or more /area label. See https://github.com/metal3-io/baremetal-operator/labels for the list of labels. 49 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature addition 3 | about: Suggest and track an idea for this project 4 | 5 | --- 6 | 7 | **User Story** 8 | 9 | As a [developer/user/operator] I would like to [high level description] for [reasons] 10 | 11 | **Detailed Description** 12 | 13 | [A clear and concise description of what you want to happen.] 14 | 15 | **Anything else you would like to add:** 16 | 17 | [Miscellaneous information that will assist in solving the issue.] 18 | 19 | /kind feature 20 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/flaking-test.yaml: -------------------------------------------------------------------------------- 1 | name: Flaking Test 2 | description: Report flaky tests or jobs in Metal3 CI 3 | body: 4 | - type: textarea 5 | id: jobs 6 | attributes: 7 | label: Which jobs are flaking? 8 | description: | 9 | Please only use this template for submitting reports about flaky tests or jobs (pass or fail with no underlying change in code) in Metal3 CI. 10 | validations: 11 | required: true 12 | 13 | - type: textarea 14 | id: tests 15 | attributes: 16 | label: Which tests are flaking? 17 | validations: 18 | required: true 19 | 20 | - type: textarea 21 | id: since 22 | attributes: 23 | label: Since when has it been flaking? 24 | validations: 25 | required: true 26 | 27 | - type: input 28 | id: Jenkins 29 | attributes: 30 | label: Jenkins link 31 | 32 | - type: textarea 33 | id: reason 34 | attributes: 35 | label: Reason for failure (if possible) 36 | 37 | - type: textarea 38 | id: additional 39 | attributes: 40 | label: Anything else we need to know? 41 | 42 | - type: textarea 43 | id: templateLabel 44 | attributes: 45 | label: Label(s) to be applied 46 | value: | 47 | /kind flake 48 | One or more /area label. See https://github.com/metal3-io/baremetal-operator/labels for the list of labels. 49 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | **What this PR does / why we need it**: 5 | 6 | **Which issue(s) this PR fixes** *(optional, in `fixes #(, fixes #, ...)` format, will close the issue(s) when PR gets merged)*: 7 | Fixes # 8 | -------------------------------------------------------------------------------- /.github/workflows/build-images-action.yml: -------------------------------------------------------------------------------- 1 | name: build-images-action 2 | 3 | permissions: 4 | contents: read 5 | 6 | on: 7 | push: 8 | branches: 9 | - 'main' 10 | - 'release-*' 11 | tags: 12 | - 'v*' 13 | 14 | jobs: 15 | build_bmo: 16 | name: Build BMO container image 17 | if: github.repository == 'metal3-io/baremetal-operator' 18 | uses: metal3-io/project-infra/.github/workflows/container-image-build.yml@main 19 | with: 20 | image-name: 'baremetal-operator' 21 | pushImage: true 22 | secrets: 23 | QUAY_USERNAME: ${{ secrets.QUAY_USERNAME }} 24 | QUAY_PASSWORD: ${{ secrets.QUAY_PASSWORD }} 25 | SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }} 26 | -------------------------------------------------------------------------------- /.github/workflows/dependabot.yml: -------------------------------------------------------------------------------- 1 | name: dependabot 2 | 3 | on: 4 | pull_request: 5 | branches: 6 | - dependabot/** 7 | push: 8 | branches: 9 | - dependabot/** 10 | workflow_dispatch: 11 | 12 | permissions: {} 13 | 14 | jobs: 15 | build: 16 | name: Build 17 | runs-on: ubuntu-latest 18 | 19 | permissions: 20 | contents: write 21 | 22 | steps: 23 | - name: Check out code into the Go module directory 24 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 25 | - name: Calculate go version 26 | id: vars 27 | run: echo "go_version=$(make go-version)" >> $GITHUB_OUTPUT 28 | - name: Set up Go 29 | uses: actions/setup-go@0aaccfd150d50ccaeb58ebd88d36e91967a5f35b # v5.4.0 30 | with: 31 | go-version: ${{ steps.vars.outputs.go_version }} 32 | - uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3 33 | name: Restore go cache 34 | with: 35 | path: | 36 | ~/.cache/go-build 37 | ~/go/pkg/mod 38 | key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} 39 | restore-keys: | 40 | ${{ runner.os }}-go- 41 | - name: Install libvirt 42 | run: | 43 | sudo apt-get update 44 | sudo apt-get install -y libvirt-dev 45 | - name: Update all modules 46 | run: make mod 47 | - name: Update generated code 48 | run: make generate manifests 49 | - uses: EndBug/add-and-commit@a94899bca583c204427a224a7af87c02f9b325d5 # v9.1.4 50 | name: Commit changes 51 | with: 52 | author_name: dependabot[bot] 53 | author_email: 49699333+dependabot[bot]@users.noreply.github.com 54 | default_author: github_actor 55 | message: 'Update generated code' 56 | -------------------------------------------------------------------------------- /.github/workflows/e2e-fixture-test.yml: -------------------------------------------------------------------------------- 1 | name: E2E Fixture Test 2 | 3 | on: 4 | workflow_call: 5 | 6 | permissions: {} 7 | 8 | jobs: 9 | test: 10 | name: E2E fixture test 11 | runs-on: ubuntu-latest 12 | 13 | steps: 14 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 15 | 16 | - name: Calculate go version 17 | id: vars 18 | run: echo "go_version=$(make go-version)" >> $GITHUB_OUTPUT 19 | 20 | - name: Set up Go 21 | uses: actions/setup-go@0aaccfd150d50ccaeb58ebd88d36e91967a5f35b # v5.4.0 22 | with: 23 | go-version: ${{ steps.vars.outputs.go_version }} 24 | 25 | - name: Install libvirt 26 | run: | 27 | sudo apt-get update 28 | sudo apt-get install -y libvirt-dev 29 | 30 | - name: Build BMO e2e Docker Image 31 | env: 32 | IMG: quay.io/metal3-io/baremetal-operator:e2e 33 | run: make docker 34 | 35 | - name: Set Up Environment and Run BMO e2e Tests 36 | env: 37 | E2E_CONF_FILE: ${{ github.workspace }}/test/e2e/config/fixture.yaml 38 | USE_EXISTING_CLUSTER: "false" 39 | GINKGO_NODES: 1 40 | run: make test-e2e 41 | 42 | - name: Upload artifacts 43 | if: ${{ !cancelled() }} 44 | uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 45 | with: 46 | name: artifacts-fixture.tar.gz 47 | path: test/e2e/_artifacts 48 | if-no-files-found: error 49 | overwrite: false 50 | -------------------------------------------------------------------------------- /.github/workflows/e2e-test-optional-periodic.yml: -------------------------------------------------------------------------------- 1 | name: Periodic E2E Test Optional 2 | 3 | on: 4 | schedule: 5 | # Run every day at 04:20 UTC (it is recommended to avoid running at the start of the hour) 6 | - cron: '20 4 * * *' 7 | workflow_dispatch: 8 | 9 | permissions: {} 10 | 11 | jobs: 12 | periodic-e2e-test-optional: 13 | if: github.repository == 'metal3-io/baremetal-operator' 14 | strategy: 15 | fail-fast: false 16 | matrix: 17 | bmc-protocol: 18 | - redfish 19 | - redfish-virtualmedia 20 | - ipmi 21 | uses: ./.github/workflows/e2e-test.yml 22 | with: 23 | bmc-protocol: ${{ matrix.bmc-protocol }} 24 | ginkgo-focus: upgrade 25 | permissions: 26 | contents: read 27 | -------------------------------------------------------------------------------- /.github/workflows/e2e-test-periodic-main.yml: -------------------------------------------------------------------------------- 1 | name: Periodic E2E Test 2 | 3 | on: 4 | schedule: 5 | # Run every day at 02:20 UTC (it is recommended to avoid running at the start of the hour) 6 | - cron: '20 2 * * *' 7 | workflow_dispatch: 8 | 9 | permissions: {} 10 | 11 | jobs: 12 | periodic-e2e-test: 13 | if: github.repository == 'metal3-io/baremetal-operator' 14 | strategy: 15 | fail-fast: false 16 | matrix: 17 | bmc-protocol: 18 | - redfish 19 | - redfish-virtualmedia 20 | - ipmi 21 | uses: ./.github/workflows/e2e-test.yml 22 | with: 23 | bmc-protocol: ${{ matrix.bmc-protocol }} 24 | permissions: 25 | contents: read 26 | -------------------------------------------------------------------------------- /.github/workflows/e2e-test-periodic-release-0.10.yml: -------------------------------------------------------------------------------- 1 | name: Periodic E2E Test release-0.10 2 | 3 | on: 4 | schedule: 5 | # Run every day at 02:50 UTC (it is recommended to avoid running at the start of the hour) 6 | - cron: '50 2 * * *' 7 | workflow_dispatch: 8 | 9 | permissions: {} 10 | 11 | jobs: 12 | periodic-e2e-test: 13 | if: github.repository == 'metal3-io/baremetal-operator' 14 | strategy: 15 | fail-fast: false 16 | matrix: 17 | bmc-protocol: 18 | - redfish 19 | - redfish-virtualmedia 20 | - ipmi 21 | uses: ./.github/workflows/e2e-test.yml 22 | with: 23 | bmc-protocol: ${{ matrix.bmc-protocol }} 24 | ref: release-0.10 25 | permissions: 26 | contents: read 27 | -------------------------------------------------------------------------------- /.github/workflows/e2e-test-periodic-release-0.8.yml: -------------------------------------------------------------------------------- 1 | name: Periodic E2E Test release-0.8 2 | 3 | on: 4 | schedule: 5 | # Run every day at 02:20 UTC (it is recommended to avoid running at the start of the hour) 6 | - cron: '20 2 * * *' 7 | workflow_dispatch: 8 | 9 | permissions: {} 10 | 11 | jobs: 12 | periodic-e2e-test: 13 | if: github.repository == 'metal3-io/baremetal-operator' 14 | strategy: 15 | fail-fast: false 16 | matrix: 17 | bmc-protocol: 18 | - redfish 19 | - redfish-virtualmedia 20 | - ipmi 21 | uses: ./.github/workflows/e2e-test.yml 22 | with: 23 | bmc-protocol: ${{ matrix.bmc-protocol }} 24 | ref: release-0.8 25 | permissions: 26 | contents: read 27 | -------------------------------------------------------------------------------- /.github/workflows/e2e-test-periodic-release-0.9.yml: -------------------------------------------------------------------------------- 1 | name: Periodic E2E Test release-0.9 2 | 3 | on: 4 | schedule: 5 | # Run every day at 03:20 UTC (it is recommended to avoid running at the start of the hour) 6 | - cron: '20 3 * * *' 7 | workflow_dispatch: 8 | 9 | permissions: {} 10 | 11 | jobs: 12 | periodic-e2e-test: 13 | if: github.repository == 'metal3-io/baremetal-operator' 14 | strategy: 15 | fail-fast: false 16 | matrix: 17 | bmc-protocol: 18 | - redfish 19 | - redfish-virtualmedia 20 | - ipmi 21 | uses: ./.github/workflows/e2e-test.yml 22 | with: 23 | bmc-protocol: ${{ matrix.bmc-protocol }} 24 | ref: release-0.9 25 | permissions: 26 | contents: read 27 | -------------------------------------------------------------------------------- /.github/workflows/e2e-test.yml: -------------------------------------------------------------------------------- 1 | name: E2E Test 2 | 3 | on: 4 | workflow_call: 5 | inputs: 6 | bmc-protocol: 7 | required: true 8 | type: string 9 | runner: 10 | type: string 11 | default: "ubuntu-latest-4-cores" 12 | ginkgo-focus: 13 | type: string 14 | default: "" 15 | ref: 16 | type: string 17 | default: ${{ github.ref }} 18 | 19 | permissions: {} 20 | 21 | jobs: 22 | test: 23 | name: E2E test 24 | runs-on: ${{ inputs.runner }} 25 | timeout-minutes: 90 26 | 27 | steps: 28 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 29 | with: 30 | ref: ${{ inputs.ref }} 31 | 32 | - name: Install libvirt 33 | run: | 34 | sudo apt-get update 35 | sudo apt-get install -y libvirt-daemon-system qemu-kvm virt-manager libvirt-dev 36 | 37 | - name: Run BMO e2e Tests 38 | env: 39 | BMC_PROTOCOL: ${{ inputs.bmc-protocol }} 40 | GINKGO_FOCUS: "${{ inputs.ginkgo-focus }}" 41 | # We need a new shell to pick up the new group. That is why we do the sudo -s -u $USER ... 42 | # Remove the pre-installed go version. We install the exact version we need. 43 | run: | 44 | sudo usermod -a -G libvirt $USER 45 | sudo rm /usr/bin/go 46 | sudo -s -u $USER --preserve-env bash ${{ github.workspace }}/hack/ci-e2e.sh 47 | 48 | - name: Upload artifacts 49 | if: ${{ !cancelled() }} 50 | uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 51 | with: 52 | name: artifacts-${{ inputs.bmc-protocol }}.tar.gz 53 | path: test/e2e/_artifacts 54 | if-no-files-found: error 55 | overwrite: false 56 | -------------------------------------------------------------------------------- /.github/workflows/golangci-lint.yml: -------------------------------------------------------------------------------- 1 | name: golangci-lint 2 | 3 | on: 4 | workflow_call: 5 | 6 | 7 | permissions: {} 8 | 9 | jobs: 10 | golangci: 11 | name: lint 12 | runs-on: ubuntu-latest 13 | strategy: 14 | fail-fast: false 15 | matrix: 16 | working-directory: 17 | - "" 18 | - test 19 | - apis 20 | - pkg/hardwareutils 21 | - hack/tools 22 | steps: 23 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 24 | 25 | - name: Install libvirt 26 | run: | 27 | sudo apt-get update 28 | sudo apt-get install -y libvirt-dev 29 | - name: Calculate go version 30 | id: vars 31 | run: echo "go_version=$(make go-version)" >> $GITHUB_OUTPUT 32 | - name: Set up Go 33 | uses: actions/setup-go@0aaccfd150d50ccaeb58ebd88d36e91967a5f35b # v5.4.0 34 | with: 35 | go-version: ${{ steps.vars.outputs.go_version }} 36 | - name: golangci-lint-${{matrix.working-directory}} 37 | uses: golangci/golangci-lint-action@55c2c1448f86e01eaae002a5a3a9624417608d84 # v6.5.2 38 | with: 39 | version: v1.64.7 40 | working-directory: ${{matrix.working-directory}} 41 | args: --timeout=10m 42 | -------------------------------------------------------------------------------- /.github/workflows/lint-docs.yaml: -------------------------------------------------------------------------------- 1 | name: Check Markdown links 2 | 3 | on: 4 | pull_request: 5 | types: [opened, edited, reopened, synchronize, ready_for_review] 6 | paths: 7 | - '**.md' 8 | 9 | permissions: {} 10 | 11 | jobs: 12 | markdown-link-check: 13 | name: Broken Links 14 | runs-on: ubuntu-latest 15 | 16 | permissions: 17 | contents: read 18 | 19 | steps: 20 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 21 | - uses: gaurav-nelson/github-action-markdown-link-check@5c5dfc0ac2e225883c0e5f03a85311ec2830d368 # v1 22 | with: 23 | use-quiet-mode: 'yes' 24 | config-file: .markdownlinkcheck.json 25 | -------------------------------------------------------------------------------- /.github/workflows/pipeline.yml: -------------------------------------------------------------------------------- 1 | name: E2E Test pipeline 2 | 3 | on: 4 | pull_request: 5 | types: [opened, edited, reopened, synchronize, ready_for_review] 6 | branches: 7 | - 'main' 8 | - 'release-*' 9 | paths-ignore: 10 | - '**/*.md' 11 | - 'docs/**' 12 | - '.gitignore' 13 | - 'LICENSE' 14 | - 'SECURITY_CONTACTS' 15 | - 'DCO' 16 | - 'OWNERS' 17 | 18 | permissions: {} 19 | 20 | jobs: 21 | golangci-lint: 22 | if: github.event.pull_request.draft == false 23 | uses: ./.github/workflows/golangci-lint.yml 24 | 25 | unit: 26 | if: github.event.pull_request.draft == false 27 | uses: ./.github/workflows/unit.yml 28 | 29 | e2e-fixture-test: 30 | needs: [golangci-lint, unit] 31 | uses: ./.github/workflows/e2e-fixture-test.yml 32 | 33 | e2e-test: 34 | needs: [golangci-lint, unit] 35 | strategy: 36 | # Avoid wasting CI resources 37 | fail-fast: true 38 | matrix: 39 | bmc-protocol: 40 | - redfish-virtualmedia 41 | - ipmi 42 | uses: ./.github/workflows/e2e-test.yml 43 | with: 44 | bmc-protocol: ${{ matrix.bmc-protocol }} 45 | -------------------------------------------------------------------------------- /.github/workflows/pr-gh-workflow-approve.yaml: -------------------------------------------------------------------------------- 1 | # adapted from github.com/kubernetes-sigs/cluster-api/.github/workflows/pr-gh-workflow-approve.yaml 2 | # this workflow approves workflows if the PR has /ok-to-test 3 | # related Prow feature request https://github.com/kubernetes/test-infra/issues/25210 4 | 5 | name: Approve GH Workflows 6 | 7 | on: 8 | pull_request_target: 9 | types: [opened, edited, reopened, synchronize, ready_for_review] 10 | 11 | permissions: {} 12 | 13 | jobs: 14 | approve: 15 | name: Approve on ok-to-test 16 | runs-on: ubuntu-latest 17 | 18 | permissions: 19 | actions: write 20 | 21 | if: contains(github.event.pull_request.labels.*.name, 'ok-to-test') 22 | steps: 23 | - name: Update PR 24 | uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 25 | continue-on-error: true 26 | with: 27 | github-token: ${{ secrets.GITHUB_TOKEN }} 28 | script: | 29 | const result = await github.rest.actions.listWorkflowRunsForRepo({ 30 | owner: context.repo.owner, 31 | repo: context.repo.repo, 32 | event: "pull_request", 33 | status: "action_required", 34 | head_sha: context.payload.pull_request.head.sha, 35 | per_page: 100 36 | }); 37 | 38 | for (var run of result.data.workflow_runs) { 39 | await github.rest.actions.approveWorkflowRun({ 40 | owner: context.repo.owner, 41 | repo: context.repo.repo, 42 | run_id: run.id 43 | }); 44 | } 45 | -------------------------------------------------------------------------------- /.github/workflows/pr-verifier.yaml: -------------------------------------------------------------------------------- 1 | name: PR Verifier 2 | 3 | permissions: {} 4 | 5 | on: 6 | pull_request_target: 7 | types: [opened, edited, reopened, synchronize, ready_for_review] 8 | 9 | jobs: 10 | verify: 11 | name: verify PR contents 12 | uses: metal3-io/project-infra/.github/workflows/pr-verifier.yaml@main 13 | -------------------------------------------------------------------------------- /.github/workflows/unit.yml: -------------------------------------------------------------------------------- 1 | name: unit 2 | 3 | on: 4 | workflow_call: 5 | 6 | 7 | permissions: {} 8 | 9 | jobs: 10 | golangci: 11 | name: unit 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 15 | - name: Calculate go version 16 | id: vars 17 | run: echo "go_version=$(make go-version)" >> $GITHUB_OUTPUT 18 | - name: Set up Go 19 | uses: actions/setup-go@0aaccfd150d50ccaeb58ebd88d36e91967a5f35b # v5.4.0 20 | with: 21 | go-version: ${{ steps.vars.outputs.go_version }} 22 | - name: Run unit tests 23 | run: make -e unit-cover 24 | env: 25 | TEST_FLAGS: "-v" 26 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Temporary Build/Output Files 2 | bin 3 | build/_output 4 | build/_test 5 | _artifacts 6 | artifacts*.tar.gz 7 | test/e2e/images 8 | 9 | # Created by https://www.gitignore.io/api/go 10 | ### Go ### 11 | # Binaries for programs and plugins 12 | *.exe 13 | *.exe~ 14 | *.dll 15 | *.so 16 | *.dylib 17 | # Test binary, build with 'go test -c' 18 | *.test 19 | # Output of the go coverage tool, specifically when used with LiteIDE 20 | *.out 21 | # End of https://www.gitignore.io/api/go 22 | 23 | # go.work files 24 | go.work 25 | go.work.sum 26 | 27 | # Common editor / temporary files 28 | *~ 29 | *.tmp 30 | .DS_Store 31 | .zed* 32 | *.swp 33 | 34 | # Tilt files. 35 | .tiltbuild 36 | /tilt.d 37 | tilt-settings.json 38 | tilt_config.json 39 | 40 | *.crt 41 | *.key 42 | 43 | username 44 | password 45 | *-htpasswd 46 | ironic-auth-config 47 | ironic-inspector-auth-config 48 | ironic-rpc-auth-config 49 | ironic-username 50 | ironic-password 51 | ironic-inspector-username 52 | ironic-inspector-password 53 | HTTP_BASIC_HTPASSWD 54 | ironic-deployment/overlays/temp 55 | config/overlays/temp 56 | 57 | # Development containers (https://containers.dev/) 58 | .devcontainer 59 | # Python venv 60 | .venv 61 | 62 | .idea/ 63 | -------------------------------------------------------------------------------- /.markdownlinkcheck.json: -------------------------------------------------------------------------------- 1 | { 2 | "ignorePatterns": [{ 3 | "pattern": "^http://localhost" 4 | }], 5 | "httpHeaders": [{ 6 | "comment": "Workaround as suggested here: https://github.com/tcort/markdown-link-check/issues/201", 7 | "urls": ["https://docs.github.com/"], 8 | "headers": { 9 | "Accept-Encoding": "zstd, br, gzip, deflate" 10 | } 11 | }], 12 | "timeout": "10s", 13 | "retryOn429": true, 14 | "retryCount": 5, 15 | "fallbackRetryDelay": "30s", 16 | "aliveStatusCodes": [200, 206] 17 | } 18 | 19 | -------------------------------------------------------------------------------- /.markdownlint-cli2.yaml: -------------------------------------------------------------------------------- 1 | # Reference: https://github.com/DavidAnson/markdownlint-cli2#markdownlint-cli2yaml 2 | 3 | config: 4 | ul-indent: 5 | # Kramdown wanted us to have 3 earlier, tho this CLI recommends 2 or 4 6 | indent: 3 7 | 8 | # Don't autofix anything, we're linting here 9 | fix: false 10 | -------------------------------------------------------------------------------- /DCO: -------------------------------------------------------------------------------- 1 | Developer Certificate of Origin 2 | Version 1.1 3 | 4 | Copyright (C) 2004, 2006 The Linux Foundation and its contributors. 5 | 1 Letterman Drive 6 | Suite D4700 7 | San Francisco, CA, 94129 8 | 9 | Everyone is permitted to copy and distribute verbatim copies of this 10 | license document, but changing it is not allowed. 11 | 12 | 13 | Developer's Certificate of Origin 1.1 14 | 15 | By making a contribution to this project, I certify that: 16 | 17 | (a) The contribution was created in whole or in part by me and I 18 | have the right to submit it under the open source license 19 | indicated in the file; or 20 | 21 | (b) The contribution is based upon previous work that, to the best 22 | of my knowledge, is covered under an appropriate open source 23 | license and I have the right under that license to submit that 24 | work with modifications, whether created in whole or in part 25 | by me, under the same open source license (unless I am 26 | permitted to submit under a different license), as indicated 27 | in the file; or 28 | 29 | (c) The contribution was provided directly to me by some other 30 | person who certified (a), (b) or (c) and I have not modified 31 | it. 32 | 33 | (d) I understand and agree that this project and the contribution 34 | are public and that a record of the contribution (including all 35 | personal information I submit with it, including my sign-off) is 36 | maintained indefinitely and may be redistributed consistent with 37 | this project or the open source license(s) involved. 38 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # Support FROM override 2 | ARG BUILD_IMAGE=docker.io/golang:1.24.3@sha256:02a22753ab3426d91ba5ba6f4dfb4ac2454f19b05afdb18d61ab02cbf1a2dffe 3 | ARG BASE_IMAGE=gcr.io/distroless/static:nonroot@sha256:9ecc53c269509f63c69a266168e4a687c7eb8c0cfd753bd8bfcaa4f58a90876f 4 | 5 | # Build the manager binary 6 | FROM $BUILD_IMAGE AS builder 7 | 8 | WORKDIR /workspace 9 | 10 | # Bring in the go dependencies before anything else so we can take 11 | # advantage of caching these layers in future builds. 12 | COPY go.mod go.sum ./ 13 | COPY apis/go.mod apis/go.sum apis/ 14 | COPY hack/tools/go.mod hack/tools/go.sum hack/tools/ 15 | COPY pkg/hardwareutils/go.mod pkg/hardwareutils/go.sum pkg/hardwareutils/ 16 | RUN go mod download 17 | 18 | COPY . . 19 | RUN CGO_ENABLED=0 GO111MODULE=on go build -a -o baremetal-operator main.go 20 | 21 | # Copy the controller-manager into a thin image 22 | # BMO has a dependency preventing us to use the static one, 23 | # using the base one instead 24 | FROM $BASE_IMAGE 25 | 26 | # image.version is set during image build by automation 27 | LABEL org.opencontainers.image.authors="metal3-dev@googlegroups.com" 28 | LABEL org.opencontainers.image.description="This is the image for the Metal3 BareMetal Operator" 29 | LABEL org.opencontainers.image.documentation="https://book.metal3.io/bmo/introduction" 30 | LABEL org.opencontainers.image.licenses="Apache License 2.0" 31 | LABEL org.opencontainers.image.title="Metal3 BareMetal Operator" 32 | LABEL org.opencontainers.image.url="https://github.com/metal3-io/baremetal-operator" 33 | LABEL org.opencontainers.image.vendor="Metal3-io" 34 | 35 | WORKDIR / 36 | COPY --from=builder /workspace/baremetal-operator . 37 | USER nonroot:nonroot 38 | ENTRYPOINT ["/baremetal-operator"] 39 | -------------------------------------------------------------------------------- /OWNERS: -------------------------------------------------------------------------------- 1 | # See the OWNERS docs at https://go.k8s.io/owners 2 | 3 | approvers: 4 | - baremetal-operator-maintainers 5 | 6 | reviewers: 7 | - baremetal-operator-maintainers 8 | - baremetal-operator-reviewers 9 | 10 | emeritus_approvers: 11 | - andfasano 12 | - fmuyassarov 13 | - hardys 14 | - maelk 15 | 16 | emeritus_reviewers: 17 | - ardaguclu 18 | - bfournie 19 | - dukov 20 | - furkatgofurov7 21 | - mquhuy 22 | -------------------------------------------------------------------------------- /OWNERS_ALIASES: -------------------------------------------------------------------------------- 1 | # See the OWNERS docs: https://git.k8s.io/community/contributors/guide/owners.md 2 | 3 | aliases: 4 | baremetal-operator-maintainers: 5 | - dtantsur 6 | - elfosardo 7 | - honza 8 | - kashifest 9 | - lentzi90 10 | - Rozzii 11 | - zaneb 12 | 13 | baremetal-operator-reviewers: 14 | - adilGhaffarDev 15 | - iurygregory 16 | - peppi-lotta 17 | - s3rj1k 18 | - tuminoid 19 | - zhouhao3 20 | -------------------------------------------------------------------------------- /SECURITY_CONTACTS: -------------------------------------------------------------------------------- 1 | # Reporting a security vulnerability 2 | 3 | Please do: 4 | - not disclose any security issue publicly e.g. Pull Requests, Comments. 5 | - not disclose any security issue directly to any owner of the repository or 6 | to any other contributor. 7 | 8 | In this repository security reports are handled according to the 9 | Metal3-io project's security policy. For more information about the security 10 | policy consult the User-Guide [here](https://book.metal3.io/security_policy.html). 11 | 12 | As this repository has a rolling release cycle and there are no release 13 | branches, the security fix will be merged to the main branch. The fix will 14 | either be released in a patch release or in the next upcoming regular release 15 | based on the severity of the issue. 16 | -------------------------------------------------------------------------------- /apis/metal3.io/v1alpha1/bmceventsubscription_validation.go: -------------------------------------------------------------------------------- 1 | /* 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 | 16 | package v1alpha1 17 | 18 | import ( 19 | "errors" 20 | "fmt" 21 | "net/url" 22 | ) 23 | 24 | // validateSubscription validates BMCEventSubscription resource for creation. 25 | // 26 | // Deprecated: This method is going to be removed in a next release. 27 | func (webhook *BMCEventSubscription) validateSubscription(bmces *BMCEventSubscription) []error { 28 | var errs []error 29 | 30 | if bmces.Spec.HostName == "" { 31 | errs = append(errs, errors.New("hostName cannot be empty")) 32 | } 33 | 34 | if bmces.Spec.HTTPHeadersRef != nil { 35 | if bmces.Spec.HTTPHeadersRef.Namespace != bmces.Namespace { 36 | errs = append(errs, errors.New("httpHeadersRef secret must be in the same namespace as the BMCEventSubscription")) 37 | } 38 | } 39 | 40 | if bmces.Spec.Destination == "" { 41 | errs = append(errs, errors.New("destination cannot be empty")) 42 | } else { 43 | destinationURL, err := url.ParseRequestURI(bmces.Spec.Destination) 44 | 45 | if err != nil { 46 | errs = append(errs, fmt.Errorf("destination is invalid: %w", err)) 47 | } else if destinationURL.Path == "" { 48 | errs = append(errs, errors.New("hostname-only destination must have a trailing slash")) 49 | } 50 | } 51 | return errs 52 | } 53 | -------------------------------------------------------------------------------- /apis/metal3.io/v1alpha1/dataimage_types.go: -------------------------------------------------------------------------------- 1 | /* 2 | 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 | const DataImageFinalizer = "dataimage.metal3.io" 24 | 25 | // Contains the DataImage currently attached to the BMH. 26 | type AttachedImageReference struct { 27 | URL string `json:"url"` 28 | } 29 | 30 | // Contains the count of errors and the last error message. 31 | type DataImageError struct { 32 | Count int `json:"count"` 33 | Message string `json:"message"` 34 | } 35 | 36 | // DataImageSpec defines the desired state of DataImage. 37 | type DataImageSpec struct { 38 | // Url is the address of the dataImage that we want to attach 39 | // to a BareMetalHost 40 | URL string `json:"url"` 41 | } 42 | 43 | // DataImageStatus defines the observed state of DataImage. 44 | type DataImageStatus struct { 45 | // Time of last reconciliation 46 | // +optional 47 | LastReconciled *metav1.Time `json:"lastReconciled,omitempty"` 48 | 49 | // Currently attached DataImage 50 | AttachedImage AttachedImageReference `json:"attachedImage,omitempty"` 51 | 52 | // Error count and message when attaching/detaching 53 | Error DataImageError `json:"error,omitempty"` 54 | } 55 | 56 | //+kubebuilder:object:root=true 57 | //+kubebuilder:subresource:status 58 | 59 | // DataImage is the Schema for the dataimages API. 60 | type DataImage struct { 61 | metav1.TypeMeta `json:",inline"` 62 | metav1.ObjectMeta `json:"metadata,omitempty"` 63 | 64 | Spec DataImageSpec `json:"spec,omitempty"` 65 | Status DataImageStatus `json:"status,omitempty"` 66 | } 67 | 68 | //+kubebuilder:object:root=true 69 | 70 | // DataImageList contains a list of DataImage. 71 | type DataImageList struct { 72 | metav1.TypeMeta `json:",inline"` 73 | metav1.ListMeta `json:"metadata,omitempty"` 74 | Items []DataImage `json:"items"` 75 | } 76 | 77 | func init() { 78 | SchemeBuilder.Register(&DataImage{}, &DataImageList{}) 79 | } 80 | -------------------------------------------------------------------------------- /apis/metal3.io/v1alpha1/doc.go: -------------------------------------------------------------------------------- 1 | /* 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 | 16 | // Package v1alpha1 contains API Schema definitions for the metal3.io v1alpha1 API group 17 | // +kubebuilder:object:generate=true 18 | // +k8s:openapi-gen=true 19 | // +groupName=metal3.io 20 | package v1alpha1 21 | -------------------------------------------------------------------------------- /apis/metal3.io/v1alpha1/groupversion_info.go: -------------------------------------------------------------------------------- 1 | /* 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 | 16 | // Package v1alpha1 contains API Schema definitions for the metal3.io v1alpha1 API group 17 | // +kubebuilder:object:generate=true 18 | // +groupName=metal3.io 19 | package v1alpha1 20 | 21 | import ( 22 | "k8s.io/apimachinery/pkg/runtime/schema" 23 | "sigs.k8s.io/controller-runtime/pkg/scheme" 24 | ) 25 | 26 | var ( 27 | // GroupVersion is group version used to register these objects. 28 | GroupVersion = schema.GroupVersion{Group: "metal3.io", Version: "v1alpha1"} 29 | 30 | // SchemeBuilder is used to add go types to the GroupVersionKind scheme. 31 | SchemeBuilder = &scheme.Builder{GroupVersion: GroupVersion} 32 | 33 | // AddToScheme adds the types in this group-version to the given scheme. 34 | AddToScheme = SchemeBuilder.AddToScheme 35 | ) 36 | -------------------------------------------------------------------------------- /apis/metal3.io/v1alpha1/hardwaredata_types.go: -------------------------------------------------------------------------------- 1 | /* 2 | 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 | // HardwareDataSpec defines the desired state of HardwareData. 24 | type HardwareDataSpec struct { 25 | 26 | // The hardware discovered on the host during its inspection. 27 | HardwareDetails *HardwareDetails `json:"hardware,omitempty"` 28 | } 29 | 30 | // +kubebuilder:object:root=true 31 | // +kubebuilder:resource:path=hardwaredata,scope=Namespaced,shortName=hd 32 | // +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp",description="Time duration since creation of HardwareData" 33 | 34 | // HardwareData is the Schema for the hardwaredata API. 35 | type HardwareData struct { 36 | metav1.TypeMeta `json:",inline"` 37 | metav1.ObjectMeta `json:"metadata,omitempty"` 38 | 39 | Spec HardwareDataSpec `json:"spec,omitempty"` 40 | } 41 | 42 | //+kubebuilder:object:root=true 43 | 44 | // HardwareDataList contains a list of HardwareData. 45 | type HardwareDataList struct { 46 | metav1.TypeMeta `json:",inline"` 47 | metav1.ListMeta `json:"metadata,omitempty"` 48 | Items []HardwareData `json:"items"` 49 | } 50 | 51 | func init() { 52 | SchemeBuilder.Register(&HardwareData{}, &HardwareDataList{}) 53 | } 54 | -------------------------------------------------------------------------------- /apis/metal3.io/v1alpha1/hostupdatepolicy_types.go: -------------------------------------------------------------------------------- 1 | /* 2 | Licensed under the Apache License, Version 2.0 (the "License"); 3 | you may not use this file except in compliance with the License. 4 | You may obtain a copy of the License at 5 | 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | 8 | Unless required by applicable law or agreed to in writing, software 9 | distributed under the License is distributed on an "AS IS" BASIS, 10 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | See the License for the specific language governing permissions and 12 | limitations under the License. 13 | */ 14 | 15 | package v1alpha1 16 | 17 | import ( 18 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 19 | ) 20 | 21 | // HostUpdatePolicy enumerates the allowed host update policies. 22 | type UpdatePolicy string 23 | 24 | const ( 25 | HostUpdatePolicyOnPreparing UpdatePolicy = "onPreparing" 26 | HostUpdatePolicyOnReboot UpdatePolicy = "onReboot" 27 | ) 28 | 29 | // HostUpdatePolicySpec defines the desired state of HostUpdatePolicy. 30 | type HostUpdatePolicySpec struct { 31 | // Defines policy for changing firmware settings 32 | // +optional 33 | // +kubebuilder:validation:Enum="onPreparing";"onReboot" 34 | FirmwareSettings UpdatePolicy `json:"firmwareSettings,omitempty"` 35 | 36 | // Defines policy for updating firmware 37 | // +optional 38 | // +kubebuilder:validation:Enum="onPreparing";"onReboot" 39 | FirmwareUpdates UpdatePolicy `json:"firmwareUpdates,omitempty"` 40 | } 41 | 42 | // HostUpdatePolicyStatus defines the observed state of HostUpdatePolicy. 43 | type HostUpdatePolicyStatus struct{} 44 | 45 | // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object 46 | 47 | // HostUpdatePolicy is the Schema for the hostupdatepolicy API. 48 | type HostUpdatePolicy struct { 49 | metav1.TypeMeta `json:",inline"` 50 | metav1.ObjectMeta `json:"metadata,omitempty"` 51 | 52 | Spec HostUpdatePolicySpec `json:"spec,omitempty"` 53 | Status HostUpdatePolicyStatus `json:"status,omitempty"` 54 | } 55 | 56 | // +kubebuilder:object:root=true 57 | 58 | // HostUpdatePolicyList contains a list of HostUpdatePolicy. 59 | type HostUpdatePolicyList struct { 60 | metav1.TypeMeta `json:",inline"` 61 | metav1.ListMeta `json:"metadata,omitempty"` 62 | Items []HostUpdatePolicy `json:"items"` 63 | } 64 | 65 | func init() { 66 | SchemeBuilder.Register(&HostUpdatePolicy{}, &HostUpdatePolicyList{}) 67 | } 68 | -------------------------------------------------------------------------------- /apis/metal3.io/v1alpha1/profile/profile.go: -------------------------------------------------------------------------------- 1 | package profile 2 | 3 | import ( 4 | "fmt" 5 | 6 | metal3api "github.com/metal3-io/baremetal-operator/apis/metal3.io/v1alpha1" 7 | ) 8 | 9 | const ( 10 | // DefaultProfileName is the default hardware profile to use when 11 | // no other profile matches. 12 | DefaultProfileName string = "unknown" 13 | 14 | // EmptyProfileName is the hardware profile without configuration. 15 | EmptyProfileName string = "empty" 16 | ) 17 | 18 | // Profile holds the settings for a class of hardware. 19 | type Profile struct { 20 | // Name holds the profile name 21 | Name string 22 | 23 | // RootDeviceHints holds the suggestions for placing the storage 24 | // for the root filesystem. 25 | RootDeviceHints metal3api.RootDeviceHints 26 | } 27 | 28 | var profiles = make(map[string]Profile) 29 | 30 | func init() { 31 | profiles[DefaultProfileName] = Profile{ 32 | Name: DefaultProfileName, 33 | RootDeviceHints: metal3api.RootDeviceHints{ 34 | DeviceName: "/dev/sda", 35 | }, 36 | } 37 | 38 | profiles["libvirt"] = Profile{ 39 | Name: "libvirt", 40 | RootDeviceHints: metal3api.RootDeviceHints{ 41 | DeviceName: "/dev/vda", 42 | }, 43 | } 44 | 45 | profiles["dell"] = Profile{ 46 | Name: "dell", 47 | RootDeviceHints: metal3api.RootDeviceHints{ 48 | HCTL: "0:0:0:0", 49 | }, 50 | } 51 | 52 | profiles["dell-raid"] = Profile{ 53 | Name: "dell-raid", 54 | RootDeviceHints: metal3api.RootDeviceHints{ 55 | HCTL: "0:2:0:0", 56 | }, 57 | } 58 | 59 | profiles["openstack"] = Profile{ 60 | Name: "openstack", 61 | RootDeviceHints: metal3api.RootDeviceHints{ 62 | DeviceName: "/dev/vdb", 63 | }, 64 | } 65 | 66 | profiles[EmptyProfileName] = Profile{ 67 | Name: EmptyProfileName, 68 | } 69 | } 70 | 71 | // GetProfile returns the named profile. 72 | func GetProfile(name string) (Profile, error) { 73 | profile, ok := profiles[name] 74 | if !ok { 75 | return Profile{}, fmt.Errorf("no hardware profile named %q", name) 76 | } 77 | return profile, nil 78 | } 79 | -------------------------------------------------------------------------------- /clean_bmcs.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # This script reads BMC information in a config file and prepare VMs 4 | # whose info match those config 5 | # 6 | set -x 7 | 8 | REPO_ROOT=$(realpath "$(dirname "${BASH_SOURCE[0]}")") 9 | 10 | virsh pool-destroy default || true 11 | virsh pool-delete default || true 12 | virsh pool-undefine default || true 13 | 14 | CONFIG_FILE=$1 15 | NETWORK=${2:-"baremetal-e2e"} 16 | 17 | readarray -t BMCS < <(yq e -o=j -I=0 '.[]' "${CONFIG_FILE}") 18 | 19 | for bmc in "${BMCS[@]}"; do 20 | bootMacAddress=$(echo "${bmc}" | jq -r '.bootMacAddress') 21 | ipAddress=$(echo "${bmc}" | jq -r '.ipAddress') 22 | virsh -c qemu:///system net-update "${NETWORK}" delete ip-dhcp-host "" --live --config 23 | done 24 | "${REPO_ROOT}/tools/bmh_test/clean_local_bmh_test_setup.sh" "^bmo-e2e-" 25 | rm -rf /tmp/bmo-e2e-* 26 | rm -rf /tmp/pool_oo 27 | -------------------------------------------------------------------------------- /config/base/certmanager/certificate.yaml: -------------------------------------------------------------------------------- 1 | # The following manifests contain a self-signed issuer CR and a certificate CR. 2 | # More document can be found at https://docs.cert-manager.io 3 | # WARNING: Targets CertManager 0.11 check https://docs.cert-manager.io/en/latest/tasks/upgrading/index.html for 4 | # breaking changes 5 | apiVersion: cert-manager.io/v1 6 | kind: Issuer 7 | metadata: 8 | name: selfsigned-issuer 9 | namespace: system 10 | spec: 11 | selfSigned: {} 12 | --- 13 | apiVersion: cert-manager.io/v1 14 | kind: Certificate 15 | metadata: 16 | name: serving-cert # this name should match the one appeared in kustomizeconfig.yaml 17 | namespace: system 18 | spec: 19 | # $(SERVICE_NAME) and $(SERVICE_NAMESPACE) will be substituted by kustomize 20 | dnsNames: 21 | - $(SERVICE_NAME).$(SERVICE_NAMESPACE).svc 22 | - $(SERVICE_NAME).$(SERVICE_NAMESPACE).svc.cluster.local 23 | issuerRef: 24 | kind: Issuer 25 | name: selfsigned-issuer 26 | secretName: bmo-webhook-server-cert # this secret will not be prefixed, since it's not managed by kustomize 27 | -------------------------------------------------------------------------------- /config/base/certmanager/kustomization.yaml: -------------------------------------------------------------------------------- 1 | resources: 2 | - certificate.yaml 3 | 4 | configurations: 5 | - kustomizeconfig.yaml 6 | -------------------------------------------------------------------------------- /config/base/certmanager/kustomizeconfig.yaml: -------------------------------------------------------------------------------- 1 | # This configuration is for teaching kustomize how to update name ref and var substitution 2 | nameReference: 3 | - kind: Issuer 4 | group: cert-manager.io 5 | fieldSpecs: 6 | - kind: Certificate 7 | group: cert-manager.io 8 | path: spec/issuerRef/name 9 | 10 | varReference: 11 | - kind: Certificate 12 | group: cert-manager.io 13 | path: spec/commonName 14 | - kind: Certificate 15 | group: cert-manager.io 16 | path: spec/dnsNames 17 | -------------------------------------------------------------------------------- /config/base/crds/bases/metal3.io_hostupdatepolicies.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: apiextensions.k8s.io/v1 3 | kind: CustomResourceDefinition 4 | metadata: 5 | annotations: 6 | controller-gen.kubebuilder.io/version: v0.16.5 7 | name: hostupdatepolicies.metal3.io 8 | spec: 9 | group: metal3.io 10 | names: 11 | kind: HostUpdatePolicy 12 | listKind: HostUpdatePolicyList 13 | plural: hostupdatepolicies 14 | singular: hostupdatepolicy 15 | scope: Namespaced 16 | versions: 17 | - name: v1alpha1 18 | schema: 19 | openAPIV3Schema: 20 | description: HostUpdatePolicy is the Schema for the hostupdatepolicy API. 21 | properties: 22 | apiVersion: 23 | description: |- 24 | APIVersion defines the versioned schema of this representation of an object. 25 | Servers should convert recognized schemas to the latest internal value, and 26 | may reject unrecognized values. 27 | More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources 28 | type: string 29 | kind: 30 | description: |- 31 | Kind is a string value representing the REST resource this object represents. 32 | Servers may infer this from the endpoint the client submits requests to. 33 | Cannot be updated. 34 | In CamelCase. 35 | More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds 36 | type: string 37 | metadata: 38 | type: object 39 | spec: 40 | description: HostUpdatePolicySpec defines the desired state of HostUpdatePolicy. 41 | properties: 42 | firmwareSettings: 43 | description: Defines policy for changing firmware settings 44 | enum: 45 | - onPreparing 46 | - onReboot 47 | type: string 48 | firmwareUpdates: 49 | description: Defines policy for updating firmware 50 | enum: 51 | - onPreparing 52 | - onReboot 53 | type: string 54 | type: object 55 | status: 56 | description: HostUpdatePolicyStatus defines the observed state of HostUpdatePolicy. 57 | type: object 58 | type: object 59 | served: true 60 | storage: true 61 | -------------------------------------------------------------------------------- /config/base/crds/kustomization.yaml: -------------------------------------------------------------------------------- 1 | # This kustomization.yaml is not intended to be run by itself, 2 | # since it depends on service name and namespace that are out of this kustomize package. 3 | # It should be run by config/default 4 | resources: 5 | - bases/metal3.io_baremetalhosts.yaml 6 | - bases/metal3.io_hostfirmwarecomponents.yaml 7 | - bases/metal3.io_hostfirmwaresettings.yaml 8 | - bases/metal3.io_firmwareschemas.yaml 9 | - bases/metal3.io_preprovisioningimages.yaml 10 | - bases/metal3.io_bmceventsubscriptions.yaml 11 | - bases/metal3.io_hardwaredata.yaml 12 | - bases/metal3.io_dataimages.yaml 13 | - bases/metal3.io_hostupdatepolicies.yaml 14 | #+kubebuilder:scaffold:crdkustomizeresource 15 | 16 | patches: 17 | # [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix. 18 | # patches here are for enabling the conversion webhook for each CRD 19 | #- patches/webhook_in_baremetalhosts.yaml 20 | #- patches/webhook_in_hostfirmwarecomponents.yaml 21 | #- patches/webhook_in_hostfirmwaresettings.yaml 22 | #- patches/webhook_in_firmwareschemas.yaml 23 | #- patches/webhook_in_preprovisioningimages.yaml 24 | #- patches/webhook_in_bmceventsubscriptions.yaml 25 | #- patches/webhook_in_hardwaredata.yaml 26 | #- patches/webhook_in_dataimages.yaml 27 | #+kubebuilder:scaffold:crdkustomizewebhookpatch 28 | 29 | # [CERTMANAGER] To enable webhook, uncomment all the sections with [CERTMANAGER] prefix. 30 | # patches here are for enabling the CA injection for each CRD 31 | - path: patches/cainjection_in_baremetalhosts.yaml 32 | #- patches/cainjection_in_hostfirmwarecomponents.yaml 33 | #- patches/cainjection_in_hostfirmwaresettings.yaml 34 | #- patches/cainjection_in_firmwareschemas.yaml 35 | #- patches/cainjection_in_preprovisioningimages.yaml 36 | #- patches/cainjection_in_bmceventsubscriptions.yaml 37 | #- patches/cainjection_in_hardwaredata.yaml 38 | #- patches/cainjection_in_dataimages.yaml 39 | #+kubebuilder:scaffold:crdkustomizecainjectionpatch 40 | 41 | # the following config is for teaching kustomize how to do kustomization for CRDs. 42 | configurations: 43 | - kustomizeconfig.yaml 44 | -------------------------------------------------------------------------------- /config/base/crds/kustomizeconfig.yaml: -------------------------------------------------------------------------------- 1 | # This file is for teaching kustomize how to substitute name and namespace reference in CRD 2 | nameReference: 3 | - kind: Service 4 | version: v1 5 | fieldSpecs: 6 | - kind: CustomResourceDefinition 7 | version: v1 8 | group: apiextensions.k8s.io 9 | path: spec/conversion/webhook/clientConfig/service/name 10 | 11 | namespace: 12 | - kind: CustomResourceDefinition 13 | version: v1 14 | group: apiextensions.k8s.io 15 | path: spec/conversion/webhook/clientConfig/service/namespace 16 | create: false 17 | 18 | varReference: 19 | - path: metadata/annotations 20 | -------------------------------------------------------------------------------- /config/base/crds/patches/cainjection_in_baremetalhosts.yaml: -------------------------------------------------------------------------------- 1 | # The following patch adds a directive for certmanager to inject CA into the CRD 2 | apiVersion: apiextensions.k8s.io/v1 3 | kind: CustomResourceDefinition 4 | metadata: 5 | annotations: 6 | cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) 7 | name: baremetalhosts.metal3.io 8 | -------------------------------------------------------------------------------- /config/base/crds/patches/cainjection_in_bmceventsubscriptions.yaml: -------------------------------------------------------------------------------- 1 | # The following patch adds a directive for certmanager to inject CA into the CRD 2 | apiVersion: apiextensions.k8s.io/v1 3 | kind: CustomResourceDefinition 4 | metadata: 5 | annotations: 6 | cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) 7 | name: bmceventsubscriptions.metal3.io 8 | -------------------------------------------------------------------------------- /config/base/crds/patches/cainjection_in_dataimages.yaml: -------------------------------------------------------------------------------- 1 | # The following patch adds a directive for certmanager to inject CA into the CRD 2 | apiVersion: apiextensions.k8s.io/v1 3 | kind: CustomResourceDefinition 4 | metadata: 5 | annotations: 6 | cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) 7 | name: dataimages.metal3.io 8 | -------------------------------------------------------------------------------- /config/base/crds/patches/cainjection_in_firmwareschemas.yaml: -------------------------------------------------------------------------------- 1 | # The following patch adds a directive for certmanager to inject CA into the CRD 2 | # CRD conversion requires k8s 1.13 or later. 3 | apiVersion: apiextensions.k8s.io/v1 4 | kind: CustomResourceDefinition 5 | metadata: 6 | annotations: 7 | cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) 8 | name: firmwareschemas.metal3.io 9 | -------------------------------------------------------------------------------- /config/base/crds/patches/cainjection_in_hardwaredata.yaml: -------------------------------------------------------------------------------- 1 | # The following patch adds a directive for certmanager to inject CA into the CRD 2 | apiVersion: apiextensions.k8s.io/v1 3 | kind: CustomResourceDefinition 4 | metadata: 5 | annotations: 6 | cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) 7 | name: hardwaredata.metal3.io.metal3.io 8 | -------------------------------------------------------------------------------- /config/base/crds/patches/cainjection_in_hostfirmwarecomponents.yaml: -------------------------------------------------------------------------------- 1 | # The following patch adds a directive for certmanager to inject CA into the CRD 2 | apiVersion: apiextensions.k8s.io/v1 3 | kind: CustomResourceDefinition 4 | metadata: 5 | annotations: 6 | cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) 7 | name: hostfirmwarecomponents.metal3.io 8 | -------------------------------------------------------------------------------- /config/base/crds/patches/cainjection_in_hostfirmwaresettings.yaml: -------------------------------------------------------------------------------- 1 | # The following patch adds a directive for certmanager to inject CA into the CRD 2 | # CRD conversion requires k8s 1.13 or later. 3 | apiVersion: apiextensions.k8s.io/v1 4 | kind: CustomResourceDefinition 5 | metadata: 6 | annotations: 7 | cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) 8 | name: hostfirmwaresettings.metal3.io 9 | -------------------------------------------------------------------------------- /config/base/crds/patches/cainjection_in_preprovisioningimages.yaml: -------------------------------------------------------------------------------- 1 | # The following patch adds a directive for certmanager to inject CA into the CRD 2 | # CRD conversion requires k8s 1.13 or later. 3 | apiVersion: apiextensions.k8s.io/v1 4 | kind: CustomResourceDefinition 5 | metadata: 6 | annotations: 7 | cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) 8 | name: preprovisioningimages.metal3.io 9 | -------------------------------------------------------------------------------- /config/base/crds/patches/webhook_in_baremetalhosts.yaml: -------------------------------------------------------------------------------- 1 | # The following patch enables a conversion webhook for the CRD 2 | apiVersion: apiextensions.k8s.io/v1 3 | kind: CustomResourceDefinition 4 | metadata: 5 | name: baremetalhosts.metal3.io 6 | spec: 7 | conversion: 8 | strategy: Webhook 9 | webhook: 10 | clientConfig: 11 | service: 12 | namespace: system 13 | name: webhook-service 14 | path: /convert 15 | caBundle: Cg== 16 | conversionReviewVersions: 17 | - v1 18 | -------------------------------------------------------------------------------- /config/base/crds/patches/webhook_in_bmceventsubscriptions.yaml: -------------------------------------------------------------------------------- 1 | # The following patch enables a conversion webhook for the CRD 2 | apiVersion: apiextensions.k8s.io/v1 3 | kind: CustomResourceDefinition 4 | metadata: 5 | name: bmceventsubscriptions.metal3.io 6 | spec: 7 | conversion: 8 | strategy: Webhook 9 | webhook: 10 | clientConfig: 11 | service: 12 | namespace: system 13 | name: webhook-service 14 | path: /convert 15 | -------------------------------------------------------------------------------- /config/base/crds/patches/webhook_in_dataimages.yaml: -------------------------------------------------------------------------------- 1 | # The following patch enables a conversion webhook for the CRD 2 | apiVersion: apiextensions.k8s.io/v1 3 | kind: CustomResourceDefinition 4 | metadata: 5 | name: dataimages.metal3.io 6 | spec: 7 | conversion: 8 | strategy: Webhook 9 | webhook: 10 | clientConfig: 11 | service: 12 | namespace: system 13 | name: webhook-service 14 | path: /convert 15 | caBundle: Cg== 16 | conversionReviewVersions: 17 | - v1 18 | -------------------------------------------------------------------------------- /config/base/crds/patches/webhook_in_firmwareschemas.yaml: -------------------------------------------------------------------------------- 1 | # The following patch enables conversion webhook for CRD 2 | # CRD conversion requires k8s 1.13 or later. 3 | apiVersion: apiextensions.k8s.io/v1 4 | kind: CustomResourceDefinition 5 | metadata: 6 | name: firmwareschemas.metal3.io 7 | spec: 8 | conversion: 9 | strategy: Webhook 10 | webhook: 11 | clientConfig: 12 | service: 13 | namespace: system 14 | name: webhook-service 15 | path: /convert 16 | caBundle: Cg== 17 | conversionReviewVersions: 18 | - v1 19 | -------------------------------------------------------------------------------- /config/base/crds/patches/webhook_in_hardwaredata.yaml: -------------------------------------------------------------------------------- 1 | # The following patch enables a conversion webhook for the CRD 2 | apiVersion: apiextensions.k8s.io/v1 3 | kind: CustomResourceDefinition 4 | metadata: 5 | name: hardwaredata.metal3.io.metal3.io 6 | spec: 7 | conversion: 8 | strategy: Webhook 9 | webhook: 10 | clientConfig: 11 | service: 12 | namespace: system 13 | name: webhook-service 14 | path: /convert 15 | conversionReviewVersions: 16 | - v1 17 | -------------------------------------------------------------------------------- /config/base/crds/patches/webhook_in_hostfirmwarecomponents.yaml: -------------------------------------------------------------------------------- 1 | # The following patch enables a conversion webhook for the CRD 2 | apiVersion: apiextensions.k8s.io/v1 3 | kind: CustomResourceDefinition 4 | metadata: 5 | name: hostfirmwarecomponents.metal3.io 6 | spec: 7 | conversion: 8 | strategy: Webhook 9 | webhook: 10 | clientConfig: 11 | service: 12 | namespace: system 13 | name: webhook-service 14 | path: /convert 15 | conversionReviewVersions: 16 | - v1 17 | -------------------------------------------------------------------------------- /config/base/crds/patches/webhook_in_hostfirmwaresettings.yaml: -------------------------------------------------------------------------------- 1 | # The following patch enables conversion webhook for CRD 2 | # CRD conversion requires k8s 1.13 or later. 3 | apiVersion: apiextensions.k8s.io/v1 4 | kind: CustomResourceDefinition 5 | metadata: 6 | name: hostfirmwaresettings.metal3.io 7 | spec: 8 | conversion: 9 | strategy: Webhook 10 | webhook: 11 | clientConfig: 12 | service: 13 | namespace: system 14 | name: webhook-service 15 | path: /convert 16 | caBundle: Cg== 17 | conversionReviewVersions: 18 | - v1 19 | 20 | -------------------------------------------------------------------------------- /config/base/crds/patches/webhook_in_preprovisioningimages.yaml: -------------------------------------------------------------------------------- 1 | # The following patch enables conversion webhook for CRD 2 | # CRD conversion requires k8s 1.13 or later. 3 | apiVersion: apiextensions.k8s.io/v1 4 | kind: CustomResourceDefinition 5 | metadata: 6 | name: preprovisioningimages.metal3.io 7 | spec: 8 | conversion: 9 | strategy: Webhook 10 | webhook: 11 | clientConfig: 12 | service: 13 | namespace: system 14 | name: webhook-service 15 | path: /convert 16 | caBundle: Cg== 17 | conversionReviewVersions: 18 | - v1 19 | -------------------------------------------------------------------------------- /config/base/manager.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: controller-manager 5 | namespace: system 6 | labels: 7 | control-plane: controller-manager 8 | webhook: metal3-io-v1alpha1-baremetalhost 9 | spec: 10 | selector: 11 | matchLabels: 12 | control-plane: controller-manager 13 | replicas: 1 14 | minReadySeconds: 10 15 | template: 16 | metadata: 17 | labels: 18 | control-plane: controller-manager 19 | webhook: metal3-io-v1alpha1-baremetalhost 20 | spec: 21 | containers: 22 | - command: 23 | - /baremetal-operator 24 | args: 25 | - --enable-leader-election 26 | - --tls-min-version=TLS13 27 | ports: 28 | - containerPort: 8443 29 | protocol: TCP 30 | name: https 31 | image: quay.io/metal3-io/baremetal-operator 32 | imagePullPolicy: Always 33 | env: 34 | - name: POD_NAME 35 | valueFrom: 36 | fieldRef: 37 | fieldPath: metadata.name 38 | - name: POD_NAMESPACE 39 | valueFrom: 40 | fieldRef: 41 | fieldPath: metadata.namespace 42 | envFrom: 43 | - configMapRef: 44 | name: ironic 45 | name: manager 46 | securityContext: 47 | allowPrivilegeEscalation: false 48 | capabilities: 49 | drop: 50 | - ALL 51 | privileged: false 52 | runAsUser: 65532 53 | runAsGroup: 65532 54 | livenessProbe: 55 | httpGet: 56 | path: /healthz 57 | port: 9440 58 | initialDelaySeconds: 10 59 | periodSeconds: 10 60 | timeoutSeconds: 2 61 | successThreshold: 1 62 | failureThreshold: 10 63 | readinessProbe: 64 | httpGet: 65 | path: /readyz 66 | port: 9440 67 | initialDelaySeconds: 10 68 | periodSeconds: 10 69 | timeoutSeconds: 2 70 | successThreshold: 1 71 | failureThreshold: 10 72 | terminationGracePeriodSeconds: 10 73 | securityContext: 74 | runAsNonRoot: true 75 | seccompProfile: 76 | type: RuntimeDefault 77 | serviceAccountName: controller-manager 78 | -------------------------------------------------------------------------------- /config/base/manager_webhook_patch.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: controller-manager 5 | namespace: system 6 | spec: 7 | template: 8 | spec: 9 | containers: 10 | - name: manager 11 | ports: 12 | - containerPort: 9443 13 | name: webhook-server 14 | protocol: TCP 15 | volumeMounts: 16 | - mountPath: /tmp/k8s-webhook-server/serving-certs 17 | name: cert 18 | readOnly: true 19 | volumes: 20 | - name: cert 21 | secret: 22 | defaultMode: 420 23 | secretName: bmo-webhook-server-cert 24 | -------------------------------------------------------------------------------- /config/base/prometheus/kustomization.yaml: -------------------------------------------------------------------------------- 1 | resources: 2 | - monitor.yaml 3 | -------------------------------------------------------------------------------- /config/base/prometheus/monitor.yaml: -------------------------------------------------------------------------------- 1 | 2 | # Prometheus Monitor Service (Metrics) 3 | apiVersion: monitoring.coreos.com/v1 4 | kind: ServiceMonitor 5 | metadata: 6 | labels: 7 | control-plane: controller-manager 8 | name: controller-manager-metrics-monitor 9 | namespace: system 10 | spec: 11 | endpoints: 12 | - path: /metrics 13 | port: https 14 | scheme: https 15 | bearerTokenFile: /var/run/secrets/kubernetes.io/serviceaccount/token 16 | tlsConfig: 17 | insecureSkipVerify: true 18 | selector: 19 | matchLabels: 20 | control-plane: controller-manager 21 | -------------------------------------------------------------------------------- /config/base/rbac/baremetalhost_editor_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions for end users to edit baremetalhosts. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | name: baremetalhost-editor-role 6 | rules: 7 | - apiGroups: 8 | - metal3.io 9 | resources: 10 | - baremetalhosts 11 | verbs: 12 | - create 13 | - delete 14 | - get 15 | - list 16 | - patch 17 | - update 18 | - watch 19 | - apiGroups: 20 | - metal3.io 21 | resources: 22 | - baremetalhosts/status 23 | verbs: 24 | - get 25 | -------------------------------------------------------------------------------- /config/base/rbac/baremetalhost_viewer_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions for end users to view baremetalhosts. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | name: baremetalhost-viewer-role 6 | rules: 7 | - apiGroups: 8 | - metal3.io 9 | resources: 10 | - baremetalhosts 11 | verbs: 12 | - get 13 | - list 14 | - watch 15 | - apiGroups: 16 | - metal3.io 17 | resources: 18 | - baremetalhosts/status 19 | verbs: 20 | - get 21 | -------------------------------------------------------------------------------- /config/base/rbac/bmceventsubscription_editor_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions for end users to edit bmceventsubscriptions. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | name: bmceventsubscription-editor-role 6 | rules: 7 | - apiGroups: 8 | - metal3.io 9 | resources: 10 | - bmceventsubscriptions 11 | verbs: 12 | - create 13 | - delete 14 | - get 15 | - list 16 | - patch 17 | - update 18 | - watch 19 | - apiGroups: 20 | - metal3.io 21 | resources: 22 | - bmceventsubscriptions/status 23 | verbs: 24 | - get 25 | -------------------------------------------------------------------------------- /config/base/rbac/bmceventsubscription_viewer_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions for end users to view bmceventsubscriptions. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | name: bmceventsubscription-viewer-role 6 | rules: 7 | - apiGroups: 8 | - metal3.io 9 | resources: 10 | - bmceventsubscriptions 11 | verbs: 12 | - get 13 | - list 14 | - watch 15 | - apiGroups: 16 | - metal3.io 17 | resources: 18 | - bmceventsubscriptions/status 19 | verbs: 20 | - get 21 | -------------------------------------------------------------------------------- /config/base/rbac/dataimage_editor_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions for end users to edit dataimages. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | name: dataimage-editor-role 6 | rules: 7 | - apiGroups: 8 | - metal3.io 9 | resources: 10 | - dataimages 11 | verbs: 12 | - create 13 | - delete 14 | - get 15 | - list 16 | - patch 17 | - update 18 | - watch 19 | - apiGroups: 20 | - metal3.io 21 | resources: 22 | - dataimages/status 23 | verbs: 24 | - get 25 | -------------------------------------------------------------------------------- /config/base/rbac/dataimage_viewer_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions for end users to view dataimages. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | name: dataimage-viewer-role 6 | rules: 7 | - apiGroups: 8 | - metal3.io 9 | resources: 10 | - dataimages 11 | verbs: 12 | - get 13 | - list 14 | - watch 15 | - apiGroups: 16 | - metal3.io 17 | resources: 18 | - dataimages/status 19 | verbs: 20 | - get 21 | -------------------------------------------------------------------------------- /config/base/rbac/firmwareschema_editor_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions for end users to edit firmwareschemas. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | name: firmwareschema-editor-role 6 | rules: 7 | - apiGroups: 8 | - metal3.io 9 | resources: 10 | - firmwareschemas 11 | verbs: 12 | - create 13 | - delete 14 | - get 15 | - list 16 | - patch 17 | - update 18 | - watch 19 | - apiGroups: 20 | - metal3.io 21 | resources: 22 | - firmwareschemas/status 23 | verbs: 24 | - get 25 | -------------------------------------------------------------------------------- /config/base/rbac/firmwareschema_viewer_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions for end users to view firmwareschemas. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | name: firmwareschema-viewer-role 6 | rules: 7 | - apiGroups: 8 | - metal3.io 9 | resources: 10 | - firmwareschemas 11 | verbs: 12 | - get 13 | - list 14 | - watch 15 | - apiGroups: 16 | - metal3.io 17 | resources: 18 | - firmwareschemas/status 19 | verbs: 20 | - get 21 | -------------------------------------------------------------------------------- /config/base/rbac/hardwaredata_editor_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions for end users to edit hardwaredata. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | name: hardwaredata-editor-role 6 | rules: 7 | - apiGroups: 8 | - metal3.io 9 | resources: 10 | - hardwaredata 11 | verbs: 12 | - create 13 | - delete 14 | - get 15 | - list 16 | - patch 17 | - update 18 | - watch 19 | -------------------------------------------------------------------------------- /config/base/rbac/hardwaredata_viewer_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions for end users to view hardwaredata. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | name: hardwaredata-viewer-role 6 | rules: 7 | - apiGroups: 8 | - metal3.io 9 | resources: 10 | - hardwaredata 11 | verbs: 12 | - get 13 | - list 14 | - watch 15 | -------------------------------------------------------------------------------- /config/base/rbac/hostfirmwarecomponents_editor_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions for end users to edit hostfirmwarecomponents. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | labels: 6 | app.kubernetes.io/name: clusterrole 7 | app.kubernetes.io/instance: hostfirmwarecomponents-editor-role 8 | app.kubernetes.io/component: rbac 9 | app.kubernetes.io/created-by: baremetal-operator 10 | app.kubernetes.io/part-of: baremetal-operator 11 | app.kubernetes.io/managed-by: kustomize 12 | name: hostfirmwarecomponents-editor-role 13 | rules: 14 | - apiGroups: 15 | - metal3.io 16 | resources: 17 | - hostfirmwarecomponents 18 | verbs: 19 | - create 20 | - delete 21 | - get 22 | - list 23 | - patch 24 | - update 25 | - watch 26 | - apiGroups: 27 | - metal3.io 28 | resources: 29 | - hostfirmwarecomponents/status 30 | verbs: 31 | - get 32 | -------------------------------------------------------------------------------- /config/base/rbac/hostfirmwarecomponents_viewer_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions for end users to view hostfirmwarecomponents. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | labels: 6 | app.kubernetes.io/name: clusterrole 7 | app.kubernetes.io/instance: hostfirmwarecomponents-viewer-role 8 | app.kubernetes.io/component: rbac 9 | app.kubernetes.io/created-by: baremetal-operator 10 | app.kubernetes.io/part-of: baremetal-operator 11 | app.kubernetes.io/managed-by: kustomize 12 | name: hostfirmwarecomponents-viewer-role 13 | rules: 14 | - apiGroups: 15 | - metal3.io 16 | resources: 17 | - hostfirmwarecomponents 18 | verbs: 19 | - get 20 | - list 21 | - watch 22 | - apiGroups: 23 | - metal3.io 24 | resources: 25 | - hostfirmwarecomponents/status 26 | verbs: 27 | - get 28 | -------------------------------------------------------------------------------- /config/base/rbac/hostfirmwaresettings_editor_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions for end users to edit hostfirmwaresettings. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | name: hostfirmwaresettings-editor-role 6 | rules: 7 | - apiGroups: 8 | - metal3.io 9 | resources: 10 | - hostfirmwaresettings 11 | verbs: 12 | - create 13 | - delete 14 | - get 15 | - list 16 | - patch 17 | - update 18 | - watch 19 | - apiGroups: 20 | - metal3.io 21 | resources: 22 | - hostfirmwaresettings/status 23 | verbs: 24 | - get 25 | -------------------------------------------------------------------------------- /config/base/rbac/hostfirmwaresettings_viewer_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions for end users to view hostfirmwaresettings. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | name: hostfirmwaresettings-viewer-role 6 | rules: 7 | - apiGroups: 8 | - metal3.io 9 | resources: 10 | - hostfirmwaresettings 11 | verbs: 12 | - get 13 | - list 14 | - watch 15 | - apiGroups: 16 | - metal3.io 17 | resources: 18 | - hostfirmwaresettings/status 19 | verbs: 20 | - get 21 | -------------------------------------------------------------------------------- /config/base/rbac/kustomization.yaml: -------------------------------------------------------------------------------- 1 | resources: 2 | # All RBAC will be applied under this service account in 3 | # the deployment namespace. You may comment out this resource 4 | # if your manager will use a service account that exists at 5 | # runtime. Be sure to update RoleBinding and ClusterRoleBinding 6 | # subjects if changing service account names. 7 | - service_account.yaml 8 | - role.yaml 9 | - role_binding.yaml 10 | - leader_election_role.yaml 11 | - leader_election_role_binding.yaml 12 | # The following RBAC configurations are used to protect 13 | # the metrics endpoint with authn/authz. These configurations 14 | # ensure that only authorized users and service accounts 15 | # can access the metrics endpoint. Comment the following 16 | # permissions if you want to disable this protection. 17 | # More info: https://book.kubebuilder.io/reference/metrics.html 18 | - metrics_auth_role.yaml 19 | - metrics_auth_role_binding.yaml 20 | - metrics_reader_role.yaml 21 | - metrics_service.yaml 22 | -------------------------------------------------------------------------------- /config/base/rbac/leader_election_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions to do leader election. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: Role 4 | metadata: 5 | name: leader-election-role 6 | rules: 7 | - apiGroups: 8 | - "" 9 | resources: 10 | - configmaps 11 | verbs: 12 | - get 13 | - list 14 | - watch 15 | - create 16 | - update 17 | - patch 18 | - delete 19 | - apiGroups: 20 | - "" 21 | resources: 22 | - configmaps/status 23 | verbs: 24 | - get 25 | - update 26 | - patch 27 | - apiGroups: 28 | - "" 29 | resources: 30 | - events 31 | verbs: 32 | - create 33 | - apiGroups: 34 | - coordination.k8s.io 35 | resources: 36 | - leases 37 | verbs: 38 | - get 39 | - list 40 | - watch 41 | - create 42 | - update 43 | - patch 44 | - delete 45 | -------------------------------------------------------------------------------- /config/base/rbac/leader_election_role_binding.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: RoleBinding 3 | metadata: 4 | name: leader-election-rolebinding 5 | roleRef: 6 | apiGroup: rbac.authorization.k8s.io 7 | kind: Role 8 | name: leader-election-role 9 | subjects: 10 | - kind: ServiceAccount 11 | name: controller-manager 12 | namespace: system 13 | -------------------------------------------------------------------------------- /config/base/rbac/metrics_auth_role.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: ClusterRole 3 | metadata: 4 | name: metrics-auth-role 5 | rules: 6 | - apiGroups: 7 | - authentication.k8s.io 8 | resources: 9 | - tokenreviews 10 | verbs: 11 | - create 12 | - apiGroups: 13 | - authorization.k8s.io 14 | resources: 15 | - subjectaccessreviews 16 | verbs: 17 | - create 18 | -------------------------------------------------------------------------------- /config/base/rbac/metrics_auth_role_binding.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: ClusterRoleBinding 3 | metadata: 4 | name: metrics-auth-rolebinding 5 | roleRef: 6 | apiGroup: rbac.authorization.k8s.io 7 | kind: ClusterRole 8 | name: metrics-auth-role 9 | subjects: 10 | - kind: ServiceAccount 11 | name: controller-manager 12 | namespace: system 13 | -------------------------------------------------------------------------------- /config/base/rbac/metrics_reader_role.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: ClusterRole 3 | metadata: 4 | name: metrics-reader 5 | rules: 6 | - nonResourceURLs: 7 | - "/metrics" 8 | verbs: 9 | - get 10 | -------------------------------------------------------------------------------- /config/base/rbac/metrics_service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | labels: 5 | control-plane: controller-manager 6 | name: controller-manager-metrics-service 7 | namespace: system 8 | spec: 9 | ports: 10 | - name: https 11 | port: 8443 12 | targetPort: https 13 | selector: 14 | control-plane: controller-manager 15 | -------------------------------------------------------------------------------- /config/base/rbac/preprovisioningimage_editor_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions for end users to edit preprovisioningimages. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | name: preprovisioningimage-editor-role 6 | rules: 7 | - apiGroups: 8 | - metal3.io 9 | resources: 10 | - preprovisioningimages 11 | verbs: 12 | - create 13 | - delete 14 | - get 15 | - list 16 | - patch 17 | - update 18 | - watch 19 | - apiGroups: 20 | - metal3.io 21 | resources: 22 | - preprovisioningimages/status 23 | verbs: 24 | - get 25 | -------------------------------------------------------------------------------- /config/base/rbac/preprovisioningimage_viewer_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions for end users to view preprovisioningimages. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | name: preprovisioningimage-viewer-role 6 | rules: 7 | - apiGroups: 8 | - metal3.io 9 | resources: 10 | - preprovisioningimages 11 | verbs: 12 | - get 13 | - list 14 | - watch 15 | - apiGroups: 16 | - metal3.io 17 | resources: 18 | - preprovisioningimages/status 19 | verbs: 20 | - get 21 | -------------------------------------------------------------------------------- /config/base/rbac/role.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | name: manager-role 6 | rules: 7 | - apiGroups: 8 | - "" 9 | resources: 10 | - events 11 | verbs: 12 | - create 13 | - get 14 | - list 15 | - patch 16 | - update 17 | - watch 18 | - apiGroups: 19 | - "" 20 | resources: 21 | - secrets 22 | verbs: 23 | - delete 24 | - get 25 | - list 26 | - update 27 | - watch 28 | - apiGroups: 29 | - metal3.io 30 | resources: 31 | - baremetalhosts 32 | - bmceventsubscriptions 33 | - dataimages 34 | - firmwareschemas 35 | - hardwaredata 36 | - hostfirmwarecomponents 37 | - hostfirmwaresettings 38 | - preprovisioningimages 39 | verbs: 40 | - create 41 | - delete 42 | - get 43 | - list 44 | - patch 45 | - update 46 | - watch 47 | - apiGroups: 48 | - metal3.io 49 | resources: 50 | - baremetalhosts/finalizers 51 | - dataimages/finalizers 52 | - hardware/finalizers 53 | - hostfirmwarecomponents/finalizers 54 | verbs: 55 | - update 56 | - apiGroups: 57 | - metal3.io 58 | resources: 59 | - baremetalhosts/status 60 | - bmceventsubscriptions/status 61 | - dataimages/status 62 | - firmwareschemas/status 63 | - hostfirmwarecomponents/status 64 | - hostfirmwaresettings/status 65 | - preprovisioningimages/status 66 | verbs: 67 | - get 68 | - patch 69 | - update 70 | - apiGroups: 71 | - metal3.io 72 | resources: 73 | - hostupdatepolicies 74 | verbs: 75 | - get 76 | - list 77 | - update 78 | - watch 79 | -------------------------------------------------------------------------------- /config/base/rbac/role_binding.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: ClusterRoleBinding 3 | metadata: 4 | name: manager-rolebinding 5 | roleRef: 6 | apiGroup: rbac.authorization.k8s.io 7 | kind: ClusterRole 8 | name: manager-role 9 | subjects: 10 | - kind: ServiceAccount 11 | name: controller-manager 12 | namespace: system 13 | -------------------------------------------------------------------------------- /config/base/rbac/service_account.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ServiceAccount 3 | metadata: 4 | name: controller-manager 5 | namespace: system 6 | -------------------------------------------------------------------------------- /config/base/webhook/kustomization.yaml: -------------------------------------------------------------------------------- 1 | resources: 2 | - manifests.yaml 3 | - service_patch.yaml 4 | 5 | configurations: 6 | - kustomizeconfig.yaml -------------------------------------------------------------------------------- /config/base/webhook/kustomizeconfig.yaml: -------------------------------------------------------------------------------- 1 | # the following config is for teaching kustomize where to look at when substituting vars. 2 | # It requires kustomize v2.1.0 or newer to work properly. 3 | nameReference: 4 | - kind: Service 5 | version: v1 6 | fieldSpecs: 7 | # - kind: MutatingWebhookConfiguration 8 | # group: admissionregistration.k8s.io 9 | # path: webhooks/clientConfig/service/name 10 | - kind: ValidatingWebhookConfiguration 11 | group: admissionregistration.k8s.io 12 | path: webhooks/clientConfig/service/name 13 | 14 | namespace: 15 | #- kind: MutatingWebhookConfiguration 16 | # group: admissionregistration.k8s.io 17 | # path: webhooks/clientConfig/service/namespace 18 | # create: true 19 | - kind: ValidatingWebhookConfiguration 20 | group: admissionregistration.k8s.io 21 | path: webhooks/clientConfig/service/namespace 22 | create: true 23 | 24 | varReference: 25 | - path: metadata/annotations 26 | -------------------------------------------------------------------------------- /config/base/webhook/manifests.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: admissionregistration.k8s.io/v1 3 | kind: ValidatingWebhookConfiguration 4 | metadata: 5 | name: validating-webhook-configuration 6 | webhooks: 7 | - admissionReviewVersions: 8 | - v1 9 | - v1beta 10 | clientConfig: 11 | service: 12 | name: webhook-service 13 | namespace: system 14 | path: /validate-metal3-io-v1alpha1-baremetalhost 15 | failurePolicy: Fail 16 | name: baremetalhost.metal3.io 17 | rules: 18 | - apiGroups: 19 | - metal3.io 20 | apiVersions: 21 | - v1alpha1 22 | operations: 23 | - CREATE 24 | - UPDATE 25 | resources: 26 | - baremetalhosts 27 | sideEffects: None 28 | - admissionReviewVersions: 29 | - v1 30 | - v1beta 31 | clientConfig: 32 | service: 33 | name: webhook-service 34 | namespace: system 35 | path: /validate-metal3-io-v1alpha1-bmceventsubscription 36 | failurePolicy: Fail 37 | name: bmceventsubscription.metal3.io 38 | rules: 39 | - apiGroups: 40 | - metal3.io 41 | apiVersions: 42 | - v1alpha1 43 | operations: 44 | - CREATE 45 | - UPDATE 46 | resources: 47 | - bmceventsubscriptions 48 | sideEffects: None 49 | -------------------------------------------------------------------------------- /config/base/webhook/service_patch.yaml: -------------------------------------------------------------------------------- 1 | 2 | apiVersion: v1 3 | kind: Service 4 | metadata: 5 | name: webhook-service 6 | namespace: system 7 | spec: 8 | ports: 9 | - port: 443 10 | targetPort: 9443 11 | selector: 12 | control-plane: controller-manager 13 | webhook: metal3-io-v1alpha1-baremetalhost -------------------------------------------------------------------------------- /config/base/webhookcainjection_patch.yaml: -------------------------------------------------------------------------------- 1 | # This patch add annotation to admission webhook config and 2 | # the variables $(CERTIFICATE_NAMESPACE) and $(CERTIFICATE_NAME) will be substituted by kustomize. 3 | #apiVersion: admissionregistration.k8s.io/v1 4 | #kind: MutatingWebhookConfiguration 5 | #metadata: 6 | # name: mutating-webhook-configuration 7 | # annotations: 8 | # cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) 9 | #--- 10 | apiVersion: admissionregistration.k8s.io/v1 11 | kind: ValidatingWebhookConfiguration 12 | metadata: 13 | name: validating-webhook-configuration 14 | annotations: 15 | cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) 16 | -------------------------------------------------------------------------------- /config/components/basic-auth/credentials_patch.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: controller-manager 5 | namespace: system 6 | spec: 7 | template: 8 | spec: 9 | containers: 10 | - name: manager 11 | volumeMounts: 12 | - name: ironic-credentials 13 | mountPath: "/opt/metal3/auth/ironic" 14 | readOnly: true 15 | volumes: 16 | - name: ironic-credentials 17 | secret: 18 | secretName: ironic-credentials 19 | -------------------------------------------------------------------------------- /config/components/basic-auth/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1alpha1 2 | kind: Component 3 | 4 | # NOTE: This component requires a secret with the basic auth credentials! 5 | # How you create it is up to you. The required secrets is: 6 | # - ironic-credentials 7 | # 8 | # It should contain 2 fields: username and password. Example: 9 | # 10 | # apiVersion: v1 11 | # kind: Secret 12 | # metadata: 13 | # name: ironic-credentials 14 | # data: 15 | # password: 16 | # username: 17 | 18 | patches: 19 | - path: credentials_patch.yaml 20 | -------------------------------------------------------------------------------- /config/components/tls/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1alpha1 2 | kind: Component 3 | 4 | patches: 5 | - path: tls_ca_patch.yaml 6 | target: 7 | kind: Deployment 8 | name: controller-manager 9 | -------------------------------------------------------------------------------- /config/components/tls/tls_ca_patch.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: controller-manager 5 | spec: 6 | template: 7 | spec: 8 | containers: 9 | - name: manager 10 | volumeMounts: 11 | - name: cacert 12 | mountPath: "/opt/metal3/certs/ca" 13 | readOnly: true 14 | volumes: 15 | - name: cacert 16 | secret: 17 | secretName: ironic-cacert 18 | -------------------------------------------------------------------------------- /config/default/ironic.env: -------------------------------------------------------------------------------- 1 | HTTP_PORT=6180 2 | PROVISIONING_INTERFACE=eth2 3 | DHCP_RANGE=172.22.0.10,172.22.0.100 4 | DEPLOY_KERNEL_URL=http://172.22.0.2:6180/images/ironic-python-agent.kernel 5 | DEPLOY_RAMDISK_URL=http://172.22.0.2:6180/images/ironic-python-agent.initramfs 6 | IRONIC_ENDPOINT=http://172.22.0.2:6385/v1/ 7 | CACHEURL=http://172.22.0.1/images 8 | -------------------------------------------------------------------------------- /config/default/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | namespace: baremetal-operator-system 4 | resources: 5 | - ../base 6 | 7 | generatorOptions: 8 | disableNameSuffixHash: true 9 | 10 | configMapGenerator: 11 | - name: ironic 12 | behavior: create 13 | envs: 14 | - ironic.env 15 | -------------------------------------------------------------------------------- /config/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | 4 | resources: 5 | - namespace 6 | - default 7 | -------------------------------------------------------------------------------- /config/namespace/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | 4 | resources: 5 | - namespace.yaml -------------------------------------------------------------------------------- /config/namespace/namespace.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Namespace 3 | metadata: 4 | labels: 5 | control-plane: controller-manager 6 | name: baremetal-operator-system -------------------------------------------------------------------------------- /config/overlays/basic-auth_tls/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | 4 | resources: 5 | - ../../namespace 6 | - ../../base 7 | 8 | components: 9 | - ../../components/basic-auth 10 | - ../../components/tls 11 | # Example for how to generate the necessary secrets: 12 | # secretGenerator: 13 | # - name: ironic-credentials 14 | # files: 15 | # - username=ironic-username 16 | # - password=ironic-password 17 | -------------------------------------------------------------------------------- /config/overlays/e2e-release-0.10/ironic.env: -------------------------------------------------------------------------------- 1 | DEPLOY_KERNEL_URL=http://192.168.222.1:6180/images/ironic-python-agent.kernel 2 | DEPLOY_RAMDISK_URL=http://192.168.222.1:6180/images/ironic-python-agent.initramfs 3 | IRONIC_ENDPOINT=https://192.168.222.1:6385/v1/ 4 | -------------------------------------------------------------------------------- /config/overlays/e2e-release-0.10/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | namespace: baremetal-operator-system 4 | resources: 5 | - https://github.com/metal3-io/baremetal-operator/config/overlays/basic-auth_tls?ref=release-0.10&timeout=120s 6 | configMapGenerator: 7 | - name: ironic 8 | behavior: create 9 | envs: 10 | - ironic.env 11 | patches: 12 | - patch: | 13 | # Don't try to pull again the pre-loaded image 14 | - op: replace 15 | path: /spec/template/spec/containers/0/imagePullPolicy 16 | value: IfNotPresent 17 | target: 18 | kind: Deployment 19 | name: controller-manager 20 | images: 21 | - name: quay.io/metal3-io/baremetal-operator 22 | newTag: release-0.10 23 | # We cannot use suffix hashes since the kustomizations we build on 24 | # cannot be aware of what suffixes we add. 25 | generatorOptions: 26 | disableNameSuffixHash: true 27 | # NOTE: These credentials are generated automatically in hack/ci-e2e.sh 28 | secretGenerator: 29 | - name: ironic-credentials 30 | files: 31 | - username=ironic-username 32 | - password=ironic-password 33 | -------------------------------------------------------------------------------- /config/overlays/e2e-release-0.8/ironic.env: -------------------------------------------------------------------------------- 1 | DEPLOY_KERNEL_URL=http://192.168.222.1:6180/images/ironic-python-agent.kernel 2 | DEPLOY_RAMDISK_URL=http://192.168.222.1:6180/images/ironic-python-agent.initramfs 3 | IRONIC_ENDPOINT=https://192.168.222.1:6385/v1/ 4 | -------------------------------------------------------------------------------- /config/overlays/e2e-release-0.8/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | namespace: baremetal-operator-system 4 | resources: 5 | - https://github.com/metal3-io/baremetal-operator/config/overlays/basic-auth_tls?ref=release-0.8&timeout=120s 6 | configMapGenerator: 7 | - name: ironic 8 | behavior: create 9 | envs: 10 | - ironic.env 11 | patches: 12 | - patch: | 13 | # Don't try to pull again the pre-loaded image 14 | - op: replace 15 | path: /spec/template/spec/containers/0/imagePullPolicy 16 | value: IfNotPresent 17 | target: 18 | kind: Deployment 19 | name: controller-manager 20 | images: 21 | - name: quay.io/metal3-io/baremetal-operator 22 | newTag: release-0.8 23 | # We cannot use suffix hashes since the kustomizations we build on 24 | # cannot be aware of what suffixes we add. 25 | generatorOptions: 26 | disableNameSuffixHash: true 27 | # NOTE: These credentials are generated automatically in hack/ci-e2e.sh 28 | secretGenerator: 29 | - name: ironic-credentials 30 | files: 31 | - username=ironic-username 32 | - password=ironic-password 33 | -------------------------------------------------------------------------------- /config/overlays/e2e-release-0.9/ironic.env: -------------------------------------------------------------------------------- 1 | DEPLOY_KERNEL_URL=http://192.168.222.1:6180/images/ironic-python-agent.kernel 2 | DEPLOY_RAMDISK_URL=http://192.168.222.1:6180/images/ironic-python-agent.initramfs 3 | IRONIC_ENDPOINT=https://192.168.222.1:6385/v1/ 4 | -------------------------------------------------------------------------------- /config/overlays/e2e-release-0.9/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | namespace: baremetal-operator-system 4 | resources: 5 | - https://github.com/metal3-io/baremetal-operator/config/overlays/basic-auth_tls?ref=release-0.9&timeout=120s 6 | configMapGenerator: 7 | - name: ironic 8 | behavior: create 9 | envs: 10 | - ironic.env 11 | patches: 12 | - patch: | 13 | # Don't try to pull again the pre-loaded image 14 | - op: replace 15 | path: /spec/template/spec/containers/0/imagePullPolicy 16 | value: IfNotPresent 17 | target: 18 | kind: Deployment 19 | name: controller-manager 20 | images: 21 | - name: quay.io/metal3-io/baremetal-operator 22 | newTag: release-0.9 23 | # We cannot use suffix hashes since the kustomizations we build on 24 | # cannot be aware of what suffixes we add. 25 | generatorOptions: 26 | disableNameSuffixHash: true 27 | # NOTE: These credentials are generated automatically in hack/ci-e2e.sh 28 | secretGenerator: 29 | - name: ironic-credentials 30 | files: 31 | - username=ironic-username 32 | - password=ironic-password 33 | -------------------------------------------------------------------------------- /config/overlays/e2e/ironic.env: -------------------------------------------------------------------------------- 1 | DEPLOY_KERNEL_URL=http://192.168.222.1:6180/images/ironic-python-agent.kernel 2 | DEPLOY_RAMDISK_URL=http://192.168.222.1:6180/images/ironic-python-agent.initramfs 3 | IRONIC_ENDPOINT=https://192.168.222.1:6385/v1/ 4 | -------------------------------------------------------------------------------- /config/overlays/e2e/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | namespace: baremetal-operator-system 4 | resources: 5 | - ../basic-auth_tls 6 | 7 | configMapGenerator: 8 | - name: ironic 9 | behavior: create 10 | envs: 11 | - ironic.env 12 | 13 | patches: 14 | - patch: | 15 | # Don't try to pull again the pre-loaded image 16 | - op: replace 17 | path: /spec/template/spec/containers/0/imagePullPolicy 18 | value: IfNotPresent 19 | target: 20 | kind: Deployment 21 | name: controller-manager 22 | 23 | images: 24 | - name: quay.io/metal3-io/baremetal-operator 25 | newTag: e2e 26 | 27 | # We cannot use suffix hashes since the kustomizations we build on 28 | # cannot be aware of what suffixes we add. 29 | generatorOptions: 30 | disableNameSuffixHash: true 31 | 32 | # NOTE: These credentials are generated automatically in hack/ci-e2e.sh 33 | secretGenerator: 34 | - name: ironic-credentials 35 | files: 36 | - username=ironic-username 37 | - password=ironic-password 38 | -------------------------------------------------------------------------------- /config/overlays/fixture-release-0.10/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | resources: 4 | - https://github.com/metal3-io/baremetal-operator/config/namespace?ref=release-0.10 5 | - https://github.com/metal3-io/baremetal-operator/config/default?ref=release-0.10 6 | patches: 7 | - patch: | 8 | # Enable test mode (fixture provider instead of ironic) 9 | - op: add 10 | path: /spec/template/spec/containers/0/args/- 11 | value: --test-mode 12 | # Don't try to pull again the pre-loaded image 13 | - op: replace 14 | path: /spec/template/spec/containers/0/imagePullPolicy 15 | value: IfNotPresent 16 | target: 17 | kind: Deployment 18 | name: controller-manager 19 | images: 20 | - name: quay.io/metal3-io/baremetal-operator 21 | newTag: release-0.10 22 | -------------------------------------------------------------------------------- /config/overlays/fixture-release-0.8/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | resources: 4 | - https://github.com/metal3-io/baremetal-operator/config/namespace?ref=release-0.8 5 | - https://github.com/metal3-io/baremetal-operator/config/default?ref=release-0.8 6 | patches: 7 | - patch: | 8 | # Enable test mode (fixture provider instead of ironic) 9 | - op: add 10 | path: /spec/template/spec/containers/0/args/- 11 | value: --test-mode 12 | # Don't try to pull again the pre-loaded image 13 | - op: replace 14 | path: /spec/template/spec/containers/0/imagePullPolicy 15 | value: IfNotPresent 16 | target: 17 | kind: Deployment 18 | name: controller-manager 19 | images: 20 | - name: quay.io/metal3-io/baremetal-operator 21 | newTag: release-0.8 22 | -------------------------------------------------------------------------------- /config/overlays/fixture-release-0.9/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | resources: 4 | - https://github.com/metal3-io/baremetal-operator/config/namespace?ref=release-0.9 5 | - https://github.com/metal3-io/baremetal-operator/config/default?ref=release-0.9 6 | patches: 7 | - patch: | 8 | # Enable test mode (fixture provider instead of ironic) 9 | - op: add 10 | path: /spec/template/spec/containers/0/args/- 11 | value: --test-mode 12 | # Don't try to pull again the pre-loaded image 13 | - op: replace 14 | path: /spec/template/spec/containers/0/imagePullPolicy 15 | value: IfNotPresent 16 | target: 17 | kind: Deployment 18 | name: controller-manager 19 | images: 20 | - name: quay.io/metal3-io/baremetal-operator 21 | newTag: release-0.9 22 | -------------------------------------------------------------------------------- /config/overlays/fixture/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | 4 | resources: 5 | - ../../namespace 6 | - ../../default 7 | 8 | patches: 9 | - patch: | 10 | # Enable test mode (fixture provider instead of ironic) 11 | - op: add 12 | path: /spec/template/spec/containers/0/args/- 13 | value: --test-mode 14 | # Don't try to pull again the pre-loaded image 15 | - op: replace 16 | path: /spec/template/spec/containers/0/imagePullPolicy 17 | value: IfNotPresent 18 | target: 19 | kind: Deployment 20 | name: controller-manager 21 | 22 | images: 23 | - name: quay.io/metal3-io/baremetal-operator 24 | newTag: e2e 25 | -------------------------------------------------------------------------------- /config/samples/metal3.io_v1alpha1_baremetalhost.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: metal3.io/v1alpha1 2 | kind: BareMetalHost 3 | metadata: 4 | name: baremetalhost-sample 5 | spec: 6 | # Add fields here 7 | foo: bar 8 | -------------------------------------------------------------------------------- /config/samples/metal3.io_v1alpha1_dataimage.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: metal3.io/v1alpha1 2 | kind: DataImage 3 | metadata: 4 | name: dataimage-sample 5 | spec: 6 | url: "http://dataimage.example.com/non-bootable.iso" 7 | -------------------------------------------------------------------------------- /config/samples/metal3.io_v1alpha1_firmwareschema.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: metal3.io/v1alpha1 2 | kind: FirmwareSchema 3 | metadata: 4 | name: firmwareschema-sample 5 | spec: 6 | status: 7 | referenceCount: 2 8 | hardwareVendor: "VendorA" 9 | hardwareModel: "ModelT" 10 | schema: 11 | - "ProcVirtualization": 12 | attribute_type: "Enumeration" 13 | allowable_values: 14 | - "Enabled" 15 | - "Disabled" 16 | read_only: false 17 | - "ProcCPUCores": 18 | attribute_type: "Integer" 19 | lower_bound: 1 20 | read_only: true 21 | upper_bound: 100 22 | - "SRIOV": 23 | allowable_values: 24 | - "Enabled" 25 | - "Disabled" 26 | - "SystemModelName": 27 | attribute_type: "String" 28 | max_length: 40 29 | min_length: 0 30 | read_only: true 31 | - "SerialNumber": 32 | attribute_type: "String" 33 | max_length: 16 34 | min_length: 0 35 | -------------------------------------------------------------------------------- /config/samples/metal3.io_v1alpha1_hostfirmwarecomponents.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: metal3.io/v1alpha1 2 | kind: HostFirmwareComponents 3 | metadata: 4 | labels: 5 | app.kubernetes.io/name: hostfirmwarecomponents 6 | app.kubernetes.io/instance: hostfirmwarecomponents-sample 7 | app.kubernetes.io/part-of: baremetal-operator 8 | app.kubernetes.io/managed-by: kustomize 9 | app.kubernetes.io/created-by: baremetal-operator 10 | name: hostfirmwarecomponents-sample 11 | spec: 12 | updates: 13 | - name: bios 14 | url: https://myurl.with.firmware.for.bios 15 | - name: bmc 16 | url: https://myurl.with.firmware.for.bmc 17 | status: 18 | components: 19 | - component: bios 20 | initialVersion: "v1.0.0" 21 | currentVersion: "v1.5.0" 22 | lastVersionFlashed: "v1.5.0" 23 | updatedAt: "2023-10-13T13:50:06Z" 24 | - component: bmc 25 | initialVersion: "v1.0.5" 26 | currentVersion: "v1.2.0" 27 | lastVersionFlashed: "v1.2.0" 28 | updatedAt: "2023-10-13T13:50:06Z" 29 | updates: 30 | - name: bios 31 | url: https://myurl.with.firmware.for.bios 32 | - name: bmc 33 | url: https://myurl.with.firmware.for.bmc 34 | lastUpdated: "2023-10-13T13:50:06Z" 35 | -------------------------------------------------------------------------------- /config/samples/metal3.io_v1alpha1_hostfirmwaresettings.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: metal3.io/v1alpha1 2 | kind: HostFirmwareSettings 3 | metadata: 4 | name: hostfirmwaresettings-sample 5 | spec: 6 | settings: 7 | - "ProcVirtualization": "Enabled" 8 | - "SRIOV": "Enabled" 9 | - "NetworkBootRetryCount": "20" 10 | status: 11 | settings: 12 | - "ProcVirtualization": "Enabled" 13 | - "ProcCPUCores": "100" 14 | - "SRIOV": "Enabled" 15 | - "SystemModelName": "QYZ12345" 16 | - "SerialNumber": "22654891" 17 | - "NetworkBootRetryCount": "10" 18 | -------------------------------------------------------------------------------- /config/samples/metal3.io_v1alpha1_preprovisioningimage.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: metal3.io/v1alpha1 2 | kind: PreprovisioningImage 3 | metadata: 4 | name: preprovisioningimage-sample 5 | spec: 6 | # Add fields here 7 | foo: bar 8 | -------------------------------------------------------------------------------- /docs/BaremetalHost_ProvisioningState.dot: -------------------------------------------------------------------------------- 1 | digraph BaremetalHost { 2 | Created [shape=house] 3 | Created -> Unmanaged [label="BMC.* == \"\""] 4 | Created -> Registering [label="BMC.* != \"\""] 5 | 6 | Unmanaged [shape=doublecircle] 7 | Unmanaged -> Registering [label="BMC.* != \"\""] 8 | Unmanaged -> Deleting [label="!DeletionTimestamp.IsZero()"] 9 | 10 | ExternallyProvisioned [label="Externally\nProvisioned"] 11 | 12 | Registering -> Inspecting [label="!externallyProvisioned &&\nNeedsHardwareInspection()"] 13 | Registering -> Preparing [label="!externallyProvisioned &&\ninspectionDisabled()"] 14 | Registering -> ExternallyProvisioned [label="externallyProvisioned"] 15 | Registering -> Deleting [label="!DeletionTimestamp.IsZero()"] 16 | 17 | ExternallyProvisioned -> Inspecting [label="!externallyProvisioned &&\nNeedsHardwareInspection()"] 18 | ExternallyProvisioned -> Preparing [label="!externallyProvisioned &&\n!NeedsHardwareInspection()"] 19 | Available -> ExternallyProvisioned [label="externallyProvisioned"] 20 | 21 | Inspecting -> Preparing [label="done"] 22 | Inspecting -> PoweringOffBeforeDelete [label="!DeletionTimestamp.IsZero()"] 23 | 24 | Preparing -> Available [label="done"] 25 | Preparing -> PoweringOffBeforeDelete [label="!DeletionTimestamp.IsZero()"] 26 | 27 | Available [shape=doublecircle] 28 | Available -> Provisioning [label="NeedsProvisioning()"] 29 | Available -> Preparing [label="saveHostProvisioningSettings()"] 30 | Available -> Preparing [label="getHostFirmwareSettings()"] 31 | Available -> PoweringOffBeforeDelete [label="!DeletionTimestamp.IsZero()"] 32 | Available -> Inspecting [label="hasInspectAnnotation()"] 33 | 34 | Deleting7 [shape=point] 35 | 36 | Provisioning -> Provisioned [label=done] 37 | Provisioning -> Deprovisioning [label="failed ||\n!DeletionTimestamp.IsZero()"] 38 | 39 | Provisioned [shape=doublecircle] 40 | Provisioned -> Deprovisioning [label="provisioningCancelled()"] 41 | Provisioned -> Deprovisioning [label="!DeletionTimestamp.IsZero()"] 42 | 43 | ExternallyProvisioned [shape=doublecircle] 44 | ExternallyProvisioned -> PoweringOffBeforeDelete [label="!DeletionTimestamp.IsZero()"] 45 | 46 | Deprovisioning -> Provisioning [label="NeedsProvisioning()"] 47 | Deprovisioning -> Available [label="!NeedsProvisioning()"] 48 | 49 | Deprovisioning -> PoweringOffBeforeDelete 50 | PoweringOffBeforeDelete -> Deleting 51 | 52 | Deleting [shape=doublecircle] 53 | } 54 | -------------------------------------------------------------------------------- /docs/BaremetalHost_ProvisioningState.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/metal3-io/baremetal-operator/41bf9a50bbee6838c57d442b6f8dbccec8fcdde6/docs/BaremetalHost_ProvisioningState.png -------------------------------------------------------------------------------- /docs/baremetalhost-states.md: -------------------------------------------------------------------------------- 1 | # BaremetalHost Provisioning States 2 | 3 | The following diagram shows the possible Provisioning State transitions for the 4 | BaremetalHost object: 5 | 6 | ![BaremetalHost ProvisioningState transitions](BaremetalHost_ProvisioningState.png) 7 | 8 | ## Created 9 | 10 | Newly created hosts move immediately to Discovered or Registering. No 11 | host stays in the Created state while the operator is working 12 | properly. 13 | 14 | ## Unmanaged 15 | 16 | An Unmanaged host is missing both the BMC address and credentials 17 | secret name, and does not have any information to access the BMC 18 | for registration. 19 | 20 | ## Externally Provisioned 21 | 22 | An Externally Provisioned host was deployed using another tool and 23 | then a host object was created with the externallyProvisioned flag 24 | set. Hosts in this state are monitored, and only their power status is 25 | managed. 26 | 27 | ## Registering 28 | 29 | The host will stay in the Registering state while the BMC access 30 | details are being validated. 31 | 32 | ## Inspecting 33 | 34 | After the host is registered, an agent image will be booted on it 35 | using a ramdisk. The agent collects information about the available 36 | hardware components, and this process is called "inspection." The host 37 | will stay in the Inspecting state until this process is completed. 38 | 39 | ## Preparing 40 | 41 | When setting up RAID, BIOS and other similar configurations, 42 | the host will be in Preparing state. For ironic provisioner, 43 | we build and set up manual clean steps in Preparing state. 44 | 45 | ## Available 46 | 47 | A host in the Available state is available to be provisioned. 48 | In older versions of the baremetal-operator, this state was called 49 | Ready. 50 | 51 | ## Provisioning 52 | 53 | While an image is being copied to the host and it is being configured 54 | to run the image the host will be in the Provisioning state. 55 | 56 | ## Provisioned 57 | 58 | After an image is copied to the host and the host is running the 59 | image, it will be in the Provisioned state. 60 | 61 | ## Deprovisioning 62 | 63 | When the previously provisioned image is being removed from the host, 64 | it will be in the Deprovisioning state. 65 | 66 | ## Error 67 | 68 | If an error occurs during one of the processing states (Registering, 69 | Inspecting, Provisioning, Deprovisioning) the host will enter the 70 | Error state. 71 | 72 | ## Deleting 73 | 74 | When the host is marked to be deleted, it will move from its current 75 | state to Deleting, at which point the resource record is deleted from 76 | kubernetes. 77 | -------------------------------------------------------------------------------- /docs/inspectAnnotation.md: -------------------------------------------------------------------------------- 1 | # Inspect Annotation 2 | 3 | Baremetalhost's(BMH) _Status_ sub-resource contains a _hardware_ key 4 | which contains the result of introspection which is carried out during 5 | BMH registration. 6 | 7 | In some circumstances it may be desirable to disable this inspection process, 8 | and provide data from external source. The _Inspect Annotation_ provides some 9 | interfaces to enable this. 10 | 11 | Note the `inspect.metal3.io/hardwaredetails` annotation is consumed: 12 | 13 | * At any time when `inspect.metal3.io: disabled` is specified 14 | * When there is no existing HardwareDetails data in the Status 15 | 16 | The `inspect.metal3.io/hardwaredetails` annotation will be removed when 17 | successfully processed or when the status is already set, generating an 18 | event in each case. 19 | 20 | The structure of the annotation's value should match the hardware status 21 | field schema, or a subset of that schema, for example: 22 | 23 | ```yaml 24 | inspect.metal3.io: disabled 25 | inspect.metal3.io/hardwaredetails: '{"systemVendor":{"manufacturer":"QEMU", 26 | "productName":"Standard PC (Q35 + ICH9, 2009)","serialNumber":""}, 27 | "firmware":{"bios":{"date":"","vendor":"","version":""}},"ramMebibytes":4096, 28 | "nics":[{"name":"eth0","model":"0x1af4 0x0001","mac":"00:b7:8b:bb:3d:f6", 29 | "ip":"172.22.0.64","speedGbps":0,"vlanId":0,"pxe":true}], 30 | "storage":[{"name":"/dev/sda","rotational":true,"sizeBytes":53687091200, 31 | "vendor":"QEMU","model":"QEMU HARDDISK","serialNumber":"drive-scsi0-0-0-0", 32 | "hctl":"6:0:0:0"}],"cpu":{"arch":"x86_64", 33 | "model":"Intel Xeon E3-12xx v2 (IvyBridge)","clockMegahertz":2494.224, 34 | "flags":["foo"],"count":4},"hostname":"hwdAnnotation-0"}' 35 | ``` 36 | 37 | Apart from that, sometimes you might want to request re-inspection for an 38 | already inspected host. This might be necessary when there was a hardware 39 | change on the host and you want to ensure that BMH status contains the latest 40 | inspection data about your host. To request a new inspection, simply annotating 41 | the host with `inspect.metal3.io` is enough. Once inspection is requested, you should 42 | see the BMH in `inspecting` state until inspection is completed and by the end of 43 | inspection the `inspect.metal3.io` annotation will be removed by Baremetal Operator. 44 | 45 | Note that, inspection can be requested only when BMH is in `Ready` state (i.e. before 46 | it is provisioned). The reason for this limitation is because requesting an inspection 47 | for provisioned BMH will result in rebooting the host, which will result in application 48 | downtime running on that host. 49 | -------------------------------------------------------------------------------- /docs/ironic-authentication.md: -------------------------------------------------------------------------------- 1 | # Authenticating to Ironic 2 | 3 | Because hosts under the control of Metal³ need to contact the Ironic 4 | API during inspection and provisioning, it is highly advisable to 5 | require authentication on this API, since the provisioned hosts running user 6 | workloads will remain connected to the provisioning network. 7 | 8 | ## Configuration 9 | 10 | The `baremetal-operator` supports connecting to Ironic configured with the 11 | following `auth_strategy` modes: 12 | 13 | * `noauth` (no authentication) 14 | * `http_basic` (HTTP [Basic access authentication](https://en.wikipedia.org/wiki/Basic_access_authentication)) 15 | 16 | Note that Keystone authentication methods are not yet supported. 17 | 18 | Authentication configuration is read from the filesystem, beginning at the root 19 | directory specified in the environment variable `METAL3_AUTH_ROOT_DIR`. If this 20 | variable is empty or not specified, the default is `/opt/metal3/auth`. 21 | 22 | Within the root directory there is a separate subdirectory `ironic` for 23 | Ironic client configuration. 24 | 25 | ### `noauth` 26 | 27 | This is the default, and will be chosen if the auth root directory does not 28 | exist. In this mode, the baremetal-operator does not attempt to do any 29 | authentication against the Ironic APIs. 30 | 31 | ### `http_basic` 32 | 33 | This mode is configured by files in each authentication subdirectory named 34 | `username` and `password`, and containing the Basic auth username and password, 35 | respectively. 36 | -------------------------------------------------------------------------------- /docs/ironic-endpoint-keepalived-configuration.md: -------------------------------------------------------------------------------- 1 | # Maintain Ironic Endpoint with Keepalived 2 | 3 | The motivation behind maintaining Ironic Endpoint with Keepalived is to ensure 4 | that the Ironic Endpoint IP is also passed onto the target cluster control 5 | plane. This also guarantees that once pivoting is done and the management 6 | cluster is taken down, target cluster controlplane can re-claim the ironic 7 | endpoint IP through keepalived. The end goal is to make ironic endpoint 8 | reachable in the target cluster. 9 | 10 | ## Command to deploy Ironic with Keepalived container 11 | 12 | ```bash 13 | 14 | kustomize build $BMOPATH/ironic-deployment/keepalived | kubectl apply -f - 15 | 16 | ``` 17 | 18 | where $BMOPATH points to the baremetal-operator path. 19 | 20 | ## Ironic Keepalived Container 21 | 22 | Ironic Endpoint IP is maintained with Keepalived which now runs in a separate 23 | docker container in bmo deployment. The container is named 24 | `ironic-endpoint-keepalived`. The container files reside in 25 | `resources/keepalived-docker` path. 26 | 27 | ```bash 28 | 29 | tree resources/keepalived-docker/ 30 | 31 | ├── Dockerfile 32 | ├── manage-keepalived.sh 33 | ├── OWNERS 34 | └── sample.keepalived.conf 35 | 36 | ``` 37 | 38 | It is assumed that the docker image is uploaded in some registry and the image 39 | URL is used with `ironic-deployment/keepalived/keepalived_patch.yaml` to replace 40 | the default image URL with the correct URL through kustomization. 41 | 42 | **Important Note** 43 | When the baremetal-operator is deployed through metal3-dev-env, this container 44 | inherits the following environment variables through configmap: 45 | 46 | ```bash 47 | 48 | $PROVISIONING_IP 49 | $PROVISIONING_INTERFACE 50 | 51 | ``` 52 | 53 | In case you are deploying baremetal-operator locally, make sure to populate and 54 | export these environment variables before deploying. 55 | -------------------------------------------------------------------------------- /examples/example-host-bad-credentials.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: Secret 4 | metadata: 5 | name: example-baremetalhost-secret-no-username 6 | type: Opaque 7 | data: 8 | username: "" 9 | password: MWYyZDFlMmU2N2Rm 10 | 11 | --- 12 | apiVersion: v1 13 | kind: Secret 14 | metadata: 15 | name: example-baremetalhost-secret-no-password 16 | type: Opaque 17 | data: 18 | username: YWRtaW4= 19 | password: "" 20 | 21 | --- 22 | apiVersion: metal3.io/v1alpha1 23 | kind: BareMetalHost 24 | metadata: 25 | name: example-baremetalhost 26 | spec: 27 | online: true 28 | bmc: 29 | address: ipmi://192.168.122.1:6233 30 | credentialsName: example-baremetalhost-secret-no-password 31 | -------------------------------------------------------------------------------- /examples/example-host.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: Secret 4 | metadata: 5 | name: example-baremetalhost-secret 6 | type: Opaque 7 | data: 8 | username: YWRtaW4= 9 | password: MWYyZDFlMmU2N2Rm 10 | 11 | --- 12 | apiVersion: metal3.io/v1alpha1 13 | kind: BareMetalHost 14 | metadata: 15 | name: example-baremetalhost 16 | spec: 17 | online: true 18 | bmc: 19 | address: ipmi://192.168.122.1:6233 20 | credentialsName: example-baremetalhost-secret 21 | -------------------------------------------------------------------------------- /examples/worker-0.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: Secret 4 | metadata: 5 | name: worker-0-bmc-secret 6 | type: Opaque 7 | data: 8 | username: YWRtaW4= 9 | password: cGFzc3dvcmQ= 10 | 11 | --- 12 | apiVersion: metal3.io/v1alpha1 13 | kind: BareMetalHost 14 | metadata: 15 | name: worker-0 16 | spec: 17 | online: true 18 | bmc: 19 | address: libvirt://192.168.122.1:6233/ 20 | credentialsName: worker-0-bmc-secret 21 | bootMACAddress: "00:1a:74:74:e5:cb" 22 | image: 23 | url: "http://172.22.0.1/images/rhcos-ootpa-latest.qcow2" 24 | checksum: "97830b21ed272a3d854615beb54cf004" 25 | userData: 26 | name: worker-user-data 27 | namespace: openshift-machine-api 28 | -------------------------------------------------------------------------------- /hack/boilerplate.go.txt: -------------------------------------------------------------------------------- 1 | /* 2 | 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/clean-e2e.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | REPO_ROOT=$(dirname "${BASH_SOURCE[0]}")/.. 4 | cd "${REPO_ROOT}" || exit 1 5 | 6 | docker rm -f vbmc 7 | docker rm -f image-server-e2e 8 | docker rm -f sushy-tools 9 | 10 | "${REPO_ROOT}/tools/bmh_test/clean_local_bmh_test_setup.sh" "^bmo-e2e-" 11 | 12 | rm -rf "${REPO_ROOT}/test/e2e/_artifacts" 13 | rm -rf "${REPO_ROOT}"/artifacts-* 14 | rm -rf "${REPO_ROOT}/test/e2e/images" 15 | 16 | # Clear network 17 | virsh -c qemu:///system net-destroy baremetal-e2e 18 | virsh -c qemu:///system net-undefine baremetal-e2e 19 | 20 | # Clean volume pool directory 21 | rm -rf /tmp/pool_oo/* 22 | 23 | # Clean volume pool 24 | virsh pool-destroy default || true 25 | virsh pool-delete default || true 26 | virsh pool-undefine default || true 27 | -------------------------------------------------------------------------------- /hack/e2e/ensure_go.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -eux 4 | 5 | MINIMUM_GO_VERSION=go1.24.3 6 | 7 | # Ensure the go tool exists and is a viable version, or installs it 8 | verify_go_version() 9 | { 10 | # If go is not available on the path, get it 11 | if ! [ -x "$(command -v go)" ]; then 12 | if [[ "${OSTYPE}" == "linux-gnu" ]]; then 13 | echo 'go not found, installing' 14 | curl -sLo "/tmp/${MINIMUM_GO_VERSION}.linux-amd64.tar.gz" "https://go.dev/dl/${MINIMUM_GO_VERSION}.linux-amd64.tar.gz" 15 | sudo tar -C /usr/local -xzf "/tmp/${MINIMUM_GO_VERSION}.linux-amd64.tar.gz" 16 | export PATH=/usr/local/go/bin:$PATH 17 | else 18 | echo "Missing required binary in path: go" 19 | return 2 20 | fi 21 | fi 22 | 23 | local go_version 24 | IFS=" " read -ra go_version <<< "$(go version)" 25 | if [[ "${MINIMUM_GO_VERSION}" != $(echo -e "${MINIMUM_GO_VERSION}\n${go_version[2]}" | sort -s -t. -k 1,1 -k 2,2n -k 3,3n | head -n1) ]] && [[ "${go_version[2]}" != "devel" ]]; then 26 | cat << EOF 27 | Detected go version: ${go_version[2]}. 28 | Requires ${MINIMUM_GO_VERSION} or greater. 29 | Please install ${MINIMUM_GO_VERSION} or later. 30 | EOF 31 | return 2 32 | fi 33 | } 34 | 35 | verify_go_version 36 | -------------------------------------------------------------------------------- /hack/e2e/ensure_htpasswd.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -eux 4 | 5 | # Check if htpasswd is installed and install it if not 6 | verify_htpasswd() 7 | { 8 | if ! [ -x "$(command -v htpasswd)" ]; then 9 | echo "htpasswd could not be found, installing..." 10 | sudo apt-get update 11 | sudo apt-get install -y apache2-utils 12 | fi 13 | } 14 | 15 | verify_htpasswd 16 | -------------------------------------------------------------------------------- /hack/e2e/ensure_kubectl.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Copyright 2021 The Kubernetes Authors. 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 | set -eux 18 | 19 | USR_LOCAL_BIN="/usr/local/bin" 20 | MINIMUM_KUBECTL_VERSION=v1.28.1 21 | 22 | # Ensure the kubectl tool exists and is a viable version, or installs it 23 | verify_kubectl_version() 24 | { 25 | # If kubectl is not available on the path, get it 26 | if ! [ -x "$(command -v kubectl)" ]; then 27 | if [[ "${OSTYPE}" == "linux-gnu" ]]; then 28 | echo "kubectl not found, installing" 29 | curl -LO "https://dl.k8s.io/release/${MINIMUM_KUBECTL_VERSION}/bin/linux/amd64/kubectl" 30 | sudo install kubectl "${USR_LOCAL_BIN}/kubectl" 31 | else 32 | echo "Missing required binary in path: kubectl" 33 | return 2 34 | fi 35 | fi 36 | 37 | local kubectl_version 38 | IFS=" " read -ra kubectl_version <<< "$(kubectl version --client)" 39 | if [[ "${MINIMUM_KUBECTL_VERSION}" != $(echo -e "${MINIMUM_KUBECTL_VERSION}\n${kubectl_version[2]}" | sort -s -t. -k 1,1 -k 2,2n -k 3,3n | head -n1) ]]; then 40 | cat << EOF 41 | Detected kubectl version: ${kubectl_version[2]}. 42 | Requires ${MINIMUM_KUBECTL_VERSION} or greater. 43 | Please install ${MINIMUM_KUBECTL_VERSION} or later. 44 | EOF 45 | return 2 46 | fi 47 | } 48 | 49 | verify_kubectl_version 50 | -------------------------------------------------------------------------------- /hack/e2e/ensure_yq.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -eux 4 | 5 | USR_LOCAL_BIN="/usr/local/bin" 6 | YQ_VERSION="v4.40.5" 7 | 8 | # Check if yq tool is installed and install it if not 9 | verify_yq() 10 | { 11 | if ! [[ -x "$(command -v yq)" ]]; then 12 | if [[ "${OSTYPE}" == "linux-gnu" ]]; then 13 | echo "yq not found, installing" 14 | curl -LO "https://github.com/mikefarah/yq/releases/download/${YQ_VERSION}/yq_linux_amd64.tar.gz" 15 | tar xvf yq_linux_amd64.tar.gz 16 | sudo install yq_linux_amd64 "${USR_LOCAL_BIN}/yq" 17 | else 18 | echo "Missing required binary in path: yq" 19 | return 2 20 | fi 21 | fi 22 | } 23 | 24 | verify_yq 25 | -------------------------------------------------------------------------------- /hack/e2e/net.xml: -------------------------------------------------------------------------------- 1 | 2 | baremetal-e2e 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /hack/generate.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Ignore the rule that says we should always quote variables, because 4 | # in this script we *do* want globbing. 5 | # shellcheck disable=SC2086,SC2292 6 | 7 | set -eux 8 | 9 | IS_CONTAINER="${IS_CONTAINER:-false}" 10 | ARTIFACTS="${ARTIFACTS:-/tmp}" 11 | CONTAINER_RUNTIME="${CONTAINER_RUNTIME:-podman}" 12 | WORKDIR="${WORKDIR:-/workdir}" 13 | 14 | if [ "${IS_CONTAINER}" != "false" ]; then 15 | # we need to tell git its OK to use dir owned by someone else 16 | git config --global safe.directory "${WORKDIR}" 17 | export XDG_CACHE_HOME="/tmp/.cache" 18 | 19 | INPUT_FILES="$(git ls-files config) $(git ls-files | grep zz_generated)" 20 | cksum ${INPUT_FILES} > "${ARTIFACTS}/lint.cksums.before" 21 | export VERBOSE="--verbose" 22 | make generate manifests 23 | cksum ${INPUT_FILES} > "${ARTIFACTS}/lint.cksums.after" 24 | diff "${ARTIFACTS}/lint.cksums.before" "${ARTIFACTS}/lint.cksums.after" 25 | 26 | else 27 | "${CONTAINER_RUNTIME}" run --rm \ 28 | --pull=always \ 29 | --env IS_CONTAINER=TRUE \ 30 | --env DEPLOY_KERNEL_URL=http://172.22.0.1/images/ironic-python-agent.kernel \ 31 | --env DEPLOY_RAMDISK_URL=http://172.22.0.1/images/ironic-python-agent.initramfs \ 32 | --env IRONIC_ENDPOINT=http://localhost:6385/v1/ \ 33 | --volume "${PWD}:${WORKDIR}:rw,z" \ 34 | --entrypoint sh \ 35 | --workdir "${WORKDIR}" \ 36 | quay.io/metal3-io/basic-checks:golang-1.24 \ 37 | "${WORKDIR}"/hack/generate.sh "$@" 38 | fi 39 | -------------------------------------------------------------------------------- /hack/gomod.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # 1. Verify that `go mod tidy` can be executed successfully 4 | # 2. Verify that running the above doesn't change go.mod and go.sum 5 | # 6 | # NOTE: This won't work unless the build environment has internet access 7 | # shellcheck disable=SC2292 8 | 9 | set -eux 10 | 11 | IS_CONTAINER=${IS_CONTAINER:-false} 12 | CONTAINER_RUNTIME="${CONTAINER_RUNTIME:-podman}" 13 | WORKDIR="${WORKDIR:-/workdir}" 14 | 15 | if [ "${IS_CONTAINER}" != "false" ]; then 16 | export XDG_CACHE_HOME=/tmp/.cache 17 | 18 | mkdir /tmp/gomod 19 | cp -r . /tmp/gomod 20 | cd /tmp/gomod 21 | 22 | STATUS="$(git status --porcelain)" 23 | if [ -n "${STATUS}" ]; then 24 | echo "Dirty tree: refusing to continue out of caution" 25 | exit 1 26 | fi 27 | 28 | make mod 29 | 30 | STATUS="$(git status --porcelain)" 31 | if [ -n "${STATUS}" ]; then 32 | echo "one of the go.mod and/or go.sum files changed" 33 | echo "${STATUS}" 34 | echo "Please run 'go mod tidy' and commit the changes" 35 | exit 1 36 | fi 37 | 38 | else 39 | "${CONTAINER_RUNTIME}" run --rm \ 40 | --pull=always \ 41 | --env IS_CONTAINER=TRUE \ 42 | --volume "${PWD}:${WORKDIR}:ro,z" \ 43 | --entrypoint sh \ 44 | --workdir "${WORKDIR}" \ 45 | quay.io/metal3-io/basic-checks:golang-1.24 \ 46 | "${WORKDIR}"/hack/gomod.sh "$@" 47 | fi 48 | -------------------------------------------------------------------------------- /hack/ironic_ci.env: -------------------------------------------------------------------------------- 1 | HTTP_PORT=6180 2 | PROVISIONING_IP=172.22.0.2 3 | PROVISIONING_INTERFACE=ironicendpoint 4 | DHCP_RANGE=172.22.0.10,172.22.0.100 5 | DEPLOY_KERNEL_URL=http://172.22.0.2:6180/images/ironic-python-agent.kernel 6 | DEPLOY_RAMDISK_URL=http://172.22.0.2:6180/images/ironic-python-agent.initramfs 7 | IRONIC_ENDPOINT=http://172.22.0.2:6385/v1/ 8 | CACHEURL=http://172.22.0.1/images 9 | -------------------------------------------------------------------------------- /hack/manifestlint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # shellcheck disable=SC2292 3 | 4 | set -eux 5 | 6 | IS_CONTAINER="${IS_CONTAINER:-false}" 7 | CONTAINER_RUNTIME="${CONTAINER_RUNTIME:-podman}" 8 | WORKDIR="${WORKDIR:-/workdir}" 9 | K8S_VERSION="${K8S_VERSION:-master}" 10 | 11 | # --strict: Disallow additional properties not in schema. 12 | # --ignore-missing-schemas: Skip validation for resource 13 | # definitions without a schema. This will skip the checks 14 | # for the Custom Resource Definitions(CRDs). 15 | # --ignore-filename-pattern string: ignore pattern, can give multiple 16 | # We are skipping validation for the files that 17 | # matches our regexp pattern (i.e. kustom, patch). 18 | # --output string: The format of the output of this script. 19 | # --kubernetes-version string: which k8s version schema to test against 20 | 21 | # KUBECONFORM_PATH is needed as kubeconform binary in the official image 22 | # is at the root /kubeconform, but it is not at default path, while 23 | # in non-container run, it is on go bin path and can't have leading / 24 | 25 | if [ "${IS_CONTAINER}" != "false" ]; then 26 | { set +x; } 2>/dev/null 27 | echo "<-------------------------STARTING MANIFESTS VALIDATION CHECKS------------------------->" 28 | "${KUBECONFORM_PATH:-}"kubeconform --strict --ignore-missing-schemas \ 29 | --kubernetes-version "${K8S_VERSION}" \ 30 | --ignore-filename-pattern kustom --ignore-filename-pattern patch \ 31 | --ignore-filename-pattern controller_manager_config \ 32 | --output tap \ 33 | config/ examples/ 34 | echo "<-------------------------COMPLETED MANIFESTS VALIDATION CHECKS------------------------>" 35 | else 36 | "${CONTAINER_RUNTIME}" run --rm \ 37 | --env IS_CONTAINER=TRUE \ 38 | --env KUBECONFORM_PATH="/" \ 39 | --volume "${PWD}:${WORKDIR}:ro,z" \ 40 | --entrypoint sh \ 41 | --workdir "${WORKDIR}" \ 42 | ghcr.io/yannh/kubeconform:v0.6.7-alpine@sha256:824e0c248809e4b2da2a768b16b107cf17ada88a89ec6aa6050e566ba93ebbc6 \ 43 | "${WORKDIR}"/hack/manifestlint.sh "$@" 44 | fi 45 | -------------------------------------------------------------------------------- /hack/markdownlint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # markdownlint-cli2 has config file(s) named .markdownlint-cli2.yaml in the repo 3 | # shellcheck disable=SC2292 4 | 5 | set -eux 6 | 7 | IS_CONTAINER="${IS_CONTAINER:-false}" 8 | CONTAINER_RUNTIME="${CONTAINER_RUNTIME:-podman}" 9 | WORKDIR="${WORKDIR:-/workdir}" 10 | 11 | # all md files, but ignore .github 12 | if [ "${IS_CONTAINER}" != "false" ]; then 13 | markdownlint-cli2 "**/*.md" "#.github" 14 | else 15 | "${CONTAINER_RUNTIME}" run --rm \ 16 | --env IS_CONTAINER=TRUE \ 17 | --volume "${PWD}:${WORKDIR}:ro,z" \ 18 | --entrypoint sh \ 19 | --workdir "${WORKDIR}" \ 20 | docker.io/pipelinecomponents/markdownlint-cli2:0.12.0@sha256:a3977fba9814f10d33a1d69ae607dc808e7a6470b2ba03e84c17193c0791aac0 \ 21 | "${WORKDIR}"/hack/markdownlint.sh "$@" 22 | fi 23 | -------------------------------------------------------------------------------- /hack/shellcheck.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # shellcheck disable=SC2292 3 | 4 | set -eux 5 | 6 | IS_CONTAINER="${IS_CONTAINER:-false}" 7 | CONTAINER_RUNTIME="${CONTAINER_RUNTIME:-podman}" 8 | WORKDIR="${WORKDIR:-/workdir}" 9 | 10 | if [ "${IS_CONTAINER}" != "false" ]; then 11 | TOP_DIR="${1:-.}" 12 | find "${TOP_DIR}" -path ./vendor -prune -o -name '*.sh' -type f -exec shellcheck -s bash {} \+ 13 | else 14 | "${CONTAINER_RUNTIME}" run --rm \ 15 | --env IS_CONTAINER=TRUE \ 16 | --volume "${PWD}:${WORKDIR}:ro,z" \ 17 | --entrypoint sh \ 18 | --workdir "${WORKDIR}" \ 19 | docker.io/koalaman/shellcheck-alpine:v0.10.0@sha256:5921d946dac740cbeec2fb1c898747b6105e585130cc7f0602eec9a10f7ddb63 \ 20 | "${WORKDIR}"/hack/shellcheck.sh "$@" 21 | fi 22 | -------------------------------------------------------------------------------- /hack/tools/deploy-cli/.gitignore: -------------------------------------------------------------------------------- 1 | deploy-cli 2 | -------------------------------------------------------------------------------- /hack/tools/deploy-cli/templates/bmo-kustomize-bmopath.tpl: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | resources: 4 | - ../../namespace/ 5 | - ../../base/ 6 | namespace: baremetal-operator-system 7 | 8 | components: 9 | {{- if .DeployBasicAuth }} 10 | - ../../components/basic-auth/ 11 | {{ end }} 12 | {{- if .DeployTLS }} 13 | - ../../components/tls/ 14 | {{ end }} 15 | 16 | {{- if .DeployBasicAuth }} 17 | secretGenerator: 18 | - name: ironic-credentials 19 | files: 20 | - username=ironic-username 21 | - password=ironic-password 22 | type: Opaque 23 | {{ end }} 24 | configMapGenerator: 25 | - name: ironic 26 | behavior: create 27 | envs: 28 | - ironic.env 29 | -------------------------------------------------------------------------------- /hack/tools/deploy-cli/templates/bmo-kustomize.tpl: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | resources: 4 | - https://github.com/metal3-io/baremetal-operator/config/namespace/ 5 | - https://github.com/metal3-io/baremetal-operator/config/base/ 6 | namespace: baremetal-operator-system 7 | 8 | components: 9 | {{- if .DeployBasicAuth }} 10 | - https://github.com/metal3-io/baremetal-operator/config/components/basic-auth/ 11 | {{ end }} 12 | {{- if .DeployTLS }} 13 | - https://github.com/metal3-io/baremetal-operator/config/components/tls/ 14 | {{ end }} 15 | 16 | {{- if .DeployBasicAuth }} 17 | secretGenerator: 18 | - name: ironic-credentials 19 | files: 20 | - username=ironic-username 21 | - password=ironic-password 22 | type: Opaque 23 | {{ end }} 24 | configMapGenerator: 25 | - name: ironic 26 | behavior: create 27 | envs: 28 | - ironic.env 29 | 30 | -------------------------------------------------------------------------------- /hack/tools/deploy-cli/templates/ironic-kustomize-bmopath.tpl: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | resources: 4 | - ../../../config/namespace 5 | {{- if and .DeployBasicAuth .DeployTLS }} 6 | - ../../overlays/basic-auth_tls/ 7 | {{- else if .DeployBasicAuth }} 8 | - ../../base/ 9 | {{ end }} 10 | namePrefix: baremetal-operator- 11 | namespace: baremetal-operator-system 12 | components: 13 | {{- if and .DeployBasicAuth (not .DeployTLS) }} 14 | - ../../components/basic-auth/ 15 | {{ end }} 16 | {{- if .DeployKeepAlived }} 17 | - ../../components/keepalived/ 18 | {{ end }} 19 | {{- if .DeployMariadb }} 20 | - ../../components/mariadb/ 21 | {{ end }} 22 | 23 | {{- if .DeployBasicAuth }} 24 | secretGenerator: 25 | - files: 26 | - htpasswd=ironic-htpasswd 27 | name: ironic-htpasswd 28 | type: Opaque 29 | {{ end }} 30 | configMapGenerator: 31 | - behavior: create 32 | envs: 33 | - ironic_bmo_configmap.env 34 | name: ironic-bmo-configmap 35 | {{/* This configMap is solely so that we can replace the IP placeholders */}} 36 | - name: ironic-host-ip 37 | literals: 38 | - IRONIC_HOST_IP={{ .IronicHostIP }} 39 | - MARIADB_HOST_IP={{ .MariaDBHostIP }} 40 | 41 | replacements: 42 | {{/* Replace *_HOST_IP in certificates with the *_HOST_IP from the configmap */}} 43 | - source: 44 | kind: ConfigMap 45 | name: ironic-host-ip 46 | fieldPath: .data.IRONIC_HOST_IP 47 | targets: 48 | - select: 49 | version: v1 50 | group: cert-manager.io 51 | kind: Certificate 52 | name: ironic-cert 53 | fieldPaths: 54 | - .spec.ipAddresses.0 55 | - select: 56 | version: v1 57 | group: cert-manager.io 58 | kind: Certificate 59 | name: ironic-cacert 60 | fieldPaths: 61 | - .spec.ipAddresses.0 62 | {{- if .DeployMariadb }} 63 | - source: 64 | kind: ConfigMap 65 | name: ironic-host-ip 66 | fieldPath: .data.MARIADB_HOST_IP 67 | targets: 68 | - select: 69 | version: v1 70 | group: cert-manager.io 71 | kind: Certificate 72 | name: mariadb-cert 73 | fieldPaths: 74 | - .spec.ipAddresses.0 75 | {{ end }} 76 | -------------------------------------------------------------------------------- /hack/tools/deploy-cli/templates/ironic.env.tpl: -------------------------------------------------------------------------------- 1 | HTTP_PORT=6180 2 | PROVISIONING_INTERFACE=eth2 3 | DHCP_RANGE=172.22.0.10,172.22.0.100 4 | DEPLOY_KERNEL_URL=http://172.22.0.2:6180/images/ironic-python-agent.kernel 5 | DEPLOY_RAMDISK_URL=http://172.22.0.2:6180/images/ironic-python-agent.initramfs 6 | CACHEURL=http://172.22.0.1/images 7 | {{- if .DeployTLS }} 8 | IRONIC_ENDPOINT=https://172.22.0.2:6385/v1/ 9 | {{- else }} 10 | IRONIC_ENDPOINT=http://172.22.0.2:6385/v1/ 11 | {{ end }} 12 | -------------------------------------------------------------------------------- /hack/tools/deploy-cli/templates/ironic_bmo_configmap_env.tpl: -------------------------------------------------------------------------------- 1 | HTTP_PORT=6180 2 | {{- if .DeployKeepAlived }} 3 | PROVISIONING_IP=172.22.0.2 4 | PROVISIONING_INTERFACE=ironicendpoint 5 | {{- else }} 6 | PROVISIONING_INTERFACE=eth2 7 | IRONIC_INSPECTOR_VLAN_INTERFACES=all 8 | {{ end }} 9 | DHCP_RANGE=172.22.0.10,172.22.0.100 10 | DEPLOY_KERNEL_URL=http://172.22.0.2:6180/images/ironic-python-agent.kernel 11 | DEPLOY_RAMDISK_URL=http://172.22.0.2:6180/images/ironic-python-agent.initramfs 12 | CACHEURL=http://172.22.0.1/images 13 | IRONIC_KERNEL_PARAMS=console=ttyS0 14 | USE_IRONIC_INSPECTOR=false 15 | {{- if .DeployMariadb }} 16 | IRONIC_USE_MARIADB=true 17 | {{- else }} 18 | IRONIC_USE_MARIADB=false 19 | {{ end }} 20 | {{- if eq .RestartContainerCertificateUpdated "true" }} 21 | RESTART_CONTAINER_CERTIFICATE_UPDATED="true" 22 | {{- else }} 23 | RESTART_CONTAINER_CERTIFICATE_UPDATED="false" 24 | {{ end }} 25 | {{- if .DeployTLS }} 26 | IRONIC_ENDPOINT=https://172.22.0.2:6385/v1/ 27 | {{- else }} 28 | IRONIC_ENDPOINT=http://172.22.0.2:6385/v1/ 29 | {{ end }} 30 | -------------------------------------------------------------------------------- /hack/tools/tools.go: -------------------------------------------------------------------------------- 1 | //go:build tools 2 | // +build tools 3 | 4 | // Official workaround to track tool dependencies with go modules: 5 | // https://github.com/golang/go/wiki/Modules#how-can-i-track-tool-dependencies-for-a-module 6 | 7 | package tools 8 | 9 | import ( 10 | _ "sigs.k8s.io/controller-tools/cmd/controller-gen" 11 | _ "sigs.k8s.io/kustomize/kustomize/v5" 12 | ) 13 | -------------------------------------------------------------------------------- /internal/controller/metal3.io/action_result_test.go: -------------------------------------------------------------------------------- 1 | package controllers 2 | 3 | import ( 4 | "math" 5 | "testing" 6 | "time" 7 | 8 | "github.com/stretchr/testify/assert" 9 | ) 10 | 11 | func TestBackoffIncrements(t *testing.T) { 12 | var backOff time.Duration 13 | for i := 1; i <= maxBackOffCount; i++ { 14 | prev := backOff 15 | backOff = calculateBackoff(i) 16 | 17 | assert.GreaterOrEqual(t, backOff.Milliseconds(), prev.Milliseconds()) 18 | } 19 | } 20 | 21 | func TestMaxBackoffDuration(t *testing.T) { 22 | maxBackOffDuration := (time.Minute * time.Duration(math.Exp2(float64(maxBackOffCount)))).Milliseconds() 23 | 24 | assert.LessOrEqual(t, calculateBackoff(maxBackOffCount-1).Milliseconds(), maxBackOffDuration) 25 | assert.LessOrEqual(t, calculateBackoff(maxBackOffCount+1).Milliseconds(), maxBackOffDuration) 26 | assert.LessOrEqual(t, calculateBackoff(maxBackOffCount+100).Milliseconds(), maxBackOffDuration) 27 | } 28 | -------------------------------------------------------------------------------- /internal/controller/metal3.io/controller_test.go: -------------------------------------------------------------------------------- 1 | package controllers 2 | 3 | import ( 4 | metal3api "github.com/metal3-io/baremetal-operator/apis/metal3.io/v1alpha1" 5 | "k8s.io/client-go/kubernetes/scheme" 6 | logf "sigs.k8s.io/controller-runtime/pkg/log" 7 | logz "sigs.k8s.io/controller-runtime/pkg/log/zap" 8 | ) 9 | 10 | func init() { 11 | logf.SetLogger(logz.New(logz.UseDevMode(true))) 12 | // Register our package types with the global scheme 13 | err := metal3api.AddToScheme(scheme.Scheme) 14 | if err != nil { 15 | logf.Log.Error(err, "Cannot Add scheme into metal3api") 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /internal/controller/metal3.io/errors.go: -------------------------------------------------------------------------------- 1 | package controllers 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | // EmptyBMCAddressError is returned when the BMC address field 8 | // for a host is empty. 9 | type EmptyBMCAddressError struct { 10 | message string 11 | } 12 | 13 | func (e EmptyBMCAddressError) Error() string { 14 | return fmt.Sprintf("Empty BMC address %s", 15 | e.message) 16 | } 17 | 18 | // EmptyBMCSecretError is returned when the BMC secret 19 | // for a host is empty. 20 | type EmptyBMCSecretError struct { 21 | message string 22 | } 23 | 24 | func (e EmptyBMCSecretError) Error() string { 25 | return fmt.Sprintf("No BMC CredentialsName defined %s", 26 | e.message) 27 | } 28 | 29 | // ResolveBMCSecretRefError is returned when the BMC secret 30 | // for a host is defined but cannot be found. 31 | type ResolveBMCSecretRefError struct { 32 | message string 33 | } 34 | 35 | func (e ResolveBMCSecretRefError) Error() string { 36 | return fmt.Sprintf("BMC CredentialsName secret doesn't exist %s", 37 | e.message) 38 | } 39 | 40 | // NoDataInSecretError is returned when host configuration 41 | // data were not found in referenced secret. 42 | type NoDataInSecretError struct { 43 | secret string 44 | key string 45 | } 46 | 47 | func (e NoDataInSecretError) Error() string { 48 | return fmt.Sprintf("Secret %s does not contain key %s", e.secret, e.key) 49 | } 50 | -------------------------------------------------------------------------------- /internal/webhooks/metal3.io/v1alpha1/bmceventsubscription_validation.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2025 The Metal3 Authors. 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 | http://www.apache.org/licenses/LICENSE-2.0 8 | Unless required by applicable law or agreed to in writing, software 9 | distributed under the License is distributed on an "AS IS" BASIS, 10 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | See the License for the specific language governing permissions and 12 | limitations under the License. 13 | */ 14 | 15 | package webhooks 16 | 17 | import ( 18 | "errors" 19 | "fmt" 20 | "net/url" 21 | 22 | metal3api "github.com/metal3-io/baremetal-operator/apis/metal3.io/v1alpha1" 23 | ) 24 | 25 | // validateSubscription validates BMCEventSubscription resource for creation. 26 | func (webhook *BMCEventSubscription) validateSubscription(bmces *metal3api.BMCEventSubscription) []error { 27 | var errs []error 28 | 29 | if bmces.Spec.HostName == "" { 30 | errs = append(errs, errors.New("hostName cannot be empty")) 31 | } 32 | 33 | if bmces.Spec.HTTPHeadersRef != nil { 34 | if bmces.Spec.HTTPHeadersRef.Namespace != bmces.Namespace { 35 | errs = append(errs, errors.New("httpHeadersRef secret must be in the same namespace as the BMCEventSubscription")) 36 | } 37 | } 38 | 39 | if bmces.Spec.Destination == "" { 40 | errs = append(errs, errors.New("destination cannot be empty")) 41 | } else { 42 | destinationURL, err := url.ParseRequestURI(bmces.Spec.Destination) 43 | 44 | if err != nil { 45 | errs = append(errs, fmt.Errorf("destination is invalid: %w", err)) 46 | } else if destinationURL.Path == "" { 47 | errs = append(errs, errors.New("hostname-only destination must have a trailing slash")) 48 | } 49 | } 50 | return errs 51 | } 52 | -------------------------------------------------------------------------------- /internal/webhooks/metal3.io/v1alpha1/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 The Kubernetes Authors. 3 | Copyright 2025 The Metal3 Authors. 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 | 18 | // Package webhooks contains external webhook implementations for some of our API types. 19 | package webhooks 20 | -------------------------------------------------------------------------------- /ironic-deployment/README.md: -------------------------------------------------------------------------------- 1 | # Kustomizations for Ironic 2 | 3 | This folder contains kustomizations for Ironic. They are mainly used 4 | traditionally been used through the [deploy.sh](../tools/deploy.sh) script, 5 | which takes care of generating the necessary config for basic-auth and TLS. 6 | 7 | Experimentally, instead of `deploy.sh`, you can use the new golang-based 8 | [deploy-cli](../hack/tools/deploy-cli) library, 9 | which, at the moment, handles everything `deploy.sh` does. You can either: 10 | 11 | - Run the package with `go run`. From the root of BMO repository: 12 | 13 | ```shell 14 | cd hack/tools/deploy-cli 15 | go run *.go 16 | ``` 17 | 18 | - Otherwise, build the package to a static binary: 19 | 20 | ```shell 21 | make deploy-cli 22 | ``` 23 | 24 | And run the binary with: 25 | 26 | ```shell 27 | ./tools/bin/deploy-cli -h 28 | ``` 29 | 30 | To check which options are available, run the script/binary with `-h`. 31 | 32 | Here is a basic introduction of the kustomize structure: 33 | 34 | - **base** - This is the kustomize base that we start from. 35 | - **components** - In here you will find re-usable kustomize components 36 | for running Ironic with TLS, basic-auth, keepalived or mariadb. 37 | - **basic-auth** - Enable basic authentication. Note that the 38 | basic-auth component is missing the actual credentials. This is on 39 | purpose, to make sure that the user is setting the password. 40 | - **tls** - Enable TLS. The TLS component needs to have the proper 41 | IP/SAN set for the certificates. 42 | - **keepalived** - Add a keepalived container to the deployment. This 43 | is useful when using a VIP for exposing the Ironic endpoint, so 44 | that the IP can move with the pod. 45 | - **mariadb** - Use MariaDB instead of SQLite. TLS required for this 46 | to work. 47 | - **default** - A minimal, fully working, Ironic kustomization including 48 | configmap and password. Use only for development! The DB password is 49 | hard coded in the repo and there is no TLS or basic-auth. 50 | - **overlays** - Here you will find ready made overlays that use the 51 | above mentioned components. 52 | -------------------------------------------------------------------------------- /ironic-deployment/base/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | resources: 4 | - ironic.yaml -------------------------------------------------------------------------------- /ironic-deployment/components/basic-auth/auth.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: ironic 5 | spec: 6 | template: 7 | spec: 8 | containers: 9 | - name: ironic 10 | envFrom: 11 | - configMapRef: 12 | name: ironic-bmo-configmap 13 | volumeMounts: 14 | - name: ironic-htpasswd 15 | mountPath: "/auth/ironic" 16 | readOnly: true 17 | volumes: 18 | - name: ironic-htpasswd 19 | secret: 20 | secretName: ironic-htpasswd 21 | -------------------------------------------------------------------------------- /ironic-deployment/components/basic-auth/ironic-auth-config-tpl: -------------------------------------------------------------------------------- 1 | [ironic] 2 | auth_type=http_basic 3 | username=${IRONIC_USERNAME} 4 | password=${IRONIC_PASSWORD} -------------------------------------------------------------------------------- /ironic-deployment/components/basic-auth/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1alpha1 2 | kind: Component 3 | 4 | # NOTE: This component requires secrets with the basic auth credential! 5 | # How you create them is up to you. The required secret is ironic-htpasswd. 6 | # 7 | # The content should be as in these examples: 8 | # 9 | # apiVersion: v1 10 | # kind: Secret 11 | # metadata: 12 | # name: ironic-htpasswd 13 | # data: 14 | # IRONIC_HTPASSWD: 15 | 16 | patches: 17 | - path: auth.yaml 18 | -------------------------------------------------------------------------------- /ironic-deployment/components/ipxe-tls/certificate.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: cert-manager.io/v1 2 | kind: Issuer 3 | metadata: 4 | name: selfsigned-issuer 5 | spec: 6 | selfSigned: {} 7 | --- 8 | apiVersion: cert-manager.io/v1 9 | kind: Certificate 10 | metadata: 11 | name: ipxe-cacert 12 | spec: 13 | commonName: ipxe-ca 14 | isCA: true 15 | ipAddresses: 16 | - IRONIC_HOST_IP 17 | issuerRef: 18 | kind: Issuer 19 | name: selfsigned-issuer 20 | secretName: ipxe-cacert 21 | --- 22 | apiVersion: cert-manager.io/v1 23 | kind: Issuer 24 | metadata: 25 | name: ca-issuer 26 | spec: 27 | ca: 28 | secretName: ipxe-cacert 29 | --- 30 | apiVersion: cert-manager.io/v1 31 | kind: Certificate 32 | metadata: 33 | name: ipxe-cert 34 | spec: 35 | commonName: ipxe-cert 36 | ipAddresses: 37 | - IRONIC_HOST_IP 38 | issuerRef: 39 | kind: Issuer 40 | name: ca-issuer 41 | secretName: ipxe-cert 42 | -------------------------------------------------------------------------------- /ironic-deployment/components/ipxe-tls/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1alpha1 2 | kind: Component 3 | 4 | resources: 5 | - certificate.yaml 6 | 7 | patches: 8 | - path: tls.yaml 9 | 10 | configurations: 11 | - kustomizeconfig.yaml 12 | -------------------------------------------------------------------------------- /ironic-deployment/components/ipxe-tls/kustomizeconfig.yaml: -------------------------------------------------------------------------------- 1 | # This configuration is for teaching kustomize how to update name ref and var substitution 2 | nameReference: 3 | - kind: Issuer 4 | group: cert-manager.io 5 | fieldSpecs: 6 | - kind: Certificate 7 | group: cert-manager.io 8 | path: spec/issuerRef/name 9 | 10 | varReference: 11 | - kind: Certificate 12 | group: cert-manager.io 13 | path: spec/commonName 14 | - kind: Certificate 15 | group: cert-manager.io 16 | path: spec/dnsNames 17 | -------------------------------------------------------------------------------- /ironic-deployment/components/ipxe-tls/tls.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: ironic 5 | spec: 6 | template: 7 | spec: 8 | initContainers: 9 | - name: ipxe-builder 10 | image: quay.io/metal3-io/ipxe-builder 11 | command: 12 | - /bin/buildipxe.sh 13 | envFrom: 14 | - configMapRef: 15 | name: ipxe-configmap 16 | volumeMounts: 17 | - mountPath: /shared 18 | name: ironic-data-volume 19 | - name: cert-ipxe 20 | mountPath: "/certs/ipxe" 21 | readOnly: true 22 | - name: cert-ipxe-ca 23 | mountPath: "/certs/ca/ipxe" 24 | readOnly: true 25 | securityContext: 26 | allowPrivilegeEscalation: false 27 | capabilities: 28 | drop: 29 | - ALL 30 | privileged: false 31 | runAsUser: 997 # ironic 32 | runAsGroup: 994 # ironic 33 | volumes: 34 | - name: cert-ipxe-ca 35 | secret: 36 | secretName: ipxe-cacert 37 | - name: cert-ipxe 38 | secret: 39 | secretName: ipxe-cert 40 | 41 | -------------------------------------------------------------------------------- /ironic-deployment/components/keepalived/ironic_bmo_configmap.env: -------------------------------------------------------------------------------- 1 | HTTP_PORT=6180 2 | PROVISIONING_IP=172.22.0.2 3 | PROVISIONING_INTERFACE=ironicendpoint 4 | DHCP_RANGE=172.22.0.10,172.22.0.100 5 | DEPLOY_KERNEL_URL=http://172.22.0.2:6180/images/ironic-python-agent.kernel 6 | DEPLOY_RAMDISK_URL=http://172.22.0.2:6180/images/ironic-python-agent.initramfs 7 | IRONIC_ENDPOINT=http://172.22.0.2:6385/v1/ 8 | CACHEURL=http://172.22.0.1/images 9 | IRONIC_KERNEL_PARAMS=console=ttyS0 10 | USE_IRONIC_INSPECTOR=false 11 | -------------------------------------------------------------------------------- /ironic-deployment/components/keepalived/keepalived_patch.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: ironic 5 | spec: 6 | template: 7 | spec: 8 | containers: 9 | # Change the value of image field below to your controller image URL 10 | - image: quay.io/metal3-io/keepalived 11 | name: ironic-endpoint-keepalived 12 | securityContext: 13 | # Must be true so dnsmasq may get the capabilities via file caps 14 | # KEP: https://github.com/kubernetes/enhancements/blob/master/keps/sig-security/2763-ambient-capabilities/README.md 15 | allowPrivilegeEscalation: true 16 | capabilities: 17 | drop: 18 | - ALL 19 | add: 20 | - NET_ADMIN 21 | - NET_BROADCAST 22 | - NET_RAW 23 | privileged: false 24 | runAsUser: 65532 25 | runAsGroup: 65532 26 | envFrom: 27 | - configMapRef: 28 | name: ironic-bmo-configmap 29 | -------------------------------------------------------------------------------- /ironic-deployment/components/keepalived/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1alpha1 2 | kind: Component 3 | 4 | patches: 5 | - path: keepalived_patch.yaml 6 | 7 | configMapGenerator: 8 | - envs: 9 | - ironic_bmo_configmap.env 10 | behavior: merge 11 | name: ironic-bmo-configmap 12 | -------------------------------------------------------------------------------- /ironic-deployment/components/mariadb/certificate.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: cert-manager.io/v1 3 | kind: Certificate 4 | metadata: 5 | name: mariadb-cert 6 | spec: 7 | commonName: mariadb-cert 8 | ipAddresses: 9 | - MARIADB_HOST_IP 10 | issuerRef: 11 | kind: Issuer 12 | name: ca-issuer 13 | secretName: mariadb-cert 14 | -------------------------------------------------------------------------------- /ironic-deployment/components/mariadb/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1alpha1 2 | kind: Component 3 | 4 | resources: 5 | - certificate.yaml 6 | 7 | patches: 8 | - path: mariadb_patch.yaml 9 | 10 | secretGenerator: 11 | - literals: 12 | - password=changeme 13 | name: mariadb-password 14 | type: Opaque 15 | -------------------------------------------------------------------------------- /ironic-deployment/components/mariadb/mariadb_patch.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: ironic 5 | spec: 6 | template: 7 | spec: 8 | containers: 9 | - name: mariadb 10 | image: quay.io/metal3-io/mariadb 11 | imagePullPolicy: Always 12 | securityContext: 13 | allowPrivilegeEscalation: false 14 | capabilities: 15 | drop: 16 | - ALL 17 | privileged: false 18 | runAsUser: 27 19 | runAsGroup: 27 20 | livenessProbe: 21 | exec: 22 | command: ["sh", "-c", "mysqladmin status -uironic -p$(printenv MARIADB_PASSWORD)"] 23 | initialDelaySeconds: 30 24 | periodSeconds: 30 25 | timeoutSeconds: 10 26 | successThreshold: 1 27 | failureThreshold: 10 28 | readinessProbe: 29 | exec: 30 | command: ["sh", "-c", "mysqladmin status -uironic -p$(printenv MARIADB_PASSWORD)"] 31 | initialDelaySeconds: 30 32 | periodSeconds: 30 33 | timeoutSeconds: 10 34 | successThreshold: 1 35 | failureThreshold: 10 36 | volumeMounts: 37 | - mountPath: /shared 38 | name: ironic-data-volume 39 | - name: cert-mariadb 40 | mountPath: "/certs/mariadb" 41 | readOnly: true 42 | - name: cert-mariadb-ca 43 | mountPath: "/certs/ca/mariadb" 44 | readOnly: true 45 | env: 46 | - name: MARIADB_PASSWORD 47 | valueFrom: 48 | secretKeyRef: 49 | name: mariadb-password 50 | key: password 51 | - name: RESTART_CONTAINER_CERTIFICATE_UPDATED 52 | valueFrom: 53 | configMapKeyRef: 54 | name: ironic-bmo-configmap 55 | key: RESTART_CONTAINER_CERTIFICATE_UPDATED 56 | - name: ironic 57 | env: 58 | - name: MARIADB_PASSWORD 59 | valueFrom: 60 | secretKeyRef: 61 | name: mariadb-password 62 | key: password 63 | - name: IRONIC_USE_MARIADB 64 | value: "true" 65 | volumeMounts: 66 | - name: cert-mariadb-ca 67 | mountPath: "/certs/ca/mariadb" 68 | readOnly: true 69 | volumes: 70 | - name: cert-mariadb 71 | secret: 72 | secretName: mariadb-cert 73 | - name: cert-mariadb-ca 74 | secret: 75 | secretName: ironic-cacert 76 | -------------------------------------------------------------------------------- /ironic-deployment/components/tls/certificate.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: cert-manager.io/v1 2 | kind: Issuer 3 | metadata: 4 | name: selfsigned-issuer 5 | spec: 6 | selfSigned: {} 7 | --- 8 | apiVersion: cert-manager.io/v1 9 | kind: Certificate 10 | metadata: 11 | name: ironic-cacert 12 | spec: 13 | commonName: ironic-ca 14 | isCA: true 15 | issuerRef: 16 | kind: Issuer 17 | name: selfsigned-issuer 18 | secretName: ironic-cacert 19 | --- 20 | apiVersion: cert-manager.io/v1 21 | kind: Issuer 22 | metadata: 23 | name: ca-issuer 24 | spec: 25 | ca: 26 | secretName: ironic-cacert 27 | --- 28 | apiVersion: cert-manager.io/v1 29 | kind: Certificate 30 | metadata: 31 | name: ironic-cert 32 | spec: 33 | commonName: ironic-cert 34 | ipAddresses: 35 | - IRONIC_HOST_IP 36 | issuerRef: 37 | kind: Issuer 38 | name: ca-issuer 39 | secretName: ironic-cert 40 | -------------------------------------------------------------------------------- /ironic-deployment/components/tls/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1alpha1 2 | kind: Component 3 | 4 | resources: 5 | - certificate.yaml 6 | 7 | patches: 8 | - path: tls.yaml 9 | 10 | configurations: 11 | - kustomizeconfig.yaml 12 | -------------------------------------------------------------------------------- /ironic-deployment/components/tls/kustomizeconfig.yaml: -------------------------------------------------------------------------------- 1 | # This configuration is for teaching kustomize how to update name ref and var substitution 2 | nameReference: 3 | - kind: Issuer 4 | group: cert-manager.io 5 | fieldSpecs: 6 | - kind: Certificate 7 | group: cert-manager.io 8 | path: spec/issuerRef/name 9 | 10 | varReference: 11 | - kind: Certificate 12 | group: cert-manager.io 13 | path: spec/commonName 14 | - kind: Certificate 15 | group: cert-manager.io 16 | path: spec/dnsNames 17 | -------------------------------------------------------------------------------- /ironic-deployment/components/tls/tls.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: ironic 5 | spec: 6 | template: 7 | spec: 8 | containers: 9 | - name: ironic 10 | env: 11 | - name: IRONIC_REVERSE_PROXY_SETUP 12 | value: "true" 13 | volumeMounts: 14 | - name: cert-ironic-ca 15 | mountPath: "/certs/ca/ironic" 16 | readOnly: true 17 | - name: ironic-httpd 18 | livenessProbe: 19 | exec: 20 | command: ["sh", "-c", "curl -sSfk https://127.0.0.1:6385"] 21 | readinessProbe: 22 | exec: 23 | command: ["sh", "-c", "curl -sSfk https://127.0.0.1:6385"] 24 | env: 25 | - name: IRONIC_REVERSE_PROXY_SETUP 26 | value: "true" 27 | volumeMounts: 28 | - name: cert-ironic 29 | mountPath: "/certs/ironic" 30 | readOnly: true 31 | - name: cert-ironic-ca 32 | mountPath: "/certs/ca/ironic" 33 | readOnly: true 34 | volumes: 35 | - name: cert-ironic-ca 36 | secret: 37 | secretName: ironic-cacert 38 | - name: cert-ironic 39 | secret: 40 | secretName: ironic-cert 41 | -------------------------------------------------------------------------------- /ironic-deployment/default/ironic_bmo_configmap.env: -------------------------------------------------------------------------------- 1 | HTTP_PORT=6180 2 | PROVISIONING_INTERFACE=eth2 3 | DHCP_RANGE=172.22.0.10,172.22.0.100 4 | DEPLOY_KERNEL_URL=http://172.22.0.2:6180/images/ironic-python-agent.kernel 5 | DEPLOY_RAMDISK_URL=http://172.22.0.2:6180/images/ironic-python-agent.initramfs 6 | IRONIC_ENDPOINT=http://172.22.0.2:6385/v1/ 7 | CACHEURL=http://172.22.0.1/images 8 | IRONIC_KERNEL_PARAMS=console=ttyS0 9 | IRONIC_INSPECTOR_VLAN_INTERFACES=all 10 | USE_IRONIC_INSPECTOR=false 11 | -------------------------------------------------------------------------------- /ironic-deployment/default/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | namespace: baremetal-operator-system 4 | namePrefix: baremetal-operator- 5 | resources: 6 | - ../base 7 | configMapGenerator: 8 | - envs: 9 | - ironic_bmo_configmap.env 10 | name: ironic-bmo-configmap 11 | -------------------------------------------------------------------------------- /ironic-deployment/overlays/basic-auth_tls/basic-auth_tls.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: ironic 5 | spec: 6 | template: 7 | spec: 8 | containers: 9 | - name: ironic-httpd 10 | envFrom: 11 | - configMapRef: 12 | name: ironic-bmo-configmap 13 | volumeMounts: 14 | - name: ironic-htpasswd 15 | mountPath: "/auth/ironic" 16 | readOnly: true 17 | volumes: 18 | - name: ironic-htpasswd 19 | secret: 20 | secretName: ironic-htpasswd 21 | -------------------------------------------------------------------------------- /ironic-deployment/overlays/basic-auth_tls/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | # namespace: baremetal-operator-system 4 | # namePrefix: baremetal-operator- 5 | resources: 6 | - ../../base 7 | 8 | components: 9 | - ../../components/basic-auth 10 | - ../../components/tls 11 | 12 | # Example of how to generate config map 13 | # configMapGenerator: 14 | # - envs: 15 | # - ironic_bmo.env 16 | # name: ironic-bmo-configmap 17 | # behavior: create 18 | 19 | # When using TLS, the ironic-httpd container is acting as a reverse-proxy. 20 | # This means that we need to add the basic-auth related environment 21 | # variables on ironic-httpd with this patch. 22 | patches: 23 | - path: basic-auth_tls.yaml 24 | # Example for how to generate the necessary secrets: 25 | # secretGenerator: 26 | # - behavior: create 27 | # files: 28 | # - htpasswd=ironic-htpasswd 29 | # name: ironic-htpasswd 30 | # type: Opaque 31 | -------------------------------------------------------------------------------- /ironic-deployment/overlays/basic-auth_tls_keepalived/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | # namespace: baremetal-operator-system 4 | # namePrefix: baremetal-operator- 5 | 6 | resources: 7 | - ../basic-auth_tls 8 | 9 | components: 10 | - ../../components/keepalived 11 | -------------------------------------------------------------------------------- /ironic-deployment/overlays/basic-auth_tls_keepalived_mariadb/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | # namespace: baremetal-operator-system 4 | # namePrefix: baremetal-operator- 5 | 6 | resources: 7 | - ../basic-auth_tls_keepalived 8 | 9 | components: 10 | - ../../components/mariadb 11 | -------------------------------------------------------------------------------- /ironic-deployment/overlays/e2e-local-ironic/ironic-patch.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: ironic 5 | spec: 6 | template: 7 | spec: 8 | containers: 9 | - name: ironic-dnsmasq 10 | $patch: delete 11 | -------------------------------------------------------------------------------- /ironic-deployment/overlays/e2e-local-ironic/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | namespace: baremetal-operator-system 4 | resources: 5 | - ../e2e 6 | 7 | patches: 8 | - path: ironic-patch.yaml 9 | - patch: | 10 | # Don't try to pull again the pre-loaded image 11 | - op: replace 12 | path: /spec/template/spec/containers/0/imagePullPolicy 13 | value: IfNotPresent 14 | - op: replace 15 | path: /spec/template/spec/containers/1/imagePullPolicy 16 | value: IfNotPresent 17 | - op: replace 18 | path: /spec/template/spec/containers/2/imagePullPolicy 19 | value: IfNotPresent 20 | target: 21 | kind: Deployment 22 | name: ironic 23 | 24 | images: 25 | - name: quay.io/metal3-io/ironic 26 | newTag: local 27 | 28 | -------------------------------------------------------------------------------- /ironic-deployment/overlays/e2e-release-26.0/ironic-patch.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: ironic 5 | spec: 6 | template: 7 | spec: 8 | containers: 9 | - name: ironic-dnsmasq 10 | $patch: delete 11 | -------------------------------------------------------------------------------- /ironic-deployment/overlays/e2e-release-26.0/ironic_bmo_configmap.env: -------------------------------------------------------------------------------- 1 | HTTP_PORT=6180 2 | PROVISIONING_INTERFACE=eth0 3 | CACHEURL=http://192.168.222.1/images 4 | IRONIC_HTTP_URL=http://192.168.222.1:6180 5 | IRONIC_BASE_URL=https://192.168.222.1:6385 6 | IRONIC_EXTERNAL_CALLBACK_URL=https://192.168.222.1:6385 7 | IRONIC_KERNEL_PARAMS=console=ttyS0 8 | IRONIC_INSPECTOR_VLAN_INTERFACES=all 9 | USE_IRONIC_INSPECTOR=false 10 | -------------------------------------------------------------------------------- /ironic-deployment/overlays/e2e-release-26.0/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | namespace: baremetal-operator-system 4 | resources: 5 | - https://github.com/metal3-io/baremetal-operator/config/namespace?ref=release-0.8&timeout=120s 6 | - https://github.com/metal3-io/baremetal-operator/ironic-deployment/base?ref=release-0.8&timeout=120s 7 | 8 | components: 9 | - https://github.com/metal3-io/baremetal-operator/ironic-deployment/components/basic-auth?ref=release-0.8&timeout=120s 10 | - https://github.com/metal3-io/baremetal-operator/ironic-deployment/components/tls?ref=release-0.8&timeout=120s 11 | 12 | configMapGenerator: 13 | - envs: 14 | - ironic_bmo_configmap.env 15 | name: ironic-bmo-configmap 16 | behavior: create 17 | 18 | patches: 19 | - path: ironic-patch.yaml 20 | # The TLS component adds certificates but it cannot know the exact IPs of our environment. 21 | # Here we patch the certificates to have the correct IPs. 22 | # - 192.168.222.1: management computer IP, forwarded to ironic inside kind 23 | - patch: |- 24 | - op: replace 25 | path: /spec/ipAddresses/0 26 | value: 192.168.222.1 27 | target: 28 | kind: Certificate 29 | name: ironic-cert 30 | 31 | images: 32 | - name: quay.io/metal3-io/ironic 33 | newTag: release-26.0 34 | 35 | # NOTE: These credentials are generated automatically in hack/ci-e2e.sh 36 | secretGenerator: 37 | - name: ironic-htpasswd 38 | behavior: create 39 | envs: 40 | - ironic-htpasswd 41 | -------------------------------------------------------------------------------- /ironic-deployment/overlays/e2e-release-27.0/ironic-patch.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: ironic 5 | spec: 6 | template: 7 | spec: 8 | containers: 9 | - name: ironic-dnsmasq 10 | $patch: delete 11 | -------------------------------------------------------------------------------- /ironic-deployment/overlays/e2e-release-27.0/ironic_bmo_configmap.env: -------------------------------------------------------------------------------- 1 | HTTP_PORT=6180 2 | PROVISIONING_INTERFACE=eth0 3 | CACHEURL=http://192.168.222.1/images 4 | IRONIC_HTTP_URL=http://192.168.222.1:6180 5 | IRONIC_BASE_URL=https://192.168.222.1:6385 6 | IRONIC_EXTERNAL_CALLBACK_URL=https://192.168.222.1:6385 7 | IRONIC_KERNEL_PARAMS=console=ttyS0 8 | IRONIC_INSPECTOR_VLAN_INTERFACES=all 9 | USE_IRONIC_INSPECTOR=false 10 | -------------------------------------------------------------------------------- /ironic-deployment/overlays/e2e-release-27.0/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | namespace: baremetal-operator-system 4 | resources: 5 | - https://github.com/metal3-io/baremetal-operator/config/namespace?ref=release-0.9&timeout=120s 6 | - https://github.com/metal3-io/baremetal-operator/ironic-deployment/base?ref=release-0.9&timeout=120s 7 | 8 | components: 9 | - https://github.com/metal3-io/baremetal-operator/ironic-deployment/components/basic-auth?ref=release-0.9&timeout=120s 10 | - https://github.com/metal3-io/baremetal-operator/ironic-deployment/components/tls?ref=release-0.9&timeout=120s 11 | 12 | configMapGenerator: 13 | - envs: 14 | - ironic_bmo_configmap.env 15 | name: ironic-bmo-configmap 16 | behavior: create 17 | 18 | patches: 19 | - path: ironic-patch.yaml 20 | # The TLS component adds certificates but it cannot know the exact IPs of our environment. 21 | # Here we patch the certificates to have the correct IPs. 22 | # - 192.168.222.1: management computer IP, forwarded to ironic inside kind 23 | - patch: |- 24 | - op: replace 25 | path: /spec/ipAddresses/0 26 | value: 192.168.222.1 27 | target: 28 | kind: Certificate 29 | name: ironic-cert 30 | 31 | images: 32 | - name: quay.io/metal3-io/ironic 33 | newTag: release-27.0 34 | 35 | # NOTE: These credentials are generated automatically in hack/ci-e2e.sh 36 | secretGenerator: 37 | - name: ironic-htpasswd 38 | behavior: create 39 | envs: 40 | - ironic-htpasswd 41 | -------------------------------------------------------------------------------- /ironic-deployment/overlays/e2e-release-28.0/ironic-patch.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: ironic 5 | spec: 6 | template: 7 | spec: 8 | containers: 9 | - name: ironic-dnsmasq 10 | $patch: delete 11 | -------------------------------------------------------------------------------- /ironic-deployment/overlays/e2e-release-28.0/ironic_bmo_configmap.env: -------------------------------------------------------------------------------- 1 | HTTP_PORT=6180 2 | PROVISIONING_INTERFACE=eth0 3 | CACHEURL=http://192.168.222.1/images 4 | IRONIC_HTTP_URL=http://192.168.222.1:6180 5 | IRONIC_BASE_URL=https://192.168.222.1:6385 6 | IRONIC_EXTERNAL_CALLBACK_URL=https://192.168.222.1:6385 7 | IRONIC_KERNEL_PARAMS=console=ttyS0 8 | IRONIC_INSPECTOR_VLAN_INTERFACES=all 9 | USE_IRONIC_INSPECTOR=false 10 | -------------------------------------------------------------------------------- /ironic-deployment/overlays/e2e-release-28.0/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | namespace: baremetal-operator-system 4 | resources: 5 | - https://github.com/metal3-io/baremetal-operator/config/namespace?ref=release-0.10&timeout=120s 6 | - https://github.com/metal3-io/baremetal-operator/ironic-deployment/base?ref=release-0.10&timeout=120s 7 | 8 | components: 9 | - https://github.com/metal3-io/baremetal-operator/ironic-deployment/components/basic-auth?ref=release-0.10&timeout=120s 10 | - https://github.com/metal3-io/baremetal-operator/ironic-deployment/components/tls?ref=release-0.10&timeout=120s 11 | 12 | configMapGenerator: 13 | - envs: 14 | - ironic_bmo_configmap.env 15 | name: ironic-bmo-configmap 16 | behavior: create 17 | 18 | patches: 19 | - path: ironic-patch.yaml 20 | # The TLS component adds certificates but it cannot know the exact IPs of our environment. 21 | # Here we patch the certificates to have the correct IPs. 22 | # - 192.168.222.1: management computer IP, forwarded to ironic inside kind 23 | - patch: |- 24 | - op: replace 25 | path: /spec/ipAddresses/0 26 | value: 192.168.222.1 27 | target: 28 | kind: Certificate 29 | name: ironic-cert 30 | 31 | images: 32 | - name: quay.io/metal3-io/ironic 33 | newTag: release-28.0 34 | 35 | # NOTE: These credentials are generated automatically in hack/ci-e2e.sh 36 | secretGenerator: 37 | - name: ironic-htpasswd 38 | behavior: create 39 | envs: 40 | - ironic-htpasswd 41 | -------------------------------------------------------------------------------- /ironic-deployment/overlays/e2e-release-29.0/ironic-patch.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: ironic 5 | spec: 6 | template: 7 | spec: 8 | containers: 9 | - name: ironic-dnsmasq 10 | $patch: delete 11 | -------------------------------------------------------------------------------- /ironic-deployment/overlays/e2e-release-29.0/ironic_bmo_configmap.env: -------------------------------------------------------------------------------- 1 | HTTP_PORT=6180 2 | PROVISIONING_INTERFACE=eth0 3 | CACHEURL=http://192.168.222.1/images 4 | IRONIC_EXTERNAL_CALLBACK_URL=https://192.168.222.1:6385 5 | IRONIC_KERNEL_PARAMS=console=ttyS0 6 | IRONIC_INSPECTOR_VLAN_INTERFACES=all 7 | USE_IRONIC_INSPECTOR=false 8 | -------------------------------------------------------------------------------- /ironic-deployment/overlays/e2e-release-29.0/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | namespace: baremetal-operator-system 4 | resources: 5 | - https://github.com/metal3-io/baremetal-operator/config/namespace?ref=release-0.10&timeout=120s 6 | - https://github.com/metal3-io/baremetal-operator/ironic-deployment/base?ref=release-0.10&timeout=120s 7 | 8 | components: 9 | - https://github.com/metal3-io/baremetal-operator/ironic-deployment/components/basic-auth?ref=release-0.10&timeout=120s 10 | - https://github.com/metal3-io/baremetal-operator/ironic-deployment/components/tls?ref=release-0.10&timeout=120s 11 | 12 | configMapGenerator: 13 | - envs: 14 | - ironic_bmo_configmap.env 15 | name: ironic-bmo-configmap 16 | behavior: create 17 | 18 | patches: 19 | - path: ironic-patch.yaml 20 | # The TLS component adds certificates but it cannot know the exact IPs of our environment. 21 | # Here we patch the certificates to have the correct IPs. 22 | # - 192.168.222.1: management computer IP, forwarded to ironic inside kind 23 | - patch: |- 24 | - op: replace 25 | path: /spec/ipAddresses/0 26 | value: 192.168.222.1 27 | target: 28 | kind: Certificate 29 | name: ironic-cert 30 | 31 | images: 32 | - name: quay.io/metal3-io/ironic 33 | newTag: release-29.0 34 | 35 | # NOTE: These credentials are generated automatically in hack/ci-e2e.sh 36 | secretGenerator: 37 | - name: ironic-htpasswd 38 | behavior: create 39 | envs: 40 | - ironic-htpasswd 41 | -------------------------------------------------------------------------------- /ironic-deployment/overlays/e2e/ironic-patch.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: ironic 5 | spec: 6 | template: 7 | spec: 8 | containers: 9 | - name: ironic-dnsmasq 10 | $patch: delete 11 | -------------------------------------------------------------------------------- /ironic-deployment/overlays/e2e/ironic_bmo_configmap.env: -------------------------------------------------------------------------------- 1 | HTTP_PORT=6180 2 | PROVISIONING_INTERFACE=eth0 3 | CACHEURL=http://192.168.222.1/images 4 | IRONIC_HTTP_URL=http://192.168.222.1:6180 5 | IRONIC_BASE_URL=https://192.168.222.1:6385 6 | IRONIC_EXTERNAL_CALLBACK_URL=https://192.168.222.1:6385 7 | IRONIC_KERNEL_PARAMS=console=ttyS0 8 | IRONIC_INSPECTOR_VLAN_INTERFACES=all 9 | USE_IRONIC_INSPECTOR=false 10 | DISABLE_DEEP_IMAGE_INSPECTION=true 11 | -------------------------------------------------------------------------------- /ironic-deployment/overlays/e2e/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | namespace: baremetal-operator-system 4 | resources: 5 | - ../../../config/namespace 6 | - ../../base 7 | 8 | components: 9 | - ../../components/basic-auth 10 | - ../../components/tls 11 | 12 | configMapGenerator: 13 | - envs: 14 | - ironic_bmo_configmap.env 15 | name: ironic-bmo-configmap 16 | behavior: create 17 | 18 | patches: 19 | - path: ironic-patch.yaml 20 | # The TLS component adds certificates but it cannot know the exact IPs of our environment. 21 | # Here we patch the certificates to have the correct IPs. 22 | # - 192.168.222.1: management computer IP, forwarded to ironic inside kind 23 | - patch: |- 24 | - op: replace 25 | path: /spec/ipAddresses/0 26 | value: 192.168.222.1 27 | target: 28 | kind: Certificate 29 | name: ironic-cert 30 | 31 | # NOTE: These credentials are generated automatically in hack/ci-e2e.sh 32 | secretGenerator: 33 | - name: ironic-htpasswd 34 | behavior: create 35 | files: 36 | - htpasswd=ironic-htpasswd 37 | type: Opaque 38 | -------------------------------------------------------------------------------- /ironic-deployment/overlays/with-ipxe-builder-tls/ipxe-configmap.env: -------------------------------------------------------------------------------- 1 | IPXE_ENABLE_TLS_CENV_ARG=true 2 | IPXE_ENABLE_IPV6_CENV_ARG=true 3 | IRONIC_IP=192.168.222.199 4 | -------------------------------------------------------------------------------- /ironic-deployment/overlays/with-ipxe-builder-tls/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | namespace: baremetal-operator-system 4 | resources: 5 | - https://github.com/metal3-io/baremetal-operator/config/namespace?ref=release-0.8&timeout=120s 6 | - https://github.com/metal3-io/baremetal-operator/ironic-deployment/base?ref=release-0.8&timeout=120s 7 | 8 | components: 9 | - https://github.com/metal3-io/baremetal-operator/ironic-deployment/components/basic-auth?ref=release-0.8&timeout=120s 10 | - https://github.com/metal3-io/baremetal-operator/ironic-deployment/components/tls?ref=release-0.8&timeout=120s 11 | - ../../components/ipxe-tls 12 | 13 | configMapGenerator: 14 | - envs: 15 | - ipxe-configmap.env 16 | name: ipxe-configmap 17 | behavior: create 18 | -------------------------------------------------------------------------------- /ironic-deployment/overlays/with-ipxe-builder/ipxe-builder-patch.yaml: -------------------------------------------------------------------------------- 1 | - op: add 2 | path: /spec/template/spec/initContainers/0 3 | value: 4 | name: ipxe-builder 5 | image: quay.io/metal3-io/ipxe-builder 6 | command: 7 | - /bin/buildipxe.sh 8 | envFrom: 9 | - configMapRef: 10 | name: ipxe-configmap 11 | volumeMounts: 12 | - mountPath: /shared 13 | name: ironic-data-volume 14 | securityContext: 15 | allowPrivilegeEscalation: false 16 | capabilities: 17 | drop: 18 | - ALL 19 | privileged: false 20 | runAsUser: 997 # ironic 21 | runAsGroup: 994 # ironic 22 | 23 | -------------------------------------------------------------------------------- /ironic-deployment/overlays/with-ipxe-builder/ipxe-configmap.env: -------------------------------------------------------------------------------- 1 | IPXE_ENABLE_TLS_CENV_ARG=false 2 | IPXE_ENABLE_IPV6_CENV_ARG=true 3 | IRONIC_IP=192.168.222.199 4 | -------------------------------------------------------------------------------- /ironic-deployment/overlays/with-ipxe-builder/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | namespace: baremetal-operator-system 4 | resources: 5 | - https://github.com/metal3-io/baremetal-operator/config/namespace?ref=release-0.8&timeout=120s 6 | - https://github.com/metal3-io/baremetal-operator/ironic-deployment/base?ref=release-0.8&timeout=120s 7 | 8 | configMapGenerator: 9 | - envs: 10 | - ipxe-configmap.env 11 | name: ipxe-configmap 12 | behavior: create 13 | 14 | patches: 15 | - path: ipxe-builder-patch.yaml 16 | -------------------------------------------------------------------------------- /pkg/hardwareutils/bmc/credentials.go: -------------------------------------------------------------------------------- 1 | package bmc 2 | 3 | // Credentials holds the information for authenticating with the BMC. 4 | type Credentials struct { 5 | Username string 6 | Password string 7 | } 8 | 9 | // Validate returns an error if the credentials are invalid. 10 | func (creds Credentials) Validate() error { 11 | if creds.Username == "" { 12 | return &CredentialsValidationError{message: "Missing BMC connection detail 'username' in credentials"} 13 | } 14 | if creds.Password == "" { 15 | return &CredentialsValidationError{message: "Missing BMC connection details 'password' in credentials"} 16 | } 17 | return nil 18 | } 19 | -------------------------------------------------------------------------------- /pkg/hardwareutils/bmc/credentials_test.go: -------------------------------------------------------------------------------- 1 | package bmc 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestValidCredentials(t *testing.T) { 8 | creds := Credentials{ 9 | Username: "username", 10 | Password: "password", 11 | } 12 | err := creds.Validate() 13 | if err != nil { 14 | t.Fatalf("got unexpected validation error: %q", err) 15 | } 16 | } 17 | 18 | func TestMissingUser(t *testing.T) { 19 | creds := Credentials{ 20 | Password: "password", 21 | } 22 | err := creds.Validate() 23 | if err == nil { 24 | t.Fatal("got unexpected valid result") 25 | } 26 | } 27 | 28 | func TestMissingPassword(t *testing.T) { 29 | creds := Credentials{ 30 | Username: "username", 31 | } 32 | err := creds.Validate() 33 | if err == nil { 34 | t.Fatal("got unexpected valid result") 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /pkg/hardwareutils/bmc/errors.go: -------------------------------------------------------------------------------- 1 | package bmc 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | // UnknownBMCTypeError is returned when the provided BMC address cannot be 8 | // mapped to a driver. 9 | type UnknownBMCTypeError struct { 10 | address string 11 | bmcType string 12 | } 13 | 14 | func (e UnknownBMCTypeError) Error() string { 15 | return fmt.Sprintf("Unknown BMC type '%s' for address %s", 16 | e.bmcType, e.address) 17 | } 18 | 19 | // CredentialsValidationError is returned when the provided BMC credentials 20 | // are invalid (e.g. null). 21 | type CredentialsValidationError struct { 22 | message string 23 | } 24 | 25 | func (e CredentialsValidationError) Error() string { 26 | return fmt.Sprintf("Validation error with BMC credentials: %s", 27 | e.message) 28 | } 29 | -------------------------------------------------------------------------------- /pkg/hardwareutils/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/metal3-io/baremetal-operator/pkg/hardwareutils 2 | 3 | go 1.22 4 | -------------------------------------------------------------------------------- /pkg/hardwareutils/go.sum: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/metal3-io/baremetal-operator/41bf9a50bbee6838c57d442b6f8dbccec8fcdde6/pkg/hardwareutils/go.sum -------------------------------------------------------------------------------- /pkg/imageprovider/default.go: -------------------------------------------------------------------------------- 1 | package imageprovider 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | 7 | "github.com/go-logr/logr" 8 | metal3api "github.com/metal3-io/baremetal-operator/apis/metal3.io/v1alpha1" 9 | ) 10 | 11 | type envImageProvider struct { 12 | isoURL string 13 | initrdURL string 14 | } 15 | 16 | func NewDefaultImageProvider() ImageProvider { 17 | return envImageProvider{ 18 | isoURL: os.Getenv("DEPLOY_ISO_URL"), 19 | initrdURL: os.Getenv("DEPLOY_RAMDISK_URL"), 20 | } 21 | } 22 | 23 | func (eip envImageProvider) SupportsArchitecture(_ string) bool { 24 | return true 25 | } 26 | 27 | func (eip envImageProvider) SupportsFormat(format metal3api.ImageFormat) bool { 28 | switch format { 29 | case metal3api.ImageFormatISO: 30 | return eip.isoURL != "" 31 | case metal3api.ImageFormatInitRD: 32 | // Assume we are running inside the same process as the BMH controller - 33 | // if there is no kernel URL then it will be unable to use the initrd. 34 | if os.Getenv("DEPLOY_KERNEL_URL") == "" { 35 | return false 36 | } 37 | return eip.initrdURL != "" 38 | default: 39 | return false 40 | } 41 | } 42 | 43 | func (eip envImageProvider) BuildImage(data ImageData, _ NetworkData, _ logr.Logger) (image GeneratedImage, err error) { 44 | switch data.Format { 45 | case metal3api.ImageFormatISO: 46 | image.ImageURL = eip.isoURL 47 | case metal3api.ImageFormatInitRD: 48 | image.ImageURL = eip.initrdURL 49 | default: 50 | err = BuildInvalidError(fmt.Errorf("unsupported image format \"%s\"", data.Format)) 51 | } 52 | return 53 | } 54 | 55 | func (eip envImageProvider) DiscardImage(_ ImageData) error { 56 | return nil 57 | } 58 | -------------------------------------------------------------------------------- /pkg/imageprovider/imageprovider.go: -------------------------------------------------------------------------------- 1 | package imageprovider 2 | 3 | import ( 4 | "github.com/go-logr/logr" 5 | metal3api "github.com/metal3-io/baremetal-operator/apis/metal3.io/v1alpha1" 6 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 7 | ) 8 | 9 | // ImageData contains information about the image type being requested, and 10 | // metadata about the request. 11 | type ImageData struct { 12 | ImageMetadata *metav1.ObjectMeta 13 | Format metal3api.ImageFormat 14 | Architecture string 15 | NetworkDataStatus metal3api.SecretStatus 16 | } 17 | 18 | // GeneratedImage contains information about the generated image. At least the 19 | // URL must be populated. 20 | type GeneratedImage struct { 21 | ImageURL string 22 | KernelURL string 23 | ExtraKernelParams string 24 | } 25 | 26 | type NetworkData map[string][]byte 27 | 28 | type ImageProvider interface { 29 | // SupportsArchitecture returns whether the ImageProvider can provide 30 | // images for the given processor architecture. 31 | SupportsArchitecture(string) bool 32 | 33 | // SupportsFormat returns whether the ImageProvider can provide images in 34 | // the given format. 35 | SupportsFormat(metal3api.ImageFormat) bool 36 | 37 | // BuildImage requests the ImageProvider to build an image with the 38 | // supplied network data and return a URL where it can be accessed. 39 | BuildImage(ImageData, NetworkData, logr.Logger) (GeneratedImage, error) 40 | 41 | // DiscardImage notifies the ImageProvider that a previously built image 42 | // is no longer required. 43 | DiscardImage(ImageData) error 44 | } 45 | -------------------------------------------------------------------------------- /pkg/imageprovider/invalid.go: -------------------------------------------------------------------------------- 1 | package imageprovider 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | type ImageBuildInvalid struct { 8 | err error 9 | } 10 | 11 | func (ibf ImageBuildInvalid) Error() string { 12 | return fmt.Sprintf("Cannot generate image: %s", ibf.err.Error()) 13 | } 14 | 15 | func (ibf ImageBuildInvalid) Unwrap() error { 16 | return ibf.err 17 | } 18 | 19 | func BuildInvalidError(err error) ImageBuildInvalid { 20 | return ImageBuildInvalid{err: err} 21 | } 22 | 23 | type ImageNotReady struct{} 24 | 25 | func (inr ImageNotReady) Error() string { 26 | return "Image is not ready yet" 27 | } 28 | -------------------------------------------------------------------------------- /pkg/provisioner/ironic/clients/features_test.go: -------------------------------------------------------------------------------- 1 | package clients 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | ) 7 | 8 | func TestAvailableFeatures_ChooseMicroversion(t *testing.T) { 9 | microVersion := "1.95" 10 | type fields struct { 11 | MaxVersion int 12 | } 13 | tests := []struct { 14 | name string 15 | feature fields 16 | want string 17 | }{ 18 | { 19 | name: fmt.Sprintf("MaxVersion < %d return microversion %s", 89, baseline), 20 | feature: fields{ 21 | MaxVersion: 50, 22 | }, 23 | want: baseline, 24 | }, 25 | { 26 | name: fmt.Sprintf("MaxVersion = %d return %s", 89, microVersion), 27 | feature: fields{ 28 | MaxVersion: 95, 29 | }, 30 | want: microVersion, 31 | }, 32 | { 33 | name: fmt.Sprintf("MaxVersion > %d return %s", 89, microVersion), 34 | feature: fields{ 35 | MaxVersion: 100, 36 | }, 37 | want: microVersion, 38 | }, 39 | } 40 | for _, tt := range tests { 41 | t.Run(tt.name, func(t *testing.T) { 42 | af := AvailableFeatures{ 43 | MaxVersion: tt.feature.MaxVersion, 44 | } 45 | if got := af.ChooseMicroversion(); got != tt.want { 46 | t.Errorf("ChooseMicroversion() = %v, want %v", got, tt.want) 47 | } 48 | }) 49 | } 50 | } 51 | 52 | func TestAvailableFeatures_HasFirmwareUpdates(t *testing.T) { 53 | maxVersion := 86 54 | type fields struct { 55 | MaxVersion int 56 | } 57 | tests := []struct { 58 | name string 59 | feature fields 60 | want bool 61 | }{ 62 | { 63 | name: fmt.Sprintf("Firmware < %d", maxVersion), 64 | feature: fields{ 65 | MaxVersion: 50, 66 | }, 67 | want: false, 68 | }, 69 | { 70 | name: fmt.Sprintf("Firmware = %d", maxVersion), 71 | feature: fields{ 72 | MaxVersion: 86, 73 | }, 74 | want: true, 75 | }, 76 | { 77 | name: fmt.Sprintf("Firmware > %d", maxVersion), 78 | feature: fields{ 79 | MaxVersion: 100, 80 | }, 81 | want: true, 82 | }, 83 | } 84 | for _, tt := range tests { 85 | t.Run(tt.name, func(t *testing.T) { 86 | af := AvailableFeatures{ 87 | MaxVersion: tt.feature.MaxVersion, 88 | } 89 | if got := af.HasFirmwareUpdates(); got != tt.want { 90 | t.Errorf("HasFirmwareUpdates() = %v, want %v", got, tt.want) 91 | } 92 | }) 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /pkg/provisioner/ironic/dependencies.go: -------------------------------------------------------------------------------- 1 | package ironic 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/gophercloud/gophercloud/v2/openstack/baremetal/v1/drivers" 7 | "github.com/gophercloud/gophercloud/v2/pagination" 8 | "github.com/metal3-io/baremetal-operator/pkg/provisioner/ironic/clients" 9 | ) 10 | 11 | // TryInit checks if the provisioning backend is available. 12 | func (p *ironicProvisioner) TryInit() (ready bool, err error) { 13 | p.debugLog.Info("verifying ironic provisioner dependencies") 14 | 15 | p.availableFeatures, err = clients.GetAvailableFeatures(p.ctx, p.client) 16 | if err != nil { 17 | p.log.Info("error caught while checking endpoint, will retry", "endpoint", p.client.Endpoint, "error", err) 18 | return false, nil 19 | } 20 | 21 | p.client.Microversion = p.availableFeatures.ChooseMicroversion() 22 | p.availableFeatures.Log(p.debugLog) 23 | 24 | return p.checkIronicConductor() 25 | } 26 | 27 | func (p *ironicProvisioner) checkIronicConductor() (ready bool, err error) { 28 | pager := drivers.ListDrivers(p.client, drivers.ListDriversOpts{ 29 | Detail: false, 30 | }) 31 | err = pager.Err 32 | 33 | if err != nil { 34 | return ready, err 35 | } 36 | 37 | driverCount := 0 38 | _ = pager.EachPage(p.ctx, func(_ context.Context, page pagination.Page) (bool, error) { 39 | actual, driverErr := drivers.ExtractDrivers(page) 40 | if driverErr != nil { 41 | return false, driverErr 42 | } 43 | driverCount += len(actual) 44 | return true, nil 45 | }) 46 | 47 | // If we have any drivers, conductor is up. 48 | ready = driverCount > 0 49 | 50 | return ready, err 51 | } 52 | -------------------------------------------------------------------------------- /pkg/provisioner/ironic/devicehints/devicehints.go: -------------------------------------------------------------------------------- 1 | package devicehints 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | 7 | metal3api "github.com/metal3-io/baremetal-operator/apis/metal3.io/v1alpha1" 8 | ) 9 | 10 | // MakeHintMap converts a RootDeviceHints instance into a string map 11 | // suitable to pass to ironic. 12 | func MakeHintMap(source *metal3api.RootDeviceHints) map[string]string { 13 | hints := map[string]string{} 14 | 15 | if source == nil { 16 | return hints 17 | } 18 | 19 | if source.DeviceName != "" { 20 | if strings.HasPrefix(source.DeviceName, "/dev/disk/by-path/") { 21 | hints["by_path"] = fmt.Sprintf("s== %s", source.DeviceName) 22 | } else { 23 | hints["name"] = fmt.Sprintf("s== %s", source.DeviceName) 24 | } 25 | } 26 | if source.HCTL != "" { 27 | hints["hctl"] = fmt.Sprintf("s== %s", source.HCTL) 28 | } 29 | if source.Model != "" { 30 | hints["model"] = fmt.Sprintf(" %s", source.Model) 31 | } 32 | if source.Vendor != "" { 33 | hints["vendor"] = fmt.Sprintf(" %s", source.Vendor) 34 | } 35 | if source.SerialNumber != "" { 36 | hints["serial"] = fmt.Sprintf("s== %s", source.SerialNumber) 37 | } 38 | if source.MinSizeGigabytes != 0 { 39 | hints["size"] = fmt.Sprintf(">= %d", source.MinSizeGigabytes) 40 | } 41 | if source.WWN != "" { 42 | hints["wwn"] = fmt.Sprintf("s== %s", source.WWN) 43 | } 44 | if source.WWNWithExtension != "" { 45 | hints["wwn_with_extension"] = fmt.Sprintf("s== %s", source.WWNWithExtension) 46 | } 47 | if source.WWNVendorExtension != "" { 48 | hints["wwn_vendor_extension"] = fmt.Sprintf("s== %s", source.WWNVendorExtension) 49 | } 50 | switch { 51 | case source.Rotational == nil: 52 | case *source.Rotational: 53 | hints["rotational"] = "true" 54 | case !*source.Rotational: 55 | hints["rotational"] = "false" 56 | } 57 | 58 | return hints 59 | } 60 | -------------------------------------------------------------------------------- /pkg/provisioner/ironic/findhost_test.go: -------------------------------------------------------------------------------- 1 | package ironic 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/gophercloud/gophercloud/v2/openstack/baremetal/v1/nodes" 7 | "github.com/metal3-io/baremetal-operator/pkg/hardwareutils/bmc" 8 | "github.com/metal3-io/baremetal-operator/pkg/provisioner/ironic/clients" 9 | "github.com/metal3-io/baremetal-operator/pkg/provisioner/ironic/testserver" 10 | ) 11 | 12 | func TestFindExistingHost(t *testing.T) { 13 | cases := []struct { 14 | name string 15 | ironic *testserver.IronicMock 16 | 17 | hostName string 18 | provisioningID string 19 | nodeName string 20 | }{ 21 | { 22 | name: "no-node", 23 | hostName: "name", 24 | provisioningID: "uuid", 25 | ironic: testserver.NewIronic(t).NoNode("myns" + nameSeparator + "name").NoNode("name").NoNode("uuid"), 26 | }, 27 | { 28 | name: "by-name", 29 | hostName: "name", 30 | provisioningID: "uuid", 31 | ironic: testserver.NewIronic(t).NoNode("uuid"). 32 | Node(nodes.Node{ 33 | Name: "myns" + nameSeparator + "name", 34 | UUID: "different-uuid", 35 | }), 36 | nodeName: "myns" + nameSeparator + "name", 37 | }, 38 | { 39 | name: "by-uuid", 40 | hostName: "name", 41 | provisioningID: "uuid", 42 | ironic: testserver.NewIronic(t).NoNode("myns" + nameSeparator + "name").NoNode("name"). 43 | Node(nodes.Node{ 44 | Name: "myns" + nameSeparator + "different-name", 45 | UUID: "uuid", 46 | }), 47 | nodeName: "myns" + nameSeparator + "different-name", 48 | }, 49 | } 50 | 51 | for _, tc := range cases { 52 | t.Run(tc.name, func(t *testing.T) { 53 | if tc.ironic != nil { 54 | tc.ironic.Start() 55 | defer tc.ironic.Stop() 56 | } 57 | 58 | auth := clients.AuthConfig{Type: clients.NoAuth} 59 | 60 | // Update the default host to match the test settings 61 | host := makeHost() 62 | host.ObjectMeta.Name = tc.hostName 63 | host.Status.Provisioning.ID = tc.provisioningID 64 | 65 | prov, err := newProvisionerWithSettings(host, bmc.Credentials{}, nil, tc.ironic.Endpoint(), auth) 66 | if err != nil { 67 | t.Fatalf("could not create provisioner: %s", err) 68 | } 69 | 70 | node, err := prov.findExistingHost("") 71 | t.Logf("requests: %s", tc.ironic.Requests) 72 | if err != nil { 73 | t.Fatalf("could not look up host: %s", err) 74 | } 75 | 76 | if tc.nodeName == "" && node != nil { 77 | t.Fatalf("found unexpected node %s (%s)", node.Name, node.UUID) 78 | } 79 | }) 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /pkg/provisioner/ironic/ready_test.go: -------------------------------------------------------------------------------- 1 | package ironic 2 | 3 | import ( 4 | "net/http" 5 | "testing" 6 | 7 | "github.com/metal3-io/baremetal-operator/pkg/hardwareutils/bmc" 8 | "github.com/metal3-io/baremetal-operator/pkg/provisioner/ironic/clients" 9 | "github.com/metal3-io/baremetal-operator/pkg/provisioner/ironic/testserver" 10 | "github.com/stretchr/testify/assert" 11 | "github.com/stretchr/testify/require" 12 | ) 13 | 14 | func TestProvisionerIsReady(t *testing.T) { 15 | cases := []struct { 16 | name string 17 | ironic *testserver.IronicMock 18 | 19 | expectedIronicCalls string 20 | expectedIsReady bool 21 | expectedError string 22 | }{ 23 | { 24 | name: "IsReady", 25 | ironic: testserver.NewIronic(t).WithDrivers(), 26 | expectedIronicCalls: "/v1/;/v1/drivers;", 27 | expectedIsReady: true, 28 | }, 29 | { 30 | name: "NoDriversLoaded", 31 | ironic: testserver.NewIronic(t), 32 | expectedIronicCalls: "/v1/;/v1/drivers;", 33 | }, 34 | { 35 | name: "IronicDown", 36 | expectedIsReady: false, 37 | }, 38 | { 39 | name: "IronicNotOk", 40 | ironic: testserver.NewIronic(t).NotReady(http.StatusInternalServerError), 41 | expectedIsReady: false, 42 | expectedIronicCalls: "/v1/;", 43 | }, 44 | { 45 | name: "IronicNotOkAndNotExpected", 46 | ironic: testserver.NewIronic(t).NotReady(http.StatusBadGateway), 47 | expectedIsReady: false, 48 | expectedIronicCalls: "/v1/;", 49 | }, 50 | } 51 | 52 | for _, tc := range cases { 53 | t.Run(tc.name, func(t *testing.T) { 54 | if tc.ironic != nil { 55 | tc.ironic.Start() 56 | defer tc.ironic.Stop() 57 | } 58 | 59 | auth := clients.AuthConfig{Type: clients.NoAuth} 60 | 61 | ironicEndpoint := tc.ironic.Endpoint() 62 | prov, err := newProvisionerWithSettings(makeHost(), bmc.Credentials{}, nil, ironicEndpoint, auth) 63 | if err != nil { 64 | t.Fatalf("could not create provisioner: %s", err) 65 | } 66 | 67 | ready, err := prov.TryInit() 68 | if err != nil { 69 | t.Fatalf("could not determine ready state: %s", err) 70 | } 71 | 72 | if tc.ironic != nil { 73 | assert.Equal(t, tc.expectedIronicCalls, tc.ironic.Requests, "ironic calls") 74 | } 75 | 76 | if tc.expectedError != "" { 77 | assert.Regexp(t, tc.expectedError, err, "error message") 78 | } else { 79 | require.NoError(t, err) 80 | assert.Equal(t, tc.expectedIsReady, ready, "ready flag") 81 | } 82 | }) 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /pkg/provisioner/ironic/result.go: -------------------------------------------------------------------------------- 1 | package ironic 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/metal3-io/baremetal-operator/pkg/provisioner" 7 | ) 8 | 9 | func retryAfterDelay(delay time.Duration) (provisioner.Result, error) { 10 | // TODO(zaneb): this is currently indistinguishable from the result of 11 | // operationContinuing() from the caller's perspective. Changes are 12 | // required to the Result structure to enable this to be distinguished. 13 | return provisioner.Result{ 14 | Dirty: true, 15 | RequeueAfter: delay, 16 | }, nil 17 | } 18 | 19 | func operationContinuing(delay time.Duration) (provisioner.Result, error) { 20 | return provisioner.Result{ 21 | Dirty: true, 22 | RequeueAfter: delay, 23 | }, nil 24 | } 25 | 26 | func operationComplete() (provisioner.Result, error) { 27 | return provisioner.Result{}, nil 28 | } 29 | 30 | func operationFailed(message string) (provisioner.Result, error) { 31 | return provisioner.Result{ErrorMessage: message}, nil 32 | } 33 | 34 | func transientError(err error) (provisioner.Result, error) { 35 | return provisioner.Result{}, err 36 | } 37 | -------------------------------------------------------------------------------- /pkg/provisioner/ironic/testserver/ironic_test.go: -------------------------------------------------------------------------------- 1 | package testserver 2 | 3 | import ( 4 | "net/http" 5 | "testing" 6 | 7 | "github.com/gophercloud/gophercloud/v2/openstack/baremetal/v1/nodes" 8 | ) 9 | 10 | func TestIronicDatabaseClearing(t *testing.T) { 11 | ironic := NewIronic(t).WithDefaultResponses() 12 | ironic.AddDefaultResponse("/v1/nodes", "POST", http.StatusCreated, "{}") 13 | ironic.Start() 14 | defer ironic.Stop() 15 | 16 | endpoint := ironic.Endpoint() 17 | 18 | url := endpoint + "nodes/uuid" 19 | 20 | resp, err := http.Get(url) // #nosec 21 | 22 | if err != nil { 23 | t.Error(err) 24 | } 25 | 26 | defer resp.Body.Close() 27 | 28 | if resp.StatusCode != http.StatusOK { 29 | t.Fail() 30 | } 31 | 32 | ironic.ClearDatabase() 33 | 34 | resp, err = http.Get(url) // #nosec 35 | 36 | if err != nil { 37 | t.Error(err) 38 | } 39 | 40 | defer resp.Body.Close() 41 | 42 | if resp.StatusCode != http.StatusNotFound { 43 | t.Fail() 44 | } 45 | 46 | // After clearing the db, POSTs should still work 47 | resp, err = http.PostForm(endpoint+"nodes", nil) 48 | 49 | if err != nil { 50 | t.Error(err) 51 | } 52 | 53 | defer resp.Body.Close() 54 | 55 | if resp.StatusCode != http.StatusCreated { 56 | t.Fail() 57 | } 58 | } 59 | 60 | func TestIronicDatabaseClearingNode(t *testing.T) { 61 | ironic := NewIronic(t).WithDefaultResponses().Node(nodes.Node{ 62 | UUID: "abc", 63 | }) 64 | ironic.AddDefaultResponse("/v1/nodes", "POST", http.StatusCreated, "{}") 65 | ironic.Start() 66 | defer ironic.Stop() 67 | 68 | endpoint := ironic.Endpoint() 69 | 70 | url := endpoint + "nodes/uuid" 71 | 72 | resp, err := http.Get(url) // #nosec 73 | 74 | if err != nil { 75 | t.Error(err) 76 | } 77 | 78 | defer resp.Body.Close() 79 | 80 | if resp.StatusCode != http.StatusOK { 81 | t.Fail() 82 | } 83 | 84 | ironic.ClearDatabase() 85 | 86 | resp, err = http.Get(url) // #nosec 87 | 88 | if err != nil { 89 | t.Error(err) 90 | } 91 | 92 | defer resp.Body.Close() 93 | 94 | if resp.StatusCode != http.StatusNotFound { 95 | t.Fail() 96 | } 97 | 98 | // After clearing the db, POSTs should still work 99 | resp, err = http.PostForm(endpoint+"nodes", nil) 100 | 101 | if err != nil { 102 | t.Error(err) 103 | } 104 | 105 | defer resp.Body.Close() 106 | 107 | if resp.StatusCode != http.StatusCreated { 108 | t.Fail() 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /pkg/secretutils/label.go: -------------------------------------------------------------------------------- 1 | package secretutils 2 | 3 | import ( 4 | corev1 "k8s.io/api/core/v1" 5 | "k8s.io/apimachinery/pkg/labels" 6 | "sigs.k8s.io/controller-runtime/pkg/cache" 7 | "sigs.k8s.io/controller-runtime/pkg/client" 8 | ) 9 | 10 | const ( 11 | LabelEnvironmentName = "environment.metal3.io" 12 | LabelEnvironmentValue = "baremetal" 13 | ) 14 | 15 | // AddSecretSelector adds a selector to a cache.SelectorsByObject that filters 16 | // Secrets so that only those labelled as part of the baremetal environment get 17 | // cached. The input may be nil. 18 | func AddSecretSelector(selectors map[client.Object]cache.ByObject) map[client.Object]cache.ByObject { 19 | secret := &corev1.Secret{} 20 | newSelectors := map[client.Object]cache.ByObject{ 21 | secret: { 22 | Label: labels.SelectorFromSet( 23 | labels.Set{ 24 | LabelEnvironmentName: LabelEnvironmentValue, 25 | }), 26 | }, 27 | } 28 | 29 | if selectors == nil { 30 | return newSelectors 31 | } 32 | 33 | selectors[secret] = newSelectors[secret] 34 | return selectors 35 | } 36 | -------------------------------------------------------------------------------- /pkg/utils/stringlist.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | // StringInList returns a boolean indicating whether strToSearch is a 4 | // member of the string slice passed as the first argument. 5 | func StringInList(list []string, strToSearch string) bool { 6 | for _, item := range list { 7 | if item == strToSearch { 8 | return true 9 | } 10 | } 11 | return false 12 | } 13 | 14 | // FilterStringFromList produces a new string slice that does not 15 | // include the strToFilter argument. 16 | func FilterStringFromList(list []string, strToFilter string) (newList []string) { 17 | for _, item := range list { 18 | if item != strToFilter { 19 | newList = append(newList, item) 20 | } 21 | } 22 | return 23 | } 24 | -------------------------------------------------------------------------------- /pkg/utils/stringlist_test.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "reflect" 5 | "testing" 6 | ) 7 | 8 | func TestStringInList(t *testing.T) { 9 | cases := []struct { 10 | list []string 11 | str string 12 | expect bool 13 | }{ 14 | { 15 | list: []string{"a", "b", "c"}, 16 | str: "a", 17 | expect: true, 18 | }, 19 | { 20 | list: []string{"a", "b", "c"}, 21 | str: "d", 22 | expect: false, 23 | }, 24 | } 25 | 26 | for _, c := range cases { 27 | got := StringInList(c.list, c.str) 28 | if c.expect != got { 29 | t.Errorf("Expected '%t', but got '%t'", c.expect, got) 30 | } 31 | } 32 | } 33 | 34 | func TestFilterStringFromList(t *testing.T) { 35 | cases := []struct { 36 | list []string 37 | str string 38 | expectStr []string 39 | }{ 40 | { 41 | list: []string{"a", "b", "c"}, 42 | str: "a", 43 | expectStr: []string{"b", "c"}, 44 | }, 45 | { 46 | list: []string{"a", "b", "c"}, 47 | str: "d", 48 | expectStr: []string{"a", "b", "c"}, 49 | }, 50 | } 51 | 52 | for _, c := range cases { 53 | newList := FilterStringFromList(c.list, c.str) 54 | if !reflect.DeepEqual(newList, c.expectStr) { 55 | t.Errorf("Expected '%s', but got '%s'", c.expectStr, newList) 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /pkg/version/version.go: -------------------------------------------------------------------------------- 1 | // Package version includes the version information. 2 | package version 3 | 4 | import "fmt" 5 | 6 | var ( 7 | // Raw is the string representation of the version. This will be replaced 8 | // with the calculated version at build time. 9 | // set in the Makefile. 10 | Raw = "was not built with version info" 11 | 12 | // String is the human-friendly representation of the version. 13 | String = fmt.Sprintf("metal3-io/baremetal-operator %s", Raw) 14 | 15 | // Commit is the commit hash from which the software was built. 16 | // Set via LDFLAGS in Makefile. 17 | Commit = "unknown" 18 | 19 | // BuildTime is the string representation of build time. 20 | // Set via LDFLAGS in Makefile. 21 | BuildTime = "unknown" 22 | ) 23 | -------------------------------------------------------------------------------- /releasenotes/v0.10.1.md: -------------------------------------------------------------------------------- 1 | 2 | # Changes since v0.10.0 3 | 4 | ## :bug: Bug Fixes 5 | 6 | - Pin ironic to version 29.0 (#2419) 7 | 8 | The image for this release is: v0.10.1 9 | 10 | _Thanks to all our contributors!_ 😊 11 | -------------------------------------------------------------------------------- /releasenotes/v0.9.1.md: -------------------------------------------------------------------------------- 1 | 2 | # Changes since v0.9.0 3 | 4 | ## :warning: Breaking Changes 5 | 6 | This patch release contains fix for the security advisory 7 | [GHSA-c98h-7hp9-v9hq](https://github.com/metal3-io/baremetal-operator/security/advisories/GHSA-c98h-7hp9-v9hq): 8 | 9 | - Only accept HTTPHeadersRef in same namespace (#2321) 10 | 11 | ## :bug: Bug Fixes 12 | 13 | - bump x/net, x/crypto and x/oauth2 (#2317) 14 | - update rescue iso used for e2e tests (#2320) 15 | - bump actions/cache to v4.2.2 (#2280) 16 | - Fix metrics service validation (#2188) 17 | - Add validation against cross-namespace secret references (#2206) 18 | 19 | ## :seedling: Others 20 | 21 | - Bump the kubernetes group to v0.31.7 (#2312) 22 | - Bump sigs.k8s.io/controller-runtime from 0.19.6 to 0.19.7 (#2286) 23 | - Bump go.etcd.io/etcd/client/pkg/v3 from 3.5.18 to 3.5.19 (#2285) 24 | - E2E: Fix ironic overlays for 25.0 and 26.0 (#2301) 25 | - bump local hack scripts to basic-checks:golang-1.23 (#2299) 26 | - bump golang to 1.23.7 (#2298) 27 | - E2E: Remove ensure_kind.sh script (#2283) 28 | - Bump CAPI to 1.9.5 (#2267) 29 | - Bump github.com/cert-manager/cert-manager from 1.16.3 to 1.16.4 in /test (#2255) 30 | - pin osv-scanner image in verify-release.sh (#2245) 31 | - Switch e2e to kind (#2222) 32 | - Bump sigs.k8s.io/controller-runtime from 0.19.4 to 0.19.5 (#2219) 33 | - Bump go.etcd.io/etcd/client/pkg/v3 from 3.5.17 to 3.5.18 (#2218) 34 | - Bump github.com/cert-manager/cert-manager from 1.16.2 to 1.16.3 in /test (#2200) 35 | - Update README badges and e2e triggers (#2175) 36 | - Bump github.com/onsi/ginkgo/v2 from 2.22.1 to 2.22.2 in /test (#2168) 37 | - Bump libvirt.org/go/libvirt from 1.10009.0 to 1.10009.1 in /test (#2153) 38 | - Bump github.com/onsi/gomega from 1.36.1 to 1.36.2 in /test (#2160) 39 | - Bump github.com/onsi/gomega from 1.36.1 to 1.36.2 (#2158) 40 | - Bump google/osv-scanner from 1.9.1 to 1.9.2 (#2162) 41 | - Bump github/codeql-action from 3.27.5 to 3.27.9 (#2141) 42 | 43 | ## :recycle: Superseded or Reverted 44 | 45 | - #2247, #2225, #2216, #2251, #2199, #2186, #2159, #2253, #2178, #2152 46 | 47 | The image for this release is: v0.9.1 48 | 49 | _Thanks to all our contributors!_ 😊 50 | -------------------------------------------------------------------------------- /template/sarif.tpl: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://schemastore.azurewebsites.net/schemas/json/sarif-2.1.0-rtm.4.json", 3 | "version": "2.1.0", 4 | "runs": [ 5 | {{- $run_first := true }} 6 | {{- range $report_index, $report := . }} 7 | {{- if and $report.Valid (not (eq $report.Message "This resource kind is not supported by kubesec")) -}} 8 | {{- if $run_first -}} 9 | {{- $run_first = false -}} 10 | {{ else -}} 11 | , 12 | {{- end }} 13 | { 14 | "tool": { 15 | "driver": { 16 | "name": "Kubesec", 17 | "fullName": "Kubesec Kubernetes Resource Security Policy Validator", 18 | "rules": [ 19 | {{- $rule_first := true }} 20 | {{- range .Rules }} 21 | {{- if $rule_first -}} 22 | {{- $rule_first = false -}} 23 | {{ else -}} 24 | , 25 | {{- end }} 26 | { 27 | "id": "{{ .ID }}", 28 | "shortDescription": { 29 | "text": "{{ .Reason }}" 30 | }, 31 | "messageStrings": { 32 | "selector": { 33 | "text": {{ escapeString .Selector | printf "%q" }} 34 | } 35 | }, 36 | "properties": { 37 | "points": "{{ .Points }}" 38 | } 39 | } 40 | {{- end -}} 41 | ] 42 | } 43 | }, 44 | "results": [ 45 | {{- $result_first := true }} 46 | {{- range $result_index, $res := joinSlices .Scoring.Advise .Scoring.Critical -}} 47 | {{- if $result_first -}} 48 | {{- $result_first = false -}} 49 | {{ else -}} 50 | , 51 | {{- end }} 52 | { 53 | "ruleId": "{{ $res.ID }}", 54 | "level": "warning", 55 | "message": { 56 | "text": {{ endWithPeriod $res.Reason | printf "%q" }}, 57 | "properties": { 58 | "score": "{{ $res.Points }}", 59 | "selector": {{ escapeString $res.Selector | printf "%q" }} 60 | } 61 | }, 62 | "locations": [ 63 | { 64 | "physicalLocation": { 65 | "artifactLocation": { 66 | "uri": "{{ $report.FileName }}" 67 | } 68 | } 69 | } 70 | ] 71 | } 72 | {{- end -}} 73 | ], 74 | "columnKind": "utf16CodeUnits" 75 | } 76 | {{- end -}} 77 | {{- end }} 78 | ] 79 | } 80 | -------------------------------------------------------------------------------- /test/e2e/bmc.go: -------------------------------------------------------------------------------- 1 | //go:build e2e 2 | // +build e2e 3 | 4 | package e2e 5 | 6 | import ( 7 | "os" 8 | 9 | "gopkg.in/yaml.v2" 10 | ) 11 | 12 | // BMC defines connection details for a baseboard management controller 13 | // and other details needed for creating a virtual machine related to it. 14 | type BMC struct { 15 | // User is the username for accessing the BMC. 16 | User string `yaml:"user,omitempty"` 17 | // Password is the password for accessing the BMC. 18 | Password string `yaml:"password,omitempty"` 19 | // Address of the BMC, e.g. "redfish-virtualmedia+http://192.168.222.1:8000/redfish/v1/Systems/bmo-e2e-1". 20 | Address string `yaml:"address,omitempty"` 21 | // BootMacAddress is the MAC address of the VMs network interface. 22 | BootMacAddress string `yaml:"bootMacAddress,omitempty"` 23 | // Name of the machine associated with this BMC. 24 | Name string `yaml:"name,omitempty"` 25 | // NetworkName is the name of the network that the new VM should be attached to 26 | NetworkName string `yaml:"networkName,omitempty"` 27 | // IPAddress is a reserved IP address for the VM. 28 | // This will be paired with the MAC address in the DHCP configuration. 29 | // Example: 192.168.222.122 30 | IPAddress string `yaml:"ipAddress,omitempty"` 31 | } 32 | 33 | func LoadBMCConfig(configPath string) ([]BMC, error) { 34 | configData, err := os.ReadFile(configPath) //#nosec 35 | var bmcs []BMC 36 | if err != nil { 37 | return nil, err 38 | } 39 | if err := yaml.Unmarshal(configData, &bmcs); err != nil { 40 | return nil, err 41 | } 42 | return bmcs, nil 43 | } 44 | -------------------------------------------------------------------------------- /test/e2e/config/bmcs-fixture.yaml: -------------------------------------------------------------------------------- 1 | # For fixture it doesn't matter much what we put here. The number of BMCs, 2 | # however, needs to be equal or larger than `GINKGO_NODES` or we will 3 | # run out of BMCs to test on. 4 | - user: admin 5 | password: password 6 | address: "redfish+http://192.168.222.1:8000/redfish/v1/Systems/bmo-e2e-0" 7 | bootMacAddress: "00:60:2f:31:81:01" 8 | - user: admin 9 | password: password 10 | address: "redfish+http://192.168.222.1:8000/redfish/v1/Systems/bmo-e2e-1" 11 | bootMacAddress: "00:60:2f:31:81:02" 12 | -------------------------------------------------------------------------------- /test/e2e/config/bmcs-ipmi.yaml: -------------------------------------------------------------------------------- 1 | - user: admin 2 | password: password 3 | address: "ipmi://192.168.222.1:16230" 4 | bootMacAddress: "00:60:2f:31:81:01" 5 | name: "bmo-e2e-0" 6 | ipAddress: "192.168.222.122" 7 | - user: admin 8 | password: password 9 | address: "ipmi://192.168.222.1:16231" 10 | bootMacAddress: "00:60:2f:31:81:02" 11 | name: "bmo-e2e-1" 12 | ipAddress: "192.168.222.123" 13 | -------------------------------------------------------------------------------- /test/e2e/config/bmcs-redfish-virtualmedia.yaml: -------------------------------------------------------------------------------- 1 | - user: admin 2 | password: password 3 | address: "redfish-virtualmedia+http://192.168.222.1:8000/redfish/v1/Systems/bmo-e2e-0" 4 | bootMacAddress: "00:60:2f:31:81:01" 5 | name: "bmo-e2e-0" 6 | ipAddress: "192.168.222.122" 7 | - user: admin 8 | password: password 9 | address: "redfish-virtualmedia+http://192.168.222.1:8000/redfish/v1/Systems/bmo-e2e-1" 10 | bootMacAddress: "00:60:2f:31:81:02" 11 | name: "bmo-e2e-1" 12 | ipAddress: "192.168.222.123" 13 | -------------------------------------------------------------------------------- /test/e2e/config/bmcs-redfish.yaml: -------------------------------------------------------------------------------- 1 | - user: admin 2 | password: password 3 | address: "redfish+http://192.168.222.1:8000/redfish/v1/Systems/bmo-e2e-0" 4 | bootMacAddress: "00:60:2f:31:81:01" 5 | name: "bmo-e2e-0" 6 | ipAddress: "192.168.222.122" 7 | - user: admin 8 | password: password 9 | address: "redfish+http://192.168.222.1:8000/redfish/v1/Systems/bmo-e2e-1" 10 | bootMacAddress: "00:60:2f:31:81:02" 11 | name: "bmo-e2e-1" 12 | ipAddress: "192.168.222.123" 13 | -------------------------------------------------------------------------------- /test/e2e/sushy-tools/sushy-emulator.conf: -------------------------------------------------------------------------------- 1 | # Listen on the local IP address 192.168.222.1 2 | SUSHY_EMULATOR_LISTEN_IP = u'192.168.222.1' 3 | 4 | # Bind to TCP port 8000 5 | SUSHY_EMULATOR_LISTEN_PORT = 8000 6 | 7 | # Serve this SSL certificate to the clients 8 | SUSHY_EMULATOR_SSL_CERT = None 9 | 10 | # If SSL certificate is being served, this is its RSA private key 11 | SUSHY_EMULATOR_SSL_KEY = None 12 | 13 | # The OpenStack cloud ID to use. This option enables OpenStack driver. 14 | SUSHY_EMULATOR_OS_CLOUD = None 15 | # The libvirt URI to use. This option enables libvirt driver. 16 | SUSHY_EMULATOR_LIBVIRT_URI = u'qemu:///system' 17 | 18 | # Instruct the libvirt driver to ignore any instructions to 19 | # set the boot device. Allowing the UEFI firmware to instead 20 | # rely on the EFI Boot Manager 21 | # Note: This sets the legacy boot element to dev="fd" 22 | # and relies on the floppy not existing, it likely wont work 23 | # your VM has a floppy drive. 24 | SUSHY_EMULATOR_IGNORE_BOOT_DEVICE = False 25 | 26 | # The map of firmware loaders dependant on the boot mode and 27 | # system architecture. Ideally the x86_64 loader will be capable 28 | # of secure boot or not based on the chosen nvram. 29 | SUSHY_EMULATOR_BOOT_LOADER_MAP = { 30 | u'UEFI': { 31 | u'x86_64': u'/usr/share/OVMF/OVMF_CODE.secboot.fd' 32 | }, 33 | u'Legacy': { 34 | u'x86_64': None 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /test/vbmctl/templates/pool.xml.tpl: -------------------------------------------------------------------------------- 1 | 2 | {{ .PoolName }} 3 | 4 | {{ .PoolPath }} 5 | 6 | 0755 7 | -1 8 | -1 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /test/vbmctl/templates/volume.xml.tpl: -------------------------------------------------------------------------------- 1 | 2 | {{ .VolumeName }}.qcow2 3 | {{ .VolumeCapacityInGB }} 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /tilt-provider.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "metal3-bmo", 3 | "config": { 4 | "kustomize_config": false, 5 | "image": "quay.io/metal3-io/baremetal-operator", 6 | "live_reload_deps": [ 7 | "apis", "cmd", "pkg", "config", "internal", "go.mod", "go.sum", "main.go" 8 | ] 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /tools/bmh_test/clean_local_bmh_test_setup.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -ux 4 | 5 | BMH_NAME_REGEX="${1:-^bmh-test-}" 6 | # Get a list of all virtual machines 7 | VM_LIST=$(virsh -c qemu:///system list --all --name | grep "${BMH_NAME_REGEX}") 8 | 9 | if [[ -n "${VM_LIST}" ]]; then 10 | # Loop through the list and delete each virtual machine 11 | for vm_name in ${VM_LIST}; do 12 | virsh -c qemu:///system destroy --domain "${vm_name}" 13 | virsh -c qemu:///system undefine --domain "${vm_name}" --remove-all-storage --nvram 14 | kubectl delete baremetalhost "${vm_name}" 15 | done 16 | else 17 | echo "No virtual machines found. Skipping..." 18 | fi 19 | 20 | # Clear vbmc 21 | docker rm -f vbmc 22 | 23 | # Clear network 24 | virsh -c qemu:///system net-destroy baremetal-e2e 25 | virsh -c qemu:///system net-undefine baremetal-e2e 26 | 27 | # Cleanup VM and volume qcow2 28 | rm -rf /tmp/bmo-e2e-*.qcow2 29 | rm -rf /tmp/pool_oo/bmo-e2e-*.qcow2 30 | -------------------------------------------------------------------------------- /tools/bmh_test/create_vm.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -eux 4 | 5 | VM_NAME="${1:?}" 6 | MAC_ADDRESS="${2:?}" 7 | SERIAL_LOG_PATH="/var/log/libvirt/qemu/${VM_NAME}-serial0.log" 8 | 9 | # Create a virtual machine 10 | virt-install \ 11 | --connect qemu:///system \ 12 | --name "${VM_NAME}" \ 13 | --description "Virtualized BareMetalHost" \ 14 | --osinfo=ubuntu-lts-latest \ 15 | --ram=4096 \ 16 | --vcpus=2 \ 17 | --disk size=20 \ 18 | --graphics=none \ 19 | --console pty,target_type=serial \ 20 | --serial file,path="${SERIAL_LOG_PATH}" \ 21 | --xml "./devices/serial/@type=pty" \ 22 | --xml "./devices/serial/log/@file=${SERIAL_LOG_PATH}" \ 23 | --xml "./devices/serial/log/@append=on" \ 24 | --pxe \ 25 | --network network=baremetal-e2e,mac="${MAC_ADDRESS}" \ 26 | --noautoconsole 27 | -------------------------------------------------------------------------------- /tools/bmh_test/run_local_bmh_test_setup.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -eux 4 | 5 | REPO_ROOT=$(realpath "$(dirname "${BASH_SOURCE[0]}")/../..") 6 | 7 | cd "${REPO_ROOT}" || exit 1 8 | 9 | # List of packages to check 10 | commands=("virt-install" "virsh") 11 | 12 | # Check each package 13 | for cmd in "${commands[@]}"; do 14 | if ! command -v "${cmd}" &> /dev/null; then 15 | echo "ERROR: ${cmd} not found. Please install it." 16 | exit 1 17 | fi 18 | done 19 | 20 | # Define and start a virtual network 21 | virsh -c qemu:///system net-define "${REPO_ROOT}/hack/e2e/net.xml" 22 | virsh -c qemu:///system net-start baremetal-e2e 23 | 24 | # Start VBMC 25 | docker run --name vbmc --network host -d \ 26 | -v /var/run/libvirt/libvirt-sock:/var/run/libvirt/libvirt-sock \ 27 | -v /var/run/libvirt/libvirt-sock-ro:/var/run/libvirt/libvirt-sock-ro \ 28 | quay.io/metal3-io/vbmc 29 | -------------------------------------------------------------------------------- /tools/bmh_test/vm2vbmc.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -eux 4 | 5 | NAME="${1:?}" 6 | VBMC_PORT="${2:?}" 7 | VBMC_ADDRESS="${3:-"::"}" 8 | 9 | # Add the BareMetalHost VM to VBMC 10 | docker exec vbmc vbmc add "${NAME}" --port "${VBMC_PORT}" --address "${VBMC_ADDRESS}" --libvirt-uri "qemu:///system" 11 | docker exec vbmc vbmc start "${NAME}" 12 | docker exec vbmc vbmc list 13 | -------------------------------------------------------------------------------- /tools/clean_demo_hosts.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -x 2 | 3 | oc delete baremetalhost -l metal3demo 4 | -------------------------------------------------------------------------------- /tools/clean_host.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -x 2 | 3 | num=$1 4 | 5 | openstack baremetal node list 6 | sudo virsh list --all 7 | oc get baremetalhosts 8 | 9 | openstack baremetal node maintenance set "openshift-worker-${num}" 10 | openstack baremetal node delete "openshift-worker-${num}" 11 | sudo virsh shutdown "openshift_worker_${num}" 12 | oc delete baremetalhost "openshift-worker-${num}" 13 | -------------------------------------------------------------------------------- /tools/remove_local_ironic.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -xe 4 | 5 | # This script removes local ironic containers. 6 | 7 | CONTAINER_RUNTIME="${CONTAINER_RUNTIME:-podman}" 8 | 9 | for name in ironic ironic-inspector dnsmasq httpd mariadb ipa-downloader ironic-endpoint-keepalived \ 10 | ironic-log-watch ; do 11 | sudo "${CONTAINER_RUNTIME}" ps | grep -w "$name$" && sudo "${CONTAINER_RUNTIME}" kill "$name" 12 | sudo "${CONTAINER_RUNTIME}" ps --all | grep -w "$name$" && sudo "${CONTAINER_RUNTIME}" rm "$name" -f 13 | done 14 | 15 | set +xe 16 | --------------------------------------------------------------------------------