mirror of
https://github.com/YuzuZensai/netbird-kubernetes-operator.git
synced 2026-09-13 10:49:15 +00:00
Add ingress feature to controller (#5)
Co-authored-by: Maycon Santos <mlsmaycon@gmail.com>
This commit is contained in:
co-authored by
Maycon Santos
parent
cea60745d2
commit
166091b8e0
@@ -0,0 +1,82 @@
|
||||
package v1
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"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"
|
||||
netbird "github.com/netbirdio/netbird/management/client/rest"
|
||||
)
|
||||
|
||||
// nolint:unused
|
||||
// log is for logging in this package.
|
||||
var nbgrouplog = logf.Log.WithName("nbgroup-resource")
|
||||
|
||||
// SetupNBGroupWebhookWithManager registers the webhook for NBGroup in the manager.
|
||||
func SetupNBGroupWebhookWithManager(mgr ctrl.Manager, managementURL, apiKey string) error {
|
||||
return ctrl.NewWebhookManagedBy(mgr).For(&netbirdiov1.NBGroup{}).
|
||||
WithValidator(&NBGroupCustomValidator{netbird: netbird.New(managementURL, apiKey), client: mgr.GetClient()}).
|
||||
Complete()
|
||||
}
|
||||
|
||||
// NBGroupCustomValidator struct is responsible for validating the NBGroup resource
|
||||
// when it is created, updated, or deleted.
|
||||
type NBGroupCustomValidator struct {
|
||||
netbird *netbird.Client
|
||||
client client.Client
|
||||
}
|
||||
|
||||
var _ webhook.CustomValidator = &NBGroupCustomValidator{}
|
||||
|
||||
// ValidateCreate implements webhook.CustomValidator so a webhook will be registered for the type NBGroup.
|
||||
func (v *NBGroupCustomValidator) 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 NBGroup.
|
||||
func (v *NBGroupCustomValidator) 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 NBGroup.
|
||||
func (v *NBGroupCustomValidator) ValidateDelete(ctx context.Context, obj runtime.Object) (admission.Warnings, error) {
|
||||
nbgroup, ok := obj.(*netbirdiov1.NBGroup)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("expected a NBGroup object but got %T", obj)
|
||||
}
|
||||
nbgrouplog.Info("Validation for NBGroup upon deletion", "name", nbgroup.GetName())
|
||||
|
||||
for _, o := range nbgroup.OwnerReferences {
|
||||
if o.Kind == (&netbirdiov1.NBResource{}).Kind {
|
||||
var nbResource netbirdiov1.NBResource
|
||||
err := v.client.Get(ctx, types.NamespacedName{Namespace: nbgroup.Namespace, Name: o.Name}, &nbResource)
|
||||
if err != nil && !errors.IsNotFound(err) {
|
||||
return nil, err
|
||||
}
|
||||
if err == nil && nbResource.DeletionTimestamp == nil {
|
||||
return nil, fmt.Errorf("group attached to NBResource %s/%s", nbgroup.Namespace, o.Name)
|
||||
}
|
||||
}
|
||||
if o.Kind == (&netbirdiov1.NBRoutingPeer{}).Kind {
|
||||
var nbResource netbirdiov1.NBRoutingPeer
|
||||
err := v.client.Get(ctx, types.NamespacedName{Namespace: nbgroup.Namespace, Name: o.Name}, &nbResource)
|
||||
if err != nil && !errors.IsNotFound(err) {
|
||||
return nil, err
|
||||
}
|
||||
if err == nil && nbResource.DeletionTimestamp == nil {
|
||||
return nil, fmt.Errorf("group attached to NBRoutingPeer %s/%s", nbgroup.Namespace, o.Name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
package v1
|
||||
|
||||
import (
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
. "github.com/onsi/gomega"
|
||||
|
||||
netbirdiov1 "github.com/netbirdio/kubernetes-operator/api/v1"
|
||||
// TODO (user): Add any additional imports if needed
|
||||
)
|
||||
|
||||
var _ = Describe("NBGroup Webhook", func() {
|
||||
var (
|
||||
obj *netbirdiov1.NBGroup
|
||||
oldObj *netbirdiov1.NBGroup
|
||||
validator NBGroupCustomValidator
|
||||
)
|
||||
|
||||
BeforeEach(func() {
|
||||
Skip("Not implemented yet")
|
||||
obj = &netbirdiov1.NBGroup{}
|
||||
oldObj = &netbirdiov1.NBGroup{}
|
||||
validator = NBGroupCustomValidator{}
|
||||
Expect(validator).NotTo(BeNil(), "Expected validator to be initialized")
|
||||
Expect(oldObj).NotTo(BeNil(), "Expected oldObj to be initialized")
|
||||
Expect(obj).NotTo(BeNil(), "Expected obj to be initialized")
|
||||
// TODO (user): Add any setup logic common to all tests
|
||||
})
|
||||
|
||||
AfterEach(func() {
|
||||
// TODO (user): Add any teardown logic common to all tests
|
||||
})
|
||||
|
||||
Context("When creating or updating NBGroup under Validating Webhook", func() {
|
||||
// TODO (user): Add logic for validating webhooks
|
||||
// Example:
|
||||
// It("Should deny creation if a required field is missing", func() {
|
||||
// By("simulating an invalid creation scenario")
|
||||
// obj.SomeRequiredField = ""
|
||||
// Expect(validator.ValidateCreate(ctx, obj)).Error().To(HaveOccurred())
|
||||
// })
|
||||
//
|
||||
// It("Should admit creation if all required fields are present", func() {
|
||||
// By("simulating an invalid creation scenario")
|
||||
// obj.SomeRequiredField = "valid_value"
|
||||
// Expect(validator.ValidateCreate(ctx, obj)).To(BeNil())
|
||||
// })
|
||||
//
|
||||
// It("Should validate updates correctly", func() {
|
||||
// By("simulating a valid update scenario")
|
||||
// oldObj.SomeRequiredField = "updated_value"
|
||||
// obj.SomeRequiredField = "updated_value"
|
||||
// Expect(validator.ValidateUpdate(ctx, oldObj, obj)).To(BeNil())
|
||||
// })
|
||||
})
|
||||
|
||||
})
|
||||
@@ -0,0 +1,70 @@
|
||||
package v1
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"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"
|
||||
netbird "github.com/netbirdio/netbird/management/client/rest"
|
||||
)
|
||||
|
||||
// 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, managementURL, apiKey string) error {
|
||||
return ctrl.NewWebhookManagedBy(mgr).For(&netbirdiov1.NBResource{}).
|
||||
WithValidator(&NBResourceCustomValidator{netbird: netbird.New(managementURL, apiKey), client: mgr.GetClient()}).
|
||||
Complete()
|
||||
}
|
||||
|
||||
// NBResourceCustomValidator struct is responsible for validating the NBResource resource
|
||||
// when it is created, updated, or deleted.
|
||||
type NBResourceCustomValidator struct {
|
||||
netbird *netbird.Client
|
||||
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 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
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
package v1
|
||||
|
||||
import (
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
. "github.com/onsi/gomega"
|
||||
|
||||
netbirdiov1 "github.com/netbirdio/kubernetes-operator/api/v1"
|
||||
// TODO (user): Add any additional imports if needed
|
||||
)
|
||||
|
||||
var _ = Describe("NBResource Webhook", func() {
|
||||
var (
|
||||
obj *netbirdiov1.NBResource
|
||||
oldObj *netbirdiov1.NBResource
|
||||
validator NBResourceCustomValidator
|
||||
)
|
||||
|
||||
BeforeEach(func() {
|
||||
Skip("Not implemented yet")
|
||||
obj = &netbirdiov1.NBResource{}
|
||||
oldObj = &netbirdiov1.NBResource{}
|
||||
validator = NBResourceCustomValidator{}
|
||||
Expect(validator).NotTo(BeNil(), "Expected validator to be initialized")
|
||||
Expect(oldObj).NotTo(BeNil(), "Expected oldObj to be initialized")
|
||||
Expect(obj).NotTo(BeNil(), "Expected obj to be initialized")
|
||||
// TODO (user): Add any setup logic common to all tests
|
||||
})
|
||||
|
||||
AfterEach(func() {
|
||||
// TODO (user): Add any teardown logic common to all tests
|
||||
})
|
||||
|
||||
Context("When creating or updating NBResource under Validating Webhook", func() {
|
||||
// TODO (user): Add logic for validating webhooks
|
||||
// Example:
|
||||
// It("Should deny creation if a required field is missing", func() {
|
||||
// By("simulating an invalid creation scenario")
|
||||
// obj.SomeRequiredField = ""
|
||||
// Expect(validator.ValidateCreate(ctx, obj)).Error().To(HaveOccurred())
|
||||
// })
|
||||
//
|
||||
// It("Should admit creation if all required fields are present", func() {
|
||||
// By("simulating an invalid creation scenario")
|
||||
// obj.SomeRequiredField = "valid_value"
|
||||
// Expect(validator.ValidateCreate(ctx, obj)).To(BeNil())
|
||||
// })
|
||||
//
|
||||
// It("Should validate updates correctly", func() {
|
||||
// By("simulating a valid update scenario")
|
||||
// oldObj.SomeRequiredField = "updated_value"
|
||||
// obj.SomeRequiredField = "updated_value"
|
||||
// Expect(validator.ValidateUpdate(ctx, oldObj, obj)).To(BeNil())
|
||||
// })
|
||||
})
|
||||
|
||||
})
|
||||
@@ -0,0 +1,76 @@
|
||||
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"
|
||||
netbird "github.com/netbirdio/netbird/management/client/rest"
|
||||
)
|
||||
|
||||
// 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, managementURL, apiKey string) error {
|
||||
return ctrl.NewWebhookManagedBy(mgr).For(&netbirdiov1.NBRoutingPeer{}).
|
||||
WithValidator(&NBRoutingPeerCustomValidator{netbird: netbird.New(managementURL, apiKey), client: mgr.GetClient()}).
|
||||
Complete()
|
||||
}
|
||||
|
||||
// NBRoutingPeerCustomValidator struct is responsible for validating the NBRoutingPeer resource
|
||||
// when it is created, updated, or deleted.
|
||||
type NBRoutingPeerCustomValidator struct {
|
||||
netbird *netbird.Client
|
||||
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
|
||||
}
|
||||
|
||||
for _, r := range nbResources.Items {
|
||||
if r.Spec.NetworkID == *nbroutingpeer.Status.NetworkID {
|
||||
err = v.client.Delete(ctx, &r, client.DryRunAll)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%s/%s: %w", r.Namespace, r.Name, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
package v1
|
||||
|
||||
import (
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
. "github.com/onsi/gomega"
|
||||
|
||||
netbirdiov1 "github.com/netbirdio/kubernetes-operator/api/v1"
|
||||
// TODO (user): Add any additional imports if needed
|
||||
)
|
||||
|
||||
var _ = Describe("NBRoutingPeer Webhook", func() {
|
||||
var (
|
||||
obj *netbirdiov1.NBRoutingPeer
|
||||
oldObj *netbirdiov1.NBRoutingPeer
|
||||
validator NBRoutingPeerCustomValidator
|
||||
)
|
||||
|
||||
BeforeEach(func() {
|
||||
Skip("Not implemented yet")
|
||||
obj = &netbirdiov1.NBRoutingPeer{}
|
||||
oldObj = &netbirdiov1.NBRoutingPeer{}
|
||||
validator = NBRoutingPeerCustomValidator{}
|
||||
Expect(validator).NotTo(BeNil(), "Expected validator to be initialized")
|
||||
Expect(oldObj).NotTo(BeNil(), "Expected oldObj to be initialized")
|
||||
Expect(obj).NotTo(BeNil(), "Expected obj to be initialized")
|
||||
// TODO (user): Add any setup logic common to all tests
|
||||
})
|
||||
|
||||
AfterEach(func() {
|
||||
// TODO (user): Add any teardown logic common to all tests
|
||||
})
|
||||
|
||||
Context("When creating or updating NBRoutingPeer under Validating Webhook", func() {
|
||||
// TODO (user): Add logic for validating webhooks
|
||||
// Example:
|
||||
// It("Should deny creation if a required field is missing", func() {
|
||||
// By("simulating an invalid creation scenario")
|
||||
// obj.SomeRequiredField = ""
|
||||
// Expect(validator.ValidateCreate(ctx, obj)).Error().To(HaveOccurred())
|
||||
// })
|
||||
//
|
||||
// It("Should admit creation if all required fields are present", func() {
|
||||
// By("simulating an invalid creation scenario")
|
||||
// obj.SomeRequiredField = "valid_value"
|
||||
// Expect(validator.ValidateCreate(ctx, obj)).To(BeNil())
|
||||
// })
|
||||
//
|
||||
// It("Should validate updates correctly", func() {
|
||||
// By("simulating a valid update scenario")
|
||||
// oldObj.SomeRequiredField = "updated_value"
|
||||
// obj.SomeRequiredField = "updated_value"
|
||||
// Expect(validator.ValidateUpdate(ctx, oldObj, obj)).To(BeNil())
|
||||
// })
|
||||
})
|
||||
|
||||
})
|
||||
@@ -83,7 +83,7 @@ func (d *PodNetbirdInjector) Default(ctx context.Context, obj runtime.Object) er
|
||||
// ensure the NBSetupKey is ready.
|
||||
ready := false
|
||||
for _, c := range nbSetupKey.Status.Conditions {
|
||||
if c.Type == netbirdiov1.Ready {
|
||||
if c.Type == netbirdiov1.NBSetupKeyReady {
|
||||
ready = c.Status == corev1.ConditionTrue
|
||||
}
|
||||
}
|
||||
|
||||
@@ -108,9 +108,9 @@ var _ = Describe("Pod Webhook", func() {
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
sk.Status = netbirdiov1.NBSetupKeyStatus{
|
||||
Conditions: []netbirdiov1.NBSetupKeyCondition{
|
||||
Conditions: []netbirdiov1.NBCondition{
|
||||
{
|
||||
Type: netbirdiov1.Ready,
|
||||
Type: netbirdiov1.NBSetupKeyReady,
|
||||
Status: corev1.ConditionTrue,
|
||||
},
|
||||
},
|
||||
|
||||
@@ -124,6 +124,15 @@ 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())
|
||||
|
||||
// +kubebuilder:scaffold:webhook
|
||||
|
||||
go func() {
|
||||
|
||||
Reference in New Issue
Block a user