-
Notifications
You must be signed in to change notification settings - Fork 970
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fdc brownfield setup #8150
base: master
Are you sure you want to change the base?
Fdc brownfield setup #8150
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great work! Excited to have brown field support very soon!
@@ -200,18 +201,6 @@ export async function setupIAMUsers( | |||
); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we update jsdoc now that permissions part are moved out?
Like this clean up.
ELSE FALSE | ||
END AS schema_exists, | ||
(SELECT pg_get_userbyid(nspowner) FROM pg_namespace WHERE nspname = '${schema}') AS schema_owner;`, | ||
], |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
SELECT pg_get_userbyid(nspowner) AS schema_owner
FROM pg_namespace
WHERE nspname = '${schema}'
Would this work for the purpose? A bit simpler
owner: row.tableowner, | ||
}; | ||
}); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
After tables
, the below lines are to find setupSchema
.
nit: Can we extract a helper caller getupSetupSchema
? To make it clear that other fields are the same in 3 branches below.
@@ -237,10 +237,19 @@ export async function grantRoleToUserInSchema(options: Options, schema: Schema) | |||
} | |||
|
|||
// Run the database roles setup. This should be idempotent. | |||
await setupIAMUsers(instanceId, databaseId, options); | |||
const schemaInfo = await getSchemaMetaData(instanceId, databaseId, DEFAULT_SCHEMA, options); | |||
if (schemaInfo.setupStatus === SchemaSetupStatus.GreenField) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The condition here looks wrong.
Green field database would always return early without grant any permissions.
`ALTER DEFAULT PRIVILEGES IN SCHEMA "${schema}" GRANT SELECT, USAGE ON SEQUENCES TO "${firebaseReaderRole}";`, | ||
`ALTER DEFAULT PRIVILEGES IN SCHEMA "${schema}" GRANT EXECUTE ON FUNCTIONS TO "${firebaseReaderRole}"`, | ||
`SET ROLE = cloudsqlsuperuser`, | ||
function defaultPermissions(databaseId: string, schema: string, ownerRole: string) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice cleanup.
Would it make sense to port those changes to backend code as well to keep them reasonably aligned?
I know they should do the same thing for green field.
To make this sync easier in the future, it might make sense to split caller logics to separate files.
We can split logics that aren't copied to the backend permissions.go into permissions_setup.ts
. It can have getSchemaMetaData
, greenFieldSchemaSetup
, brownfieldSqlSetup
, etc.
}; | ||
} | ||
|
||
export async function getUserAccessLevel( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it used?
// Step 1: Grant firebasesuperuser access to the original owner | ||
const uniqueTablesOwners = [...new Set(schemaInfo.tables.map((t) => t.owner))]; | ||
const grants = uniqueTablesOwners.map((owner) => `GRANT ${owner} TO ${FIREBASE_SUPER_USER}`); | ||
await executeSqlCmdsAsSuperUser(options, instanceId, databaseId, grants, silent); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: any reason, this couldn't be executed in the same executeSqlCmdsAsSuperUser
as below.
Each executeSqlCmdsAsSuperUser
would call UpsertUser with random password. Adds a few seconds of delay.
return true; | ||
} | ||
} else { | ||
logger.info(`Detected schema "${schema}" isn't fully setup or setup as brownfield project.`); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: Shall we log schemaInfo.setupStatus
here to help debugging?
logger.info( | ||
clc.yellow( | ||
"Setting up database in brownfield mode.\n" + | ||
"Note: SQL migrations can't be done through FDC in this mode.", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done through clc.bold("firebase dataconnect:sql:migrate")
Be specific about what command we are referring to.
// until we confirm stability it's ok to run it on every migration by admin user. | ||
if (userIsCSQLAdmin) { | ||
await setupIAMUsers(instanceId, databaseId, options); | ||
const schemaInfo = await getSchemaMetaData(instanceId, databaseId, DEFAULT_SCHEMA, options); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we check setupStatus
the first thing in handleIncompatibleSchemaError
?
When handleIncompatibleSchemaError
is called, we have detected some diffs in sql:migrate
. We should surface any permission setup error before any SQL migration specific errors.
Description
Support brownfield database setup. When we detect the database schema already have other tables (brownfield or greenfield but then a table was added manually) we ask the user if they want to setup the database as greenfield.
1- They say yes -> We transfer schema and table ownerships to firebase roles, and grant previous roles writer access only.
2- They say no -> We setup the database in brownfield mode which means that deployed connectors have writer permissions but SQL migration will stop working through cli.
Note: We should find a way to automatically turn on compat mode when database is setup as greenfield from brownfield to avoid accidentally dropping important tables.
Note 2: I think I can improve the brownfield experience by allowing firebase deploy to run if the database diff isn't incompatible (say only drops tables and remove fields). Right now sql:migrate will fail if database is found as brownfield which will fail firebase deploy.
Scenarios Tested
Fresh greenfield setup
Setup greenfield database from scratch using firebase deploy/sql:migrate -> no change
Brownfield to greenfield setup
I created one database with one table (not owned by firebase) and then user agrees to set it up as greenfield.
Brownfield setup test
I create a new database with one table not owned by firebase and firebase isn't setup.
User runs
firebase deploy
, we then detect it's brownfield and ask the user if they want to setup the database as greenfield. -> User chooses to keep project as brownfield which sets up firebase writer/reader roles and p4sa access but deployment fails.Confirming the user which has firebase writer role now can edit and select but can't create new tables.
BrownField Default Permissions Test
Finally, I test default permissions when database is setup. Firebase writer/reader should be able to see and edit tables created by other users in the schema ("public") after the setup is done in brownfield mode.
Additional flags test
Trying
--force
when migrating a brownfield database fails.Sample Commands
firebase dataconnect:sql:setup