Skip to content

Commit e6de39c

Browse files
committed
feat: wip
1 parent 2687c19 commit e6de39c

File tree

7 files changed

+158
-15
lines changed

7 files changed

+158
-15
lines changed

.mise.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
#:schema https://mise.jdx.dev/schema/mise.json
22

33
[tools]
4-
go = '1.25.5'
4+
go = '1.25.6'
55
golangci-lint = "2.7.2"
66
"aqua:operator-framework/operator-sdk" = "1.42.0"
77
"aqua:kubernetes-sigs/kind" = "0.31.0"

api/postgresql/v1alpha1/postgresqlbackup_types.go

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,8 +57,6 @@ type PostgresqlBackupStatus struct {
5757
// True if all resources are in a ready state and all work is done.
5858
// +optional
5959
Ready bool `json:"ready,omitempty"`
60-
// Resource Spec hash
61-
Hash string `json:"hash,omitempty"`
6260
// BackupProvider spec hash
6361
ProviderHash string `json:"providerHash,omitempty"`
6462
}

api/postgresql/v1alpha1/postgresqlbackupprovider_types.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,9 +47,10 @@ type PostgresqlBackupProviderSpec struct {
4747
// More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations
4848
// +optional
4949
Annotations map[string]string `json:"annotations,omitempty"`
50-
// Generated Secret name used to populate pg_dump information
50+
// Generated Secret name prefix used to populate pg_dump information
51+
// Must be limited to 20 characters
5152
// +optional
52-
GeneratedSecretName string `json:"generatedSecretName,omitempty"`
53+
GeneratedSecretNamePrefix string `json:"generatedSecretNamePrefix,omitempty"`
5354
// Wait for linked resource to be deleted
5455
// +optional
5556
WaitLinkedResourcesDeletion bool `json:"waitLinkedResourcesDeletion,omitempty"`
@@ -74,7 +75,7 @@ type PostgresqlBackupProviderStatus struct {
7475
// +optional
7576
Ready bool `json:"ready,omitempty"`
7677
// Generated secret name
77-
GeneratedSecretName string `json:"generatedSecretName,omitempty"`
78+
GeneratedSecretNamePrefix string `json:"generatedSecretNamePrefix,omitempty"`
7879
// Cron job name
7980
CronJobName string `json:"cronJobName,omitempty"`
8081
}

config/crd/bases/postgresql.easymile.com_postgresqlbackups.yaml

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -74,9 +74,6 @@ spec:
7474
status:
7575
description: PostgresqlBackupStatus defines the observed state of PostgresqlBackup.
7676
properties:
77-
hash:
78-
description: Resource Spec hash
79-
type: string
8077
message:
8178
description: Human-readable message indicating details about current
8279
operator phase or error.

internal/controller/postgresql/postgresqlbackup_controller.go

Lines changed: 142 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,16 +19,21 @@ package postgresql
1919
import (
2020
"context"
2121
"reflect"
22+
"strconv"
23+
"strings"
2224
"time"
2325

2426
"github.com/go-logr/logr"
2527
"github.com/prometheus/client_golang/prometheus"
2628
"k8s.io/apimachinery/pkg/api/errors"
2729
"k8s.io/apimachinery/pkg/runtime"
30+
"k8s.io/apimachinery/pkg/types"
2831
"k8s.io/client-go/tools/record"
2932
"sigs.k8s.io/controller-runtime/pkg/client"
3033
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
3134

35+
corev1 "k8s.io/api/core/v1"
36+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
3237
ctrl "sigs.k8s.io/controller-runtime"
3338

3439
postgresqlv1alpha1 "github.com/easymile/postgresql-operator/api/postgresql/v1alpha1"
@@ -47,6 +52,15 @@ type PostgresqlBackupReconciler struct {
4752
ReconcileTimeout time.Duration
4853
}
4954

55+
const (
56+
pgDumpEnvHost = "PGHOST"
57+
pgDumpEnvPort = "PGPORT"
58+
pgDumpEnvUser = "PGUSER"
59+
pgDumpEnvPassword = "PGPASSWORD"
60+
pgDumpEnvDatabase = "PGDATABASE"
61+
pgDumpEnvSSLMode = "PGSSLMODE"
62+
)
63+
5064
// +kubebuilder:rbac:groups=postgresql.easymile.com,resources=postgresqlbackups,verbs=get;list;watch;create;update;patch;delete
5165
// +kubebuilder:rbac:groups=postgresql.easymile.com,resources=postgresqlbackups/status,verbs=get;update;patch
5266
// +kubebuilder:rbac:groups=postgresql.easymile.com,resources=postgresqlbackups/finalizers,verbs=update
@@ -158,13 +172,27 @@ func (r *PostgresqlBackupReconciler) mainReconcile(
158172
if err != nil {
159173
return r.manageError(ctx, reqLogger, instance, originalPatch, err)
160174
}
175+
// Check that postgres database is ready before continue but only if it is the first time
176+
// If not, requeue event
177+
if !database.Status.Ready {
178+
reqLogger.Info("PostgresqlDatabase not ready, waiting for it")
179+
r.Recorder.Event(instance, "Warning", "Processing", "Processing stopped because PostgresqlDatabase isn't ready. Waiting for it.")
180+
181+
return ctrl.Result{}, nil
182+
}
161183

162184
// Find pgec
163185
pgec, err := utils.FindPgEngineCfg(ctx, r.Client, database)
164186
// Check error
165187
if err != nil {
166188
return r.manageError(ctx, reqLogger, instance, originalPatch, err)
167189
}
190+
if !pgec.Status.Ready {
191+
reqLogger.Info("PostgresqlEngineConfiguration not ready, waiting for it")
192+
r.Recorder.Event(instance, "Warning", "Processing", "Processing stopped because PostgresqlEngineConfiguration isn't ready. Waiting for it.")
193+
194+
return ctrl.Result{}, nil
195+
}
168196

169197
// Find pgec secret
170198
pgecSecret, err := utils.FindSecretPgEngineCfg(ctx, r.Client, pgec)
@@ -180,12 +208,125 @@ func (r *PostgresqlBackupReconciler) mainReconcile(
180208
return r.manageError(ctx, reqLogger, instance, originalPatch, err)
181209
}
182210

183-
// Compute spec hash
211+
_, err = r.manageSecret(ctx, instance, database, pgec, pgecSecret, backupProvider)
212+
if err != nil {
213+
return r.manageError(ctx, reqLogger, instance, originalPatch, err)
214+
}
215+
216+
// Manage cronjob part
184217

185218
// Success
186219
return r.manageSuccess(ctx, reqLogger, instance, originalPatch)
187220
}
188221

222+
func (r *PostgresqlBackupReconciler) manageSecret(
223+
ctx context.Context,
224+
instance *postgresqlv1alpha1.PostgresqlBackup,
225+
database *postgresqlv1alpha1.PostgresqlDatabase,
226+
pgec *postgresqlv1alpha1.PostgresqlEngineConfiguration,
227+
pgecSecret *corev1.Secret,
228+
backupProvider *postgresqlv1alpha1.PostgresqlBackupProvider,
229+
) (string, error) {
230+
// TODO Add a random string to secret
231+
secretName := backupProvider.Spec.GeneratedSecretNamePrefix
232+
if secretName == "" {
233+
return "", errors.NewBadRequest("backup provider generated secret name is empty")
234+
}
235+
236+
user := string(pgecSecret.Data[pgecSecretUserKey])
237+
password := string(pgecSecret.Data[pgecSecretPassKey])
238+
if user == "" || password == "" {
239+
return "", errors.NewBadRequest("engine configuration secret must contain \"user\" and \"password\" values")
240+
}
241+
242+
databaseName := database.Status.Database
243+
if databaseName == "" {
244+
return "", errors.NewBadRequest("database name is empty")
245+
}
246+
247+
host := pgec.Spec.Host
248+
port := pgec.Spec.Port
249+
uriArgs := pgec.Spec.URIArgs
250+
251+
data := map[string][]byte{
252+
pgDumpEnvHost: []byte(host),
253+
pgDumpEnvPort: []byte(strconv.Itoa(port)),
254+
pgDumpEnvUser: []byte(user),
255+
pgDumpEnvPassword: []byte(password),
256+
pgDumpEnvDatabase: []byte(databaseName),
257+
}
258+
259+
if uriArgs != "" {
260+
for _, part := range strings.Split(uriArgs, "&") {
261+
if part == "" {
262+
continue
263+
}
264+
keyValue := strings.SplitN(part, "=", 2)
265+
if len(keyValue) == 2 && keyValue[0] == "sslmode" && keyValue[1] != "" {
266+
data[pgDumpEnvSSLMode] = []byte(keyValue[1])
267+
268+
break
269+
}
270+
}
271+
}
272+
273+
// Create secret structure
274+
secret := &corev1.Secret{
275+
ObjectMeta: metav1.ObjectMeta{
276+
Name: secretName,
277+
Namespace: instance.Namespace,
278+
Labels: backupProvider.Spec.Labels,
279+
Annotations: backupProvider.Spec.Annotations,
280+
},
281+
Type: corev1.SecretTypeOpaque,
282+
Data: data,
283+
}
284+
285+
// Add controller reference
286+
err := controllerutil.SetControllerReference(instance, secret, r.Scheme)
287+
if err != nil {
288+
return "", err
289+
}
290+
291+
// Try to find it in kubernetes
292+
found := &corev1.Secret{}
293+
err = r.Get(
294+
ctx,
295+
types.NamespacedName{
296+
Name: secret.Name,
297+
Namespace: secret.Namespace,
298+
},
299+
found,
300+
)
301+
// Check if error is present and it isn't a not found error
302+
if err != nil && !errors.IsNotFound(err) {
303+
return "", err
304+
}
305+
306+
// Check if error is present and if it is a not found error
307+
if err != nil && errors.IsNotFound(err) {
308+
// Create secret
309+
return secretName, r.Create(ctx, secret)
310+
}
311+
312+
// Update case
313+
314+
// Check if update is needed
315+
if !reflect.DeepEqual(found.Data, secret.Data) ||
316+
!reflect.DeepEqual(found.Labels, secret.Labels) ||
317+
!reflect.DeepEqual(found.Annotations, secret.Annotations) {
318+
found.Data = secret.Data
319+
found.Labels = secret.Labels
320+
found.Annotations = secret.Annotations
321+
322+
// Update
323+
return secretName, r.Update(ctx, found)
324+
}
325+
326+
// Nothing to update or patch
327+
return secretName, nil
328+
}
329+
189330
func (r *PostgresqlBackupReconciler) updateInstance(
190331
ctx context.Context,
191332
instance *postgresqlv1alpha1.PostgresqlBackup,

internal/controller/postgresql/postgresqlbackupprovider_controller.go

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@ type PostgresqlBackupProviderReconciler struct {
4848
ReconcileTimeout time.Duration
4949
}
5050

51+
const MaxGeneratedSecretNamePrefixLength = 20
52+
5153
// +kubebuilder:rbac:groups=postgresql.easymile.com,resources=postgresqlbackupproviders,verbs=get;list;watch;create;update;patch;delete
5254
// +kubebuilder:rbac:groups=postgresql.easymile.com,resources=postgresqlbackupproviders/status,verbs=get;update;patch
5355
// +kubebuilder:rbac:groups=postgresql.easymile.com,resources=postgresqlbackupproviders/finalizers,verbs=update
@@ -156,6 +158,8 @@ func (r *PostgresqlBackupProviderReconciler) mainReconcile(
156158
return ctrl.Result{}, nil
157159
}
158160

161+
// TODO Validate instance
162+
159163
// Success
160164
return r.manageSuccess(ctx, reqLogger, instance, originalPatch)
161165
}
@@ -171,9 +175,9 @@ func (r *PostgresqlBackupProviderReconciler) updateInstance(
171175
controllerutil.AddFinalizer(instance, config.Finalizer)
172176

173177
// Check if generated secret name is set
174-
if instance.Spec.GeneratedSecretName == "" {
175-
instance.Spec.GeneratedSecretName = strings.ToLower(
176-
utils.GetRandomString(DefaultWorkGeneratedSecretNameRandomLength),
178+
if instance.Spec.GeneratedSecretNamePrefix == "" {
179+
instance.Spec.GeneratedSecretNamePrefix = strings.ToLower(
180+
utils.GetRandomString(MaxGeneratedSecretNamePrefixLength),
177181
)
178182
}
179183

internal/controller/postgresql/postgresqlengineconfiguration_controller.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ import (
4141
const (
4242
DefaultPGPort = 5432
4343
DefaultBouncerPort = 6432
44+
pgecSecretUserKey = "user"
45+
pgecSecretPassKey = "password"
4446
)
4547

4648
// PostgresqlEngineConfigurationReconciler reconciles a PostgresqlEngineConfiguration object.
@@ -247,8 +249,8 @@ func (r *PostgresqlEngineConfigurationReconciler) mainReconcile(
247249

248250
// Got secret
249251
// Check that secret is valid
250-
user := string(secret.Data["user"])
251-
password := string(secret.Data["password"])
252+
user := string(secret.Data[pgecSecretUserKey])
253+
password := string(secret.Data[pgecSecretPassKey])
252254

253255
if user == "" || password == "" {
254256
return r.manageError(

0 commit comments

Comments
 (0)