mirror of
https://github.com/google/nomulus.git
synced 2025-06-12 23:44:46 +02:00
Set up deployment of the Spec11 pipeline with JPA TM (#716)
* Set up deployment of the Spec11 pipeline with JPA TM * Remove unnecessarily pipeline options setting * Use enviroment name in BeamJpaModuleTest * Fix checkstyle error
This commit is contained in:
parent
d9f0380fc7
commit
d7202fb6e6
9 changed files with 94 additions and 45 deletions
|
@ -54,37 +54,39 @@ public class BeamJpaModule {
|
||||||
|
|
||||||
private static final String GCS_SCHEME = "gs://";
|
private static final String GCS_SCHEME = "gs://";
|
||||||
|
|
||||||
@Nullable private final String credentialFilePath;
|
@Nullable private final String sqlAccessInfoFile;
|
||||||
|
@Nullable private final String cloudKmsProjectId;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs a new instance of {@link BeamJpaModule}.
|
* Constructs a new instance of {@link BeamJpaModule}.
|
||||||
*
|
*
|
||||||
* <p>Note: it is an unfortunately necessary antipattern to check for the validity of
|
* <p>Note: it is an unfortunately necessary antipattern to check for the validity of
|
||||||
* credentialFilePath in {@link #provideCloudSqlAccessInfo} rather than in the constructor.
|
* sqlAccessInfoFile in {@link #provideCloudSqlAccessInfo} rather than in the constructor.
|
||||||
* Unfortunately, this is a restriction imposed upon us by Dagger. Specifically, because we use
|
* Unfortunately, this is a restriction imposed upon us by Dagger. Specifically, because we use
|
||||||
* this in at least one 1 {@link google.registry.tools.RegistryTool} command(s), it must be
|
* this in at least one 1 {@link google.registry.tools.RegistryTool} command(s), it must be
|
||||||
* instantiated in {@code google.registry.tools.RegistryToolComponent} for all possible commands;
|
* instantiated in {@code google.registry.tools.RegistryToolComponent} for all possible commands;
|
||||||
* Dagger doesn't permit it to ever be null. For the vast majority of commands, it will never be
|
* Dagger doesn't permit it to ever be null. For the vast majority of commands, it will never be
|
||||||
* used (so a null credential file path is fine in those cases).
|
* used (so a null credential file path is fine in those cases).
|
||||||
*
|
*
|
||||||
* @param credentialFilePath the path to a Cloud SQL credential file. This must refer to either a
|
* @param sqlAccessInfoFile the path to a Cloud SQL credential file. This must refer to either a
|
||||||
* real encrypted file on GCS as returned by {@link
|
* real encrypted file on GCS as returned by {@link
|
||||||
* BackupPaths#getCloudSQLCredentialFilePatterns} or an unencrypted file on local filesystem
|
* BackupPaths#getCloudSQLCredentialFilePatterns} or an unencrypted file on local filesystem
|
||||||
* with credentials to a test database.
|
* with credentials to a test database.
|
||||||
*/
|
*/
|
||||||
public BeamJpaModule(@Nullable String credentialFilePath) {
|
public BeamJpaModule(@Nullable String sqlAccessInfoFile, @Nullable String cloudKmsProjectId) {
|
||||||
this.credentialFilePath = credentialFilePath;
|
this.sqlAccessInfoFile = sqlAccessInfoFile;
|
||||||
|
this.cloudKmsProjectId = cloudKmsProjectId;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns true if the credential file is on GCS (and therefore expected to be encrypted). */
|
/** Returns true if the credential file is on GCS (and therefore expected to be encrypted). */
|
||||||
private boolean isCloudSqlCredential() {
|
private boolean isCloudSqlCredential() {
|
||||||
return credentialFilePath.startsWith(GCS_SCHEME);
|
return sqlAccessInfoFile.startsWith(GCS_SCHEME);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@Singleton
|
@Singleton
|
||||||
SqlAccessInfo provideCloudSqlAccessInfo(Lazy<CloudSqlCredentialDecryptor> lazyDecryptor) {
|
SqlAccessInfo provideCloudSqlAccessInfo(Lazy<CloudSqlCredentialDecryptor> lazyDecryptor) {
|
||||||
checkArgument(!isNullOrEmpty(credentialFilePath), "Null or empty credentialFilePath");
|
checkArgument(!isNullOrEmpty(sqlAccessInfoFile), "Null or empty credentialFilePath");
|
||||||
String line = readOnlyLineFromCredentialFile();
|
String line = readOnlyLineFromCredentialFile();
|
||||||
if (isCloudSqlCredential()) {
|
if (isCloudSqlCredential()) {
|
||||||
line = lazyDecryptor.get().decrypt(line);
|
line = lazyDecryptor.get().decrypt(line);
|
||||||
|
@ -101,7 +103,7 @@ public class BeamJpaModule {
|
||||||
|
|
||||||
String readOnlyLineFromCredentialFile() {
|
String readOnlyLineFromCredentialFile() {
|
||||||
try {
|
try {
|
||||||
ResourceId resourceId = FileSystems.matchSingleFileSpec(credentialFilePath).resourceId();
|
ResourceId resourceId = FileSystems.matchSingleFileSpec(sqlAccessInfoFile).resourceId();
|
||||||
try (BufferedReader reader =
|
try (BufferedReader reader =
|
||||||
new BufferedReader(
|
new BufferedReader(
|
||||||
new InputStreamReader(
|
new InputStreamReader(
|
||||||
|
@ -141,8 +143,8 @@ public class BeamJpaModule {
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@Config("beamCloudKmsProjectId")
|
@Config("beamCloudKmsProjectId")
|
||||||
static String kmsProjectId() {
|
String kmsProjectId() {
|
||||||
return "domain-registry-dev";
|
return cloudKmsProjectId;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
|
|
|
@ -24,7 +24,6 @@ import google.registry.backup.AppEngineEnvironment;
|
||||||
import google.registry.backup.VersionedEntity;
|
import google.registry.backup.VersionedEntity;
|
||||||
import google.registry.beam.initsql.BeamJpaModule.JpaTransactionManagerComponent;
|
import google.registry.beam.initsql.BeamJpaModule.JpaTransactionManagerComponent;
|
||||||
import google.registry.beam.initsql.Transforms.RemoveDomainBaseForeignKeys;
|
import google.registry.beam.initsql.Transforms.RemoveDomainBaseForeignKeys;
|
||||||
import google.registry.beam.initsql.Transforms.SerializableSupplier;
|
|
||||||
import google.registry.model.billing.BillingEvent;
|
import google.registry.model.billing.BillingEvent;
|
||||||
import google.registry.model.contact.ContactResource;
|
import google.registry.model.contact.ContactResource;
|
||||||
import google.registry.model.domain.DomainBase;
|
import google.registry.model.domain.DomainBase;
|
||||||
|
@ -227,7 +226,7 @@ public class InitSqlPipeline implements Serializable {
|
||||||
transformId,
|
transformId,
|
||||||
options.getMaxConcurrentSqlWriters(),
|
options.getMaxConcurrentSqlWriters(),
|
||||||
options.getSqlWriteBatchSize(),
|
options.getSqlWriteBatchSize(),
|
||||||
new JpaSupplierFactory(credentialFileUrl, jpaGetter)));
|
new JpaSupplierFactory(credentialFileUrl, options.getCloudKmsProjectId(), jpaGetter)));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ImmutableList<String> toKindStrings(Collection<Class<?>> entityClasses) {
|
private static ImmutableList<String> toKindStrings(Collection<Class<?>> entityClasses) {
|
||||||
|
@ -235,26 +234,4 @@ public class InitSqlPipeline implements Serializable {
|
||||||
return entityClasses.stream().map(Key::getKind).collect(ImmutableList.toImmutableList());
|
return entityClasses.stream().map(Key::getKind).collect(ImmutableList.toImmutableList());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static class JpaSupplierFactory implements SerializableSupplier<JpaTransactionManager> {
|
|
||||||
private static final long serialVersionUID = 1L;
|
|
||||||
|
|
||||||
private String credentialFileUrl;
|
|
||||||
private SerializableFunction<JpaTransactionManagerComponent, JpaTransactionManager> jpaGetter;
|
|
||||||
|
|
||||||
JpaSupplierFactory(
|
|
||||||
String credentialFileUrl,
|
|
||||||
SerializableFunction<JpaTransactionManagerComponent, JpaTransactionManager> jpaGetter) {
|
|
||||||
this.credentialFileUrl = credentialFileUrl;
|
|
||||||
this.jpaGetter = jpaGetter;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public JpaTransactionManager get() {
|
|
||||||
return jpaGetter.apply(
|
|
||||||
DaggerBeamJpaModule_JpaTransactionManagerComponent.builder()
|
|
||||||
.beamJpaModule(new BeamJpaModule(credentialFileUrl))
|
|
||||||
.build());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -60,6 +60,13 @@ public interface InitSqlPipelineOptions extends GcpOptions {
|
||||||
|
|
||||||
void setEnvironment(String environment);
|
void setEnvironment(String environment);
|
||||||
|
|
||||||
|
@Description("The GCP project that contains the keyring used for decrypting the "
|
||||||
|
+ "SQL credential file.")
|
||||||
|
@Nullable
|
||||||
|
String getCloudKmsProjectId();
|
||||||
|
|
||||||
|
void setCloudKmsProjectId(String cloudKmsProjectId);
|
||||||
|
|
||||||
@Description(
|
@Description(
|
||||||
"The maximum JDBC connection pool size on a VM. "
|
"The maximum JDBC connection pool size on a VM. "
|
||||||
+ "This value should be equal to or greater than the number of cores on the VM.")
|
+ "This value should be equal to or greater than the number of cores on the VM.")
|
||||||
|
|
|
@ -0,0 +1,48 @@
|
||||||
|
// Copyright 2020 The Nomulus Authors. All Rights Reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package google.registry.beam.initsql;
|
||||||
|
|
||||||
|
import google.registry.beam.initsql.BeamJpaModule.JpaTransactionManagerComponent;
|
||||||
|
import google.registry.beam.initsql.Transforms.SerializableSupplier;
|
||||||
|
import google.registry.persistence.transaction.JpaTransactionManager;
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
import org.apache.beam.sdk.transforms.SerializableFunction;
|
||||||
|
|
||||||
|
public class JpaSupplierFactory implements SerializableSupplier<JpaTransactionManager> {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
private final String credentialFileUrl;
|
||||||
|
@Nullable private final String cloudKmsProjectId;
|
||||||
|
private final SerializableFunction<JpaTransactionManagerComponent, JpaTransactionManager>
|
||||||
|
jpaGetter;
|
||||||
|
|
||||||
|
public JpaSupplierFactory(
|
||||||
|
String credentialFileUrl,
|
||||||
|
@Nullable String cloudKmsProjectId,
|
||||||
|
SerializableFunction<JpaTransactionManagerComponent, JpaTransactionManager> jpaGetter) {
|
||||||
|
this.credentialFileUrl = credentialFileUrl;
|
||||||
|
this.cloudKmsProjectId = cloudKmsProjectId;
|
||||||
|
this.jpaGetter = jpaGetter;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JpaTransactionManager get() {
|
||||||
|
return jpaGetter.apply(
|
||||||
|
DaggerBeamJpaModule_JpaTransactionManagerComponent.builder()
|
||||||
|
.beamJpaModule(new BeamJpaModule(credentialFileUrl, cloudKmsProjectId))
|
||||||
|
.build());
|
||||||
|
}
|
||||||
|
}
|
|
@ -16,17 +16,29 @@ package google.registry.tools;
|
||||||
|
|
||||||
import com.beust.jcommander.Parameters;
|
import com.beust.jcommander.Parameters;
|
||||||
import google.registry.beam.spec11.Spec11Pipeline;
|
import google.registry.beam.spec11.Spec11Pipeline;
|
||||||
|
import google.registry.config.CredentialModule.LocalCredential;
|
||||||
|
import google.registry.config.RegistryConfig.Config;
|
||||||
|
import google.registry.util.GoogleCredentialsBundle;
|
||||||
|
import google.registry.util.Retrier;
|
||||||
|
import javax.annotation.Nullable;
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
/** Nomulus command that deploys the {@link Spec11Pipeline} template. */
|
/** Nomulus command that deploys the {@link Spec11Pipeline} template. */
|
||||||
@Parameters(commandDescription = "Deploy the Spec11 pipeline to GCS.")
|
@Parameters(commandDescription = "Deploy the Spec11 pipeline to GCS.")
|
||||||
public class DeploySpec11PipelineCommand implements Command {
|
public class DeploySpec11PipelineCommand implements Command {
|
||||||
|
|
||||||
@Inject Spec11Pipeline spec11Pipeline;
|
@Inject @Config("projectId") String projectId;
|
||||||
|
@Inject @Config("beamStagingUrl") String beamStagingUrl;
|
||||||
|
@Inject @Config("spec11TemplateUrl")String spec11TemplateUrl;
|
||||||
|
@Inject @Config("reportingBucketUrl")String reportingBucketUrl;
|
||||||
|
@Inject @LocalCredential GoogleCredentialsBundle googleCredentialsBundle;
|
||||||
|
@Inject Retrier retrier;
|
||||||
|
@Inject @Nullable @Config("sqlAccessInfoFile") String sqlAccessInfoFile;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
spec11Pipeline.deploy();
|
Spec11Pipeline pipeline = new Spec11Pipeline(projectId, beamStagingUrl, spec11TemplateUrl,
|
||||||
|
reportingBucketUrl, googleCredentialsBundle, retrier);
|
||||||
|
pipeline.deploy();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -29,7 +29,7 @@ import com.google.appengine.tools.remoteapi.RemoteApiOptions;
|
||||||
import com.google.common.base.Throwables;
|
import com.google.common.base.Throwables;
|
||||||
import com.google.common.collect.ImmutableMap;
|
import com.google.common.collect.ImmutableMap;
|
||||||
import com.google.common.collect.Iterables;
|
import com.google.common.collect.Iterables;
|
||||||
import google.registry.beam.initsql.BeamJpaModule;
|
import google.registry.backup.AppEngineEnvironment;
|
||||||
import google.registry.config.RegistryConfig;
|
import google.registry.config.RegistryConfig;
|
||||||
import google.registry.model.ofy.ObjectifyService;
|
import google.registry.model.ofy.ObjectifyService;
|
||||||
import google.registry.persistence.transaction.TransactionManagerFactory;
|
import google.registry.persistence.transaction.TransactionManagerFactory;
|
||||||
|
@ -68,9 +68,8 @@ final class RegistryCli implements AutoCloseable, CommandRunner {
|
||||||
|
|
||||||
@Parameter(
|
@Parameter(
|
||||||
names = {"--sql_access_info"},
|
names = {"--sql_access_info"},
|
||||||
description =
|
description = "Name of a file containing space-separated SQL access info used when deploying "
|
||||||
"Name of a file containing space-separated SQL access info used when deploying "
|
+ "Beam pipelines")
|
||||||
+ "Beam pipelines")
|
|
||||||
private String sqlAccessInfoFile = null;
|
private String sqlAccessInfoFile = null;
|
||||||
|
|
||||||
// Do not make this final - compile-time constant inlining may interfere with JCommander.
|
// Do not make this final - compile-time constant inlining may interfere with JCommander.
|
||||||
|
@ -168,7 +167,7 @@ final class RegistryCli implements AutoCloseable, CommandRunner {
|
||||||
component =
|
component =
|
||||||
DaggerRegistryToolComponent.builder()
|
DaggerRegistryToolComponent.builder()
|
||||||
.credentialFilePath(credentialJson)
|
.credentialFilePath(credentialJson)
|
||||||
.beamJpaModule(new BeamJpaModule(sqlAccessInfoFile))
|
.sqlAccessInfoFile(sqlAccessInfoFile)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
// JCommander stores sub-commands as nested JCommander objects containing a list of user objects
|
// JCommander stores sub-commands as nested JCommander objects containing a list of user objects
|
||||||
|
@ -179,7 +178,7 @@ final class RegistryCli implements AutoCloseable, CommandRunner {
|
||||||
Iterables.getOnlyElement(jcommander.getCommands().get(parsedCommand).getObjects());
|
Iterables.getOnlyElement(jcommander.getCommands().get(parsedCommand).getObjects());
|
||||||
loggingParams.configureLogging(); // Must be called after parameters are parsed.
|
loggingParams.configureLogging(); // Must be called after parameters are parsed.
|
||||||
|
|
||||||
try {
|
try (AppEngineEnvironment env = new AppEngineEnvironment()) {
|
||||||
runCommand(command);
|
runCommand(command);
|
||||||
} catch (RuntimeException ex) {
|
} catch (RuntimeException ex) {
|
||||||
if (Throwables.getRootCause(ex) instanceof LoginRequiredException) {
|
if (Throwables.getRootCause(ex) instanceof LoginRequiredException) {
|
||||||
|
|
|
@ -134,6 +134,9 @@ interface RegistryToolComponent {
|
||||||
@BindsInstance
|
@BindsInstance
|
||||||
Builder credentialFilePath(@Nullable @Config("credentialFilePath") String credentialFilePath);
|
Builder credentialFilePath(@Nullable @Config("credentialFilePath") String credentialFilePath);
|
||||||
|
|
||||||
|
@BindsInstance
|
||||||
|
Builder sqlAccessInfoFile(@Nullable @Config("sqlAccessInfoFile") String sqlAccessInfoFile);
|
||||||
|
|
||||||
Builder beamJpaModule(BeamJpaModule beamJpaModule);
|
Builder beamJpaModule(BeamJpaModule beamJpaModule);
|
||||||
|
|
||||||
RegistryToolComponent build();
|
RegistryToolComponent build();
|
||||||
|
|
|
@ -54,7 +54,7 @@ public final class BeamJpaExtension implements BeforeEachCallback, AfterEachCall
|
||||||
if (beamJpaModule != null) {
|
if (beamJpaModule != null) {
|
||||||
return beamJpaModule;
|
return beamJpaModule;
|
||||||
}
|
}
|
||||||
return beamJpaModule = new BeamJpaModule(credentialFile.getAbsolutePath());
|
return beamJpaModule = new BeamJpaModule(credentialFile.getAbsolutePath(), null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -82,7 +82,8 @@ class BeamJpaModuleTest {
|
||||||
DaggerBeamJpaModule_JpaTransactionManagerComponent.builder()
|
DaggerBeamJpaModule_JpaTransactionManagerComponent.builder()
|
||||||
.beamJpaModule(
|
.beamJpaModule(
|
||||||
new BeamJpaModule(
|
new BeamJpaModule(
|
||||||
BackupPaths.getCloudSQLCredentialFilePatterns(environmentName).get(0)))
|
BackupPaths.getCloudSQLCredentialFilePatterns(environmentName).get(0),
|
||||||
|
String.format("domain-registry-%s", environmentName)))
|
||||||
.build()
|
.build()
|
||||||
.cloudSqlJpaTransactionManager();
|
.cloudSqlJpaTransactionManager();
|
||||||
assertThat(
|
assertThat(
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue