mirror of
https://github.com/YuzuZensai/netbird-kubernetes-operator.git
synced 2026-09-13 10:49:15 +00:00
Refactor uninstallation process (#74)
Docs PR: https://github.com/netbirdio/docs/pull/457 Changes: * Split kubernetes-operator Chart to operator only chart (kubernetes-operator) and configuration chart (netbird-operator-config) * Remove delete validation webhooks for non-needed resources * Allow abandoning Groups when still linked to a resource for over a minute after deletion * Fix duplciate Network Resource retrying heavily * Fix SetupKey creation duplication * Skip deleting routing peer since deleting network automatically deletes it anyway
This commit is contained in:
@@ -5,6 +5,7 @@ on:
|
||||
paths:
|
||||
# update this file to trigger helm chart release
|
||||
- 'helm/kubernetes-operator/Chart.yaml'
|
||||
- 'helm/netbird-operator-config/Chart.yaml'
|
||||
branches:
|
||||
- main
|
||||
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
name: Docker
|
||||
|
||||
on:
|
||||
push:
|
||||
paths:
|
||||
- 'Dockerfile.kubectl'
|
||||
tags:
|
||||
- "v*"
|
||||
branches:
|
||||
- main
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
kubectl-docker:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
# This is used to complete the identity challenge
|
||||
# with sigstore/fulcio when running outside of PRs.
|
||||
id-token: write
|
||||
|
||||
steps:
|
||||
- name: Docker meta
|
||||
id: meta
|
||||
uses: docker/metadata-action@v5
|
||||
with:
|
||||
# list of Docker images to use as base name for tags
|
||||
images: |
|
||||
netbirdio/kubectl
|
||||
# generate Docker tags based on the following events/attributes
|
||||
tags: |
|
||||
type=ref,event=pr
|
||||
type=ref,event=branch
|
||||
type=semver,pattern={{version}}
|
||||
|
||||
- name: Login to Docker Hub
|
||||
if: github.repository == github.event.pull_request.head.repo.full_name || !github.head_ref
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_USER }}
|
||||
password: ${{ secrets.DOCKER_TOKEN }}
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v3
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Build and push
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
platforms: linux/amd64,linux/arm64
|
||||
push: true
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
file: "Dockerfile.kubectl"
|
||||
labels: |
|
||||
"org.opencontainers.image.created={{.Date}}"
|
||||
"org.opencontainers.image.title={{.ProjectName}}"
|
||||
"org.opencontainers.image.version={{.Version}}"
|
||||
"org.opencontainers.image.revision={{.FullCommit}}"
|
||||
"org.opencontainers.image.version={{.Version}}"
|
||||
"maintainer=dev@netbird.io"
|
||||
@@ -0,0 +1,10 @@
|
||||
FROM alpine:3 AS builder
|
||||
|
||||
RUN apk update && apk add curl
|
||||
RUN curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl" && \
|
||||
chmod +x kubectl && \
|
||||
mv kubectl /usr/local/bin/kubectl
|
||||
|
||||
FROM alpine:3 AS final
|
||||
|
||||
COPY --from=builder /usr/local/bin/kubectl /usr/local/bin/kubectl
|
||||
-10
@@ -294,16 +294,6 @@ func main() {
|
||||
}
|
||||
|
||||
if enableWebhooks {
|
||||
if err = webhooknetbirdiov1.SetupNBResourceWebhookWithManager(mgr); err != nil {
|
||||
setupLog.Error(err, "unable to create webhook", "webhook", "NBResource")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if err = webhooknetbirdiov1.SetupNBRoutingPeerWebhookWithManager(mgr); err != nil {
|
||||
setupLog.Error(err, "unable to create webhook", "webhook", "NBRoutingPeer")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if err = webhooknetbirdiov1.SetupNBGroupWebhookWithManager(mgr); err != nil {
|
||||
setupLog.Error(err, "unable to create webhook", "webhook", "NBGroup")
|
||||
os.Exit(1)
|
||||
|
||||
@@ -3,13 +3,6 @@
|
||||
# tag: "0.1.0"
|
||||
ingress:
|
||||
enabled: true
|
||||
router:
|
||||
enabled: true
|
||||
# policies:
|
||||
# default:
|
||||
# name: Kubernetes Default Policy
|
||||
# sourceGroups:
|
||||
# - All
|
||||
|
||||
netbirdAPI:
|
||||
# Replace with valid NetBird Service Account token (PAT)
|
||||
@@ -0,0 +1,7 @@
|
||||
router:
|
||||
enabled: true
|
||||
policies:
|
||||
default:
|
||||
name: Kubernetes Default Policy
|
||||
sourceGroups:
|
||||
- All
|
||||
@@ -2,5 +2,5 @@ apiVersion: v2
|
||||
name: kubernetes-operator
|
||||
description: NetBird Kubernetes Operator
|
||||
type: application
|
||||
version: 0.1.15
|
||||
appVersion: "0.1.5"
|
||||
version: 0.2.0
|
||||
appVersion: "0.2.0"
|
||||
|
||||
@@ -27,7 +27,7 @@ spec:
|
||||
spec:
|
||||
initContainers:
|
||||
- name: wait-network-ready
|
||||
image: "bitnami/kubectl:latest"
|
||||
image: "netbirdio/kubectl:latest"
|
||||
command:
|
||||
- bash
|
||||
- -c
|
||||
@@ -35,7 +35,7 @@ spec:
|
||||
- kubectl wait --for 'jsonpath={.status.networkID}' -n {{ $routerNS }} nbroutingpeer router;
|
||||
containers:
|
||||
- name: apply-nbresource
|
||||
image: "bitnami/kubectl:latest"
|
||||
image: "netbirdio/kubectl:latest"
|
||||
env:
|
||||
- name: NBRESOURCE_VALUE
|
||||
value: |
|
||||
|
||||
@@ -8,6 +8,8 @@ metadata:
|
||||
labels:
|
||||
app.kubernetes.io/component: operator
|
||||
{{- include "kubernetes-operator.labels" $ | nindent 4 }}
|
||||
annotations:
|
||||
helm.sh/resource-policy: keep
|
||||
name: {{ $k }}
|
||||
spec:
|
||||
name: {{ $v.name }}
|
||||
|
||||
@@ -10,6 +10,8 @@ metadata:
|
||||
labels:
|
||||
app.kubernetes.io/component: operator
|
||||
{{- include "kubernetes-operator.labels" $ | nindent 4 }}
|
||||
annotations:
|
||||
helm.sh/resource-policy: keep
|
||||
name: router
|
||||
namespace: {{ $k }}
|
||||
{{ $spec := merge $defaults $v }}
|
||||
@@ -51,6 +53,8 @@ metadata:
|
||||
labels:
|
||||
app.kubernetes.io/component: operator
|
||||
{{- include "kubernetes-operator.labels" $ | nindent 4 }}
|
||||
annotations:
|
||||
helm.sh/resource-policy: keep
|
||||
name: router
|
||||
{{- if or (or (or .replicas .resources) (or .labels .annotations)) (or .nodeSelector .tolerations) }}
|
||||
spec:
|
||||
|
||||
@@ -1,69 +1,41 @@
|
||||
apiVersion: batch/v1
|
||||
kind: Job
|
||||
metadata:
|
||||
name: {{ include "kubernetes-operator.fullname" . }}-delete-routers
|
||||
labels:
|
||||
app.kubernetes.io/component: operator
|
||||
{{- include "kubernetes-operator.labels" . | nindent 4 }}
|
||||
annotations:
|
||||
helm.sh/hook: pre-delete
|
||||
helm.sh/hook-delete-policy: before-hook-creation,hook-succeeded
|
||||
spec:
|
||||
backoffLimit: 3
|
||||
template:
|
||||
metadata:
|
||||
name: {{ include "kubernetes-operator.fullname" . }}
|
||||
labels:
|
||||
app.kubernetes.io/component: operator
|
||||
{{- include "kubernetes-operator.labels" . | nindent 8 }}
|
||||
{{- with .Values.operator.podLabels }}
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
spec:
|
||||
containers:
|
||||
- name: pre-delete
|
||||
image: "bitnami/kubectl:latest"
|
||||
args:
|
||||
- delete
|
||||
- --all
|
||||
- -A
|
||||
- --cascade=foreground
|
||||
- --ignore-not-found
|
||||
- NBRoutingPeer
|
||||
serviceAccountName: {{ include "kubernetes-operator.serviceAccountName" . }}
|
||||
restartPolicy: Never
|
||||
---
|
||||
apiVersion: batch/v1
|
||||
kind: Job
|
||||
metadata:
|
||||
name: {{ include "kubernetes-operator.fullname" . }}-delete-policies
|
||||
labels:
|
||||
app.kubernetes.io/component: operator
|
||||
{{- include "kubernetes-operator.labels" . | nindent 4 }}
|
||||
annotations:
|
||||
helm.sh/hook: pre-delete
|
||||
helm.sh/hook-delete-policy: before-hook-creation,hook-succeeded
|
||||
spec:
|
||||
backoffLimit: 3
|
||||
template:
|
||||
metadata:
|
||||
name: {{ include "kubernetes-operator.fullname" . }}
|
||||
labels:
|
||||
app.kubernetes.io/component: operator
|
||||
{{- include "kubernetes-operator.labels" . | nindent 8 }}
|
||||
{{- with .Values.operator.podLabels }}
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
spec:
|
||||
containers:
|
||||
- name: pre-delete
|
||||
image: "bitnami/kubectl:latest"
|
||||
args:
|
||||
- delete
|
||||
- --all
|
||||
- --cascade=foreground
|
||||
- --ignore-not-found
|
||||
- NBPolicy
|
||||
serviceAccountName: {{ include "kubernetes-operator.serviceAccountName" . }}
|
||||
restartPolicy: Never
|
||||
---
|
||||
{{/*apiVersion: batch/v1*/}}
|
||||
{{/*kind: Job*/}}
|
||||
{{/*metadata:*/}}
|
||||
{{/* name: {{ include "kubernetes-operator.fullname" . }}-delete-router-deployments*/}}
|
||||
{{/* labels:*/}}
|
||||
{{/* app.kubernetes.io/component: operator*/}}
|
||||
{{/* {{- include "kubernetes-operator.labels" . | nindent 4 }}*/}}
|
||||
{{/* annotations:*/}}
|
||||
{{/* helm.sh/hook: pre-delete*/}}
|
||||
{{/* helm.sh/hook-delete-policy: before-hook-creation,hook-succeeded*/}}
|
||||
{{/*spec:*/}}
|
||||
{{/* backoffLimit: 3*/}}
|
||||
{{/* template:*/}}
|
||||
{{/* metadata:*/}}
|
||||
{{/* name: {{ include "kubernetes-operator.fullname" . }}*/}}
|
||||
{{/* labels:*/}}
|
||||
{{/* app.kubernetes.io/component: operator*/}}
|
||||
{{/* {{- include "kubernetes-operator.labels" . | nindent 8 }}*/}}
|
||||
{{/* {{- with .Values.operator.podLabels }}*/}}
|
||||
{{/* {{- toYaml . | nindent 8 }}*/}}
|
||||
{{/* {{- end }}*/}}
|
||||
{{/* spec:*/}}
|
||||
{{/* containers:*/}}
|
||||
{{/* - name: pre-delete*/}}
|
||||
{{/* image: "netbirdio/kubectl:latest"*/}}
|
||||
{{/* imagePullPolicy: {{ .Values.operator.image.pullPolicy }}*/}}
|
||||
{{/* command:*/}}
|
||||
{{/* - sh*/}}
|
||||
{{/* - -c*/}}
|
||||
{{/* args:*/}}
|
||||
{{/* - kubectl get NBRoutingPeer -A --no-headers -o custom-columns=NAMESPACE:.metadata.namespace,NAME:.metadata.name | while read "L"; do kubectl patch --type=json -p '[{"op":"replace","path":"/spec/disableDeployment","value":true}]' NBRoutingPeer -n $(echo "$L" | awk '{print $1}') $(echo "$L" | awk '{print $2}'); done*/}}
|
||||
{{/* - name: delete-wait*/}}
|
||||
{{/* image: "netbirdio/kubectl:latest"*/}}
|
||||
{{/* imagePullPolicy: {{ .Values.operator.image.pullPolicy }}*/}}
|
||||
{{/* command:*/}}
|
||||
{{/* - sh*/}}
|
||||
{{/* - -c*/}}
|
||||
{{/* args:*/}}
|
||||
{{/* - kubectl get NBRoutingPeer -A --no-headers -o custom-columns=NAMESPACE:.metadata.namespace,NAME:.metadata.name | while read "L"; do kubectl wait --for=delete deployment -n $(echo "$L" | awk '{print $1}') $(echo "$L" | awk '{print $2}'); done*/}}
|
||||
{{/* serviceAccountName: {{ include "kubernetes-operator.serviceAccountName" . }}*/}}
|
||||
{{/* restartPolicy: Never*/}}
|
||||
|
||||
@@ -96,84 +96,6 @@ webhooks:
|
||||
apiVersion: admissionregistration.k8s.io/v1
|
||||
kind: ValidatingWebhookConfiguration
|
||||
metadata:
|
||||
{{- if $.Values.webhook.enableCertManager }}
|
||||
annotations:
|
||||
cert-manager.io/inject-ca-from: {{ .Release.Namespace }}/{{ template "kubernetes-operator.fullname" . }}-serving-cert
|
||||
{{- end }}
|
||||
name: {{ include "kubernetes-operator.fullname" . }}-vnbresource-webhook
|
||||
labels:
|
||||
{{- include "kubernetes-operator.labels" . | nindent 4 }}
|
||||
webhooks:
|
||||
- clientConfig:
|
||||
{{- if not $.Values.webhook.enableCertManager }}
|
||||
caBundle: {{ $tls.caCert }}
|
||||
{{ end }}
|
||||
service:
|
||||
name: {{ template "kubernetes-operator.webhookService" . }}
|
||||
namespace: {{ $.Release.Namespace }}
|
||||
path: /validate-netbird-io-v1-nbresource
|
||||
failurePolicy: {{ .Values.webhook.failurePolicy }}
|
||||
name: vnbresource-v1.netbird.io
|
||||
admissionReviewVersions:
|
||||
- v1
|
||||
{{- if .Values.webhook.namespaceSelectors }}
|
||||
namespaceSelector:
|
||||
matchExpressions:
|
||||
{{ toYaml .Values.webhook.namespaceSelectors | nindent 4 }}
|
||||
{{ end }}
|
||||
rules:
|
||||
- apiGroups:
|
||||
- netbird.io
|
||||
apiVersions:
|
||||
- v1
|
||||
operations:
|
||||
- DELETE
|
||||
resources:
|
||||
- "nbresources"
|
||||
sideEffects: None
|
||||
---
|
||||
apiVersion: admissionregistration.k8s.io/v1
|
||||
kind: ValidatingWebhookConfiguration
|
||||
metadata:
|
||||
{{- if $.Values.webhook.enableCertManager }}
|
||||
annotations:
|
||||
cert-manager.io/inject-ca-from: {{ .Release.Namespace }}/{{ template "kubernetes-operator.fullname" . }}-serving-cert
|
||||
{{- end }}
|
||||
name: {{ include "kubernetes-operator.fullname" . }}-vnbroutingpeer-webhook
|
||||
labels:
|
||||
{{- include "kubernetes-operator.labels" . | nindent 4 }}
|
||||
webhooks:
|
||||
- clientConfig:
|
||||
{{- if not $.Values.webhook.enableCertManager }}
|
||||
caBundle: {{ $tls.caCert }}
|
||||
{{ end }}
|
||||
service:
|
||||
name: {{ template "kubernetes-operator.webhookService" . }}
|
||||
namespace: {{ $.Release.Namespace }}
|
||||
path: /validate-netbird-io-v1-nbroutingpeer
|
||||
failurePolicy: {{ .Values.webhook.failurePolicy }}
|
||||
name: vnbroutingpeer-v1.netbird.io
|
||||
admissionReviewVersions:
|
||||
- v1
|
||||
{{- if .Values.webhook.namespaceSelectors }}
|
||||
namespaceSelector:
|
||||
matchExpressions:
|
||||
{{ toYaml .Values.webhook.namespaceSelectors | nindent 4 }}
|
||||
{{ end }}
|
||||
rules:
|
||||
- apiGroups:
|
||||
- netbird.io
|
||||
apiVersions:
|
||||
- v1
|
||||
operations:
|
||||
- DELETE
|
||||
resources:
|
||||
- "nbroutingpeers"
|
||||
sideEffects: None
|
||||
---
|
||||
apiVersion: admissionregistration.k8s.io/v1
|
||||
kind: ValidatingWebhookConfiguration
|
||||
metadata:
|
||||
{{- if $.Values.webhook.enableCertManager }}
|
||||
annotations:
|
||||
cert-manager.io/inject-ca-from: {{ .Release.Namespace }}/{{ template "kubernetes-operator.fullname" . }}-serving-cert
|
||||
|
||||
@@ -140,14 +140,14 @@ ingress:
|
||||
namespacedNetworks: false
|
||||
# Allow creating policies through Service annotations
|
||||
allowAutomaticPolicyCreation: false
|
||||
kubernetesAPI:
|
||||
kubernetesAPI: # DEPRECATED: Use netbirdio/netbird-operator-configs Chart instead
|
||||
enabled: false
|
||||
groups: []
|
||||
# - group1
|
||||
# - group2
|
||||
policies: []
|
||||
# - default
|
||||
router:
|
||||
router: # DEPRECATED: Use netbirdio/netbird-operator-configs Chart instead
|
||||
# Deploy routing peer(s)
|
||||
enabled: false
|
||||
# replicas: 3
|
||||
@@ -178,7 +178,7 @@ ingress:
|
||||
# nodeSelector: {}
|
||||
# tolerations: []
|
||||
# NetBird Policies for use with exposed services
|
||||
policies: {}
|
||||
policies: {} # DEPRECATED: Use netbirdio/netbird-operator-configs Chart instead
|
||||
# default:
|
||||
# name: Kubernetes Default Policy
|
||||
# sourceGroups:
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
# Patterns to ignore when building packages.
|
||||
# This supports shell glob matching, relative path matching, and
|
||||
# negation (prefixed with !). Only one pattern per line.
|
||||
.DS_Store
|
||||
# Common VCS dirs
|
||||
.git/
|
||||
.gitignore
|
||||
.bzr/
|
||||
.bzrignore
|
||||
.hg/
|
||||
.hgignore
|
||||
.svn/
|
||||
# Common backup files
|
||||
*.swp
|
||||
*.bak
|
||||
*.tmp
|
||||
*.orig
|
||||
*~
|
||||
# Various IDEs
|
||||
.project
|
||||
.idea/
|
||||
*.tmproj
|
||||
.vscode/
|
||||
@@ -0,0 +1,6 @@
|
||||
apiVersion: v2
|
||||
name: netbird-operator-config
|
||||
description: A Helm chart for Kubernetes
|
||||
type: application
|
||||
version: 0.1.0
|
||||
appVersion: "0.0.0"
|
||||
@@ -0,0 +1,62 @@
|
||||
{{/*
|
||||
Create a default fully qualified app name.
|
||||
We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
|
||||
If release name contains chart name it will be used as a full name.
|
||||
*/}}
|
||||
{{- define "netbird-operator-config.fullname" -}}
|
||||
{{- if .Values.fullnameOverride }}
|
||||
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
|
||||
{{- else }}
|
||||
{{- $name := default .Chart.Name .Values.nameOverride }}
|
||||
{{- if contains $name .Release.Name }}
|
||||
{{- .Release.Name | trunc 63 | trimSuffix "-" }}
|
||||
{{- else }}
|
||||
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
||||
{{/*
|
||||
Expand the name of the chart.
|
||||
*/}}
|
||||
{{- define "netbird-operator-config.name" -}}
|
||||
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
|
||||
{{- end }}
|
||||
|
||||
{{/*
|
||||
Create chart name and version as used by the chart label.
|
||||
*/}}
|
||||
{{- define "netbird-operator-config.chart" -}}
|
||||
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
|
||||
{{- end }}
|
||||
|
||||
{{/*
|
||||
Common labels
|
||||
*/}}
|
||||
{{- define "netbird-operator-config.labels" -}}
|
||||
helm.sh/chart: {{ include "netbird-operator-config.chart" . }}
|
||||
{{ include "netbird-operator-config.selectorLabels" . }}
|
||||
{{- if .Chart.AppVersion }}
|
||||
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
|
||||
{{- end }}
|
||||
app.kubernetes.io/managed-by: {{ .Release.Service }}
|
||||
{{- end }}
|
||||
|
||||
{{/*
|
||||
Selector labels
|
||||
*/}}
|
||||
{{- define "netbird-operator-config.selectorLabels" -}}
|
||||
app.kubernetes.io/name: {{ include "netbird-operator-config.name" . }}
|
||||
app.kubernetes.io/instance: {{ .Release.Name }}
|
||||
{{- end }}
|
||||
|
||||
{{/*
|
||||
Create the name of the service account to use
|
||||
*/}}
|
||||
{{- define "netbird-operator-config.serviceAccountName" -}}
|
||||
{{- if .Values.serviceAccount.create }}
|
||||
{{- default (include "netbird-operator-config.fullname" .) .Values.serviceAccount.name }}
|
||||
{{- else }}
|
||||
{{- default "default" .Values.serviceAccount.name }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
@@ -0,0 +1,71 @@
|
||||
{{- if and .Values.ingress.enabled .Values.ingress.kubernetesAPI.enabled }}
|
||||
{{- $routerNS := .Release.Namespace }}
|
||||
{{- if .Values.ingress.namespacedNetworks }}
|
||||
{{- $routerNS = "default" }}
|
||||
{{- end }}
|
||||
apiVersion: batch/v1
|
||||
kind: Job
|
||||
metadata:
|
||||
name: {{ include "netbird-operator-config.fullname" . }}-kubernetes-service-expose
|
||||
labels:
|
||||
app.kubernetes.io/component: operator
|
||||
{{- include "netbird-operator-config.labels" . | nindent 4 }}
|
||||
annotations:
|
||||
helm.sh/hook: post-upgrade,post-install
|
||||
helm.sh/hook-delete-policy: before-hook-creation,hook-succeeded
|
||||
spec:
|
||||
backoffLimit: 3
|
||||
template:
|
||||
metadata:
|
||||
name: {{ include "netbird-operator-config.fullname" . }}
|
||||
labels:
|
||||
app.kubernetes.io/component: operator
|
||||
{{- include "netbird-operator-config.labels" . | nindent 8 }}
|
||||
{{- with .Values.operator.podLabels }}
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
spec:
|
||||
initContainers:
|
||||
- name: wait-network-ready
|
||||
image: "netbirdio/kubectl:latest"
|
||||
command:
|
||||
- bash
|
||||
- -c
|
||||
args:
|
||||
- kubectl wait --for 'jsonpath={.status.networkID}' -n {{ $routerNS }} nbroutingpeer router;
|
||||
containers:
|
||||
- name: apply-nbresource
|
||||
image: "netbirdio/kubectl:latest"
|
||||
env:
|
||||
- name: NBRESOURCE_VALUE
|
||||
value: |
|
||||
apiVersion: netbird.io/v1
|
||||
kind: NBResource
|
||||
metadata:
|
||||
finalizers:
|
||||
- netbird.io/cleanup
|
||||
name: kubernetes
|
||||
namespace: default
|
||||
spec:
|
||||
address: kubernetes.default.{{.Values.cluster.dns}}
|
||||
groups:
|
||||
{{- if .Values.kubernetesAPI.groups }}
|
||||
{{ toYaml .Values.kubernetesAPI.groups }}
|
||||
{{- else }}
|
||||
- {{ .Values.cluster.name }}-default-api-access
|
||||
{{- end }}
|
||||
name: {{ .Values.kubernetesAPI.resourceName | default "default-kubernetes-api" }}
|
||||
networkID: ${NETWORK_ID}
|
||||
{{- if .Values.kubernetesAPI.policies }}
|
||||
policyName: "{{ join "," .Values.kubernetesAPI.policies }}"
|
||||
{{- end }}
|
||||
tcpPorts:
|
||||
- 443
|
||||
command:
|
||||
- bash
|
||||
- -c
|
||||
args:
|
||||
- kubectl delete NBResource --ignore-not-found -n default kubernetes; export NETWORK_ID=$(kubectl get NBRoutingPeer -n {{ $routerNS }} router -o 'jsonpath={.status.networkID}'); echo "$NBRESOURCE_VALUE" | envsubst | kubectl apply -f -
|
||||
serviceAccountName: {{ include "netbird-operator-config.serviceAccountName" . }}
|
||||
restartPolicy: Never
|
||||
{{- end }}
|
||||
@@ -0,0 +1,30 @@
|
||||
{{- range $k, $v := $.Values.policies }}
|
||||
---
|
||||
apiVersion: netbird.io/v1
|
||||
kind: NBPolicy
|
||||
metadata:
|
||||
annotations:
|
||||
helm.sh/resource-policy: keep
|
||||
finalizers:
|
||||
- netbird.io/cleanup
|
||||
labels:
|
||||
app.kubernetes.io/component: operator
|
||||
{{- include "netbird-operator-config.labels" $ | nindent 4 }}
|
||||
name: {{ $k }}
|
||||
spec:
|
||||
name: {{ $v.name }}
|
||||
sourceGroups:
|
||||
{{ toYaml $v.sourceGroups | nindent 4}}
|
||||
{{- if $v.description }}
|
||||
description: {{ $v.description }}
|
||||
{{- end }}
|
||||
{{- if $v.protocols }}
|
||||
protocols: {{ $v.protocols }}
|
||||
{{- end }}
|
||||
{{- if $v.ports }}
|
||||
ports: {{ $v.ports }}
|
||||
{{- end }}
|
||||
{{- if hasKey $v "bidirectional" }}
|
||||
bidirectional: {{ $v.bidirectional }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
@@ -0,0 +1,83 @@
|
||||
{{- if .Values.router.enabled }}
|
||||
{{- if .Values.namespacedNetworks }}
|
||||
{{ $defaults := .Values.router }}
|
||||
{{ range $k, $v := .Values.router.namespaces }}
|
||||
apiVersion: netbird.io/v1
|
||||
kind: NBRoutingPeer
|
||||
metadata:
|
||||
finalizers:
|
||||
- netbird.io/cleanup
|
||||
labels:
|
||||
app.kubernetes.io/component: operator
|
||||
{{- include "netbird-operator-config.labels" $ | nindent 4 }}
|
||||
name: router
|
||||
namespace: {{ $k }}
|
||||
{{ $spec := merge $defaults $v }}
|
||||
{{- if or (or (or $spec.replicas $spec.resources) (or $spec.labels $spec.annotations)) (or $spec.nodeSelector $spec.tolerations) }}
|
||||
spec:
|
||||
{{- if $spec.replicas }}
|
||||
replicas: {{ $spec.replicas }}
|
||||
{{- end }}
|
||||
{{- if $spec.resources }}
|
||||
resources:
|
||||
{{- toYaml $spec.resources | nindent 4 }}
|
||||
{{- end }}
|
||||
{{- if $spec.labels }}
|
||||
labels:
|
||||
{{- toYaml $spec.labels | nindent 4 }}
|
||||
{{- end }}
|
||||
{{- if $spec.annotations }}
|
||||
annotations:
|
||||
{{- toYaml $spec.annotations | nindent 4 }}
|
||||
{{- end }}
|
||||
{{- if $spec.nodeSelector }}
|
||||
nodeSelector:
|
||||
{{- toYaml $spec.nodeSelector | nindent 4 }}
|
||||
{{- end }}
|
||||
{{- if $spec.tolerations }}
|
||||
tolerations:
|
||||
{{- toYaml $spec.tolerations | nindent 4 }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
---
|
||||
{{- end }}
|
||||
{{- else }}
|
||||
{{- with .Values.router }}
|
||||
apiVersion: netbird.io/v1
|
||||
kind: NBRoutingPeer
|
||||
metadata:
|
||||
finalizers:
|
||||
- netbird.io/cleanup
|
||||
labels:
|
||||
app.kubernetes.io/component: operator
|
||||
{{- include "netbird-operator-config.labels" $ | nindent 4 }}
|
||||
name: router
|
||||
{{- if or (or (or .replicas .resources) (or .labels .annotations)) (or .nodeSelector .tolerations) }}
|
||||
spec:
|
||||
{{- if .replicas }}
|
||||
replicas: {{ .replicas }}
|
||||
{{- end }}
|
||||
{{- if .resources }}
|
||||
resources:
|
||||
{{- toYaml .resources | nindent 4 }}
|
||||
{{- end }}
|
||||
{{- if .labels }}
|
||||
labels:
|
||||
{{- toYaml .labels | nindent 4 }}
|
||||
{{- end }}
|
||||
{{- if .annotations }}
|
||||
annotations:
|
||||
{{- toYaml .annotations | nindent 4 }}
|
||||
{{- end }}
|
||||
{{- if .nodeSelector }}
|
||||
nodeSelector:
|
||||
{{- toYaml .nodeSelector | nindent 4 }}
|
||||
{{- end }}
|
||||
{{- if .tolerations }}
|
||||
tolerations:
|
||||
{{- toYaml .tolerations | nindent 4 }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
@@ -0,0 +1,45 @@
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
name: {{ include "netbird-operator-config.fullname" . }}
|
||||
labels:
|
||||
{{- include "netbird-operator-config.labels" . | nindent 4 }}
|
||||
rules:
|
||||
- apiGroups:
|
||||
- netbird.io
|
||||
resources:
|
||||
- nbresources
|
||||
verbs:
|
||||
- patch
|
||||
- update
|
||||
- list
|
||||
- watch
|
||||
- create
|
||||
- delete
|
||||
- apiGroups:
|
||||
- netbird.io
|
||||
resources:
|
||||
- nbroutingpeers
|
||||
verbs:
|
||||
- get
|
||||
- apiGroups:
|
||||
- netbird.io
|
||||
resources:
|
||||
- nbresources/finalizers
|
||||
verbs:
|
||||
- update
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRoleBinding
|
||||
metadata:
|
||||
name: {{ include "netbird-operator-config.fullname" . }}
|
||||
labels:
|
||||
{{- include "netbird-operator-config.labels" . | nindent 4 }}
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: ClusterRole
|
||||
name: {{ include "netbird-operator-config.fullname" . }}
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: {{ include "netbird-operator-config.serviceAccountName" . }}
|
||||
namespace: {{ .Release.Namespace }}
|
||||
@@ -0,0 +1,13 @@
|
||||
{{- if and .Values.kubernetesAPI.enabled .Values.serviceAccount.create -}}
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: {{ include "netbird-operator-config.serviceAccountName" . }}
|
||||
labels:
|
||||
{{- include "netbird-operator-config.labels" . | nindent 4 }}
|
||||
{{- with .Values.serviceAccount.annotations }}
|
||||
annotations:
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
automountServiceAccountToken: true
|
||||
{{- end }}
|
||||
@@ -0,0 +1,57 @@
|
||||
cluster:
|
||||
name: "kubernetes"
|
||||
dns: "svc.cluster.local"
|
||||
|
||||
# Create router per namespace, useful for strict networking requirements
|
||||
namespacedNetworks: false
|
||||
|
||||
router:
|
||||
# Deploy routing peer(s)
|
||||
enabled: false
|
||||
# replicas: 3
|
||||
# resources:
|
||||
# requests:
|
||||
# cpu: 100m
|
||||
# memory: 100Mi
|
||||
# limits:
|
||||
# cpu: 100m
|
||||
# memory: 100Mi
|
||||
# labels: {}
|
||||
# annotations: {}
|
||||
# nodeSelector: {}
|
||||
# tolerations: []
|
||||
# Only needed if namespacedNetworks is set to true
|
||||
namespaces: {}
|
||||
# default:
|
||||
# replicas: 3
|
||||
# resources:
|
||||
# requests:
|
||||
# cpu: 100m
|
||||
# memory: 100Mi
|
||||
# limits:
|
||||
# cpu: 100m
|
||||
# memory: 100Mi
|
||||
# labels: {}
|
||||
# annotations: {}
|
||||
# nodeSelector: {}
|
||||
# tolerations: []
|
||||
# NetBird Policies for use with exposed services
|
||||
policies: {}
|
||||
# default:
|
||||
# name: Kubernetes Default Policy
|
||||
# sourceGroups:
|
||||
# - All
|
||||
|
||||
kubernetesAPI:
|
||||
enabled: false
|
||||
groups: []
|
||||
# - group1
|
||||
# - group2
|
||||
policies: []
|
||||
# - default
|
||||
# resourceName: "my-cluster-kubernetes"
|
||||
|
||||
serviceAccount:
|
||||
create: true
|
||||
name: ""
|
||||
annotations: {}
|
||||
@@ -151,7 +151,7 @@ func (r *NBGroupReconciler) handleDelete(ctx context.Context, nbGroup netbirdiov
|
||||
return err
|
||||
}
|
||||
|
||||
if err != nil && strings.Contains(err.Error(), "linked") {
|
||||
if err != nil && strings.Contains(err.Error(), "linked") && !nbGroup.DeletionTimestamp.Add(time.Minute).Before(time.Now()) {
|
||||
logger.Info("group still linked to resources on netbird", "err", err)
|
||||
// Check if group is defined elsewhere in the cluster
|
||||
var groups netbirdiov1.NBGroupList
|
||||
|
||||
@@ -2,11 +2,15 @@ package controller
|
||||
|
||||
import (
|
||||
"context"
|
||||
nerrors "errors"
|
||||
"fmt"
|
||||
"slices"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"sigs.k8s.io/controller-runtime/pkg/log"
|
||||
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
@@ -23,6 +27,10 @@ import (
|
||||
"github.com/netbirdio/netbird/management/server/http/api"
|
||||
)
|
||||
|
||||
var (
|
||||
errDuplicateResource = fmt.Errorf("duplicate resource")
|
||||
)
|
||||
|
||||
// NBResourceReconciler reconciles a NBResource object
|
||||
type NBResourceReconciler struct {
|
||||
client.Client
|
||||
@@ -87,6 +95,10 @@ func (r *NBResourceReconciler) Reconcile(ctx context.Context, req ctrl.Request)
|
||||
}
|
||||
|
||||
resource, err := r.handleNetBirdResource(ctx, nbResource, groupIDs, logger)
|
||||
if err != nil && nerrors.Is(err, errDuplicateResource) {
|
||||
return ctrl.Result{RequeueAfter: defaultRequeueAfter}, nil
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
nbResource.Status.Conditions = netbirdiov1.NBConditionFalse("internalError", fmt.Sprintf("Error occurred handling NetBird Network Resource: %v", err))
|
||||
return ctrl.Result{}, err
|
||||
@@ -406,6 +418,12 @@ func (r *NBResourceReconciler) handleNetBirdResource(ctx context.Context, nbReso
|
||||
Name: nbResource.Spec.Name,
|
||||
})
|
||||
|
||||
if err != nil && strings.Contains(err.Error(), "already exists") {
|
||||
log.Log.Error(errNetBirdAPI, "network resource with the same name already exists", "err", err)
|
||||
nbResource.Status.Conditions = netbirdiov1.NBConditionFalse("DuplicateName", "Resource name already exists")
|
||||
return nil, errDuplicateResource
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
logger.Error(errNetBirdAPI, "error creating resource", "err", err)
|
||||
return nil, err
|
||||
@@ -562,6 +580,22 @@ func (r *NBResourceReconciler) handleGroups(ctx context.Context, req ctrl.Reques
|
||||
}
|
||||
|
||||
func (r *NBResourceReconciler) handleDelete(ctx context.Context, req ctrl.Request, nbResource *netbirdiov1.NBResource, logger logr.Logger) error {
|
||||
var svc corev1.Service
|
||||
err := r.Client.Get(ctx, req.NamespacedName, &svc)
|
||||
if !errors.IsNotFound(err) {
|
||||
logger.Error(errKubernetesAPI, "error getting Service", "err", err, "svc", req.NamespacedName.String())
|
||||
return err
|
||||
} else if err == nil {
|
||||
if _, ok := svc.Annotations[ServiceExposeAnnotation]; ok {
|
||||
delete(svc.Annotations, ServiceExposeAnnotation)
|
||||
err = r.Client.Update(ctx, &svc)
|
||||
if err != nil {
|
||||
logger.Error(errKubernetesAPI, "error updating Service", "err", err, "svc", req.NamespacedName.String())
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if nbResource.Status.PolicyName != nil {
|
||||
for _, policy := range util.SplitTrim(*nbResource.Status.PolicyName, ",") {
|
||||
var nbPolicy netbirdiov1.NBPolicy
|
||||
@@ -593,7 +627,7 @@ func (r *NBResourceReconciler) handleDelete(ctx context.Context, req ctrl.Reques
|
||||
}
|
||||
|
||||
nbGroupList := netbirdiov1.NBGroupList{}
|
||||
err := r.Client.List(ctx, &nbGroupList, &client.ListOptions{Namespace: req.Namespace})
|
||||
err = r.Client.List(ctx, &nbGroupList, &client.ListOptions{Namespace: req.Namespace})
|
||||
if err != nil {
|
||||
logger.Error(errKubernetesAPI, "error listing NBGroup", "err", err)
|
||||
return err
|
||||
|
||||
@@ -52,12 +52,6 @@ func (r *NBRoutingPeerReconciler) Reconcile(ctx context.Context, req ctrl.Reques
|
||||
|
||||
originalNBRP := nbrp.DeepCopy()
|
||||
defer func() {
|
||||
if err != nil {
|
||||
// double check result is nil, otherwise error is not printed
|
||||
// and exponential backoff doesn't work properly
|
||||
res = ctrl.Result{}
|
||||
return
|
||||
}
|
||||
if originalNBRP.DeletionTimestamp != nil && len(nbrp.Finalizers) == 0 {
|
||||
return
|
||||
}
|
||||
@@ -67,6 +61,12 @@ func (r *NBRoutingPeerReconciler) Reconcile(ctx context.Context, req ctrl.Reques
|
||||
logger.Error(errKubernetesAPI, "error updating NBRoutingPeer Status", "err", err)
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
// double check result is nil, otherwise error is not printed
|
||||
// and exponential backoff doesn't work properly
|
||||
res = ctrl.Result{}
|
||||
return
|
||||
}
|
||||
if !res.Requeue && res.RequeueAfter == 0 {
|
||||
res.RequeueAfter = defaultRequeueAfter
|
||||
}
|
||||
@@ -210,7 +210,7 @@ func (r *NBRoutingPeerReconciler) handleDeployment(ctx context.Context, req ctrl
|
||||
nbrp.Status.Conditions = netbirdiov1.NBConditionFalse("internalError", fmt.Sprintf("error creating Deployment: %v", err))
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
} else if err == nil {
|
||||
updatedDeployment := routingPeerDeployment.DeepCopy()
|
||||
updatedDeployment.ObjectMeta.Name = nbrp.Name
|
||||
updatedDeployment.ObjectMeta.Namespace = nbrp.Namespace
|
||||
@@ -291,7 +291,7 @@ func (r *NBRoutingPeerReconciler) handleDeployment(ctx context.Context, req ctrl
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
return err
|
||||
}
|
||||
|
||||
// handleRouter reconcile network routing peer in NetBird management API
|
||||
@@ -393,6 +393,14 @@ func (r *NBRoutingPeerReconciler) handleSetupKey(ctx context.Context, req ctrl.R
|
||||
}
|
||||
err = r.Client.Create(ctx, &skSecret)
|
||||
if errors.IsAlreadyExists(err) {
|
||||
err = r.Client.Get(ctx, req.NamespacedName, &skSecret)
|
||||
if err != nil {
|
||||
logger.Error(errNetBirdAPI, "error getting secret", "err", err)
|
||||
return &ctrl.Result{}, err
|
||||
}
|
||||
skSecret.Data = map[string][]byte{
|
||||
"setupKey": []byte(setupKey.Key),
|
||||
}
|
||||
err = r.Client.Update(ctx, &skSecret)
|
||||
}
|
||||
|
||||
@@ -410,7 +418,7 @@ func (r *NBRoutingPeerReconciler) handleSetupKey(ctx context.Context, req ctrl.R
|
||||
return &ctrl.Result{}, err
|
||||
}
|
||||
|
||||
if err != nil || setupKey.Revoked {
|
||||
if (err != nil && strings.Contains(err.Error(), "not found")) || setupKey == nil || setupKey.Revoked {
|
||||
if setupKey != nil && setupKey.Revoked {
|
||||
err = r.netbird.SetupKeys.Delete(ctx, *nbrp.Status.SetupKeyID)
|
||||
|
||||
@@ -586,16 +594,6 @@ func (r *NBRoutingPeerReconciler) handleDelete(ctx context.Context, req ctrl.Req
|
||||
logger.Info("Setup key deleted", "id", setupKeyID)
|
||||
}
|
||||
|
||||
if nbrp.Status.RouterID != nil {
|
||||
err = r.netbird.Networks.Routers(*nbrp.Status.NetworkID).Delete(ctx, *nbrp.Status.RouterID)
|
||||
if err != nil && !strings.Contains(err.Error(), "not found") {
|
||||
logger.Error(errNetBirdAPI, "error deleting Network Router", "err", err)
|
||||
return ctrl.Result{}, err
|
||||
}
|
||||
|
||||
nbrp.Status.RouterID = nil
|
||||
}
|
||||
|
||||
nbGroup := netbirdiov1.NBGroup{}
|
||||
err = r.Client.Get(ctx, req.NamespacedName, &nbGroup)
|
||||
if err != nil && !errors.IsNotFound(err) {
|
||||
@@ -631,6 +629,7 @@ func (r *NBRoutingPeerReconciler) handleDelete(ctx context.Context, req ctrl.Req
|
||||
}
|
||||
|
||||
nbrp.Status.NetworkID = nil
|
||||
nbrp.Status.RouterID = nil
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -912,10 +912,8 @@ var _ = Describe("NBRoutingPeer Controller", func() {
|
||||
})
|
||||
When("NBRoutingPeer is set for deletion", func() {
|
||||
networkDeleted := false
|
||||
routerDeleted := false
|
||||
BeforeEach(func() {
|
||||
networkDeleted = false
|
||||
routerDeleted = false
|
||||
nbroutingpeer.Status.SetupKeyID = util.Ptr("skid")
|
||||
Expect(k8sClient.Status().Update(ctx, nbroutingpeer)).To(Succeed())
|
||||
|
||||
@@ -953,14 +951,6 @@ var _ = Describe("NBRoutingPeer Controller", func() {
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
networkDeleted = true
|
||||
})
|
||||
|
||||
mux.HandleFunc("/api/networks/test/routers/test", func(w http.ResponseWriter, r *http.Request) {
|
||||
defer GinkgoRecover()
|
||||
Expect(r.Method).To(Equal(http.MethodDelete))
|
||||
_, err = w.Write([]byte(`{}`))
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
routerDeleted = true
|
||||
})
|
||||
})
|
||||
|
||||
It("should remove finalizer from NBGroup", func() {
|
||||
@@ -980,7 +970,7 @@ var _ = Describe("NBRoutingPeer Controller", func() {
|
||||
NamespacedName: typeNamespacedName,
|
||||
})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(routerDeleted).To(BeTrue())
|
||||
Expect(networkDeleted).To(BeTrue())
|
||||
})
|
||||
|
||||
It("should delete Network", func() {
|
||||
|
||||
@@ -1,72 +0,0 @@
|
||||
package v1
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
ctrl "sigs.k8s.io/controller-runtime"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
logf "sigs.k8s.io/controller-runtime/pkg/log"
|
||||
"sigs.k8s.io/controller-runtime/pkg/webhook"
|
||||
"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
|
||||
|
||||
netbirdiov1 "github.com/netbirdio/kubernetes-operator/api/v1"
|
||||
"github.com/netbirdio/kubernetes-operator/internal/controller"
|
||||
)
|
||||
|
||||
// nolint:unused
|
||||
// log is for logging in this package.
|
||||
var nbresourcelog = logf.Log.WithName("nbresource-resource")
|
||||
|
||||
// SetupNBResourceWebhookWithManager registers the webhook for NBResource in the manager.
|
||||
func SetupNBResourceWebhookWithManager(mgr ctrl.Manager) error {
|
||||
return ctrl.NewWebhookManagedBy(mgr).For(&netbirdiov1.NBResource{}).
|
||||
WithValidator(&NBResourceCustomValidator{client: mgr.GetClient()}).
|
||||
Complete()
|
||||
}
|
||||
|
||||
// NBResourceCustomValidator struct is responsible for validating the NBResource resource
|
||||
// when it is created, updated, or deleted.
|
||||
type NBResourceCustomValidator struct {
|
||||
client client.Client
|
||||
}
|
||||
|
||||
var _ webhook.CustomValidator = &NBResourceCustomValidator{}
|
||||
|
||||
// ValidateCreate implements webhook.CustomValidator so a webhook will be registered for the type NBResource.
|
||||
func (v *NBResourceCustomValidator) ValidateCreate(ctx context.Context, obj runtime.Object) (admission.Warnings, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// ValidateUpdate implements webhook.CustomValidator so a webhook will be registered for the type NBResource.
|
||||
func (v *NBResourceCustomValidator) ValidateUpdate(ctx context.Context, oldObj, newObj runtime.Object) (admission.Warnings, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// ValidateDelete implements webhook.CustomValidator so a webhook will be registered for the type NBResource.
|
||||
func (v *NBResourceCustomValidator) ValidateDelete(ctx context.Context, obj runtime.Object) (admission.Warnings, error) {
|
||||
nbresource, ok := obj.(*netbirdiov1.NBResource)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("expected a NBResource object but got %T", obj)
|
||||
}
|
||||
nbresourcelog.Info("Validation for NBResource upon deletion", "name", nbresource.GetName())
|
||||
|
||||
var svc corev1.Service
|
||||
err := v.client.Get(ctx, types.NamespacedName{Namespace: nbresource.Namespace, Name: nbresource.Name}, &svc)
|
||||
if errors.IsNotFound(err) {
|
||||
return nil, nil
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if _, ok := svc.Annotations[controller.ServiceExposeAnnotation]; ok && svc.DeletionTimestamp == nil {
|
||||
return nil, fmt.Errorf("service %s/%s still has netbird.io/expose annotation", svc.Namespace, svc.Name)
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
@@ -1,122 +0,0 @@
|
||||
package v1
|
||||
|
||||
import (
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
. "github.com/onsi/gomega"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/apimachinery/pkg/util/intstr"
|
||||
|
||||
netbirdiov1 "github.com/netbirdio/kubernetes-operator/api/v1"
|
||||
)
|
||||
|
||||
var _ = Describe("NBResource Webhook", func() {
|
||||
var (
|
||||
obj *netbirdiov1.NBResource
|
||||
oldObj *netbirdiov1.NBResource
|
||||
validator NBResourceCustomValidator
|
||||
)
|
||||
|
||||
BeforeEach(func() {
|
||||
obj = &netbirdiov1.NBResource{}
|
||||
oldObj = &netbirdiov1.NBResource{}
|
||||
validator = NBResourceCustomValidator{
|
||||
client: k8sClient,
|
||||
}
|
||||
})
|
||||
|
||||
Context("When creating or updating NBResource under Validating Webhook", func() {
|
||||
It("should allow creation", func() {
|
||||
Expect(validator.ValidateCreate(ctx, obj)).Error().NotTo(HaveOccurred())
|
||||
})
|
||||
It("should allow update", func() {
|
||||
Expect(validator.ValidateUpdate(ctx, oldObj, obj)).Error().NotTo(HaveOccurred())
|
||||
})
|
||||
When("No services are exposed", func() {
|
||||
BeforeEach(func() {
|
||||
obj.Name = "maw"
|
||||
obj.Namespace = "default"
|
||||
svc := &corev1.Service{
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
Name: "ne",
|
||||
Namespace: "default",
|
||||
},
|
||||
Spec: corev1.ServiceSpec{
|
||||
Ports: []corev1.ServicePort{
|
||||
{
|
||||
Protocol: corev1.ProtocolTCP,
|
||||
Port: 80,
|
||||
TargetPort: intstr.FromInt32(80),
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
Expect(k8sClient.Create(ctx, svc)).To(Succeed())
|
||||
})
|
||||
AfterEach(func() {
|
||||
svc := &netbirdiov1.NBResource{}
|
||||
err := k8sClient.Get(ctx, types.NamespacedName{Namespace: "default", Name: "ne"}, svc)
|
||||
if !errors.IsNotFound(err) {
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
if len(svc.Finalizers) > 0 {
|
||||
svc.Finalizers = nil
|
||||
Expect(k8sClient.Update(ctx, svc)).To(Succeed())
|
||||
}
|
||||
err = k8sClient.Delete(ctx, svc)
|
||||
if !errors.IsNotFound(err) {
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
}
|
||||
}
|
||||
})
|
||||
It("should allow deletion", func() {
|
||||
Expect(validator.ValidateDelete(ctx, obj)).Error().NotTo(HaveOccurred())
|
||||
})
|
||||
})
|
||||
|
||||
When("A service is exposed", func() {
|
||||
BeforeEach(func() {
|
||||
obj.Name = "maw"
|
||||
obj.Namespace = "default"
|
||||
svc := &corev1.Service{
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
Name: "maw",
|
||||
Namespace: "default",
|
||||
Annotations: map[string]string{
|
||||
"netbird.io/expose": "true",
|
||||
},
|
||||
},
|
||||
Spec: corev1.ServiceSpec{
|
||||
Ports: []corev1.ServicePort{
|
||||
{
|
||||
Protocol: corev1.ProtocolTCP,
|
||||
Port: 80,
|
||||
TargetPort: intstr.FromInt32(80),
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
Expect(k8sClient.Create(ctx, svc)).To(Succeed())
|
||||
})
|
||||
AfterEach(func() {
|
||||
svc := &corev1.Service{}
|
||||
err := k8sClient.Get(ctx, types.NamespacedName{Namespace: "default", Name: "maw"}, svc)
|
||||
if !errors.IsNotFound(err) {
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
if len(svc.Finalizers) > 0 {
|
||||
svc.Finalizers = nil
|
||||
Expect(k8sClient.Update(ctx, svc)).To(Succeed())
|
||||
}
|
||||
err = k8sClient.Delete(ctx, svc)
|
||||
if !errors.IsNotFound(err) {
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
}
|
||||
}
|
||||
})
|
||||
It("should deny deletion", func() {
|
||||
Expect(validator.ValidateDelete(ctx, obj)).Error().To(HaveOccurred())
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -1,76 +0,0 @@
|
||||
package v1
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
ctrl "sigs.k8s.io/controller-runtime"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
logf "sigs.k8s.io/controller-runtime/pkg/log"
|
||||
"sigs.k8s.io/controller-runtime/pkg/webhook"
|
||||
"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
|
||||
|
||||
netbirdiov1 "github.com/netbirdio/kubernetes-operator/api/v1"
|
||||
)
|
||||
|
||||
// nolint:unused
|
||||
// log is for logging in this package.
|
||||
var nbroutingpeerlog = logf.Log.WithName("nbroutingpeer-resource")
|
||||
|
||||
// SetupNBRoutingPeerWebhookWithManager registers the webhook for NBRoutingPeer in the manager.
|
||||
func SetupNBRoutingPeerWebhookWithManager(mgr ctrl.Manager) error {
|
||||
return ctrl.NewWebhookManagedBy(mgr).For(&netbirdiov1.NBRoutingPeer{}).
|
||||
WithValidator(&NBRoutingPeerCustomValidator{client: mgr.GetClient()}).
|
||||
Complete()
|
||||
}
|
||||
|
||||
// NBRoutingPeerCustomValidator struct is responsible for validating the NBRoutingPeer resource
|
||||
// when it is created, updated, or deleted.
|
||||
type NBRoutingPeerCustomValidator struct {
|
||||
client client.Client
|
||||
}
|
||||
|
||||
var _ webhook.CustomValidator = &NBRoutingPeerCustomValidator{}
|
||||
|
||||
// ValidateCreate implements webhook.CustomValidator so a webhook will be registered for the type NBRoutingPeer.
|
||||
func (v *NBRoutingPeerCustomValidator) ValidateCreate(ctx context.Context, obj runtime.Object) (admission.Warnings, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// ValidateUpdate implements webhook.CustomValidator so a webhook will be registered for the type NBRoutingPeer.
|
||||
func (v *NBRoutingPeerCustomValidator) ValidateUpdate(ctx context.Context, oldObj, newObj runtime.Object) (admission.Warnings, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// ValidateDelete implements webhook.CustomValidator so a webhook will be registered for the type NBRoutingPeer.
|
||||
func (v *NBRoutingPeerCustomValidator) ValidateDelete(ctx context.Context, obj runtime.Object) (admission.Warnings, error) {
|
||||
nbroutingpeer, ok := obj.(*netbirdiov1.NBRoutingPeer)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("expected a NBRoutingPeer object but got %T", obj)
|
||||
}
|
||||
nbroutingpeerlog.Info("Validation for NBRoutingPeer upon deletion", "name", nbroutingpeer.GetName())
|
||||
|
||||
if nbroutingpeer.Status.NetworkID == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
var nbResources netbirdiov1.NBResourceList
|
||||
err := v.client.List(ctx, &nbResources)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resourceValidator := &NBResourceCustomValidator{client: v.client}
|
||||
|
||||
for _, r := range nbResources.Items {
|
||||
if r.Spec.NetworkID == *nbroutingpeer.Status.NetworkID {
|
||||
_, err = resourceValidator.ValidateDelete(ctx, &r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
@@ -1,242 +0,0 @@
|
||||
package v1
|
||||
|
||||
import (
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
. "github.com/onsi/gomega"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/apimachinery/pkg/util/intstr"
|
||||
|
||||
netbirdiov1 "github.com/netbirdio/kubernetes-operator/api/v1"
|
||||
"github.com/netbirdio/kubernetes-operator/internal/util"
|
||||
)
|
||||
|
||||
var _ = Describe("NBRoutingPeer Webhook", func() {
|
||||
var (
|
||||
obj *netbirdiov1.NBRoutingPeer
|
||||
oldObj *netbirdiov1.NBRoutingPeer
|
||||
validator NBRoutingPeerCustomValidator
|
||||
)
|
||||
|
||||
BeforeEach(func() {
|
||||
obj = &netbirdiov1.NBRoutingPeer{}
|
||||
oldObj = &netbirdiov1.NBRoutingPeer{}
|
||||
validator = NBRoutingPeerCustomValidator{
|
||||
client: k8sClient,
|
||||
}
|
||||
})
|
||||
|
||||
Context("When creating or updating NBRoutingPeer under Validating Webhook", func() {
|
||||
It("should allow creation", func() {
|
||||
Expect(validator.ValidateCreate(ctx, obj)).Error().NotTo(HaveOccurred())
|
||||
})
|
||||
It("should allow update", func() {
|
||||
Expect(validator.ValidateUpdate(ctx, oldObj, obj)).Error().NotTo(HaveOccurred())
|
||||
})
|
||||
When("No NBResources Exist", func() {
|
||||
It("should allow deletion", func() {
|
||||
Expect(validator.ValidateDelete(ctx, obj)).Error().NotTo(HaveOccurred())
|
||||
})
|
||||
})
|
||||
When("Deleteable NBResources Exist", func() {
|
||||
BeforeEach(func() {
|
||||
nbResource := &netbirdiov1.NBResource{
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
Name: "isexist",
|
||||
Namespace: "default",
|
||||
},
|
||||
Spec: netbirdiov1.NBResourceSpec{
|
||||
Name: "test1",
|
||||
NetworkID: "test2",
|
||||
Address: "test3",
|
||||
Groups: []string{"test"},
|
||||
},
|
||||
}
|
||||
|
||||
Expect(k8sClient.Create(ctx, nbResource)).To(Succeed())
|
||||
|
||||
obj = &netbirdiov1.NBRoutingPeer{
|
||||
Status: netbirdiov1.NBRoutingPeerStatus{
|
||||
NetworkID: util.Ptr("test2"),
|
||||
},
|
||||
}
|
||||
})
|
||||
|
||||
AfterEach(func() {
|
||||
nbResource := &netbirdiov1.NBResource{}
|
||||
err := k8sClient.Get(ctx, types.NamespacedName{Namespace: "default", Name: "isexist"}, nbResource)
|
||||
if !errors.IsNotFound(err) {
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
if len(nbResource.Finalizers) > 0 {
|
||||
nbResource.Finalizers = nil
|
||||
Expect(k8sClient.Update(ctx, nbResource)).To(Succeed())
|
||||
}
|
||||
err = k8sClient.Delete(ctx, nbResource)
|
||||
if !errors.IsNotFound(err) {
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
}
|
||||
}
|
||||
})
|
||||
It("should allow deletion", func() {
|
||||
Expect(validator.ValidateDelete(ctx, obj)).Error().NotTo(HaveOccurred())
|
||||
})
|
||||
})
|
||||
When("Exposed Services for Network Exist", func() {
|
||||
BeforeEach(func() {
|
||||
nbResource := &netbirdiov1.NBResource{
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
Name: "maw",
|
||||
Namespace: "default",
|
||||
},
|
||||
Spec: netbirdiov1.NBResourceSpec{
|
||||
Name: "test1",
|
||||
NetworkID: "test2",
|
||||
Address: "test3",
|
||||
Groups: []string{"test"},
|
||||
},
|
||||
}
|
||||
|
||||
Expect(k8sClient.Create(ctx, nbResource)).To(Succeed())
|
||||
|
||||
svc := &corev1.Service{
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
Name: "maw",
|
||||
Namespace: "default",
|
||||
Annotations: map[string]string{
|
||||
"netbird.io/expose": "true",
|
||||
},
|
||||
},
|
||||
Spec: corev1.ServiceSpec{
|
||||
Ports: []corev1.ServicePort{
|
||||
{
|
||||
Protocol: corev1.ProtocolTCP,
|
||||
Port: 80,
|
||||
TargetPort: intstr.FromInt32(80),
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
Expect(k8sClient.Create(ctx, svc)).To(Succeed())
|
||||
|
||||
obj = &netbirdiov1.NBRoutingPeer{
|
||||
Status: netbirdiov1.NBRoutingPeerStatus{
|
||||
NetworkID: util.Ptr("test2"),
|
||||
},
|
||||
}
|
||||
})
|
||||
|
||||
AfterEach(func() {
|
||||
nbResource := &netbirdiov1.NBResource{}
|
||||
err := k8sClient.Get(ctx, types.NamespacedName{Namespace: "default", Name: "maw"}, nbResource)
|
||||
if !errors.IsNotFound(err) {
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
if len(nbResource.Finalizers) > 0 {
|
||||
nbResource.Finalizers = nil
|
||||
Expect(k8sClient.Update(ctx, nbResource)).To(Succeed())
|
||||
}
|
||||
err = k8sClient.Delete(ctx, nbResource)
|
||||
if !errors.IsNotFound(err) {
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
}
|
||||
}
|
||||
|
||||
svc := &corev1.Service{}
|
||||
err = k8sClient.Get(ctx, types.NamespacedName{Namespace: "default", Name: "maw"}, svc)
|
||||
if !errors.IsNotFound(err) {
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
if len(svc.Finalizers) > 0 {
|
||||
svc.Finalizers = nil
|
||||
Expect(k8sClient.Update(ctx, svc)).To(Succeed())
|
||||
}
|
||||
err = k8sClient.Delete(ctx, svc)
|
||||
if !errors.IsNotFound(err) {
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
}
|
||||
}
|
||||
})
|
||||
It("should deny deletion", func() {
|
||||
Expect(validator.ValidateDelete(ctx, obj)).Error().To(HaveOccurred())
|
||||
})
|
||||
})
|
||||
When("Exposed NBResources do not belong to network", func() {
|
||||
BeforeEach(func() {
|
||||
nbResource := &netbirdiov1.NBResource{
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
Name: "maw",
|
||||
Namespace: "default",
|
||||
},
|
||||
Spec: netbirdiov1.NBResourceSpec{
|
||||
Name: "test1",
|
||||
NetworkID: "test5",
|
||||
Address: "test3",
|
||||
Groups: []string{"test"},
|
||||
},
|
||||
}
|
||||
|
||||
Expect(k8sClient.Create(ctx, nbResource)).To(Succeed())
|
||||
|
||||
svc := &corev1.Service{
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
Name: "maw",
|
||||
Namespace: "default",
|
||||
Annotations: map[string]string{
|
||||
"netbird.io/expose": "true",
|
||||
},
|
||||
},
|
||||
Spec: corev1.ServiceSpec{
|
||||
Ports: []corev1.ServicePort{
|
||||
{
|
||||
Protocol: corev1.ProtocolTCP,
|
||||
Port: 80,
|
||||
TargetPort: intstr.FromInt32(80),
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
Expect(k8sClient.Create(ctx, svc)).To(Succeed())
|
||||
|
||||
obj = &netbirdiov1.NBRoutingPeer{
|
||||
Status: netbirdiov1.NBRoutingPeerStatus{
|
||||
NetworkID: util.Ptr("test2"),
|
||||
},
|
||||
}
|
||||
})
|
||||
|
||||
AfterEach(func() {
|
||||
nbResource := &netbirdiov1.NBResource{}
|
||||
err := k8sClient.Get(ctx, types.NamespacedName{Namespace: "default", Name: "maw"}, nbResource)
|
||||
if !errors.IsNotFound(err) {
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
if len(nbResource.Finalizers) > 0 {
|
||||
nbResource.Finalizers = nil
|
||||
Expect(k8sClient.Update(ctx, nbResource)).To(Succeed())
|
||||
}
|
||||
err = k8sClient.Delete(ctx, nbResource)
|
||||
if !errors.IsNotFound(err) {
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
}
|
||||
}
|
||||
|
||||
svc := &corev1.Service{}
|
||||
err = k8sClient.Get(ctx, types.NamespacedName{Namespace: "default", Name: "maw"}, svc)
|
||||
if !errors.IsNotFound(err) {
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
if len(svc.Finalizers) > 0 {
|
||||
svc.Finalizers = nil
|
||||
Expect(k8sClient.Update(ctx, svc)).To(Succeed())
|
||||
}
|
||||
err = k8sClient.Delete(ctx, svc)
|
||||
if !errors.IsNotFound(err) {
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
}
|
||||
}
|
||||
})
|
||||
It("should allow deletion", func() {
|
||||
Expect(validator.ValidateDelete(ctx, obj)).Error().NotTo(HaveOccurred())
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
})
|
||||
@@ -124,12 +124,6 @@ var _ = BeforeSuite(func() {
|
||||
err = SetupNBSetupKeyWebhookWithManager(mgr)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
err = SetupNBResourceWebhookWithManager(mgr)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
err = SetupNBRoutingPeerWebhookWithManager(mgr)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
err = SetupNBGroupWebhookWithManager(mgr)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
|
||||
Reference in New Issue
Block a user