Skip to content

Commit 17a7c9f

Browse files
committed
Merge branch 'main' of https://github.com/fi-ts/postgreslet into refactor-nextFreeSocket
2 parents 52a1f47 + f14adfb commit 17a7c9f

11 files changed

+178
-646
lines changed

api/v1/postgres_types.go

Lines changed: 112 additions & 144 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ package v1
88

99
import (
1010
"fmt"
11-
"time"
11+
"reflect"
1212

1313
"regexp"
1414

@@ -18,6 +18,7 @@ import (
1818
corev1 "k8s.io/api/core/v1"
1919
networkingv1 "k8s.io/api/networking/v1"
2020
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
21+
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
2122
"k8s.io/apimachinery/pkg/runtime"
2223
"k8s.io/apimachinery/pkg/types"
2324
"k8s.io/apimachinery/pkg/util/intstr"
@@ -28,57 +29,67 @@ import (
2829
// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN!
2930
// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized.
3031

32+
const (
33+
// UIDLabelName Name of the label referencing the owning Postgres resource in the control cluster
34+
UIDLabelName string = "postgres.database.fits.cloud/uuid"
35+
// TenantLabelName Name of the tenant label
36+
TenantLabelName string = "postgres.database.fits.cloud/tenant"
37+
// ProjectIDLabelName Name of the ProjectID label
38+
ProjectIDLabelName string = "postgres.database.fits.cloud/project-id"
39+
// ManagedByLabelName Name of the managed-by label
40+
ManagedByLabelName string = "postgres.database.fits.cloud/managed-by"
41+
// ManagedByLabelValue Value of the managed-by label
42+
ManagedByLabelValue string = "postgreslet"
43+
// PostgresFinalizerName Name of the finalizer to use
44+
PostgresFinalizerName string = "postgres.finalizers.database.fits.cloud"
45+
// CreatedByAnnotationKey is used to store who in person created this database
46+
CreatedByAnnotationKey string = "postgres.database.fits.cloud/created-by"
47+
// BackupConfigLabelName if set to true, this secret stores the backupConfig
48+
BackupConfigLabelName string = "postgres.database.fits.cloud/is-backup"
49+
// BackupConfigKey defines the key under which the BackupConfig is stored in the data map.
50+
BackupConfigKey = "config"
51+
)
52+
53+
// BackupConfig defines all properties to configure backup of a database.
54+
// This config is stored in the data section under the key BackupConfigKey as json payload.
55+
type BackupConfig struct {
56+
// ID of this backupConfig
57+
ID string `json:"id"`
58+
// Name is a user defined description
59+
Name string `json:"name"`
60+
// ProjectID the project this backup is mapped to
61+
ProjectID string `json:"project"`
62+
// Tenant the tenant of the backup
63+
Tenant string `json:"tenant"`
64+
// Retention defines how many versions should be held in s3
65+
Retention string `json:"retention"`
66+
// Schedule in cron syntax when to run the backup periodically
67+
Schedule string `json:"schedule"`
68+
69+
// S3Endpoint the url of the s3 endpoint
70+
S3Endpoint string `json:"s3endpoint"`
71+
// S3BucketName is the name of the bucket where the backup should be stored.
72+
S3BucketName string `json:"s3bucketname"`
73+
// S3Region the region of the aws s3
74+
S3Region string `json:"s3region"`
75+
// S3AccessKey is the accesskey which must have write access
76+
S3AccessKey string `json:"s3accesskey"`
77+
// S3SecretKey is the secretkey which must match to the accesskey
78+
S3SecretKey string `json:"s3secretkey"`
79+
}
80+
81+
var ZalandoPostgresqlTypeMeta = metav1.TypeMeta{
82+
APIVersion: "acid.zalan.do/v1",
83+
Kind: "postgresql",
84+
}
85+
3186
// +kubebuilder:object:root=true
3287
// +kubebuilder:subresource:status
3388
// +kubebuilder:printcolumn:name="Version",type=string,JSONPath=`.spec.version`
3489
// +kubebuilder:printcolumn:name="Status",type=string,JSONPath=`.status.description`
3590
// +kubebuilder:printcolumn:name="Load-Balancer-IP",type=string,JSONPath=`.status.socket.ip`
3691
// +kubebuilder:printcolumn:name="Load-Balancer-Port",type=integer,JSONPath=`.status.socket.port`
3792

38-
// UIDLabelName Name of the label referencing the owning Postgres resource in the control cluster
39-
const UIDLabelName string = "postgres.database.fits.cloud/uuid"
40-
41-
// TenantLabelName Name of the tenant label
42-
const TenantLabelName string = "postgres.database.fits.cloud/tenant"
43-
44-
// ProjectIDLabelName Name of the ProjectID label
45-
const ProjectIDLabelName string = "postgres.database.fits.cloud/project-id"
46-
47-
// ManagedByLabelName Name of the managed-by label
48-
const ManagedByLabelName string = "postgres.database.fits.cloud/managed-by"
49-
50-
// ManagedByLabelValue Value of the managed-by label
51-
const ManagedByLabelValue string = "postgreslet"
52-
53-
// PostgresFinalizerName Name of the finalizer to use
54-
const PostgresFinalizerName string = "postgres.finalizers.database.fits.cloud"
55-
56-
// Backup configure parametes of the database backup
57-
const (
58-
// S3URL defines the s3 endpoint URL for backup
59-
BackupSecretS3Endpoint = "s3Endpoint"
60-
// S3BucketName defines the name of the S3 bucket for backup
61-
BackupSecretS3BucketName = "s3BucketName"
62-
// Retention defines how many days a backup will persist
63-
BackupSecretRetention = "retention"
64-
// Schedule defines how often a backup should be made, in cron format
65-
BackupSecretSchedule = "schedule"
66-
BackupSecretAccessKey = "accesskey"
67-
BackupSecretSecretKey = "secretkey"
68-
BackupSecretProjectKey = "project"
69-
)
70-
71-
const (
72-
Sun Weekday = iota
73-
Mon
74-
Tue
75-
Wed
76-
Thu
77-
Fri
78-
Sat
79-
All
80-
)
81-
8293
// Postgres is the Schema for the postgres API
8394
type Postgres struct {
8495
metav1.TypeMeta `json:",inline"`
@@ -117,7 +128,7 @@ type PostgresSpec struct {
117128

118129
// todo: add default
119130
// Maintenance defines automatic maintenance of the database
120-
Maintenance *Maintenance `json:"maintenance,omitempty"`
131+
Maintenance []string `json:"maintenance,omitempty"`
121132

122133
// AccessList defines access restrictions
123134
AccessList *AccessList `json:"accessList,omitempty"`
@@ -132,6 +143,7 @@ type AccessList struct {
132143
SourceRanges []string `json:"sourceRanges,omitempty"`
133144
}
134145

146+
// Todo: Add defaults
135147
// Size defines the size aspects of the database
136148
type Size struct {
137149
// CPU is in the format as pod.spec.resource.request.cpu
@@ -147,23 +159,6 @@ type Size struct {
147159
StorageSize string `json:"storageSize,omitempty"`
148160
}
149161

150-
// Weekday defines a weekday or everyday
151-
type Weekday int
152-
153-
// TimeWindow defines an interval in time
154-
type TimeWindow struct {
155-
Start metav1.Time `json:"start,omitempty"`
156-
End metav1.Time `json:"end,omitempty"`
157-
}
158-
159-
// Maintenance configures database maintenance
160-
type Maintenance struct {
161-
// Weekday defines when the operator is allowed to do maintenance
162-
Weekday Weekday `json:"weekday,omitempty"`
163-
// TimeWindow defines when the maintenance should happen
164-
TimeWindow TimeWindow `json:"timeWindow,omitempty"`
165-
}
166-
167162
// PostgresStatus defines the observed state of Postgres
168163
type PostgresStatus struct {
169164
// INSERT ADDITIONAL STATUS FIELD - define observed state of cluster
@@ -307,7 +302,7 @@ func (p *Postgres) ToPeripheralResourceName() string {
307302
return p.generateTeamID() + "-" + p.generateDatabaseName()
308303
}
309304

310-
// ToUserPasswordSecret returns the secret containing user password pairs
305+
// ToUserPasswordsSecret returns the secret containing user password pairs
311306
func (p *Postgres) ToUserPasswordsSecret(src *corev1.SecretList, scheme *runtime.Scheme) (*corev1.Secret, error) {
312307
secret := &corev1.Secret{}
313308
secret.Namespace = p.Namespace
@@ -384,99 +379,63 @@ func (p *Postgres) ToPeripheralResourceNamespace() string {
384379
return p.ToPeripheralResourceName()
385380
}
386381

387-
func (p *Postgres) ToZalandoPostgres() *ZalandoPostgres {
388-
return &ZalandoPostgres{
389-
TypeMeta: ZalandoPostgresTypeMeta,
390-
ObjectMeta: metav1.ObjectMeta{
391-
Name: p.ToPeripheralResourceName(),
392-
Namespace: p.ToPeripheralResourceNamespace(),
393-
Labels: map[string]string{UIDLabelName: string(p.UID)},
394-
},
395-
Spec: ZalandoPostgresSpec{
396-
MaintenanceWindows: func() []MaintenanceWindow {
397-
if p.Spec.Maintenance == nil {
398-
return nil
399-
}
400-
isEvery := p.Spec.Maintenance.Weekday == All
401-
return []MaintenanceWindow{
402-
{Everyday: isEvery,
403-
Weekday: func() time.Weekday {
404-
if isEvery {
405-
return time.Weekday(0)
406-
}
407-
return time.Weekday(p.Spec.Maintenance.Weekday)
408-
}(),
409-
StartTime: p.Spec.Maintenance.TimeWindow.Start,
410-
EndTime: p.Spec.Maintenance.TimeWindow.End,
411-
},
412-
}
413-
}(),
414-
NumberOfInstances: p.Spec.NumberOfInstances,
415-
PostgresqlParam: PostgresqlParam{PgVersion: p.Spec.Version},
416-
Resources: func() *Resources {
417-
if p.Spec.Size.CPU == "" {
418-
return nil
419-
}
420-
return &Resources{
421-
ResourceRequests: &ResourceDescription{
422-
CPU: p.Spec.Size.CPU,
423-
Memory: p.Spec.Size.Memory,
424-
},
425-
ResourceLimits: &ResourceDescription{
426-
CPU: p.Spec.Size.CPU,
427-
Memory: p.Spec.Size.Memory,
428-
},
429-
}
430-
}(),
431-
TeamID: p.generateTeamID(),
432-
Volume: Volume{Size: p.Spec.Size.StorageSize},
433-
},
382+
func (p *Postgres) ToUnstructuredZalandoPostgresql(z *zalando.Postgresql) (*unstructured.Unstructured, error) {
383+
if z == nil {
384+
z = &zalando.Postgresql{}
434385
}
435-
}
386+
z.TypeMeta = ZalandoPostgresqlTypeMeta
387+
z.Namespace = p.ToPeripheralResourceNamespace()
388+
z.Name = p.ToPeripheralResourceName()
389+
z.Labels = p.ToZalandoPostgresqlMatchingLabels()
390+
391+
z.Spec.NumberOfInstances = p.Spec.NumberOfInstances
392+
z.Spec.PostgresqlParam.PgVersion = p.Spec.Version
393+
z.Spec.Resources.ResourceRequests.CPU = p.Spec.Size.CPU
394+
z.Spec.Resources.ResourceRequests.Memory = p.Spec.Size.Memory
395+
z.Spec.Resources.ResourceLimits.CPU = p.Spec.Size.CPU
396+
z.Spec.Resources.ResourceLimits.Memory = p.Spec.Size.Memory
397+
z.Spec.TeamID = p.generateTeamID()
398+
z.Spec.Volume.Size = p.Spec.Size.StorageSize
436399

437-
func (p *Postgres) ToPatchedZalandoPostgresql(z *zalando.Postgresql) *zalando.Postgresql {
438400
if p.HasSourceRanges() {
439401
z.Spec.AllowedSourceRanges = p.Spec.AccessList.SourceRanges
440402
}
441403

442-
z.Spec.NumberOfInstances = p.Spec.NumberOfInstances
443-
444-
// todo: Check if the validation should be performed here.
445-
z.Spec.PostgresqlParam.PgVersion = p.Spec.Version
404+
jsonZ, err := runtime.DefaultUnstructuredConverter.ToUnstructured(z)
405+
if err != nil {
406+
return nil, fmt.Errorf("failed to convert to unstructured zalando postgresql: %w", err)
407+
}
408+
jsonSpec, _ := jsonZ["spec"].(map[string]interface{})
446409

447-
z.Spec.ResourceRequests.CPU = p.Spec.Size.CPU
448-
z.Spec.ResourceRequests.Memory = p.Spec.Size.Memory
449-
z.Spec.ResourceLimits.CPU = p.Spec.Size.CPU
450-
z.Spec.ResourceLimits.Memory = p.Spec.Size.Memory
410+
// In the code, zalando's `MaintenanceWindows` is a `struct`, but in the CRD
411+
// it's an array of strings, so we can only set its `Unstructured` contents
412+
// and deal with possible `nil`.
413+
jsonSpec["maintenanceWindows"] = p.Spec.Maintenance
414+
deleteIfEmpty(jsonSpec, "maintenanceWindows")
451415

452-
// todo: Check if the validation should be performed here.
453-
z.Spec.Volume.Size = p.Spec.Size.StorageSize
416+
// Delete unused fields
417+
deleteIfEmpty(jsonSpec, "clone")
418+
deleteIfEmpty(jsonSpec, "patroni") // if in use, deleteIfEmpty needs to consider the case of struct.
419+
deleteIfEmpty(jsonSpec, "podAnnotations")
420+
deleteIfEmpty(jsonSpec, "serviceAnnotations")
421+
deleteIfEmpty(jsonSpec, "standby")
422+
deleteIfEmpty(jsonSpec, "tls")
423+
deleteIfEmpty(jsonSpec, "users")
454424

455-
z.Spec.MaintenanceWindows = func() []zalando.MaintenanceWindow {
456-
if p.Spec.Maintenance == nil {
457-
return nil
458-
}
459-
isEvery := p.Spec.Maintenance.Weekday == All
460-
return []zalando.MaintenanceWindow{
461-
{
462-
Everyday: isEvery,
463-
Weekday: func() time.Weekday {
464-
if isEvery {
465-
return time.Weekday(0)
466-
}
467-
return time.Weekday(p.Spec.Maintenance.Weekday)
468-
}(),
469-
StartTime: p.Spec.Maintenance.TimeWindow.Start,
470-
EndTime: p.Spec.Maintenance.TimeWindow.End,
471-
},
472-
}
473-
}()
425+
jsonP, _ := jsonSpec["postgresql"].(map[string]interface{})
426+
deleteIfEmpty(jsonP, "parameters")
474427

475-
return z
428+
return &unstructured.Unstructured{
429+
Object: jsonZ,
430+
}, nil
476431
}
477432

478433
func (p *Postgres) ToZalandoPostgresqlMatchingLabels() client.MatchingLabels {
479-
return client.MatchingLabels{UIDLabelName: string(p.UID)}
434+
return client.MatchingLabels{
435+
ProjectIDLabelName: p.Spec.PartitionID,
436+
TenantLabelName: p.Spec.Tenant,
437+
UIDLabelName: string(p.UID),
438+
}
480439
}
481440

482441
func (p *Postgres) HasFinalizer(finalizerName string) bool {
@@ -510,6 +469,15 @@ func removeElem(ss []string, s string) (out []string) {
510469
return
511470
}
512471

472+
func deleteIfEmpty(json map[string]interface{}, key string) {
473+
i := json[key]
474+
475+
// interface has type and value. The chained function calls deal with nil value with type.
476+
if i == nil || reflect.ValueOf(json[key]).IsNil() {
477+
delete(json, key)
478+
}
479+
}
480+
513481
func init() {
514482
SchemeBuilder.Register(&Postgres{}, &PostgresList{})
515483
}

0 commit comments

Comments
 (0)