Skip to content
Merged
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
18 changes: 11 additions & 7 deletions cmd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,8 @@ func main() {
tlsOpts = append(tlsOpts, disableHTTP2)
}

operatorInstanceName := os.Getenv("OPERATOR_INSTANCE_NAME")

pgpool, err := pgxpool.New(context.Background(), os.Getenv("DATABASE_URL"))
if err != nil {
setupLog.Error(err, "Failed to connect PostgreSQL server: %s", err)
Expand Down Expand Up @@ -207,19 +209,21 @@ func main() {
}

if err = (&controller.PostgresDatabaseReconciler{
Client: mgr.GetClient(),
Scheme: mgr.GetScheme(),
PGPools: pgpools,
Client: mgr.GetClient(),
Scheme: mgr.GetScheme(),
PGPools: pgpools,
OperatorInstanceName: operatorInstanceName,
}).SetupWithManager(mgr); err != nil {
setupLog.Error(err, "unable to create controller", "controller", "PostgresDatabase")

os.Exit(1)
}
if err = (&controller.PostgresRoleReconciler{
Client: mgr.GetClient(),
Scheme: mgr.GetScheme(),
PGPools: pgpools,
CacheRolePasswords: cacheRolePasswords,
Client: mgr.GetClient(),
Scheme: mgr.GetScheme(),
PGPools: pgpools,
OperatorInstanceName: operatorInstanceName,
CacheRolePasswords: cacheRolePasswords,
}).SetupWithManager(mgr); err != nil {
setupLog.Error(err, "unable to create controller", "controller", "PostgresRole")
os.Exit(1)
Expand Down
9 changes: 8 additions & 1 deletion internal/controller/postgresdatabase_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (

managedpostgresoperatorhoppscalecomv1alpha1 "github.com/hoppscale/managed-postgres-operator/api/v1alpha1"
"github.com/hoppscale/managed-postgres-operator/internal/postgresql"
"github.com/hoppscale/managed-postgres-operator/internal/utils"
)

const PostgresDatabaseFinalizer = "postgresdatabase.managed-postgres-operator.hoppscale.com/finalizer"
Expand All @@ -25,7 +26,8 @@ type PostgresDatabaseReconciler struct {
Scheme *runtime.Scheme
logging logr.Logger

PGPools *postgresql.PGPools
PGPools *postgresql.PGPools
OperatorInstanceName string
}

// +kubebuilder:rbac:groups=managed-postgres-operator.hoppscale.com,resources=postgresdatabases,verbs=get;list;watch;create;update;patch;delete
Expand All @@ -43,6 +45,11 @@ func (r *PostgresDatabaseReconciler) Reconcile(ctx context.Context, req ctrl.Req
return ctrlFailResult, client.IgnoreNotFound(err)
}

// Skip reconcile if the resource is not managed by this operator
if !utils.IsManagedByOperatorInstance(resource.ObjectMeta.Annotations, r.OperatorInstanceName) {
return ctrlSuccessResult, nil
}

existingDatabase, err := postgresql.GetDatabase(r.PGPools.Default, resource.Spec.Name)
if err != nil {
return ctrlFailResult, fmt.Errorf("failed to retrieve database: %s", err)
Expand Down
81 changes: 81 additions & 0 deletions internal/controller/postgresdatabase_controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import (

managedpostgresoperatorhoppscalecomv1alpha1 "github.com/hoppscale/managed-postgres-operator/api/v1alpha1"
"github.com/hoppscale/managed-postgres-operator/internal/postgresql"
"github.com/hoppscale/managed-postgres-operator/internal/utils"
)

var _ = Describe("PostgresDatabase Controller", func() {
Expand Down Expand Up @@ -87,6 +88,86 @@ var _ = Describe("PostgresDatabase Controller", func() {
}
})

When("the resource is managed by the operator's instance", func() {
It("should continue to reconcile the resource and create the database", func() {
resource := &managedpostgresoperatorhoppscalecomv1alpha1.PostgresDatabase{}
Expect(k8sClient.Get(ctx, typeNamespacedName, resource)).To(Succeed())
resource.ObjectMeta.Annotations = map[string]string{
utils.OperatorInstanceAnnotationName: "foo",
}
Expect(k8sClient.Update(ctx, resource)).To(Succeed())

controllerReconciler := &PostgresDatabaseReconciler{
Client: k8sClient,
Scheme: k8sClient.Scheme(),
PGPools: pgpools,
OperatorInstanceName: "foo",
}

pgpoolsMock["default"].ExpectQuery(fmt.Sprintf("^%s$", regexp.QuoteMeta(postgresql.GetDatabaseSQLStatement))).
WithArgs("foo").
WillReturnRows(
pgxmock.NewRows([]string{
"datname",
"owner",
}),
)
pgpoolsMock["default"].ExpectExec(`CREATE DATABASE "foo"`).
WillReturnResult(pgxmock.NewResult("", 1))
pgpoolsMock["default"].ExpectExec(`ALTER DATABASE "foo" OWNER TO "foo_owner"`).
WillReturnResult(pgxmock.NewResult("", 1))
pgpoolsMock["foo"].ExpectQuery(`SELECT extname FROM pg_extension`).
WillReturnRows(
pgxmock.NewRows([]string{
"extname",
}).
AddRow(
"plpgsql",
),
)

_, err := controllerReconciler.Reconcile(ctx, reconcile.Request{
NamespacedName: typeNamespacedName,
})

Expect(err).NotTo(HaveOccurred())
for _, poolMock := range pgpoolsMock {
if err := poolMock.ExpectationsWereMet(); err != nil {
Fail(err.Error())
}
}
})
})

When("the resource is not managed by the operator's instance", func() {
It("should skip reconciliation", func() {
resource := &managedpostgresoperatorhoppscalecomv1alpha1.PostgresDatabase{}
Expect(k8sClient.Get(ctx, typeNamespacedName, resource)).To(Succeed())
resource.ObjectMeta.Annotations = map[string]string{
utils.OperatorInstanceAnnotationName: "bar",
}
Expect(k8sClient.Update(ctx, resource)).To(Succeed())

controllerReconciler := &PostgresDatabaseReconciler{
Client: k8sClient,
Scheme: k8sClient.Scheme(),
PGPools: pgpools,
OperatorInstanceName: "foo",
}

_, err := controllerReconciler.Reconcile(ctx, reconcile.Request{
NamespacedName: typeNamespacedName,
})

Expect(err).NotTo(HaveOccurred())
for _, poolMock := range pgpoolsMock {
if err := poolMock.ExpectationsWereMet(); err != nil {
Fail(err.Error())
}
}
})
})

When("the resource is created and no database exists", func() {
It("should reconcile the resource and create the database", func() {
By("Reconciling the created resource")
Expand Down
9 changes: 8 additions & 1 deletion internal/controller/postgresrole_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import (
"github.com/go-logr/logr"
managedpostgresoperatorhoppscalecomv1alpha1 "github.com/hoppscale/managed-postgres-operator/api/v1alpha1"
"github.com/hoppscale/managed-postgres-operator/internal/postgresql"
"github.com/hoppscale/managed-postgres-operator/internal/utils"
)

const PostgresRoleFinalizer = "postgresrole.managed-postgres-operator.hoppscale.com/finalizer"
Expand All @@ -42,7 +43,8 @@ type PostgresRoleReconciler struct {
Scheme *runtime.Scheme
logging logr.Logger

PGPools *postgresql.PGPools
PGPools *postgresql.PGPools
OperatorInstanceName string

CacheRolePasswords map[string]string
}
Expand All @@ -62,6 +64,11 @@ func (r *PostgresRoleReconciler) Reconcile(ctx context.Context, req ctrl.Request
return ctrlFailResult, client.IgnoreNotFound(err)
}

// Skip reconcile if the resource is not managed by this operator
if !utils.IsManagedByOperatorInstance(resource.ObjectMeta.Annotations, r.OperatorInstanceName) {
return ctrlSuccessResult, nil
}

rolePassword := ""

if resource.Spec.PasswordSecretName != "" {
Expand Down
72 changes: 72 additions & 0 deletions internal/controller/postgresrole_controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import (

managedpostgresoperatorhoppscalecomv1alpha1 "github.com/hoppscale/managed-postgres-operator/api/v1alpha1"
"github.com/hoppscale/managed-postgres-operator/internal/postgresql"
"github.com/hoppscale/managed-postgres-operator/internal/utils"
)

var _ = Describe("PostgresRole Controller", func() {
Expand Down Expand Up @@ -122,6 +123,77 @@ var _ = Describe("PostgresRole Controller", func() {
Expect(k8sClient.Delete(ctx, resource)).To(Succeed())
})

When("the resource is managed by the operator's instance", func() {
It("should continue to reconcile the resource and create the role", func() {
resource := &managedpostgresoperatorhoppscalecomv1alpha1.PostgresRole{}
Expect(k8sClient.Get(ctx, typeNamespacedName, resource)).To(Succeed())
resource.ObjectMeta.Annotations = map[string]string{
utils.OperatorInstanceAnnotationName: "foo",
}
Expect(k8sClient.Update(ctx, resource)).To(Succeed())
controllerReconciler := &PostgresRoleReconciler{
Client: k8sClient,
Scheme: k8sClient.Scheme(),
PGPools: pgpools,
OperatorInstanceName: "foo",
CacheRolePasswords: make(map[string]string),
}

pgpoolsMock["default"].ExpectQuery(fmt.Sprintf("^%s$", regexp.QuoteMeta(postgresql.GetRoleSQLStatement))).
WithArgs("foo").
WillReturnRows(
pgxmock.NewRows([]string{
"rolname",
"rolsuper",
"rolinherit",
"rolcreaterole",
"rolcreatedb",
"rolcanlogin",
"rolreplication",
"rolbypassrls",
}),
)
pgpoolsMock["default"].ExpectExec(fmt.Sprintf("^%s$", regexp.QuoteMeta(`CREATE ROLE "foo" WITH NOSUPERUSER NOINHERIT CREATEROLE CREATEDB NOLOGIN NOREPLICATION NOBYPASSRLS`))).
WillReturnResult(pgxmock.NewResult("foo", 1))
_, err := controllerReconciler.Reconcile(ctx, reconcile.Request{
NamespacedName: typeNamespacedName,
})

Expect(err).NotTo(HaveOccurred())
if err := pgpoolsMock["default"].ExpectationsWereMet(); err != nil {
Fail(err.Error())
}
})
})

When("the resource is not managed by the operator's instance", func() {
It("should skip reconciliation", func() {
resource := &managedpostgresoperatorhoppscalecomv1alpha1.PostgresRole{}
Expect(k8sClient.Get(ctx, typeNamespacedName, resource)).To(Succeed())
resource.ObjectMeta.Annotations = map[string]string{
utils.OperatorInstanceAnnotationName: "bar",
}
Expect(k8sClient.Update(ctx, resource)).To(Succeed())

controllerReconciler := &PostgresRoleReconciler{
Client: k8sClient,
Scheme: k8sClient.Scheme(),
PGPools: pgpools,
OperatorInstanceName: "foo",
CacheRolePasswords: make(map[string]string),
}

_, err := controllerReconciler.Reconcile(ctx, reconcile.Request{
NamespacedName: typeNamespacedName,
})

Expect(err).NotTo(HaveOccurred())
if err := pgpoolsMock["default"].ExpectationsWereMet(); err != nil {
Fail(err.Error())
}
})
})

When("the resource is created without password and no role exists", func() {
It("should reconcile the resource and create the role", func() {
controllerReconciler := &PostgresRoleReconciler{
Expand Down
14 changes: 14 additions & 0 deletions internal/utils/suite_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package utils

import (
"testing"

. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)

func TestControllers(t *testing.T) {
RegisterFailHandler(Fail)

RunSpecs(t, "Utils")
}
15 changes: 15 additions & 0 deletions internal/utils/utils.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package utils

const OperatorInstanceAnnotationName string = "managed-postgres-operator.hoppscale.com/instance"

func IsManagedByOperatorInstance(annotations map[string]string, instanceName string) bool {
if instance, ok := annotations[OperatorInstanceAnnotationName]; ok && instance == instanceName {
return true
}

if instanceName == "" {
return true
}

return false
}
58 changes: 58 additions & 0 deletions internal/utils/utils_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package utils

import (
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)

var _ = Describe("Utils functions", func() {
Context("Calling IsManagedByOperatorInstance", func() {
When("operator's instance is not defined and the resource has no instance annotation", func() {

It("should return true", func() {
resourceAnnotations := map[string]string{}

result := IsManagedByOperatorInstance(resourceAnnotations, "")

Expect(result).To(BeTrue())
})
})

When("operator's instance is defined and the resource has no instance annotation", func() {

It("should return false", func() {
resourceAnnotations := map[string]string{}

result := IsManagedByOperatorInstance(resourceAnnotations, "foo")

Expect(result).To(BeFalse())
})
})

When("operator's instance is defined and the resource has another instance annotation", func() {

It("should return false", func() {
resourceAnnotations := map[string]string{
OperatorInstanceAnnotationName: "bar",
}

result := IsManagedByOperatorInstance(resourceAnnotations, "foo")

Expect(result).To(BeFalse())
})
})

When("operator's instance is defined and the resource has the same another instance annotation", func() {

It("should return false", func() {
resourceAnnotations := map[string]string{
OperatorInstanceAnnotationName: "foo",
}

result := IsManagedByOperatorInstance(resourceAnnotations, "foo")

Expect(result).To(BeTrue())
})
})
})
})
Loading