|
@@ -19,17 +19,20 @@ package controller
|
|
|
import (
|
|
|
"context"
|
|
|
|
|
|
+ databasev1 "github.com/iwanhae/nodb/api/v1"
|
|
|
+ "github.com/iwanhae/nodb/internal/templates"
|
|
|
+ "github.com/pkg/errors"
|
|
|
+ corev1 "k8s.io/api/core/v1"
|
|
|
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
|
|
"k8s.io/apimachinery/pkg/api/resource"
|
|
|
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
|
"k8s.io/apimachinery/pkg/runtime"
|
|
|
+ "k8s.io/apimachinery/pkg/types"
|
|
|
+ "k8s.io/apimachinery/pkg/util/intstr"
|
|
|
ctrl "sigs.k8s.io/controller-runtime"
|
|
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
|
|
+ "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
|
|
|
"sigs.k8s.io/controller-runtime/pkg/log"
|
|
|
-
|
|
|
- databasev1 "github.com/iwanhae/nodb/api/v1"
|
|
|
- "github.com/iwanhae/nodb/internal/templates"
|
|
|
- "github.com/pkg/errors"
|
|
|
- corev1 "k8s.io/api/core/v1"
|
|
|
)
|
|
|
|
|
|
// PostgreSQLReconciler reconciles a PostgreSQL object
|
|
@@ -52,7 +55,7 @@ type PostgreSQLReconciler struct {
|
|
|
//
|
|
|
// For more details, check Reconcile and its Result here:
|
|
|
// - https://pkg.go.dev/sigs.k8s.io/[email protected]/pkg/reconcile
|
|
|
-func (r *PostgreSQLReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
|
|
|
+func (r *PostgreSQLReconciler) Reconcile(ctx context.Context, req ctrl.Request) (result ctrl.Result, err error) {
|
|
|
logger := log.FromContext(ctx)
|
|
|
|
|
|
obj := databasev1.PostgreSQL{}
|
|
@@ -63,20 +66,91 @@ func (r *PostgreSQLReconciler) Reconcile(ctx context.Context, req ctrl.Request)
|
|
|
logger.Error(err, "resource not found")
|
|
|
return ctrl.Result{}, err
|
|
|
}
|
|
|
-
|
|
|
+ original := obj.DeepCopy()
|
|
|
logger.Info("reconcile", "namespace", obj.Namespace, "name", obj.Name)
|
|
|
|
|
|
- // Pending: Creating Pod if not exists
|
|
|
- // do not handle spec update
|
|
|
+ defer func() {
|
|
|
+ status := databasev1.Status_Ready
|
|
|
+ if obj.Status.Conditions.Pod.IsLowerThan(status) {
|
|
|
+ status = obj.Status.Conditions.Pod
|
|
|
+ }
|
|
|
+ if obj.Status.Conditions.Service.IsLowerThan(status) {
|
|
|
+ status = obj.Status.Conditions.Service
|
|
|
+ }
|
|
|
+ obj.Status.Status = status
|
|
|
+ err = r.Status().Patch(ctx, &obj, client.MergeFrom(original))
|
|
|
+ if err != nil {
|
|
|
+ logger.Error(err, "failed to update status")
|
|
|
+ }
|
|
|
+ }()
|
|
|
+
|
|
|
+ if err := r.createOrUpdatePod(ctx, &obj); err != nil {
|
|
|
+ return ctrl.Result{}, err
|
|
|
+ }
|
|
|
+
|
|
|
+ if err := r.createOrUpdateService(ctx, &obj); err != nil {
|
|
|
+ return ctrl.Result{}, err
|
|
|
+ }
|
|
|
+
|
|
|
+ return ctrl.Result{}, nil
|
|
|
+}
|
|
|
+
|
|
|
+func (r *PostgreSQLReconciler) createOrUpdateService(ctx context.Context, obj *databasev1.PostgreSQL) error {
|
|
|
+ logger := log.FromContext(ctx)
|
|
|
+
|
|
|
+ svc := corev1.Service{
|
|
|
+ ObjectMeta: metav1.ObjectMeta{Name: obj.Name, Namespace: obj.Namespace},
|
|
|
+ }
|
|
|
+ err := r.Client.Get(ctx, types.NamespacedName{
|
|
|
+ Namespace: obj.Namespace, Name: obj.Name,
|
|
|
+ }, &svc)
|
|
|
+ if err != nil && !apierrors.IsNotFound(err) {
|
|
|
+ // can't handle other than not found error
|
|
|
+ return err
|
|
|
+ }
|
|
|
+
|
|
|
+ logger.Info("create or update service")
|
|
|
+ if result, err := controllerutil.CreateOrUpdate(ctx, r.Client, &svc, func() error {
|
|
|
+ if err := controllerutil.SetOwnerReference(obj, &svc, r.Scheme); err != nil {
|
|
|
+ return errors.Wrap(err, "failed to set owner reference")
|
|
|
+ }
|
|
|
+ svc.ObjectMeta.Labels = map[string]string{
|
|
|
+ templates.LabelKeyType: templates.LabelValuePostgreSQL,
|
|
|
+ templates.LabelKeyName: obj.Name,
|
|
|
+ }
|
|
|
+ svc.Spec.Selector = map[string]string{
|
|
|
+ templates.LabelKeyType: templates.LabelValuePostgreSQL,
|
|
|
+ templates.LabelKeyName: obj.Name,
|
|
|
+ }
|
|
|
+ svc.Spec.Ports = []corev1.ServicePort{
|
|
|
+ {Name: "postgres", Port: 5432, TargetPort: intstr.FromString("postgres"), Protocol: corev1.ProtocolTCP},
|
|
|
+ }
|
|
|
+ svc.Spec.Type = corev1.ServiceTypeNodePort
|
|
|
+ svc.Spec.ExternalTrafficPolicy = corev1.ServiceExternalTrafficPolicyLocal
|
|
|
+ return nil
|
|
|
+ }); err != nil {
|
|
|
+ return err
|
|
|
+ } else if result != controllerutil.OperationResultNone {
|
|
|
+ logger.Info("service modified", "result", result)
|
|
|
+ }
|
|
|
+
|
|
|
+ obj.Status.Conditions.Service = databasev1.Status_Ready
|
|
|
+ return nil
|
|
|
+}
|
|
|
+func (r *PostgreSQLReconciler) createOrUpdatePod(ctx context.Context, obj *databasev1.PostgreSQL) error {
|
|
|
+ logger := log.FromContext(ctx)
|
|
|
|
|
|
pod := corev1.Pod{}
|
|
|
- if err := r.Client.Get(ctx, req.NamespacedName, &pod); err == nil {
|
|
|
+ if err := r.Client.Get(ctx, types.NamespacedName{
|
|
|
+ Namespace: obj.Namespace,
|
|
|
+ Name: obj.Name,
|
|
|
+ }, &pod); err == nil {
|
|
|
// if found return
|
|
|
logger.Info("ignore already existing pod")
|
|
|
- return ctrl.Result{}, nil
|
|
|
+ return nil
|
|
|
} else if !apierrors.IsNotFound(err) {
|
|
|
- // weird error
|
|
|
- return ctrl.Result{}, err
|
|
|
+ // can't handle other than not found error
|
|
|
+ return err
|
|
|
}
|
|
|
|
|
|
pod = templates.PostgreSQLPod(templates.PostgreSQLOpts{
|
|
@@ -89,21 +163,16 @@ func (r *PostgreSQLReconciler) Reconcile(ctx context.Context, req ctrl.Request)
|
|
|
Database: obj.Spec.Database,
|
|
|
|
|
|
Memory: resource.MustParse("1Gi"),
|
|
|
- Owner: &obj,
|
|
|
+ Owner: obj,
|
|
|
})
|
|
|
|
|
|
logger.Info("create pod", "namespace", pod.Namespace, "name", pod.Name)
|
|
|
if err := r.Client.Create(ctx, &pod); err != nil {
|
|
|
- return ctrl.Result{}, errors.Wrap(err, "failed to create pod")
|
|
|
+ return errors.Wrap(err, "failed to create pod")
|
|
|
}
|
|
|
|
|
|
- obj.Status.Status = databasev1.Status_Initializing
|
|
|
- logger.Info("update status")
|
|
|
- if err := r.Status().Update(ctx, &obj); err != nil {
|
|
|
- return ctrl.Result{}, errors.Wrap(err, "failed to patch status")
|
|
|
- }
|
|
|
-
|
|
|
- return ctrl.Result{}, nil
|
|
|
+ obj.Status.Conditions.Pod = databasev1.Status_Initializing
|
|
|
+ return nil
|
|
|
}
|
|
|
|
|
|
// SetupWithManager sets up the controller with the Manager.
|