From 48680b369d46d45ae274305a62f9349f98d69331 Mon Sep 17 00:00:00 2001 From: Balaji Jeevan <32107263+bjeevan-ib@users.noreply.github.com> Date: Wed, 9 Aug 2023 08:20:22 -0700 Subject: [PATCH] dbc cannot be deleted while rds migration is in progress (#164) --- controllers/databaseclaim_controller.go | 19 ++++- pkg/dbclient/client.go | 4 +- pkg/pgctl/test/pgctl_test.sql | 108 ++++++++++++++++++------ 3 files changed, 98 insertions(+), 33 deletions(-) diff --git a/controllers/databaseclaim_controller.go b/controllers/databaseclaim_controller.go index cbe912b5..efdbbe91 100644 --- a/controllers/databaseclaim_controller.go +++ b/controllers/databaseclaim_controller.go @@ -286,6 +286,17 @@ func (r *DatabaseClaimReconciler) Reconcile(ctx context.Context, req ctrl.Reques if !dbClaim.ObjectMeta.DeletionTimestamp.IsZero() { // The object is being deleted if controllerutil.ContainsFinalizer(&dbClaim, dbFinalizerName) { + // check if the claim is in the middle of rds migration, if so, wait for it to complete + if dbClaim.Status.MigrationState != "" && dbClaim.Status.MigrationState != pgctl.S_Completed.String() { + logr.Info("migration is in progress. object cannot be deleted") + dbClaim.Status.Error = "dbc cannot be deleted while migration is in progress" + err := r.Client.Status().Update(ctx, &dbClaim) + if err != nil { + logr.Error(err, "unable to update status. ignoring this error") + } + //ignore delete request, continue to process rds migration + return r.updateStatus(ctx, &dbClaim) + } // our finalizer is present, so lets handle any external dependency if err := r.deleteExternalResources(ctx, &dbClaim); err != nil { // if fail to delete the external dependency here, return with error @@ -2123,8 +2134,12 @@ func (r *DatabaseClaimReconciler) manageSuccess(ctx context.Context, dbClaim *pe } return ctrl.Result{}, err } - - return ctrl.Result{RequeueAfter: r.getPasswordRotationTime()}, nil + //if object is getting deleted then call requeue immediately + if !dbClaim.ObjectMeta.DeletionTimestamp.IsZero() { + return ctrl.Result{Requeue: true}, nil + } else { + return ctrl.Result{RequeueAfter: r.getPasswordRotationTime()}, nil + } } func GetDBName(dbClaim *persistancev1.DatabaseClaim) string { diff --git a/pkg/dbclient/client.go b/pkg/dbclient/client.go index c31a9280..5b40ccc1 100644 --- a/pkg/dbclient/client.go +++ b/pkg/dbclient/client.go @@ -184,7 +184,6 @@ func (pc *client) CreateGroup(dbName, rolename string) (bool, error) { ` grantSchemaPrivileges := ` GRANT ALL ON SCHEMA public TO %s; - GRANT ALL ON ALL TABLES IN SCHEMA public TO %s; ` _, err = pc.DB.Exec(fmt.Sprintf("CREATE ROLE %s WITH NOLOGIN", pq.QuoteIdentifier(rolename))) if err != nil { @@ -209,8 +208,7 @@ func (pc *client) CreateGroup(dbName, rolename string) (bool, error) { } defer db.Close() //grant schema privileges - _, err = db.Exec(fmt.Sprintf(grantSchemaPrivileges, pq.QuoteIdentifier(rolename), - pq.QuoteIdentifier(rolename))) + _, err = db.Exec(fmt.Sprintf(grantSchemaPrivileges, pq.QuoteIdentifier(rolename))) if err != nil { pc.log.Error(err, "could not set schema privileges to role "+rolename) metrics.UsersCreatedErrors.WithLabelValues("grant error").Inc() diff --git a/pkg/pgctl/test/pgctl_test.sql b/pkg/pgctl/test/pgctl_test.sql index 8ad195e5..ac92e458 100644 --- a/pkg/pgctl/test/pgctl_test.sql +++ b/pkg/pgctl/test/pgctl_test.sql @@ -49,7 +49,7 @@ CREATE EXTENSION IF NOT EXISTS "hstore"; DO $$ BEGIN - IF NOT EXISTS ( + IF NOT EXISTS( SELECT * FROM @@ -63,7 +63,7 @@ $$; DO $$ BEGIN - IF NOT EXISTS ( + IF NOT EXISTS( SELECT * FROM @@ -81,7 +81,7 @@ CREATE TYPE blox_text AS ( f1 text ); -CREATE FUNCTION fn_get_val (bigint) +CREATE FUNCTION fn_get_val(bigint) RETURNS blox_text AS $$ SELECT @@ -89,26 +89,26 @@ CREATE FUNCTION fn_get_val (bigint) $$ LANGUAGE SQL; -CREATE TABLE tab_1 ( +CREATE TABLE tab_1( id int PRIMARY KEY, name blox_text ); -CREATE UNIQUE INDEX ind_1_tab_1 ON tab_1 (id); +CREATE UNIQUE INDEX ind_1_tab_1 ON tab_1(id); -CREATE INDEX ind_2_tab_1 ON tab_1 (name); +CREATE INDEX ind_2_tab_1 ON tab_1(name); -CREATE TABLE tab_1_audits ( +CREATE TABLE tab_1_audits( book_id int NOT NULL, entry_date text NOT NULL ); -CREATE OR REPLACE FUNCTION auditfunc () +CREATE OR REPLACE FUNCTION auditfunc() RETURNS TRIGGER AS $my_table$ BEGIN - INSERT INTO tab_1_audits (book_id, entry_date) - VALUES (NEW.ID, CURRENT_TIMESTAMP); + INSERT INTO tab_1_audits(book_id, entry_date) + VALUES(NEW.ID, CURRENT_TIMESTAMP); RETURN NEW; END; $my_table$ @@ -117,23 +117,23 @@ LANGUAGE plpgsql; CREATE TRIGGER price_trigger AFTER INSERT ON tab_1 FOR EACH ROW - EXECUTE PROCEDURE auditfunc (); + EXECUTE PROCEDURE auditfunc(); INSERT INTO tab_1 - VALUES (generate_series(1, :end), fn_get_val (nextval('tab_1_seq'))); + VALUES (generate_series(1, :end), fn_get_val(nextval('tab_1_seq'))); CREATE SEQUENCE IF NOT EXISTS tab_2_seq; -CREATE TABLE tab_2 ( +CREATE TABLE tab_2( id int PRIMARY KEY, name blox_text, - tab_1_ref int REFERENCES tab_1 (id) + tab_1_ref int REFERENCES tab_1(id) ); -CREATE INDEX ind_1_tab_2 ON tab_2 (name, tab_1_ref); +CREATE INDEX ind_1_tab_2 ON tab_2(name, tab_1_ref); INSERT INTO tab_2 - VALUES (generate_series(1, :end), fn_get_val (nextval('tab_2_seq')), generate_series(1, :end)); + VALUES (generate_series(1, :end), fn_get_val(nextval('tab_2_seq')), generate_series(1, :end)); CREATE OR REPLACE VIEW vw_tab_1_2 AS SELECT @@ -161,18 +161,18 @@ WHERE -- Trigger CREATE SEQUENCE IF NOT EXISTS tab_3_seq; -CREATE TABLE tab_3 ( +CREATE TABLE tab_3( id int PRIMARY KEY, name blox_text, - tab_2_ref int REFERENCES tab_1 (id) + tab_2_ref int REFERENCES tab_1(id) ); -CREATE INDEX ind_1_tab_3 ON tab_3 (name, tab_2_ref); +CREATE INDEX ind_1_tab_3 ON tab_3(name, tab_2_ref); INSERT INTO tab_3 - VALUES (generate_series(1, :end), fn_get_val (nextval('tab_3_seq')), generate_series(1, :end)); + VALUES (generate_series(1, :end), fn_get_val(nextval('tab_3_seq')), generate_series(1, :end)); -CREATE TABLE tab_4 ( +CREATE TABLE tab_4( id int PRIMARY KEY, name varchar ); @@ -180,7 +180,7 @@ CREATE TABLE tab_4 ( INSERT INTO tab_4 VALUES (generate_series(1, :end), 'data' || generate_series(1, :end)); -CREATE TABLE tab_5 ( +CREATE TABLE tab_5( id int PRIMARY KEY, name varchar ); @@ -188,7 +188,7 @@ CREATE TABLE tab_5 ( INSERT INTO tab_5 VALUES (generate_series(1, :end), 'data' || generate_series(1, :end)); -CREATE TABLE tab_6 ( +CREATE TABLE tab_6( id int PRIMARY KEY, name varchar ); @@ -196,7 +196,7 @@ CREATE TABLE tab_6 ( INSERT INTO tab_6 VALUES (generate_series(1, :end), 'data' || generate_series(1, :end)); -CREATE TABLE tab_7 ( +CREATE TABLE tab_7( id int PRIMARY KEY, name varchar ); @@ -204,7 +204,7 @@ CREATE TABLE tab_7 ( INSERT INTO tab_7 VALUES (generate_series(1, :end), 'data' || generate_series(1, :end)); -CREATE TABLE tab_8 ( +CREATE TABLE tab_8( id int PRIMARY KEY, name varchar ); @@ -212,7 +212,7 @@ CREATE TABLE tab_8 ( INSERT INTO tab_8 VALUES (generate_series(1, :end), 'data' || generate_series(1, :end)); -CREATE TABLE tab_9 ( +CREATE TABLE tab_9( id int PRIMARY KEY, name varchar ); @@ -220,7 +220,7 @@ CREATE TABLE tab_9 ( INSERT INTO tab_9 VALUES (generate_series(1, :end), 'data' || generate_series(1, :end)); -CREATE TABLE tab_10 ( +CREATE TABLE tab_10( id int PRIMARY KEY, name varchar ); @@ -228,7 +228,7 @@ CREATE TABLE tab_10 ( INSERT INTO tab_10 VALUES (generate_series(1, :end), 'data' || generate_series(1, :end)); -CREATE OR REPLACE FUNCTION count_rows (schema text, tablename text) +CREATE OR REPLACE FUNCTION count_rows(schema text, tablename text) RETURNS integer AS $body$ DECLARE @@ -244,6 +244,58 @@ LANGUAGE plpgsql; GRANT SELECT ON tab_1 TO appuser; +--OPERATOR, OPERATOR CLASS, OPERATOR FAMILY and FUNCTION +CREATE DOMAIN soa_serial_number AS BIGINT +-- serial value should be in [0..2^32-1] range according to RFC-1982 +CONSTRAINT soa_serial_number_check CHECK (VALUE >= 0 + AND VALUE <= 4294967295); + +CREATE OR REPLACE FUNCTION soa_serial_number_le(soa_serial_number, soa_serial_number) + RETURNS boolean + AS $$ +BEGIN + --serial value s1 is said to be less than s2 if, and only if, s1 is not equal to s2, and + --(i1 < i2 and i2 - i1 < 2^(32 - 1)) or + --(i1 > i2 and i1 - i2 > 2^(32 - 1)) + RETURN $1::bigint <> $2::bigint + AND($1::bigint < $2::bigint + AND($2::bigint - $1::bigint) < 2147483648) + OR($1::bigint > $2::bigint + AND($1::bigint - $2::bigint) > 2147483648); +END; +$$ IMMUTABLE +LANGUAGE plpgsql; + +CREATE OPERATOR <( + LEFTARG = soa_serial_number, + RIGHTARG = soa_serial_number, + PROCEDURE = soa_serial_number_le +); + +CREATE OR REPLACE FUNCTION soa_serial_number_cmp(soa_serial_number, soa_serial_number) + RETURNS integer + AS $$ +BEGIN + RETURN CASE WHEN $1::bigint = $2::bigint THEN + 0 + WHEN($1::bigint < $2::bigint + AND($2::bigint - $1::bigint) < 2147483648) + OR($1::bigint > $2::bigint + AND($1::bigint - $2::bigint) > 2147483648) THEN + -1 + ELSE + 1 + END; +END; +$$ IMMUTABLE +LANGUAGE plpgsql; + +CREATE OPERATOR CLASS soa_serial_number_ops DEFAULT FOR TYPE soa_serial_number + USING btree AS + OPERATOR 1 <, + FUNCTION 1 soa_serial_number_cmp( soa_serial_number, soa_serial_number +); + --CREATE publication mypub FOR ALL tables; /*create subscription mysub connection 'dbname=pub host=localhost user=bjeevan port=5433' publication mypub;