Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 11 additions & 3 deletions api/v1alpha1/postgresuser_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,16 @@ import (

// PostgresUserSpec defines the desired state of PostgresUser
type PostgresUserSpec struct {
Role string `json:"role"`
Database string `json:"database"`
// Name of the PostgresRole this user will be associated with
Role string `json:"role"`
// Name of the PostgresDatabase this user will be related to
Database string `json:"database"`
// Name of the secret to create with user credentials
SecretName string `json:"secretName"`
// +optional
SecretTemplate map[string]string `json:"secretTemplate,omitempty"` // key-value, where key is secret field, value is go template
// +optional
// List of privileges to grant to this user
Privileges string `json:"privileges"`
// +optional
AWS *PostgresUserAWSSpec `json:"aws,omitempty"`
Expand All @@ -27,6 +31,8 @@ type PostgresUserSpec struct {
// PostgresUserAWSSpec encapsulates AWS specific configuration toggles.
type PostgresUserAWSSpec struct {
// +optional
// +kubebuilder:default=false
// Enable IAM authentication for this user (PostgreSQL on AWS RDS only)
EnableIamAuth bool `json:"enableIamAuth,omitempty"`
}

Expand All @@ -37,7 +43,9 @@ type PostgresUserStatus struct {
PostgresLogin string `json:"postgresLogin"`
PostgresGroup string `json:"postgresGroup"`
DatabaseName string `json:"databaseName"`
EnableIamAuth bool `json:"enableIamAuth"`
// Reflects whether IAM authentication is enabled for this user.
// +optional
EnableIamAuth bool `json:"enableIamAuth"`
}

// +kubebuilder:object:root=true
Expand Down
20 changes: 20 additions & 0 deletions api/v1alpha1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

45 changes: 24 additions & 21 deletions cmd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
clientgoscheme "k8s.io/client-go/kubernetes/scheme"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/healthz"
logf "sigs.k8s.io/controller-runtime/pkg/log"
"sigs.k8s.io/controller-runtime/pkg/log/zap"
"sigs.k8s.io/controller-runtime/pkg/metrics/filters"
metricsserver "sigs.k8s.io/controller-runtime/pkg/metrics/server"
Expand All @@ -28,10 +29,7 @@ import (
// +kubebuilder:scaffold:imports
)

var (
scheme = runtime.NewScheme()
setupLog = ctrl.Log.WithName("setup")
)
var scheme = runtime.NewScheme()

func init() {
utilruntime.Must(clientgoscheme.AddToScheme(scheme))
Expand All @@ -57,22 +55,20 @@ func main() {
"If set, the metrics endpoint is served securely via HTTPS. Use --metrics-secure=false to use HTTP instead.")
flag.BoolVar(&enableHTTP2, "enable-http2", true,
"If set, HTTP/2 will be enabled for the metrics and webhook servers")
opts := zap.Options{
Development: true,
}
opts := zap.Options{}
opts.BindFlags(flag.CommandLine)
flag.Parse()

ctrl.SetLogger(zap.New(zap.UseFlagOptions(&opts)))

logger := zap.New(zap.UseFlagOptions(&opts))
logf.SetLogger(logger)
// if the enable-http2 flag is false (the default), http/2 should be disabled
// due to its vulnerabilities. More specifically, disabling http/2 will
// prevent from being vulnerable to the HTTP/2 Stream Cancellation and
// Rapid Reset CVEs. For more information see:
// - https://github.com/advisories/GHSA-qppj-fm5r-hxr3
// - https://github.com/advisories/GHSA-4374-p667-p6c8
disableHTTP2 := func(c *tls.Config) {
setupLog.Info("disabling http/2")
logger.Info("disabling http/2")
c.NextProtos = []string{"http/1.1"}
}

Expand Down Expand Up @@ -111,9 +107,9 @@ func main() {
cfg := config.Get()
lockName := "lock"
if cfg.AnnotationFilter == "" {
setupLog.Info("No POSTGRES_INSTANCE set, this instance will only process CRs without an annotation")
logger.Info("No POSTGRES_INSTANCE set, this instance will only process CRs without an annotation")
} else {
setupLog.Info("POSTGRES_INSTANCE is set, this instance will only process CRs with the correct annotation", "annotation", cfg.AnnotationFilter)
logger.Info("POSTGRES_INSTANCE is set, this instance will only process CRs with the correct annotation", "annotation", cfg.AnnotationFilter)
lockName += "-" + cfg.AnnotationFilter
}
cacheOpts := cache.Options{}
Expand Down Expand Up @@ -145,38 +141,45 @@ func main() {
// LeaderElectionReleaseOnCancel: true,
})
if err != nil {
setupLog.Error(err, "unable to start manager")
logger.Error(err, "unable to start manager")
os.Exit(1)
}

pg, err := postgres.NewPG(cfg, ctrl.Log)
pg, err := postgres.NewPG(cfg, logger)
if err != nil {
setupLog.Error(err, "DB-Connection failed", "cfg", cfg)
// Avoid logging sensitive information like PostgresPass
logger.Error(err, "DB-Connection failed", "cfg", map[string]any{
"Host": cfg.PostgresHost,
"User": cfg.PostgresUser,
"UriArgs": cfg.PostgresUriArgs,
"CloudProvider": cfg.CloudProvider,
"DefaultDatabase": cfg.PostgresDefaultDb,
})
os.Exit(1)
}

if err = (controller.NewPostgresReconciler(mgr, cfg, pg)).SetupWithManager(mgr); err != nil {
setupLog.Error(err, "unable to create controller", "controller", "Postgres")
logger.Error(err, "unable to create controller", "controller", "Postgres")
os.Exit(1)
}
if err = (controller.NewPostgresUserReconciler(mgr, cfg, pg)).SetupWithManager(mgr); err != nil {
setupLog.Error(err, "unable to create controller", "controller", "PostgresUser")
logger.Error(err, "unable to create controller", "controller", "PostgresUser")
os.Exit(1)
}
// +kubebuilder:scaffold:builder

if err := mgr.AddHealthzCheck("healthz", healthz.Ping); err != nil {
setupLog.Error(err, "unable to set up health check")
logger.Error(err, "unable to set up health check")
os.Exit(1)
}
if err := mgr.AddReadyzCheck("readyz", healthz.Ping); err != nil {
setupLog.Error(err, "unable to set up ready check")
logger.Error(err, "unable to set up ready check")
os.Exit(1)
}

setupLog.Info("starting manager")
logger.Info("starting manager")
if err := mgr.Start(ctrl.SetupSignalHandler()); err != nil {
setupLog.Error(err, "problem running manager")
logger.Error(err, "problem running manager")
os.Exit(1)
}
}
17 changes: 11 additions & 6 deletions config/crd/bases/db.movetokube.com_postgres.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,19 @@ spec:
description: Postgres is the Schema for the postgres API
properties:
apiVersion:
description: 'APIVersion defines the versioned schema of this representation
of an object. Servers should convert recognized schemas to the latest
internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
description: |-
APIVersion defines the versioned schema of this representation of an object.
Servers should convert recognized schemas to the latest internal value, and
may reject unrecognized values.
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources
type: string
kind:
description: 'Kind is a string value representing the REST resource this
object represents. Servers may infer this from the endpoint the client
submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
description: |-
Kind is a string value representing the REST resource this object represents.
Servers may infer this from the endpoint the client submits requests to.
Cannot be updated.
In CamelCase.
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
type: string
metadata:
type: object
Expand Down
36 changes: 23 additions & 13 deletions config/crd/bases/db.movetokube.com_postgresusers.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,19 @@ spec:
description: PostgresUser is the Schema for the postgresusers API
properties:
apiVersion:
description: 'APIVersion defines the versioned schema of this representation
of an object. Servers should convert recognized schemas to the latest
internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
description: |-
APIVersion defines the versioned schema of this representation of an object.
Servers should convert recognized schemas to the latest internal value, and
may reject unrecognized values.
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources
type: string
kind:
description: 'Kind is a string value representing the REST resource this
object represents. Servers may infer this from the endpoint the client
submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
description: |-
Kind is a string value representing the REST resource this object represents.
Servers may infer this from the endpoint the client submits requests to.
Cannot be updated.
In CamelCase.
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
type: string
metadata:
type: object
Expand All @@ -39,15 +44,18 @@ spec:
type: string
type: object
aws:
description: AWS specific settings for this user.
description: PostgresUserAWSSpec encapsulates AWS specific configuration
toggles.
properties:
enableIamAuth:
description: Enable IAM authentication for this user (PostgreSQL on AWS RDS only)
default: false
description: Enable IAM authentication for this user (PostgreSQL
on AWS RDS only)
type: boolean
type: object
database:
description: Name of the PostgresDatabase this user will be related to
description: Name of the PostgresDatabase this user will be related
to
type: string
labels:
additionalProperties:
Expand All @@ -57,7 +65,8 @@ spec:
description: List of privileges to grant to this user
type: string
role:
description: Name of the PostgresRole this user will be associated with
description: Name of the PostgresRole this user will be associated
with
type: string
secretName:
description: Name of the secret to create with user credentials
Expand All @@ -74,11 +83,12 @@ spec:
status:
description: PostgresUserStatus defines the observed state of PostgresUser
properties:
enableIamAuth:
description: Reflects whether IAM authentication is enabled for this user.
type: boolean
databaseName:
type: string
enableIamAuth:
description: Reflects whether IAM authentication is enabled for this
user.
type: boolean
postgresGroup:
type: string
postgresLogin:
Expand Down
20 changes: 11 additions & 9 deletions internal/controller/postgres_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,27 +82,27 @@ func (r *PostgresReconciler) Reconcile(ctx context.Context, req ctrl.Request) (c
if !instance.GetDeletionTimestamp().IsZero() {
if r.shouldDropDB(ctx, instance, reqLogger) && instance.Status.Succeeded {
if instance.Status.Roles.Owner != "" {
err := r.pg.DropRole(instance.Status.Roles.Owner, r.pg.GetUser(), instance.Spec.Database, reqLogger)
err := r.pg.DropRole(instance.Status.Roles.Owner, r.pg.GetUser(), instance.Spec.Database)
if err != nil {
return ctrl.Result{}, err
}
instance.Status.Roles.Owner = ""
}
if instance.Status.Roles.Reader != "" {
err = r.pg.DropRole(instance.Status.Roles.Reader, r.pg.GetUser(), instance.Spec.Database, reqLogger)
err = r.pg.DropRole(instance.Status.Roles.Reader, r.pg.GetUser(), instance.Spec.Database)
if err != nil {
return ctrl.Result{}, err
}
instance.Status.Roles.Reader = ""
}
if instance.Status.Roles.Writer != "" {
err = r.pg.DropRole(instance.Status.Roles.Writer, r.pg.GetUser(), instance.Spec.Database, reqLogger)
err = r.pg.DropRole(instance.Status.Roles.Writer, r.pg.GetUser(), instance.Spec.Database)
if err != nil {
return ctrl.Result{}, err
}
instance.Status.Roles.Writer = ""
}
err = r.pg.DropDatabase(instance.Spec.Database, reqLogger)
err = r.pg.DropDatabase(instance.Spec.Database)
if err != nil {
return ctrl.Result{}, err
}
Expand Down Expand Up @@ -196,7 +196,7 @@ func (r *PostgresReconciler) Reconcile(ctx context.Context, req ctrl.Request) (c
continue
}
// Execute create extension SQL statement
err = r.pg.CreateExtension(instance.Spec.Database, extension, reqLogger)
err = r.pg.CreateExtension(instance.Spec.Database, extension)
if err != nil {
reqLogger.Error(err, fmt.Sprintf("Could not add extensions %s", extension))
continue
Expand Down Expand Up @@ -224,7 +224,7 @@ func (r *PostgresReconciler) Reconcile(ctx context.Context, req ctrl.Request) (c
}

// Create schema
err = r.pg.CreateSchema(database, owner, schema, reqLogger)
err = r.pg.CreateSchema(database, owner, schema)
if err != nil {
reqLogger.Error(err, fmt.Sprintf("Could not create schema %s", schema))
continue
Expand All @@ -243,7 +243,7 @@ func (r *PostgresReconciler) Reconcile(ctx context.Context, req ctrl.Request) (c
Privs: readerPrivs,
CreateSchema: false,
}
err = r.pg.SetSchemaPrivileges(schemaPrivilegesReader, reqLogger)
err = r.pg.SetSchemaPrivileges(schemaPrivilegesReader)
if err != nil {
reqLogger.Error(err, fmt.Sprintf("Could not give %s permissions \"%s\"", reader, readerPrivs))
continue
Expand All @@ -257,7 +257,7 @@ func (r *PostgresReconciler) Reconcile(ctx context.Context, req ctrl.Request) (c
FunctionPrivs: writerFunctionPrivs,
CreateSchema: true,
}
err = r.pg.SetSchemaPrivileges(schemaPrivilegesWriter, reqLogger)
err = r.pg.SetSchemaPrivileges(schemaPrivilegesWriter)
if err != nil {
reqLogger.Error(err, fmt.Sprintf("Could not give %s permissions \"%s\", sequence privileges \"%s\", and function privileges \"%s\"", writer, writerPrivs, writerSequencePrivs, writerFunctionPrivs))
continue
Expand All @@ -271,7 +271,7 @@ func (r *PostgresReconciler) Reconcile(ctx context.Context, req ctrl.Request) (c
FunctionPrivs: ownerFunctionPrivs,
CreateSchema: true,
}
err = r.pg.SetSchemaPrivileges(schemaPrivilegesOwner, reqLogger)
err = r.pg.SetSchemaPrivileges(schemaPrivilegesOwner)
if err != nil {
reqLogger.Error(err, fmt.Sprintf("Could not give %s permissions \"%s\", sequence privileges \"%s\", and function privileges \"%s\"", owner, ownerPrivs, ownerSequencePrivs, ownerFunctionPrivs))
continue
Expand All @@ -293,13 +293,15 @@ func (r *PostgresReconciler) Reconcile(ctx context.Context, req ctrl.Request) (c
reqLogger.Info("Reconciling done")
return ctrl.Result{}, nil
}

func (r *PostgresReconciler) addFinalizer(reqLogger logr.Logger, m *dbv1alpha1.Postgres) error {
if len(m.GetFinalizers()) < 1 && m.GetDeletionTimestamp() == nil {
reqLogger.Info("adding Finalizer for Postgres")
m.SetFinalizers([]string{"finalizer.db.movetokube.com"})
}
return nil
}

func (r *PostgresReconciler) requeue(cr *dbv1alpha1.Postgres, reason error) (ctrl.Result, error) {
cr.Status.Succeeded = false
return ctrl.Result{}, reason
Expand Down
Loading