@@ -14,6 +14,7 @@ import (
14
14
"fmt"
15
15
"net/http"
16
16
"net/url"
17
+ "strconv"
17
18
"strings"
18
19
19
20
"github.com/go-logr/logr"
@@ -23,10 +24,12 @@ import (
23
24
"k8s.io/utils/pointer"
24
25
25
26
firewall "github.com/metal-stack/firewall-controller/api/v1"
27
+ coreosv1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1"
26
28
apierrors "k8s.io/apimachinery/pkg/api/errors"
27
29
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
28
30
"k8s.io/apimachinery/pkg/runtime"
29
31
"k8s.io/apimachinery/pkg/types"
32
+ "k8s.io/apimachinery/pkg/util/intstr"
30
33
ctrl "sigs.k8s.io/controller-runtime"
31
34
"sigs.k8s.io/controller-runtime/pkg/client"
32
35
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
@@ -36,6 +39,13 @@ import (
36
39
"github.com/fi-ts/postgreslet/pkg/operatormanager"
37
40
)
38
41
42
+ const (
43
+ postgresExporterServiceName string = "postgres-exporter"
44
+ postgresExporterServicePortName string = "metrics"
45
+ postgresExporterServiceTenantAnnotationName string = pg .TenantLabelName
46
+ postgresExporterServiceProjectIDAnnotationName string = pg .ProjectIDLabelName
47
+ )
48
+
39
49
// requeue defines in how many seconds a requeue should happen
40
50
var requeue = ctrl.Result {
41
51
Requeue : true ,
@@ -53,6 +63,8 @@ type PostgresReconciler struct {
53
63
recorder record.EventRecorder
54
64
PgParamBlockList map [string ]bool
55
65
StandbyClustersSourceRanges []string
66
+ PostgresletNamespace string
67
+ SidecarsConfigMapName string
56
68
}
57
69
58
70
// Reconcile is the entry point for postgres reconciliation.
@@ -105,6 +117,11 @@ func (r *PostgresReconciler) Reconcile(ctx context.Context, req ctrl.Request) (c
105
117
}
106
118
log .Info ("corresponding Service of type LoadBalancer deleted" )
107
119
120
+ // delete the postgres-exporter service
121
+ if err := r .deleteExporterSidecarService (ctx , namespace ); client .IgnoreNotFound (err ) != nil {
122
+ return ctrl.Result {}, fmt .Errorf ("error while deleting the postgres-exporter service: %w" , err )
123
+ }
124
+
108
125
if err := r .deleteZPostgresqlByLabels (ctx , matchingLabels , namespace ); err != nil {
109
126
r .recorder .Eventf (instance , "Warning" , "Error" , "failed to delete Zalando resource: %v" , err )
110
127
return ctrl.Result {}, err
@@ -183,7 +200,29 @@ func (r *PostgresReconciler) Reconcile(ctx context.Context, req ctrl.Request) (c
183
200
return ctrl.Result {}, fmt .Errorf ("unable to create or update egress ClusterwideNetworkPolicy: %w" , err )
184
201
}
185
202
186
- if err := r .createOrUpdateZalandoPostgresql (ctx , instance , log ); err != nil {
203
+ // try to fetch the global sidecars configmap
204
+ cns := types.NamespacedName {
205
+ Namespace : r .PostgresletNamespace ,
206
+ Name : r .SidecarsConfigMapName ,
207
+ }
208
+ globalSidecarsCM := & corev1.ConfigMap {}
209
+ if err := r .SvcClient .Get (ctx , cns , globalSidecarsCM ); err != nil {
210
+ // configmap with configuration does not exists, nothing we can do here...
211
+ return ctrl.Result {}, fmt .Errorf ("could not fetch config for sidecars" )
212
+ }
213
+ // Add services for our sidecars
214
+ namespace := instance .ToPeripheralResourceNamespace ()
215
+ if err := r .createOrUpdateExporterSidecarServices (ctx , namespace , globalSidecarsCM , instance ); err != nil {
216
+ return ctrl.Result {}, fmt .Errorf ("error while creating sidecars services %v: %w" , namespace , err )
217
+ }
218
+
219
+ // Add service monitor for our exporter sidecar
220
+ err := r .createOrUpdateExporterSidecarServiceMonitor (ctx , namespace , instance )
221
+ if err != nil {
222
+ return ctrl.Result {}, fmt .Errorf ("error while creating sidecars servicemonitor %v: %w" , namespace , err )
223
+ }
224
+
225
+ if err := r .createOrUpdateZalandoPostgresql (ctx , instance , log , globalSidecarsCM ); err != nil {
187
226
r .recorder .Eventf (instance , "Warning" , "Error" , "failed to create Zalando resource: %v" , err )
188
227
return ctrl.Result {}, fmt .Errorf ("failed to create or update zalando postgresql: %w" , err )
189
228
}
@@ -224,21 +263,7 @@ func (r *PostgresReconciler) SetupWithManager(mgr ctrl.Manager) error {
224
263
Complete (r )
225
264
}
226
265
227
- func (r * PostgresReconciler ) createOrUpdateZalandoPostgresql (ctx context.Context , instance * pg.Postgres , log logr.Logger ) error {
228
- // Get the sidecar config
229
- // try to fetch the global sidecars configmap
230
- cns := types.NamespacedName {
231
- // TODO don't use string literals here! name is dependent of the release name of the helm chart!
232
- Namespace : "postgreslet-system" ,
233
- Name : "postgreslet-postgres-sidecars" ,
234
- }
235
- c := & corev1.ConfigMap {}
236
- if err := r .SvcClient .Get (ctx , cns , c ); err != nil {
237
- // configmap with configuration does not exists, nothing we can do here...
238
- log .Info ("could not fetch config for sidecars" )
239
- c = nil
240
- }
241
-
266
+ func (r * PostgresReconciler ) createOrUpdateZalandoPostgresql (ctx context.Context , instance * pg.Postgres , log logr.Logger , sidecarsCM * corev1.ConfigMap ) error {
242
267
var restoreBackupConfig * pg.BackupConfig
243
268
var restoreSouceInstance * pg.Postgres
244
269
if instance .Spec .PostgresRestore != nil {
@@ -273,7 +298,7 @@ func (r *PostgresReconciler) createOrUpdateZalandoPostgresql(ctx context.Context
273
298
return fmt .Errorf ("failed to fetch zalando postgresql: %w" , err )
274
299
}
275
300
276
- u , err := instance .ToUnstructuredZalandoPostgresql (nil , c , r .StorageClass , r .PgParamBlockList , restoreBackupConfig , restoreSouceInstance )
301
+ u , err := instance .ToUnstructuredZalandoPostgresql (nil , sidecarsCM , r .StorageClass , r .PgParamBlockList , restoreBackupConfig , restoreSouceInstance )
277
302
if err != nil {
278
303
return fmt .Errorf ("failed to convert to unstructured zalando postgresql: %w" , err )
279
304
}
@@ -289,7 +314,7 @@ func (r *PostgresReconciler) createOrUpdateZalandoPostgresql(ctx context.Context
289
314
// Update zalando postgresql
290
315
mergeFrom := client .MergeFrom (rawZ .DeepCopy ())
291
316
292
- u , err := instance .ToUnstructuredZalandoPostgresql (rawZ , c , r .StorageClass , r .PgParamBlockList , restoreBackupConfig , restoreSouceInstance )
317
+ u , err := instance .ToUnstructuredZalandoPostgresql (rawZ , sidecarsCM , r .StorageClass , r .PgParamBlockList , restoreBackupConfig , restoreSouceInstance )
293
318
if err != nil {
294
319
return fmt .Errorf ("failed to convert to unstructured zalando postgresql: %w" , err )
295
320
}
@@ -748,3 +773,171 @@ func (r *PostgresReconciler) getBackupConfig(ctx context.Context, ns, name strin
748
773
}
749
774
return & backupConfig , nil
750
775
}
776
+
777
+ // createOrUpdateExporterSidecarServices ensures the neccessary services to acces the sidecars exist
778
+ func (r * PostgresReconciler ) createOrUpdateExporterSidecarServices (ctx context.Context , namespace string , c * corev1.ConfigMap , in * pg.Postgres ) error {
779
+ log := r .Log .WithValues ("namespace" , namespace )
780
+
781
+ exporterServicePort , error := strconv .ParseInt (c .Data ["postgres-exporter-service-port" ], 10 , 32 )
782
+ if error != nil {
783
+ // todo log error
784
+ exporterServicePort = 9187
785
+ }
786
+
787
+ exporterServiceTargetPort , error := strconv .ParseInt (c .Data ["postgres-exporter-service-target-port" ], 10 , 32 )
788
+ if error != nil {
789
+ // todo log error
790
+ exporterServiceTargetPort = exporterServicePort
791
+ }
792
+
793
+ pes := & corev1.Service {}
794
+
795
+ if err := r .SetName (pes , postgresExporterServiceName ); err != nil {
796
+ return fmt .Errorf ("error while setting the name of the postgres-exporter service to %v: %w" , namespace , err )
797
+ }
798
+ if err := r .SetNamespace (pes , namespace ); err != nil {
799
+ return fmt .Errorf ("error while setting the namespace of the postgres-exporter service to %v: %w" , namespace , err )
800
+ }
801
+ labels := map [string ]string {
802
+ // "application": "spilo", // TODO check if we still need that label, IsOperatorDeletable won't work anymore if we set it.
803
+ "app" : "postgres-exporter" ,
804
+ }
805
+ if err := r .SetLabels (pes , labels ); err != nil {
806
+ return fmt .Errorf ("error while setting the labels of the postgres-exporter service to %v: %w" , labels , err )
807
+ }
808
+ annotations := map [string ]string {
809
+ postgresExporterServiceTenantAnnotationName : in .Spec .Tenant ,
810
+ postgresExporterServiceProjectIDAnnotationName : in .Spec .ProjectID ,
811
+ }
812
+ if err := r .SetAnnotations (pes , annotations ); err != nil {
813
+ return fmt .Errorf ("error while setting the annotations of the postgres-exporter service to %v: %w" , annotations , err )
814
+ }
815
+
816
+ pes .Spec .Ports = []corev1.ServicePort {
817
+ {
818
+ Name : postgresExporterServicePortName ,
819
+ Port : int32 (exporterServicePort ),
820
+ Protocol : corev1 .ProtocolTCP ,
821
+ TargetPort : intstr .FromInt (int (exporterServiceTargetPort )),
822
+ },
823
+ }
824
+ selector := map [string ]string {
825
+ "application" : "spilo" ,
826
+ }
827
+ pes .Spec .Selector = selector
828
+ pes .Spec .Type = corev1 .ServiceTypeClusterIP
829
+
830
+ // try to fetch any existing postgres-exporter service
831
+ ns := types.NamespacedName {
832
+ Namespace : namespace ,
833
+ Name : postgresExporterServiceName ,
834
+ }
835
+ old := & corev1.Service {}
836
+ if err := r .SvcClient .Get (ctx , ns , old ); err == nil {
837
+ // service exists, overwriting it (but using the same clusterip)
838
+ pes .Spec .ClusterIP = old .Spec .ClusterIP
839
+ pes .ObjectMeta .ResourceVersion = old .GetObjectMeta ().GetResourceVersion ()
840
+ if err := r .SvcClient .Update (ctx , pes ); err != nil {
841
+ return fmt .Errorf ("error while updating the postgres-exporter service: %w" , err )
842
+ }
843
+ log .Info ("postgres-exporter service updated" )
844
+ return nil
845
+ }
846
+ // todo: handle errors other than `NotFound`
847
+
848
+ // local servicemonitor does not exist, creating it
849
+ if err := r .SvcClient .Create (ctx , pes ); err != nil {
850
+ return fmt .Errorf ("error while creating the postgres-exporter service: %w" , err )
851
+ }
852
+ log .Info ("postgres-exporter service created" )
853
+
854
+ return nil
855
+ }
856
+
857
+ // createOrUpdateExporterSidecarServiceMonitor ensures the servicemonitors for the sidecars exist
858
+ func (r * PostgresReconciler ) createOrUpdateExporterSidecarServiceMonitor (ctx context.Context , namespace string , in * pg.Postgres ) error {
859
+ log := r .Log .WithValues ("namespace" , namespace )
860
+
861
+ pesm := & coreosv1.ServiceMonitor {}
862
+
863
+ // TODO what's the correct name?
864
+ if err := r .SetName (pesm , postgresExporterServiceName ); err != nil {
865
+ return fmt .Errorf ("error while setting the name of the postgres-exporter servicemonitor to %v: %w" , namespace , err )
866
+ }
867
+ if err := r .SetNamespace (pesm , namespace ); err != nil {
868
+ return fmt .Errorf ("error while setting the namespace of the postgres-exporter servicemonitor to %v: %w" , namespace , err )
869
+ }
870
+ labels := map [string ]string {
871
+ "app" : "postgres-exporter" ,
872
+ "release" : "prometheus" ,
873
+ }
874
+ if err := r .SetLabels (pesm , labels ); err != nil {
875
+ return fmt .Errorf ("error while setting the namespace of the postgres-exporter servicemonitor to %v: %w" , namespace , err )
876
+ }
877
+ annotations := map [string ]string {
878
+ postgresExporterServiceTenantAnnotationName : in .Spec .Tenant ,
879
+ postgresExporterServiceProjectIDAnnotationName : in .Spec .ProjectID ,
880
+ }
881
+ if err := r .SetAnnotations (pesm , annotations ); err != nil {
882
+ return fmt .Errorf ("error while setting the annotations of the postgres-exporter service monitor to %v: %w" , annotations , err )
883
+ }
884
+
885
+ pesm .Spec .Endpoints = []coreosv1.Endpoint {
886
+ {
887
+ Port : postgresExporterServicePortName ,
888
+ },
889
+ }
890
+ pesm .Spec .NamespaceSelector = coreosv1.NamespaceSelector {
891
+ MatchNames : []string {namespace },
892
+ }
893
+ matchLabels := map [string ]string {
894
+ // TODO use extraced string
895
+ "app" : "postgres-exporter" ,
896
+ }
897
+ pesm .Spec .Selector = metav1.LabelSelector {
898
+ MatchLabels : matchLabels ,
899
+ }
900
+
901
+ // try to fetch any existing postgres-exporter service
902
+ ns := types.NamespacedName {
903
+ Namespace : namespace ,
904
+ Name : postgresExporterServiceName ,
905
+ }
906
+ old := & coreosv1.ServiceMonitor {}
907
+ if err := r .SvcClient .Get (ctx , ns , old ); err == nil {
908
+ // Copy the resource version
909
+ pesm .ObjectMeta .ResourceVersion = old .ObjectMeta .ResourceVersion
910
+ if err := r .SvcClient .Update (ctx , pesm ); err != nil {
911
+ return fmt .Errorf ("error while updating the postgres-exporter servicemonitor: %w" , err )
912
+ }
913
+ log .Info ("postgres-exporter servicemonitor updated" )
914
+ return nil
915
+ }
916
+ // todo: handle errors other than `NotFound`
917
+
918
+ // local servicemonitor does not exist, creating it
919
+ if err := r .SvcClient .Create (ctx , pesm ); err != nil {
920
+ return fmt .Errorf ("error while creating the postgres-exporter servicemonitor: %w" , err )
921
+ }
922
+ log .Info ("postgres-exporter servicemonitor created" )
923
+
924
+ return nil
925
+ }
926
+
927
+ func (r * PostgresReconciler ) deleteExporterSidecarService (ctx context.Context , namespace string ) error {
928
+ log := r .Log .WithValues ("namespace" , namespace )
929
+
930
+ s := & corev1.Service {}
931
+ if err := r .SetName (s , postgresExporterServiceName ); err != nil {
932
+ return fmt .Errorf ("error while setting the name of the postgres-exporter service to delete to %v: %w" , postgresExporterServiceName , err )
933
+ }
934
+ if err := r .SetNamespace (s , namespace ); err != nil {
935
+ return fmt .Errorf ("error while setting the namespace of the postgres-exporter service to delete to %v: %w" , namespace , err )
936
+ }
937
+ if err := r .SvcClient .Delete (ctx , s ); err != nil {
938
+ return fmt .Errorf ("error while deleting the postgres-exporter service: %w" , err )
939
+ }
940
+ log .Info ("postgres-exporter service deleted" )
941
+
942
+ return nil
943
+ }
0 commit comments