Allow schema-push to all env with Flyway (#309)

* Make Flyway schema task work with prod and sandbox

Also renamed the 'superuser' role to 'admin' since
we do not own super user in Cloud SQL.

* Allow pushing schema to all env with Flyway

Desktop schema push to production is needed in the short term.
Long-termly we need to decide if this should be kept for glass
breaking

Schema push to sandbox and production requires interactiveconfirmation.

Also fixed a typo in initialize_roles.sql.
This commit is contained in:
Weimin Yu 2019-10-10 16:32:21 -04:00 committed by GitHub
parent ce480a5191
commit c3e3a1353b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 47 additions and 11 deletions

View file

@ -12,18 +12,45 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
import org.gradle.api.internal.tasks.userinput.UserInputHandler
plugins { plugins {
id "org.flywaydb.flyway" version "6.0.1" id "org.flywaydb.flyway" version "6.0.1"
id 'maven-publish' id 'maven-publish'
} }
ext { ext {
Set restrictedDbEnv =
[ 'sandbox', 'production' ].asUnmodifiable()
Set allDbEnv =
[ 'alpha', 'crash' ].plus(restrictedDbEnv).asUnmodifiable()
def dbServerProperty = 'dbServer' def dbServerProperty = 'dbServer'
def dbNameProperty = 'dbName' def dbNameProperty = 'dbName'
def dbServer = findProperty(dbServerProperty) def dbServer = findProperty(dbServerProperty).toString().toLowerCase()
def dbName = findProperty(dbNameProperty) def dbName = findProperty(dbNameProperty)
reconfirmRestrictedDbEnv = {
if (!restrictedDbEnv.contains(dbServer)) {
return
}
// For restricted environments, ask the user to type again to confirm.
// The following statement uses Gradle internal API to get around the
// missing console bug when Gradle Daemon is in use. Another option is
// to use the ant.input task. For details please refer to
// https://github.com/gradle/gradle/issues/1251.
def dbServerAgain = services.get(UserInputHandler.class).askQuestion(
"""\
Are you sure? Operating on ${dbServer} from desktop is unsafe.
Please type '${dbServer}' again to proceed: """.stripIndent(),
'').trim()
if (dbServer != dbServerAgain) {
throw new RuntimeException(
"Failed to confirm for restricted database environment. Operation aborted.")
}
}
getAccessInfoByHostPort = { hostAndPort -> getAccessInfoByHostPort = { hostAndPort ->
return [ return [
url: "jdbc:postgresql://${hostAndPort}/${dbName}", url: "jdbc:postgresql://${hostAndPort}/${dbName}",
@ -31,8 +58,8 @@ ext {
password: findProperty('dbPassword')] password: findProperty('dbPassword')]
} }
getSocketFactoryAccessInfo = { getSocketFactoryAccessInfo = { env ->
def cred = getCloudSqlCredential('alpha', 'superuser').split(' ') def cred = getCloudSqlCredential(env, 'admin').split(' ')
def sqlInstance = cred[0] def sqlInstance = cred[0]
return [ return [
url: """\ url: """\
@ -46,11 +73,10 @@ ext {
} }
getJdbcAccessInfo = { getJdbcAccessInfo = {
switch (dbServer.toString().toLowerCase()) { if (allDbEnv.contains(dbServer)) {
case 'alpha': return getSocketFactoryAccessInfo(dbServer)
return getSocketFactoryAccessInfo() } else {
default: return getAccessInfoByHostPort(dbServer)
return getAccessInfoByHostPort(dbServer)
} }
} }
@ -62,13 +88,16 @@ ext {
// later). // later).
getCloudSqlCredential = { env, role -> getCloudSqlCredential = { env, role ->
env = env == 'production' ? '' : "-${env}" env = env == 'production' ? '' : "-${env}"
def keyProject = env == '-crash'
? 'domain-registry-crash-kms-keys'
: "domain-registry${env}-keys"
def command = def command =
"""gsutil cp \ """gsutil cp \
gs://domain-registry${env}-cloudsql-credentials/${role}.enc - | \ gs://domain-registry${env}-cloudsql-credentials/${role}_credential.enc - | \
gcloud kms decrypt --location global --keyring nomulus \ gcloud kms decrypt --location global --keyring nomulus \
--key sql-credentials-on-gcs-key --plaintext-file=- \ --key sql-credentials-on-gcs-key --plaintext-file=- \
--ciphertext-file=- \ --ciphertext-file=- \
--project=domain-registry${env}-keys""" --project=${keyProject}"""
return execInBash(command, '/tmp') return execInBash(command, '/tmp')
} }
@ -114,6 +143,13 @@ flyway {
locations = [ "classpath:sql/flyway" ] locations = [ "classpath:sql/flyway" ]
} }
tasks.flywayMigrate.dependsOn(
tasks.create('confirmMigrateOnRestrictedDb') {
doLast {
project.ext.reconfirmRestrictedDbEnv()
}
})
dependencies { dependencies {
def deps = rootProject.dependencyMap def deps = rootProject.dependencyMap

View file

@ -16,7 +16,7 @@
-- This script should run once under the **'postgres'** user before any other -- This script should run once under the **'postgres'** user before any other
-- roles or users are created. -- roles or users are created.
# Prevent backdoor grants through the implicit 'public' role. -- Prevent backdoor grants through the implicit 'public' role.
REVOKE ALL PRIVILEGES ON SCHEMA public from public; REVOKE ALL PRIVILEGES ON SCHEMA public from public;
CREATE ROLE readonly; CREATE ROLE readonly;