mirror of
https://github.com/YuzuZensai/netbird-kubernetes-operator.git
synced 2026-09-13 18:59:09 +00:00
This change adds two new resources, NetworkRouter and NetworkResource, which enable clusters to expose Kubernetes services to Netbird. The NetworkRouter is responsible for creating the network, group, setup key and routing peer all of which are unique to the isntance. Along with the deployment of the client in the cluster. The NetworkResource exposes a service by linking to the specific router it wants to expose to. This makes coupling between the resource and network easy to understand. Routers also set a DNS zone which is used to give names to resources based on the name and namespace of the service being exposed. Part of #172 Signed-off-by: Philip Laine <philip.laine@gmail.com>
194 lines
5.4 KiB
Go
194 lines
5.4 KiB
Go
package controller
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"time"
|
|
|
|
"github.com/fluxcd/pkg/runtime/conditions"
|
|
"github.com/fluxcd/pkg/runtime/patch"
|
|
netbird "github.com/netbirdio/netbird/shared/management/client/rest"
|
|
"github.com/netbirdio/netbird/shared/management/http/api"
|
|
corev1 "k8s.io/api/core/v1"
|
|
kerrors "k8s.io/apimachinery/pkg/api/errors"
|
|
corev1ac "k8s.io/client-go/applyconfigurations/core/v1"
|
|
"k8s.io/utils/ptr"
|
|
ctrl "sigs.k8s.io/controller-runtime"
|
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
|
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
|
|
|
|
nbv1alpha1 "github.com/netbirdio/kubernetes-operator/api/v1alpha1"
|
|
"github.com/netbirdio/kubernetes-operator/internal/netbirdutil"
|
|
"github.com/netbirdio/kubernetes-operator/internal/ssautil"
|
|
)
|
|
|
|
const (
|
|
SetupKeySecretKey = "setup-key"
|
|
)
|
|
|
|
type SetupKeyReconciler struct {
|
|
client.Client
|
|
|
|
Netbird *netbird.Client
|
|
}
|
|
|
|
// +kubebuilder:rbac:groups=netbird.io,resources=setupkeys,verbs=get;list;watch;create;update;patch;delete
|
|
// +kubebuilder:rbac:groups=netbird.io,resources=setupkeys/status,verbs=get;update;patch
|
|
// +kubebuilder:rbac:groups=netbird.io,resources=setupkeys/finalizers,verbs=update
|
|
func (r *SetupKeyReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
|
|
setupKey := &nbv1alpha1.SetupKey{}
|
|
err := r.Get(ctx, req.NamespacedName, setupKey)
|
|
if err != nil {
|
|
return ctrl.Result{}, client.IgnoreNotFound(err)
|
|
}
|
|
owner, err := ssautil.OwnerReference(setupKey, r.Client.Scheme())
|
|
if err != nil {
|
|
return ctrl.Result{}, err
|
|
}
|
|
sp := patch.NewSerialPatcher(setupKey, r.Client)
|
|
|
|
if !setupKey.DeletionTimestamp.IsZero() {
|
|
return r.reconcileDelete(ctx, sp, setupKey)
|
|
}
|
|
|
|
autoGroupIDs, err := netbirdutil.GetGroupIDs(ctx, r.Client, r.Netbird, setupKey.Spec.AutoGroups, setupKey.Namespace)
|
|
if err != nil {
|
|
return ctrl.Result{}, err
|
|
}
|
|
|
|
controllerutil.AddFinalizer(setupKey, nbv1alpha1.NetbirdFinalizer)
|
|
err = sp.Patch(ctx, setupKey)
|
|
if err != nil {
|
|
return ctrl.Result{}, err
|
|
}
|
|
|
|
// Check if setup key is up to date.
|
|
ok, err := func() (bool, error) {
|
|
if setupKey.Status.SetupKeyID == "" {
|
|
return false, nil
|
|
}
|
|
|
|
// Check setup key in Netbird.
|
|
resp, err := r.Netbird.SetupKeys.Get(ctx, setupKey.Status.SetupKeyID)
|
|
if netbird.IsNotFound(err) {
|
|
return false, nil
|
|
}
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
|
|
switch resp.State {
|
|
case "valid":
|
|
case "overused":
|
|
return false, errors.New("setup key is overused")
|
|
default:
|
|
return false, nil
|
|
}
|
|
|
|
// Secret exists and has not been modified.
|
|
secret := &corev1.Secret{}
|
|
err = r.Client.Get(ctx, client.ObjectKey{Name: setupKey.SecretName(), Namespace: req.Namespace}, secret)
|
|
if kerrors.IsNotFound(err) {
|
|
return false, nil
|
|
}
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
if resp.Key[:5] != string(secret.Data[SetupKeySecretKey])[:5] {
|
|
return false, nil
|
|
}
|
|
|
|
// Auto groups have not been changed.
|
|
setupKeyReq := api.PutApiSetupKeysKeyIdJSONRequestBody{
|
|
AutoGroups: autoGroupIDs,
|
|
}
|
|
_, err = r.Netbird.SetupKeys.Update(ctx, setupKey.Status.SetupKeyID, setupKeyReq)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
|
|
return true, nil
|
|
}()
|
|
if err != nil {
|
|
return ctrl.Result{}, err
|
|
}
|
|
if ok {
|
|
return ctrl.Result{RequeueAfter: 15 * time.Minute}, nil
|
|
}
|
|
oldSetupKeyID := setupKey.Status.SetupKeyID
|
|
|
|
// Setup key does not exist so we create one.
|
|
expiresIn := 0
|
|
if setupKey.Spec.Duration != nil {
|
|
expiresIn = int(setupKey.Spec.Duration.Seconds())
|
|
}
|
|
setupKeyReq := api.PostApiSetupKeysJSONRequestBody{
|
|
AllowExtraDnsLabels: ptr.To(false),
|
|
AutoGroups: autoGroupIDs,
|
|
Ephemeral: ptr.To(setupKey.Spec.Ephemeral),
|
|
ExpiresIn: expiresIn,
|
|
Name: setupKey.Spec.Name,
|
|
Type: "reusable",
|
|
UsageLimit: 0,
|
|
}
|
|
resp, err := r.Netbird.SetupKeys.Create(ctx, setupKeyReq)
|
|
if err != nil {
|
|
return ctrl.Result{}, err
|
|
}
|
|
setupKey.Status.SetupKeyID = resp.Id
|
|
err = sp.Patch(ctx, setupKey)
|
|
if err != nil {
|
|
return ctrl.Result{}, err
|
|
}
|
|
|
|
// Create the secret containing the key.
|
|
data := map[string]string{
|
|
SetupKeySecretKey: resp.Key,
|
|
}
|
|
secret := corev1ac.Secret(setupKey.SecretName(), req.Namespace).
|
|
WithStringData(data).
|
|
WithOwnerReferences(owner)
|
|
err = r.Client.Apply(ctx, secret)
|
|
if err != nil {
|
|
return ctrl.Result{}, err
|
|
}
|
|
|
|
// Delete the old status key if we are recreating.
|
|
if oldSetupKeyID != "" {
|
|
err = r.Netbird.SetupKeys.Delete(ctx, oldSetupKeyID)
|
|
if err != nil && !netbird.IsNotFound(err) {
|
|
return ctrl.Result{}, err
|
|
}
|
|
}
|
|
|
|
conditions.MarkTrue(setupKey, nbv1alpha1.ReadyCondition, nbv1alpha1.ReconciledReason, "")
|
|
err = sp.Patch(ctx, setupKey, patch.WithStatusObservedGeneration{})
|
|
if err != nil {
|
|
return ctrl.Result{}, err
|
|
}
|
|
return ctrl.Result{RequeueAfter: 15 * time.Minute}, nil
|
|
}
|
|
|
|
func (r *SetupKeyReconciler) reconcileDelete(ctx context.Context, sp *patch.SerialPatcher, setupKey *nbv1alpha1.SetupKey) (ctrl.Result, error) {
|
|
if setupKey.Status.SetupKeyID != "" {
|
|
err := r.Netbird.SetupKeys.Delete(ctx, setupKey.Status.SetupKeyID)
|
|
if err != nil && !netbird.IsNotFound(err) {
|
|
return ctrl.Result{}, err
|
|
}
|
|
}
|
|
|
|
controllerutil.RemoveFinalizer(setupKey, nbv1alpha1.NetbirdFinalizer)
|
|
err := sp.Patch(ctx, setupKey)
|
|
if err != nil {
|
|
return ctrl.Result{}, err
|
|
}
|
|
return ctrl.Result{}, nil
|
|
}
|
|
|
|
func (r *SetupKeyReconciler) SetupWithManager(mgr ctrl.Manager) error {
|
|
return ctrl.NewControllerManagedBy(mgr).
|
|
For(&nbv1alpha1.SetupKey{}).
|
|
Owns(&corev1.Secret{}).
|
|
Complete(r)
|
|
}
|