├── .github ├── dependabot.yml ├── pull_request_template.md └── workflows │ ├── bump_metallb.yml │ ├── ci.yaml │ ├── classify.yaml │ ├── composite │ ├── collectlogs │ │ └── action.yaml │ └── setup │ │ └── action.yaml │ ├── publish.yaml │ └── verify_version.yaml ├── .gitignore ├── CODEOWNERS ├── Dockerfile ├── LICENSE ├── Makefile ├── PROJECT ├── README.md ├── api └── v1beta1 │ ├── groupversion_info.go │ ├── metallb_types.go │ ├── metallb_webhook.go │ ├── metallb_webhook_test.go │ └── zz_generated.deepcopy.go ├── bin └── metallb-operator.yaml ├── bindata └── deployment │ └── helm │ ├── frr-k8s │ ├── Chart.lock │ ├── Chart.yaml │ ├── README.md │ ├── templates │ │ ├── NOTES.txt │ │ ├── _helpers.tpl │ │ ├── controller.yaml │ │ ├── service-monitor.yaml │ │ └── webhooks.yaml │ ├── values.schema.json │ └── values.yaml │ └── metallb │ ├── Chart.lock │ ├── Chart.yaml │ ├── README.md │ ├── policy │ ├── controller.rego │ ├── rbac.rego │ └── speaker.rego │ ├── templates │ ├── NOTES.txt │ ├── _helpers.tpl │ ├── controller.yaml │ ├── deprecated_configInline.yaml │ ├── exclude-l2-config.yaml │ ├── podmonitor.yaml │ ├── prometheusrules.yaml │ ├── servicemonitor.yaml │ └── speaker.yaml │ ├── values.schema.json │ └── values.yaml ├── bundle.Dockerfile ├── bundle ├── manifests │ ├── frr-k8s-metrics-reader_rbac.authorization.k8s.io_v1_clusterrole.yaml │ ├── frrk8s.metallb.io_frrconfigurations.yaml │ ├── frrk8s.metallb.io_frrnodestates.yaml │ ├── metallb-manager-role_rbac.authorization.k8s.io_v1_role.yaml │ ├── metallb-manager-rolebinding_rbac.authorization.k8s.io_v1_rolebinding.yaml │ ├── metallb-operator-webhook-server-cert_v1_secret.yaml │ ├── metallb-operator-webhook-service_v1_service.yaml │ ├── metallb-operator.clusterserviceversion.yaml │ ├── metallb-webhook-cert_v1_secret.yaml │ ├── metallb-webhook-service_v1_service.yaml │ ├── metallb.io_bfdprofiles.yaml │ ├── metallb.io_bgpadvertisements.yaml │ ├── metallb.io_bgppeers.yaml │ ├── metallb.io_communities.yaml │ ├── metallb.io_ipaddresspools.yaml │ ├── metallb.io_l2advertisements.yaml │ ├── metallb.io_metallbs.yaml │ ├── metallb.io_servicebgpstatuses.yaml │ ├── metallb.io_servicel2statuses.yaml │ ├── speaker_v1_serviceaccount.yaml │ └── webhook-server-cert_v1_secret.yaml ├── metadata │ └── annotations.yaml └── tests │ └── scorecard │ └── config.yaml ├── config ├── crd │ ├── bases │ │ ├── frrk8s.metallb.io_frrconfigurations.yaml │ │ ├── frrk8s.metallb.io_frrnodestates.yaml │ │ ├── metallb.io_bfdprofiles.yaml │ │ ├── metallb.io_bgpadvertisements.yaml │ │ ├── metallb.io_bgppeers.yaml │ │ ├── metallb.io_communities.yaml │ │ ├── metallb.io_ipaddresspools.yaml │ │ ├── metallb.io_l2advertisements.yaml │ │ ├── metallb.io_metallbs.yaml │ │ ├── metallb.io_servicebgpstatuses.yaml │ │ └── metallb.io_servicel2statuses.yaml │ ├── kustomization.yaml │ ├── kustomizeconfig.yaml │ └── patches │ │ └── crd-conversion-patch-bgppeers.yaml ├── default │ ├── kustomization.yaml │ └── kustomizeconfig │ │ └── add-prefix.yaml ├── frr-webhook-prometheus │ ├── bgpTypeFRR.yaml │ ├── kustomization.yaml │ └── prometheus_sa.yaml ├── manager │ ├── env.yaml │ ├── kustomization.yaml │ └── manager.yaml ├── manifests │ ├── bases │ │ └── metallb-operator.clusterserviceversion.yaml │ ├── disable-cert-rotation.yaml │ └── kustomization.yaml ├── metallb_rbac │ ├── frrk8s_role_binding.yaml │ ├── kube_rbac_proxy.yaml │ ├── kustomization.yaml │ ├── metallb-openshift.yaml │ ├── metallb.yaml │ └── speaker_role_binding.yaml ├── olm-install │ ├── install-resources.yaml │ └── kustomization.yaml ├── openshift │ ├── custom-namespace-transformer.yaml │ ├── kustomization.yaml │ ├── patch-deployment-controller-manager.yaml │ ├── patch-namespace.yaml │ └── privileged-role-binding.yaml ├── rbac │ ├── kustomization.yaml │ ├── manager_service_account.yaml │ ├── role.yaml │ └── role_binding.yaml ├── samples │ ├── demo │ │ ├── metallb-advanced-config.yaml │ │ └── metallb.yaml │ ├── frr-k8s-basic.yaml │ ├── frr-k8s-status.yaml │ ├── frr-k8s-two-neighs.yaml │ ├── frr-k8s-two-routers-vrfs.yaml │ ├── kustomization.yaml │ ├── metallb.io_v1beta1_bfdprofile.yaml │ ├── metallb.io_v1beta1_bgppeer.yaml │ ├── metallb.io_v1beta1_ipaddresspool.yaml │ ├── metallb.io_v1beta2_bgppeer.yaml │ ├── metallb.yaml │ ├── metallb_frrk8s.yaml │ ├── metallb_v1beta1_bgpadvertisement.yaml │ ├── metallb_v1beta1_l2advertisement.yaml │ └── peer.yaml ├── scorecard │ ├── bases │ │ └── config.yaml │ ├── kustomization.yaml │ └── patches │ │ ├── basic.config.yaml │ │ └── olm.config.yaml └── webhook │ ├── backend │ ├── backend.yaml │ └── kustomization.yaml │ ├── kustomization.yaml │ ├── manifests.yaml │ ├── operator-service.yaml │ ├── secret.yaml │ ├── service.yaml │ └── webhook │ ├── kustomization.yaml │ ├── kustomizeconfig.yaml │ ├── manifests.yaml │ ├── patches │ ├── patch_webhook_configuration.yaml │ └── webhook_conversion.yaml │ ├── secret.yaml │ └── service.yaml ├── controllers ├── metallb_controller.go ├── metallb_controller_test.go └── suite_test.go ├── go.mod ├── go.sum ├── hack ├── boilerplate.go.txt ├── bump_metallb.sh ├── bump_versions.sh ├── common.sh ├── compare-gen-manifests.sh ├── deploy_prometheus.sh ├── fetch_latest_metallb.sh ├── gen_relnotes.sh ├── generate-metallb-manifests.sh ├── kind-multi-node-cluster-without-registry.sh ├── kind │ ├── config_vanilla.yaml │ ├── config_with_registry.yaml │ ├── kind.sh │ └── registry_configmap.yaml ├── kube-prometheus │ ├── README.md │ ├── build.sh │ ├── config.jsonnet │ ├── jsonnetfile.json │ ├── jsonnetfile.lock.json │ └── manifests │ │ ├── kube-prometheus-prometheusRule.yaml │ │ ├── kubernetes-prometheusRule.yaml │ │ ├── kubernetes-serviceMonitorApiserver.yaml │ │ ├── kubernetes-serviceMonitorCoreDNS.yaml │ │ ├── kubernetes-serviceMonitorKubeControllerManager.yaml │ │ ├── kubernetes-serviceMonitorKubeScheduler.yaml │ │ ├── kubernetes-serviceMonitorKubelet.yaml │ │ ├── prometheus-clusterRole.yaml │ │ ├── prometheus-clusterRoleBinding.yaml │ │ ├── prometheus-networkPolicy.yaml │ │ ├── prometheus-operator-prometheusRule.yaml │ │ ├── prometheus-operator-serviceMonitor.yaml │ │ ├── prometheus-podDisruptionBudget.yaml │ │ ├── prometheus-prometheus.yaml │ │ ├── prometheus-prometheusRule.yaml │ │ ├── prometheus-roleBindingConfig.yaml │ │ ├── prometheus-roleBindingSpecificNamespaces.yaml │ │ ├── prometheus-roleConfig.yaml │ │ ├── prometheus-roleSpecificNamespaces.yaml │ │ ├── prometheus-service.yaml │ │ ├── prometheus-serviceAccount.yaml │ │ ├── prometheus-serviceMonitor.yaml │ │ └── setup │ │ ├── 0namespace-namespace.yaml │ │ ├── prometheus-operator-0alertmanagerConfigCustomResourceDefinition.yaml │ │ ├── prometheus-operator-0alertmanagerCustomResourceDefinition.yaml │ │ ├── prometheus-operator-0podmonitorCustomResourceDefinition.yaml │ │ ├── prometheus-operator-0probeCustomResourceDefinition.yaml │ │ ├── prometheus-operator-0prometheusCustomResourceDefinition.yaml │ │ ├── prometheus-operator-0prometheusruleCustomResourceDefinition.yaml │ │ ├── prometheus-operator-0servicemonitorCustomResourceDefinition.yaml │ │ ├── prometheus-operator-0thanosrulerCustomResourceDefinition.yaml │ │ ├── prometheus-operator-clusterRole.yaml │ │ ├── prometheus-operator-clusterRoleBinding.yaml │ │ ├── prometheus-operator-deployment.yaml │ │ ├── prometheus-operator-networkPolicy.yaml │ │ ├── prometheus-operator-service.yaml │ │ └── prometheus-operator-serviceAccount.yaml ├── kube-rbac-controller.json ├── kube-rbac-frr.json ├── kube-rbac-speaker.json ├── lint.sh ├── metallb_ref.txt ├── metallb_version.txt ├── ocp-kustomize-overlay │ ├── controller-webhook-patch.yaml │ ├── crd-conversion-patch.yaml │ ├── crdcainjection-patch.yaml │ ├── kustomization.yaml │ ├── permission-patch.yaml │ ├── rbac.yaml │ ├── webhookcainjection-patch.yaml │ └── webhookservicecainjection_patch.yaml ├── openshiftapicrds │ ├── clusteroperators.yaml │ └── networks.yaml ├── operator_version.txt ├── securityContext.yaml ├── verify_generated.sh └── wait-for-csv.sh ├── main.go ├── pkg ├── apply │ ├── apply.go │ ├── merge.go │ └── merge_test.go ├── helm │ ├── config.go │ ├── config_test.go │ ├── frrk8s.go │ ├── frrk8s_test.go │ ├── metallb.go │ ├── metallb_test.go │ └── testdata │ │ ├── ocp-metrics-controller-monitor.golden │ │ ├── ocp-metrics-controller.golden │ │ ├── ocp-metrics-frr-k8s-daemon.golden │ │ ├── ocp-metrics-frr-k8s-monitor.golden │ │ ├── ocp-metrics-frr-k8s-webhook.golden │ │ ├── ocp-metrics-speaker-monitor.golden │ │ ├── ocp-metrics-speaker.golden │ │ ├── ocp-speaker.golden │ │ ├── vanilla-metrics-controller-monitor.golden │ │ ├── vanilla-metrics-speaker-monitor.golden │ │ └── vanilla-metrics-speaker.golden ├── openshift │ ├── openshift.go │ └── openshift_test.go ├── params │ ├── params.go │ └── params_test.go ├── platform │ ├── platform.go │ └── types.go └── status │ ├── status.go │ └── status_test.go └── test ├── README.md ├── consts └── const.go └── e2e ├── client └── client.go ├── functional ├── e2e_test.go └── tests │ └── e2e.go ├── k8sreporter └── reporter.go ├── metallb └── metallb.go ├── namespaces └── namespaces.go └── validation ├── tests └── validation.go └── validation_test.go /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: gomod 4 | directory: / 5 | schedule: 6 | interval: daily 7 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | 13 | 14 | **Is this a BUG FIX or a FEATURE ?**: 15 | 16 | > Uncomment only one, leave it on its own line: 17 | > 18 | > /kind bug 19 | > /kind cleanup 20 | > /kind feature 21 | > /kind design 22 | > /kind flake 23 | > /kind failing 24 | > /kind documentation 25 | > /kind regression 26 | 27 | **What this PR does / why we need it**: 28 | 29 | **Special notes for your reviewer**: 30 | 31 | **Release note**: 32 | 37 | 38 | ```release-note 39 | 40 | ``` 41 | -------------------------------------------------------------------------------- /.github/workflows/bump_metallb.yml: -------------------------------------------------------------------------------- 1 | # Note: If this is failing to create pull requests, it can be triaged 2 | # by inspecting the workflow run under the actions tab in github. 3 | 4 | name: Periodic MetalLB Bump 5 | on: 6 | schedule: 7 | # every day at 7am & 7pm (GMT+2) 8 | - cron: "0 5,17 * * *" 9 | jobs: 10 | bump-metallb-and-create-pr: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - name: Checkout Latest Metal LB Operator 14 | uses: actions/checkout@v4 15 | with: 16 | path: metallboperator 17 | - name: Checkout repository 18 | uses: actions/checkout@v4 19 | with: 20 | repository: metallb/metallb 21 | path: metallb 22 | fetch-depth: 0 23 | - name: Get current metallb commit hash 24 | id: old-metallb 25 | run: | 26 | echo "commit_sha=$(cat metallboperator/hack/metallb_ref.txt)" >> $GITHUB_OUTPUT 27 | - name: Get old commit info 28 | id: old-commit-info 29 | run: | 30 | cd metallb 31 | echo "commit_title=$(git log -1 --pretty=%s ${{ steps.old-metallb.outputs.commit_sha }})" >> $GITHUB_OUTPUT 32 | echo "commit_date=$(git log -n1 --pretty='format:%cd' --date=format:'%Y-%m-%d' ${{ steps.old-metallb.outputs.commit_sha }})" >> $GITHUB_OUTPUT 33 | - name: Get new commit info 34 | id: new-commit-info 35 | run: | 36 | cd metallb 37 | echo "commit_sha=$(git rev-parse HEAD)" >> $GITHUB_OUTPUT 38 | echo "commit_title=$(git log -1 --pretty=%s HEAD)" >> $GITHUB_OUTPUT 39 | echo "commit_date=$(git log -n1 --pretty='format:%cd' --date=format:'%Y-%m-%d' HEAD)" >> $GITHUB_OUTPUT 40 | - name: Check if there are changes 41 | id: check-for-changes 42 | run: | 43 | if [[ ${{ steps.old-metallb.outputs.commit_sha }} == ${{ steps.new-commit-info.outputs.commit_sha }} ]]; then 44 | echo "has_changes=false" >> $GITHUB_OUTPUT 45 | else 46 | echo "has_changes=true" >> $GITHUB_OUTPUT 47 | fi 48 | - uses: actions/checkout@v4 49 | with: 50 | token: ${{ secrets.GITHUB_TOKEN }} 51 | - name: Bump metallb 52 | if: ${{ steps.check-for-changes.outputs.has_changes == 'true' }} 53 | run: make bump_metallb 54 | - uses: tibdex/github-app-token@v1 55 | id: generate-token 56 | with: 57 | app_id: ${{ secrets.AUTO_BUMP_APP_ID }} 58 | private_key: ${{ secrets.AUTO_BUMP_APP_PRIVATE_KEY }} 59 | - name: Create pull request 60 | if: ${{ steps.check-for-changes.outputs.has_changes == 'true'}} 61 | uses: peter-evans/create-pull-request@v5 62 | with: 63 | token: ${{ steps.generate-token.outputs.token }} 64 | commit-message: | 65 | Bump MetalLB 66 | 67 | This commit bumps MetalLB: 68 | from: ${{ steps.old-metallb.outputs.commit_sha }} 69 | ${{ steps.old-commit-info.outputs.commit_title }} (${{ steps.old-commit-info.outputs.commit_date }}) 70 | 71 | to: ${{ steps.new-commit-info.outputs.commit_sha }} 72 | ${{ steps.new-commit-info.outputs.commit_title }} (${{ steps.new-commit-info.outputs.commit_date }}) 73 | author: github-actions[bot] 74 | committer: github-actions[bot] 75 | signoff: true 76 | title: "Bump MetalLB" 77 | branch: "bumpmetallb" 78 | delete-branch: true 79 | base: "main" 80 | -------------------------------------------------------------------------------- /.github/workflows/classify.yaml: -------------------------------------------------------------------------------- 1 | name: Add Labels 2 | 3 | on: 4 | pull_request_target: 5 | types: [opened, edited] 6 | 7 | jobs: 8 | add_labels: 9 | runs-on: ubuntu-latest 10 | if: github.actor != 'dependabot[bot]' 11 | steps: 12 | - name: checkout 13 | uses: actions/checkout@v4 14 | 15 | - uses: actions-ecosystem/action-remove-labels@v1 16 | with: 17 | labels: | 18 | kind/api-change 19 | kind/bug 20 | kind/cleanup 21 | kind/deprecation 22 | kind/design 23 | kind/documentation 24 | kind/failing 25 | kind/feature 26 | kind/flake 27 | kind/regression 28 | 29 | - uses: actions-ecosystem/action-regex-match@v2 30 | id: regex-match 31 | with: 32 | text: ${{ github.event.pull_request.body }} 33 | regex: '(? )\/kind (\w+)' 34 | 35 | - name: Check 36 | run: | 37 | if [[ ! "${{ steps.regex-match.outputs.group1 }}" =~ ^(api-change|bug|cleanup|deprecation|design|documentation|failing|feature|flake|regression)$ ]]; then 38 | echo "kind must belong to 39 | - api-change \ 40 | - bug \ 41 | - cleanup \ 42 | - deprecation \ 43 | - design \ 44 | - documentation \ 45 | - failing \ 46 | - feature \ 47 | - flake \ 48 | - regression \ 49 | please add /kind [type] to the body of the PR" 50 | exit 1 51 | fi 52 | - uses: actions-ecosystem/action-add-labels@v1 53 | with: 54 | labels: kind/${{ steps.regex-match.outputs.group1 }} 55 | 56 | - name: Check release notes were not deleted 57 | uses: actions/github-script@v7 58 | with: 59 | script: | 60 | const pr = await github.rest.pulls.get({ 61 | owner: context.repo.owner, 62 | repo: context.repo.repo, 63 | pull_number: context.issue.number 64 | }); 65 | if (!pr.data.body.includes("```release-note")) { 66 | throw new Error("Please don't cancel the ```release-note ``` section as it is used to build the release notes"); 67 | } 68 | -------------------------------------------------------------------------------- /.github/workflows/composite/collectlogs/action.yaml: -------------------------------------------------------------------------------- 1 | name: "collectlogs" 2 | description: "Collect and upload the kind logs" 3 | 4 | inputs: 5 | artifact-name: 6 | description: "the name of artifacts to store" 7 | required: true 8 | 9 | runs: 10 | using: "composite" 11 | steps: 12 | - name: Archive E2E Tests logs 13 | uses: actions/upload-artifact@v4 14 | with: 15 | name: ${{ inputs.artifact-name }}-logs 16 | path: /tmp/kind_logs 17 | 18 | - name: Export kind logs 19 | shell: bash 20 | run: | 21 | kind export logs /tmp/kind_logs 22 | 23 | - name: Change permissions for kind logs 24 | shell: bash 25 | run: | 26 | sudo chmod -R o+r /tmp/kind_logs 27 | 28 | - name: Archive kind logs 29 | uses: actions/upload-artifact@v4 30 | with: 31 | name: ${{ inputs.artifact-name }}-kind-logs 32 | path: /tmp/kind_logs 33 | -------------------------------------------------------------------------------- /.github/workflows/composite/setup/action.yaml: -------------------------------------------------------------------------------- 1 | name: "install-deps" 2 | description: "Install deps required for metallb-operator CI" 3 | 4 | runs: 5 | using: "composite" 6 | steps: 7 | - name: Checkout Metal LB Operator 8 | uses: actions/checkout@v4 9 | with: 10 | path: metallboperator 11 | fetch-depth: 0 # Fetch all history for all tags and branches 12 | 13 | - uses: actions/setup-go@v5 14 | with: 15 | go-version-file: "go.mod" 16 | cache: true 17 | 18 | - name: Verify modules 19 | shell: bash 20 | run: | 21 | go mod verify 22 | 23 | - name: Verify format 24 | shell: bash 25 | run: | 26 | make fmt 27 | git diff --exit-code 28 | -------------------------------------------------------------------------------- /.github/workflows/verify_version.yaml: -------------------------------------------------------------------------------- 1 | name: Verify Release 2 | 3 | on: 4 | pull_request: 5 | branches: 6 | - main 7 | - "v[0-9]+.[0-9]+.[0-9]+" 8 | - "v[0-9]+.[0-9]+" 9 | tags: 10 | - "v[0-9]+.[0-9]+.[0-9]+" 11 | - "v[0-9]+.[0-9]+" 12 | jobs: 13 | verify_branch: 14 | runs-on: ubuntu-latest 15 | steps: 16 | - name: Checkout code 17 | uses: actions/checkout@v4 18 | with: 19 | fetch-depth: 0 20 | - name: Verify Version Bump 21 | run: | 22 | make bump_versions 23 | make check_generated 24 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Temporary Build Files 2 | _cache 3 | testbin/ 4 | 5 | # Binaries for programs and plugins 6 | *.exe 7 | *.exe~ 8 | *.dll 9 | *.so 10 | *.dylib 11 | 12 | # Test binary, built with `go test -c` 13 | *.test 14 | 15 | # Output of the go coverage tool, specifically when used with LiteIDE 16 | *.out 17 | 18 | # Dependency directories (remove the comment below to include it) 19 | # vendor/ 20 | 21 | # Ignore .idea files 22 | .idea/ 23 | *.swp 24 | -------------------------------------------------------------------------------- /CODEOWNERS: -------------------------------------------------------------------------------- 1 | # For more information on the syntax of the CODEOWNERS file, see: 2 | # https://docs.github.com/en/github/creating-cloning-and-archiving-repositories/about-code-owners 3 | 4 | # The MetalLB maintainers team 5 | * @fedepaol @gclawes @oribon 6 | 7 | # Emeritus Maintainers 8 | # 9 | # The following people were previously members of the maintainers team, but are 10 | # not currently focused on the project. 11 | # 12 | # - danderson # Creator of MetalLB 13 | # - daxmc99 14 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # syntax=docker/dockerfile:1.2 2 | 3 | FROM --platform=$BUILDPLATFORM docker.io/golang:1.22.12 AS builder 4 | ARG GIT_COMMIT=dev 5 | ARG GIT_BRANCH=dev 6 | 7 | WORKDIR /workspace 8 | # Copy the Go Modules manifests 9 | COPY go.mod go.mod 10 | COPY go.sum go.sum 11 | # cache deps before building and copying source so that we don't need to re-download as much 12 | # and so that source changes don't invalidate our downloaded layer 13 | RUN go mod download 14 | 15 | # Copy the go source 16 | COPY main.go main.go 17 | COPY api/ api/ 18 | COPY controllers/ controllers/ 19 | COPY pkg/ pkg/ 20 | COPY bindata/deployment/ bindata/deployment/ 21 | COPY .git/ .git/ 22 | COPY Makefile Makefile 23 | 24 | ARG TARGETARCH 25 | ARG TARGETOS 26 | ARG TARGETPLATFORM 27 | 28 | RUN case ${TARGETPLATFORM} in \ 29 | "linux/arm/v6") export VARIANT="6" ;; \ 30 | "linux/arm/v7") export VARIANT="7" ;; \ 31 | *) export VARIANT="" ;; \ 32 | esac 33 | 34 | # Cache builds directory for faster rebuild 35 | RUN --mount=type=cache,target=/root/.cache/go-build \ 36 | --mount=type=cache,target=/go/pkg \ 37 | CGO_ENABLED=0 GOOS=$TARGETOS GOARCH=$TARGETARCH GOARM=$VARIANT \ 38 | go build -v -o /build/configmaptocrs \ 39 | -ldflags "-X 'main.build==${GIT_COMMIT}'" \ 40 | -o manager main.go 41 | 42 | 43 | # Use distroless as minimal base image to package the manager binary 44 | # Refer to https://github.com/GoogleContainerTools/distroless for more details 45 | FROM gcr.io/distroless/static:nonroot 46 | WORKDIR / 47 | 48 | COPY LICENSE / 49 | COPY --from=builder /workspace/manager . 50 | COPY --from=builder /workspace/bindata/deployment /bindata/deployment 51 | 52 | LABEL org.opencontainers.image.authors="metallb" \ 53 | org.opencontainers.image.url="https://github.com/metallb/metallb-operator" \ 54 | org.opencontainers.image.documentation="https://metallb.universe.tf" \ 55 | org.opencontainers.image.source="https://github.com/metallb/metallb-operator" \ 56 | org.opencontainers.image.vendor="metallb" \ 57 | org.opencontainers.image.licenses="Apache-2.0" \ 58 | org.opencontainers.image.description="Metallb Operator" \ 59 | org.opencontainers.image.title="metallb operator" \ 60 | org.opencontainers.image.base.name="gcr.io/distroless/static:nonroot" 61 | 62 | USER nonroot:nonroot 63 | 64 | ENTRYPOINT ["/manager"] 65 | -------------------------------------------------------------------------------- /PROJECT: -------------------------------------------------------------------------------- 1 | domain: metallb.io 2 | layout: 3 | - go.kubebuilder.io/v4 4 | plugins: 5 | manifests.sdk.operatorframework.io/v2: {} 6 | scorecard.sdk.operatorframework.io/v2: {} 7 | projectName: metallb-operator 8 | repo: github.com/metallb/metallb-operator 9 | resources: 10 | - api: 11 | crdVersion: v1 12 | namespaced: true 13 | controller: true 14 | domain: metallb.io 15 | group: metallb.io 16 | kind: MetalLB 17 | path: metallb.io/api/v1beta1 18 | version: v1beta1 19 | version: "3" 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # MetalLB Operator 2 | 3 | This is the official MetalLB Operator, implementing the [operator pattern](https://kubernetes.io/docs/concepts/extend-kubernetes/operator/) 4 | for deploying MetalLB on a kubernetes cluster, as described in the [related design proposal](https://github.com/metallb/metallb/blob/main/design/metallb-operator.md). 5 | 6 | ## Quick Setup 7 | 8 | To install the MetalLB Operator using the prebuilt manifests, run the following: 9 | ```shell 10 | kubectl apply -f bin/metallb-operator.yaml 11 | ``` 12 | 13 | ## Installation 14 | 15 | To install the MetalLB Operator using a prebuilt image, run the following: 16 | ```shell 17 | make deploy 18 | ``` 19 | 20 | ## Installation on OpenShift 21 | 22 | To install the MetalLB Operator using a prebuilt image on OpenShift, run the following: 23 | ```shell 24 | make deploy-openshift 25 | ``` 26 | > **Note:** This requires kustomize 4.5.6 or above. 27 | 28 | > **Note:** To undeploy on OpenShift, use the `undeploy-openshift` target. 29 | 30 | ## Usage 31 | 32 | Once the MetalLB Operator is installed, you have to create a `MetalLB` custom resource to deploy a MetalLB instance. The operator will consume this resource and create all required MetalLB resources based on it. The `MetalLB` custom resource needs to be created inside the `metallb-system` namespace and be named `metallb`. Only one `MetalLB` resource can exist in a cluster. 33 | 34 | Following is a sample `MetalLB` resource: 35 | 36 | ```yaml 37 | apiVersion: metallb.io/v1beta1 38 | kind: MetalLB 39 | metadata: 40 | name: metallb 41 | namespace: metallb-system 42 | ``` 43 | 44 | ## Setting up a development environment 45 | 46 | ### Quick local installation 47 | 48 | A quick, local installation can be done using a [kind](https://kind.sigs.k8s.io/) cluster and a local registry. Follow the steps below to run a locally-built metallb-operator on kind. 49 | 50 | ```shell 51 | make deploy 52 | ``` 53 | 54 | ### Create a MetalLB deployment 55 | 56 | To create a MetalLB deployment, a MetalLB Operator configuration resource needs to be created. Run the following command to create it: 57 | 58 | ```shell 59 | cat << EOF | kubectl apply -f - 60 | apiVersion: metallb.io/v1beta1 61 | kind: MetalLB 62 | metadata: 63 | name: metallb 64 | namespace: metallb-system 65 | EOF 66 | ``` 67 | 68 | ### Running tests 69 | 70 | To run metallb-operator unit tests (no cluster required), execute the following: 71 | 72 | ```shell 73 | make test 74 | ``` 75 | 76 | To run metallb-operator e2e tests, execute the following: 77 | 78 | ```shell 79 | make test-e2e 80 | ``` 81 | 82 | The e2e test need a running cluster with a MetalLB Operator deployed. 83 | 84 | ### Make 85 | 86 | Most tasks in the project are automated using a Makefile. 87 | Run `make help` to see the details. 88 | 89 | ## Releasing 90 | 91 | The Operator assumes the same branching structure as MetalLB. 92 | Each minor version must have a corresponding branch where we tag releases out. 93 | Versioned branches must be pinned to specific MetalLB / Operator images. 94 | 95 | The current version of the images is bumped under `hack/metallb_version.txt` and 96 | under `hack/operator_version.txt`. 97 | 98 | A convenience `make bump_versions` makefile target aligns the versions in the manifests to 99 | the content of those files. 100 | 101 | Another Makefile target `make fetch_metallb_version` updates `hack/metallb_version.txt` with the 102 | latest tag of metallb. 103 | 104 | In order to make a release, a tag must be made out of a release branch, pinning the relevant images. 105 | -------------------------------------------------------------------------------- /api/v1beta1/groupversion_info.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 v1beta1 contains API Schema definitions for the metallb v1beta1 API group 18 | // +kubebuilder:object:generate=true 19 | // +groupName=metallb.io 20 | package v1beta1 21 | 22 | import ( 23 | "k8s.io/apimachinery/pkg/runtime/schema" 24 | "sigs.k8s.io/controller-runtime/pkg/scheme" 25 | ) 26 | 27 | var ( 28 | // GroupVersion is group version used to register these objects 29 | GroupVersion = schema.GroupVersion{Group: "metallb.io", Version: "v1beta1"} 30 | 31 | // SchemeBuilder is used to add go types to the GroupVersionKind scheme 32 | SchemeBuilder = &scheme.Builder{GroupVersion: GroupVersion} 33 | 34 | // AddToScheme adds the types in this group-version to the given scheme. 35 | AddToScheme = SchemeBuilder.AddToScheme 36 | ) 37 | -------------------------------------------------------------------------------- /api/v1beta1/metallb_webhook_test.go: -------------------------------------------------------------------------------- 1 | package v1beta1 2 | 3 | import ( 4 | "errors" 5 | "testing" 6 | ) 7 | 8 | func TestValidateFRRK8sConfig(t *testing.T) { 9 | t.Run("NilConfig", func(t *testing.T) { 10 | err := validateFRRK8sConfig(MetalLBSpec{}) 11 | if err != nil { 12 | t.Errorf("Expected nil error, got: %v", err) 13 | } 14 | }) 15 | 16 | t.Run("ValidConfig", func(t *testing.T) { 17 | config := &FRRK8SConfig{ 18 | AlwaysBlock: []string{"192.168.0.0/24", "10.0.0.0/16"}, 19 | } 20 | err := validateFRRK8sConfig(MetalLBSpec{ 21 | FRRK8SConfig: config, 22 | }) 23 | if err != nil { 24 | t.Errorf("Expected nil error, got: %v", err) 25 | } 26 | }) 27 | 28 | t.Run("InvalidCIDR", func(t *testing.T) { 29 | config := &FRRK8SConfig{ 30 | AlwaysBlock: []string{"192.168.0.0/24", "invalid_cidr"}, 31 | } 32 | err := validateFRRK8sConfig(MetalLBSpec{ 33 | FRRK8SConfig: config, 34 | }) 35 | expectedErr := errors.New("invalid CIDR invalid_cidr in AlwaysBlock") 36 | if err == nil || err.Error() != expectedErr.Error() { 37 | t.Errorf("Expected error: %v, got: %v", expectedErr, err) 38 | } 39 | }) 40 | 41 | t.Run("ValidIPv6Config", func(t *testing.T) { 42 | config := &FRRK8SConfig{ 43 | AlwaysBlock: []string{"2001:db8::/32", "2001:db8:85a3::/48"}, 44 | } 45 | err := validateFRRK8sConfig(MetalLBSpec{ 46 | FRRK8SConfig: config, 47 | }) 48 | 49 | if err != nil { 50 | t.Errorf("Expected nil error, got: %v", err) 51 | } 52 | }) 53 | 54 | t.Run("External, no config, no default", func(t *testing.T) { 55 | oldNs := ExternalFRRK8sNamespace 56 | defer func() { ExternalFRRK8sNamespace = oldNs }() 57 | ExternalFRRK8sNamespace = "" 58 | err := validateFRRK8sConfig(MetalLBSpec{ 59 | BGPBackend: FRRK8sExternalMode, 60 | }) 61 | 62 | if err == nil { 63 | t.Errorf("Expected error, got no error") 64 | } 65 | }) 66 | 67 | t.Run("External, config, no ns, no default", func(t *testing.T) { 68 | oldNs := ExternalFRRK8sNamespace 69 | defer func() { ExternalFRRK8sNamespace = oldNs }() 70 | ExternalFRRK8sNamespace = "" 71 | err := validateFRRK8sConfig(MetalLBSpec{ 72 | BGPBackend: FRRK8sExternalMode, 73 | FRRK8SConfig: &FRRK8SConfig{}, 74 | }) 75 | 76 | if err == nil { 77 | t.Errorf("Expected error, got no error") 78 | } 79 | }) 80 | 81 | t.Run("External, default namespace", func(t *testing.T) { 82 | oldNs := ExternalFRRK8sNamespace 83 | defer func() { ExternalFRRK8sNamespace = oldNs }() 84 | ExternalFRRK8sNamespace = "foo" 85 | err := validateFRRK8sConfig(MetalLBSpec{ 86 | BGPBackend: FRRK8sExternalMode, 87 | }) 88 | 89 | if err != nil { 90 | t.Errorf("Expected no error, got: %v", err) 91 | } 92 | }) 93 | } 94 | -------------------------------------------------------------------------------- /bindata/deployment/helm/frr-k8s/Chart.lock: -------------------------------------------------------------------------------- 1 | dependencies: 2 | - name: crds 3 | repository: "" 4 | version: 0.0.17 5 | digest: sha256:8f233f695efb015d5aaad9bb1e8f8ee0b0b24c13356b15d40b96b40cd7eb2b25 6 | generated: "2025-01-10T10:23:08.228305485+01:00" 7 | -------------------------------------------------------------------------------- /bindata/deployment/helm/frr-k8s/Chart.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v2 2 | name: frr-k8s 3 | description: A cloud native wrapper of FRR 4 | home: https://metallb.universe.tf 5 | sources: 6 | - https://github.com/metallb/frr-k8s 7 | icon: https://metallb.universe.tf/images/logo/metallb-white.png 8 | # A chart can be either an 'application' or a 'library' chart. 9 | # 10 | # Application charts are a collection of templates that can be packaged into versioned archives 11 | # to be deployed. 12 | # 13 | # Library charts provide useful utilities or functions for the chart developer. They're included as 14 | # a dependency of application charts to inject those utilities and functions into the rendering 15 | # pipeline. Library charts do not define any templates and therefore cannot be deployed. 16 | type: application 17 | # The optional kubeVersion field can define semver constraints on supported Kubernetes versions. 18 | # Helm will validate the version constraints when installing the chart and fail if the cluster 19 | # runs an unsupported Kubernetes version. 20 | kubeVersion: ">= 1.19.0-0" 21 | # This is the chart version. This version number should be incremented each time you make changes 22 | # to the chart and its templates, including the app version. 23 | # Versions are expected to follow Semantic Versioning (https://semver.org/) 24 | # NOTE: this value is updated by the frrk8s release process 25 | version: 0.0.17 26 | # This is the version number of the application being deployed. This version number should be 27 | # incremented each time you make changes to the application. Versions are not expected to 28 | # follow Semantic Versioning. They should reflect the version the application is using. 29 | # NOTE: this value is updated by the frrk8s release process 30 | appVersion: v0.0.17 31 | -------------------------------------------------------------------------------- /bindata/deployment/helm/frr-k8s/templates/NOTES.txt: -------------------------------------------------------------------------------- 1 | FRR-k8s is now running in the cluster. 2 | 3 | Now you can configure it via its CRs. Please refer to the frr-k8s official docs 4 | on how to use the CRs. 5 | -------------------------------------------------------------------------------- /bindata/deployment/helm/frr-k8s/templates/_helpers.tpl: -------------------------------------------------------------------------------- 1 | {{/* vim: set filetype=mustache: */}} 2 | {{/* 3 | Expand the name of the chart. 4 | */}} 5 | {{- define "frrk8s.name" -}} 6 | {{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} 7 | {{- end }} 8 | 9 | {{/* 10 | Create a default fully qualified app name. 11 | We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). 12 | If release name contains chart name it will be used as a full name. 13 | */}} 14 | {{- define "frrk8s.fullname" -}} 15 | {{- if .Values.fullnameOverride }} 16 | {{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} 17 | {{- else }} 18 | {{- $name := default .Chart.Name .Values.nameOverride }} 19 | {{- if contains $name .Release.Name }} 20 | {{- .Release.Name | trunc 63 | trimSuffix "-" }} 21 | {{- else }} 22 | {{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} 23 | {{- end }} 24 | {{- end }} 25 | {{- end }} 26 | 27 | {{/* 28 | Create chart name and version as used by the chart label. 29 | */}} 30 | {{- define "frrk8s.chart" -}} 31 | {{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} 32 | {{- end }} 33 | 34 | {{/* 35 | Common labels 36 | */}} 37 | {{- define "frrk8s.labels" -}} 38 | helm.sh/chart: {{ include "frrk8s.chart" . }} 39 | {{ include "frrk8s.selectorLabels" . }} 40 | {{- if .Chart.AppVersion }} 41 | app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} 42 | {{- end }} 43 | app.kubernetes.io/managed-by: {{ .Release.Service }} 44 | {{- end }} 45 | 46 | {{/* 47 | Selector labels 48 | */}} 49 | {{- define "frrk8s.selectorLabels" -}} 50 | app: {{ include "frrk8s.name" . }} 51 | {{- end }} 52 | 53 | {{/* 54 | Create the name of the frrk8s service account to use 55 | */}} 56 | {{- define "frrk8s.serviceAccountName" -}} 57 | {{- if .Values.frrk8s.serviceAccount.create }} 58 | {{- default (printf "%s-controller" (include "frrk8s.fullname" .)) .Values.frrk8s.serviceAccount.name }} 59 | {{- else }} 60 | {{- default "default" .Values.frrk8s.serviceAccount.name }} 61 | {{- end }} 62 | {{- end }} 63 | -------------------------------------------------------------------------------- /bindata/deployment/helm/metallb/Chart.lock: -------------------------------------------------------------------------------- 1 | dependencies: 2 | - name: crds 3 | repository: "" 4 | version: 0.0.0 5 | - name: frr-k8s 6 | repository: https://metallb.github.io/frr-k8s 7 | version: 0.0.17 8 | digest: sha256:1e128afb9157c71a2217e88694612ba074fdd2168366d1747b36095d741e4808 9 | generated: "2025-01-10T13:24:27.191428406+01:00" 10 | -------------------------------------------------------------------------------- /bindata/deployment/helm/metallb/Chart.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v2 2 | name: metallb 3 | description: A network load-balancer implementation for Kubernetes using standard routing protocols 4 | home: https://metallb.universe.tf 5 | sources: 6 | - https://github.com/metallb/metallb 7 | icon: https://metallb.universe.tf/images/logo/metallb-white.png 8 | # A chart can be either an 'application' or a 'library' chart. 9 | # 10 | # Application charts are a collection of templates that can be packaged into versioned archives 11 | # to be deployed. 12 | # 13 | # Library charts provide useful utilities or functions for the chart developer. They're included as 14 | # a dependency of application charts to inject those utilities and functions into the rendering 15 | # pipeline. Library charts do not define any templates and therefore cannot be deployed. 16 | type: application 17 | # The optional kubeVersion field can define semver constraints on supported Kubernetes versions. 18 | # Helm will validate the version constraints when installing the chart and fail if the cluster 19 | # runs an unsupported Kubernetes version. 20 | kubeVersion: ">= 1.19.0-0" 21 | # This is the chart version. This version number should be incremented each time you make changes 22 | # to the chart and its templates, including the app version. 23 | # Versions are expected to follow Semantic Versioning (https://semver.org/) 24 | # NOTE: this value is updated by the metallb release process 25 | # MetalLB chart version 26 | version: 0.0.0 27 | # This is the version number of the application being deployed. This version number should be 28 | # incremented each time you make changes to the application. Versions are not expected to 29 | # follow Semantic Versioning. They should reflect the version the application is using. 30 | # NOTE: this value is updated by the metallb release process 31 | appVersion: v0.0.0 32 | -------------------------------------------------------------------------------- /bindata/deployment/helm/metallb/policy/controller.rego: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | # validate serviceAccountName 4 | deny[msg] { 5 | input.kind == "Deployment" 6 | serviceAccountName := input.spec.template.spec.serviceAccountName 7 | not serviceAccountName == "release-name-metallb-controller" 8 | msg = sprintf("controller serviceAccountName '%s' does not match expected value", [serviceAccountName]) 9 | } 10 | 11 | # validate node selector includes builtin when custom ones are provided 12 | deny[msg] { 13 | input.kind == "Deployment" 14 | not input.spec.template.spec.nodeSelector["kubernetes.io/os"] == "linux" 15 | msg = "controller nodeSelector does not include '\"kubernetes.io/os\": linux'" 16 | } 17 | -------------------------------------------------------------------------------- /bindata/deployment/helm/metallb/policy/rbac.rego: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | # Validate PSP exists in ClusterRole :controller 4 | deny[msg] { 5 | input.kind == "ClusterRole" 6 | input.metadata.name == "metallb:controller" 7 | input.rules[3] == { 8 | "apiGroups": ["policy"], 9 | "resources": ["podsecuritypolicies"], 10 | "resourceNames": ["metallb-controller"], 11 | "verbs": ["use"] 12 | } 13 | msg = "ClusterRole metallb:controller does not include PSP rule" 14 | } 15 | 16 | # Validate PSP exists in ClusterRole :speaker 17 | deny[msg] { 18 | input.kind == "ClusterRole" 19 | input.metadata.name == "metallb:speaker" 20 | input.rules[3] == { 21 | "apiGroups": ["policy"], 22 | "resources": ["podsecuritypolicies"], 23 | "resourceNames": ["metallb-controller"], 24 | "verbs": ["use"] 25 | } 26 | msg = "ClusterRole metallb:speaker does not include PSP rule" 27 | } 28 | -------------------------------------------------------------------------------- /bindata/deployment/helm/metallb/policy/speaker.rego: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | # validate serviceAccountName 4 | deny[msg] { 5 | input.kind == "DaemonSet" 6 | serviceAccountName := input.spec.template.spec.serviceAccountName 7 | not serviceAccountName == "release-name-metallb-speaker" 8 | msg = sprintf("speaker serviceAccountName '%s' does not match expected value", [serviceAccountName]) 9 | } 10 | 11 | # validate METALLB_ML_SECRET_KEY (memberlist) 12 | deny[msg] { 13 | input.kind == "DaemonSet" 14 | not input.spec.template.spec.containers[0].env[5].name == "METALLB_ML_SECRET_KEY_PATH" 15 | msg = "speaker env does not contain METALLB_ML_SECRET_KEY_PATH at env[5]" 16 | } 17 | 18 | # validate node selector includes builtin when custom ones are provided 19 | deny[msg] { 20 | input.kind == "DaemonSet" 21 | not input.spec.template.spec.nodeSelector["kubernetes.io/os"] == "linux" 22 | msg = "controller nodeSelector does not include '\"kubernetes.io/os\": linux'" 23 | } 24 | 25 | # validate tolerations include the builtins when custom ones are provided 26 | deny[msg] { 27 | input.kind == "DaemonSet" 28 | not input.spec.template.spec.tolerations[0] == { "key": "node-role.kubernetes.io/master", "effect": "NoSchedule", "operator": "Exists" } 29 | msg = "controller tolerations does not include node-role.kubernetes.io/master:NoSchedule" 30 | } 31 | -------------------------------------------------------------------------------- /bindata/deployment/helm/metallb/templates/NOTES.txt: -------------------------------------------------------------------------------- 1 | MetalLB is now running in the cluster. 2 | 3 | Now you can configure it via its CRs. Please refer to the metallb official docs 4 | on how to use the CRs. 5 | -------------------------------------------------------------------------------- /bindata/deployment/helm/metallb/templates/_helpers.tpl: -------------------------------------------------------------------------------- 1 | {{/* vim: set filetype=mustache: */}} 2 | {{/* 3 | Expand the name of the chart. 4 | */}} 5 | {{- define "metallb.name" -}} 6 | {{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} 7 | {{- end }} 8 | 9 | {{/* 10 | Create a default fully qualified app name. 11 | We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). 12 | If release name contains chart name it will be used as a full name. 13 | */}} 14 | {{- define "metallb.fullname" -}} 15 | {{- if .Values.fullnameOverride }} 16 | {{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} 17 | {{- else }} 18 | {{- $name := default .Chart.Name .Values.nameOverride }} 19 | {{- if contains $name .Release.Name }} 20 | {{- .Release.Name | trunc 63 | trimSuffix "-" }} 21 | {{- else }} 22 | {{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} 23 | {{- end }} 24 | {{- end }} 25 | {{- end }} 26 | 27 | {{/* 28 | Create chart name and version as used by the chart label. 29 | */}} 30 | {{- define "metallb.chart" -}} 31 | {{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} 32 | {{- end }} 33 | 34 | {{/* 35 | Common labels 36 | */}} 37 | {{- define "metallb.labels" -}} 38 | helm.sh/chart: {{ include "metallb.chart" . }} 39 | {{ include "metallb.selectorLabels" . }} 40 | {{- if .Chart.AppVersion }} 41 | app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} 42 | {{- end }} 43 | app.kubernetes.io/managed-by: {{ .Release.Service }} 44 | {{- end }} 45 | 46 | {{/* 47 | Selector labels 48 | */}} 49 | {{- define "metallb.selectorLabels" -}} 50 | app: {{ include "metallb.name" . }} 51 | {{- end }} 52 | 53 | {{/* 54 | Create the name of the controller service account to use 55 | */}} 56 | {{- define "metallb.controller.serviceAccountName" -}} 57 | {{- if .Values.controller.serviceAccount.create }} 58 | {{- default (printf "%s-controller" (include "metallb.fullname" .)) .Values.controller.serviceAccount.name }} 59 | {{- else }} 60 | {{- default "default" .Values.controller.serviceAccount.name }} 61 | {{- end }} 62 | {{- end }} 63 | 64 | {{/* 65 | Create the name of the speaker service account to use 66 | */}} 67 | {{- define "metallb.speaker.serviceAccountName" -}} 68 | {{- if .Values.speaker.serviceAccount.create }} 69 | {{- default (printf "%s-speaker" (include "metallb.fullname" .)) .Values.speaker.serviceAccount.name }} 70 | {{- else }} 71 | {{- default "default" .Values.speaker.serviceAccount.name }} 72 | {{- end }} 73 | {{- end }} 74 | 75 | {{/* 76 | Create the name of the settings Secret to use. 77 | */}} 78 | {{- define "metallb.secretName" -}} 79 | {{ default ( printf "%s-memberlist" (include "metallb.fullname" .)) .Values.speaker.secretName | trunc 63 | trimSuffix "-" }} 80 | {{- end -}} 81 | 82 | {{- define "metrics.exposedportname" -}} 83 | {{- if .Values.prometheus.secureMetricsPort -}} 84 | "metricshttps" 85 | {{- else -}} 86 | "metrics" 87 | {{- end -}} 88 | {{- end -}} 89 | 90 | {{- define "metrics.exposedfrrportname" -}} 91 | {{- if .Values.speaker.frr.secureMetricsPort -}} 92 | "frrmetricshttps" 93 | {{- else -}} 94 | "frrmetrics" 95 | {{- end }} 96 | {{- end }} 97 | 98 | {{- define "metrics.exposedport" -}} 99 | {{- if .Values.prometheus.secureMetricsPort -}} 100 | {{ .Values.prometheus.secureMetricsPort }} 101 | {{- else -}} 102 | {{ .Values.prometheus.metricsPort }} 103 | {{- end -}} 104 | {{- end }} 105 | 106 | {{- define "metrics.exposedfrrport" -}} 107 | {{- if .Values.speaker.frr.secureMetricsPort -}} 108 | {{ .Values.speaker.frr.secureMetricsPort }} 109 | {{- else -}} 110 | {{ .Values.speaker.frr.metricsPort }} 111 | {{- end }} 112 | {{- end }} 113 | 114 | -------------------------------------------------------------------------------- /bindata/deployment/helm/metallb/templates/deprecated_configInline.yaml: -------------------------------------------------------------------------------- 1 | {{- if .Values.configInline }} 2 | {{- fail "Starting from v0.13.0 configInline is no longer supported. Please see https://metallb.universe.tf/#backward-compatibility" }} 3 | {{- end }} 4 | -------------------------------------------------------------------------------- /bindata/deployment/helm/metallb/templates/exclude-l2-config.yaml: -------------------------------------------------------------------------------- 1 | {{- if and .Values.speaker.enabled .Values.speaker.excludeInterfaces.enabled }} 2 | apiVersion: v1 3 | kind: ConfigMap 4 | metadata: 5 | name: metallb-excludel2 6 | namespace: {{ .Release.Namespace | quote }} 7 | labels: 8 | {{- include "metallb.labels" . | nindent 4 }} 9 | data: 10 | excludel2.yaml: | 11 | announcedInterfacesToExclude: 12 | - ^docker.* 13 | - ^cbr.* 14 | - ^dummy.* 15 | - ^virbr.* 16 | - ^lxcbr.* 17 | - ^veth.* 18 | - ^lo$ 19 | - ^cali.* 20 | - ^tunl.* 21 | - ^flannel.* 22 | - ^kube-ipvs.* 23 | - ^cni.* 24 | - ^nodelocaldns.* 25 | {{- end }} -------------------------------------------------------------------------------- /bindata/deployment/helm/metallb/templates/podmonitor.yaml: -------------------------------------------------------------------------------- 1 | {{- if .Values.prometheus.podMonitor.enabled }} 2 | apiVersion: monitoring.coreos.com/v1 3 | kind: PodMonitor 4 | metadata: 5 | name: controller 6 | labels: 7 | {{- include "metallb.labels" . | nindent 4 }} 8 | app.kubernetes.io/component: controller 9 | {{- if .Values.prometheus.podMonitor.additionalLabels }} 10 | {{ toYaml .Values.prometheus.podMonitor.additionalLabels | indent 4 }} 11 | {{- end }} 12 | {{- if .Values.prometheus.podMonitor.annotations }} 13 | annotations: 14 | {{ toYaml .Values.prometheus.podMonitor.annotations | indent 4 }} 15 | {{- end }} 16 | spec: 17 | jobLabel: {{ .Values.prometheus.podMonitor.jobLabel | quote }} 18 | selector: 19 | matchLabels: 20 | {{- include "metallb.selectorLabels" . | nindent 6 }} 21 | app.kubernetes.io/component: controller 22 | namespaceSelector: 23 | matchNames: 24 | - {{ .Release.Namespace }} 25 | podMetricsEndpoints: 26 | - port: monitoring 27 | path: /metrics 28 | {{- if .Values.prometheus.podMonitor.interval }} 29 | interval: {{ .Values.prometheus.podMonitor.interval }} 30 | {{- end }} 31 | {{- if .Values.prometheus.podMonitor.metricRelabelings }} 32 | metricRelabelings: 33 | {{- toYaml .Values.prometheus.podMonitor.metricRelabelings | nindent 4 }} 34 | {{- end }} 35 | {{- if .Values.prometheus.podMonitor.relabelings }} 36 | relabelings: 37 | {{- toYaml .Values.prometheus.podMonitor.relabelings | nindent 4 }} 38 | {{- end }} 39 | {{- if .Values.speaker.enabled }} 40 | --- 41 | apiVersion: monitoring.coreos.com/v1 42 | kind: PodMonitor 43 | metadata: 44 | name: speaker 45 | labels: 46 | {{- include "metallb.labels" . | nindent 4 }} 47 | app.kubernetes.io/component: speaker 48 | {{- if .Values.prometheus.podMonitor.additionalLabels }} 49 | {{ toYaml .Values.prometheus.podMonitor.additionalLabels | indent 4 }} 50 | {{- end }} 51 | {{- if .Values.prometheus.podMonitor.annotations }} 52 | annotations: 53 | {{ toYaml .Values.prometheus.podMonitor.annotations | indent 4 }} 54 | {{- end }} 55 | spec: 56 | jobLabel: {{ .Values.prometheus.podMonitor.jobLabel | quote }} 57 | selector: 58 | matchLabels: 59 | {{- include "metallb.selectorLabels" . | nindent 6 }} 60 | app.kubernetes.io/component: speaker 61 | namespaceSelector: 62 | matchNames: 63 | - {{ .Release.Namespace }} 64 | podMetricsEndpoints: 65 | - port: monitoring 66 | path: /metrics 67 | {{- if .Values.prometheus.podMonitor.interval }} 68 | interval: {{ .Values.prometheus.podMonitor.interval }} 69 | {{- end }} 70 | {{- if .Values.prometheus.podMonitor.metricRelabelings }} 71 | metricRelabelings: 72 | {{- toYaml .Values.prometheus.podMonitor.metricRelabelings | nindent 4 }} 73 | {{- end }} 74 | {{- if .Values.prometheus.podMonitor.relabelings }} 75 | relabelings: 76 | {{- toYaml .Values.prometheus.podMonitor.relabelings | nindent 4 }} 77 | {{- end }} 78 | {{- end }} 79 | --- 80 | {{- if .Values.prometheus.rbacPrometheus }} 81 | apiVersion: rbac.authorization.k8s.io/v1 82 | kind: Role 83 | metadata: 84 | name: prometheus 85 | rules: 86 | - apiGroups: 87 | - "" 88 | resources: 89 | - pods 90 | verbs: 91 | - get 92 | - list 93 | - watch 94 | --- 95 | apiVersion: rbac.authorization.k8s.io/v1 96 | kind: RoleBinding 97 | metadata: 98 | name: prometheus 99 | roleRef: 100 | apiGroup: rbac.authorization.k8s.io 101 | kind: Role 102 | name: prometheus 103 | subjects: 104 | - kind: ServiceAccount 105 | name: {{ required ".Values.prometheus.serviceAccount must be defined when .Values.prometheus.podMonitor.enabled == true" .Values.prometheus.serviceAccount }} 106 | namespace: {{ required ".Values.prometheus.namespace must be defined when .Values.prometheus.podMonitor.enabled == true" .Values.prometheus.namespace }} 107 | {{- end }} 108 | {{- end }} 109 | -------------------------------------------------------------------------------- /bundle.Dockerfile: -------------------------------------------------------------------------------- 1 | FROM scratch 2 | 3 | # Core bundle labels. 4 | LABEL operators.operatorframework.io.bundle.mediatype.v1=registry+v1 5 | LABEL operators.operatorframework.io.bundle.manifests.v1=manifests/ 6 | LABEL operators.operatorframework.io.bundle.metadata.v1=metadata/ 7 | LABEL operators.operatorframework.io.bundle.package.v1=metallb-operator 8 | LABEL operators.operatorframework.io.bundle.channels.v1=alpha 9 | LABEL operators.operatorframework.io.metrics.builder=operator-sdk-v1.38.0 10 | LABEL operators.operatorframework.io.metrics.mediatype.v1=metrics+v1 11 | LABEL operators.operatorframework.io.metrics.project_layout=go.kubebuilder.io/v4 12 | 13 | # Labels for testing. 14 | LABEL operators.operatorframework.io.test.mediatype.v1=scorecard+v1 15 | LABEL operators.operatorframework.io.test.config.v1=tests/scorecard/ 16 | 17 | # Copy files to locations specified by labels. 18 | COPY bundle/manifests /manifests/ 19 | COPY bundle/metadata /metadata/ 20 | COPY bundle/tests/scorecard /tests/scorecard/ 21 | -------------------------------------------------------------------------------- /bundle/manifests/frr-k8s-metrics-reader_rbac.authorization.k8s.io_v1_clusterrole.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: ClusterRole 3 | metadata: 4 | creationTimestamp: null 5 | labels: 6 | app.kubernetes.io/component: kube-rbac-proxy 7 | app.kubernetes.io/created-by: frr-k8s 8 | app.kubernetes.io/instance: metrics-reader 9 | app.kubernetes.io/managed-by: kustomize 10 | app.kubernetes.io/name: clusterrole 11 | app.kubernetes.io/part-of: frr-k8s 12 | name: frr-k8s-metrics-reader 13 | rules: 14 | - nonResourceURLs: 15 | - /metrics 16 | verbs: 17 | - get 18 | -------------------------------------------------------------------------------- /bundle/manifests/frrk8s.metallb.io_frrnodestates.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apiextensions.k8s.io/v1 2 | kind: CustomResourceDefinition 3 | metadata: 4 | annotations: 5 | controller-gen.kubebuilder.io/version: v0.14.0 6 | creationTimestamp: null 7 | name: frrnodestates.frrk8s.metallb.io 8 | spec: 9 | group: frrk8s.metallb.io 10 | names: 11 | kind: FRRNodeState 12 | listKind: FRRNodeStateList 13 | plural: frrnodestates 14 | singular: frrnodestate 15 | scope: Cluster 16 | versions: 17 | - name: v1beta1 18 | schema: 19 | openAPIV3Schema: 20 | description: FRRNodeState exposes the status of the FRR instance running on 21 | each node. 22 | properties: 23 | apiVersion: 24 | description: |- 25 | APIVersion defines the versioned schema of this representation of an object. 26 | Servers should convert recognized schemas to the latest internal value, and 27 | may reject unrecognized values. 28 | More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources 29 | type: string 30 | kind: 31 | description: |- 32 | Kind is a string value representing the REST resource this object represents. 33 | Servers may infer this from the endpoint the client submits requests to. 34 | Cannot be updated. 35 | In CamelCase. 36 | More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds 37 | type: string 38 | metadata: 39 | type: object 40 | spec: 41 | description: FRRNodeStateSpec defines the desired state of FRRNodeState. 42 | type: object 43 | status: 44 | description: FRRNodeStateStatus defines the observed state of FRRNodeState. 45 | properties: 46 | lastConversionResult: 47 | description: LastConversionResult is the status of the last translation 48 | between the `FRRConfiguration`s resources and FRR's configuration, 49 | contains "success" or an error. 50 | type: string 51 | lastReloadResult: 52 | description: LastReloadResult represents the status of the last configuration 53 | update operation by FRR, contains "success" or an error. 54 | type: string 55 | runningConfig: 56 | description: RunningConfig represents the current FRR running config, 57 | which is the configuration the FRR instance is currently running 58 | with. 59 | type: string 60 | type: object 61 | type: object 62 | served: true 63 | storage: true 64 | subresources: 65 | status: {} 66 | status: 67 | acceptedNames: 68 | kind: "" 69 | plural: "" 70 | conditions: null 71 | storedVersions: null 72 | -------------------------------------------------------------------------------- /bundle/manifests/metallb-manager-role_rbac.authorization.k8s.io_v1_role.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: Role 3 | metadata: 4 | creationTimestamp: null 5 | name: metallb-manager-role 6 | rules: 7 | - apiGroups: 8 | - "" 9 | resources: 10 | - configmaps 11 | verbs: 12 | - create 13 | - delete 14 | - get 15 | - list 16 | - patch 17 | - update 18 | - watch 19 | - apiGroups: 20 | - "" 21 | resources: 22 | - events 23 | verbs: 24 | - create 25 | - patch 26 | - apiGroups: 27 | - "" 28 | resources: 29 | - services 30 | verbs: 31 | - create 32 | - delete 33 | - get 34 | - patch 35 | - update 36 | - apiGroups: 37 | - apps 38 | resources: 39 | - daemonsets 40 | - deployments 41 | verbs: 42 | - create 43 | - delete 44 | - get 45 | - list 46 | - patch 47 | - update 48 | - watch 49 | - apiGroups: 50 | - coordination.k8s.io 51 | resources: 52 | - leases 53 | verbs: 54 | - create 55 | - delete 56 | - get 57 | - list 58 | - patch 59 | - update 60 | - watch 61 | -------------------------------------------------------------------------------- /bundle/manifests/metallb-manager-rolebinding_rbac.authorization.k8s.io_v1_rolebinding.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: RoleBinding 3 | metadata: 4 | creationTimestamp: null 5 | name: metallb-manager-rolebinding 6 | roleRef: 7 | apiGroup: rbac.authorization.k8s.io 8 | kind: Role 9 | name: metallb-manager-role 10 | subjects: 11 | - kind: ServiceAccount 12 | name: manager-account 13 | namespace: metallb-system 14 | -------------------------------------------------------------------------------- /bundle/manifests/metallb-operator-webhook-server-cert_v1_secret.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Secret 3 | metadata: 4 | name: metallb-operator-webhook-server-cert 5 | -------------------------------------------------------------------------------- /bundle/manifests/metallb-operator-webhook-service_v1_service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | creationTimestamp: null 5 | name: metallb-operator-webhook-service 6 | spec: 7 | ports: 8 | - port: 443 9 | targetPort: 9443 10 | selector: 11 | control-plane: controller-manager 12 | status: 13 | loadBalancer: {} 14 | -------------------------------------------------------------------------------- /bundle/manifests/metallb-webhook-cert_v1_secret.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Secret 3 | metadata: 4 | name: metallb-webhook-cert 5 | -------------------------------------------------------------------------------- /bundle/manifests/metallb-webhook-service_v1_service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | creationTimestamp: null 5 | name: metallb-webhook-service 6 | spec: 7 | ports: 8 | - port: 443 9 | targetPort: 9443 10 | selector: 11 | component: webhook-server 12 | status: 13 | loadBalancer: {} 14 | -------------------------------------------------------------------------------- /bundle/manifests/metallb.io_communities.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apiextensions.k8s.io/v1 2 | kind: CustomResourceDefinition 3 | metadata: 4 | annotations: 5 | controller-gen.kubebuilder.io/version: v0.17.2 6 | creationTimestamp: null 7 | name: communities.metallb.io 8 | spec: 9 | group: metallb.io 10 | names: 11 | kind: Community 12 | listKind: CommunityList 13 | plural: communities 14 | singular: community 15 | scope: Namespaced 16 | versions: 17 | - name: v1beta1 18 | schema: 19 | openAPIV3Schema: 20 | description: |- 21 | Community is a collection of aliases for communities. 22 | Users can define named aliases to be used in the BGPPeer CRD. 23 | properties: 24 | apiVersion: 25 | description: |- 26 | APIVersion defines the versioned schema of this representation of an object. 27 | Servers should convert recognized schemas to the latest internal value, and 28 | may reject unrecognized values. 29 | More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources 30 | type: string 31 | kind: 32 | description: |- 33 | Kind is a string value representing the REST resource this object represents. 34 | Servers may infer this from the endpoint the client submits requests to. 35 | Cannot be updated. 36 | In CamelCase. 37 | More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds 38 | type: string 39 | metadata: 40 | type: object 41 | spec: 42 | description: CommunitySpec defines the desired state of Community. 43 | properties: 44 | communities: 45 | items: 46 | properties: 47 | name: 48 | description: The name of the alias for the community. 49 | type: string 50 | value: 51 | description: |- 52 | The BGP community value corresponding to the given name. Can be a standard community of the form 1234:1234 53 | or a large community of the form large:1234:1234:1234. 54 | type: string 55 | type: object 56 | type: array 57 | type: object 58 | status: 59 | description: CommunityStatus defines the observed state of Community. 60 | type: object 61 | type: object 62 | served: true 63 | storage: true 64 | subresources: 65 | status: {} 66 | status: 67 | acceptedNames: 68 | kind: "" 69 | plural: "" 70 | conditions: null 71 | storedVersions: null 72 | -------------------------------------------------------------------------------- /bundle/manifests/metallb.io_servicebgpstatuses.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apiextensions.k8s.io/v1 2 | kind: CustomResourceDefinition 3 | metadata: 4 | annotations: 5 | controller-gen.kubebuilder.io/version: v0.17.2 6 | creationTimestamp: null 7 | name: servicebgpstatuses.metallb.io 8 | spec: 9 | group: metallb.io 10 | names: 11 | kind: ServiceBGPStatus 12 | listKind: ServiceBGPStatusList 13 | plural: servicebgpstatuses 14 | singular: servicebgpstatus 15 | scope: Namespaced 16 | versions: 17 | - additionalPrinterColumns: 18 | - jsonPath: .status.node 19 | name: Node 20 | type: string 21 | - jsonPath: .status.serviceName 22 | name: Service Name 23 | type: string 24 | - jsonPath: .status.serviceNamespace 25 | name: Service Namespace 26 | type: string 27 | name: v1beta1 28 | schema: 29 | openAPIV3Schema: 30 | description: ServiceBGPStatus exposes the BGP peers a service is configured 31 | to be advertised to, per relevant node. 32 | properties: 33 | apiVersion: 34 | description: |- 35 | APIVersion defines the versioned schema of this representation of an object. 36 | Servers should convert recognized schemas to the latest internal value, and 37 | may reject unrecognized values. 38 | More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources 39 | type: string 40 | kind: 41 | description: |- 42 | Kind is a string value representing the REST resource this object represents. 43 | Servers may infer this from the endpoint the client submits requests to. 44 | Cannot be updated. 45 | In CamelCase. 46 | More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds 47 | type: string 48 | metadata: 49 | type: object 50 | spec: 51 | description: ServiceBGPStatusSpec defines the desired state of ServiceBGPStatus. 52 | type: object 53 | status: 54 | description: MetalLBServiceBGPStatus defines the observed state of ServiceBGPStatus. 55 | properties: 56 | node: 57 | description: Node indicates the node announcing the service. 58 | type: string 59 | x-kubernetes-validations: 60 | - message: Value is immutable 61 | rule: self == oldSelf 62 | peers: 63 | description: |- 64 | Peers indicate the BGP peers for which the service is configured to be advertised to. 65 | The service being actually advertised to a given peer depends on the session state and is not indicated here. 66 | items: 67 | type: string 68 | type: array 69 | serviceName: 70 | description: ServiceName indicates the service this status represents. 71 | type: string 72 | x-kubernetes-validations: 73 | - message: Value is immutable 74 | rule: self == oldSelf 75 | serviceNamespace: 76 | description: ServiceNamespace indicates the namespace of the service. 77 | type: string 78 | x-kubernetes-validations: 79 | - message: Value is immutable 80 | rule: self == oldSelf 81 | type: object 82 | type: object 83 | served: true 84 | storage: true 85 | subresources: 86 | status: {} 87 | status: 88 | acceptedNames: 89 | kind: "" 90 | plural: "" 91 | conditions: null 92 | storedVersions: null 93 | -------------------------------------------------------------------------------- /bundle/manifests/metallb.io_servicel2statuses.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apiextensions.k8s.io/v1 2 | kind: CustomResourceDefinition 3 | metadata: 4 | annotations: 5 | controller-gen.kubebuilder.io/version: v0.17.2 6 | creationTimestamp: null 7 | name: servicel2statuses.metallb.io 8 | spec: 9 | group: metallb.io 10 | names: 11 | kind: ServiceL2Status 12 | listKind: ServiceL2StatusList 13 | plural: servicel2statuses 14 | singular: servicel2status 15 | scope: Namespaced 16 | versions: 17 | - additionalPrinterColumns: 18 | - jsonPath: .status.node 19 | name: Allocated Node 20 | type: string 21 | - jsonPath: .status.serviceName 22 | name: Service Name 23 | type: string 24 | - jsonPath: .status.serviceNamespace 25 | name: Service Namespace 26 | type: string 27 | name: v1beta1 28 | schema: 29 | openAPIV3Schema: 30 | description: ServiceL2Status reveals the actual traffic status of loadbalancer 31 | services in layer2 mode. 32 | properties: 33 | apiVersion: 34 | description: |- 35 | APIVersion defines the versioned schema of this representation of an object. 36 | Servers should convert recognized schemas to the latest internal value, and 37 | may reject unrecognized values. 38 | More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources 39 | type: string 40 | kind: 41 | description: |- 42 | Kind is a string value representing the REST resource this object represents. 43 | Servers may infer this from the endpoint the client submits requests to. 44 | Cannot be updated. 45 | In CamelCase. 46 | More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds 47 | type: string 48 | metadata: 49 | type: object 50 | spec: 51 | description: ServiceL2StatusSpec defines the desired state of ServiceL2Status. 52 | type: object 53 | status: 54 | description: MetalLBServiceL2Status defines the observed state of ServiceL2Status. 55 | properties: 56 | interfaces: 57 | description: Interfaces indicates the interfaces that receive the 58 | directed traffic 59 | items: 60 | description: InterfaceInfo defines interface info of layer2 announcement. 61 | properties: 62 | name: 63 | description: Name the name of network interface card 64 | type: string 65 | type: object 66 | type: array 67 | node: 68 | description: Node indicates the node that receives the directed traffic 69 | type: string 70 | x-kubernetes-validations: 71 | - message: Value is immutable 72 | rule: self == oldSelf 73 | serviceName: 74 | description: ServiceName indicates the service this status represents 75 | type: string 76 | x-kubernetes-validations: 77 | - message: Value is immutable 78 | rule: self == oldSelf 79 | serviceNamespace: 80 | description: ServiceNamespace indicates the namespace of the service 81 | type: string 82 | x-kubernetes-validations: 83 | - message: Value is immutable 84 | rule: self == oldSelf 85 | type: object 86 | type: object 87 | served: true 88 | storage: true 89 | subresources: 90 | status: {} 91 | status: 92 | acceptedNames: 93 | kind: "" 94 | plural: "" 95 | conditions: null 96 | storedVersions: null 97 | -------------------------------------------------------------------------------- /bundle/manifests/speaker_v1_serviceaccount.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ServiceAccount 3 | metadata: 4 | creationTimestamp: null 5 | labels: 6 | app: metallb 7 | name: speaker 8 | -------------------------------------------------------------------------------- /bundle/manifests/webhook-server-cert_v1_secret.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Secret 3 | metadata: 4 | name: metallb-webhook-cert 5 | -------------------------------------------------------------------------------- /bundle/metadata/annotations.yaml: -------------------------------------------------------------------------------- 1 | annotations: 2 | # Core bundle annotations. 3 | operators.operatorframework.io.bundle.mediatype.v1: registry+v1 4 | operators.operatorframework.io.bundle.manifests.v1: manifests/ 5 | operators.operatorframework.io.bundle.metadata.v1: metadata/ 6 | operators.operatorframework.io.bundle.package.v1: metallb-operator 7 | operators.operatorframework.io.bundle.channels.v1: alpha 8 | operators.operatorframework.io.metrics.builder: operator-sdk-v1.38.0 9 | operators.operatorframework.io.metrics.mediatype.v1: metrics+v1 10 | operators.operatorframework.io.metrics.project_layout: go.kubebuilder.io/v4 11 | 12 | # Annotations for testing. 13 | operators.operatorframework.io.test.mediatype.v1: scorecard+v1 14 | operators.operatorframework.io.test.config.v1: tests/scorecard/ 15 | -------------------------------------------------------------------------------- /bundle/tests/scorecard/config.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: scorecard.operatorframework.io/v1alpha3 2 | kind: Configuration 3 | metadata: 4 | name: config 5 | stages: 6 | - parallel: true 7 | tests: 8 | - entrypoint: 9 | - scorecard-test 10 | - basic-check-spec 11 | image: quay.io/operator-framework/scorecard-test:master 12 | labels: 13 | suite: basic 14 | test: basic-check-spec-test 15 | storage: 16 | spec: 17 | mountPath: {} 18 | - entrypoint: 19 | - scorecard-test 20 | - olm-bundle-validation 21 | image: quay.io/operator-framework/scorecard-test:master 22 | labels: 23 | suite: olm 24 | test: olm-bundle-validation-test 25 | storage: 26 | spec: 27 | mountPath: {} 28 | - entrypoint: 29 | - scorecard-test 30 | - olm-crds-have-validation 31 | image: quay.io/operator-framework/scorecard-test:master 32 | labels: 33 | suite: olm 34 | test: olm-crds-have-validation-test 35 | storage: 36 | spec: 37 | mountPath: {} 38 | - entrypoint: 39 | - scorecard-test 40 | - olm-crds-have-resources 41 | image: quay.io/operator-framework/scorecard-test:master 42 | labels: 43 | suite: olm 44 | test: olm-crds-have-resources-test 45 | storage: 46 | spec: 47 | mountPath: {} 48 | - entrypoint: 49 | - scorecard-test 50 | - olm-spec-descriptors 51 | image: quay.io/operator-framework/scorecard-test:master 52 | labels: 53 | suite: olm 54 | test: olm-spec-descriptors-test 55 | storage: 56 | spec: 57 | mountPath: {} 58 | - entrypoint: 59 | - scorecard-test 60 | - olm-status-descriptors 61 | image: quay.io/operator-framework/scorecard-test:master 62 | labels: 63 | suite: olm 64 | test: olm-status-descriptors-test 65 | storage: 66 | spec: 67 | mountPath: {} 68 | storage: 69 | spec: 70 | mountPath: {} 71 | -------------------------------------------------------------------------------- /config/crd/bases/frrk8s.metallb.io_frrnodestates.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apiextensions.k8s.io/v1 2 | kind: CustomResourceDefinition 3 | metadata: 4 | annotations: 5 | controller-gen.kubebuilder.io/version: v0.14.0 6 | name: frrnodestates.frrk8s.metallb.io 7 | spec: 8 | group: frrk8s.metallb.io 9 | names: 10 | kind: FRRNodeState 11 | listKind: FRRNodeStateList 12 | plural: frrnodestates 13 | singular: frrnodestate 14 | scope: Cluster 15 | versions: 16 | - name: v1beta1 17 | schema: 18 | openAPIV3Schema: 19 | description: FRRNodeState exposes the status of the FRR instance running on 20 | each node. 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: FRRNodeStateSpec defines the desired state of FRRNodeState. 41 | type: object 42 | status: 43 | description: FRRNodeStateStatus defines the observed state of FRRNodeState. 44 | properties: 45 | lastConversionResult: 46 | description: LastConversionResult is the status of the last translation 47 | between the `FRRConfiguration`s resources and FRR's configuration, 48 | contains "success" or an error. 49 | type: string 50 | lastReloadResult: 51 | description: LastReloadResult represents the status of the last configuration 52 | update operation by FRR, contains "success" or an error. 53 | type: string 54 | runningConfig: 55 | description: RunningConfig represents the current FRR running config, 56 | which is the configuration the FRR instance is currently running 57 | with. 58 | type: string 59 | type: object 60 | type: object 61 | served: true 62 | storage: true 63 | subresources: 64 | status: {} 65 | -------------------------------------------------------------------------------- /config/crd/bases/metallb.io_communities.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apiextensions.k8s.io/v1 2 | kind: CustomResourceDefinition 3 | metadata: 4 | annotations: 5 | controller-gen.kubebuilder.io/version: v0.17.2 6 | name: communities.metallb.io 7 | spec: 8 | group: metallb.io 9 | names: 10 | kind: Community 11 | listKind: CommunityList 12 | plural: communities 13 | singular: community 14 | scope: Namespaced 15 | versions: 16 | - name: v1beta1 17 | schema: 18 | openAPIV3Schema: 19 | description: |- 20 | Community is a collection of aliases for communities. 21 | Users can define named aliases to be used in the BGPPeer CRD. 22 | properties: 23 | apiVersion: 24 | description: |- 25 | APIVersion defines the versioned schema of this representation of an object. 26 | Servers should convert recognized schemas to the latest internal value, and 27 | may reject unrecognized values. 28 | More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources 29 | type: string 30 | kind: 31 | description: |- 32 | Kind is a string value representing the REST resource this object represents. 33 | Servers may infer this from the endpoint the client submits requests to. 34 | Cannot be updated. 35 | In CamelCase. 36 | More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds 37 | type: string 38 | metadata: 39 | type: object 40 | spec: 41 | description: CommunitySpec defines the desired state of Community. 42 | properties: 43 | communities: 44 | items: 45 | properties: 46 | name: 47 | description: The name of the alias for the community. 48 | type: string 49 | value: 50 | description: |- 51 | The BGP community value corresponding to the given name. Can be a standard community of the form 1234:1234 52 | or a large community of the form large:1234:1234:1234. 53 | type: string 54 | type: object 55 | type: array 56 | type: object 57 | status: 58 | description: CommunityStatus defines the observed state of Community. 59 | type: object 60 | type: object 61 | served: true 62 | storage: true 63 | subresources: 64 | status: {} 65 | -------------------------------------------------------------------------------- /config/crd/bases/metallb.io_servicebgpstatuses.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apiextensions.k8s.io/v1 2 | kind: CustomResourceDefinition 3 | metadata: 4 | annotations: 5 | controller-gen.kubebuilder.io/version: v0.17.2 6 | name: servicebgpstatuses.metallb.io 7 | spec: 8 | group: metallb.io 9 | names: 10 | kind: ServiceBGPStatus 11 | listKind: ServiceBGPStatusList 12 | plural: servicebgpstatuses 13 | singular: servicebgpstatus 14 | scope: Namespaced 15 | versions: 16 | - additionalPrinterColumns: 17 | - jsonPath: .status.node 18 | name: Node 19 | type: string 20 | - jsonPath: .status.serviceName 21 | name: Service Name 22 | type: string 23 | - jsonPath: .status.serviceNamespace 24 | name: Service Namespace 25 | type: string 26 | name: v1beta1 27 | schema: 28 | openAPIV3Schema: 29 | description: ServiceBGPStatus exposes the BGP peers a service is configured 30 | to be advertised to, per relevant node. 31 | properties: 32 | apiVersion: 33 | description: |- 34 | APIVersion defines the versioned schema of this representation of an object. 35 | Servers should convert recognized schemas to the latest internal value, and 36 | may reject unrecognized values. 37 | More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources 38 | type: string 39 | kind: 40 | description: |- 41 | Kind is a string value representing the REST resource this object represents. 42 | Servers may infer this from the endpoint the client submits requests to. 43 | Cannot be updated. 44 | In CamelCase. 45 | More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds 46 | type: string 47 | metadata: 48 | type: object 49 | spec: 50 | description: ServiceBGPStatusSpec defines the desired state of ServiceBGPStatus. 51 | type: object 52 | status: 53 | description: MetalLBServiceBGPStatus defines the observed state of ServiceBGPStatus. 54 | properties: 55 | node: 56 | description: Node indicates the node announcing the service. 57 | type: string 58 | x-kubernetes-validations: 59 | - message: Value is immutable 60 | rule: self == oldSelf 61 | peers: 62 | description: |- 63 | Peers indicate the BGP peers for which the service is configured to be advertised to. 64 | The service being actually advertised to a given peer depends on the session state and is not indicated here. 65 | items: 66 | type: string 67 | type: array 68 | serviceName: 69 | description: ServiceName indicates the service this status represents. 70 | type: string 71 | x-kubernetes-validations: 72 | - message: Value is immutable 73 | rule: self == oldSelf 74 | serviceNamespace: 75 | description: ServiceNamespace indicates the namespace of the service. 76 | type: string 77 | x-kubernetes-validations: 78 | - message: Value is immutable 79 | rule: self == oldSelf 80 | type: object 81 | type: object 82 | served: true 83 | storage: true 84 | subresources: 85 | status: {} 86 | -------------------------------------------------------------------------------- /config/crd/bases/metallb.io_servicel2statuses.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apiextensions.k8s.io/v1 2 | kind: CustomResourceDefinition 3 | metadata: 4 | annotations: 5 | controller-gen.kubebuilder.io/version: v0.17.2 6 | name: servicel2statuses.metallb.io 7 | spec: 8 | group: metallb.io 9 | names: 10 | kind: ServiceL2Status 11 | listKind: ServiceL2StatusList 12 | plural: servicel2statuses 13 | singular: servicel2status 14 | scope: Namespaced 15 | versions: 16 | - additionalPrinterColumns: 17 | - jsonPath: .status.node 18 | name: Allocated Node 19 | type: string 20 | - jsonPath: .status.serviceName 21 | name: Service Name 22 | type: string 23 | - jsonPath: .status.serviceNamespace 24 | name: Service Namespace 25 | type: string 26 | name: v1beta1 27 | schema: 28 | openAPIV3Schema: 29 | description: ServiceL2Status reveals the actual traffic status of loadbalancer 30 | services in layer2 mode. 31 | properties: 32 | apiVersion: 33 | description: |- 34 | APIVersion defines the versioned schema of this representation of an object. 35 | Servers should convert recognized schemas to the latest internal value, and 36 | may reject unrecognized values. 37 | More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources 38 | type: string 39 | kind: 40 | description: |- 41 | Kind is a string value representing the REST resource this object represents. 42 | Servers may infer this from the endpoint the client submits requests to. 43 | Cannot be updated. 44 | In CamelCase. 45 | More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds 46 | type: string 47 | metadata: 48 | type: object 49 | spec: 50 | description: ServiceL2StatusSpec defines the desired state of ServiceL2Status. 51 | type: object 52 | status: 53 | description: MetalLBServiceL2Status defines the observed state of ServiceL2Status. 54 | properties: 55 | interfaces: 56 | description: Interfaces indicates the interfaces that receive the 57 | directed traffic 58 | items: 59 | description: InterfaceInfo defines interface info of layer2 announcement. 60 | properties: 61 | name: 62 | description: Name the name of network interface card 63 | type: string 64 | type: object 65 | type: array 66 | node: 67 | description: Node indicates the node that receives the directed traffic 68 | type: string 69 | x-kubernetes-validations: 70 | - message: Value is immutable 71 | rule: self == oldSelf 72 | serviceName: 73 | description: ServiceName indicates the service this status represents 74 | type: string 75 | x-kubernetes-validations: 76 | - message: Value is immutable 77 | rule: self == oldSelf 78 | serviceNamespace: 79 | description: ServiceNamespace indicates the namespace of the service 80 | type: string 81 | x-kubernetes-validations: 82 | - message: Value is immutable 83 | rule: self == oldSelf 84 | type: object 85 | type: object 86 | served: true 87 | storage: true 88 | subresources: 89 | status: {} 90 | -------------------------------------------------------------------------------- /config/crd/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/metallb.io_metallbs.yaml 6 | - bases/metallb.io_bgppeers.yaml 7 | - bases/metallb.io_bfdprofiles.yaml 8 | - bases/metallb.io_ipaddresspools.yaml 9 | - bases/metallb.io_l2advertisements.yaml 10 | - bases/metallb.io_bgpadvertisements.yaml 11 | - bases/metallb.io_communities.yaml 12 | - bases/metallb.io_servicel2statuses.yaml 13 | - bases/frrk8s.metallb.io_frrconfigurations.yaml 14 | - bases/frrk8s.metallb.io_frrnodestates.yaml 15 | - bases/metallb.io_servicebgpstatuses.yaml 16 | 17 | # +kubebuilder:scaffold:crdkustomizeresource 18 | 19 | # the following config is for teaching kustomize how to do kustomization for CRDs. 20 | configurations: 21 | - kustomizeconfig.yaml 22 | 23 | patches: 24 | - path: patches/crd-conversion-patch-bgppeers.yaml 25 | target: 26 | kind: CustomResourceDefinition 27 | name: bgppeers.metallb.io 28 | -------------------------------------------------------------------------------- /config/crd/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 | group: apiextensions.k8s.io 8 | path: spec/conversion/webhookClientConfig/service/name 9 | 10 | namespace: 11 | - kind: CustomResourceDefinition 12 | group: apiextensions.k8s.io 13 | path: spec/conversion/webhookClientConfig/service/namespace 14 | create: false 15 | 16 | varReference: 17 | - path: metadata/annotations 18 | -------------------------------------------------------------------------------- /config/crd/patches/crd-conversion-patch-bgppeers.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apiextensions.k8s.io/v1 2 | kind: CustomResourceDefinition 3 | metadata: 4 | name: bgppeers.metallb.io 5 | spec: 6 | conversion: 7 | strategy: Webhook 8 | webhook: 9 | conversionReviewVersions: ["v1beta1", "v1beta2"] 10 | clientConfig: 11 | service: 12 | namespace: metallb-system 13 | name: metallb-webhook-service 14 | path: /convert 15 | -------------------------------------------------------------------------------- /config/default/kustomization.yaml: -------------------------------------------------------------------------------- 1 | # Adds namespace to all resources. 2 | namespace: metallb-system 3 | 4 | # Labels to add to all resources and selectors. 5 | #commonLabels: 6 | # someName: someValue 7 | 8 | resources: 9 | - ../crd 10 | - ../rbac 11 | - ../manager 12 | - ../webhook 13 | 14 | transformers: 15 | - ./kustomizeconfig/add-prefix.yaml 16 | apiVersion: kustomize.config.k8s.io/v1beta1 17 | kind: Kustomization 18 | -------------------------------------------------------------------------------- /config/default/kustomizeconfig/add-prefix.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: builtin 2 | kind: PrefixSuffixTransformer 3 | metadata: 4 | name: customPrefixer 5 | prefix: metallb-operator- 6 | fieldSpecs: 7 | - kind: Deployment 8 | path: metadata/name 9 | - kind: Daemonset 10 | path: metadata/name -------------------------------------------------------------------------------- /config/frr-webhook-prometheus/bgpTypeFRR.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 | env: 12 | - name: DEPLOY_SERVICEMONITORS 13 | value: "true" 14 | - name: FRR_HTTPS_METRICS_PORT 15 | value: "9120" 16 | - name: HTTPS_METRICS_PORT 17 | value: "9121" 18 | - name: FRRK8S_HTTPS_METRICS_PORT 19 | value: "9122" 20 | 21 | --- 22 | apiVersion: apps/v1 23 | kind: Deployment 24 | metadata: 25 | name: webhook-server 26 | namespace: system 27 | spec: 28 | template: 29 | spec: 30 | containers: 31 | - name: webhook-server 32 | env: 33 | - name: METALLB_BGP_TYPE 34 | value: "frr" 35 | -------------------------------------------------------------------------------- /config/frr-webhook-prometheus/kustomization.yaml: -------------------------------------------------------------------------------- 1 | 2 | patchesStrategicMerge: 3 | - bgpTypeFRR.yaml 4 | apiVersion: kustomize.config.k8s.io/v1beta1 5 | kind: Kustomization 6 | resources: 7 | - ../default 8 | - prometheus_sa.yaml 9 | namespace: metallb-system 10 | -------------------------------------------------------------------------------- /config/frr-webhook-prometheus/prometheus_sa.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: Role 3 | metadata: 4 | name: prometheus-k8s 5 | namespace: metallb-system 6 | rules: 7 | - apiGroups: 8 | - "" 9 | resources: 10 | - services 11 | - endpoints 12 | - pods 13 | verbs: 14 | - get 15 | - list 16 | - watch 17 | --- 18 | apiVersion: rbac.authorization.k8s.io/v1 19 | kind: RoleBinding 20 | metadata: 21 | name: prometheus-k8s 22 | namespace: metallb-system 23 | roleRef: 24 | apiGroup: rbac.authorization.k8s.io 25 | kind: Role 26 | name: prometheus-k8s 27 | subjects: 28 | - kind: ServiceAccount 29 | name: prometheus-k8s 30 | namespace: monitoring 31 | -------------------------------------------------------------------------------- /config/manager/env.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 | env: 12 | - name: SPEAKER_IMAGE 13 | value: "quay.io/metallb/speaker:main" 14 | - name: CONTROLLER_IMAGE 15 | value: "quay.io/metallb/controller:main" 16 | - name: METALLB_BGP_TYPE 17 | value: "native" 18 | - name: FRR_IMAGE 19 | value: "quay.io/frrouting/frr:9.1.0" 20 | - name: KUBE_RBAC_PROXY_IMAGE 21 | value: "quay.io/brancz/kube-rbac-proxy:v0.11.0" 22 | - name: DEPLOY_KUBE_RBAC_PROXIES 23 | value: "false" 24 | - name: FRRK8S_IMAGE 25 | value: "quay.io/metallb/frr-k8s:v0.0.17" 26 | - name: FRRK8S_EXTERNAL_NAMESPACE 27 | value: "frr-k8s-system" 28 | -------------------------------------------------------------------------------- /config/manager/kustomization.yaml: -------------------------------------------------------------------------------- 1 | resources: 2 | - manager.yaml 3 | apiVersion: kustomize.config.k8s.io/v1beta1 4 | kind: Kustomization 5 | patchesStrategicMerge: 6 | - env.yaml 7 | images: 8 | - name: controller 9 | newName: quay.io/metallb/metallb-operator 10 | newTag: main 11 | -------------------------------------------------------------------------------- /config/manager/manager.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Namespace 3 | metadata: 4 | labels: 5 | control-plane: controller-manager 6 | name: system 7 | --- 8 | apiVersion: apps/v1 9 | kind: Deployment 10 | metadata: 11 | name: controller-manager 12 | namespace: system 13 | labels: 14 | control-plane: controller-manager 15 | spec: 16 | selector: 17 | matchLabels: 18 | control-plane: controller-manager 19 | replicas: 1 20 | template: 21 | metadata: 22 | labels: 23 | control-plane: controller-manager 24 | spec: 25 | serviceAccountName: manager-account 26 | containers: 27 | - command: 28 | - /manager 29 | args: 30 | - --enable-leader-election 31 | image: controller # Should be controller, this is the name that kustomize looks for and edits on deploy 32 | name: manager 33 | ports: 34 | - containerPort: 9443 35 | name: webhook-server 36 | protocol: TCP 37 | readinessProbe: 38 | failureThreshold: 3 39 | httpGet: 40 | path: /readyz 41 | port: 8080 42 | initialDelaySeconds: 30 43 | periodSeconds: 10 44 | successThreshold: 1 45 | timeoutSeconds: 1 46 | resources: 47 | requests: 48 | cpu: 50m 49 | memory: 20Mi 50 | env: 51 | - name: OPERATOR_NAMESPACE 52 | valueFrom: 53 | fieldRef: 54 | fieldPath: metadata.namespace 55 | volumeMounts: 56 | - mountPath: /tmp/k8s-webhook-server/serving-certs 57 | name: cert 58 | readOnly: true 59 | terminationGracePeriodSeconds: 10 60 | volumes: 61 | - name: cert 62 | secret: 63 | defaultMode: 420 64 | secretName: metallb-operator-webhook-server-cert 65 | -------------------------------------------------------------------------------- /config/manifests/disable-cert-rotation.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: webhook-server 5 | namespace: system 6 | spec: 7 | template: 8 | spec: 9 | containers: 10 | - name: webhook-server 11 | args: 12 | - --disable-cert-rotation=true 13 | - --port=7472 14 | - --log-level=info 15 | - --webhook-mode=onlywebhook 16 | --- 17 | apiVersion: apps/v1 18 | kind: Deployment 19 | metadata: 20 | name: controller-manager 21 | namespace: system 22 | spec: 23 | template: 24 | spec: 25 | containers: 26 | - name: manager 27 | args: 28 | - --enable-leader-election 29 | - --disable-cert-rotation=true -------------------------------------------------------------------------------- /config/manifests/kustomization.yaml: -------------------------------------------------------------------------------- 1 | # These resources constitute the fully configured set of manifests 2 | # used to generate the 'manifests/' directory in a bundle. 3 | resources: 4 | - bases/metallb-operator.clusterserviceversion.yaml 5 | - ../default 6 | - ../metallb_rbac 7 | - ../samples 8 | - ../scorecard 9 | 10 | patchesStrategicMerge: 11 | - disable-cert-rotation.yaml 12 | -------------------------------------------------------------------------------- /config/metallb_rbac/frrk8s_role_binding.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: Role 3 | metadata: 4 | name: frr-k8s-daemon-scc 5 | namespace: metallb-system 6 | rules: 7 | - apiGroups: 8 | - security.openshift.io 9 | resourceNames: 10 | - privileged 11 | resources: 12 | - securitycontextconstraints 13 | verbs: 14 | - use 15 | --- 16 | apiVersion: rbac.authorization.k8s.io/v1 17 | kind: RoleBinding 18 | metadata: 19 | name: frr-k8s-daemon-scc-binding 20 | namespace: metallb-system 21 | roleRef: 22 | apiGroup: rbac.authorization.k8s.io 23 | kind: Role 24 | name: frr-k8s-daemon-scc 25 | subjects: 26 | - kind: ServiceAccount 27 | name: frr-k8s-daemon 28 | namespace: metallb-system 29 | -------------------------------------------------------------------------------- /config/metallb_rbac/kube_rbac_proxy.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: ClusterRole 3 | metadata: 4 | labels: 5 | app: metallb 6 | name: metallb-system:kube-rbac-proxy 7 | namespace: metallb-system 8 | rules: 9 | - apiGroups: 10 | - authentication.k8s.io 11 | resources: 12 | - tokenreviews 13 | verbs: 14 | - create 15 | - apiGroups: 16 | - authorization.k8s.io 17 | resources: 18 | - subjectaccessreviews 19 | verbs: 20 | - create 21 | --- 22 | apiVersion: rbac.authorization.k8s.io/v1 23 | kind: ClusterRoleBinding 24 | metadata: 25 | labels: 26 | app: metallb 27 | name: kube-rbac-proxy 28 | namespace: metallb-system 29 | roleRef: 30 | apiGroup: rbac.authorization.k8s.io 31 | kind: ClusterRole 32 | name: metallb-system:kube-rbac-proxy 33 | subjects: 34 | - kind: ServiceAccount 35 | name: controller 36 | namespace: metallb-system 37 | - kind: ServiceAccount 38 | name: speaker 39 | namespace: metallb-system 40 | -------------------------------------------------------------------------------- /config/metallb_rbac/kustomization.yaml: -------------------------------------------------------------------------------- 1 | resources: 2 | - metallb.yaml 3 | - speaker_role_binding.yaml 4 | - kube_rbac_proxy.yaml 5 | - frrk8s_role_binding.yaml 6 | apiVersion: kustomize.config.k8s.io/v1beta1 7 | kind: Kustomization 8 | namespace: metallb-system 9 | -------------------------------------------------------------------------------- /config/metallb_rbac/speaker_role_binding.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: Role 3 | metadata: 4 | name: speaker 5 | namespace: metallb-system 6 | rules: 7 | - apiGroups: 8 | - security.openshift.io 9 | resourceNames: 10 | - privileged 11 | resources: 12 | - securitycontextconstraints 13 | verbs: 14 | - use 15 | --- 16 | apiVersion: rbac.authorization.k8s.io/v1 17 | kind: RoleBinding 18 | metadata: 19 | name: speaker 20 | namespace: metallb-system 21 | roleRef: 22 | apiGroup: rbac.authorization.k8s.io 23 | kind: Role 24 | name: speaker 25 | subjects: 26 | - kind: ServiceAccount 27 | name: speaker 28 | namespace: metallb-system 29 | -------------------------------------------------------------------------------- /config/olm-install/install-resources.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Namespace 3 | metadata: 4 | name: mymetallb 5 | --- 6 | apiVersion: operators.coreos.com/v1alpha1 7 | kind: CatalogSource 8 | metadata: 9 | name: metallb-operator 10 | namespace: mymetallb 11 | spec: 12 | displayName: MetalLB Operator Upstream 13 | image: quay.io/metallb/metallb-operator-bundle-index:main 14 | publisher: github.com/metallb/metallb-operator 15 | sourceType: grpc 16 | --- 17 | apiVersion: operators.coreos.com/v1 18 | kind: OperatorGroup 19 | metadata: 20 | name: metallb-operator 21 | namespace: mymetallb 22 | --- 23 | apiVersion: operators.coreos.com/v1alpha1 24 | kind: Subscription 25 | metadata: 26 | name: metallb-operator-sub 27 | namespace: mymetallb 28 | spec: 29 | name: metallb-operator 30 | channel: alpha 31 | source: metallb-operator 32 | sourceNamespace: mymetallb 33 | -------------------------------------------------------------------------------- /config/olm-install/kustomization.yaml: -------------------------------------------------------------------------------- 1 | resources: 2 | - install-resources.yaml 3 | -------------------------------------------------------------------------------- /config/openshift/custom-namespace-transformer.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: builtin 2 | kind: NamespaceTransformer 3 | metadata: 4 | name: custom-namespace-transformer 5 | namespace: metallb-system 6 | setRoleBindingSubjects: allServiceAccounts 7 | -------------------------------------------------------------------------------- /config/openshift/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | resources: 4 | - ../default 5 | - privileged-role-binding.yaml 6 | transformers: 7 | - custom-namespace-transformer.yaml 8 | patches: 9 | - path: patch-namespace.yaml 10 | - path: patch-deployment-controller-manager.yaml 11 | namespace: metallb-system 12 | -------------------------------------------------------------------------------- /config/openshift/patch-deployment-controller-manager.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 | env: 12 | - name: DEPLOY_SERVICEMONITORS 13 | value: "true" 14 | - name: METALLB_BGP_TYPE 15 | value: "frr" 16 | -------------------------------------------------------------------------------- /config/openshift/patch-namespace.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Namespace 3 | metadata: 4 | name: system 5 | labels: 6 | security.openshift.io/scc.podSecurityLabelSync: "false" 7 | pod-security.kubernetes.io/enforce: privileged 8 | pod-security.kubernetes.io/warn: privileged 9 | pod-security.kubernetes.io/audit: privileged 10 | -------------------------------------------------------------------------------- /config/openshift/privileged-role-binding.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: ClusterRoleBinding 3 | metadata: 4 | name: system:openshift:scc:privileged 5 | roleRef: 6 | apiGroup: rbac.authorization.k8s.io 7 | kind: ClusterRole 8 | name: system:openshift:scc:privileged 9 | subjects: 10 | - kind: ServiceAccount 11 | name: controller 12 | namespace: system 13 | -------------------------------------------------------------------------------- /config/rbac/kustomization.yaml: -------------------------------------------------------------------------------- 1 | resources: 2 | - manager_service_account.yaml 3 | - role.yaml 4 | - role_binding.yaml 5 | -------------------------------------------------------------------------------- /config/rbac/manager_service_account.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ServiceAccount 3 | metadata: 4 | name: manager-account 5 | namespace: metallb-system 6 | -------------------------------------------------------------------------------- /config/rbac/role.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | name: metallb-manager-role 6 | rules: 7 | - apiGroups: 8 | - "" 9 | resources: 10 | - secrets 11 | verbs: 12 | - create 13 | - delete 14 | - get 15 | - list 16 | - patch 17 | - update 18 | - watch 19 | - apiGroups: 20 | - admissionregistration.k8s.io 21 | resources: 22 | - validatingwebhookconfigurations 23 | verbs: 24 | - create 25 | - delete 26 | - get 27 | - list 28 | - patch 29 | - update 30 | - watch 31 | - apiGroups: 32 | - apiextensions.k8s.io 33 | resources: 34 | - customresourcedefinitions 35 | verbs: 36 | - get 37 | - list 38 | - watch 39 | - apiGroups: 40 | - apps 41 | resources: 42 | - daemonsets 43 | - deployments 44 | verbs: 45 | - list 46 | - watch 47 | - apiGroups: 48 | - config.openshift.io 49 | resources: 50 | - clusteroperators 51 | verbs: 52 | - get 53 | - list 54 | - watch 55 | - apiGroups: 56 | - metallb.io 57 | resources: 58 | - metallbs 59 | verbs: 60 | - create 61 | - delete 62 | - get 63 | - list 64 | - patch 65 | - update 66 | - watch 67 | - apiGroups: 68 | - metallb.io 69 | resources: 70 | - metallbs/finalizers 71 | verbs: 72 | - delete 73 | - get 74 | - patch 75 | - update 76 | - apiGroups: 77 | - metallb.io 78 | resources: 79 | - metallbs/status 80 | verbs: 81 | - get 82 | - patch 83 | - update 84 | - apiGroups: 85 | - monitoring.coreos.com 86 | resources: 87 | - podmonitors 88 | verbs: 89 | - create 90 | - delete 91 | - get 92 | - list 93 | - patch 94 | - update 95 | - watch 96 | - apiGroups: 97 | - monitoring.coreos.com 98 | resources: 99 | - servicemonitors 100 | verbs: 101 | - create 102 | - delete 103 | - get 104 | - list 105 | - patch 106 | - update 107 | - watch 108 | - apiGroups: 109 | - operator.openshift.io 110 | resources: 111 | - networks 112 | verbs: 113 | - get 114 | - list 115 | - update 116 | - watch 117 | - apiGroups: 118 | - policy 119 | resources: 120 | - podsecuritypolicies 121 | verbs: 122 | - create 123 | - delete 124 | - get 125 | - list 126 | - patch 127 | - update 128 | - watch 129 | --- 130 | apiVersion: rbac.authorization.k8s.io/v1 131 | kind: Role 132 | metadata: 133 | name: metallb-manager-role 134 | namespace: metallb-system 135 | rules: 136 | - apiGroups: 137 | - "" 138 | resources: 139 | - configmaps 140 | verbs: 141 | - create 142 | - delete 143 | - get 144 | - list 145 | - patch 146 | - update 147 | - watch 148 | - apiGroups: 149 | - "" 150 | resources: 151 | - events 152 | verbs: 153 | - create 154 | - patch 155 | - apiGroups: 156 | - "" 157 | resources: 158 | - services 159 | verbs: 160 | - create 161 | - delete 162 | - get 163 | - patch 164 | - update 165 | - apiGroups: 166 | - apps 167 | resources: 168 | - daemonsets 169 | - deployments 170 | verbs: 171 | - create 172 | - delete 173 | - get 174 | - list 175 | - patch 176 | - update 177 | - watch 178 | - apiGroups: 179 | - coordination.k8s.io 180 | resources: 181 | - leases 182 | verbs: 183 | - create 184 | - delete 185 | - get 186 | - list 187 | - patch 188 | - update 189 | - watch 190 | -------------------------------------------------------------------------------- /config/rbac/role_binding.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: ClusterRoleBinding 3 | metadata: 4 | name: metallb-manager-rolebinding 5 | roleRef: 6 | apiGroup: rbac.authorization.k8s.io 7 | kind: ClusterRole 8 | name: metallb-manager-role 9 | subjects: 10 | - kind: ServiceAccount 11 | name: manager-account 12 | namespace: metallb-system 13 | --- 14 | apiVersion: rbac.authorization.k8s.io/v1 15 | kind: RoleBinding 16 | metadata: 17 | name: metallb-manager-rolebinding 18 | roleRef: 19 | apiGroup: rbac.authorization.k8s.io 20 | kind: Role 21 | name: metallb-manager-role 22 | subjects: 23 | - kind: ServiceAccount 24 | name: manager-account 25 | namespace: metallb-system 26 | --- 27 | -------------------------------------------------------------------------------- /config/samples/demo/metallb-advanced-config.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: node.k8s.io/v1 2 | kind: RuntimeClass 3 | metadata: 4 | name: myclass 5 | handler: myconfiguration 6 | --- 7 | apiVersion: scheduling.k8s.io/v1 8 | kind: PriorityClass 9 | metadata: 10 | name: high-priority 11 | value: 1000000 12 | --- 13 | apiVersion: metallb.io/v1beta1 14 | kind: MetalLB 15 | metadata: 16 | name: metallb 17 | namespace: metallb-system 18 | spec: 19 | logLevel: debug 20 | nodeSelector: 21 | feature.node.kubernetes.io/metalLB.capable: 'true' 22 | loadBalancerClass: 'metallb.universe.tf/metallb' 23 | speakerTolerations: 24 | - key: "Example" 25 | operator: "Exists" 26 | effect: "NoExecute" 27 | controllerNodeSelector: 28 | feature.node.kubernetes.io/metalLB.capable: 'true' 29 | controllerTolerations: 30 | - key: "Example" 31 | operator: "Exists" 32 | effect: "NoExecute" 33 | controllerConfig: 34 | priorityClassName: high-priority 35 | runtimeClassName: myclass 36 | annotations: 37 | controller: demo 38 | affinity: 39 | podAffinity: 40 | requiredDuringSchedulingIgnoredDuringExecution: 41 | - labelSelector: 42 | matchLabels: 43 | component: controller 44 | topologyKey: kubernetes.io/hostname 45 | resources: 46 | limits: 47 | cpu: "200m" 48 | speakerConfig: 49 | priorityClassName: high-priority 50 | runtimeClassName: myclass 51 | annotations: 52 | speaker: demo 53 | affinity: 54 | podAffinity: 55 | requiredDuringSchedulingIgnoredDuringExecution: 56 | - labelSelector: 57 | matchLabels: 58 | component: speaker 59 | topologyKey: kubernetes.io/hostname 60 | resources: 61 | limits: 62 | cpu: "300m" 63 | -------------------------------------------------------------------------------- /config/samples/demo/metallb.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: metallb.io/v1beta1 2 | kind: MetalLB 3 | metadata: 4 | name: metallb 5 | namespace: metallb-system 6 | spec: 7 | nodeSelector: 8 | feature.node.kubernetes.io/metalLB.capable: 'true' 9 | loadBalancerClass: 'metallb.universe.tf/metallb' 10 | speakerTolerations: 11 | - key: "Example" 12 | operator: "Exists" 13 | effect: "NoExecute" 14 | -------------------------------------------------------------------------------- /config/samples/frr-k8s-basic.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: frrk8s.metallb.io/v1beta1 2 | kind: FRRConfiguration 3 | metadata: 4 | name: basic 5 | namespace: frr-k8s-system 6 | spec: 7 | bgp: 8 | routers: 9 | - asn: 64512 10 | neighbors: 11 | - address: 172.30.0.3 12 | asn: 64512 13 | port: 180 14 | toAdvertise: 15 | allowed: 16 | mode: all 17 | toReceive: 18 | allowed: 19 | mode: all 20 | -------------------------------------------------------------------------------- /config/samples/frr-k8s-status.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: frrk8s.metallb.io/v1beta1 2 | kind: FRRNodeState 3 | metadata: 4 | name: frr-k8s-worker 5 | status: 6 | lastConversionResult: success 7 | lastReloadResult: success 8 | runningConfig: | 9 | Building configuration... 10 | 11 | Current configuration: 12 | ! 13 | frr version 9.0.2_git 14 | frr defaults traditional 15 | hostname frr-k8s-worker 16 | log file /etc/frr/frr.log informational 17 | log timestamp precision 3 18 | no ipv6 forwarding 19 | service integrated-vtysh-config 20 | ! 21 | ip nht resolve-via-default 22 | ! 23 | ipv6 nht resolve-via-default 24 | ! 25 | end 26 | -------------------------------------------------------------------------------- /config/samples/frr-k8s-two-neighs.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: frrk8s.metallb.io/v1beta1 2 | kind: FRRConfiguration 3 | metadata: 4 | name: two-neighs 5 | namespace: frr-k8s-system 6 | spec: 7 | bgp: 8 | routers: 9 | - asn: 64512 10 | neighbors: 11 | - address: 172.30.0.3 12 | asn: 4200000000 13 | ebgpMultiHop: true 14 | port: 180 15 | toAdvertise: 16 | allowed: 17 | mode: all 18 | - address: 172.18.0.6 19 | asn: 4200000000 20 | port: 179 21 | toAdvertise: 22 | allowed: 23 | prefixes: 24 | - 192.168.2.0/24 25 | prefixes: 26 | - 192.168.2.0/24 27 | - 192.169.2.0/24 28 | -------------------------------------------------------------------------------- /config/samples/frr-k8s-two-routers-vrfs.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: frrk8s.metallb.io/v1beta1 2 | kind: FRRConfiguration 3 | metadata: 4 | name: two-routers-vrf 5 | namespace: frr-k8s-system 6 | spec: 7 | bgp: 8 | routers: 9 | - asn: 64513 10 | neighbors: 11 | - address: 172.32.0.3 12 | asn: 4200000000 13 | ebgpMultiHop: true 14 | port: 180 15 | toAdvertise: 16 | allowed: 17 | mode: all 18 | prefixes: 19 | - 192.168.2.0/24 20 | - 192.169.2.0/24 21 | - asn: 64515 22 | vrf: red 23 | neighbors: 24 | - address: 172.31.0.3 25 | asn: 4200000000 26 | port: 179 27 | toAdvertise: 28 | allowed: 29 | mode: all 30 | prefixes: 31 | - 192.168.2.0/24 32 | - 192.169.2.0/24 33 | -------------------------------------------------------------------------------- /config/samples/kustomization.yaml: -------------------------------------------------------------------------------- 1 | ## Append samples you want in your CSV to this file as resources ## 2 | resources: 3 | - metallb.yaml 4 | - metallb.io_v1beta1_bfdprofile.yaml 5 | - metallb.io_v1beta1_bgppeer.yaml 6 | - metallb.io_v1beta2_bgppeer.yaml 7 | - metallb.io_v1beta1_ipaddresspool.yaml 8 | - metallb_v1beta1_bgpadvertisement.yaml 9 | - metallb_v1beta1_l2advertisement.yaml 10 | - frr-k8s-basic.yaml 11 | - frr-k8s-two-neighs.yaml 12 | - frr-k8s-two-routers-vrfs.yaml 13 | - frr-k8s-status.yaml 14 | #+kubebuilder:scaffold:manifestskustomizesamples 15 | -------------------------------------------------------------------------------- /config/samples/metallb.io_v1beta1_bfdprofile.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: metallb.io/v1beta1 2 | kind: BFDProfile 3 | metadata: 4 | name: bfd-profile-sample 5 | namespace: metallb-system 6 | spec: 7 | receiveInterval: 380 8 | transmitInterval: 270 9 | -------------------------------------------------------------------------------- /config/samples/metallb.io_v1beta1_bgppeer.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: metallb.io/v1beta1 2 | kind: BGPPeer 3 | metadata: 4 | name: bgp-peer-beta1 5 | namespace: metallb-system 6 | spec: 7 | myASN: 64512 8 | peerASN: 64512 9 | peerAddress: 172.30.0.3 10 | -------------------------------------------------------------------------------- /config/samples/metallb.io_v1beta1_ipaddresspool.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: metallb.io/v1beta1 2 | kind: IPAddressPool 3 | metadata: 4 | name: ip-addresspool-sample1 5 | namespace: metallb-system 6 | spec: 7 | addresses: 8 | - 192.168.10.0/24 9 | - 192.168.9.1-192.168.9.5 10 | - fc00:f853:0ccd:e799::/124 11 | --- 12 | apiVersion: metallb.io/v1beta1 13 | kind: IPAddressPool 14 | metadata: 15 | name: ip-addresspool-sample2 16 | namespace: metallb-system 17 | labels: 18 | test: ipv4 19 | spec: 20 | addresses: 21 | - 172.20.0.100/24 22 | autoAssign: false 23 | --- 24 | apiVersion: metallb.io/v1beta1 25 | kind: IPAddressPool 26 | metadata: 27 | name: ip-addresspool-sample3 28 | namespace: metallb-system 29 | labels: 30 | test: ipv6 31 | spec: 32 | addresses: 33 | - 2002:2:2::1-2002:2:2::100 34 | -------------------------------------------------------------------------------- /config/samples/metallb.io_v1beta2_bgppeer.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: metallb.io/v1beta2 2 | kind: BGPPeer 3 | metadata: 4 | name: bgp-peer-sample1 5 | namespace: metallb-system 6 | spec: 7 | myASN: 64512 8 | peerASN: 64512 9 | peerAddress: 172.30.0.3 10 | --- 11 | apiVersion: metallb.io/v1beta2 12 | kind: BGPPeer 13 | metadata: 14 | name: bgp-peer-sample2 15 | namespace: metallb-system 16 | spec: 17 | myASN: 64512 18 | peerASN: 64512 19 | peerAddress: 172.30.0.3 20 | bfdProfile: bfd-profile-sample 21 | --- 22 | apiVersion: metallb.io/v1beta2 23 | kind: BGPPeer 24 | metadata: 25 | name: bgp-peer-sample3 26 | namespace: metallb-system 27 | spec: 28 | holdTime: 9m0s 29 | keepaliveTime: 0s 30 | myASN: 64512 31 | passwordSecret: 32 | name: secretname 33 | namespace: metallb-system 34 | peerASN: 64512 35 | peerAddress: 172.30.0.3 36 | peerPort: 180 37 | -------------------------------------------------------------------------------- /config/samples/metallb.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: metallb.io/v1beta1 2 | kind: MetalLB 3 | metadata: 4 | name: metallb 5 | namespace: metallb-system 6 | -------------------------------------------------------------------------------- /config/samples/metallb_frrk8s.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: metallb.io/v1beta1 2 | kind: MetalLB 3 | metadata: 4 | name: metallb 5 | namespace: metallb-system 6 | spec: 7 | bgpBackend: frr-k8s 8 | -------------------------------------------------------------------------------- /config/samples/metallb_v1beta1_bgpadvertisement.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: metallb.io/v1beta1 2 | kind: BGPAdvertisement 3 | metadata: 4 | name: bgp-adv-sample1 5 | namespace: metallb-system 6 | spec: 7 | ipAddressPools: 8 | - ip-addresspool-sample1 9 | peers: 10 | - ebgp-single-hop0 11 | --- 12 | apiVersion: metallb.io/v1beta1 13 | kind: BGPAdvertisement 14 | metadata: 15 | name: bgp-adv-sample2 16 | namespace: metallb-system 17 | spec: 18 | ipAddressPoolSelectors: 19 | - matchLabels: 20 | test: ipv6 21 | - matchLabels: 22 | test: ipv4 23 | --- 24 | apiVersion: metallb.io/v1beta1 25 | kind: BGPAdvertisement 26 | metadata: 27 | name: bgp-adv-sample3 28 | namespace: metallb-system 29 | spec: 30 | aggregationLength: 32 31 | aggregationLengthV6: 128 32 | communities: 33 | - 65535:65282 34 | ipAddressPools: 35 | - ip-addresspool-sample1 36 | localPref: 50 37 | --- 38 | apiVersion: metallb.io/v1beta1 39 | kind: BGPAdvertisement 40 | metadata: 41 | namespace: metallb-system 42 | name: bgp-adv-sample4 43 | spec: 44 | aggregationLength: 32 45 | aggregationLengthV6: 128 46 | communities: 47 | - NO_ADVERTISE 48 | ipAddressPools: 49 | - ip-addresspool-sample1 50 | localPref: 50 51 | --- 52 | apiVersion: metallb.io/v1beta1 53 | kind: Community 54 | metadata: 55 | name: community1 56 | namespace: metallb-system 57 | spec: 58 | communities: 59 | - name: NO_ADVERTISE 60 | value: 65535:65282 61 | --- 62 | apiVersion: metallb.io/v1beta1 63 | kind: BGPAdvertisement 64 | metadata: 65 | name: bgp-adv-sample5 66 | namespace: metallb-system 67 | spec: 68 | aggregationLength: 32 69 | aggregationLengthV6: 128 70 | ipAddressPools: 71 | - ip-addresspool-sample1 72 | nodeSelectors: 73 | - matchLabels: 74 | kubernetes.io/hostname: kind-control-plane 75 | - matchLabels: 76 | kubernetes.io/hostname: kind-worker 77 | -------------------------------------------------------------------------------- /config/samples/metallb_v1beta1_l2advertisement.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: metallb.io/v1beta1 2 | kind: L2Advertisement 3 | metadata: 4 | name: l2-adv-sample1 5 | namespace: metallb-system 6 | spec: 7 | ipAddressPools: 8 | - ip-addresspool-sample1 9 | --- 10 | apiVersion: metallb.io/v1beta1 11 | kind: L2Advertisement 12 | metadata: 13 | name: l2-adv-sample2 14 | namespace: metallb-system 15 | spec: 16 | nodeSelectors: 17 | - matchLabels: 18 | kubernetes.io/hostname: kind-control-plane 19 | -------------------------------------------------------------------------------- /config/samples/peer.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: metallb.io/v1beta1 2 | kind: BGPPeer 3 | metadata: 4 | name: bgp-peer-sample1 5 | namespace: metallb-system 6 | spec: 7 | myASN: 64512 8 | peerASN: 64512 9 | peerAddress: 172.30.0.3 10 | -------------------------------------------------------------------------------- /config/scorecard/bases/config.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: scorecard.operatorframework.io/v1alpha3 2 | kind: Configuration 3 | metadata: 4 | name: config 5 | stages: 6 | - parallel: true 7 | tests: [] 8 | -------------------------------------------------------------------------------- /config/scorecard/kustomization.yaml: -------------------------------------------------------------------------------- 1 | resources: 2 | - bases/config.yaml 3 | patchesJson6902: 4 | - path: patches/basic.config.yaml 5 | target: 6 | group: scorecard.operatorframework.io 7 | version: v1alpha3 8 | kind: Configuration 9 | name: config 10 | - path: patches/olm.config.yaml 11 | target: 12 | group: scorecard.operatorframework.io 13 | version: v1alpha3 14 | kind: Configuration 15 | name: config 16 | # +kubebuilder:scaffold:patchesJson6902 17 | -------------------------------------------------------------------------------- /config/scorecard/patches/basic.config.yaml: -------------------------------------------------------------------------------- 1 | - op: add 2 | path: /stages/0/tests/- 3 | value: 4 | entrypoint: 5 | - scorecard-test 6 | - basic-check-spec 7 | image: quay.io/operator-framework/scorecard-test:master 8 | labels: 9 | suite: basic 10 | test: basic-check-spec-test 11 | -------------------------------------------------------------------------------- /config/scorecard/patches/olm.config.yaml: -------------------------------------------------------------------------------- 1 | - op: add 2 | path: /stages/0/tests/- 3 | value: 4 | entrypoint: 5 | - scorecard-test 6 | - olm-bundle-validation 7 | image: quay.io/operator-framework/scorecard-test:master 8 | labels: 9 | suite: olm 10 | test: olm-bundle-validation-test 11 | - op: add 12 | path: /stages/0/tests/- 13 | value: 14 | entrypoint: 15 | - scorecard-test 16 | - olm-crds-have-validation 17 | image: quay.io/operator-framework/scorecard-test:master 18 | labels: 19 | suite: olm 20 | test: olm-crds-have-validation-test 21 | - op: add 22 | path: /stages/0/tests/- 23 | value: 24 | entrypoint: 25 | - scorecard-test 26 | - olm-crds-have-resources 27 | image: quay.io/operator-framework/scorecard-test:master 28 | labels: 29 | suite: olm 30 | test: olm-crds-have-resources-test 31 | - op: add 32 | path: /stages/0/tests/- 33 | value: 34 | entrypoint: 35 | - scorecard-test 36 | - olm-spec-descriptors 37 | image: quay.io/operator-framework/scorecard-test:master 38 | labels: 39 | suite: olm 40 | test: olm-spec-descriptors-test 41 | - op: add 42 | path: /stages/0/tests/- 43 | value: 44 | entrypoint: 45 | - scorecard-test 46 | - olm-status-descriptors 47 | image: quay.io/operator-framework/scorecard-test:master 48 | labels: 49 | suite: olm 50 | test: olm-status-descriptors-test 51 | -------------------------------------------------------------------------------- /config/webhook/backend/backend.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | labels: 5 | app: metallb 6 | component: webhook-server 7 | name: webhook-server 8 | namespace: system 9 | spec: 10 | revisionHistoryLimit: 3 11 | selector: 12 | matchLabels: 13 | app: metallb 14 | component: webhook-server 15 | template: 16 | metadata: 17 | annotations: 18 | prometheus.io/port: '7472' 19 | prometheus.io/scrape: 'true' 20 | labels: 21 | app: metallb 22 | component: webhook-server 23 | spec: 24 | containers: 25 | - args: 26 | - --webhook-mode=onlywebhook 27 | - --port=7472 28 | - --log-level=info 29 | image: controller 30 | name: webhook-server 31 | ports: 32 | - containerPort: 7472 33 | name: monitoring 34 | - containerPort: 9443 35 | name: webhook-server 36 | protocol: TCP 37 | livenessProbe: 38 | httpGet: 39 | path: /metrics 40 | port: monitoring 41 | initialDelaySeconds: 10 42 | periodSeconds: 10 43 | timeoutSeconds: 1 44 | successThreshold: 1 45 | failureThreshold: 3 46 | readinessProbe: 47 | httpGet: 48 | path: /metrics 49 | port: monitoring 50 | initialDelaySeconds: 10 51 | periodSeconds: 10 52 | timeoutSeconds: 1 53 | successThreshold: 1 54 | failureThreshold: 3 55 | securityContext: 56 | allowPrivilegeEscalation: false 57 | capabilities: 58 | drop: 59 | - all 60 | readOnlyRootFilesystem: true 61 | volumeMounts: 62 | - mountPath: /tmp/k8s-webhook-server/serving-certs 63 | name: cert 64 | readOnly: true 65 | env: 66 | - name: METALLB_BGP_TYPE 67 | value: "native" 68 | nodeSelector: 69 | kubernetes.io/os: linux 70 | securityContext: 71 | runAsNonRoot: true 72 | runAsUser: 65534 73 | fsGroup: 65534 74 | serviceAccountName: controller 75 | terminationGracePeriodSeconds: 0 76 | volumes: 77 | - name: cert 78 | secret: 79 | defaultMode: 420 80 | secretName: metallb-webhook-cert 81 | -------------------------------------------------------------------------------- /config/webhook/backend/kustomization.yaml: -------------------------------------------------------------------------------- 1 | resources: 2 | - backend.yaml 3 | apiVersion: kustomize.config.k8s.io/v1beta1 4 | kind: Kustomization 5 | images: 6 | - name: controller 7 | newName: quay.io/metallb/controller 8 | newTag: main 9 | -------------------------------------------------------------------------------- /config/webhook/kustomization.yaml: -------------------------------------------------------------------------------- 1 | # These resources constitute the fully configured set of manifests 2 | # used to generate the 'manifests/' directory in a bundle. 3 | resources: 4 | - ./webhook 5 | - ./backend 6 | - manifests.yaml 7 | - secret.yaml 8 | - operator-service.yaml 9 | patchesStrategicMerge: 10 | - service.yaml 11 | -------------------------------------------------------------------------------- /config/webhook/manifests.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: admissionregistration.k8s.io/v1 3 | kind: ValidatingWebhookConfiguration 4 | metadata: 5 | name: metallb-operator-webhook-configuration 6 | webhooks: 7 | - admissionReviewVersions: 8 | - v1 9 | clientConfig: 10 | service: 11 | name: metallb-operator-webhook-service 12 | namespace: system 13 | path: /validate-metallb-io-v1beta1-metallb 14 | failurePolicy: Fail 15 | name: metallbvalidationwebhook.metallb.io 16 | rules: 17 | - apiGroups: 18 | - metallb.io 19 | apiVersions: 20 | - v1beta1 21 | operations: 22 | - CREATE 23 | - UPDATE 24 | resources: 25 | - metallbs 26 | sideEffects: None 27 | -------------------------------------------------------------------------------- /config/webhook/operator-service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: metallb-operator-webhook-service 5 | namespace: system 6 | spec: 7 | ports: 8 | - port: 443 9 | targetPort: 9443 10 | selector: 11 | control-plane: controller-manager 12 | -------------------------------------------------------------------------------- /config/webhook/secret.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Secret 3 | metadata: 4 | name: metallb-operator-webhook-server-cert 5 | -------------------------------------------------------------------------------- /config/webhook/service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: metallb-webhook-service 5 | namespace: system 6 | spec: 7 | selector: 8 | component: webhook-server -------------------------------------------------------------------------------- /config/webhook/webhook/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | 4 | resources: 5 | - manifests.yaml 6 | - service.yaml 7 | - secret.yaml 8 | 9 | configurations: 10 | - kustomizeconfig.yaml 11 | 12 | patches: 13 | - path: patches/patch_webhook_configuration.yaml 14 | target: 15 | kind: ValidatingWebhookConfiguration 16 | name: validating-webhook-configuration 17 | version: v1 18 | -------------------------------------------------------------------------------- /config/webhook/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: ValidatingWebhookConfiguration 8 | group: admissionregistration.k8s.io 9 | path: webhooks/clientConfig/service/name 10 | 11 | namespace: 12 | - kind: ValidatingWebhookConfiguration 13 | group: admissionregistration.k8s.io 14 | path: webhooks/clientConfig/service/namespace 15 | create: true 16 | 17 | varReference: 18 | - path: metadata/annotations 19 | -------------------------------------------------------------------------------- /config/webhook/webhook/manifests.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: admissionregistration.k8s.io/v1 3 | kind: ValidatingWebhookConfiguration 4 | metadata: 5 | creationTimestamp: null 6 | name: validating-webhook-configuration 7 | webhooks: 8 | - admissionReviewVersions: 9 | - v1 10 | clientConfig: 11 | service: 12 | name: metallb-webhook-service 13 | namespace: system 14 | path: /validate-metallb-io-v1beta2-bgppeer 15 | failurePolicy: Fail 16 | name: bgppeersvalidationwebhook.metallb.io 17 | rules: 18 | - apiGroups: 19 | - metallb.io 20 | apiVersions: 21 | - v1beta2 22 | operations: 23 | - CREATE 24 | - UPDATE 25 | resources: 26 | - bgppeers 27 | sideEffects: None 28 | - admissionReviewVersions: 29 | - v1 30 | clientConfig: 31 | service: 32 | name: metallb-webhook-service 33 | namespace: system 34 | path: /validate-metallb-io-v1beta1-bfdprofile 35 | failurePolicy: Fail 36 | name: bfdprofilevalidationwebhook.metallb.io 37 | rules: 38 | - apiGroups: 39 | - metallb.io 40 | apiVersions: 41 | - v1beta1 42 | operations: 43 | - CREATE 44 | - DELETE 45 | resources: 46 | - bfdprofiles 47 | sideEffects: None 48 | - admissionReviewVersions: 49 | - v1 50 | clientConfig: 51 | service: 52 | name: metallb-webhook-service 53 | namespace: system 54 | path: /validate-metallb-io-v1beta1-bgpadvertisement 55 | failurePolicy: Fail 56 | name: bgpadvertisementvalidationwebhook.metallb.io 57 | rules: 58 | - apiGroups: 59 | - metallb.io 60 | apiVersions: 61 | - v1beta1 62 | operations: 63 | - CREATE 64 | - UPDATE 65 | resources: 66 | - bgpadvertisements 67 | sideEffects: None 68 | - admissionReviewVersions: 69 | - v1 70 | clientConfig: 71 | service: 72 | name: metallb-webhook-service 73 | namespace: system 74 | path: /validate-metallb-io-v1beta1-community 75 | failurePolicy: Fail 76 | name: communityvalidationwebhook.metallb.io 77 | rules: 78 | - apiGroups: 79 | - metallb.io 80 | apiVersions: 81 | - v1beta1 82 | operations: 83 | - CREATE 84 | - UPDATE 85 | resources: 86 | - communities 87 | sideEffects: None 88 | - admissionReviewVersions: 89 | - v1 90 | clientConfig: 91 | service: 92 | name: metallb-webhook-service 93 | namespace: system 94 | path: /validate-metallb-io-v1beta1-ipaddresspool 95 | failurePolicy: Fail 96 | name: ipaddresspoolvalidationwebhook.metallb.io 97 | rules: 98 | - apiGroups: 99 | - metallb.io 100 | apiVersions: 101 | - v1beta1 102 | operations: 103 | - CREATE 104 | - UPDATE 105 | resources: 106 | - ipaddresspools 107 | sideEffects: None 108 | - admissionReviewVersions: 109 | - v1 110 | clientConfig: 111 | service: 112 | name: metallb-webhook-service 113 | namespace: system 114 | path: /validate-metallb-io-v1beta1-l2advertisement 115 | failurePolicy: Fail 116 | name: l2advertisementvalidationwebhook.metallb.io 117 | rules: 118 | - apiGroups: 119 | - metallb.io 120 | apiVersions: 121 | - v1beta1 122 | operations: 123 | - CREATE 124 | - UPDATE 125 | resources: 126 | - l2advertisements 127 | sideEffects: None 128 | -------------------------------------------------------------------------------- /config/webhook/webhook/patches/patch_webhook_configuration.yaml: -------------------------------------------------------------------------------- 1 | - op: replace 2 | path: /metadata/name 3 | value: metallb-webhook-configuration 4 | -------------------------------------------------------------------------------- /config/webhook/webhook/patches/webhook_conversion.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apiextensions.k8s.io/v1 2 | kind: CustomResourceDefinition 3 | metadata: 4 | name: bgppeers.metallb.io 5 | spec: 6 | conversion: 7 | strategy: Webhook 8 | webhook: 9 | conversionReviewVersions: ["v1beta1", "v1beta2"] 10 | clientConfig: 11 | # this is "\n" used as a placeholder, otherwise it will be rejected by the apiserver for being blank, 12 | # but we're going to set it later using the cert-controller. 13 | caBundle: Cg== 14 | service: 15 | namespace: metallb-system 16 | name: metallb-webhook-service 17 | path: /convert 18 | -------------------------------------------------------------------------------- /config/webhook/webhook/secret.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Secret 3 | metadata: 4 | name: metallb-webhook-cert 5 | -------------------------------------------------------------------------------- /config/webhook/webhook/service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: metallb-webhook-service 5 | namespace: system 6 | spec: 7 | ports: 8 | - port: 443 9 | targetPort: 9443 10 | selector: 11 | component: controller 12 | -------------------------------------------------------------------------------- /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/bump_metallb.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | HASH=`git ls-remote https://github.com/metallb/metallb | grep refs/heads/main | cut -f 1` 4 | echo $HASH > hack/metallb_ref.txt 5 | -------------------------------------------------------------------------------- /hack/bump_versions.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | 4 | metallb_version=$(cat hack/metallb_version.txt) 5 | 6 | yq e --inplace '.spec.install.spec.deployments.[0].spec.template.spec.containers[0].env[] |= select (.name=="SPEAKER_IMAGE").value|="quay.io/metallb/speaker:'$metallb_version'"' bundle/manifests/metallb-operator.clusterserviceversion.yaml 7 | yq e --inplace '.spec.install.spec.deployments.[0].spec.template.spec.containers[0].env[] |= select (.name=="CONTROLLER_IMAGE").value|="quay.io/metallb/controller:'$metallb_version'"' bundle/manifests/metallb-operator.clusterserviceversion.yaml 8 | yq e --inplace '.spec.template.spec.containers[0].env[] |= select (.name=="SPEAKER_IMAGE").value|="quay.io/metallb/speaker:'$metallb_version'"' config/manager/env.yaml 9 | yq e --inplace '.spec.template.spec.containers[0].env[] |= select (.name=="CONTROLLER_IMAGE").value|="quay.io/metallb/controller:'$metallb_version'"' config/manager/env.yaml 10 | 11 | operator_version=$(cat hack/operator_version.txt) 12 | csv_version=$(echo "$operator_version" | sed 's/v//') 13 | if [ $operator_version = "main" ]; then # operator sdk doesn't like string versions, if we are on main we don't care about the version in the csv 14 | csv_version="0.0.0" 15 | fi 16 | 17 | yq e --inplace '.spec.install.spec.deployments.[0].spec.template.spec.containers[0].image |= "quay.io/metallb/metallb-operator:'$operator_version'"' bundle/manifests/metallb-operator.clusterserviceversion.yaml 18 | yq e --inplace '.spec.version |= "'$csv_version'"' bundle/manifests/metallb-operator.clusterserviceversion.yaml 19 | yq e --inplace '.images[] |= select (.name == "controller") |= .newTag="'$operator_version'"' config/manager/kustomization.yaml 20 | 21 | if [ "$operator_version" != "main" ]; then 22 | sed -i "s/name: metallb-operator.v.*$/name: metallb-operator.$operator_version/" bundle/manifests/metallb-operator.clusterserviceversion.yaml 23 | fi 24 | 25 | yq e --inplace '. |= select (.kind == "CatalogSource") |= .spec.image="quay.io/metallb/metallb-operator-bundle-index:'$operator_version'"' config/olm-install/install-resources.yaml 26 | 27 | sed -E -i "s/^VERSION \?= .*$/VERSION \?= $operator_version/g" Makefile 28 | 29 | sed -i "s/quay.io\/metallb\/speaker:.*$/quay.io\/metallb\/speaker:$metallb_version/g" bin/metallb-operator.yaml 30 | sed -i "s/quay.io\/metallb\/controller:.*$/quay.io\/metallb\/controller:$metallb_version/g" bin/metallb-operator.yaml 31 | sed -i "s/quay.io\/metallb\/metallb-operator:.*$/quay.io\/metallb\/metallb-operator:$operator_version/g" bin/metallb-operator.yaml 32 | -------------------------------------------------------------------------------- /hack/common.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | pushd . 6 | cd "$(dirname "$0")/.." 7 | 8 | function finish { 9 | popd 10 | } 11 | trap finish EXIT 12 | 13 | GOPATH="${GOPATH:-~/go}" 14 | export GOFLAGS="${GOFLAGS:-"-mod=vendor"}" 15 | 16 | export PATH=$PATH:$GOPATH/bin 17 | 18 | mkdir -p _cache 19 | 20 | export METALLB_COMMIT_ID=$(cat hack/metallb_ref.txt) 21 | export METALLB_PATH=_cache/metallb 22 | 23 | export METALLB_SC_FILE=$(dirname "$0")/securityContext.yaml 24 | 25 | function fetch_metallb() { 26 | if [[ ! -d "$METALLB_PATH" ]]; then 27 | curl -L https://github.com/metallb/metallb/tarball/"$METALLB_COMMIT_ID" | tar zx -C _cache 28 | rm -rf "$METALLB_PATH" 29 | mv _cache/metallb-metallb-* "$METALLB_PATH" 30 | fi 31 | } 32 | 33 | export FRRK8S_PATH=_cache/frr-k8s 34 | function fetch_frrk8s() { # first arg is frr-k8s version, corresponding to metallb's chart dependency 35 | if [[ ! -d "$FRRK8S_PATH" ]]; then 36 | curl -L https://github.com/metallb/frr-k8s/tarball/"$1" | tar zx -C _cache 37 | rm -rf "$FRRK8S_PATH" 38 | mv _cache/metallb-frr-k8s-* "$FRRK8S_PATH" 39 | fi 40 | } 41 | -------------------------------------------------------------------------------- /hack/compare-gen-manifests.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | . $(dirname "$0")/common.sh 4 | 5 | METALLB_MANIFESTS_FILE="metallb-frr-k8s.yaml" 6 | 7 | mv config/metallb_rbac/${METALLB_MANIFESTS_FILE} _cache/${METALLB_MANIFESTS_FILE}.rbac 8 | 9 | hack/generate-metallb-manifests.sh 10 | 11 | diff config/metallb_rbac/${METALLB_MANIFESTS_FILE} _cache/${METALLB_MANIFESTS_FILE}.rbac -q || { echo "Current MetalLB RBAC manifests differ from the RBAC manifests in the MetalLB repo"; exit 1; } 12 | -------------------------------------------------------------------------------- /hack/deploy_prometheus.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | kubectl apply --server-side -f hack/kube-prometheus/manifests/setup 4 | until kubectl get servicemonitors --all-namespaces ; do date; sleep 1; echo ""; done 5 | kubectl apply -f hack/kube-prometheus/manifests/ 6 | echo "Waiting for prometheus pods to be running" 7 | kubectl -n monitoring wait --for=condition=Ready --all pods --timeout 300s -------------------------------------------------------------------------------- /hack/fetch_latest_metallb.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | last_tag=$(git -c 'versionsort.suffix=-' \ 4 | ls-remote --exit-code --refs --sort='version:refname' --tags https://github.com/metallb/metallb.git '*.*.*' \ 5 | | tail --lines=1 \ 6 | | cut --delimiter='/' --fields=3) 7 | 8 | echo "$last_tag" > hack/metallb_version.txt 9 | -------------------------------------------------------------------------------- /hack/gen_relnotes.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -e 2 | set -x 3 | 4 | if [[ ! -v GITHUB_TOKEN ]]; then 5 | echo "GITHUB_TOKEN is not set, please set it with a token with read permissions on commits and PRs" 6 | exit 1 7 | fi 8 | 9 | script_dir=$(dirname "$(readlink -f "$0")") 10 | 11 | branch=$1 12 | from=$2 13 | to=$3 14 | release_notes=$(mktemp) 15 | 16 | end() { 17 | rm $release_notes 18 | } 19 | 20 | trap end EXIT SIGINT SIGTERM SIGSTOP 21 | 22 | GOFLAGS=-mod=mod go run k8s.io/release/cmd/release-notes@v0.16.5 \ 23 | --branch $branch \ 24 | --required-author "" \ 25 | --org metallb \ 26 | --dependencies=false \ 27 | --repo metallb-operator \ 28 | --start-sha $from \ 29 | --end-sha $to \ 30 | --output $release_notes 31 | 32 | cat $release_notes 33 | 34 | 35 | echo "Contributors" 36 | git log --format="%aN" $(git merge-base $to $from)..$to | sort -u | tr '\n' ',' | sed -e 's/,/, /g' 37 | -------------------------------------------------------------------------------- /hack/kind-multi-node-cluster-without-registry.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -o errexit 3 | 4 | # desired cluster name; default is "kind" 5 | KIND_CLUSTER_NAME="${KIND_CLUSTER_NAME:-kind}" 6 | 7 | cat </dev/null || true)" 24 | if [ "${running}" != 'true' ]; then 25 | docker run \ 26 | -d --restart=always -p "5000:5000" --name "kind-registry" \ 27 | registry:2 28 | fi 29 | 30 | fi 31 | 32 | # create a cluster with the local registry enabled in containerd 33 | "${KIND_BIN}" create cluster --name "${KIND_CLUSTER_NAME}" --config=${CONFIG_NAME} 34 | 35 | if [[ ! -z $KIND_WITH_REGISTRY ]]; then 36 | 37 | # connect the registry to the cluster network 38 | docker network connect "kind" "kind-registry" || true 39 | 40 | # Document the local registry 41 | # https://github.com/kubernetes/enhancements/tree/master/keps/sig-cluster-lifecycle/generic/1755-communicating-a-local-registry 42 | kubectl apply -f hack/kind/registry_configmap.yaml 43 | 44 | fi 45 | 46 | if docker network ls | grep -q network2; then 47 | docker network rm network2 48 | fi 49 | # Create a second network interface, useful for metallb's e2e tests 50 | docker network create --ipv6 --subnet fc00:f853:ccd:e791::/64 -d bridge network2 51 | 52 | KIND_NODES=$(kind get nodes --name "${KIND_CLUSTER_NAME}") 53 | for n in $KIND_NODES; do 54 | docker network connect network2 "$n" 55 | done 56 | 57 | # remove the exclude-from-external-loadbalancers annotation 58 | for node in $KIND_NODES; do 59 | kubectl label nodes $node node.kubernetes.io/exclude-from-external-load-balancers- 60 | done 61 | -------------------------------------------------------------------------------- /hack/kind/registry_configmap.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ConfigMap 3 | metadata: 4 | name: local-registry-hosting 5 | namespace: kube-public 6 | data: 7 | localRegistryHosting.v1: | 8 | host: "localhost:5000" 9 | help: "https://kind.sigs.k8s.io/docs/user/local-registry/" -------------------------------------------------------------------------------- /hack/kube-prometheus/README.md: -------------------------------------------------------------------------------- 1 | # Prometheus operator manifests 2 | 3 | This is a stripped down prometheus operator deployment following the instructions at 4 | https://github.com/prometheus-operator/kube-prometheus/blob/main/docs/customizing.md 5 | -------------------------------------------------------------------------------- /hack/kube-prometheus/build.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # This script uses arg $1 (name of *.jsonnet file to use) to generate the manifests/*.yaml files. 4 | 5 | set -e 6 | set -x 7 | # only exit with zero if all commands of the pipeline exit successfully 8 | set -o pipefail 9 | 10 | # Make sure to use project tooling 11 | PATH="$(pwd)/tmp/bin:${PATH}" 12 | 13 | # Make sure to start with a clean 'manifests' dir 14 | rm -rf manifests 15 | mkdir -p manifests/setup 16 | 17 | # Calling gojsontoyaml is optional, but we would like to generate yaml, not json 18 | jsonnet -J vendor -m manifests "${1-example.jsonnet}" | xargs -I{} sh -c 'cat {} | gojsontoyaml > {}.yaml' -- {} 19 | 20 | # Make sure to remove json files 21 | find manifests -type f ! -name '*.yaml' -delete 22 | rm -f kustomization 23 | 24 | -------------------------------------------------------------------------------- /hack/kube-prometheus/config.jsonnet: -------------------------------------------------------------------------------- 1 | local kp = 2 | (import 'kube-prometheus/main.libsonnet') + 3 | // Uncomment the following imports to enable its patches 4 | // (import 'kube-prometheus/addons/anti-affinity.libsonnet') + 5 | // (import 'kube-prometheus/addons/managed-cluster.libsonnet') + 6 | // (import 'kube-prometheus/addons/node-ports.libsonnet') + 7 | // (import 'kube-prometheus/addons/static-etcd.libsonnet') + 8 | // (import 'kube-prometheus/addons/custom-metrics.libsonnet') + 9 | // (import 'kube-prometheus/addons/external-metrics.libsonnet') + 10 | // (import 'kube-prometheus/addons/pyrra.libsonnet') + 11 | { 12 | values+:: { 13 | common+: { 14 | namespace: 'monitoring', 15 | }, 16 | }, 17 | }; 18 | 19 | { 'setup/0namespace-namespace': kp.kubePrometheus.namespace } + 20 | { 21 | ['setup/prometheus-operator-' + name]: kp.prometheusOperator[name] 22 | for name in std.filter((function(name) name != 'serviceMonitor' && name != 'prometheusRule'), std.objectFields(kp.prometheusOperator)) 23 | } + 24 | // { 'setup/pyrra-slo-CustomResourceDefinition': kp.pyrra.crd } + 25 | // serviceMonitor and prometheusRule are separated so that they can be created after the CRDs are ready 26 | { 'prometheus-operator-serviceMonitor': kp.prometheusOperator.serviceMonitor } + 27 | { 'prometheus-operator-prometheusRule': kp.prometheusOperator.prometheusRule } + 28 | { 'kube-prometheus-prometheusRule': kp.kubePrometheus.prometheusRule } + 29 | // { ['pyrra-' + name]: kp.pyrra[name] for name in std.objectFields(kp.pyrra) if name != 'crd' } + 30 | { ['kubernetes-' + name]: kp.kubernetesControlPlane[name] for name in std.objectFields(kp.kubernetesControlPlane) } 31 | { ['prometheus-' + name]: kp.prometheus[name] for name in std.objectFields(kp.prometheus) } 32 | -------------------------------------------------------------------------------- /hack/kube-prometheus/jsonnetfile.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 1, 3 | "dependencies": [ 4 | { 5 | "source": { 6 | "git": { 7 | "remote": "https://github.com/prometheus-operator/kube-prometheus.git", 8 | "subdir": "jsonnet/kube-prometheus" 9 | } 10 | }, 11 | "version": "v0.11.0" 12 | } 13 | ], 14 | "legacyImports": true 15 | } 16 | -------------------------------------------------------------------------------- /hack/kube-prometheus/manifests/kubernetes-serviceMonitorCoreDNS.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: monitoring.coreos.com/v1 2 | kind: ServiceMonitor 3 | metadata: 4 | labels: 5 | app.kubernetes.io/name: coredns 6 | app.kubernetes.io/part-of: kube-prometheus 7 | name: coredns 8 | namespace: monitoring 9 | spec: 10 | endpoints: 11 | - bearerTokenFile: /var/run/secrets/kubernetes.io/serviceaccount/token 12 | interval: 15s 13 | metricRelabelings: 14 | - action: drop 15 | regex: coredns_cache_misses_total 16 | sourceLabels: 17 | - __name__ 18 | port: metrics 19 | jobLabel: app.kubernetes.io/name 20 | namespaceSelector: 21 | matchNames: 22 | - kube-system 23 | selector: 24 | matchLabels: 25 | k8s-app: kube-dns 26 | -------------------------------------------------------------------------------- /hack/kube-prometheus/manifests/kubernetes-serviceMonitorKubeScheduler.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: monitoring.coreos.com/v1 2 | kind: ServiceMonitor 3 | metadata: 4 | labels: 5 | app.kubernetes.io/name: kube-scheduler 6 | app.kubernetes.io/part-of: kube-prometheus 7 | name: kube-scheduler 8 | namespace: monitoring 9 | spec: 10 | endpoints: 11 | - bearerTokenFile: /var/run/secrets/kubernetes.io/serviceaccount/token 12 | interval: 30s 13 | port: https-metrics 14 | scheme: https 15 | tlsConfig: 16 | insecureSkipVerify: true 17 | jobLabel: app.kubernetes.io/name 18 | namespaceSelector: 19 | matchNames: 20 | - kube-system 21 | selector: 22 | matchLabels: 23 | app.kubernetes.io/name: kube-scheduler 24 | -------------------------------------------------------------------------------- /hack/kube-prometheus/manifests/prometheus-clusterRole.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: ClusterRole 3 | metadata: 4 | labels: 5 | app.kubernetes.io/component: prometheus 6 | app.kubernetes.io/instance: k8s 7 | app.kubernetes.io/name: prometheus 8 | app.kubernetes.io/part-of: kube-prometheus 9 | app.kubernetes.io/version: 2.36.1 10 | name: prometheus-k8s 11 | rules: 12 | - apiGroups: 13 | - "" 14 | resources: 15 | - nodes/metrics 16 | verbs: 17 | - get 18 | - nonResourceURLs: 19 | - /metrics 20 | verbs: 21 | - get 22 | -------------------------------------------------------------------------------- /hack/kube-prometheus/manifests/prometheus-clusterRoleBinding.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: ClusterRoleBinding 3 | metadata: 4 | labels: 5 | app.kubernetes.io/component: prometheus 6 | app.kubernetes.io/instance: k8s 7 | app.kubernetes.io/name: prometheus 8 | app.kubernetes.io/part-of: kube-prometheus 9 | app.kubernetes.io/version: 2.36.1 10 | name: prometheus-k8s 11 | roleRef: 12 | apiGroup: rbac.authorization.k8s.io 13 | kind: ClusterRole 14 | name: prometheus-k8s 15 | subjects: 16 | - kind: ServiceAccount 17 | name: prometheus-k8s 18 | namespace: monitoring 19 | -------------------------------------------------------------------------------- /hack/kube-prometheus/manifests/prometheus-networkPolicy.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: networking.k8s.io/v1 2 | kind: NetworkPolicy 3 | metadata: 4 | labels: 5 | app.kubernetes.io/component: prometheus 6 | app.kubernetes.io/instance: k8s 7 | app.kubernetes.io/name: prometheus 8 | app.kubernetes.io/part-of: kube-prometheus 9 | app.kubernetes.io/version: 2.36.1 10 | name: prometheus-k8s 11 | namespace: monitoring 12 | spec: 13 | egress: 14 | - {} 15 | ingress: 16 | - from: 17 | - podSelector: 18 | matchLabels: 19 | app.kubernetes.io/name: prometheus 20 | ports: 21 | - port: 9090 22 | protocol: TCP 23 | - port: 8080 24 | protocol: TCP 25 | - from: 26 | - podSelector: 27 | matchLabels: 28 | app.kubernetes.io/name: grafana 29 | ports: 30 | - port: 9090 31 | protocol: TCP 32 | podSelector: 33 | matchLabels: 34 | app.kubernetes.io/component: prometheus 35 | app.kubernetes.io/instance: k8s 36 | app.kubernetes.io/name: prometheus 37 | app.kubernetes.io/part-of: kube-prometheus 38 | policyTypes: 39 | - Egress 40 | - Ingress 41 | -------------------------------------------------------------------------------- /hack/kube-prometheus/manifests/prometheus-operator-serviceMonitor.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: monitoring.coreos.com/v1 2 | kind: ServiceMonitor 3 | metadata: 4 | labels: 5 | app.kubernetes.io/component: controller 6 | app.kubernetes.io/name: prometheus-operator 7 | app.kubernetes.io/part-of: kube-prometheus 8 | app.kubernetes.io/version: 0.57.0 9 | name: prometheus-operator 10 | namespace: monitoring 11 | spec: 12 | endpoints: 13 | - bearerTokenFile: /var/run/secrets/kubernetes.io/serviceaccount/token 14 | honorLabels: true 15 | port: https 16 | scheme: https 17 | tlsConfig: 18 | insecureSkipVerify: true 19 | selector: 20 | matchLabels: 21 | app.kubernetes.io/component: controller 22 | app.kubernetes.io/name: prometheus-operator 23 | app.kubernetes.io/part-of: kube-prometheus 24 | app.kubernetes.io/version: 0.57.0 25 | -------------------------------------------------------------------------------- /hack/kube-prometheus/manifests/prometheus-podDisruptionBudget.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: policy/v1 2 | kind: PodDisruptionBudget 3 | metadata: 4 | labels: 5 | app.kubernetes.io/component: prometheus 6 | app.kubernetes.io/instance: k8s 7 | app.kubernetes.io/name: prometheus 8 | app.kubernetes.io/part-of: kube-prometheus 9 | app.kubernetes.io/version: 2.36.1 10 | name: prometheus-k8s 11 | namespace: monitoring 12 | spec: 13 | minAvailable: 1 14 | selector: 15 | matchLabels: 16 | app.kubernetes.io/component: prometheus 17 | app.kubernetes.io/instance: k8s 18 | app.kubernetes.io/name: prometheus 19 | app.kubernetes.io/part-of: kube-prometheus 20 | -------------------------------------------------------------------------------- /hack/kube-prometheus/manifests/prometheus-prometheus.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: monitoring.coreos.com/v1 2 | kind: Prometheus 3 | metadata: 4 | labels: 5 | app.kubernetes.io/component: prometheus 6 | app.kubernetes.io/instance: k8s 7 | app.kubernetes.io/name: prometheus 8 | app.kubernetes.io/part-of: kube-prometheus 9 | app.kubernetes.io/version: 2.36.1 10 | name: k8s 11 | namespace: monitoring 12 | spec: 13 | alerting: 14 | alertmanagers: 15 | - apiVersion: v2 16 | name: alertmanager-main 17 | namespace: monitoring 18 | port: web 19 | enableFeatures: [] 20 | externalLabels: {} 21 | image: quay.io/prometheus/prometheus:v2.36.1 22 | nodeSelector: 23 | kubernetes.io/os: linux 24 | podMetadata: 25 | labels: 26 | app.kubernetes.io/component: prometheus 27 | app.kubernetes.io/instance: k8s 28 | app.kubernetes.io/name: prometheus 29 | app.kubernetes.io/part-of: kube-prometheus 30 | app.kubernetes.io/version: 2.36.1 31 | podMonitorNamespaceSelector: {} 32 | podMonitorSelector: {} 33 | probeNamespaceSelector: {} 34 | probeSelector: {} 35 | replicas: 2 36 | resources: 37 | requests: 38 | memory: 400Mi 39 | ruleNamespaceSelector: {} 40 | ruleSelector: {} 41 | securityContext: 42 | fsGroup: 2000 43 | runAsNonRoot: true 44 | runAsUser: 1000 45 | serviceAccountName: prometheus-k8s 46 | serviceMonitorNamespaceSelector: {} 47 | serviceMonitorSelector: {} 48 | version: 2.36.1 49 | -------------------------------------------------------------------------------- /hack/kube-prometheus/manifests/prometheus-roleBindingConfig.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: RoleBinding 3 | metadata: 4 | labels: 5 | app.kubernetes.io/component: prometheus 6 | app.kubernetes.io/instance: k8s 7 | app.kubernetes.io/name: prometheus 8 | app.kubernetes.io/part-of: kube-prometheus 9 | app.kubernetes.io/version: 2.36.1 10 | name: prometheus-k8s-config 11 | namespace: monitoring 12 | roleRef: 13 | apiGroup: rbac.authorization.k8s.io 14 | kind: Role 15 | name: prometheus-k8s-config 16 | subjects: 17 | - kind: ServiceAccount 18 | name: prometheus-k8s 19 | namespace: monitoring 20 | -------------------------------------------------------------------------------- /hack/kube-prometheus/manifests/prometheus-roleBindingSpecificNamespaces.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | items: 3 | - apiVersion: rbac.authorization.k8s.io/v1 4 | kind: RoleBinding 5 | metadata: 6 | labels: 7 | app.kubernetes.io/component: prometheus 8 | app.kubernetes.io/instance: k8s 9 | app.kubernetes.io/name: prometheus 10 | app.kubernetes.io/part-of: kube-prometheus 11 | app.kubernetes.io/version: 2.36.1 12 | name: prometheus-k8s 13 | namespace: default 14 | roleRef: 15 | apiGroup: rbac.authorization.k8s.io 16 | kind: Role 17 | name: prometheus-k8s 18 | subjects: 19 | - kind: ServiceAccount 20 | name: prometheus-k8s 21 | namespace: monitoring 22 | - apiVersion: rbac.authorization.k8s.io/v1 23 | kind: RoleBinding 24 | metadata: 25 | labels: 26 | app.kubernetes.io/component: prometheus 27 | app.kubernetes.io/instance: k8s 28 | app.kubernetes.io/name: prometheus 29 | app.kubernetes.io/part-of: kube-prometheus 30 | app.kubernetes.io/version: 2.36.1 31 | name: prometheus-k8s 32 | namespace: kube-system 33 | roleRef: 34 | apiGroup: rbac.authorization.k8s.io 35 | kind: Role 36 | name: prometheus-k8s 37 | subjects: 38 | - kind: ServiceAccount 39 | name: prometheus-k8s 40 | namespace: monitoring 41 | - apiVersion: rbac.authorization.k8s.io/v1 42 | kind: RoleBinding 43 | metadata: 44 | labels: 45 | app.kubernetes.io/component: prometheus 46 | app.kubernetes.io/instance: k8s 47 | app.kubernetes.io/name: prometheus 48 | app.kubernetes.io/part-of: kube-prometheus 49 | app.kubernetes.io/version: 2.36.1 50 | name: prometheus-k8s 51 | namespace: monitoring 52 | roleRef: 53 | apiGroup: rbac.authorization.k8s.io 54 | kind: Role 55 | name: prometheus-k8s 56 | subjects: 57 | - kind: ServiceAccount 58 | name: prometheus-k8s 59 | namespace: monitoring 60 | kind: RoleBindingList 61 | -------------------------------------------------------------------------------- /hack/kube-prometheus/manifests/prometheus-roleConfig.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: Role 3 | metadata: 4 | labels: 5 | app.kubernetes.io/component: prometheus 6 | app.kubernetes.io/instance: k8s 7 | app.kubernetes.io/name: prometheus 8 | app.kubernetes.io/part-of: kube-prometheus 9 | app.kubernetes.io/version: 2.36.1 10 | name: prometheus-k8s-config 11 | namespace: monitoring 12 | rules: 13 | - apiGroups: 14 | - "" 15 | resources: 16 | - configmaps 17 | verbs: 18 | - get 19 | -------------------------------------------------------------------------------- /hack/kube-prometheus/manifests/prometheus-roleSpecificNamespaces.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | items: 3 | - apiVersion: rbac.authorization.k8s.io/v1 4 | kind: Role 5 | metadata: 6 | labels: 7 | app.kubernetes.io/component: prometheus 8 | app.kubernetes.io/instance: k8s 9 | app.kubernetes.io/name: prometheus 10 | app.kubernetes.io/part-of: kube-prometheus 11 | app.kubernetes.io/version: 2.36.1 12 | name: prometheus-k8s 13 | namespace: default 14 | rules: 15 | - apiGroups: 16 | - "" 17 | resources: 18 | - services 19 | - endpoints 20 | - pods 21 | verbs: 22 | - get 23 | - list 24 | - watch 25 | - apiGroups: 26 | - extensions 27 | resources: 28 | - ingresses 29 | verbs: 30 | - get 31 | - list 32 | - watch 33 | - apiGroups: 34 | - networking.k8s.io 35 | resources: 36 | - ingresses 37 | verbs: 38 | - get 39 | - list 40 | - watch 41 | - apiVersion: rbac.authorization.k8s.io/v1 42 | kind: Role 43 | metadata: 44 | labels: 45 | app.kubernetes.io/component: prometheus 46 | app.kubernetes.io/instance: k8s 47 | app.kubernetes.io/name: prometheus 48 | app.kubernetes.io/part-of: kube-prometheus 49 | app.kubernetes.io/version: 2.36.1 50 | name: prometheus-k8s 51 | namespace: kube-system 52 | rules: 53 | - apiGroups: 54 | - "" 55 | resources: 56 | - services 57 | - endpoints 58 | - pods 59 | verbs: 60 | - get 61 | - list 62 | - watch 63 | - apiGroups: 64 | - extensions 65 | resources: 66 | - ingresses 67 | verbs: 68 | - get 69 | - list 70 | - watch 71 | - apiGroups: 72 | - networking.k8s.io 73 | resources: 74 | - ingresses 75 | verbs: 76 | - get 77 | - list 78 | - watch 79 | - apiVersion: rbac.authorization.k8s.io/v1 80 | kind: Role 81 | metadata: 82 | labels: 83 | app.kubernetes.io/component: prometheus 84 | app.kubernetes.io/instance: k8s 85 | app.kubernetes.io/name: prometheus 86 | app.kubernetes.io/part-of: kube-prometheus 87 | app.kubernetes.io/version: 2.36.1 88 | name: prometheus-k8s 89 | namespace: monitoring 90 | rules: 91 | - apiGroups: 92 | - "" 93 | resources: 94 | - services 95 | - endpoints 96 | - pods 97 | verbs: 98 | - get 99 | - list 100 | - watch 101 | - apiGroups: 102 | - extensions 103 | resources: 104 | - ingresses 105 | verbs: 106 | - get 107 | - list 108 | - watch 109 | - apiGroups: 110 | - networking.k8s.io 111 | resources: 112 | - ingresses 113 | verbs: 114 | - get 115 | - list 116 | - watch 117 | kind: RoleList 118 | -------------------------------------------------------------------------------- /hack/kube-prometheus/manifests/prometheus-service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | labels: 5 | app.kubernetes.io/component: prometheus 6 | app.kubernetes.io/instance: k8s 7 | app.kubernetes.io/name: prometheus 8 | app.kubernetes.io/part-of: kube-prometheus 9 | app.kubernetes.io/version: 2.36.1 10 | name: prometheus-k8s 11 | namespace: monitoring 12 | spec: 13 | ports: 14 | - name: web 15 | port: 9090 16 | targetPort: web 17 | - name: reloader-web 18 | port: 8080 19 | targetPort: reloader-web 20 | selector: 21 | app.kubernetes.io/component: prometheus 22 | app.kubernetes.io/instance: k8s 23 | app.kubernetes.io/name: prometheus 24 | app.kubernetes.io/part-of: kube-prometheus 25 | sessionAffinity: ClientIP 26 | -------------------------------------------------------------------------------- /hack/kube-prometheus/manifests/prometheus-serviceAccount.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | automountServiceAccountToken: false 3 | kind: ServiceAccount 4 | metadata: 5 | labels: 6 | app.kubernetes.io/component: prometheus 7 | app.kubernetes.io/instance: k8s 8 | app.kubernetes.io/name: prometheus 9 | app.kubernetes.io/part-of: kube-prometheus 10 | app.kubernetes.io/version: 2.36.1 11 | name: prometheus-k8s 12 | namespace: monitoring 13 | -------------------------------------------------------------------------------- /hack/kube-prometheus/manifests/prometheus-serviceMonitor.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: monitoring.coreos.com/v1 2 | kind: ServiceMonitor 3 | metadata: 4 | labels: 5 | app.kubernetes.io/component: prometheus 6 | app.kubernetes.io/instance: k8s 7 | app.kubernetes.io/name: prometheus 8 | app.kubernetes.io/part-of: kube-prometheus 9 | app.kubernetes.io/version: 2.36.1 10 | name: prometheus-k8s 11 | namespace: monitoring 12 | spec: 13 | endpoints: 14 | - interval: 30s 15 | port: web 16 | - interval: 30s 17 | port: reloader-web 18 | selector: 19 | matchLabels: 20 | app.kubernetes.io/component: prometheus 21 | app.kubernetes.io/instance: k8s 22 | app.kubernetes.io/name: prometheus 23 | app.kubernetes.io/part-of: kube-prometheus 24 | -------------------------------------------------------------------------------- /hack/kube-prometheus/manifests/setup/0namespace-namespace.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Namespace 3 | metadata: 4 | name: monitoring 5 | -------------------------------------------------------------------------------- /hack/kube-prometheus/manifests/setup/prometheus-operator-clusterRole.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: ClusterRole 3 | metadata: 4 | labels: 5 | app.kubernetes.io/component: controller 6 | app.kubernetes.io/name: prometheus-operator 7 | app.kubernetes.io/part-of: kube-prometheus 8 | app.kubernetes.io/version: 0.57.0 9 | name: prometheus-operator 10 | rules: 11 | - apiGroups: 12 | - monitoring.coreos.com 13 | resources: 14 | - alertmanagers 15 | - alertmanagers/finalizers 16 | - alertmanagerconfigs 17 | - prometheuses 18 | - prometheuses/finalizers 19 | - prometheuses/status 20 | - thanosrulers 21 | - thanosrulers/finalizers 22 | - servicemonitors 23 | - podmonitors 24 | - probes 25 | - prometheusrules 26 | verbs: 27 | - '*' 28 | - apiGroups: 29 | - apps 30 | resources: 31 | - statefulsets 32 | verbs: 33 | - '*' 34 | - apiGroups: 35 | - "" 36 | resources: 37 | - configmaps 38 | - secrets 39 | verbs: 40 | - '*' 41 | - apiGroups: 42 | - "" 43 | resources: 44 | - pods 45 | verbs: 46 | - list 47 | - delete 48 | - apiGroups: 49 | - "" 50 | resources: 51 | - services 52 | - services/finalizers 53 | - endpoints 54 | verbs: 55 | - get 56 | - create 57 | - update 58 | - delete 59 | - apiGroups: 60 | - "" 61 | resources: 62 | - nodes 63 | verbs: 64 | - list 65 | - watch 66 | - apiGroups: 67 | - "" 68 | resources: 69 | - namespaces 70 | verbs: 71 | - get 72 | - list 73 | - watch 74 | - apiGroups: 75 | - networking.k8s.io 76 | resources: 77 | - ingresses 78 | verbs: 79 | - get 80 | - list 81 | - watch 82 | - apiGroups: 83 | - authentication.k8s.io 84 | resources: 85 | - tokenreviews 86 | verbs: 87 | - create 88 | - apiGroups: 89 | - authorization.k8s.io 90 | resources: 91 | - subjectaccessreviews 92 | verbs: 93 | - create 94 | -------------------------------------------------------------------------------- /hack/kube-prometheus/manifests/setup/prometheus-operator-clusterRoleBinding.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: ClusterRoleBinding 3 | metadata: 4 | labels: 5 | app.kubernetes.io/component: controller 6 | app.kubernetes.io/name: prometheus-operator 7 | app.kubernetes.io/part-of: kube-prometheus 8 | app.kubernetes.io/version: 0.57.0 9 | name: prometheus-operator 10 | roleRef: 11 | apiGroup: rbac.authorization.k8s.io 12 | kind: ClusterRole 13 | name: prometheus-operator 14 | subjects: 15 | - kind: ServiceAccount 16 | name: prometheus-operator 17 | namespace: monitoring 18 | -------------------------------------------------------------------------------- /hack/kube-prometheus/manifests/setup/prometheus-operator-deployment.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | labels: 5 | app.kubernetes.io/component: controller 6 | app.kubernetes.io/name: prometheus-operator 7 | app.kubernetes.io/part-of: kube-prometheus 8 | app.kubernetes.io/version: 0.57.0 9 | name: prometheus-operator 10 | namespace: monitoring 11 | spec: 12 | replicas: 1 13 | selector: 14 | matchLabels: 15 | app.kubernetes.io/component: controller 16 | app.kubernetes.io/name: prometheus-operator 17 | app.kubernetes.io/part-of: kube-prometheus 18 | template: 19 | metadata: 20 | annotations: 21 | kubectl.kubernetes.io/default-container: prometheus-operator 22 | labels: 23 | app.kubernetes.io/component: controller 24 | app.kubernetes.io/name: prometheus-operator 25 | app.kubernetes.io/part-of: kube-prometheus 26 | app.kubernetes.io/version: 0.57.0 27 | spec: 28 | automountServiceAccountToken: true 29 | containers: 30 | - args: 31 | - --kubelet-service=kube-system/kubelet 32 | - --prometheus-config-reloader=quay.io/prometheus-operator/prometheus-config-reloader:v0.57.0 33 | image: quay.io/prometheus-operator/prometheus-operator:v0.57.0 34 | name: prometheus-operator 35 | ports: 36 | - containerPort: 8080 37 | name: http 38 | resources: 39 | limits: 40 | cpu: 200m 41 | memory: 200Mi 42 | requests: 43 | cpu: 100m 44 | memory: 100Mi 45 | securityContext: 46 | allowPrivilegeEscalation: false 47 | capabilities: 48 | drop: 49 | - ALL 50 | readOnlyRootFilesystem: true 51 | - args: 52 | - --logtostderr 53 | - --secure-listen-address=:8443 54 | - --tls-cipher-suites=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305 55 | - --upstream=http://127.0.0.1:8080/ 56 | image: quay.io/brancz/kube-rbac-proxy:v0.12.0 57 | name: kube-rbac-proxy 58 | ports: 59 | - containerPort: 8443 60 | name: https 61 | resources: 62 | limits: 63 | cpu: 20m 64 | memory: 40Mi 65 | requests: 66 | cpu: 10m 67 | memory: 20Mi 68 | securityContext: 69 | allowPrivilegeEscalation: false 70 | capabilities: 71 | drop: 72 | - ALL 73 | readOnlyRootFilesystem: true 74 | runAsGroup: 65532 75 | runAsNonRoot: true 76 | runAsUser: 65532 77 | nodeSelector: 78 | kubernetes.io/os: linux 79 | securityContext: 80 | runAsNonRoot: true 81 | runAsUser: 65534 82 | serviceAccountName: prometheus-operator 83 | -------------------------------------------------------------------------------- /hack/kube-prometheus/manifests/setup/prometheus-operator-networkPolicy.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: networking.k8s.io/v1 2 | kind: NetworkPolicy 3 | metadata: 4 | labels: 5 | app.kubernetes.io/component: controller 6 | app.kubernetes.io/name: prometheus-operator 7 | app.kubernetes.io/part-of: kube-prometheus 8 | app.kubernetes.io/version: 0.57.0 9 | name: prometheus-operator 10 | namespace: monitoring 11 | spec: 12 | egress: 13 | - {} 14 | ingress: 15 | - from: 16 | - podSelector: 17 | matchLabels: 18 | app.kubernetes.io/name: prometheus 19 | ports: 20 | - port: 8443 21 | protocol: TCP 22 | podSelector: 23 | matchLabels: 24 | app.kubernetes.io/component: controller 25 | app.kubernetes.io/name: prometheus-operator 26 | app.kubernetes.io/part-of: kube-prometheus 27 | policyTypes: 28 | - Egress 29 | - Ingress 30 | -------------------------------------------------------------------------------- /hack/kube-prometheus/manifests/setup/prometheus-operator-service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | labels: 5 | app.kubernetes.io/component: controller 6 | app.kubernetes.io/name: prometheus-operator 7 | app.kubernetes.io/part-of: kube-prometheus 8 | app.kubernetes.io/version: 0.57.0 9 | name: prometheus-operator 10 | namespace: monitoring 11 | spec: 12 | clusterIP: None 13 | ports: 14 | - name: https 15 | port: 8443 16 | targetPort: https 17 | selector: 18 | app.kubernetes.io/component: controller 19 | app.kubernetes.io/name: prometheus-operator 20 | app.kubernetes.io/part-of: kube-prometheus 21 | -------------------------------------------------------------------------------- /hack/kube-prometheus/manifests/setup/prometheus-operator-serviceAccount.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | automountServiceAccountToken: false 3 | kind: ServiceAccount 4 | metadata: 5 | labels: 6 | app.kubernetes.io/component: controller 7 | app.kubernetes.io/name: prometheus-operator 8 | app.kubernetes.io/part-of: kube-prometheus 9 | app.kubernetes.io/version: 0.57.0 10 | name: prometheus-operator 11 | namespace: monitoring 12 | -------------------------------------------------------------------------------- /hack/kube-rbac-controller.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "kube-rbac-proxy", 3 | "image": "{{.KubeRbacProxy}}", 4 | "imagePullPolicy": "IfNotPresent", 5 | "args": [ 6 | "--logtostderr", 7 | "--secure-listen-address=:{{.MetricsPortHttps}}", 8 | "--tls-cipher-suites=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,TLS_RSA_WITH_AES_128_CBC_SHA256,TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256,TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256", 9 | "--upstream=http://$(METALLB_HOST):{{.MetricsPort}}/", 10 | "--tls-private-key-file=/etc/metrics/tls.key", 11 | "--tls-cert-file=/etc/metrics/tls.crt" 12 | ], 13 | "ports": [ 14 | { 15 | "containerPort": "{{.MetricsPortHttps}}", 16 | "name": "https-metrics" 17 | } 18 | ], 19 | "env": [ 20 | { 21 | "name": "METALLB_HOST", 22 | "valueFrom": { 23 | "fieldRef": { 24 | "fieldPath": "status.hostIP" 25 | } 26 | } 27 | } 28 | ], 29 | "resources": { 30 | "requests": { 31 | "cpu": "10m", 32 | "memory": "20Mi" 33 | } 34 | }, 35 | "terminationMessagePolicy": "FallbackToLogsOnError", 36 | "volumeMounts": [ 37 | { 38 | "name": "controller-certs", 39 | "mountPath": "/etc/metrics", 40 | "readOnly": true 41 | } 42 | ] 43 | } 44 | -------------------------------------------------------------------------------- /hack/kube-rbac-frr.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "kube-rbac-proxy-frr", 3 | "image": "{{.KubeRbacProxy}}", 4 | "imagePullPolicy": "IfNotPresent", 5 | "args": [ 6 | "--logtostderr", 7 | "--secure-listen-address=:{{.FRRMetricsPortHttps}}", 8 | "--tls-cipher-suites=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,TLS_RSA_WITH_AES_128_CBC_SHA256,TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256,TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256", 9 | "--upstream=http://127.0.0.1:{{.FRRMetricsPort}}/", 10 | "--tls-private-key-file=/etc/metrics/tls.key", 11 | "--tls-cert-file=/etc/metrics/tls.crt" 12 | ], 13 | "ports": [ 14 | { 15 | "containerPort": "{{.FRRMetricsPortHttps}}", 16 | "name": "https-metrics" 17 | } 18 | ], 19 | "resources": { 20 | "requests": { 21 | "cpu": "10m", 22 | "memory": "20Mi" 23 | } 24 | }, 25 | "terminationMessagePolicy": "FallbackToLogsOnError", 26 | "volumeMounts": [ 27 | { 28 | "name": "speaker-certs", 29 | "mountPath": "/etc/metrics", 30 | "readOnly": true 31 | } 32 | ] 33 | } 34 | -------------------------------------------------------------------------------- /hack/kube-rbac-speaker.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "kube-rbac-proxy", 3 | "image": "{{.KubeRbacProxy}}", 4 | "imagePullPolicy": "IfNotPresent", 5 | "args": [ 6 | "--logtostderr", 7 | "--secure-listen-address=:{{.MetricsPortHttps}}", 8 | "--tls-cipher-suites=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,TLS_RSA_WITH_AES_128_CBC_SHA256,TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256,TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256", 9 | "--upstream=http://$(METALLB_HOST):{{.MetricsPort}}/", 10 | "--tls-private-key-file=/etc/metrics/tls.key", 11 | "--tls-cert-file=/etc/metrics/tls.crt" 12 | ], 13 | "ports": [ 14 | { 15 | "containerPort": "{{.MetricsPortHttps}}", 16 | "name": "https-metrics" 17 | } 18 | ], 19 | "env": [ 20 | { 21 | "name": "METALLB_HOST", 22 | "valueFrom": { 23 | "fieldRef": { 24 | "fieldPath": "status.hostIP" 25 | } 26 | } 27 | } 28 | ], 29 | "resources": { 30 | "requests": { 31 | "cpu": "10m", 32 | "memory": "20Mi" 33 | } 34 | }, 35 | "terminationMessagePolicy": "FallbackToLogsOnError", 36 | "volumeMounts": [ 37 | { 38 | "name": "speaker-certs", 39 | "mountPath": "/etc/metrics", 40 | "readOnly": true 41 | } 42 | ] 43 | } 44 | -------------------------------------------------------------------------------- /hack/lint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | . $(dirname "$0")/common.sh 4 | 5 | VERSION="v1.54.2" 6 | 7 | docker run --rm -v $(pwd):/app:z -w /app -e GO111MODULE=on golangci/golangci-lint:${VERSION} \ 8 | golangci-lint run --verbose --print-resources-usage --timeout=15m0s 9 | -------------------------------------------------------------------------------- /hack/metallb_ref.txt: -------------------------------------------------------------------------------- 1 | 5b6a2f977561c98a03e034668033015db76f65f8 2 | -------------------------------------------------------------------------------- /hack/metallb_version.txt: -------------------------------------------------------------------------------- 1 | main 2 | -------------------------------------------------------------------------------- /hack/ocp-kustomize-overlay/controller-webhook-patch.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: controller 5 | namespace: metallb-system 6 | spec: 7 | template: 8 | spec: 9 | containers: 10 | - name: controller 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 | args: 20 | - --webhook-mode=disabled 21 | - --port=7472 22 | - --log-level=info 23 | volumes: 24 | - name: cert 25 | secret: 26 | defaultMode: 420 27 | secretName: metallb-webhook-cert 28 | -------------------------------------------------------------------------------- /hack/ocp-kustomize-overlay/crd-conversion-patch.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apiextensions.k8s.io/v1 2 | kind: CustomResourceDefinition 3 | metadata: 4 | name: bgppeers.metallb.io 5 | spec: 6 | conversion: 7 | strategy: Webhook 8 | webhook: 9 | conversionReviewVersions: ["v1beta1", "v1beta2"] 10 | clientConfig: 11 | # this is "\n" used as a placeholder, otherwise it will be rejected by the apiserver for being blank, 12 | # but we're going to patch it later. 13 | caBundle: Cg== 14 | service: 15 | namespace: system-metallb 16 | name: metallb-webhook-service 17 | path: /convert 18 | -------------------------------------------------------------------------------- /hack/ocp-kustomize-overlay/crdcainjection-patch.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apiextensions.k8s.io/v1 2 | kind: CustomResourceDefinition 3 | metadata: 4 | annotations: 5 | service.beta.openshift.io/inject-cabundle: "true" 6 | name: bgppeers.metallb.io 7 | -------------------------------------------------------------------------------- /hack/ocp-kustomize-overlay/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | resources: 4 | - ../../_cache/metallb/config/frr 5 | - rbac.yaml 6 | 7 | patchesStrategicMerge: 8 | - controller-webhook-patch.yaml 9 | - webhookcainjection-patch.yaml 10 | - crd-conversion-patch.yaml 11 | - crdcainjection-patch.yaml 12 | - webhookservicecainjection_patch.yaml 13 | - permission-patch.yaml 14 | 15 | namespace: metallb-system 16 | -------------------------------------------------------------------------------- /hack/ocp-kustomize-overlay/permission-patch.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: controller 5 | namespace: metallb-system 6 | spec: 7 | template: 8 | spec: 9 | securityContext: 10 | runAsNonRoot: true 11 | $patch: replace 12 | --- 13 | apiVersion: apps/v1 14 | kind: DaemonSet 15 | metadata: 16 | name: speaker 17 | namespace: metallb-system 18 | spec: 19 | template: 20 | spec: 21 | initContainers: 22 | - name: cp-frr-files 23 | securityContext: 24 | $patch: delete 25 | 26 | -------------------------------------------------------------------------------- /hack/ocp-kustomize-overlay/rbac.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: ClusterRole 3 | metadata: 4 | name: allow-privileged 5 | rules: 6 | - apiGroups: 7 | - security.openshift.io 8 | resourceNames: 9 | - privileged 10 | resources: 11 | - securitycontextconstraints 12 | verbs: 13 | - use 14 | --- 15 | apiVersion: rbac.authorization.k8s.io/v1 16 | kind: ClusterRoleBinding 17 | metadata: 18 | name: metallb-allow-privileged 19 | roleRef: 20 | apiGroup: rbac.authorization.k8s.io 21 | kind: ClusterRole 22 | name: allow-privileged 23 | subjects: 24 | - kind: ServiceAccount 25 | name: speaker 26 | namespace: metallb-system 27 | - kind: ServiceAccount 28 | name: frr-k8s-daemon 29 | namespace: metallb-system 30 | --- 31 | 32 | -------------------------------------------------------------------------------- /hack/ocp-kustomize-overlay/webhookcainjection-patch.yaml: -------------------------------------------------------------------------------- 1 | # This patch add annotation to admission webhook config 2 | apiVersion: admissionregistration.k8s.io/v1 3 | kind: ValidatingWebhookConfiguration 4 | metadata: 5 | name: metallb-webhook-configuration 6 | annotations: 7 | service.beta.openshift.io/inject-cabundle: "true" 8 | -------------------------------------------------------------------------------- /hack/ocp-kustomize-overlay/webhookservicecainjection_patch.yaml: -------------------------------------------------------------------------------- 1 | # This patch add annotation to the webhook service config 2 | apiVersion: v1 3 | kind: Service 4 | metadata: 5 | name: metallb-webhook-service 6 | namespace: system 7 | annotations: 8 | service.alpha.openshift.io/serving-cert-secret-name: metallb-webhook-cert 9 | -------------------------------------------------------------------------------- /hack/operator_version.txt: -------------------------------------------------------------------------------- 1 | main 2 | -------------------------------------------------------------------------------- /hack/securityContext.yaml: -------------------------------------------------------------------------------- 1 | runAsNonRoot: true 2 | {{ if not .IsOpenShift }} 3 | runAsUser: 65534 4 | {{ end }} 5 | -------------------------------------------------------------------------------- /hack/verify_generated.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [[ -n "$(git status --porcelain .)" ]]; then 4 | echo "uncommitted generated files. Please check the differences and commit them." 5 | echo "$(git status --porcelain .)" 6 | exit 1 7 | fi 8 | -------------------------------------------------------------------------------- /hack/wait-for-csv.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | VERSION="v${VERSION:-"0.1.0"}" 4 | CSV_NAME="metallb-operator.${VERSION}" 5 | NAMESPACE=${NAMESPACE:-"metallb-system"} 6 | 7 | ATTEMPTS=0 8 | MAX_ATTEMPTS=60 9 | csv_created=false 10 | until $csv_created || [ $ATTEMPTS -eq $MAX_ATTEMPTS ] 11 | do 12 | echo "waiting for csv to be created attempt:${ATTEMPTS}" 13 | if kubectl get csv -n $NAMESPACE $CSV_NAME; then 14 | echo "csv created!" 15 | csv_created=true 16 | else 17 | echo "failed, retrying" 18 | sleep 5 19 | fi 20 | (( ATTEMPTS++ )) 21 | done 22 | 23 | if ! $csv_created; then 24 | echo "Timed out waiting for csv to be created" 25 | exit 1 26 | fi 27 | 28 | ATTEMPTS=0 29 | MAX_ATTEMPTS=60 30 | csv_succeeded=false 31 | until $csv_succeeded || [ $ATTEMPTS -eq $MAX_ATTEMPTS ] 32 | do 33 | echo "waiting for csv to be in phase Succeeded attempt:${ATTEMPTS}" 34 | if [[ $(kubectl get csv -n $NAMESPACE $CSV_NAME -o 'jsonpath={.status.phase}') == "Succeeded" ]]; then 35 | echo "csv phase Succeeded" 36 | csv_succeeded=true 37 | else 38 | echo "failed, retrying" 39 | sleep 5 40 | fi 41 | (( ATTEMPTS++ )) 42 | done 43 | 44 | if ! $csv_succeeded; then 45 | echo "Timed out waiting for csv to be in Succeeded phase" 46 | exit 1 47 | fi 48 | -------------------------------------------------------------------------------- /pkg/apply/apply.go: -------------------------------------------------------------------------------- 1 | package apply 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "log" 7 | 8 | "github.com/pkg/errors" 9 | 10 | "k8s.io/apimachinery/pkg/api/equality" 11 | apierrors "k8s.io/apimachinery/pkg/api/errors" 12 | uns "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" 13 | "k8s.io/apimachinery/pkg/types" 14 | k8sclient "sigs.k8s.io/controller-runtime/pkg/client" 15 | ) 16 | 17 | func getExistingObject(ctx context.Context, client k8sclient.Client, obj *uns.Unstructured) (*uns.Unstructured, string, error) { 18 | name := obj.GetName() 19 | namespace := obj.GetNamespace() 20 | if name == "" { 21 | return nil, "", errors.Errorf("Object %s has no name", obj.GroupVersionKind().String()) 22 | } 23 | gvk := obj.GroupVersionKind() 24 | // used for logging and errors 25 | objDesc := fmt.Sprintf("(%s) %s/%s", gvk.String(), namespace, name) 26 | log.Printf("reconciling %s", objDesc) 27 | 28 | if err := IsObjectSupported(obj); err != nil { 29 | return nil, objDesc, errors.Wrapf(err, "object %s unsupported", objDesc) 30 | } 31 | 32 | // Get existing 33 | existing := &uns.Unstructured{} 34 | existing.SetGroupVersionKind(gvk) 35 | err := client.Get(ctx, types.NamespacedName{Name: obj.GetName(), Namespace: obj.GetNamespace()}, existing) 36 | return existing, objDesc, err 37 | } 38 | 39 | // ApplyObject applies the desired object against the apiserver, 40 | // merging it with any existing objects if already present. 41 | func ApplyObject(ctx context.Context, client k8sclient.Client, obj *uns.Unstructured) error { 42 | existing, objDesc, err := getExistingObject(ctx, client, obj) 43 | 44 | if err != nil && apierrors.IsNotFound(err) { 45 | log.Printf("does not exist, creating %s", objDesc) 46 | err := client.Create(ctx, obj) 47 | if err != nil { 48 | return err 49 | } 50 | log.Printf("successfully created %s", objDesc) 51 | return nil 52 | } 53 | 54 | // Merge the desired object with what actually exists 55 | if err := MergeObjectForUpdate(existing, obj); err != nil { 56 | return errors.Wrapf(err, "could not merge object %s with existing", objDesc) 57 | } 58 | if !equality.Semantic.DeepEqual(existing, obj) { 59 | if err := client.Update(ctx, obj); err != nil { 60 | return errors.Wrapf(err, "could not update object %s", objDesc) 61 | } else { 62 | log.Printf("update was successful") 63 | } 64 | } 65 | 66 | return nil 67 | } 68 | -------------------------------------------------------------------------------- /pkg/helm/config_test.go: -------------------------------------------------------------------------------- 1 | package helm 2 | 3 | import ( 4 | "testing" 5 | 6 | . "github.com/onsi/gomega" 7 | ) 8 | 9 | func TestGetImageNameTag(t *testing.T) { 10 | tests := []struct { 11 | input string 12 | expectedImage string 13 | expectedTag string 14 | }{ 15 | { 16 | input: "quay.io/metallb/speaker:v0.13.9", 17 | expectedImage: "quay.io/metallb/speaker", 18 | expectedTag: "v0.13.9", 19 | }, 20 | { 21 | input: "quay.io:5000/metallb/speaker:v0.13.9", 22 | expectedImage: "quay.io:5000/metallb/speaker", 23 | expectedTag: "v0.13.9", 24 | }, 25 | { 26 | input: "quay.io/metallb/speaker", 27 | expectedImage: "quay.io/metallb/speaker", 28 | expectedTag: "", 29 | }, 30 | { 31 | input: "quay.io:5000/metallb/speaker", 32 | expectedImage: "quay.io:5000/metallb/speaker", 33 | expectedTag: "", 34 | }, 35 | { 36 | input: "speaker:v0.13.9", 37 | expectedImage: "speaker", 38 | expectedTag: "v0.13.9", 39 | }, 40 | { 41 | input: "speaker", 42 | expectedImage: "speaker", 43 | expectedTag: "", 44 | }, 45 | } 46 | 47 | g := NewGomegaWithT(t) 48 | for _, test := range tests { 49 | img, tag := getImageNameTag(test.input) 50 | g.Expect(img).To(Equal(test.expectedImage)) 51 | g.Expect(tag).To(Equal(test.expectedTag)) 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /pkg/helm/testdata/ocp-metrics-controller-monitor.golden: -------------------------------------------------------------------------------- 1 | { 2 | "apiVersion": "monitoring.coreos.com/v1", 3 | "kind": "ServiceMonitor", 4 | "metadata": { 5 | "annotations": { 6 | "service.beta.openshift.io/serving-cert-secret-name": "controller-certs-secret" 7 | }, 8 | "labels": { 9 | "app": "metallb", 10 | "app.kubernetes.io/managed-by": "Helm", 11 | "app.kubernetes.io/version": "v0.0.0", 12 | "helm.sh/chart": "metallb-0.0.0" 13 | }, 14 | "name": "controller-monitor", 15 | "namespace": "metallb-test-namespace" 16 | }, 17 | "spec": { 18 | "endpoints": [ 19 | { 20 | "bearerTokenFile": "/var/run/secrets/kubernetes.io/serviceaccount/token", 21 | "honorLabels": true, 22 | "port": "metricshttps", 23 | "scheme": "https", 24 | "tlsConfig": { 25 | "caFile": "/etc/prometheus/configmaps/serving-certs-ca-bundle/service-ca.crt", 26 | "certFile": "/etc/prometheus/secrets/metrics-client-certs/tls.crt", 27 | "insecureSkipVerify": false, 28 | "keyFile": "/etc/prometheus/secrets/metrics-client-certs/tls.key", 29 | "serverName": "controller-monitor-service.metallb-test-namespace.svc" 30 | } 31 | } 32 | ], 33 | "jobLabel": "app.kubernetes.io/name", 34 | "namespaceSelector": { 35 | "matchNames": [ 36 | "metallb-test-namespace" 37 | ] 38 | }, 39 | "selector": { 40 | "matchLabels": { 41 | "name": "controller-monitor-service" 42 | } 43 | } 44 | } 45 | } -------------------------------------------------------------------------------- /pkg/helm/testdata/ocp-metrics-frr-k8s-monitor.golden: -------------------------------------------------------------------------------- 1 | { 2 | "apiVersion": "monitoring.coreos.com/v1", 3 | "kind": "ServiceMonitor", 4 | "metadata": { 5 | "annotations": { 6 | "service.beta.openshift.io/serving-cert-secret-name": "frr-k8s-certs-secret" 7 | }, 8 | "labels": { 9 | "app": "frr-k8s", 10 | "app.kubernetes.io/managed-by": "Helm", 11 | "app.kubernetes.io/version": "v0.0.17", 12 | "component": "frr-k8s", 13 | "helm.sh/chart": "frr-k8s-0.0.17" 14 | }, 15 | "name": "frr-k8s-monitor", 16 | "namespace": "metallb-test-namespace" 17 | }, 18 | "spec": { 19 | "endpoints": [ 20 | { 21 | "bearerTokenFile": "/var/run/secrets/kubernetes.io/serviceaccount/token", 22 | "honorLabels": true, 23 | "metricRelabelings": [ 24 | { 25 | "regex": "frrk8s_bgp_(.*)", 26 | "replacement": "metallb_bgp_$1", 27 | "sourceLabels": [ 28 | "__name__" 29 | ], 30 | "targetLabel": "__name__" 31 | }, 32 | { 33 | "regex": "frrk8s_bfd_(.*)", 34 | "replacement": "metallb_bfd_$1", 35 | "sourceLabels": [ 36 | "__name__" 37 | ], 38 | "targetLabel": "__name__" 39 | } 40 | ], 41 | "port": "metricshttps", 42 | "scheme": "https", 43 | "tlsConfig": { 44 | "caFile": "/etc/prometheus/configmaps/serving-certs-ca-bundle/service-ca.crt", 45 | "certFile": "/etc/prometheus/secrets/metrics-client-certs/tls.crt", 46 | "insecureSkipVerify": false, 47 | "keyFile": "/etc/prometheus/secrets/metrics-client-certs/tls.key", 48 | "serverName": "frr-k8s-monitor-service.metallb-test-namespace.svc" 49 | } 50 | }, 51 | { 52 | "bearerTokenFile": "/var/run/secrets/kubernetes.io/serviceaccount/token", 53 | "honorLabels": true, 54 | "metricRelabelings": [ 55 | { 56 | "regex": "frrk8s_bgp_(.*)", 57 | "replacement": "metallb_bgp_$1", 58 | "sourceLabels": [ 59 | "__name__" 60 | ], 61 | "targetLabel": "__name__" 62 | }, 63 | { 64 | "regex": "frrk8s_bfd_(.*)", 65 | "replacement": "metallb_bfd_$1", 66 | "sourceLabels": [ 67 | "__name__" 68 | ], 69 | "targetLabel": "__name__" 70 | } 71 | ], 72 | "port": "frrmetricshttps", 73 | "scheme": "https", 74 | "tlsConfig": { 75 | "caFile": "/etc/prometheus/configmaps/serving-certs-ca-bundle/service-ca.crt", 76 | "certFile": "/etc/prometheus/secrets/metrics-client-certs/tls.crt", 77 | "insecureSkipVerify": false, 78 | "keyFile": "/etc/prometheus/secrets/metrics-client-certs/tls.key", 79 | "serverName": "frr-k8s-monitor-service.metallb-test-namespace.svc" 80 | } 81 | } 82 | ], 83 | "jobLabel": "app.kubernetes.io/name", 84 | "namespaceSelector": { 85 | "matchNames": [ 86 | "metallb-test-namespace" 87 | ] 88 | }, 89 | "selector": { 90 | "matchLabels": { 91 | "name": "frr-k8s-monitor-service" 92 | } 93 | } 94 | } 95 | } -------------------------------------------------------------------------------- /pkg/helm/testdata/ocp-metrics-speaker-monitor.golden: -------------------------------------------------------------------------------- 1 | { 2 | "apiVersion": "monitoring.coreos.com/v1", 3 | "kind": "ServiceMonitor", 4 | "metadata": { 5 | "annotations": { 6 | "service.beta.openshift.io/serving-cert-secret-name": "speaker-certs-secret" 7 | }, 8 | "labels": { 9 | "app": "metallb", 10 | "app.kubernetes.io/managed-by": "Helm", 11 | "app.kubernetes.io/version": "v0.0.0", 12 | "component": "speaker", 13 | "helm.sh/chart": "metallb-0.0.0" 14 | }, 15 | "name": "speaker-monitor", 16 | "namespace": "metallb-test-namespace" 17 | }, 18 | "spec": { 19 | "endpoints": [ 20 | { 21 | "bearerTokenFile": "/var/run/secrets/kubernetes.io/serviceaccount/token", 22 | "honorLabels": true, 23 | "port": "metricshttps", 24 | "scheme": "https", 25 | "tlsConfig": { 26 | "caFile": "/etc/prometheus/configmaps/serving-certs-ca-bundle/service-ca.crt", 27 | "certFile": "/etc/prometheus/secrets/metrics-client-certs/tls.crt", 28 | "insecureSkipVerify": false, 29 | "keyFile": "/etc/prometheus/secrets/metrics-client-certs/tls.key", 30 | "serverName": "speaker-monitor-service.metallb-test-namespace.svc" 31 | } 32 | }, 33 | { 34 | "bearerTokenFile": "/var/run/secrets/kubernetes.io/serviceaccount/token", 35 | "honorLabels": true, 36 | "port": "frrmetricshttps", 37 | "scheme": "https", 38 | "tlsConfig": { 39 | "caFile": "/etc/prometheus/configmaps/serving-certs-ca-bundle/service-ca.crt", 40 | "certFile": "/etc/prometheus/secrets/metrics-client-certs/tls.crt", 41 | "insecureSkipVerify": false, 42 | "keyFile": "/etc/prometheus/secrets/metrics-client-certs/tls.key", 43 | "serverName": "speaker-monitor-service.metallb-test-namespace.svc" 44 | } 45 | } 46 | ], 47 | "jobLabel": "app.kubernetes.io/name", 48 | "namespaceSelector": { 49 | "matchNames": [ 50 | "metallb-test-namespace" 51 | ] 52 | }, 53 | "selector": { 54 | "matchLabels": { 55 | "name": "speaker-monitor-service" 56 | } 57 | } 58 | } 59 | } -------------------------------------------------------------------------------- /pkg/helm/testdata/vanilla-metrics-controller-monitor.golden: -------------------------------------------------------------------------------- 1 | { 2 | "apiVersion": "monitoring.coreos.com/v1", 3 | "kind": "ServiceMonitor", 4 | "metadata": { 5 | "labels": { 6 | "app": "metallb", 7 | "app.kubernetes.io/managed-by": "Helm", 8 | "app.kubernetes.io/version": "v0.0.0", 9 | "helm.sh/chart": "metallb-0.0.0" 10 | }, 11 | "name": "controller-monitor", 12 | "namespace": "metallb-test-namespace" 13 | }, 14 | "spec": { 15 | "endpoints": [ 16 | { 17 | "bearerTokenFile": "/var/run/secrets/kubernetes.io/serviceaccount/token", 18 | "honorLabels": true, 19 | "port": "metricshttps", 20 | "scheme": "https", 21 | "tlsConfig": { 22 | "insecureSkipVerify": true 23 | } 24 | } 25 | ], 26 | "jobLabel": "app.kubernetes.io/name", 27 | "namespaceSelector": { 28 | "matchNames": [ 29 | "metallb-test-namespace" 30 | ] 31 | }, 32 | "selector": { 33 | "matchLabels": { 34 | "name": "controller-monitor-service" 35 | } 36 | } 37 | } 38 | } -------------------------------------------------------------------------------- /pkg/helm/testdata/vanilla-metrics-speaker-monitor.golden: -------------------------------------------------------------------------------- 1 | { 2 | "apiVersion": "monitoring.coreos.com/v1", 3 | "kind": "ServiceMonitor", 4 | "metadata": { 5 | "labels": { 6 | "app": "metallb", 7 | "app.kubernetes.io/managed-by": "Helm", 8 | "app.kubernetes.io/version": "v0.0.0", 9 | "component": "speaker", 10 | "helm.sh/chart": "metallb-0.0.0" 11 | }, 12 | "name": "speaker-monitor", 13 | "namespace": "metallb-test-namespace" 14 | }, 15 | "spec": { 16 | "endpoints": [ 17 | { 18 | "bearerTokenFile": "/var/run/secrets/kubernetes.io/serviceaccount/token", 19 | "honorLabels": true, 20 | "port": "metricshttps", 21 | "scheme": "https", 22 | "tlsConfig": { 23 | "insecureSkipVerify": true 24 | } 25 | }, 26 | { 27 | "bearerTokenFile": "/var/run/secrets/kubernetes.io/serviceaccount/token", 28 | "honorLabels": true, 29 | "port": "frrmetricshttps", 30 | "scheme": "https", 31 | "tlsConfig": { 32 | "insecureSkipVerify": true 33 | } 34 | } 35 | ], 36 | "jobLabel": "app.kubernetes.io/name", 37 | "namespaceSelector": { 38 | "matchNames": [ 39 | "metallb-test-namespace" 40 | ] 41 | }, 42 | "selector": { 43 | "matchLabels": { 44 | "name": "speaker-monitor-service" 45 | } 46 | } 47 | } 48 | } -------------------------------------------------------------------------------- /pkg/openshift/openshift.go: -------------------------------------------------------------------------------- 1 | package openshift 2 | 3 | import ( 4 | "context" 5 | "slices" 6 | 7 | "github.com/Masterminds/semver/v3" 8 | "github.com/metallb/metallb-operator/pkg/params" 9 | openshiftconfigv1 "github.com/openshift/api/config/v1" 10 | openshiftapiv1 "github.com/openshift/api/operator/v1" 11 | "github.com/pkg/errors" 12 | "k8s.io/apimachinery/pkg/types" 13 | "sigs.k8s.io/controller-runtime/pkg/client" 14 | ) 15 | 16 | func SupportsFRRK8s(ctx context.Context, cli client.Client, envConfig params.EnvConfig) (bool, error) { 17 | cno := &openshiftconfigv1.ClusterOperator{} 18 | err := cli.Get(ctx, types.NamespacedName{Name: "network"}, cno) 19 | if err != nil { 20 | return false, errors.Wrapf(err, "get openshift network operator failed") 21 | } 22 | supports, err := cnoSupportsFRRK8s(cno, envConfig) 23 | if err != nil { 24 | return false, err 25 | } 26 | return supports, nil 27 | } 28 | 29 | func cnoSupportsFRRK8s(cno *openshiftconfigv1.ClusterOperator, envConfig params.EnvConfig) (bool, error) { 30 | for _, v := range cno.Status.Versions { 31 | if v.Name == "operator" { 32 | v, err := semver.NewVersion(v.Version) 33 | if err != nil { 34 | return false, errors.Wrapf(err, "failed to parse semver for network operator") 35 | } 36 | validVersion, _ := semver.NewVersion(envConfig.CNOMinFRRK8sVersion) 37 | valid := !v.LessThan(validVersion) 38 | return valid, nil 39 | } 40 | } 41 | return false, errors.New("failed to find \"operator\" in network operator versions") 42 | } 43 | 44 | func DeployFRRK8s(ctx context.Context, cli client.Client) error { 45 | network := &openshiftapiv1.Network{} 46 | err := cli.Get(ctx, types.NamespacedName{Name: "cluster"}, network) 47 | if err != nil { 48 | return errors.Wrapf(err, "get openshift network failed") 49 | } 50 | if network.Spec.AdditionalRoutingCapabilities == nil { 51 | network.Spec.AdditionalRoutingCapabilities = &openshiftapiv1.AdditionalRoutingCapabilities{} 52 | } 53 | if slices.Contains(network.Spec.AdditionalRoutingCapabilities.Providers, openshiftapiv1.RoutingCapabilitiesProviderFRR) { 54 | return nil 55 | } 56 | network.Spec.AdditionalRoutingCapabilities.Providers = append(network.Spec.AdditionalRoutingCapabilities.Providers, openshiftapiv1.RoutingCapabilitiesProviderFRR) 57 | err = cli.Update(ctx, network) 58 | if err != nil { 59 | return err 60 | } 61 | return nil 62 | } 63 | -------------------------------------------------------------------------------- /pkg/openshift/openshift_test.go: -------------------------------------------------------------------------------- 1 | package openshift 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/metallb/metallb-operator/pkg/params" 7 | openshiftconfigv1 "github.com/openshift/api/config/v1" 8 | ) 9 | 10 | func TestCnoSupportsFRRK8s(t *testing.T) { 11 | tests := []struct { 12 | name string 13 | cno *openshiftconfigv1.ClusterOperator 14 | expected bool 15 | shouldErr bool 16 | }{ 17 | { 18 | name: "old version", 19 | cno: &openshiftconfigv1.ClusterOperator{ 20 | Status: openshiftconfigv1.ClusterOperatorStatus{ 21 | Versions: []openshiftconfigv1.OperandVersion{ 22 | { 23 | Name: "operator", 24 | Version: "4.16.0", 25 | }, 26 | }, 27 | }, 28 | }, 29 | expected: false, 30 | }, { 31 | name: "exact same version", 32 | cno: &openshiftconfigv1.ClusterOperator{ 33 | Status: openshiftconfigv1.ClusterOperatorStatus{ 34 | Versions: []openshiftconfigv1.OperandVersion{ 35 | { 36 | Name: "operator", 37 | Version: "4.17.0", 38 | }, 39 | }, 40 | }, 41 | }, 42 | expected: true, 43 | }, { 44 | name: "greater version", 45 | cno: &openshiftconfigv1.ClusterOperator{ 46 | Status: openshiftconfigv1.ClusterOperatorStatus{ 47 | Versions: []openshiftconfigv1.OperandVersion{ 48 | { 49 | Name: "operator", 50 | Version: "4.17.5", 51 | }, 52 | }, 53 | }, 54 | }, 55 | expected: true, 56 | }, { 57 | name: "invalid version", 58 | cno: &openshiftconfigv1.ClusterOperator{ 59 | Status: openshiftconfigv1.ClusterOperatorStatus{ 60 | Versions: []openshiftconfigv1.OperandVersion{ 61 | { 62 | Name: "operator", 63 | Version: "4.17.5.6.7.8.9.fooo", 64 | }, 65 | }, 66 | }, 67 | }, 68 | shouldErr: true, 69 | }, { 70 | name: "no operator version", 71 | cno: &openshiftconfigv1.ClusterOperator{ 72 | Status: openshiftconfigv1.ClusterOperatorStatus{ 73 | Versions: []openshiftconfigv1.OperandVersion{ 74 | { 75 | Name: "hello", 76 | Version: "4.17.5.6.7.8.9.fooo", 77 | }, { 78 | Name: "world", 79 | Version: "4.17.5.6.7.8.9.fooo", 80 | }, 81 | }, 82 | }, 83 | }, 84 | shouldErr: true, 85 | }, 86 | { 87 | name: "nightly", 88 | cno: &openshiftconfigv1.ClusterOperator{ 89 | Status: openshiftconfigv1.ClusterOperatorStatus{ 90 | Versions: []openshiftconfigv1.OperandVersion{ 91 | { 92 | Name: "hello", 93 | Version: "4.17.5.6.7.8.9.fooo", 94 | }, { 95 | Name: "operator", 96 | Version: "4.17.0-0.nightly-2024-07-17-18340", 97 | }, 98 | }, 99 | }, 100 | }, 101 | expected: true, 102 | }, 103 | } 104 | 105 | for _, test := range tests { 106 | t.Run(test.name, func(t *testing.T) { 107 | envConfig := params.EnvConfig{CNOMinFRRK8sVersion: "4.17.0-0"} 108 | supports, err := cnoSupportsFRRK8s(test.cno, envConfig) 109 | if test.shouldErr && err == nil { 110 | t.Fatalf("expected error, got nil") 111 | } 112 | if !test.shouldErr && err != nil { 113 | t.Fatalf("expected no error, got %v", err) 114 | } 115 | if supports != test.expected { 116 | t.Fatalf("supports %v different from expected: %v", supports, test.expected) 117 | } 118 | }) 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /pkg/platform/platform.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 platform 16 | 17 | import ( 18 | "k8s.io/client-go/discovery" 19 | "k8s.io/client-go/rest" 20 | ctrl "sigs.k8s.io/controller-runtime" 21 | "sigs.k8s.io/controller-runtime/pkg/client/config" 22 | ) 23 | 24 | var log = ctrl.Log.WithName("platform") 25 | 26 | type k8SBasedPlatformVersioner struct{} 27 | 28 | /* 29 | GetPlatformInfo examines the Kubernetes-based environment and determines the running platform, version, & OS. 30 | Accepts or instantiated 'cfg' rest config parameter. 31 | 32 | Result: PlatformInfo{ Name: OpenShift, K8SVersion: 1.13+, OS: linux/amd64 } 33 | */ 34 | func GetPlatformInfo(cfg *rest.Config) (PlatformInfo, error) { 35 | return k8SBasedPlatformVersioner{}.getPlatformInfo(nil, cfg) 36 | } 37 | 38 | /* 39 | GetPlatformName is a helper method to return the platform name from GetPlatformInfo results 40 | Accepts or instantiated 'cfg' rest config parameter. 41 | */ 42 | func GetPlatformName(cfg *rest.Config) (string, error) { 43 | info, err := GetPlatformInfo(cfg) 44 | if err != nil { 45 | return "", err 46 | } 47 | return string(info.Name), nil 48 | } 49 | 50 | // deal with cfg coming from legacy method signature and allow injection for client testing 51 | func (k8SBasedPlatformVersioner) defaultArgs(client discovery.DiscoveryInterface, cfg *rest.Config) (discovery.DiscoveryInterface, *rest.Config, error) { 52 | if cfg == nil { 53 | var err error 54 | cfg, err = config.GetConfig() 55 | if err != nil { 56 | return nil, nil, err 57 | } 58 | } 59 | if client == nil { 60 | var err error 61 | client, err = discovery.NewDiscoveryClientForConfig(cfg) 62 | if err != nil { 63 | return nil, nil, err 64 | } 65 | } 66 | return client, cfg, nil 67 | } 68 | 69 | func (pv k8SBasedPlatformVersioner) getPlatformInfo(client discovery.DiscoveryInterface, cfg *rest.Config) (PlatformInfo, error) { 70 | log.Info("detecting platform version...") 71 | info := PlatformInfo{Name: Kubernetes} 72 | 73 | var err error 74 | client, _, err = pv.defaultArgs(client, cfg) 75 | if err != nil { 76 | log.Info("issue occurred while defaulting client/cfg args") 77 | return info, err 78 | } 79 | 80 | k8sVersion, err := client.ServerVersion() 81 | if err != nil { 82 | log.Info("issue occurred while fetching ServerVersion") 83 | return info, err 84 | } 85 | info.K8SVersion = k8sVersion.Major + "." + k8sVersion.Minor 86 | info.OS = k8sVersion.Platform 87 | 88 | apiList, err := client.ServerGroups() 89 | if err != nil { 90 | log.Info("issue occurred while fetching ServerGroups") 91 | return info, err 92 | } 93 | 94 | for _, v := range apiList.Groups { 95 | if v.Name == "route.openshift.io" { 96 | 97 | log.Info("route.openshift.io found in apis, platform is OpenShift") 98 | info.Name = OpenShift 99 | break 100 | } 101 | } 102 | log.Info(info.String()) 103 | return info, nil 104 | } 105 | -------------------------------------------------------------------------------- /pkg/platform/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 platform 16 | 17 | import "fmt" 18 | 19 | type PlatformType string 20 | 21 | const ( 22 | OpenShift PlatformType = "OpenShift" 23 | Kubernetes PlatformType = "Kubernetes" 24 | ) 25 | 26 | type PlatformInfo struct { 27 | Name PlatformType `json:"name"` 28 | K8SVersion string `json:"k8sVersion"` 29 | OS string `json:"os"` 30 | } 31 | 32 | func (info PlatformInfo) IsOpenShift() bool { 33 | return info.Name == OpenShift 34 | } 35 | 36 | func (info PlatformInfo) String() string { 37 | return "PlatformInfo [" + 38 | "Name: " + fmt.Sprintf("%v", info.Name) + 39 | ", K8SVersion: " + info.K8SVersion + 40 | ", OS: " + info.OS + "]" 41 | } 42 | -------------------------------------------------------------------------------- /pkg/status/status_test.go: -------------------------------------------------------------------------------- 1 | package status 2 | 3 | import ( 4 | "testing" 5 | 6 | . "github.com/onsi/gomega" 7 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 8 | ) 9 | 10 | func TestGetConditionsAvailable(t *testing.T) { 11 | g := NewGomegaWithT(t) 12 | conditions := getConditions(ConditionAvailable, "testReason", "testMessage") 13 | validateUnsetConditions(g, conditions, []int{2, 3}) 14 | validateConditionTypes(g, conditions) 15 | g.Expect(conditions[0].Status).To(Equal(metav1.ConditionTrue)) 16 | g.Expect(conditions[1].Status).To(Equal(metav1.ConditionTrue)) 17 | } 18 | 19 | func TestGetConditionsProgressing(t *testing.T) { 20 | g := NewGomegaWithT(t) 21 | conditions := getConditions(ConditionProgressing, "testReason", "testMessage") 22 | validateUnsetConditions(g, conditions, []int{0, 1, 3}) 23 | validateConditionTypes(g, conditions) 24 | g.Expect(conditions[2].Status).To(Equal(metav1.ConditionTrue)) 25 | g.Expect(conditions[2].Message).To(Equal("testMessage")) 26 | g.Expect(conditions[2].Reason).To(Equal("testReason")) 27 | } 28 | 29 | func TestGetConditionsDegraded(t *testing.T) { 30 | g := NewGomegaWithT(t) 31 | conditions := getConditions(ConditionDegraded, "testReason", "testMessage") 32 | validateUnsetConditions(g, conditions, []int{0, 1, 2}) 33 | validateConditionTypes(g, conditions) 34 | g.Expect(conditions[3].Status).To(Equal(metav1.ConditionTrue)) 35 | g.Expect(conditions[3].Message).To(Equal("testMessage")) 36 | g.Expect(conditions[3].Reason).To(Equal("testReason")) 37 | } 38 | 39 | func validateUnsetConditions(g *GomegaWithT, conditions []metav1.Condition, indexes []int) { 40 | for _, index := range indexes { 41 | g.Expect(conditions[index].Status).To(Equal(metav1.ConditionFalse)) 42 | g.Expect(conditions[index].Message).To(Equal("")) 43 | } 44 | } 45 | 46 | func validateConditionTypes(g *GomegaWithT, conditions []metav1.Condition) { 47 | g.Expect(conditions[0].Type).To(Equal(ConditionAvailable)) 48 | g.Expect(conditions[1].Type).To(Equal(ConditionUpgradeable)) 49 | g.Expect(conditions[2].Type).To(Equal(ConditionProgressing)) 50 | g.Expect(conditions[3].Type).To(Equal(ConditionDegraded)) 51 | } 52 | -------------------------------------------------------------------------------- /test/README.md: -------------------------------------------------------------------------------- 1 | # Tests 2 | 3 | ## E2E mode 4 | 5 | The metallb e2e tests are divided into 2 suites: 6 | - validation - verify if the metallb operator has been installed correctly 7 | - functional - verify that the installation of metallb by the operator is working correctly 8 | 9 | To run the tests use the following make targets: 10 | - make test-validation - run the validation tests 11 | - make test-functional - run the functional tests 12 | - make test-e2e - run all tests 13 | -------------------------------------------------------------------------------- /test/consts/const.go: -------------------------------------------------------------------------------- 1 | package consts 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/metallb/metallb-operator/pkg/apply" 7 | ) 8 | 9 | const ( 10 | // MetalLBOperatorDeploymentName contains the name of the MetalLB Operator deployment 11 | MetalLBOperatorDeploymentName = "metallb-operator-controller-manager" 12 | // MetalLBOperatorDeploymentLabel contains the label of the MetalLB Operator deployment 13 | MetalLBOperatorDeploymentLabel = "controller-manager" 14 | // MetalLBOperatorCRDName contains the name of the MetalLB Operator CRD 15 | MetalLBOperatorCRDName = "metallbs.metallb.io" 16 | // MetalLBCRFile contains the MetalLB custom resource deployment 17 | MetalLBCRFile = "metallb.yaml" 18 | // MetalLBDeploymentName contains the name of the MetalLB deployment 19 | MetalLBDeploymentName = "controller" 20 | // MetalLBDaemonsetName contains the name of the MetalLB daemonset 21 | MetalLBDaemonsetName = "speaker" 22 | // MetalLBConfigMapName contains created configmap 23 | MetalLBConfigMapName = apply.MetalLBConfigMap 24 | // FRRK8SDaemonsetName contains the name of the frr-k8s daemonset 25 | FRRK8SDaemonsetName = "frr-k8s" 26 | // FRRK8SWebhookDeploymentName contains the name of the frr-k8s webhook-server deployment 27 | FRRK8SWebhookDeploymentName = "frr-k8s-webhook-server" 28 | // DefaultOperatorNameSpace is the default operator namespace 29 | DefaultOperatorNameSpace = "metallb-system" 30 | // LogsExtractDuration represents how much in the past to fetch the logs from 31 | LogsExtractDuration = 10 * time.Minute 32 | ) 33 | -------------------------------------------------------------------------------- /test/e2e/client/client.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "os" 5 | 6 | "github.com/golang/glog" 7 | metallbv1beta1 "github.com/metallb/metallb-operator/api/v1beta1" 8 | apiext "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" 9 | "k8s.io/apimachinery/pkg/runtime" 10 | discovery "k8s.io/client-go/discovery" 11 | "k8s.io/client-go/kubernetes/scheme" 12 | appsv1client "k8s.io/client-go/kubernetes/typed/apps/v1" 13 | corev1client "k8s.io/client-go/kubernetes/typed/core/v1" 14 | networkv1client "k8s.io/client-go/kubernetes/typed/networking/v1" 15 | rbacv1client "k8s.io/client-go/kubernetes/typed/rbac/v1" 16 | "k8s.io/client-go/rest" 17 | "k8s.io/client-go/tools/clientcmd" 18 | "sigs.k8s.io/controller-runtime/pkg/client" 19 | ) 20 | 21 | // Client defines the client set that will be used for testing 22 | var Client *ClientSet 23 | 24 | func init() { 25 | Client = New("") 26 | } 27 | 28 | // ClientSet provides the struct to talk with relevant API 29 | type ClientSet struct { 30 | client.Client 31 | corev1client.CoreV1Interface 32 | networkv1client.NetworkingV1Client 33 | appsv1client.AppsV1Interface 34 | rbacv1client.RbacV1Interface 35 | discovery.DiscoveryInterface 36 | Config *rest.Config 37 | } 38 | 39 | // New returns a *ClientBuilder with the given kubeconfig. 40 | func New(kubeconfig string) *ClientSet { 41 | var config *rest.Config 42 | var err error 43 | 44 | if kubeconfig == "" { 45 | kubeconfig = os.Getenv("KUBECONFIG") 46 | } 47 | 48 | if kubeconfig != "" { 49 | glog.V(4).Infof("Loading kube client config from path %q", kubeconfig) 50 | config, err = clientcmd.BuildConfigFromFlags("", kubeconfig) 51 | } else { 52 | glog.V(4).Infof("Using in-cluster kube client config") 53 | config, err = rest.InClusterConfig() 54 | } 55 | if err != nil { 56 | glog.Infof("Failed to init kubernetes client, please check the $KUBECONFIG environment variable") 57 | return nil 58 | } 59 | 60 | clientSet := &ClientSet{} 61 | clientSet.CoreV1Interface = corev1client.NewForConfigOrDie(config) 62 | clientSet.AppsV1Interface = appsv1client.NewForConfigOrDie(config) 63 | clientSet.RbacV1Interface = rbacv1client.NewForConfigOrDie(config) 64 | clientSet.DiscoveryInterface = discovery.NewDiscoveryClientForConfigOrDie(config) 65 | clientSet.NetworkingV1Client = *networkv1client.NewForConfigOrDie(config) 66 | clientSet.Config = config 67 | 68 | myScheme := runtime.NewScheme() 69 | if err = scheme.AddToScheme(myScheme); err != nil { 70 | panic(err) 71 | } 72 | 73 | // Setup Scheme for all resources 74 | if err := apiext.AddToScheme(myScheme); err != nil { 75 | panic(err) 76 | } 77 | 78 | if err := metallbv1beta1.AddToScheme(myScheme); err != nil { 79 | panic(err) 80 | } 81 | 82 | clientSet.Client, err = client.New(config, client.Options{ 83 | Scheme: myScheme, 84 | }) 85 | 86 | if err != nil { 87 | return nil 88 | } 89 | 90 | return clientSet 91 | } 92 | -------------------------------------------------------------------------------- /test/e2e/functional/e2e_test.go: -------------------------------------------------------------------------------- 1 | //go:build e2etests 2 | // +build e2etests 3 | 4 | package functional 5 | 6 | import ( 7 | "flag" 8 | "os" 9 | "path" 10 | "testing" 11 | 12 | . "github.com/onsi/ginkgo/v2" 13 | . "github.com/onsi/gomega" 14 | 15 | "github.com/metallb/metallb-operator/test/consts" 16 | _ "github.com/metallb/metallb-operator/test/e2e/functional/tests" 17 | "github.com/metallb/metallb-operator/test/e2e/k8sreporter" 18 | kniK8sReporter "github.com/openshift-kni/k8sreporter" 19 | ) 20 | 21 | var OperatorNameSpace = consts.DefaultOperatorNameSpace 22 | 23 | var junitPath *string 24 | var reportPath *string 25 | var r *kniK8sReporter.KubernetesReporter 26 | 27 | func init() { 28 | if ns := os.Getenv("OO_INSTALL_NAMESPACE"); len(ns) != 0 { 29 | OperatorNameSpace = ns 30 | } 31 | 32 | junitPath = flag.String("junit", "", "the path for the junit format report") 33 | reportPath = flag.String("report", "", "the path of the report file containing details for failed tests") 34 | } 35 | 36 | func TestE2E(t *testing.T) { 37 | // We want to collect logs before any resource is deleted in AfterEach, so we register the global fail handler 38 | // in a way such that the reporter's Dump is always called before the default Fail. 39 | RegisterFailHandler(func(message string, callerSkip ...int) { 40 | if r != nil { 41 | r.Dump(consts.LogsExtractDuration, CurrentSpecReport().FullText()) 42 | } 43 | 44 | // Ensure failing line location is not affected by this wrapper 45 | for i := range callerSkip { 46 | callerSkip[i]++ 47 | } 48 | Fail(message, callerSkip...) 49 | }) 50 | 51 | _, reporterConfig := GinkgoConfiguration() 52 | 53 | if *junitPath != "" { 54 | junitFile := path.Join(*junitPath, "e2e_junit.xml") 55 | reporterConfig.JUnitReport = junitFile 56 | } 57 | 58 | if *reportPath != "" { 59 | kubeconfig := os.Getenv("KUBECONFIG") 60 | reportPath := path.Join(*reportPath, "metallb_failure_report.log") 61 | r = k8sreporter.New(kubeconfig, reportPath, OperatorNameSpace) 62 | } 63 | 64 | RunSpecs(t, "Metallb Operator E2E Suite", reporterConfig) 65 | } 66 | -------------------------------------------------------------------------------- /test/e2e/k8sreporter/reporter.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier:Apache-2.0 2 | 3 | package k8sreporter 4 | 5 | import ( 6 | "errors" 7 | "log" 8 | "os" 9 | 10 | "github.com/openshift-kni/k8sreporter" 11 | corev1 "k8s.io/api/core/v1" 12 | "k8s.io/apimachinery/pkg/runtime" 13 | 14 | "github.com/metallb/metallb-operator/api/v1beta1" 15 | ) 16 | 17 | const MetalLBTestNameSpace = "metallb-test-namespace" 18 | 19 | func New(kubeconfig, path, namespace string) *k8sreporter.KubernetesReporter { 20 | // When using custom crds, we need to add them to the scheme 21 | addToScheme := func(s *runtime.Scheme) error { 22 | err := v1beta1.AddToScheme(s) 23 | if err != nil { 24 | return err 25 | } 26 | return nil 27 | } 28 | 29 | // The namespaces we want to dump resources for (including pods and pod logs) 30 | dumpNamespace := func(ns string) bool { 31 | switch { 32 | case ns == namespace: 33 | return true 34 | case ns == MetalLBTestNameSpace: 35 | return true 36 | } 37 | return false 38 | } 39 | 40 | // The list of CRDs we want to dump 41 | crds := []k8sreporter.CRData{ 42 | {Cr: &v1beta1.MetalLBList{}}, 43 | {Cr: &corev1.ServiceList{}}, 44 | } 45 | 46 | err := os.Mkdir(path, 0755) 47 | if err != nil && !errors.Is(err, os.ErrExist) { 48 | log.Fatalf("Failed to create the reporter dir: %s", err) 49 | } 50 | 51 | reporter, err := k8sreporter.New(kubeconfig, addToScheme, dumpNamespace, path, crds...) 52 | if err != nil { 53 | log.Fatalf("Failed to initialize the reporter %s", err) 54 | } 55 | return reporter 56 | } 57 | -------------------------------------------------------------------------------- /test/e2e/namespaces/namespaces.go: -------------------------------------------------------------------------------- 1 | package namespaces 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "time" 7 | 8 | k8sv1 "k8s.io/api/core/v1" 9 | "k8s.io/apimachinery/pkg/api/errors" 10 | k8serrors "k8s.io/apimachinery/pkg/api/errors" 11 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 12 | "k8s.io/apimachinery/pkg/util/wait" 13 | "k8s.io/utils/ptr" 14 | 15 | testclient "github.com/metallb/metallb-operator/test/e2e/client" 16 | ) 17 | 18 | // Test is the namespace to be use for testing 19 | //const Test = "sriov-conformance-testing" 20 | 21 | // WaitForDeletion waits until the namespace will be removed from the cluster 22 | func WaitForDeletion(cs *testclient.ClientSet, nsName string, timeout time.Duration) error { 23 | return wait.PollUntilContextTimeout(context.Background(), time.Second, timeout, true, func(c context.Context) (bool, error) { 24 | _, err := cs.Namespaces().Get(context.Background(), nsName, metav1.GetOptions{}) 25 | if errors.IsNotFound(err) { 26 | return true, nil 27 | } 28 | return false, nil 29 | }) 30 | } 31 | 32 | // Create creates a new namespace with the given name. 33 | // If the namespace exists, it returns. 34 | func Create(namespace string, cs *testclient.ClientSet) error { 35 | _, err := cs.Namespaces().Create(context.Background(), &k8sv1.Namespace{ 36 | ObjectMeta: metav1.ObjectMeta{ 37 | Name: namespace, 38 | }}, metav1.CreateOptions{}) 39 | 40 | if k8serrors.IsAlreadyExists(err) { 41 | return nil 42 | } 43 | return err 44 | } 45 | 46 | // DeleteAndWait deletes a namespace and waits until delete 47 | func DeleteAndWait(cs *testclient.ClientSet, namespace string, timeout time.Duration) error { 48 | err := cs.Namespaces().Delete(context.Background(), namespace, metav1.DeleteOptions{}) 49 | if err != nil { 50 | return err 51 | } 52 | return WaitForDeletion(cs, namespace, timeout) 53 | } 54 | 55 | // Exists tells whether the given namespace exists 56 | func Exists(namespace string, cs *testclient.ClientSet) bool { 57 | _, err := cs.Namespaces().Get(context.Background(), namespace, metav1.GetOptions{}) 58 | return err == nil || !k8serrors.IsNotFound(err) 59 | } 60 | 61 | // CleanPods deletes all pods in namespace 62 | func CleanPods(namespace string, cs *testclient.ClientSet) error { 63 | if !Exists(namespace, cs) { 64 | return nil 65 | } 66 | err := cs.Pods(namespace).DeleteCollection(context.Background(), metav1.DeleteOptions{ 67 | GracePeriodSeconds: ptr.To(int64(0)), 68 | }, metav1.ListOptions{}) 69 | if err != nil { 70 | return fmt.Errorf("Failed to delete pods %v", err) 71 | } 72 | return err 73 | } 74 | 75 | // Clean cleans all dangling objects from the given namespace. 76 | func Clean(operatorNamespace, namespace string, cs *testclient.ClientSet, discoveryEnabled bool) error { 77 | err := CleanPods(namespace, cs) 78 | if err != nil { 79 | return err 80 | } 81 | if discoveryEnabled { 82 | return nil 83 | } 84 | return nil 85 | } 86 | -------------------------------------------------------------------------------- /test/e2e/validation/tests/validation.go: -------------------------------------------------------------------------------- 1 | package tests 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "os" 7 | 8 | ctrl "sigs.k8s.io/controller-runtime" 9 | 10 | . "github.com/onsi/ginkgo/v2" 11 | . "github.com/onsi/gomega" 12 | 13 | "github.com/metallb/metallb-operator/pkg/platform" 14 | "github.com/metallb/metallb-operator/test/consts" 15 | testclient "github.com/metallb/metallb-operator/test/e2e/client" 16 | "github.com/metallb/metallb-operator/test/e2e/metallb" 17 | corev1 "k8s.io/api/core/v1" 18 | apiext "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" 19 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 20 | goclient "sigs.k8s.io/controller-runtime/pkg/client" 21 | ) 22 | 23 | var TestIsOpenShift = false 24 | 25 | var OperatorNameSpace = consts.DefaultOperatorNameSpace 26 | 27 | func init() { 28 | if len(os.Getenv("IS_OPENSHIFT")) != 0 { 29 | TestIsOpenShift = true 30 | } 31 | 32 | if ns := os.Getenv("OO_INSTALL_NAMESPACE"); len(ns) != 0 { 33 | OperatorNameSpace = ns 34 | } 35 | } 36 | 37 | var _ = Describe("metallb", func() { 38 | Context("Platform Check", func() { 39 | It("Should have the MetalLB Operator namespace", func() { 40 | _, err := testclient.Client.Namespaces().Get(context.Background(), OperatorNameSpace, metav1.GetOptions{}) 41 | Expect(err).ToNot(HaveOccurred(), "Should have the MetalLB Operator namespace") 42 | }) 43 | It("should be either Kubernetes or OpenShift platform", func() { 44 | cfg := ctrl.GetConfigOrDie() 45 | platforminfo, err := platform.GetPlatformInfo(cfg) 46 | Expect(err).ToNot(HaveOccurred()) 47 | Expect(platforminfo.IsOpenShift()).Should(Equal(TestIsOpenShift)) 48 | }) 49 | }) 50 | 51 | Context("MetalLB", func() { 52 | It("should have the MetalLB Operator deployment in running state", func() { 53 | Eventually(func() error { 54 | deploy, err := testclient.Client.Deployments(OperatorNameSpace).Get(context.Background(), consts.MetalLBOperatorDeploymentName, metav1.GetOptions{}) 55 | if err != nil { 56 | return err 57 | } 58 | 59 | pods, err := testclient.Client.Pods(OperatorNameSpace).List(context.Background(), metav1.ListOptions{ 60 | LabelSelector: fmt.Sprintf("control-plane=%s", consts.MetalLBOperatorDeploymentLabel)}) 61 | if err != nil { 62 | return err 63 | } 64 | 65 | if len(pods.Items) != int(deploy.Status.Replicas) { 66 | return fmt.Errorf("deployment %s pods are not ready, expected %d replicas got %d pods", consts.MetalLBOperatorDeploymentName, deploy.Status.Replicas, len(pods.Items)) 67 | } 68 | 69 | for _, pod := range pods.Items { 70 | if pod.Status.Phase != corev1.PodRunning { 71 | return fmt.Errorf("deployment %s pod %s is not running, expected status %s got %s", consts.MetalLBOperatorDeploymentName, pod.Name, corev1.PodRunning, pod.Status.Phase) 72 | } 73 | } 74 | 75 | return nil 76 | }, metallb.DeployTimeout, metallb.Interval).ShouldNot(HaveOccurred()) 77 | }) 78 | 79 | It("should have the MetalLB CRD available in the cluster", func() { 80 | crd := &apiext.CustomResourceDefinition{} 81 | err := testclient.Client.Get(context.Background(), goclient.ObjectKey{Name: consts.MetalLBOperatorCRDName}, crd) 82 | Expect(err).ToNot(HaveOccurred()) 83 | }) 84 | }) 85 | }) 86 | -------------------------------------------------------------------------------- /test/e2e/validation/validation_test.go: -------------------------------------------------------------------------------- 1 | //go:build validationtests 2 | // +build validationtests 3 | 4 | package validation 5 | 6 | import ( 7 | "flag" 8 | "os" 9 | "path" 10 | "testing" 11 | 12 | . "github.com/onsi/ginkgo/v2" 13 | . "github.com/onsi/gomega" 14 | 15 | "github.com/metallb/metallb-operator/test/consts" 16 | "github.com/metallb/metallb-operator/test/e2e/k8sreporter" 17 | _ "github.com/metallb/metallb-operator/test/e2e/validation/tests" 18 | kniK8sReporter "github.com/openshift-kni/k8sreporter" 19 | ) 20 | 21 | var OperatorNameSpace = consts.DefaultOperatorNameSpace 22 | 23 | var junitPath *string 24 | var reportPath *string 25 | var r *kniK8sReporter.KubernetesReporter 26 | 27 | func init() { 28 | if ns := os.Getenv("OO_INSTALL_NAMESPACE"); len(ns) != 0 { 29 | OperatorNameSpace = ns 30 | } 31 | 32 | junitPath = flag.String("junit", "", "the path for the junit format report") 33 | reportPath = flag.String("report", "", "the path of the report file containing details for failed tests") 34 | } 35 | 36 | func TestValidation(t *testing.T) { 37 | // We want to collect logs before any resource is deleted in AfterEach, so we register the global fail handler 38 | // in a way such that the reporter's Dump is always called before the default Fail. 39 | RegisterFailHandler(func(message string, callerSkip ...int) { 40 | if r != nil { 41 | r.Dump(consts.LogsExtractDuration, CurrentSpecReport().FullText()) 42 | } 43 | 44 | // Ensure failing line location is not affected by this wrapper 45 | for i := range callerSkip { 46 | callerSkip[i]++ 47 | } 48 | Fail(message, callerSkip...) 49 | }) 50 | _, reporterConfig := GinkgoConfiguration() 51 | 52 | if *junitPath != "" { 53 | junitFile := path.Join(*junitPath, "validation_junit.xml") 54 | reporterConfig.JUnitReport = junitFile 55 | } 56 | 57 | if *reportPath != "" { 58 | kubeconfig := os.Getenv("KUBECONFIG") 59 | reportPath := path.Join(*reportPath, "metallb_failure_report.log") 60 | r = k8sreporter.New(kubeconfig, reportPath, OperatorNameSpace) 61 | } 62 | 63 | RunSpecs(t, "Metallb Operator Validation Suite", reporterConfig) 64 | } 65 | --------------------------------------------------------------------------------