Files
open5gs-legacy-operator/internal/controller/open5gsuser_controller.go
jpontongradiant 2c9cd54da0 first commit
2024-11-14 15:34:57 +01:00

252 lines
11 KiB
Go

package controller
import (
"context"
"fmt"
netv1 "gradiant/open5gs-legacy-operator/api/v1"
"log"
"strings"
"time"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/runtime"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
)
// Core resources
//+kubebuilder:rbac:groups="",resources=secrets,verbs=get;list;watch;create;update;patch;delete
//+kubebuilder:rbac:groups="",resources=configmaps,verbs=get;list;watch;create;update;patch;delete
//+kubebuilder:rbac:groups="",resources=services,verbs=get;list;watch;create;update;patch;delete
//+kubebuilder:rbac:groups="",resources=serviceaccounts,verbs=get;list;watch;create;update;patch;delete
//+kubebuilder:rbac:groups="",resources=persistentvolumeclaims,verbs=get;list;watch;create;update;patch;delete
//+kubebuilder:rbac:groups="",resources=pods,verbs=get;list;watch;create;update;patch;delete
//+kubebuilder:rbac:groups="",resources=events,verbs=get;list;watch;create;update;patch
//+kubebuilder:rbac:groups="",resources=namespaces,verbs=get;list;watch
// net group resources
//+kubebuilder:rbac:groups=net.gradiant.org,resources=open5gs,verbs=get;list;watch;create;update;patch;delete
//+kubebuilder:rbac:groups=net.gradiant.org,resources=open5gs/status,verbs=get;update;patch
//+kubebuilder:rbac:groups=net.gradiant.org,resources=open5gs/finalizers,verbs=update
//+kubebuilder:rbac:groups=net.gradiant.org,resources=open5gsusers,verbs=get;list;watch;create;update;patch;delete
// Apps resources
//+kubebuilder:rbac:groups=apps,resources=deployments,verbs=get;list;watch;create;update;patch;delete
//+kubebuilder:rbac:groups=apps,resources=statefulsets,verbs=get;list;watch;create;update;patch;delete
// Batch resources
//+kubebuilder:rbac:groups=batch,resources=jobs,verbs=get;list;watch;create;update;patch;delete
//+kubebuilder:rbac:groups=batch,resources=cronjobs,verbs=get;list;watch;create;update;patch;delete
// RBAC resources
//+kubebuilder:rbac:groups=rbac.authorization.k8s.io,resources=roles,verbs=get;list;watch;create;update;patch;delete
//+kubebuilder:rbac:groups=rbac.authorization.k8s.io,resources=rolebindings,verbs=get;list;watch;create;update;patch;delete
// Autoscaling resources
//+kubebuilder:rbac:groups=autoscaling,resources=horizontalpodautoscalers,verbs=get;list;watch;create;update;patch;delete
// Policy resources
//+kubebuilder:rbac:groups=policy,resources=poddisruptionbudgets,verbs=get;list;watch;create;update;patch;delete
// Network resources
//+kubebuilder:rbac:groups=networking.k8s.io,resources=ingresses,verbs=get;list;watch;create;update;patch;delete
//+kubebuilder:rbac:groups=networking.k8s.io,resources=networkpolicies,verbs=get;list;watch;create;update;patch;delete
// Monitoring resources
//+kubebuilder:rbac:groups=monitoring.coreos.com,resources=servicemonitors,verbs=get;list;watch;create;update;patch;delete
// Helm-specific resources
//+kubebuilder:rbac:groups=helm.toolkit.fluxcd.io,resources=helmreleases,verbs=get;list;watch;create;update;patch;delete
const (
Open5GSUserFinalizer = "finalizer.open5gsuser.net.gradiant.org/user"
lastAppliedConfig = "kubectl.kubernetes.io/last-applied-configuration"
)
type Open5GSUserReconciler struct {
client.Client
Scheme *runtime.Scheme
}
func (r *Open5GSUserReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
var Open5GSUser netv1.Open5GSUser
var ipService string
var err error
if err := r.Get(ctx, req.NamespacedName, &Open5GSUser); err != nil {
if errors.IsNotFound(err) {
return ctrl.Result{}, nil
}
log.Printf("Error retrieving Open5GSUser %s: %v", req.NamespacedName, err)
return ctrl.Result{}, err
}
serviceName := fmt.Sprintf("%s-mongodb", strings.ToLower(Open5GSUser.Spec.Open5GS.Name))
if Open5GSUser.Spec.Open5GS.Namespace == "" {
ipService, err = r.GetServiceIp(ctx, serviceName, Open5GSUser.Namespace)
} else {
ipService, err = r.GetServiceIp(ctx, serviceName, Open5GSUser.Spec.Open5GS.Namespace)
}
if err != nil {
log.Printf("Failed to get IP for service %s: %v", serviceName, err)
return ctrl.Result{}, err
}
mongoURI := "mongodb://" + ipService + ":27017"
if Open5GSUser.ObjectMeta.DeletionTimestamp.IsZero() {
// Resource is not being deleted
if !containsString(Open5GSUser.ObjectMeta.Finalizers, Open5GSUserFinalizer) {
log.Printf("Creating subscriber IMSI: %s", Open5GSUser.Spec.IMSI)
if Open5GSUser.Spec.SST != "" || Open5GSUser.Spec.SD != "" {
// SST or SD specified, call function handling slice.
if err := addSubscriberWithSlice(Open5GSUser, mongoURI); err != nil {
log.Printf("Failed to create subscriber with slice %s", err)
return ctrl.Result{}, err
}
log.Printf("Subscriber with slice created successfully IMSI: %s", Open5GSUser.Spec.IMSI)
Open5GSUser.ObjectMeta.Finalizers = append(Open5GSUser.ObjectMeta.Finalizers, Open5GSUserFinalizer)
if err := r.Update(ctx, &Open5GSUser); err != nil {
return ctrl.Result{}, err
}
} else if Open5GSUser.Spec.APN != "" {
// APN specified without SST and SD, use function for default APN.
if err := addSubscriberWithAPN(Open5GSUser, mongoURI); err != nil {
log.Printf("Failed to create subscriber with APN %s", err)
return ctrl.Result{}, err
}
log.Printf("Subscriber with APN created successfully IMSI: %s", Open5GSUser.Spec.IMSI)
Open5GSUser.ObjectMeta.Finalizers = append(Open5GSUser.ObjectMeta.Finalizers, Open5GSUserFinalizer)
if err := r.Update(ctx, &Open5GSUser); err != nil {
return ctrl.Result{}, err
}
} else {
// If SST, SD, and APN not specified but Key and OPC are, use defaults.
if Open5GSUser.Spec.Key != "" && Open5GSUser.Spec.OPC != "" {
if err := addSubscriberWithDefaults(Open5GSUser, mongoURI); err != nil {
log.Printf("Failed to create subscriber with defaults %s", err)
return ctrl.Result{}, err
}
log.Printf("Subscriber with defaults created successfully IMSI: %s", Open5GSUser.Spec.IMSI)
Open5GSUser.ObjectMeta.Finalizers = append(Open5GSUser.ObjectMeta.Finalizers, Open5GSUserFinalizer)
if err := r.Update(ctx, &Open5GSUser); err != nil {
return ctrl.Result{}, err
}
} else {
// Handle the case where necessary data is not provided.
log.Printf("Cannot create subscriber, missing key data IMSI: %s", Open5GSUser.Spec.IMSI)
return ctrl.Result{}, fmt.Errorf("insufficient data provided for subscriber creation")
}
}
} else {
// Resource already has the finalizer
var subscriber bson.M
clientOptions := options.Client().ApplyURI(mongoURI)
client, err := mongo.Connect(ctx, clientOptions)
if err != nil {
return ctrl.Result{}, fmt.Errorf("failed to connect to mongo: %v", err)
}
defer client.Disconnect(ctx)
collection := client.Database("open5gs").Collection("subscribers")
err = collection.FindOne(ctx, bson.M{"imsi": Open5GSUser.Spec.IMSI}).Decode(&subscriber)
if err != nil {
if err == mongo.ErrNoDocuments {
log.Printf("Subscriber %s not found in database, creating it", Open5GSUser.Spec.IMSI)
if Open5GSUser.Spec.SST != "" || Open5GSUser.Spec.SD != "" {
// SST or SD specified, call function handling slice.
if err := addSubscriberWithSlice(Open5GSUser, mongoURI); err != nil {
log.Printf("Failed to create subscriber with slice %s", err)
return ctrl.Result{}, err
}
log.Printf("Subscriber with slice created successfully IMSI: %s", Open5GSUser.Spec.IMSI)
} else if Open5GSUser.Spec.APN != "" {
// APN specified without SST and SD, use function for default APN.
if err := addSubscriberWithAPN(Open5GSUser, mongoURI); err != nil {
log.Printf("Failed to create subscriber with APN %s", err)
return ctrl.Result{}, err
}
log.Printf("Subscriber with APN created successfully IMSI: %s", Open5GSUser.Spec.IMSI)
} else {
// If SST, SD, and APN not specified but Key and OPC are, use defaults.
if Open5GSUser.Spec.Key != "" && Open5GSUser.Spec.OPC != "" {
if err := addSubscriberWithDefaults(Open5GSUser, mongoURI); err != nil {
log.Printf("Failed to create subscriber with defaults %s", err)
return ctrl.Result{}, err
}
log.Printf("Subscriber with defaults created successfully IMSI: %s", Open5GSUser.Spec.IMSI)
} else {
// Handle the case where necessary data is not provided.
log.Printf("Cannot create subscriber, missing key data IMSI: %s", Open5GSUser.Spec.IMSI)
return ctrl.Result{}, fmt.Errorf("insufficient data provided for subscriber creation")
}
}
} else {
log.Printf("Failed to find subscriber %s in database: %v", Open5GSUser.Spec.IMSI, err)
return ctrl.Result{}, err
}
} else {
if hasDrift(Open5GSUser, subscriber) {
log.Printf("Change detected for subscriber %s, updating it", Open5GSUser.Spec.IMSI)
if err := updateSubscriber(Open5GSUser, mongoURI); err != nil {
log.Printf("Failed to correct drift for subscriber %s: %v", Open5GSUser.Spec.IMSI, err)
return ctrl.Result{}, err
}
}
}
}
} else {
if containsString(Open5GSUser.ObjectMeta.Finalizers, Open5GSUserFinalizer) {
log.Printf("Deleting subscriber for Open5GSUser %s", Open5GSUser.Name)
if err := deleteSubscriber(Open5GSUser, mongoURI); err != nil {
log.Printf("Failed to delete subscriber for Open5GSUser %s: %v", Open5GSUser.Name, err)
return ctrl.Result{}, err
}
Open5GSUser.ObjectMeta.Finalizers = removeString(Open5GSUser.ObjectMeta.Finalizers, Open5GSUserFinalizer)
if err := r.Update(ctx, &Open5GSUser); err != nil {
log.Printf("Failed to remove finalizer for Open5GSUser %s: %v", Open5GSUser.Name, err)
return ctrl.Result{}, err
}
}
}
return ctrl.Result{RequeueAfter: 15 * time.Second}, nil // Requeue every 15 seconds
}
func (r *Open5GSUserReconciler) GetServiceIp(ctx context.Context, serviceName string, namespace string) (string, error) {
var service corev1.Service
namespacedName := client.ObjectKey{Name: serviceName, Namespace: namespace}
if err := r.Get(ctx, namespacedName, &service); err != nil {
log.Printf("Failed to get service %s: %v", serviceName, err)
return "", err
}
return service.Spec.ClusterIP, nil
}
func containsString(slice []string, s string) bool {
for _, item := range slice {
if item == s {
return true
}
}
return false
}
func removeString(slice []string, s string) (result []string) {
for _, item := range slice {
if item != s {
result = append(result, item)
}
}
return
}
func (r *Open5GSUserReconciler) SetupWithManager(mgr ctrl.Manager) error {
return ctrl.NewControllerManagedBy(mgr).
For(&netv1.Open5GSUser{}).
Complete(r)
}