1
0
mirror of https://github.com/google/nomulus synced 2026-05-18 05:41:51 +00:00

Compare commits

...

141 Commits

Author SHA1 Message Date
gbrodman
969fa2b68c Fix weird flake (#1394) 2021-10-15 18:00:46 -04:00
gbrodman
9a569198fb Ignore class visibility in EntityTest (#1389) 2021-10-15 17:08:51 -04:00
gbrodman
8a53edd57b Use multiple transactions in IcannReportingUploadAction (#1386)
Relevant error log message: https://pantheon.corp.google.com/logs/viewer?project=domain-registry&minLogLevel=0&expandAll=false&timestamp=2021-10-11T15:28:01.047783000Z&customFacets=&limitCustomFacetWidth=true&dateRangeEnd=2021-10-11T20:51:40.591Z&interval=PT1H&resource=gae_app&logName=projects%2Fdomain-registry%2Flogs%2Fappengine.googleapis.com%252Frequest_log&scrollTimestamp=2021-10-11T15:10:23.174336000Z&filters=text:icannReportingUpload&dateRangeUnbound=backwardInTime&advancedFilter=resource.type%3D%22gae_app%22%0AlogName%3D%22projects%2Fdomain-registry%2Flogs%2Fappengine.googleapis.com%252Frequest_log%22%0A%22icannReportingUpload%22%0Aoperation.id%3D%22616453df00ff02a873d26cedb40001737e646f6d61696e2d726567697374727900016261636b656e643a6e6f6d756c75732d76303233000100%22

note the "invalid handle" bit

From https://cloud.google.com/datastore/docs/concepts/transactions:
"Transactions expire after 270 seconds or if idle for 60 seconds."

From b/202309933: "There is a 60 second timeout on Datastore operations
after which they will automatically rollback and the handles become
invalid."

From the logs we can see that the action is lasting significantly longer
than 270 seconds -- roughly 480 seconds in the linked log (more or
less). My running theory is that ICANN is, for some reason, now being
significantly more slow to respond than they used to be. Some uploads in
the log linked above are taking upwards of 10 seconds, especially when
they have to retry. Because we have >=45 TLDs, it's not surprising that
the action is taking >400 seconds to run.

The fix here is to perform each per-TLD operation in its own
transaction. The only reason why we need the transactions is for the
cursors anyway, and we can just grab and store those at the beginning of
the transaction.
2021-10-15 15:38:37 -04:00
Lai Jiang
d25d4073f5 Add a beam pipeline to create synthetic history entries in SQL (#1383)
* Add a beam pipeline to create synthetic history entries in SQL

The logic is mostly lifted from CreateSyntheticHistoryEntriesAction. We
do not need to test for the existence of an embedded EPP resource in the
history entry before create a synthetic one because after
InitSqlPipeline runs it is guaranteed that no embedded resource exists.
2021-10-15 14:51:01 -04:00
Ben McIlwain
6ffe84e93d Add a scrap command to hard-delete a host resource (#1391) 2021-10-15 12:28:18 -04:00
Ben McIlwain
a451524010 Add tests for obscure hostname canonicalization rule (#1388)
Also correctly configures Gradle for the util subproject (it wasn't possible to
run tests in IntelliJ without these changes).
2021-10-14 14:53:28 -04:00
Rachel Guan
bb8988ee4e Set payload in success response after sending notification emails (#1377)
* Set payload in success response after sending expiring certificate notification emails

* Modify log message and test cases for run() in sendExpiringCertificateNotificationEmailAction
2021-10-13 15:58:25 -04:00
Rachel Guan
2aff72b3b6 Add reason and requestedByRegistrar to domain renew flow (#1378)
* Resolve merge conflict

* Include reason and requestedByRegistrar in URS test file

* Modify test cases for new parameters in renew flow

* Add reason and registrar_request to renew domain command

* Update comments for new params in renew flow

* Make changes based on feedback
2021-10-13 11:41:02 -04:00
Weimin Yu
35fd61f771 Update parameter to Datastore wipe pipeline (#1385)
* Update parameter to Datastore wipe pipeline

Add the newly required RegistryEnvironment parameter to
BulkDeleteDatastorePipeline.

Remove the nullable annotation for this parameter in options
class.

Update metadata files regarding this parameter.
2021-10-11 17:31:50 -04:00
Michael Muller
13cb17e9a4 Implement several fixes affecting test flakiness (#1379)
* Implement several fixes affecting test flakiness

- Continued to do transaction manager cleanups on reply failure (lack of this
  may be causing cascading failures.
- Fix UpdateDomainCommandTest's output check (the test was checking for error
  output in standard error, but the command writes its output to the logs.
  Apparently, these may or may not be reflected in standard error depending on
  current global state)
- Remove unnecessary locking and incorrect comment in CommandTestCase.  The
  JUnit tests are not run in parallel in the same JVM and, in general, there
  are much bigger obstacles to this than standard output stream locking.

* Fix bad log message check
2021-10-11 12:54:03 -04:00
Ben McIlwain
4f1c317bbc Revert update auto timestamp non-transactional fallback (#1380)
This was added recently in PR #1341 as an attempted fix for our test flakiness,
but it turns out that it didn't address the root issue (whereas PR #1361
did). So this removes the fallback, as there's no reason this should ever be
called outside of a transactional context.
2021-10-08 16:44:45 -04:00
gbrodman
c8aa32ef05 Include more info in host/domain name failures (#1346)
We're seeing some of these in CreateSyntheticHistoryEntriesAction and I
can't tell why from the logs (it doesn't appear to print the repo ID or
domain/host name)
2021-10-08 15:17:22 -04:00
gbrodman
95a1bbf66a Temporarily disable SQL->DS replay in all tests (#1363) 2021-10-08 14:15:57 -04:00
Rachel Guan
23aa16469e Add WipeOutContactHistoryPiiAction to prod (#1356) 2021-10-08 11:46:26 -04:00
Ben McIlwain
0277c5c25a Add TmOverrideExtension for more safe TM overrides in tests (#1382)
* Add TmOverrideExtension for more safe TM overrides in tests

This is safer to use than calling setTmForTest() directly because this extension
also handles the corresponding call to removeTmOverrideForTest() automatically,
the forgetting of which has been a source of test flakiness/instability in the
past.

There are now broadly two ways to get tests to run in JPA: either use
DualDatabaseTest, an AppEngineExtension, and the corresponding JPA-specific
@Test annotations, OR use this override alongside a
JpaTransactionManagerExtension.
2021-10-07 19:26:25 -04:00
Ben McIlwain
b1b0589281 Elaborate on database read-only error message (#1355)
* Elaborate on database read-only error message
2021-10-07 13:25:24 -04:00
Ben McIlwain
28628564cc Set response payload when wiping out contact history PII (#1376)
Also uses smaller batches in tests so that they don't take so long.
2021-10-07 12:43:41 -04:00
Michael Muller
835f93f555 Add a reference to RDAP conformance checker (#1358)
* Add a reference to RDAP conformance checker

Make a note of the RDAP conformance checker for the next time that we deal
with the RDAP code - would be nice to have this in the test suite.

* Reformat comment
2021-10-07 12:34:41 -04:00
Ben McIlwain
276c188e9d Canonicalize domain/host names in initial import script (#1347)
* Canonicalize domain/host names in initial import script

* Add tests and make reduce some method visibility
2021-10-07 11:59:46 -04:00
Rachel Guan
34ecc6fbe7 Add new parameter renew_one_year to URS (#1364)
* Add autorenews to URS (#1343)

* Add autorenews to URS

* Add autorenews to existing xml files for test cases

* Harmonize domain.get() in existing code

* Fix typo in test case name

* Modify existing test helper method to allow testing with different domain bases
2021-10-06 20:40:43 -04:00
gbrodman
0f4156c563 Use a more efficient query to find resources in histories (#1354) 2021-10-06 15:20:31 -04:00
Michael Muller
e1827ab939 Defer python discovery until presubmit task (#1352)
* Customize LGTM build command

Our presubmit requires a version of python that is more recent than what
lgtm.com's build environments have installed.  Instead of trying to upgrade
them or downgrade our python version, just do the steps of the build that LGTM
needs (i.e. just build the main classes and test classes).
2021-10-06 10:09:13 -04:00
Ben McIlwain
51b2887709 Fix BigQuery data set name handling in activity reporting (#1361)
* Fix BigQuery data set name handling in activity reporting

This is not a constant (as it depends on runtime state), so it can't be named
using UPPER_SNAKE_CASE. Additionally, it's not good practice to use field
initialization when there's logic depending on runtime state involved. So this
PR changes the class to use constructor injection and moves the logic into the
constructor.

* Add fix for ICANN reporting provide

* Extract out ICANN reporting data set

* Inject TransactionManager

* Make TransactionInfo static (per Mike)

* Use ofyTm() in BackupTestStore

* Revert extraneous formatting

* Use auditedOfy in CommitLogMutationTest
2021-10-05 15:11:03 -04:00
Lai Jiang
62eb8801c5 Finish RDE pipeline implementation in SQL mode (#1330)
This PR adds the final step in RDE pipeline (enqueueing the next action
  to Cloud Tasks) and makes some necessary changes, namely by making all
  CloudTasksUtils related classes serializable, so that they can be used
  on Beam.

<!-- Reviewable:start -->
This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/google/nomulus/1330)
<!-- Reviewable:end -->
2021-10-04 21:02:44 -04:00
Lai Jiang
f6920454f6 Fix the beam staging script, take 3 (#1370)
The number of arguments changed in https://github.com/google/nomulus/pull/1369, so the check needs to change as well.

<!-- Reviewable:start -->
This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/google/nomulus/1370)
<!-- Reviewable:end -->
2021-10-04 16:44:32 -04:00
Lai Jiang
9103216a46 Fix beam deployment script again. (#1369)
uberjar task and uberjar name are now different (beamPipelineCommon and
beam_pipeline_common, respectively). This is more idiomatic with regard
to naming conventions but we need to take two different variables now.

<!-- Reviewable:start -->
---
This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/google/nomulus/1369)
<!-- Reviewable:end -->
2021-10-04 14:23:28 -04:00
Lai Jiang
c6705d1956 Fix sandbox cron (#1366)
* Fix sandbox cron

"synchronized" can only be used to specify a 24h time range that is
evenly divided by the interval value, e. g. "every 2 hours
synchronized".

* Change to a different time
2021-10-04 11:09:55 -04:00
Lai Jiang
737f65bd33 Change Beam uber jar name in Nomulu release GCB config (#1367)
The uber jar name was changed in #1351.

<!-- Reviewable:start -->
---
This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/google/nomulus/1367)
<!-- Reviewable:end -->
2021-10-04 10:47:27 -04:00
Lai Jiang
c8caa8f80b Remove the use of AppEngineEnvironment in Spec11Pipeline (#1365)
After #1348 it is no longer necessary to use AppEngineEnvironment in
Beam pipelines. In tests it is taken care of by the
DatastoreEntityExtension whereas on Dataflow the
RegistryPipelineWorkerInitializer does the same initialization for Ofy.
2021-10-02 19:23:09 -04:00
Rachel Guan
65ef18052b Add autorenews to URS (#1343)
* Add autorenews to URS

* Add autorenews to existing xml files for test cases
2021-10-01 19:11:46 -04:00
Lai Jiang
f7938e80f7 Streamline how to fake an App Engine environment (#1348)
Both `DatastoreEntityExtension.PlaceholderEnvironment` and `AppEngineEnvironment` does the same thing, so there is no point having both of them exist. To use `AppEngineEnvionrment` as an autoclosable requires the user to be mindful of where a fake App Engine environment is required. It is better to set this either in the `DatastoreEntityExtension` for tests, or in the worker initializer in Beam. It also makes it easier to remove the fake environment when we are completely datastore free.

Also made a change to how `IdService` allocate Ids in Beam.
<!-- Reviewable:start -->
This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/google/nomulus/1348)
<!-- Reviewable:end -->
2021-10-01 16:46:46 -04:00
Lai Jiang
d8b3a30a20 Rename UpdateKmsKeyringCommand (#1353)
This brings it in line with GetKeyringSecretCommand. We still need to
remove the rest of remaining Cloud KMS related code in the future.

<!-- Reviewable:start -->
---
This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/google/nomulus/1353)
<!-- Reviewable:end -->
2021-10-01 16:45:45 -04:00
sarahcaseybot
93715c6f9e Add VKey workaround to spec11 pipeline (#1339)
* Add VKey workaround to spec11 pipeline

* Parallelize entity loading
2021-10-01 15:21:16 -04:00
Rachel Guan
90cf4519c5 Add a cron job to periodically empty out fields on deleted entities t… (#1303)
* Add a cron job to periodically empty out fields on deleted entities that are at least 18 months old

* Process ContactHistory entities via batching

* Improve test cases by not making assertions in a loop
2021-09-30 15:17:37 -04:00
Michael Muller
3a177f36b1 Make :core:cleanTest depend on FilterTests (#1342)
* Make :core:cleanTest depend on FilterTests

The "cleanTest" target doesn't work for our specialized tests derived from
FilterTest.  Make them all explicit dependencies of cleanTest so we can reset
the tests from a single target.
2021-09-30 10:46:36 -04:00
Lai Jiang
fbbe014e96 Make it possible to stage a single Beam pipeline (#1351) 2021-09-29 18:27:23 -04:00
Ben McIlwain
b05b77cfd1 Add/use more DatabaseHelper convenience methods (#1327)
* Add/use more DatabaseHelper convenience methods

This also fixes up some existing uses of "put" in test code that should be
inserts or updates (depending on which is intended). Doing an insert/update
makes stronger guarantees about an entity either not existing or existing,
depending on what you're doing.

* Convert more Object -> ImmutableObject

* Merge branch 'master' into tx-manager-sigs

* Revert breaking PremiumListDao change

* Refactor more insertInDb()

* Fight more testing errors

* Merge branch 'master' into tx-manager-sigs

* Merge branch 'master' into tx-manager-sigs

* Merge branch 'master' into tx-manager-sigs

* Merge branch 'master' into tx-manager-sigs

* Add removeTmOverrideForTest() calls

* Merge branch 'master' into tx-manager-sigs
2021-09-28 17:16:54 -04:00
Michael Muller
420a0b8b9a Use debian10 image for builder, not ubuntu1804 (#1345)
The debian10 image is generally a bit more recent and, in particular, includes
python 3.7.3, which we're currently using as a baseline for our builds.
2021-09-28 14:49:13 -04:00
gbrodman
cc062e3528 Reduce # shards in CreateSyntheticHistoryEntriesAction (#1344)
We need these to get created (we are blocked from moving to SQL until 30
days after their creation) so reduce this to 3 in the hopes of avoiding
the SQL overloads while we debug why those are occurring in the first
place.
2021-09-28 10:46:50 -04:00
Michael Muller
56a0e35314 Find a suitable version of python. (#1338)
* Find a suitable version of python.

When running presubmit, we were using /usr/bin/python3, which works fine on
systems that have a reasonably recent python version there.  However, our CI
system has a very old version of python there and prefers the use of "pyenv"
to modify the PATH to provide the desired version of python as simply
"python".  So add a check to use the first of "python" or "/usr/bin/python3"
that is at least version 3.7.3.
2021-09-27 16:43:45 -04:00
sarahcaseybot
de434f861f Migrate ICANN activity reports to Cloud SQL on BQ (#1332)
* Migrate ICANN activity reports to Cloud SQL on BQ

* Fix data set name
2021-09-27 15:27:20 -04:00
Ben McIlwain
3caee5fba7 Improve some log messages for readability/consistency (#1333)
* Improve some log messages for readability/consistency

* Address code review comments
2021-09-27 11:35:14 -04:00
Ben McIlwain
ff3c848def Add handling for UpdateAutoTimestamp when not in a transaction (#1341)
* Add handling for UpdateAutoTimestamp when not in a transaction

It's not clear why this is sometimes causing test flakes, but getting better
logging involved should help clear it up.

This also changes AppEngineExtension to insert without reloading the initial
test data, rather than putting it (potentially involving a merge) and reloading
it in a separate transaction. This should hopefully reduce the chance of weird
conflicts.
2021-09-27 11:32:15 -04:00
Rachel Guan
f0b3be5bb6 Improves test file for SendExpiringCertificateNotificationEmailAction (#1335)
* Improves test cases for SendExpiringCertificateNotificationEmailAction
2021-09-27 09:56:16 -04:00
gbrodman
18b808bd34 Fix injection with BackfillRegistryLocksCommand (#1337)
It would have been nice if this had failed at compile-time rather than
an NPE, but we need to make sure to specify that we need to inject this
command to get e.g. the random string generator

In addition, print out only the names of the failed domains (rather than
the entire domain object) for readability.
2021-09-24 14:08:30 -04:00
Lai Jiang
d7689539d7 Remove mention of bazel run (#1340)
Also provides a workaround in the error message.

<!-- Reviewable:start -->
---
This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/google/nomulus/1340)
<!-- Reviewable:end -->
2021-09-24 11:44:27 -04:00
Lai Jiang
c14ce6866b Remove remnants of JUnit 4 rules (#1336)
<!-- Reviewable:start -->
This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/google/nomulus/1336)
<!-- Reviewable:end -->
2021-09-24 06:35:35 -04:00
Michael Muller
3b84542e46 Add a presubmit to verify no new JS dependencies (#1334)
* Add a presubmit to verify no new JS dependencies

Verify that we have a known set of javascript dependencies.  This guards
against the inadvertent introduction of a new dependency with a disallowed
license.

TESTED: Added a new package to packages.json, observed presubmit failure.

* Replaced f-strings, printed python version

For some reason, it looks like we're using a python version older than 3.6 on
our CI machines.

* Remove python version trace.
2021-09-23 14:42:47 -04:00
Lai Jiang
fc7db91d70 Consolidate the use of URL parameters to specify database override (#1331)
There are actions for which we want to provide an override for the database
to use, like when launching Spec11 and Invoicing pipelines. It make sense to
consolidate around the same parameter provided from the same module for
consistency in all cases, instead of defining an override for each action.

<!-- Reviewable:start -->
---
This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/google/nomulus/1331)
<!-- Reviewable:end -->
2021-09-22 20:01:19 -04:00
Weimin Yu
3d8aa85d63 Fix ReadOnlyCheckingQuery's streaming method (#1329)
* Fix ReadOnlyCheckingQuery's streaming method

Following up to PR 1314: fix one more query defaulting to List when
stream() is invoked.
2021-09-21 15:40:50 -04:00
gbrodman
e14cd8bfa2 Add locking and a response in ReplicateToDatastoreAction (#1328)
* Add locking and a response in ReplicateToDatastoreAction

The response is necessary to get nicer logs in GAE and nicer cron job
behavior.

In addition:
- fix issues where locks would be backed up and replayed to Datastore
(they shouldn't be replayed)
- do ignore-read-only writes when replaying the transactions
2021-09-21 10:12:27 -04:00
Michael Muller
82e8641816 Fix javadoc problems with SoyInfo and subprojects (#1326)
* Fix javadoc problems with SoyInfo and subprojects

The *SoyInfo.java files generated by the soy compiler contain deprecation
warnings with links to files that are not imported.  This causes a javadoc
warning.  Temporarily fix this by replacing the link tags with "LINK".  This
also allows us to remove the exclusion of these files, which is a bit nicer.

Also disable javadoc tasks from subprojects.  These just break because they
don't have access to the legacy javadoc classes in the root.
2021-09-20 10:35:07 -04:00
gbrodman
7461ada0fb Include Cursor in the initial SQL population (#1323)
The test for this also required a bit of a fix in the Cursor scope
initialization. If you persist a Key<?> in some object in Datastore, it
persists just the standard data you'd expect, basically the parent, the
kind, and the object's ID/name. Then, when you load it back in from
Datastore it uses the app ID of whatever environment you're loading in
(the Key contains this info even though it isn't included in the
toString() of Key)

If you persist the websafe string format of a Key (which is what we do
for Cursors), it includes the app ID so when you load it, it contains
the old app ID not the new one (if the app ID has changed).

In the pipelines, we use the standard default environment which has a
different app ID from the test environment that's set up by the
DatastoreEntityExtension.

As a result, we should check in Cursor to see if the key is *any*
cross-tld-key, rather than the exact one that exists within this app
environment.
2021-09-19 09:23:02 -04:00
Ben McIlwain
d91ca0eb8a Clean up tx manager insert() signature and add convenience helper method (#1325)
* Clean up tx manager insert() signature and add convenience helper method

This is the first of a series of PRs to clean up the type signatures on the
TransactionManager methods (which are way too generic), along with creating some
helper methods for use in tests only that don't require creating transactions
all over the place, thus reducing visual noise at callsites. This first method
is DatabaseHelper.insertInDb(), but there will be plenty of others. Note that
this is only for the Cloud SQL transaction manager -- I'm not bothering to
migrate any Datastore-only code, as that will be going away soon enough.
2021-09-17 14:45:07 -04:00
gbrodman
12dac76dc8 Skip synthetic history entries for resources that don't need them (#1320)
* Skip synthetic history entries for resources that don't need them

The reason for creating synthetic history entries is so that we can
guarantee that each EppResource's most recent *History object contains
that resource at that point in time. If the most recent *History object
in SQL contains that resource already, there is no need to create a
synthetic *History object for that resource.
2021-09-17 12:10:15 -04:00
Michael Muller
ad6471b3fd Clean up a few lint warnings (#1324)
The build is generating the following lint warnings:

core/src/main/java/google/registry/flows/certs/CertificateChecker.java:246:
warning: [ReferenceEquality] Compariso
n using reference equality instead of value equality
        && (lastExpiringNotificationSentDate == START_OF_TIME
                                             ^
    (see https://errorprone.info/bugpattern/ReferenceEquality)
core/src/test/java/google/registry/backup/ReplayCommitLogsToSqlActionTest.java:350:
warning: [UnnecessaryParenthes
es] These grouping parentheses are unnecessary; it is unlikely the code will
be misinterpreted without them
        .that(jpaTm().transact((() -> jpaTm().loadByEntity(contactResource))))
2021-09-17 09:15:48 -04:00
Rachel Guan
942584b880 Update the initial value for lastExpiringCertNotificationSentDate to START_OF_TIME (#1321)
* Update the initial value for lastExpiringCertNotificationSentDate to START_OF_TIME
2021-09-16 13:06:47 -04:00
Ben McIlwain
8d421e995e Rename client ID to registrar ID in most places (#1317)
* Rename client ID to registrar ID in most places

This is a code-only change, that shouldn't require any sort of data
migration. Correspondingly, there are some existing uses of clientId that are
not migrated (e.g. Datastore fields, task queue payloads, URL parameters for
actions that might be hit from task queues, etc.). And it of course doesn't
modify any fields in EPP XML. Note that the Cloud SQL schema fields are
already named using the registar_id pattern.

This also doesn't yet touch on the -c parameters in nomulus tools; that will be
coming later (since that is an external manual touch-point, it will require a
lot more in the way of changes to various meta scripts and documentation).

* Change more client IDs

* Merge branch 'master' into clientid-to-registrarid
2021-09-16 12:57:43 -04:00
gbrodman
adc10131a0 Don't change UpdateAutoTimestamp on DS->SQL replay (#1322)
* Don't change UpdateAutoTimestamp on DS->SQL replay
2021-09-16 10:44:53 -04:00
Weimin Yu
a8ddb5c053 Add indexes to DomainHistory sub tables (#1319)
* Add indexes to DomainHistory sub tables

Add indexes to DomainTransactionRecord and DomainDsDataHistory to speed
up query for DomainHistory. Without these indexes, DomainHistory loading
is extremely slow: 2 QPS with current production data.
2021-09-14 14:54:21 -04:00
Ben McIlwain
88be34808d Rename Spec11Pipeline's Subdomain -> DomainNameInfo (#1318)
* Rename Spec11Pipeline's Subdomain -> DomainNameInfo

"Subdomain" never made any sense as a class name because these are all
second-level domain names, along with a little bit of metadata such as some
registrar info. "DomainNameInfo" is a better fit.
2021-09-14 14:07:26 -04:00
sarahcaseybot
099555c789 Remove "Datastore" from comments when using both DBs (#1310)
* Change datastore references in comments to be more generic

* Update flows doc
2021-09-13 18:02:41 -04:00
Weimin Yu
652d099e0e Preserve update_time when replicating to SQL (#1316)
* Preserve update_time when replicating to SQL

Prevent InitSqlPipeline from changing the UpdateAutoTimestamp fields in
entities.
2021-09-13 16:55:20 -04:00
gbrodman
f31d77c570 Update references to RDAP RFCs (#1313)
* Update references to RDAP RFCs

There were minor changes to the RDAP RFCs used -- we don't need to
change anything since we already comply with all of the changes, but we
should refer to the newer RFCs in the code.
2021-09-13 15:12:52 -04:00
gbrodman
1ced2b0a5d Fix URS check in BackfillRegistryLocksCommand (#1315)
* Fix URS check in BackfillRegistryLocksCommand

"reason" can be null
2021-09-13 14:15:25 -04:00
Rachel Guan
0e6b5e949d Set up sendExpiringCertificateNotificationEmail cron job in prod (#1272)
* Set up sendExpiringCertificateNotificationEmail cron job in prod
2021-09-13 13:36:11 -04:00
Weimin Yu
fe714329c9 Add a temporary fix to Hibernate detach in BEAM queries (#1314)
* Add a temporary fix to Hibernate detach in query

Make all queries in RegistryQuery (exclusively used by BEAM) use
EntityManager.clear() to detach entities. This is a temporary measure
that unblocks work in BEAM. We will revert the work once
JpaTransactionManager can detach entities properly for all types of
queries.

Also fixed regression bugs that broke query result streaming:
- The code that sets query fetch size was not carried over from
QueryComposer.
- The new ReadOnlyCheckingTypedQuery class did not override parent's
getResultStream() method, which calls getList().
2021-09-10 21:23:07 -04:00
Ben McIlwain
9297b11a57 Add the DNS refresh request time field to the Domain tables (#1279)
* Add the DNS refresh request time field to the Domain tables

This isn't used yet, but it will eventually be the replacement for the dns-pull
task queue once we get further in the migration.

* Merge branch 'master' into domain-dns-dirty
2021-09-10 14:18:32 -04:00
Michael Muller
031f4ea063 Remove VKeyTranslatorFactory.createVKey(String) (#1312)
* Remove VKeyTranslatorFactory.createVKey(String)

This method serves the same function as VKey.fromWebsafeKey(), and isn't used
anywhere.  Move the test for it into VKeyTest and use it to instead test
fromWebsafeKey() (which didn't previously have a test).
2021-09-09 12:24:02 -04:00
Rachel Guan
0a25182fea Keep new lines/spaces as the way it shows in config yaml (#1311)
* Paste the email content as the way it is in doc.
2021-09-08 22:00:32 -04:00
Lai Jiang
f1dcb1299f Reformat a file (#1305)
* Reformat a file

Constants should be SNAKE_CASE named and static fields usually go before
instance fields.
2021-09-03 16:56:57 -04:00
gbrodman
d46594c610 Select correct history entries when creating canceling records (#1309) 2021-09-03 11:25:53 -04:00
gbrodman
0a9fa8cf23 Add RetryingTest annotation to flaky RDE test (#1306)
I'm not sure why this test is failing. It's failing saying that the
listObjects call is failing to include
"soy_2000-01-01_thin_S1_R1.xml.ghostryde" in the results, however the
verifyFiles method that we call right beforehand verifies that file and
its contents
2021-09-03 09:39:55 -04:00
sarahcaseybot
db4bf90538 Remove Datastore references from DomainLabelEntry (#1307) 2021-09-02 17:55:55 -04:00
Rachel Guan
d6127e4c0c Log registrar and certificate info before sending an email (#1308)
* Log registrar and certificate info before sending an email
2021-09-02 16:58:58 -04:00
sarahcaseybot
447bfa162b Remove Datastore references in BaseDomainLabelList (#1304) 2021-09-02 13:21:45 -04:00
Rachel Guan
c9efa61198 Update expiring certificate notification email content (#1294)
* Update expiring certificate notification email content

* Improve test cases
2021-08-30 11:51:05 -04:00
gbrodman
054c0625a8 Add SQL functionality to DeleteProberDataAction (#1218)
This includes a change to how the JPA transaction manager handles
existence and load checks for entities with compound IDs. Previously, we
relied on the fields all being named the same in the ID entity and the
parent entity. This didn't work for History objects (e.g. DomainHistory)
so existence checks were broken. Now, we use the methods the same way
that Hibernate does (if possible).

Note as well that there's a bit of semi-duplicated logic in
DeleteProberDataAction (between the mapper and the SQL logic). The
mapper code will be deleted once we've shifted to SQL, and for now it's
better to keep it in place for logging purposes.
2021-08-27 21:09:08 -04:00
gbrodman
b03639d7fc Implement read-only transaction manager modes for R3.0 migration (#1241)
This involves:
- Altering both transaction managers to check for a read-only mode at
the start of standard write actions (e.g. delete, put).
- Altering both raw layers (entity manager, ofy) to throw exceptions on
write actions as well
- Implementing bypass routes for reading / setting / removing the schedule itself
so that we don't get "stuck"
2021-08-27 15:59:16 -04:00
Rachel Guan
bd9af0de84 Improve logging for SendExpiringCertificateNotificationEmailAction.java (#1302)
* Improve logging for SendExpiringCertificateNotificationEmailAction.java
2021-08-27 13:11:54 -04:00
gbrodman
ae911a5280 Fix semantic merge conflict accidentally introduced (#1301) 2021-08-26 16:15:56 -04:00
gbrodman
d57597f40f Clean up ReplicateToDatastoreAction and tests (#1299)
* Clean up ReplicateToDatastoreAction and tests

1. applyTransaction should throw an error if it fails; this allows us to
have more information in the caller (and it shouldn't usually happen)
2. Set a response code + payload now, since this is an action that is
called by cron
3. Add a method to the test log subject that allows us to check if a
severe log with a particular Throwable cause was logged (since the cause
isn't contained in the log message itself directly)
2021-08-25 14:45:05 -06:00
gbrodman
2641d0d462 Save indexes when replaying EppResources SQL->DS (#1300)
* Save indexes when replaying EppResources SQL->DS

We implement this similarly to how we implement the
beforeSqlSaveOnReplay callback in the other direction -- a
beforeDatastoreSaveOnReplay method that is called when replaying a
Mutation to Datastore. This means that the asynchronous replay will
create the relevant ForeignKeyIndex and EppResourceIndex objects for
EppResources saved when SQL is primary.
2021-08-25 14:44:44 -06:00
sarahcaseybot
5b41f0b9b6 Remove ClaimsList from Datastore Schema (#1298)
* Remove ClaimsList from Datastore schema

* Remove some Datastore references

* Remove unnecessary annotations
2021-08-25 11:58:44 -04:00
Lai Jiang
1a26677d72 Implement a util class to manage push queues using Cloud Tasks API (#1290)
* Implement a util class to manage push queues using Cloud Tasks API

Push queues were part of App Engine when they debuted. As a result the
Task Queue API were part of the App Engine SDK and can only be used in
App Engine classic runtime. The new Cloud Tasks API can be used in any
runtime but it only supports push queues. In this PR we implement a util
class (CloudTasksUtils) like TaskQueueUtils to handle enqueuing tasks to
push queues using Cloud Tasks. One action (TldFanoutAction) was
converted to use the new API as a demo. Mass migration of other call sites of
the old API will follow in a separate PR.

TESTED=deployed to alpha and verified that tasks are corrected enqueued
and executed.
2021-08-24 21:13:54 -04:00
gbrodman
f1beeb4016 Add double-replay to remaining existing ReplayExtension calls (#1297)
The only other change is that we need to reconstitute
serverApproveEntities for DomainTransferData in more situations (to fill
out the ofy keys)
2021-08-23 15:08:09 -04:00
gbrodman
5c33286056 Compare SQL and Datastore objects in SQL->DS replay testing (#1291)
Add double-replay to the Host*Flow tests to show how this works. The
only change to the double replay itself is that now we store the
Datastore entity in the TransactionEntity object -- this is because we
use Objectify to serialize the objects into bytes and we need it to know
about the entity in question.
2021-08-23 11:05:14 -04:00
gbrodman
603a95d719 Add DS->SQL replay cron job to production (#1292)
* Add DS->SQL replay cron job to production

This won't do anything until we set the migration schedule to
DATASTORE_PRIMARY. Actions in order:

1. Add this cron job (it'll be a no-op)
2. Run the init-sql-pipeline to populate production's SQL DB
3. Set the SqlReplayCheckpoint to a time before the smear backup that
was used in step #1 (maybe 30 minutes)
4. Set the database migration schedule to transition to
DATASTORE_PRIMARY at some point
2021-08-23 07:59:51 -06:00
gbrodman
0a3774d3f7 Add withDsAndCloudSql to flow test (#1293)
* Add withDsAndCloudSql to flow test

Not sure why this wasn't failing before
2021-08-20 09:07:38 -06:00
Rachel Guan
cc60b27dd3 Add sending notification email mechanism for expiring certificates (#1179)
* Resolve rebase conflict

* Fix and imporove based on feedback.
2021-08-19 12:49:45 -04:00
Rachel Guan
52c18f9967 Remove files that are not longer used for create/update premium list (#1288)
* Remove files that are not longer used for create/update premium list

* Remove comments/notes related to create/update premium list action files
2021-08-18 14:04:57 -04:00
gbrodman
5339b3cb6c Remove -- from crash cron comment (#1289)
This is causing the release build to fail, see https://pantheon.corp.google.com/cloud-build/builds;region=global/22ec980b-c2b6-43fe-994a-aa98c0dbc9d4?project=domain-registry-dev
2021-08-18 11:30:01 -04:00
sarahcaseybot
d18dab3327 Remove ReservedList from Datastore schema (#1285)
* Remove ReservedList from Datastore schema

* Remove some Datastore references

* Add a different non-replicated entity to ReplayCommitLogsToSqlActionTest
2021-08-17 16:56:00 -04:00
gbrodman
61932c1809 Use direct ofyTm reference when clearing cache in tool (#1287)
We shouldn't reference tm() at all before initializing the JPA
transaction manager, since tm() looks at the database migration schedule
when figuring out which transaction manager to use.
2021-08-17 13:20:17 -06:00
sarahcaseybot
8eb8c810e8 Remove DeleteEntityAction (#1282) 2021-08-16 13:21:00 -04:00
Weimin Yu
c03a7b0b33 Update cron jobs in crash (#1284)
* Update cron jobs in crash

Add wipeout cron jobs for the duration of migration testing with
production data.

* Disable Datastore-related cron jobs
2021-08-16 12:03:45 -04:00
gbrodman
7a4c109b36 Remove recursive load in DBMSS cache (#1286)
* Remove recursive load in DBMSS cache

This occurs because if we do a standard transaction, the JpaTxnManager
checks to see if we should be doing backups, which involves loading the
migration state schedule (causing the recursion). When starting the
transaction to load the schedule, we should explicitly
transactWithoutBackup so there's no need to check.

This wasn't hit in tests because we previously manually set the
replication to not occur in the JpaTransactionManagerExtension -- we
remove that and related setters.
2021-08-14 12:34:23 -06:00
Ben McIlwain
22b1b8d21a Add instructions for two-step DB schema updates (#1283)
* Add instructions for two-step DB schema updates

These expanded steps are required by the recent enabling of the SQL integration test suite.
2021-08-13 17:21:38 -04:00
Weimin Yu
5bbabadafd Generate string to uniquely identify a SqlEntity (#1271)
* Generate string to uniquely identify a SqlEntity

Add a method to SqlEntity that returns a string built from the entity's
primary key(s). This string can be used in logging.
2021-08-13 16:22:54 -04:00
Ben McIlwain
6c73161ff8 Add the domain DNS refresh request time field to the DB schema (#1280)
* Add the domain DNS refresh request time field to the DB schema

This isn't used yet, but it will eventually be the replacement for the dns-pull
task queue once we get further in the migration.

* Remove index
2021-08-13 15:32:18 -04:00
Rachel Guan
7faee04422 Modify class name to remove checkstyleTest warning (#1281) 2021-08-13 14:16:58 -04:00
Ben McIlwain
b340b2b5e9 Add tx/s instrumentation to replay action and re-enable it on sandbox (#1276) 2021-08-12 18:33:47 -04:00
gbrodman
7f733cd16d Store DatabaseMigrationSchedule in SQL instead of Datastore (#1269)
* Store DatabaseMigrationSchedule in SQL instead of Datastore

This requires messing around with some of the JPA unit test rule
creation since it requires saving / retrieving the schedule pretty much
always (which itself includes the hstore extension).
2021-08-12 15:57:31 -06:00
Ben McIlwain
60469479a4 Consolidate all remaining schema classes into model package (#1278) 2021-08-12 13:38:50 -04:00
Ben McIlwain
5158673f21 Consolidate all Registry/TLD-related classes into google.registry.model.tld (#1277) 2021-08-11 18:04:51 -04:00
gbrodman
743ca4106c Add SQL schema additions for DatabaseMigrationStateSchedule (#1274) 2021-08-10 16:46:07 -04:00
Ben McIlwain
2b99ee61d4 Load DatabaseMigrationStateSchedule in a more performant way (#1273)
This performs a direct load-by-key (the most efficient Datastore operation),
rather than attempting to load all entities by type using an ancestor query. The
existing implementation is possibly more error-prone as well, and might be
responsible for the "cross-group transaction need to be explicitly specified"
error we're seeing.
2021-08-10 14:47:59 -04:00
sarahcaseybot
28a1cc613c Remove PremiumList from Datastore schema (#1256)
* Remove PremiumList from Datastore schema

* Remove commented out code

* Change lastUpdateTime to creationTimestamp

* Remove extra file

* Remove currency unit from input data to parse

* Revert extra file

* Check currency in parse

* Create all PremiumEntries before saving them in bulk

* small fixes

* Fix merge conflict
2021-08-10 13:26:13 -04:00
sarahcaseybot
9811cdb85c Initialize data in cloudSqlOnly tests (#1266)
* Initialize data in cloudSqlOnly tests

* combine conditionals
2021-08-09 13:04:35 -04:00
Lai Jiang
761ae612fd Remove backported LocalStorageHelper (#1267)
* Remove backported LocalStorageHelper

The released version on Maven Central now contains the fix to the
serialization bug.
2021-08-06 21:10:32 -04:00
gbrodman
e2fa60a9c6 Use one SQL transaction per Datastore transaction in replay to SQL (#1268)
There was a subtle issue that we encountered in sandbox when using one
transaction per file that was difficult to replicate. Basically,
1. Save a domain with dsData
2. Save the domain without dsData
3. Save the domain with the same dsData as step 1
4. Delete literally any object

If one performs steps 2-4 in the same transaction, Hibernate will throw
an exception (cascade re-saving a cascade-deleted object). Note that
step 4 is in fact necessary to reproduce the issue, yay Hibernate.

We will test this and if one transaction per transaction is too slow,
we'll figure out ways to reduce the number of SQL transactions.
2021-08-06 16:05:36 -04:00
sarahcaseybot
b04dfbf740 Migrate invoicing pipeline to read from Cloud SQL (#1220)
* Save entities to Cloud SQL for tests

* Fix merge conflict

* Filter out non-real registrars and non-invoicing TLDs

* Add 1 month filter

* Handle cancellations

* Add to pipeline

* Use database in pipeline

* fix formatting

* Add a full pipeline test

* Fix repo ids in tests

* Move query to separate file

* Remove unused variables

* Remove unnecessary debugging remnant

* Reformat sql file

* Add jpql issue description

* Use DateTimeUtils

* Fix license header year

* Fix SQL formatting

* Use regex pattern

* Fix string building

* Add test for makeCloudSqlQuery

* Add clarifying comment
2021-08-06 15:56:04 -04:00
Weimin Yu
a1668ceafd Drop the KmsSecret table (#1258)
* Drop the KmsSecret table

Code using this table has been removed in PR 1252.
2021-08-04 23:23:58 -04:00
Lai Jiang
406d49ac99 Fix GCS bucket/subdir handling in IcannReportingStager (#1265)
After the migration to the new GCS API it becomes apparent that the
BlobId.of() method needs to take the bucket name (without any trailing
directories) as the first argument. I did a search on all occurrences of
"BlobId.of" in the code base and verified that it is only in the ICANN
reporting job that the API was misused.

<!-- Reviewable:start -->
This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/google/nomulus/1265)
<!-- Reviewable:end -->
2021-08-04 14:01:04 -04:00
Weimin Yu
f7b82bc190 Allow db wipeouts in non-prod/sandbox enviroments (#1263)
* Allow db wipeouts in non-prod/sandbox enviroments
2021-08-03 17:41:10 -04:00
Lai Jiang
45c398149b Write RDE files and advance cursors in Beam pipeline (#1249)
This PR re-implements most of the logic in the RdeStagingReducer, with
the exception of the last enqueue operations, due to the fact that the
task queue API is not available outside of App Engine SDK. This part
will come in a separate PR.

Another deviation from the reducer is that we forwent the lock -- it is
difficult do it across different beam transforms. Instead we write each
report to a different folder according to its unique beam job name. When
enqueueing the publish tasks we will then pass the folder prefix as a
URL parameter.

<!-- Reviewable:start -->
---
This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/google/nomulus/1249)
<!-- Reviewable:end -->
2021-07-30 16:24:58 -04:00
gbrodman
183c0653fb Add a test for replaying cascading deletes to SQL (#1259)
* Add a test for replaying cascading deletes to SQL
2021-07-30 15:31:46 -04:00
gbrodman
fb002953c8 Add more logging in the event of replay put/delete failure (#1262)
* Add more logging in the event of replay put/delete failure
2021-07-30 15:09:45 -04:00
Rachel Guan
a369e57e5c Apply registrar code change after registrar schema change is taken effect (#1254)
* Apply code change after registrar schema change is taken effect
2021-07-28 16:40:49 -04:00
gbrodman
f32d80fb9d Use the DB migration schedule for SQL->DS replay (#1242)
This is instead of the current configuration parameter.

In addition, this adds some helpers to DatabaseHelper to make the
transitions easier, since we more frequently need to alter + reset the
schedule.
2021-07-27 16:05:59 -04:00
gbrodman
afa5a353f1 Use raw EntityManager to load during beforeSqlSave (#1253)
If we use the transaction manager methods, JpaTransactionManagerImpl
will attempt to detach the EppResource in question that we're loading --
this fails because that entity has been saved in the same transaction
already. We don't need detaching during these methods (it's just for
resource population) so we can use the raw loads to get around it.
2021-07-26 19:14:49 -04:00
Rachel Guan
c4c5ac85da Remove isNearingExpiration() after shouldReceiveExpiringNotification() being added to code base (#1255)
* Resolve merge conflict
2021-07-26 18:23:14 -04:00
Ben McIlwain
4d0078607f Add SECURITY.md security policy (#1257)
* Add SECURITY.md security policy
2021-07-26 17:35:59 -04:00
Rachel Guan
2b78433682 Add method that checks if client should be notified for expiring certificate (#1245)
* fix merge conflict
2021-07-26 17:20:12 -04:00
Weimin Yu
a0fcd02ed2 Remove KmsSecret model entities (#1252)
* Remove KmsSecret model entities

Now that we have been using the SecretManager for almost a month now,
remove the KmsSecret and KmsSecretRevision entities from Java code base.
A follow-up PR will drop the relevant tables in the schema.

Also removed a few unused classes in the beam package.
2021-07-26 17:09:09 -04:00
Rachel Guan
58e413af89 Expand registrar schema to support sending expiring certificate notification emails (#1247)
* Expand registrar schema to support sending expiring certificate notification emails

* Remove java change (restrictly schema change only)
2021-07-22 17:11:32 -04:00
gbrodman
38c8e81690 Fix runtime issues with commit-log-to-SQL replay (#1240)
* Fix runtime issues with commit-log-to-SQL replay

- We now use a more intelligent prefix to narrow the listObjects search
space in GCS. Otherwise, we're returning >30k objects which can take
roughly 50 seconds. This results in a listObjects time of 1-3 seconds.

- We now search hour by hour to efficiently make use of the prefixing.
Basically, we keep searching for new files until we hit the current time
or until we hit the overall replay timeout.

- Dry-run only prints out the first hour's worth of files
2021-07-22 13:59:28 -04:00
Rachel Guan
3beb207fcc Add email set up for sending expiring certificate notification emails (#1248)
* Add email set up for sending expiring certificate notification emails
2021-07-21 15:47:27 -04:00
gbrodman
8cf88b7e18 Avoid unnecessary tm() calls without ofy init in Spec11PipelineTest (#1250)
* Avoid unnecessary tm() calls without ofy init in Spec11PipelineTest
2021-07-20 15:10:50 -04:00
gbrodman
6ec2e9501d Fix flaky test issues caused by lack of ofy init (#1246) 2021-07-20 13:14:41 -04:00
sarahcaseybot
6849bf6914 Use less strict isolation level in Spec11 pipeline (#1244) 2021-07-16 15:46:34 -04:00
gbrodman
34f3823960 Fix hanging threads in GcsDiffFileLister (#1243)
* Fix hanging threads in GcsDiffFileLister

Basically, whenever we request threads using the request thread factory,
we must be on the request thread itself. Dagger doesn't guarantee this
for us if we provide the ExecutorService directly in the action (or in
the GcsDiffFileLister), but we can gurantee that we're on the request
thread itself by simply injecting a Lazy, so that the executor is
instantiated inside the request itself.

In addition, add a timeout on the futures just in case.
2021-07-16 14:13:20 -04:00
gbrodman
bb5d2dcf0a Use the DatabaseMigrationSchedule to determine which TM to use (#1233)
* Use the DatabaseMigrationSchedule to determine which TM to use

We still allow the "manual" specification of a particular transaction
manager, most useful in @DualDatabaseTest classes. If that isn't
specified, we examine the migration schedule to see which to return.

Notes:
- This requires that any test that sets the migration schedule clean up
after itself so that it won't affect future test runs of other classes
(because the migration schedule cache is static)
- One alternative would, instead of having a "test override" for the
transaction manager, be to examine the registry environment and only
override the transaction manager in the UNIT_TEST environment. This
doesn't work because there are many instances in which tests simulate
non-test environment.
2021-07-14 13:05:01 -04:00
sarahcaseybot
6ce0211537 Remove key references from BaseDomainLabelList (#1239) 2021-07-13 16:49:34 -04:00
Lai Jiang
676616a172 Remove the use of GCS APIs provided from GAE SDK (#1228)
The API provided by the GAE SDK will not be available outside GAE
runtime. This presents a problem when we migrate off of GAE. More
pressingly, the RDE pipeline migration to Beam requires that we write to
GCS on GCE. Previously we were able to sidestep the issue by delegating
the writes to FileIO provided by Beam, which knows how to write to GCS.
However the RDE pipeline cannot use FileIO directly as it needs to write
to multiple files in one go and explicit use of GCS API is needed.

An unfortunate side effect of the API migration is that the new testing
library contains a bug which makes serializing GcsUtils impossible. It
is fixed upstream but not released yet. The fix has been backported for
the time being.

<!-- Reviewable:start -->
This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/google/nomulus/1228)
<!-- Reviewable:end -->
2021-07-13 14:52:37 -04:00
Weimin Yu
62c556cebf Restore commit logs from other project (#1236)
* Restore commit logs from other project

Allow non-production projects to restore commit logs from another
project. This feature can be used to duplicate a realistic testing
environment.

An optional parameter is added that can override the default commit log
location.

Tested successfully in QA.
2021-07-12 16:56:47 -04:00
Ben McIlwain
535f84a912 Add better logging/error messages for Cloud DNS failures (#1237)
* Add better logging/error messages for Cloud DNS failures
2021-07-09 17:04:57 -04:00
sarahcaseybot
d283cf1c90 Remove old DomainList fields from Registry (#1231)
* Remove old DomainList fields from Registry

I also resaved all Registry objects in sandbox and production to make sure that the new field is populated on all entity objects.

* small fixes

* Some more small fixes

* Delete commented out code

* Remove existence check in tests
2021-07-08 17:19:11 -04:00
Rachel Guan
f5d344d5c9 Add cc support to email service (#1230)
* Add cc support to email service
2021-07-08 12:03:03 -04:00
Weimin Yu
61d029d955 Ensure VKey is actually serializable (#1235)
* Ensure VKey is actually serializable

Tighten field type so that non-serializable object cannot be set as
sqlKey.

This would make it easier to make EppResource entities Serializable in
the future.
2021-07-08 10:54:22 -04:00
Lai Jiang
2195ba90fa Add a method to set a "not in" WHERE clause in CriteriaQueryBuilder (#1225)
<!-- Reviewable:start -->
This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/google/nomulus/1225)
<!-- Reviewable:end -->
2021-07-07 15:49:29 -04:00
956 changed files with 24931 additions and 16566 deletions

4
.gitignore vendored
View File

@@ -112,3 +112,7 @@ core/**/registrar_bin*.js
core/**/registrar_dbg*.js
core/**/registrar_bin*.css
core/**/registrar_dbg*.css
# Appengine generated files
core/WEB-INF/appengine-generated/*.bin
core/WEB-INF/appengine-generated/*.xml

4
SECURITY.md Normal file
View File

@@ -0,0 +1,4 @@
To report a security issue, please use http://g.co/vulnz. We use
http://g.co/vulnz for our intake, and do coordination and disclosure here on
GitHub (including using GitHub Security Advisory). The Google Security Team will
respond within 5 working days of your report on g.co/vulnz.

View File

@@ -196,9 +196,35 @@ allprojects {
}
}
rootProject.ext {
pyver = { exe ->
try {
ext.execInBash(
exe + " -c 'import sys; print(sys.hexversion)'", "/") as Integer
} catch (org.gradle.process.internal.ExecException e) {
return -1;
}
}
}
task runPresubmits(type: Exec) {
executable '/usr/bin/python3'
args('config/presubmits.py')
doFirst {
// Find a python version greater than 3.7.3 (this is somewhat arbitrary, we
// know we'd like at least 3.6, but 3.7.3 is the latest that ships with
// Debian so it seems like that should be available anywhere).
def MIN_PY_VER = 0x3070300
if (pyver('python') >= MIN_PY_VER) {
executable 'python'
} else if (pyver('/usr/bin/python3') >= MIN_PY_VER) {
executable '/usr/bin/python3'
} else {
throw new GradleException("No usable Python version found (build " +
"requires at least python 3.7.3)");
}
}
}
def javadocSource = []
@@ -457,8 +483,6 @@ task javaIncrementalFormatApply {
task javadoc(type: Javadoc) {
source javadocSource
classpath = files(javadocClasspath)
// Exclude the misbehaving generated-by-Soy Java files
exclude "**/*SoyInfo.java"
destinationDir = file("${buildDir}/docs/javadoc")
options.encoding = "UTF-8"
// In a lot of places we don't write @return so suppress warnings about that.
@@ -483,3 +507,15 @@ task coreDev {
}
javadocDependentTasks.each { tasks.javadoc.dependsOn(it) }
// disable javadoc in subprojects, these will break because they don't have
// the correct classpath (see above).
gradle.taskGraph.whenReady { graph ->
graph.getAllTasks().each { task ->
def subprojectJavadoc = (task.path =~ /:.+:javadoc/)
if (subprojectJavadoc) {
println "Skipping ${task.path} for javadoc (only root javadoc works)"
task.enabled = false
}
}
}

View File

@@ -24,7 +24,7 @@ com.google.common.html.types:types:1.0.6
com.google.errorprone:error_prone_annotations:2.5.1
com.google.escapevelocity:escapevelocity:0.9.1
com.google.guava:failureaccess:1.0.1
com.google.guava:guava:30.1-jre
com.google.guava:guava:30.1.1-jre
com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava
com.google.http-client:google-http-client-apache-v2:1.39.0
com.google.http-client:google-http-client-appengine:1.39.0
@@ -53,7 +53,7 @@ org.apache.commons:commons-text:1.6
org.apache.httpcomponents:httpclient:4.5.13
org.apache.httpcomponents:httpcore:4.4.14
org.checkerframework:checker-compat-qual:2.5.5
org.checkerframework:checker-qual:3.5.0
org.checkerframework:checker-qual:3.8.0
org.json:json:20160212
org.ow2.asm:asm-analysis:7.0
org.ow2.asm:asm-commons:7.0

View File

@@ -24,7 +24,7 @@ com.google.common.html.types:types:1.0.6
com.google.errorprone:error_prone_annotations:2.5.1
com.google.escapevelocity:escapevelocity:0.9.1
com.google.guava:failureaccess:1.0.1
com.google.guava:guava:30.1-jre
com.google.guava:guava:30.1.1-jre
com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava
com.google.http-client:google-http-client-apache-v2:1.39.0
com.google.http-client:google-http-client-appengine:1.39.0
@@ -53,7 +53,7 @@ org.apache.commons:commons-text:1.6
org.apache.httpcomponents:httpclient:4.5.13
org.apache.httpcomponents:httpcore:4.4.14
org.checkerframework:checker-compat-qual:2.5.5
org.checkerframework:checker-qual:3.5.0
org.checkerframework:checker-qual:3.8.0
org.json:json:20160212
org.ow2.asm:asm-analysis:7.0
org.ow2.asm:asm-commons:7.0

View File

@@ -24,7 +24,7 @@ com.google.common.html.types:types:1.0.6
com.google.errorprone:error_prone_annotations:2.5.1
com.google.escapevelocity:escapevelocity:0.9.1
com.google.guava:failureaccess:1.0.1
com.google.guava:guava:30.1-jre
com.google.guava:guava:30.1.1-jre
com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava
com.google.http-client:google-http-client-apache-v2:1.39.0
com.google.http-client:google-http-client-appengine:1.39.0
@@ -53,7 +53,7 @@ org.apache.commons:commons-text:1.6
org.apache.httpcomponents:httpclient:4.5.13
org.apache.httpcomponents:httpcore:4.4.14
org.checkerframework:checker-compat-qual:2.5.5
org.checkerframework:checker-qual:3.5.0
org.checkerframework:checker-qual:3.8.0
org.json:json:20160212
org.ow2.asm:asm-analysis:7.0
org.ow2.asm:asm-commons:7.0

View File

@@ -24,7 +24,7 @@ com.google.common.html.types:types:1.0.6
com.google.errorprone:error_prone_annotations:2.5.1
com.google.escapevelocity:escapevelocity:0.9.1
com.google.guava:failureaccess:1.0.1
com.google.guava:guava:30.1-jre
com.google.guava:guava:30.1.1-jre
com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava
com.google.http-client:google-http-client-apache-v2:1.39.0
com.google.http-client:google-http-client-appengine:1.39.0
@@ -53,7 +53,7 @@ org.apache.commons:commons-text:1.6
org.apache.httpcomponents:httpclient:4.5.13
org.apache.httpcomponents:httpcore:4.4.14
org.checkerframework:checker-compat-qual:2.5.5
org.checkerframework:checker-qual:3.5.0
org.checkerframework:checker-qual:3.8.0
org.json:json:20160212
org.ow2.asm:asm-analysis:7.0
org.ow2.asm:asm-commons:7.0

View File

@@ -24,7 +24,7 @@ com.google.common.html.types:types:1.0.6
com.google.errorprone:error_prone_annotations:2.5.1
com.google.escapevelocity:escapevelocity:0.9.1
com.google.guava:failureaccess:1.0.1
com.google.guava:guava:30.1-jre
com.google.guava:guava:30.1.1-jre
com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava
com.google.http-client:google-http-client-apache-v2:1.39.0
com.google.http-client:google-http-client-appengine:1.39.0
@@ -53,7 +53,7 @@ org.apache.commons:commons-text:1.6
org.apache.httpcomponents:httpclient:4.5.13
org.apache.httpcomponents:httpcore:4.4.14
org.checkerframework:checker-compat-qual:2.5.5
org.checkerframework:checker-qual:3.5.0
org.checkerframework:checker-qual:3.8.0
org.json:json:20160212
org.ow2.asm:asm-analysis:7.0
org.ow2.asm:asm-commons:7.0

View File

@@ -24,7 +24,7 @@ com.google.common.html.types:types:1.0.6
com.google.errorprone:error_prone_annotations:2.5.1
com.google.escapevelocity:escapevelocity:0.9.1
com.google.guava:failureaccess:1.0.1
com.google.guava:guava:30.1-jre
com.google.guava:guava:30.1.1-jre
com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava
com.google.http-client:google-http-client-apache-v2:1.39.0
com.google.http-client:google-http-client-appengine:1.39.0

View File

@@ -24,7 +24,7 @@ com.google.common.html.types:types:1.0.6
com.google.errorprone:error_prone_annotations:2.5.1
com.google.escapevelocity:escapevelocity:0.9.1
com.google.guava:failureaccess:1.0.1
com.google.guava:guava:30.1-jre
com.google.guava:guava:30.1.1-jre
com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava
com.google.http-client:google-http-client-apache-v2:1.39.0
com.google.http-client:google-http-client-appengine:1.39.0

View File

@@ -24,7 +24,7 @@ com.google.common.html.types:types:1.0.6
com.google.errorprone:error_prone_annotations:2.5.1
com.google.escapevelocity:escapevelocity:0.9.1
com.google.guava:failureaccess:1.0.1
com.google.guava:guava:30.1-jre
com.google.guava:guava:30.1.1-jre
com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava
com.google.http-client:google-http-client-apache-v2:1.39.0
com.google.http-client:google-http-client-appengine:1.39.0

View File

@@ -24,7 +24,7 @@ com.google.common.html.types:types:1.0.6
com.google.errorprone:error_prone_annotations:2.5.1
com.google.escapevelocity:escapevelocity:0.9.1
com.google.guava:failureaccess:1.0.1
com.google.guava:guava:30.1-jre
com.google.guava:guava:30.1.1-jre
com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava
com.google.http-client:google-http-client-apache-v2:1.39.0
com.google.http-client:google-http-client-appengine:1.39.0

View File

@@ -2,11 +2,11 @@
# Manual edits can break the build and are not advised.
# This file is expected to be part of source control.
com.google.code.findbugs:jsr305:3.0.2
com.google.errorprone:error_prone_annotations:2.3.4
com.google.errorprone:error_prone_annotations:2.5.1
com.google.guava:failureaccess:1.0.1
com.google.guava:guava:30.1-jre
com.google.guava:guava:30.1.1-jre
com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava
com.google.j2objc:j2objc-annotations:1.3
javax.inject:javax.inject:1
joda-time:joda-time:2.9.2
org.checkerframework:checker-qual:3.5.0
org.checkerframework:checker-qual:3.8.0

View File

@@ -2,11 +2,11 @@
# Manual edits can break the build and are not advised.
# This file is expected to be part of source control.
com.google.code.findbugs:jsr305:3.0.2
com.google.errorprone:error_prone_annotations:2.3.4
com.google.errorprone:error_prone_annotations:2.5.1
com.google.guava:failureaccess:1.0.1
com.google.guava:guava:30.1-jre
com.google.guava:guava:30.1.1-jre
com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava
com.google.j2objc:j2objc-annotations:1.3
javax.inject:javax.inject:1
joda-time:joda-time:2.9.2
org.checkerframework:checker-qual:3.5.0
org.checkerframework:checker-qual:3.8.0

View File

@@ -2,11 +2,11 @@
# Manual edits can break the build and are not advised.
# This file is expected to be part of source control.
com.google.code.findbugs:jsr305:3.0.2
com.google.errorprone:error_prone_annotations:2.3.4
com.google.errorprone:error_prone_annotations:2.5.1
com.google.guava:failureaccess:1.0.1
com.google.guava:guava:30.1-jre
com.google.guava:guava:30.1.1-jre
com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava
com.google.j2objc:j2objc-annotations:1.3
javax.inject:javax.inject:1
joda-time:joda-time:2.9.2
org.checkerframework:checker-qual:3.5.0
org.checkerframework:checker-qual:3.8.0

View File

@@ -2,11 +2,11 @@
# Manual edits can break the build and are not advised.
# This file is expected to be part of source control.
com.google.code.findbugs:jsr305:3.0.2
com.google.errorprone:error_prone_annotations:2.3.4
com.google.errorprone:error_prone_annotations:2.5.1
com.google.guava:failureaccess:1.0.1
com.google.guava:guava:30.1-jre
com.google.guava:guava:30.1.1-jre
com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava
com.google.j2objc:j2objc-annotations:1.3
javax.inject:javax.inject:1
joda-time:joda-time:2.9.2
org.checkerframework:checker-qual:3.5.0
org.checkerframework:checker-qual:3.8.0

View File

@@ -2,11 +2,11 @@
# Manual edits can break the build and are not advised.
# This file is expected to be part of source control.
com.google.code.findbugs:jsr305:3.0.2
com.google.errorprone:error_prone_annotations:2.3.4
com.google.errorprone:error_prone_annotations:2.5.1
com.google.guava:failureaccess:1.0.1
com.google.guava:guava:30.1-jre
com.google.guava:guava:30.1.1-jre
com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava
com.google.j2objc:j2objc-annotations:1.3
javax.inject:javax.inject:1
joda-time:joda-time:2.9.2
org.checkerframework:checker-qual:3.5.0
org.checkerframework:checker-qual:3.8.0

View File

@@ -2,11 +2,11 @@
# Manual edits can break the build and are not advised.
# This file is expected to be part of source control.
com.google.code.findbugs:jsr305:3.0.2
com.google.errorprone:error_prone_annotations:2.3.4
com.google.errorprone:error_prone_annotations:2.5.1
com.google.guava:failureaccess:1.0.1
com.google.guava:guava:30.1-jre
com.google.guava:guava:30.1.1-jre
com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava
com.google.j2objc:j2objc-annotations:1.3
javax.inject:javax.inject:1
joda-time:joda-time:2.9.2
org.checkerframework:checker-qual:3.5.0
org.checkerframework:checker-qual:3.8.0

View File

@@ -6,7 +6,7 @@ com.google.code.findbugs:jsr305:3.0.2
com.google.errorprone:error_prone_annotations:2.5.1
com.google.flogger:flogger:0.5.1
com.google.guava:failureaccess:1.0.1
com.google.guava:guava:30.1-jre
com.google.guava:guava:30.1.1-jre
com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava
com.google.j2objc:j2objc-annotations:1.3
com.google.truth:truth:1.1.2

View File

@@ -6,7 +6,7 @@ com.google.code.findbugs:jsr305:3.0.2
com.google.errorprone:error_prone_annotations:2.5.1
com.google.flogger:flogger:0.5.1
com.google.guava:failureaccess:1.0.1
com.google.guava:guava:30.1-jre
com.google.guava:guava:30.1.1-jre
com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava
com.google.j2objc:j2objc-annotations:1.3
com.google.truth:truth:1.1.2

View File

@@ -7,7 +7,7 @@ com.google.errorprone:error_prone_annotations:2.5.1
com.google.flogger:flogger-system-backend:0.5.1
com.google.flogger:flogger:0.5.1
com.google.guava:failureaccess:1.0.1
com.google.guava:guava:30.1-jre
com.google.guava:guava:30.1.1-jre
com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava
com.google.j2objc:j2objc-annotations:1.3
com.google.truth:truth:1.1.2

View File

@@ -7,7 +7,7 @@ com.google.errorprone:error_prone_annotations:2.5.1
com.google.flogger:flogger-system-backend:0.5.1
com.google.flogger:flogger:0.5.1
com.google.guava:failureaccess:1.0.1
com.google.guava:guava:30.1-jre
com.google.guava:guava:30.1.1-jre
com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava
com.google.j2objc:j2objc-annotations:1.3
com.google.truth:truth:1.1.2

View File

@@ -6,7 +6,7 @@ com.google.code.findbugs:jsr305:3.0.2
com.google.errorprone:error_prone_annotations:2.5.1
com.google.flogger:flogger:0.5.1
com.google.guava:failureaccess:1.0.1
com.google.guava:guava:30.1-jre
com.google.guava:guava:30.1.1-jre
com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava
com.google.j2objc:j2objc-annotations:1.3
com.google.truth:truth:1.1.2

View File

@@ -6,7 +6,7 @@ com.google.code.findbugs:jsr305:3.0.2
com.google.errorprone:error_prone_annotations:2.5.1
com.google.flogger:flogger:0.5.1
com.google.guava:failureaccess:1.0.1
com.google.guava:guava:30.1-jre
com.google.guava:guava:30.1.1-jre
com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava
com.google.j2objc:j2objc-annotations:1.3
com.google.truth:truth:1.1.2

View File

@@ -7,7 +7,7 @@ com.google.errorprone:error_prone_annotations:2.5.1
com.google.flogger:flogger-system-backend:0.5.1
com.google.flogger:flogger:0.5.1
com.google.guava:failureaccess:1.0.1
com.google.guava:guava:30.1-jre
com.google.guava:guava:30.1.1-jre
com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava
com.google.j2objc:j2objc-annotations:1.3
com.google.truth:truth:1.1.2

View File

@@ -7,7 +7,7 @@ com.google.errorprone:error_prone_annotations:2.5.1
com.google.flogger:flogger-system-backend:0.5.1
com.google.flogger:flogger:0.5.1
com.google.guava:failureaccess:1.0.1
com.google.guava:guava:30.1-jre
com.google.guava:guava:30.1.1-jre
com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava
com.google.j2objc:j2objc-annotations:1.3
com.google.truth:truth:1.1.2

View File

@@ -100,6 +100,15 @@ public class DateTimeUtils {
return ZonedDateTime.ofInstant(instant, ZoneId.of(dateTime.getZone().getID()).normalized());
}
/**
* Converts a Joda {@link DateTime} object to an equivalent java.time {@link ZonedDateTime}
* object.
*/
public static ZonedDateTime toZonedDateTime(DateTime dateTime, ZoneId zoneId) {
java.time.Instant instant = java.time.Instant.ofEpochMilli(dateTime.getMillis());
return ZonedDateTime.ofInstant(instant, zoneId);
}
/**
* Converts a java.time {@link ZonedDateTime} object to an equivalent Joda {@link DateTime}
* object.

View File

@@ -39,7 +39,7 @@ public final class SystemInfo {
pid.getOutputStream().close();
pid.waitFor();
} catch (IOException e) {
logger.atWarning().withCause(e).log("%s command not available", cmd);
logger.atWarning().withCause(e).log("%s command not available.", cmd);
return false;
}
return true;

View File

@@ -140,6 +140,8 @@ PROPERTIES = [
'a BEAM pipeline to image. Setting this property to empty string '
'will disable image generation.',
'/usr/bin/dot'),
Property('pipeline',
'The name of the Beam pipeline being staged.')
]
GRADLE_FLAGS = [

View File

@@ -17,9 +17,11 @@ These aren't built in to the static code analysis tools we use (e.g. Checkstyle,
Error Prone) so we must write them manually.
"""
import json
import os
from typing import List, Tuple
import sys
import textwrap
import re
# We should never analyze any generated files
@@ -28,6 +30,13 @@ UNIVERSALLY_SKIPPED_PATTERNS = {"/build/", "cloudbuild-caches", "/out/", ".git/"
FORBIDDEN = 1
REQUIRED = 2
# The list of expected json packages and their licenses.
# These should be one of the allowed licenses in:
# config/dependency-license/allowed_licenses.json
EXPECTED_JS_PACKAGES = [
'google-closure-library', # Owned by Google, Apache 2.0
]
class PresubmitCheck:
@@ -79,8 +88,8 @@ PRESUBMITS = {
r".*Copyright 20\d{2} The Nomulus Authors\. All Rights Reserved\.",
("java", "js", "soy", "sql", "py", "sh", "gradle"), {
".git", "/build/", "/generated/", "/generated_tests/",
"node_modules/", "JUnitBackports.java", "registrar_bin.",
"registrar_dbg.", "google-java-format-diff.py",
"node_modules/", "LocalStorageHelper.java", "FakeStorageRpc.java",
"registrar_bin.", "registrar_dbg.", "google-java-format-diff.py",
"nomulus.golden.sql", "soyutils_usegoog.js", "javascript/checks.js"
}, REQUIRED):
"File did not include the license header.",
@@ -204,17 +213,16 @@ PRESUBMITS = {
{"src/test", "ActivityReportingQueryBuilder.java",
# This class contains helper method to make queries in Beam.
"RegistryJpaIO.java",
"CreateSyntheticHistoryEntriesAction.java",
# TODO(b/179158393): Remove everything below, which should be done
# using Criteria
"ForeignKeyIndex.java",
"HistoryEntryDao.java",
"JpaTransactionManager.java",
"JpaTransactionManagerImpl.java",
# CriteriaQueryBuilder is a false positive
"CriteriaQueryBuilder.java",
"RdapDomainSearchAction.java",
"RdapNameserverSearchAction.java",
"RdapSearchActionBase.java",
"ReadOnlyCheckingEntityManager.java",
"RegistryQuery",
},
):
@@ -310,6 +318,26 @@ def verify_flyway_index():
return not success
def verify_javascript_deps():
"""Verifies that we haven't introduced any new javascript dependencies."""
with open('package.json') as f:
package = json.load(f)
deps = list(package['dependencies'].keys())
if deps != EXPECTED_JS_PACKAGES:
print('Unexpected javascript dependencies. Was expecting '
'%s, got %s.' % (EXPECTED_JS_PACKAGES, deps))
print(textwrap.dedent("""
* If the new dependencies are intentional, please verify that the
* license is one of the allowed licenses (see
* config/dependency-license/allowed_licenses.json) and add an entry
* for the package (with the license in a comment) to the
* EXPECTED_JS_PACKAGES variable in config/presubmits.py.
"""))
return True
return False
def get_files():
for root, dirnames, filenames in os.walk("."):
for filename in filenames:
@@ -317,6 +345,7 @@ def get_files():
if __name__ == "__main__":
print('python version is %s' % sys.version)
failed = False
for file in get_files():
error_messages = []
@@ -333,5 +362,8 @@ if __name__ == "__main__":
# when we put it here it fails fast before all of the tests are run.
failed |= verify_flyway_index()
# Make sure we haven't introduced any javascript dependencies.
failed |= verify_javascript_deps()
if failed:
sys.exit(1)

View File

@@ -44,7 +44,6 @@ def outcastTestPatterns = [
"google/registry/flows/domain/DomainCreateFlowTest.*",
"google/registry/flows/domain/DomainUpdateFlowTest.*",
"google/registry/tools/CreateDomainCommandTest.*",
"google/registry/tools/server/CreatePremiumListActionTest.*",
]
// Tests that fail when running Gradle in a docker container, e. g. when
@@ -70,12 +69,8 @@ def dockerIncompatibleTestPatterns = [
// Nomulus classes, e.g., threads and objects retained by frameworks.
// TODO(weiminyu): identify cause and fix offending tests.
def fragileTestPatterns = [
// Problem seems to lie with AppEngine TaskQueue for test.
"google/registry/cron/TldFanoutActionTest.*",
// Test Datastore inexplicably aborts transaction.
"google/registry/model/tmch/ClaimsListShardTest.*",
// Creates large object (64MBytes), occasionally throws OOM error.
"google/registry/model/server/KmsSecretRevisionTest.*",
// Changes cache timeouts and for some reason appears to have contention
// with other tests.
"google/registry/whois/WhoisCommandFactoryTest.*",
@@ -187,6 +182,7 @@ dependencies {
compile deps['com.google.monitoring-client:metrics']
compile deps['com.google.monitoring-client:stackdriver']
compile deps['com.google.api-client:google-api-client-java6']
compile deps['com.google.api.grpc:proto-google-cloud-tasks-v2']
compile deps['com.google.apis:google-api-services-admin-directory']
compile deps['com.google.apis:google-api-services-appengine']
compile deps['com.google.apis:google-api-services-bigquery']
@@ -197,6 +193,7 @@ dependencies {
compile deps['com.google.apis:google-api-services-groupssettings']
compile deps['com.google.apis:google-api-services-monitoring']
compile deps['com.google.apis:google-api-services-sheets']
compile deps['com.google.apis:google-api-services-storage']
testCompile deps['com.google.appengine:appengine-api-stubs']
compile deps['com.google.appengine.tools:appengine-gcs-client']
compile deps['com.google.appengine.tools:appengine-mapreduce']
@@ -218,9 +215,13 @@ dependencies {
compile deps['com.google.flogger:flogger']
runtime deps['com.google.flogger:flogger-system-backend']
compile deps['com.google.guava:guava']
compile deps['com.google.protobuf:protobuf-java']
gradleLint.ignore('unused-dependency') {
compile deps['com.google.gwt:gwt-user']
}
compile deps['com.google.cloud:google-cloud-core']
compile deps['com.google.cloud:google-cloud-storage']
compile deps['com.google.cloud:google-cloud-tasks']
compile deps['com.google.http-client:google-http-client']
compile deps['com.google.http-client:google-http-client-appengine']
compile deps['com.google.http-client:google-http-client-jackson2']
@@ -315,6 +316,7 @@ dependencies {
annotationProcessor project(':processor')
testAnnotationProcessor project(':processor')
testCompile deps['com.google.cloud:google-cloud-nio']
testCompile deps['com.google.appengine:appengine-testing']
testCompile deps['com.google.guava:guava-testlib']
testCompile deps['com.google.monitoring-client:contrib']
@@ -455,6 +457,22 @@ task soyToJava {
"--srcs", "${soyFiles.join(',')}",
"--compileTimeGlobalsFile", "${resourcesSourceDir}/google/registry/ui/globals.txt"
}
// Replace the "@link" tags after the "@deprecated" tags in the generated
// files. The soy compiler doesn't generate imports for these, causing
// us to get warnings when we generate javadocs.
// TODO(b/200296387): To be fair, the deprecations are accurate: we're
// using the old "SoyInfo" classes instead of the new "Templates" files.
// When we convert to the new classes, this hack can go away.
def outputs = fileTree(outputDirectory) {
include '**/*.java'
}
outputs.each { file ->
exec {
commandLine 'sed', '-i', 's/@link/LINK/g', file.getCanonicalPath()
}
}
}
doLast {
@@ -687,7 +705,11 @@ createToolTask(
createToolTask(
'jpaDemoPipeline', 'google.registry.beam.common.JpaDemoPipeline')
'jpaDemoPipeline', 'google.registry.beam.common.JpaDemoPipeline')
createToolTask(
'createSyntheticHistoryEntries',
'google.registry.tools.javascrap.CreateSyntheticHistoryEntriesPipeline')
project.tasks.create('initSqlPipeline', JavaExec) {
main = 'google.registry.beam.initsql.InitSqlPipeline'
@@ -754,50 +776,57 @@ createUberJar('nomulus', 'nomulus', 'google.registry.tools.RegistryTool')
// This packages more code and dependency than necessary. However, without
// restructuring the source tree it is difficult to generate leaner jars.
createUberJar(
'beam_pipeline_common',
'beamPipelineCommon',
'beam_pipeline_common',
'')
// Create beam staging task if environment is alpha or crash.
// All other environments use formally released pipelines through CloudBuild.
// Create beam staging task if the environment is alpha. Production, sandbox and
// qa use formally released pipelines through CloudBuild, whereas crash and
// alpha use the pipelines staged on alpha deployment project.
//
// User should install gcloud and login to GCP before invoking this tasks.
if (environment in ['alpha', 'crash']) {
if (environment == 'alpha') {
def pipelines = [
[
mainClass: 'google.registry.beam.initsql.InitSqlPipeline',
metaData: 'google/registry/beam/init_sql_pipeline_metadata.json'
],
[
mainClass: 'google.registry.beam.datastore.BulkDeleteDatastorePipeline',
metaData: 'google/registry/beam/bulk_delete_datastore_pipeline_metadata.json'
],
[
mainClass: 'google.registry.beam.spec11.Spec11Pipeline',
metaData: 'google/registry/beam/spec11_pipeline_metadata.json'
],
[
mainClass: 'google.registry.beam.invoicing.InvoicingPipeline',
metaData: 'google/registry/beam/invoicing_pipeline_metadata.json'
],
[
mainClass: 'google.registry.beam.rde.RdePipeline',
metaData: 'google/registry/beam/rde_pipeline_metadata.json'
],
initSql :
[
mainClass: 'google.registry.beam.initsql.InitSqlPipeline',
metaData : 'google/registry/beam/init_sql_pipeline_metadata.json'
],
bulkDeleteDatastore:
[
mainClass: 'google.registry.beam.datastore.BulkDeleteDatastorePipeline',
metaData : 'google/registry/beam/bulk_delete_datastore_pipeline_metadata.json'
],
spec11 :
[
mainClass: 'google.registry.beam.spec11.Spec11Pipeline',
metaData : 'google/registry/beam/spec11_pipeline_metadata.json'
],
invoicing :
[
mainClass: 'google.registry.beam.invoicing.InvoicingPipeline',
metaData : 'google/registry/beam/invoicing_pipeline_metadata.json'
],
rde :
[
mainClass: 'google.registry.beam.rde.RdePipeline',
metaData : 'google/registry/beam/rde_pipeline_metadata.json'
],
]
project.tasks.create("stage_beam_pipelines") {
project.tasks.create("stageBeamPipelines") {
doLast {
pipelines.each {
def mainClass = it['mainClass']
def metaData = it['metaData']
def pipelineName = CaseFormat.UPPER_CAMEL.to(
CaseFormat.LOWER_UNDERSCORE,
mainClass.substring(mainClass.lastIndexOf('.') + 1))
def imageName = "gcr.io/${gcpProject}/beam/${pipelineName}"
def metaDataBaseName = metaData.substring(metaData.lastIndexOf('/') + 1)
def uberJarName = tasks.beam_pipeline_common.outputs.files.asPath
if (rootProject.pipeline == ''|| rootProject.pipeline == it.key) {
def mainClass = it.value['mainClass']
def metaData = it.value['metaData']
def pipelineName = CaseFormat.UPPER_CAMEL.to(
CaseFormat.LOWER_UNDERSCORE,
mainClass.substring(mainClass.lastIndexOf('.') + 1))
def imageName = "gcr.io/${gcpProject}/beam/${pipelineName}"
def metaDataBaseName = metaData.substring(metaData.lastIndexOf('/') + 1)
def uberJarName = tasks.beamPipelineCommon.outputs.files.asPath
def command = "\
def command = "\
gcloud dataflow flex-template build \
gs://${gcpProject}-deploy/live/beam/${metaDataBaseName} \
--image-gcr-path ${imageName}:live \
@@ -807,10 +836,11 @@ if (environment in ['alpha', 'crash']) {
--jar ${uberJarName} \
--env FLEX_TEMPLATE_JAVA_MAIN_CLASS=${mainClass} \
--project ${gcpProject}".toString()
rootProject.ext.execInBash(command, '/tmp')
rootProject.ext.execInBash(command, '/tmp')
}
}
}
}.dependsOn(tasks.beam_pipeline_common)
}.dependsOn(tasks.beamPipelineCommon)
}
// A jar with classes and resources from main sourceSet, excluding internal
@@ -1085,6 +1115,10 @@ test {
// TODO(weiminyu): Remove dependency on sqlIntegrationTest
}.dependsOn(fragileTest, outcastTest, standardTest, registryToolIntegrationTest, sqlIntegrationTest)
// When we override tests, we also break the cleanTest command.
cleanTest.dependsOn(cleanFragileTest, cleanOutcastTest, cleanStandardTest,
cleanRegistryToolIntegrationTest, cleanSqlIntegrationTest)
project.build.dependsOn devtool
project.build.dependsOn buildToolImage
project.build.dependsOn ':stage'

View File

@@ -14,14 +14,14 @@ com.google.dagger:dagger-producers:2.33
com.google.dagger:dagger-spi:2.33
com.google.dagger:dagger:2.33
com.google.errorprone:error_prone_annotation:2.3.4
com.google.errorprone:error_prone_annotations:2.3.4
com.google.errorprone:error_prone_annotations:2.5.1
com.google.errorprone:error_prone_check_api:2.3.4
com.google.errorprone:error_prone_core:2.3.4
com.google.errorprone:error_prone_type_annotations:2.3.4
com.google.errorprone:javac-shaded:9-dev-r4023-3
com.google.googlejavaformat:google-java-format:1.5
com.google.guava:failureaccess:1.0.1
com.google.guava:guava:30.1-jre
com.google.guava:guava:30.1.1-jre
com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava
com.google.j2objc:j2objc-annotations:1.3
com.google.protobuf:protobuf-java:3.4.0
@@ -32,7 +32,7 @@ javax.inject:javax.inject:1
javax.persistence:javax.persistence-api:2.2
net.ltgt.gradle.incap:incap:0.2
org.checkerframework:checker-compat-qual:2.5.3
org.checkerframework:checker-qual:3.5.0
org.checkerframework:checker-qual:3.8.0
org.checkerframework:dataflow:3.0.0
org.checkerframework:javacutil:3.0.0
org.jetbrains.kotlin:kotlin-stdlib-common:1.4.20

View File

@@ -54,12 +54,15 @@ com.google.api.grpc:proto-google-cloud-secretmanager-v1beta1:1.4.0
com.google.api.grpc:proto-google-cloud-spanner-admin-database-v1:2.0.2
com.google.api.grpc:proto-google-cloud-spanner-admin-instance-v1:2.0.2
com.google.api.grpc:proto-google-cloud-spanner-v1:2.0.2
com.google.api.grpc:proto-google-common-protos:2.1.0
com.google.api.grpc:proto-google-iam-v1:1.0.9
com.google.api:api-common:1.10.1
com.google.api:gax-grpc:1.62.0
com.google.api:gax-httpjson:0.76.1
com.google.api:gax:1.62.0
com.google.api.grpc:proto-google-cloud-tasks-v2:1.33.2
com.google.api.grpc:proto-google-cloud-tasks-v2beta2:0.89.2
com.google.api.grpc:proto-google-cloud-tasks-v2beta3:0.89.2
com.google.api.grpc:proto-google-common-protos:2.3.2
com.google.api.grpc:proto-google-iam-v1:1.0.14
com.google.api:api-common:1.10.4
com.google.api:gax-grpc:1.66.0
com.google.api:gax-httpjson:0.79.0
com.google.api:gax:1.66.0
com.google.apis:google-api-services-admin-directory:directory_v1-rev118-1.25.0
com.google.apis:google-api-services-appengine:v1-rev130-1.25.0
com.google.apis:google-api-services-bigquery:v2-rev20200916-1.30.10
@@ -76,17 +79,17 @@ com.google.apis:google-api-services-monitoring:v3-rev540-1.25.0
com.google.apis:google-api-services-pubsub:v1-rev20200713-1.30.10
com.google.apis:google-api-services-sheets:v4-rev612-1.25.0
com.google.apis:google-api-services-sqladmin:v1beta4-rev20210119-1.31.0
com.google.apis:google-api-services-storage:v1-rev20200927-1.30.10
com.google.apis:google-api-services-storage:v1-rev20210127-1.31.0
com.google.appengine.tools:appengine-gcs-client:0.8.1
com.google.appengine.tools:appengine-mapreduce:0.9
com.google.appengine.tools:appengine-pipeline:0.2.13
com.google.appengine:appengine-api-1.0-sdk:1.9.86
com.google.appengine:appengine-remote-api:1.9.86
com.google.appengine:appengine-testing:1.9.86
com.google.auth:google-auth-library-credentials:0.24.1
com.google.auth:google-auth-library-oauth2-http:0.24.1
com.google.auth:google-auth-library-credentials:0.26.0
com.google.auth:google-auth-library-oauth2-http:0.26.0
com.google.auto.service:auto-service-annotations:1.0-rc7
com.google.auto.value:auto-value-annotations:1.7.4
com.google.auto.value:auto-value-annotations:1.8.1
com.google.auto.value:auto-value:1.7.4
com.google.cloud.bigdataoss:gcsio:2.1.6
com.google.cloud.bigdataoss:util:2.1.6
@@ -97,31 +100,33 @@ com.google.cloud:google-cloud-bigquery:1.122.2
com.google.cloud:google-cloud-bigquerystorage:1.5.5
com.google.cloud:google-cloud-bigtable:1.14.0
com.google.cloud:google-cloud-core-grpc:1.93.9
com.google.cloud:google-cloud-core-http:1.93.9
com.google.cloud:google-cloud-core:1.93.9
com.google.cloud:google-cloud-core-http:1.94.1
com.google.cloud:google-cloud-core:1.94.3
com.google.cloud:google-cloud-pubsub:1.110.0
com.google.cloud:google-cloud-pubsublite:0.7.0
com.google.cloud:google-cloud-secretmanager:1.4.0
com.google.cloud:google-cloud-spanner:2.0.2
com.google.cloud:google-cloud-storage:1.113.12
com.google.cloud:google-cloud-tasks:1.33.2
com.google.code.findbugs:jsr305:3.0.2
com.google.code.gson:gson:2.8.6
com.google.code.gson:gson:2.8.7
com.google.common.html.types:types:1.0.6
com.google.dagger:dagger:2.33
com.google.errorprone:error_prone_annotations:2.5.1
com.google.errorprone:error_prone_annotations:2.7.1
com.google.escapevelocity:escapevelocity:0.9.1
com.google.flogger:flogger-system-backend:0.5.1
com.google.flogger:flogger:0.5.1
com.google.flogger:google-extensions:0.5.1
com.google.guava:failureaccess:1.0.1
com.google.guava:guava:30.1-jre
com.google.guava:guava:30.1.1-jre
com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava
com.google.gwt:gwt-user:2.9.0
com.google.http-client:google-http-client-apache-v2:1.39.0
com.google.http-client:google-http-client-appengine:1.39.0
com.google.http-client:google-http-client-gson:1.39.0
com.google.http-client:google-http-client-gson:1.39.2
com.google.http-client:google-http-client-jackson2:1.39.0
com.google.http-client:google-http-client-protobuf:1.33.0
com.google.http-client:google-http-client:1.39.0
com.google.http-client:google-http-client:1.39.2
com.google.inject.extensions:guice-multibindings:4.1.0
com.google.inject:guice:4.1.0
com.google.j2objc:j2objc-annotations:1.3
@@ -133,8 +138,8 @@ com.google.oauth-client:google-oauth-client-java6:1.31.4
com.google.oauth-client:google-oauth-client-jetty:1.31.4
com.google.oauth-client:google-oauth-client-servlet:1.31.4
com.google.oauth-client:google-oauth-client:1.31.4
com.google.protobuf:protobuf-java-util:3.15.2
com.google.protobuf:protobuf-java:3.15.2
com.google.protobuf:protobuf-java-util:3.17.3
com.google.protobuf:protobuf-java:3.17.3
com.google.re2j:re2j:1.6
com.google.template:soy:2021-02-01
com.googlecode.charts4j:charts4j:1.3
@@ -150,17 +155,17 @@ commons-logging:commons-logging:1.2
dnsjava:dnsjava:3.3.1
io.dropwizard.metrics:metrics-core:3.2.6
io.github.classgraph:classgraph:4.8.65
io.grpc:grpc-alts:1.36.0
io.grpc:grpc-api:1.36.0
io.grpc:grpc-auth:1.36.0
io.grpc:grpc-context:1.36.0
io.grpc:grpc-core:1.36.0
io.grpc:grpc-grpclb:1.36.0
io.grpc:grpc-netty-shaded:1.36.0
io.grpc:grpc-alts:1.39.0
io.grpc:grpc-api:1.39.0
io.grpc:grpc-auth:1.39.0
io.grpc:grpc-context:1.39.0
io.grpc:grpc-core:1.39.0
io.grpc:grpc-grpclb:1.39.0
io.grpc:grpc-netty-shaded:1.39.0
io.grpc:grpc-netty:1.32.2
io.grpc:grpc-protobuf-lite:1.36.0
io.grpc:grpc-protobuf:1.36.0
io.grpc:grpc-stub:1.36.0
io.grpc:grpc-protobuf-lite:1.39.0
io.grpc:grpc-protobuf:1.39.0
io.grpc:grpc-stub:1.39.0
io.netty:netty-buffer:4.1.51.Final
io.netty:netty-codec-http2:4.1.51.Final
io.netty:netty-codec-http:4.1.51.Final
@@ -221,7 +226,7 @@ org.bouncycastle:bcpg-jdk15on:1.61
org.bouncycastle:bcpkix-jdk15on:1.61
org.bouncycastle:bcprov-jdk15on:1.61
org.checkerframework:checker-compat-qual:2.5.5
org.checkerframework:checker-qual:3.7.0
org.checkerframework:checker-qual:3.8.0
org.codehaus.jackson:jackson-core-asl:1.9.13
org.codehaus.jackson:jackson-mapper-asl:1.9.13
org.codehaus.mojo:animal-sniffer-annotations:1.20
@@ -258,7 +263,7 @@ org.testcontainers:database-commons:1.15.2
org.testcontainers:jdbc:1.15.2
org.testcontainers:postgresql:1.15.2
org.testcontainers:testcontainers:1.15.2
org.threeten:threetenbp:1.5.0
org.threeten:threetenbp:1.5.1
org.tukaani:xz:1.5
org.w3c.css:sac:1.3
org.xerial.snappy:snappy-java:1.1.4

View File

@@ -53,12 +53,15 @@ com.google.api.grpc:proto-google-cloud-secretmanager-v1beta1:1.4.0
com.google.api.grpc:proto-google-cloud-spanner-admin-database-v1:2.0.2
com.google.api.grpc:proto-google-cloud-spanner-admin-instance-v1:2.0.2
com.google.api.grpc:proto-google-cloud-spanner-v1:2.0.2
com.google.api.grpc:proto-google-common-protos:2.1.0
com.google.api.grpc:proto-google-iam-v1:1.0.9
com.google.api:api-common:1.10.1
com.google.api:gax-grpc:1.62.0
com.google.api:gax-httpjson:0.76.1
com.google.api:gax:1.62.0
com.google.api.grpc:proto-google-cloud-tasks-v2:1.33.2
com.google.api.grpc:proto-google-cloud-tasks-v2beta2:0.89.2
com.google.api.grpc:proto-google-cloud-tasks-v2beta3:0.89.2
com.google.api.grpc:proto-google-common-protos:2.3.2
com.google.api.grpc:proto-google-iam-v1:1.0.14
com.google.api:api-common:1.10.4
com.google.api:gax-grpc:1.66.0
com.google.api:gax-httpjson:0.79.0
com.google.api:gax:1.66.0
com.google.apis:google-api-services-admin-directory:directory_v1-rev118-1.25.0
com.google.apis:google-api-services-appengine:v1-rev130-1.25.0
com.google.apis:google-api-services-bigquery:v2-rev20200916-1.30.10
@@ -75,17 +78,17 @@ com.google.apis:google-api-services-monitoring:v3-rev540-1.25.0
com.google.apis:google-api-services-pubsub:v1-rev20200713-1.30.10
com.google.apis:google-api-services-sheets:v4-rev612-1.25.0
com.google.apis:google-api-services-sqladmin:v1beta4-rev20210119-1.31.0
com.google.apis:google-api-services-storage:v1-rev20200927-1.30.10
com.google.apis:google-api-services-storage:v1-rev20210127-1.31.0
com.google.appengine.tools:appengine-gcs-client:0.8.1
com.google.appengine.tools:appengine-mapreduce:0.9
com.google.appengine.tools:appengine-pipeline:0.2.13
com.google.appengine:appengine-api-1.0-sdk:1.9.86
com.google.appengine:appengine-remote-api:1.9.86
com.google.appengine:appengine-testing:1.9.86
com.google.auth:google-auth-library-credentials:0.24.1
com.google.auth:google-auth-library-oauth2-http:0.24.1
com.google.auth:google-auth-library-credentials:0.26.0
com.google.auth:google-auth-library-oauth2-http:0.26.0
com.google.auto.service:auto-service-annotations:1.0-rc7
com.google.auto.value:auto-value-annotations:1.7.4
com.google.auto.value:auto-value-annotations:1.8.1
com.google.auto.value:auto-value:1.7.4
com.google.cloud.bigdataoss:gcsio:2.1.6
com.google.cloud.bigdataoss:util:2.1.6
@@ -96,30 +99,32 @@ com.google.cloud:google-cloud-bigquery:1.122.2
com.google.cloud:google-cloud-bigquerystorage:1.5.5
com.google.cloud:google-cloud-bigtable:1.14.0
com.google.cloud:google-cloud-core-grpc:1.93.9
com.google.cloud:google-cloud-core-http:1.93.9
com.google.cloud:google-cloud-core:1.93.9
com.google.cloud:google-cloud-core-http:1.94.1
com.google.cloud:google-cloud-core:1.94.3
com.google.cloud:google-cloud-pubsub:1.110.0
com.google.cloud:google-cloud-pubsublite:0.7.0
com.google.cloud:google-cloud-secretmanager:1.4.0
com.google.cloud:google-cloud-spanner:2.0.2
com.google.cloud:google-cloud-storage:1.113.12
com.google.cloud:google-cloud-tasks:1.33.2
com.google.code.findbugs:jsr305:3.0.2
com.google.code.gson:gson:2.8.6
com.google.code.gson:gson:2.8.7
com.google.common.html.types:types:1.0.6
com.google.dagger:dagger:2.33
com.google.errorprone:error_prone_annotations:2.5.1
com.google.errorprone:error_prone_annotations:2.7.1
com.google.escapevelocity:escapevelocity:0.9.1
com.google.flogger:flogger:0.5.1
com.google.flogger:google-extensions:0.5.1
com.google.guava:failureaccess:1.0.1
com.google.guava:guava:30.1-jre
com.google.guava:guava:30.1.1-jre
com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava
com.google.gwt:gwt-user:2.9.0
com.google.http-client:google-http-client-apache-v2:1.39.0
com.google.http-client:google-http-client-appengine:1.39.0
com.google.http-client:google-http-client-gson:1.39.0
com.google.http-client:google-http-client-gson:1.39.2
com.google.http-client:google-http-client-jackson2:1.39.0
com.google.http-client:google-http-client-protobuf:1.33.0
com.google.http-client:google-http-client:1.39.0
com.google.http-client:google-http-client:1.39.2
com.google.inject.extensions:guice-multibindings:4.1.0
com.google.inject:guice:4.1.0
com.google.j2objc:j2objc-annotations:1.3
@@ -131,8 +136,8 @@ com.google.oauth-client:google-oauth-client-java6:1.31.4
com.google.oauth-client:google-oauth-client-jetty:1.31.4
com.google.oauth-client:google-oauth-client-servlet:1.31.4
com.google.oauth-client:google-oauth-client:1.31.4
com.google.protobuf:protobuf-java-util:3.14.0
com.google.protobuf:protobuf-java:3.15.2
com.google.protobuf:protobuf-java-util:3.15.3
com.google.protobuf:protobuf-java:3.17.3
com.google.re2j:re2j:1.6
com.google.template:soy:2021-02-01
com.googlecode.charts4j:charts4j:1.3
@@ -148,17 +153,17 @@ commons-logging:commons-logging:1.2
dnsjava:dnsjava:3.3.1
io.dropwizard.metrics:metrics-core:3.2.6
io.github.classgraph:classgraph:4.8.65
io.grpc:grpc-alts:1.36.0
io.grpc:grpc-api:1.36.0
io.grpc:grpc-auth:1.36.0
io.grpc:grpc-context:1.36.0
io.grpc:grpc-core:1.36.0
io.grpc:grpc-grpclb:1.36.0
io.grpc:grpc-netty-shaded:1.36.0
io.grpc:grpc-alts:1.39.0
io.grpc:grpc-api:1.39.0
io.grpc:grpc-auth:1.39.0
io.grpc:grpc-context:1.39.0
io.grpc:grpc-core:1.39.0
io.grpc:grpc-grpclb:1.39.0
io.grpc:grpc-netty-shaded:1.39.0
io.grpc:grpc-netty:1.32.2
io.grpc:grpc-protobuf-lite:1.36.0
io.grpc:grpc-protobuf:1.36.0
io.grpc:grpc-stub:1.36.0
io.grpc:grpc-protobuf-lite:1.39.0
io.grpc:grpc-protobuf:1.39.0
io.grpc:grpc-stub:1.39.0
io.netty:netty-buffer:4.1.51.Final
io.netty:netty-codec-http2:4.1.51.Final
io.netty:netty-codec-http:4.1.51.Final
@@ -215,7 +220,7 @@ org.bouncycastle:bcpg-jdk15on:1.61
org.bouncycastle:bcpkix-jdk15on:1.61
org.bouncycastle:bcprov-jdk15on:1.61
org.checkerframework:checker-compat-qual:2.5.5
org.checkerframework:checker-qual:3.7.0
org.checkerframework:checker-qual:3.8.0
org.codehaus.jackson:jackson-core-asl:1.9.13
org.codehaus.jackson:jackson-mapper-asl:1.9.13
org.conscrypt:conscrypt-openjdk-uber:2.5.1
@@ -251,7 +256,7 @@ org.testcontainers:database-commons:1.15.2
org.testcontainers:jdbc:1.15.2
org.testcontainers:postgresql:1.15.2
org.testcontainers:testcontainers:1.15.2
org.threeten:threetenbp:1.5.0
org.threeten:threetenbp:1.5.1
org.tukaani:xz:1.5
org.w3c.css:sac:1.3
org.xerial.snappy:snappy-java:1.1.4

View File

@@ -58,12 +58,15 @@ com.google.api.grpc:proto-google-cloud-secretmanager-v1beta1:1.4.0
com.google.api.grpc:proto-google-cloud-spanner-admin-database-v1:2.0.2
com.google.api.grpc:proto-google-cloud-spanner-admin-instance-v1:2.0.2
com.google.api.grpc:proto-google-cloud-spanner-v1:2.0.2
com.google.api.grpc:proto-google-common-protos:2.1.0
com.google.api.grpc:proto-google-iam-v1:1.0.9
com.google.api:api-common:1.10.1
com.google.api:gax-grpc:1.62.0
com.google.api:gax-httpjson:0.76.1
com.google.api:gax:1.62.0
com.google.api.grpc:proto-google-cloud-tasks-v2:1.33.2
com.google.api.grpc:proto-google-cloud-tasks-v2beta2:0.89.2
com.google.api.grpc:proto-google-cloud-tasks-v2beta3:0.89.2
com.google.api.grpc:proto-google-common-protos:2.3.2
com.google.api.grpc:proto-google-iam-v1:1.0.14
com.google.api:api-common:1.10.4
com.google.api:gax-grpc:1.66.0
com.google.api:gax-httpjson:0.79.0
com.google.api:gax:1.66.0
com.google.apis:google-api-services-admin-directory:directory_v1-rev118-1.25.0
com.google.apis:google-api-services-appengine:v1-rev130-1.25.0
com.google.apis:google-api-services-bigquery:v2-rev20200916-1.30.10
@@ -80,17 +83,17 @@ com.google.apis:google-api-services-monitoring:v3-rev540-1.25.0
com.google.apis:google-api-services-pubsub:v1-rev20200713-1.30.10
com.google.apis:google-api-services-sheets:v4-rev612-1.25.0
com.google.apis:google-api-services-sqladmin:v1beta4-rev20210119-1.31.0
com.google.apis:google-api-services-storage:v1-rev20200927-1.30.10
com.google.apis:google-api-services-storage:v1-rev20210127-1.31.0
com.google.appengine.tools:appengine-gcs-client:0.8.1
com.google.appengine.tools:appengine-mapreduce:0.9
com.google.appengine.tools:appengine-pipeline:0.2.13
com.google.appengine:appengine-api-1.0-sdk:1.9.86
com.google.appengine:appengine-remote-api:1.9.86
com.google.appengine:appengine-testing:1.9.86
com.google.auth:google-auth-library-credentials:0.24.1
com.google.auth:google-auth-library-oauth2-http:0.24.1
com.google.auth:google-auth-library-credentials:0.26.0
com.google.auth:google-auth-library-oauth2-http:0.26.0
com.google.auto.service:auto-service-annotations:1.0-rc7
com.google.auto.value:auto-value-annotations:1.7.4
com.google.auto.value:auto-value-annotations:1.8.1
com.google.auto.value:auto-value:1.7.4
com.google.cloud.bigdataoss:gcsio:2.1.6
com.google.cloud.bigdataoss:util:2.1.6
@@ -102,31 +105,33 @@ com.google.cloud:google-cloud-bigquery:1.122.2
com.google.cloud:google-cloud-bigquerystorage:1.5.5
com.google.cloud:google-cloud-bigtable:1.14.0
com.google.cloud:google-cloud-core-grpc:1.93.9
com.google.cloud:google-cloud-core-http:1.93.9
com.google.cloud:google-cloud-core:1.93.9
com.google.cloud:google-cloud-core-http:1.94.1
com.google.cloud:google-cloud-core:1.94.3
com.google.cloud:google-cloud-pubsub:1.110.0
com.google.cloud:google-cloud-pubsublite:0.7.0
com.google.cloud:google-cloud-secretmanager:1.4.0
com.google.cloud:google-cloud-spanner:2.0.2
com.google.cloud:google-cloud-storage:1.113.12
com.google.cloud:google-cloud-tasks:1.33.2
com.google.code.findbugs:jsr305:3.0.2
com.google.code.gson:gson:2.8.6
com.google.code.gson:gson:2.8.7
com.google.common.html.types:types:1.0.6
com.google.dagger:dagger:2.33
com.google.errorprone:error_prone_annotations:2.5.1
com.google.errorprone:error_prone_annotations:2.7.1
com.google.escapevelocity:escapevelocity:0.9.1
com.google.flogger:flogger-system-backend:0.5.1
com.google.flogger:flogger:0.5.1
com.google.flogger:google-extensions:0.5.1
com.google.guava:failureaccess:1.0.1
com.google.guava:guava:30.1-jre
com.google.guava:guava:30.1.1-jre
com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava
com.google.gwt:gwt-user:2.9.0
com.google.http-client:google-http-client-apache-v2:1.39.0
com.google.http-client:google-http-client-appengine:1.39.0
com.google.http-client:google-http-client-gson:1.39.0
com.google.http-client:google-http-client-gson:1.39.2
com.google.http-client:google-http-client-jackson2:1.39.0
com.google.http-client:google-http-client-protobuf:1.33.0
com.google.http-client:google-http-client:1.39.0
com.google.http-client:google-http-client:1.39.2
com.google.inject.extensions:guice-multibindings:4.1.0
com.google.inject:guice:4.1.0
com.google.j2objc:j2objc-annotations:1.3
@@ -138,8 +143,8 @@ com.google.oauth-client:google-oauth-client-java6:1.31.4
com.google.oauth-client:google-oauth-client-jetty:1.31.4
com.google.oauth-client:google-oauth-client-servlet:1.31.4
com.google.oauth-client:google-oauth-client:1.31.4
com.google.protobuf:protobuf-java-util:3.15.2
com.google.protobuf:protobuf-java:3.15.2
com.google.protobuf:protobuf-java-util:3.17.3
com.google.protobuf:protobuf-java:3.17.3
com.google.re2j:re2j:1.6
com.google.template:soy:2021-02-01
com.googlecode.charts4j:charts4j:1.3
@@ -158,17 +163,17 @@ guru.nidi:graphviz-java-all-j2v8:0.17.0
guru.nidi:graphviz-java:0.17.0
io.dropwizard.metrics:metrics-core:3.2.6
io.github.classgraph:classgraph:4.8.65
io.grpc:grpc-alts:1.36.0
io.grpc:grpc-api:1.36.0
io.grpc:grpc-auth:1.36.0
io.grpc:grpc-context:1.36.0
io.grpc:grpc-core:1.36.0
io.grpc:grpc-grpclb:1.36.0
io.grpc:grpc-netty-shaded:1.36.0
io.grpc:grpc-alts:1.39.0
io.grpc:grpc-api:1.39.0
io.grpc:grpc-auth:1.39.0
io.grpc:grpc-context:1.39.0
io.grpc:grpc-core:1.39.0
io.grpc:grpc-grpclb:1.39.0
io.grpc:grpc-netty-shaded:1.39.0
io.grpc:grpc-netty:1.32.2
io.grpc:grpc-protobuf-lite:1.36.0
io.grpc:grpc-protobuf:1.36.0
io.grpc:grpc-stub:1.36.0
io.grpc:grpc-protobuf-lite:1.39.0
io.grpc:grpc-protobuf:1.39.0
io.grpc:grpc-stub:1.39.0
io.netty:netty-buffer:4.1.51.Final
io.netty:netty-codec-http2:4.1.51.Final
io.netty:netty-codec-http:4.1.51.Final
@@ -232,7 +237,7 @@ org.bouncycastle:bcpg-jdk15on:1.61
org.bouncycastle:bcpkix-jdk15on:1.61
org.bouncycastle:bcprov-jdk15on:1.61
org.checkerframework:checker-compat-qual:2.5.5
org.checkerframework:checker-qual:3.7.0
org.checkerframework:checker-qual:3.8.0
org.codehaus.jackson:jackson-core-asl:1.9.13
org.codehaus.jackson:jackson-mapper-asl:1.9.13
org.codehaus.mojo:animal-sniffer-annotations:1.20
@@ -272,7 +277,7 @@ org.testcontainers:database-commons:1.15.2
org.testcontainers:jdbc:1.15.2
org.testcontainers:postgresql:1.15.2
org.testcontainers:testcontainers:1.15.2
org.threeten:threetenbp:1.5.0
org.threeten:threetenbp:1.5.1
org.tukaani:xz:1.5
org.w3c.css:sac:1.3
org.webjars.npm:viz.js-for-graphviz-java:2.1.3

View File

@@ -58,12 +58,15 @@ com.google.api.grpc:proto-google-cloud-secretmanager-v1beta1:1.4.0
com.google.api.grpc:proto-google-cloud-spanner-admin-database-v1:2.0.2
com.google.api.grpc:proto-google-cloud-spanner-admin-instance-v1:2.0.2
com.google.api.grpc:proto-google-cloud-spanner-v1:2.0.2
com.google.api.grpc:proto-google-common-protos:2.1.0
com.google.api.grpc:proto-google-iam-v1:1.0.9
com.google.api:api-common:1.10.1
com.google.api:gax-grpc:1.62.0
com.google.api:gax-httpjson:0.76.1
com.google.api:gax:1.62.0
com.google.api.grpc:proto-google-cloud-tasks-v2:1.33.2
com.google.api.grpc:proto-google-cloud-tasks-v2beta2:0.89.2
com.google.api.grpc:proto-google-cloud-tasks-v2beta3:0.89.2
com.google.api.grpc:proto-google-common-protos:2.3.2
com.google.api.grpc:proto-google-iam-v1:1.0.14
com.google.api:api-common:1.10.4
com.google.api:gax-grpc:1.66.0
com.google.api:gax-httpjson:0.79.0
com.google.api:gax:1.66.0
com.google.apis:google-api-services-admin-directory:directory_v1-rev118-1.25.0
com.google.apis:google-api-services-appengine:v1-rev130-1.25.0
com.google.apis:google-api-services-bigquery:v2-rev20200916-1.30.10
@@ -80,17 +83,17 @@ com.google.apis:google-api-services-monitoring:v3-rev540-1.25.0
com.google.apis:google-api-services-pubsub:v1-rev20200713-1.30.10
com.google.apis:google-api-services-sheets:v4-rev612-1.25.0
com.google.apis:google-api-services-sqladmin:v1beta4-rev20210119-1.31.0
com.google.apis:google-api-services-storage:v1-rev20200927-1.30.10
com.google.apis:google-api-services-storage:v1-rev20210127-1.31.0
com.google.appengine.tools:appengine-gcs-client:0.8.1
com.google.appengine.tools:appengine-mapreduce:0.9
com.google.appengine.tools:appengine-pipeline:0.2.13
com.google.appengine:appengine-api-1.0-sdk:1.9.86
com.google.appengine:appengine-remote-api:1.9.86
com.google.appengine:appengine-testing:1.9.86
com.google.auth:google-auth-library-credentials:0.24.1
com.google.auth:google-auth-library-oauth2-http:0.24.1
com.google.auth:google-auth-library-credentials:0.26.0
com.google.auth:google-auth-library-oauth2-http:0.26.0
com.google.auto.service:auto-service-annotations:1.0-rc7
com.google.auto.value:auto-value-annotations:1.7.4
com.google.auto.value:auto-value-annotations:1.8.1
com.google.auto.value:auto-value:1.7.4
com.google.cloud.bigdataoss:gcsio:2.1.6
com.google.cloud.bigdataoss:util:2.1.6
@@ -102,31 +105,33 @@ com.google.cloud:google-cloud-bigquery:1.122.2
com.google.cloud:google-cloud-bigquerystorage:1.5.5
com.google.cloud:google-cloud-bigtable:1.14.0
com.google.cloud:google-cloud-core-grpc:1.93.9
com.google.cloud:google-cloud-core-http:1.93.9
com.google.cloud:google-cloud-core:1.93.9
com.google.cloud:google-cloud-core-http:1.94.1
com.google.cloud:google-cloud-core:1.94.3
com.google.cloud:google-cloud-pubsub:1.110.0
com.google.cloud:google-cloud-pubsublite:0.7.0
com.google.cloud:google-cloud-secretmanager:1.4.0
com.google.cloud:google-cloud-spanner:2.0.2
com.google.cloud:google-cloud-storage:1.113.12
com.google.cloud:google-cloud-tasks:1.33.2
com.google.code.findbugs:jsr305:3.0.2
com.google.code.gson:gson:2.8.6
com.google.code.gson:gson:2.8.7
com.google.common.html.types:types:1.0.6
com.google.dagger:dagger:2.33
com.google.errorprone:error_prone_annotations:2.5.1
com.google.errorprone:error_prone_annotations:2.7.1
com.google.escapevelocity:escapevelocity:0.9.1
com.google.flogger:flogger-system-backend:0.5.1
com.google.flogger:flogger:0.5.1
com.google.flogger:google-extensions:0.5.1
com.google.guava:failureaccess:1.0.1
com.google.guava:guava:30.1-jre
com.google.guava:guava:30.1.1-jre
com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava
com.google.gwt:gwt-user:2.9.0
com.google.http-client:google-http-client-apache-v2:1.39.0
com.google.http-client:google-http-client-appengine:1.39.0
com.google.http-client:google-http-client-gson:1.39.0
com.google.http-client:google-http-client-gson:1.39.2
com.google.http-client:google-http-client-jackson2:1.39.0
com.google.http-client:google-http-client-protobuf:1.33.0
com.google.http-client:google-http-client:1.39.0
com.google.http-client:google-http-client:1.39.2
com.google.inject.extensions:guice-multibindings:4.1.0
com.google.inject:guice:4.1.0
com.google.j2objc:j2objc-annotations:1.3
@@ -138,8 +143,8 @@ com.google.oauth-client:google-oauth-client-java6:1.31.4
com.google.oauth-client:google-oauth-client-jetty:1.31.4
com.google.oauth-client:google-oauth-client-servlet:1.31.4
com.google.oauth-client:google-oauth-client:1.31.4
com.google.protobuf:protobuf-java-util:3.15.2
com.google.protobuf:protobuf-java:3.15.2
com.google.protobuf:protobuf-java-util:3.17.3
com.google.protobuf:protobuf-java:3.17.3
com.google.re2j:re2j:1.6
com.google.template:soy:2021-02-01
com.googlecode.charts4j:charts4j:1.3
@@ -158,17 +163,17 @@ guru.nidi:graphviz-java-all-j2v8:0.17.0
guru.nidi:graphviz-java:0.17.0
io.dropwizard.metrics:metrics-core:3.2.6
io.github.classgraph:classgraph:4.8.65
io.grpc:grpc-alts:1.36.0
io.grpc:grpc-api:1.36.0
io.grpc:grpc-auth:1.36.0
io.grpc:grpc-context:1.36.0
io.grpc:grpc-core:1.36.0
io.grpc:grpc-grpclb:1.36.0
io.grpc:grpc-netty-shaded:1.36.0
io.grpc:grpc-alts:1.39.0
io.grpc:grpc-api:1.39.0
io.grpc:grpc-auth:1.39.0
io.grpc:grpc-context:1.39.0
io.grpc:grpc-core:1.39.0
io.grpc:grpc-grpclb:1.39.0
io.grpc:grpc-netty-shaded:1.39.0
io.grpc:grpc-netty:1.32.2
io.grpc:grpc-protobuf-lite:1.36.0
io.grpc:grpc-protobuf:1.36.0
io.grpc:grpc-stub:1.36.0
io.grpc:grpc-protobuf-lite:1.39.0
io.grpc:grpc-protobuf:1.39.0
io.grpc:grpc-stub:1.39.0
io.netty:netty-buffer:4.1.51.Final
io.netty:netty-codec-http2:4.1.51.Final
io.netty:netty-codec-http:4.1.51.Final
@@ -231,7 +236,7 @@ org.bouncycastle:bcpg-jdk15on:1.61
org.bouncycastle:bcpkix-jdk15on:1.61
org.bouncycastle:bcprov-jdk15on:1.61
org.checkerframework:checker-compat-qual:2.5.5
org.checkerframework:checker-qual:3.7.0
org.checkerframework:checker-qual:3.8.0
org.codehaus.jackson:jackson-core-asl:1.9.13
org.codehaus.jackson:jackson-mapper-asl:1.9.13
org.codehaus.mojo:animal-sniffer-annotations:1.20
@@ -271,7 +276,7 @@ org.testcontainers:database-commons:1.15.2
org.testcontainers:jdbc:1.15.2
org.testcontainers:postgresql:1.15.2
org.testcontainers:testcontainers:1.15.2
org.threeten:threetenbp:1.5.0
org.threeten:threetenbp:1.5.1
org.tukaani:xz:1.5
org.w3c.css:sac:1.3
org.webjars.npm:viz.js-for-graphviz-java:2.1.3

View File

@@ -54,12 +54,15 @@ com.google.api.grpc:proto-google-cloud-secretmanager-v1beta1:1.4.0
com.google.api.grpc:proto-google-cloud-spanner-admin-database-v1:2.0.2
com.google.api.grpc:proto-google-cloud-spanner-admin-instance-v1:2.0.2
com.google.api.grpc:proto-google-cloud-spanner-v1:2.0.2
com.google.api.grpc:proto-google-common-protos:2.1.0
com.google.api.grpc:proto-google-iam-v1:1.0.9
com.google.api:api-common:1.10.1
com.google.api:gax-grpc:1.62.0
com.google.api:gax-httpjson:0.76.1
com.google.api:gax:1.62.0
com.google.api.grpc:proto-google-cloud-tasks-v2:1.33.2
com.google.api.grpc:proto-google-cloud-tasks-v2beta2:0.89.2
com.google.api.grpc:proto-google-cloud-tasks-v2beta3:0.89.2
com.google.api.grpc:proto-google-common-protos:2.3.2
com.google.api.grpc:proto-google-iam-v1:1.0.14
com.google.api:api-common:1.10.4
com.google.api:gax-grpc:1.66.0
com.google.api:gax-httpjson:0.79.0
com.google.api:gax:1.66.0
com.google.apis:google-api-services-admin-directory:directory_v1-rev118-1.25.0
com.google.apis:google-api-services-appengine:v1-rev130-1.25.0
com.google.apis:google-api-services-bigquery:v2-rev20200916-1.30.10
@@ -76,17 +79,17 @@ com.google.apis:google-api-services-monitoring:v3-rev540-1.25.0
com.google.apis:google-api-services-pubsub:v1-rev20200713-1.30.10
com.google.apis:google-api-services-sheets:v4-rev612-1.25.0
com.google.apis:google-api-services-sqladmin:v1beta4-rev20210119-1.31.0
com.google.apis:google-api-services-storage:v1-rev20200927-1.30.10
com.google.apis:google-api-services-storage:v1-rev20210127-1.31.0
com.google.appengine.tools:appengine-gcs-client:0.8.1
com.google.appengine.tools:appengine-mapreduce:0.9
com.google.appengine.tools:appengine-pipeline:0.2.13
com.google.appengine:appengine-api-1.0-sdk:1.9.86
com.google.appengine:appengine-remote-api:1.9.86
com.google.appengine:appengine-testing:1.9.86
com.google.auth:google-auth-library-credentials:0.24.1
com.google.auth:google-auth-library-oauth2-http:0.24.1
com.google.auth:google-auth-library-credentials:0.26.0
com.google.auth:google-auth-library-oauth2-http:0.26.0
com.google.auto.service:auto-service-annotations:1.0-rc7
com.google.auto.value:auto-value-annotations:1.7.4
com.google.auto.value:auto-value-annotations:1.8.1
com.google.auto.value:auto-value:1.7.4
com.google.cloud.bigdataoss:gcsio:2.1.6
com.google.cloud.bigdataoss:util:2.1.6
@@ -97,31 +100,33 @@ com.google.cloud:google-cloud-bigquery:1.122.2
com.google.cloud:google-cloud-bigquerystorage:1.5.5
com.google.cloud:google-cloud-bigtable:1.14.0
com.google.cloud:google-cloud-core-grpc:1.93.9
com.google.cloud:google-cloud-core-http:1.93.9
com.google.cloud:google-cloud-core:1.93.9
com.google.cloud:google-cloud-core-http:1.94.1
com.google.cloud:google-cloud-core:1.94.3
com.google.cloud:google-cloud-pubsub:1.110.0
com.google.cloud:google-cloud-pubsublite:0.7.0
com.google.cloud:google-cloud-secretmanager:1.4.0
com.google.cloud:google-cloud-spanner:2.0.2
com.google.cloud:google-cloud-storage:1.113.12
com.google.cloud:google-cloud-tasks:1.33.2
com.google.code.findbugs:jsr305:3.0.2
com.google.code.gson:gson:2.8.6
com.google.code.gson:gson:2.8.7
com.google.common.html.types:types:1.0.6
com.google.dagger:dagger:2.33
com.google.errorprone:error_prone_annotations:2.5.1
com.google.errorprone:error_prone_annotations:2.7.1
com.google.escapevelocity:escapevelocity:0.9.1
com.google.flogger:flogger-system-backend:0.5.1
com.google.flogger:flogger:0.5.1
com.google.flogger:google-extensions:0.5.1
com.google.guava:failureaccess:1.0.1
com.google.guava:guava:30.1-jre
com.google.guava:guava:30.1.1-jre
com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava
com.google.gwt:gwt-user:2.9.0
com.google.http-client:google-http-client-apache-v2:1.39.0
com.google.http-client:google-http-client-appengine:1.39.0
com.google.http-client:google-http-client-gson:1.39.0
com.google.http-client:google-http-client-gson:1.39.2
com.google.http-client:google-http-client-jackson2:1.39.0
com.google.http-client:google-http-client-protobuf:1.33.0
com.google.http-client:google-http-client:1.39.0
com.google.http-client:google-http-client:1.39.2
com.google.inject.extensions:guice-multibindings:4.1.0
com.google.inject:guice:4.1.0
com.google.j2objc:j2objc-annotations:1.3
@@ -133,8 +138,8 @@ com.google.oauth-client:google-oauth-client-java6:1.31.4
com.google.oauth-client:google-oauth-client-jetty:1.31.4
com.google.oauth-client:google-oauth-client-servlet:1.31.4
com.google.oauth-client:google-oauth-client:1.31.4
com.google.protobuf:protobuf-java-util:3.15.2
com.google.protobuf:protobuf-java:3.15.2
com.google.protobuf:protobuf-java-util:3.17.3
com.google.protobuf:protobuf-java:3.17.3
com.google.re2j:re2j:1.6
com.google.template:soy:2021-02-01
com.googlecode.charts4j:charts4j:1.3
@@ -150,17 +155,17 @@ commons-logging:commons-logging:1.2
dnsjava:dnsjava:3.3.1
io.dropwizard.metrics:metrics-core:3.2.6
io.github.classgraph:classgraph:4.8.65
io.grpc:grpc-alts:1.36.0
io.grpc:grpc-api:1.36.0
io.grpc:grpc-auth:1.36.0
io.grpc:grpc-context:1.36.0
io.grpc:grpc-core:1.36.0
io.grpc:grpc-grpclb:1.36.0
io.grpc:grpc-netty-shaded:1.36.0
io.grpc:grpc-alts:1.39.0
io.grpc:grpc-api:1.39.0
io.grpc:grpc-auth:1.39.0
io.grpc:grpc-context:1.39.0
io.grpc:grpc-core:1.39.0
io.grpc:grpc-grpclb:1.39.0
io.grpc:grpc-netty-shaded:1.39.0
io.grpc:grpc-netty:1.32.2
io.grpc:grpc-protobuf-lite:1.36.0
io.grpc:grpc-protobuf:1.36.0
io.grpc:grpc-stub:1.36.0
io.grpc:grpc-protobuf-lite:1.39.0
io.grpc:grpc-protobuf:1.39.0
io.grpc:grpc-stub:1.39.0
io.netty:netty-buffer:4.1.51.Final
io.netty:netty-codec-http2:4.1.51.Final
io.netty:netty-codec-http:4.1.51.Final
@@ -221,7 +226,7 @@ org.bouncycastle:bcpg-jdk15on:1.61
org.bouncycastle:bcpkix-jdk15on:1.61
org.bouncycastle:bcprov-jdk15on:1.61
org.checkerframework:checker-compat-qual:2.5.5
org.checkerframework:checker-qual:3.7.0
org.checkerframework:checker-qual:3.8.0
org.codehaus.jackson:jackson-core-asl:1.9.13
org.codehaus.jackson:jackson-mapper-asl:1.9.13
org.codehaus.mojo:animal-sniffer-annotations:1.20
@@ -258,7 +263,7 @@ org.testcontainers:database-commons:1.15.2
org.testcontainers:jdbc:1.15.2
org.testcontainers:postgresql:1.15.2
org.testcontainers:testcontainers:1.15.2
org.threeten:threetenbp:1.5.0
org.threeten:threetenbp:1.5.1
org.tukaani:xz:1.5
org.w3c.css:sac:1.3
org.xerial.snappy:snappy-java:1.1.4

View File

@@ -53,12 +53,15 @@ com.google.api.grpc:proto-google-cloud-secretmanager-v1beta1:1.4.0
com.google.api.grpc:proto-google-cloud-spanner-admin-database-v1:2.0.2
com.google.api.grpc:proto-google-cloud-spanner-admin-instance-v1:2.0.2
com.google.api.grpc:proto-google-cloud-spanner-v1:2.0.2
com.google.api.grpc:proto-google-common-protos:2.1.0
com.google.api.grpc:proto-google-iam-v1:1.0.9
com.google.api:api-common:1.10.1
com.google.api:gax-grpc:1.62.0
com.google.api:gax-httpjson:0.76.1
com.google.api:gax:1.62.0
com.google.api.grpc:proto-google-cloud-tasks-v2:1.33.2
com.google.api.grpc:proto-google-cloud-tasks-v2beta2:0.89.2
com.google.api.grpc:proto-google-cloud-tasks-v2beta3:0.89.2
com.google.api.grpc:proto-google-common-protos:2.3.2
com.google.api.grpc:proto-google-iam-v1:1.0.14
com.google.api:api-common:1.10.4
com.google.api:gax-grpc:1.66.0
com.google.api:gax-httpjson:0.79.0
com.google.api:gax:1.66.0
com.google.apis:google-api-services-admin-directory:directory_v1-rev118-1.25.0
com.google.apis:google-api-services-appengine:v1-rev130-1.25.0
com.google.apis:google-api-services-bigquery:v2-rev20200916-1.30.10
@@ -75,17 +78,17 @@ com.google.apis:google-api-services-monitoring:v3-rev540-1.25.0
com.google.apis:google-api-services-pubsub:v1-rev20200713-1.30.10
com.google.apis:google-api-services-sheets:v4-rev612-1.25.0
com.google.apis:google-api-services-sqladmin:v1beta4-rev20210119-1.31.0
com.google.apis:google-api-services-storage:v1-rev20200927-1.30.10
com.google.apis:google-api-services-storage:v1-rev20210127-1.31.0
com.google.appengine.tools:appengine-gcs-client:0.8.1
com.google.appengine.tools:appengine-mapreduce:0.9
com.google.appengine.tools:appengine-pipeline:0.2.13
com.google.appengine:appengine-api-1.0-sdk:1.9.86
com.google.appengine:appengine-remote-api:1.9.86
com.google.appengine:appengine-testing:1.9.86
com.google.auth:google-auth-library-credentials:0.24.1
com.google.auth:google-auth-library-oauth2-http:0.24.1
com.google.auth:google-auth-library-credentials:0.26.0
com.google.auth:google-auth-library-oauth2-http:0.26.0
com.google.auto.service:auto-service-annotations:1.0-rc7
com.google.auto.value:auto-value-annotations:1.7.4
com.google.auto.value:auto-value-annotations:1.8.1
com.google.auto.value:auto-value:1.7.4
com.google.cloud.bigdataoss:gcsio:2.1.6
com.google.cloud.bigdataoss:util:2.1.6
@@ -96,30 +99,32 @@ com.google.cloud:google-cloud-bigquery:1.122.2
com.google.cloud:google-cloud-bigquerystorage:1.5.5
com.google.cloud:google-cloud-bigtable:1.14.0
com.google.cloud:google-cloud-core-grpc:1.93.9
com.google.cloud:google-cloud-core-http:1.93.9
com.google.cloud:google-cloud-core:1.93.9
com.google.cloud:google-cloud-core-http:1.94.1
com.google.cloud:google-cloud-core:1.94.3
com.google.cloud:google-cloud-pubsub:1.110.0
com.google.cloud:google-cloud-pubsublite:0.7.0
com.google.cloud:google-cloud-secretmanager:1.4.0
com.google.cloud:google-cloud-spanner:2.0.2
com.google.cloud:google-cloud-storage:1.113.12
com.google.cloud:google-cloud-tasks:1.33.2
com.google.code.findbugs:jsr305:3.0.2
com.google.code.gson:gson:2.8.6
com.google.code.gson:gson:2.8.7
com.google.common.html.types:types:1.0.6
com.google.dagger:dagger:2.33
com.google.errorprone:error_prone_annotations:2.5.1
com.google.errorprone:error_prone_annotations:2.7.1
com.google.escapevelocity:escapevelocity:0.9.1
com.google.flogger:flogger:0.5.1
com.google.flogger:google-extensions:0.5.1
com.google.guava:failureaccess:1.0.1
com.google.guava:guava:30.1-jre
com.google.guava:guava:30.1.1-jre
com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava
com.google.gwt:gwt-user:2.9.0
com.google.http-client:google-http-client-apache-v2:1.39.0
com.google.http-client:google-http-client-appengine:1.39.0
com.google.http-client:google-http-client-gson:1.39.0
com.google.http-client:google-http-client-gson:1.39.2
com.google.http-client:google-http-client-jackson2:1.39.0
com.google.http-client:google-http-client-protobuf:1.33.0
com.google.http-client:google-http-client:1.39.0
com.google.http-client:google-http-client:1.39.2
com.google.inject.extensions:guice-multibindings:4.1.0
com.google.inject:guice:4.1.0
com.google.j2objc:j2objc-annotations:1.3
@@ -131,8 +136,8 @@ com.google.oauth-client:google-oauth-client-java6:1.31.4
com.google.oauth-client:google-oauth-client-jetty:1.31.4
com.google.oauth-client:google-oauth-client-servlet:1.31.4
com.google.oauth-client:google-oauth-client:1.31.4
com.google.protobuf:protobuf-java-util:3.14.0
com.google.protobuf:protobuf-java:3.15.2
com.google.protobuf:protobuf-java-util:3.15.3
com.google.protobuf:protobuf-java:3.17.3
com.google.re2j:re2j:1.6
com.google.template:soy:2021-02-01
com.googlecode.charts4j:charts4j:1.3
@@ -148,17 +153,17 @@ commons-logging:commons-logging:1.2
dnsjava:dnsjava:3.3.1
io.dropwizard.metrics:metrics-core:3.2.6
io.github.classgraph:classgraph:4.8.65
io.grpc:grpc-alts:1.36.0
io.grpc:grpc-api:1.36.0
io.grpc:grpc-auth:1.36.0
io.grpc:grpc-context:1.36.0
io.grpc:grpc-core:1.36.0
io.grpc:grpc-grpclb:1.36.0
io.grpc:grpc-netty-shaded:1.36.0
io.grpc:grpc-alts:1.39.0
io.grpc:grpc-api:1.39.0
io.grpc:grpc-auth:1.39.0
io.grpc:grpc-context:1.39.0
io.grpc:grpc-core:1.39.0
io.grpc:grpc-grpclb:1.39.0
io.grpc:grpc-netty-shaded:1.39.0
io.grpc:grpc-netty:1.32.2
io.grpc:grpc-protobuf-lite:1.36.0
io.grpc:grpc-protobuf:1.36.0
io.grpc:grpc-stub:1.36.0
io.grpc:grpc-protobuf-lite:1.39.0
io.grpc:grpc-protobuf:1.39.0
io.grpc:grpc-stub:1.39.0
io.netty:netty-buffer:4.1.51.Final
io.netty:netty-codec-http2:4.1.51.Final
io.netty:netty-codec-http:4.1.51.Final
@@ -216,7 +221,7 @@ org.bouncycastle:bcpg-jdk15on:1.61
org.bouncycastle:bcpkix-jdk15on:1.61
org.bouncycastle:bcprov-jdk15on:1.61
org.checkerframework:checker-compat-qual:2.5.5
org.checkerframework:checker-qual:3.7.0
org.checkerframework:checker-qual:3.8.0
org.codehaus.jackson:jackson-core-asl:1.9.13
org.codehaus.jackson:jackson-mapper-asl:1.9.13
org.conscrypt:conscrypt-openjdk-uber:2.5.1
@@ -252,7 +257,7 @@ org.testcontainers:database-commons:1.15.2
org.testcontainers:jdbc:1.15.2
org.testcontainers:postgresql:1.15.2
org.testcontainers:testcontainers:1.15.2
org.threeten:threetenbp:1.5.0
org.threeten:threetenbp:1.5.1
org.tukaani:xz:1.5
org.w3c.css:sac:1.3
org.xerial.snappy:snappy-java:1.1.4

View File

@@ -58,12 +58,15 @@ com.google.api.grpc:proto-google-cloud-secretmanager-v1beta1:1.4.0
com.google.api.grpc:proto-google-cloud-spanner-admin-database-v1:2.0.2
com.google.api.grpc:proto-google-cloud-spanner-admin-instance-v1:2.0.2
com.google.api.grpc:proto-google-cloud-spanner-v1:2.0.2
com.google.api.grpc:proto-google-common-protos:2.1.0
com.google.api.grpc:proto-google-iam-v1:1.0.9
com.google.api:api-common:1.10.1
com.google.api:gax-grpc:1.62.0
com.google.api:gax-httpjson:0.76.1
com.google.api:gax:1.62.0
com.google.api.grpc:proto-google-cloud-tasks-v2:1.33.2
com.google.api.grpc:proto-google-cloud-tasks-v2beta2:0.89.2
com.google.api.grpc:proto-google-cloud-tasks-v2beta3:0.89.2
com.google.api.grpc:proto-google-common-protos:2.3.2
com.google.api.grpc:proto-google-iam-v1:1.0.14
com.google.api:api-common:1.10.4
com.google.api:gax-grpc:1.66.0
com.google.api:gax-httpjson:0.79.0
com.google.api:gax:1.66.0
com.google.apis:google-api-services-admin-directory:directory_v1-rev118-1.25.0
com.google.apis:google-api-services-appengine:v1-rev130-1.25.0
com.google.apis:google-api-services-bigquery:v2-rev20200916-1.30.10
@@ -80,17 +83,17 @@ com.google.apis:google-api-services-monitoring:v3-rev540-1.25.0
com.google.apis:google-api-services-pubsub:v1-rev20200713-1.30.10
com.google.apis:google-api-services-sheets:v4-rev612-1.25.0
com.google.apis:google-api-services-sqladmin:v1beta4-rev20210119-1.31.0
com.google.apis:google-api-services-storage:v1-rev20200927-1.30.10
com.google.apis:google-api-services-storage:v1-rev20210127-1.31.0
com.google.appengine.tools:appengine-gcs-client:0.8.1
com.google.appengine.tools:appengine-mapreduce:0.9
com.google.appengine.tools:appengine-pipeline:0.2.13
com.google.appengine:appengine-api-1.0-sdk:1.9.86
com.google.appengine:appengine-remote-api:1.9.86
com.google.appengine:appengine-testing:1.9.86
com.google.auth:google-auth-library-credentials:0.24.1
com.google.auth:google-auth-library-oauth2-http:0.24.1
com.google.auth:google-auth-library-credentials:0.26.0
com.google.auth:google-auth-library-oauth2-http:0.26.0
com.google.auto.service:auto-service-annotations:1.0-rc7
com.google.auto.value:auto-value-annotations:1.7.4
com.google.auto.value:auto-value-annotations:1.8.1
com.google.auto.value:auto-value:1.7.4
com.google.cloud.bigdataoss:gcsio:2.1.6
com.google.cloud.bigdataoss:util:2.1.6
@@ -101,31 +104,33 @@ com.google.cloud:google-cloud-bigquery:1.122.2
com.google.cloud:google-cloud-bigquerystorage:1.5.5
com.google.cloud:google-cloud-bigtable:1.14.0
com.google.cloud:google-cloud-core-grpc:1.93.9
com.google.cloud:google-cloud-core-http:1.93.9
com.google.cloud:google-cloud-core:1.93.9
com.google.cloud:google-cloud-core-http:1.94.1
com.google.cloud:google-cloud-core:1.94.3
com.google.cloud:google-cloud-pubsub:1.110.0
com.google.cloud:google-cloud-pubsublite:0.7.0
com.google.cloud:google-cloud-secretmanager:1.4.0
com.google.cloud:google-cloud-spanner:2.0.2
com.google.cloud:google-cloud-storage:1.113.12
com.google.cloud:google-cloud-tasks:1.33.2
com.google.code.findbugs:jsr305:3.0.2
com.google.code.gson:gson:2.8.6
com.google.code.gson:gson:2.8.7
com.google.common.html.types:types:1.0.6
com.google.dagger:dagger:2.33
com.google.errorprone:error_prone_annotations:2.5.1
com.google.errorprone:error_prone_annotations:2.7.1
com.google.escapevelocity:escapevelocity:0.9.1
com.google.flogger:flogger-system-backend:0.5.1
com.google.flogger:flogger:0.5.1
com.google.flogger:google-extensions:0.5.1
com.google.guava:failureaccess:1.0.1
com.google.guava:guava:30.1-jre
com.google.guava:guava:30.1.1-jre
com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava
com.google.gwt:gwt-user:2.9.0
com.google.http-client:google-http-client-apache-v2:1.39.0
com.google.http-client:google-http-client-appengine:1.39.0
com.google.http-client:google-http-client-gson:1.39.0
com.google.http-client:google-http-client-gson:1.39.2
com.google.http-client:google-http-client-jackson2:1.39.0
com.google.http-client:google-http-client-protobuf:1.33.0
com.google.http-client:google-http-client:1.39.0
com.google.http-client:google-http-client:1.39.2
com.google.inject.extensions:guice-multibindings:4.1.0
com.google.inject:guice:4.1.0
com.google.j2objc:j2objc-annotations:1.3
@@ -137,8 +142,8 @@ com.google.oauth-client:google-oauth-client-java6:1.31.4
com.google.oauth-client:google-oauth-client-jetty:1.31.4
com.google.oauth-client:google-oauth-client-servlet:1.31.4
com.google.oauth-client:google-oauth-client:1.31.4
com.google.protobuf:protobuf-java-util:3.15.2
com.google.protobuf:protobuf-java:3.15.2
com.google.protobuf:protobuf-java-util:3.17.3
com.google.protobuf:protobuf-java:3.17.3
com.google.re2j:re2j:1.6
com.google.template:soy:2021-02-01
com.googlecode.charts4j:charts4j:1.3
@@ -157,17 +162,17 @@ guru.nidi:graphviz-java-all-j2v8:0.17.0
guru.nidi:graphviz-java:0.17.0
io.dropwizard.metrics:metrics-core:3.2.6
io.github.classgraph:classgraph:4.8.65
io.grpc:grpc-alts:1.36.0
io.grpc:grpc-api:1.36.0
io.grpc:grpc-auth:1.36.0
io.grpc:grpc-context:1.36.0
io.grpc:grpc-core:1.36.0
io.grpc:grpc-grpclb:1.36.0
io.grpc:grpc-netty-shaded:1.36.0
io.grpc:grpc-alts:1.39.0
io.grpc:grpc-api:1.39.0
io.grpc:grpc-auth:1.39.0
io.grpc:grpc-context:1.39.0
io.grpc:grpc-core:1.39.0
io.grpc:grpc-grpclb:1.39.0
io.grpc:grpc-netty-shaded:1.39.0
io.grpc:grpc-netty:1.32.2
io.grpc:grpc-protobuf-lite:1.36.0
io.grpc:grpc-protobuf:1.36.0
io.grpc:grpc-stub:1.36.0
io.grpc:grpc-protobuf-lite:1.39.0
io.grpc:grpc-protobuf:1.39.0
io.grpc:grpc-stub:1.39.0
io.netty:netty-buffer:4.1.51.Final
io.netty:netty-codec-http2:4.1.51.Final
io.netty:netty-codec-http:4.1.51.Final
@@ -231,7 +236,7 @@ org.bouncycastle:bcpg-jdk15on:1.61
org.bouncycastle:bcpkix-jdk15on:1.61
org.bouncycastle:bcprov-jdk15on:1.61
org.checkerframework:checker-compat-qual:2.5.5
org.checkerframework:checker-qual:3.7.0
org.checkerframework:checker-qual:3.8.0
org.codehaus.jackson:jackson-core-asl:1.9.13
org.codehaus.jackson:jackson-mapper-asl:1.9.13
org.codehaus.mojo:animal-sniffer-annotations:1.20
@@ -270,7 +275,7 @@ org.testcontainers:database-commons:1.15.2
org.testcontainers:jdbc:1.15.2
org.testcontainers:postgresql:1.15.2
org.testcontainers:testcontainers:1.15.2
org.threeten:threetenbp:1.5.0
org.threeten:threetenbp:1.5.1
org.tukaani:xz:1.5
org.w3c.css:sac:1.3
org.webjars.npm:viz.js-for-graphviz-java:2.1.3

View File

@@ -58,12 +58,15 @@ com.google.api.grpc:proto-google-cloud-secretmanager-v1beta1:1.4.0
com.google.api.grpc:proto-google-cloud-spanner-admin-database-v1:2.0.2
com.google.api.grpc:proto-google-cloud-spanner-admin-instance-v1:2.0.2
com.google.api.grpc:proto-google-cloud-spanner-v1:2.0.2
com.google.api.grpc:proto-google-common-protos:2.1.0
com.google.api.grpc:proto-google-iam-v1:1.0.9
com.google.api:api-common:1.10.1
com.google.api:gax-grpc:1.62.0
com.google.api:gax-httpjson:0.76.1
com.google.api:gax:1.62.0
com.google.api.grpc:proto-google-cloud-tasks-v2:1.33.2
com.google.api.grpc:proto-google-cloud-tasks-v2beta2:0.89.2
com.google.api.grpc:proto-google-cloud-tasks-v2beta3:0.89.2
com.google.api.grpc:proto-google-common-protos:2.3.2
com.google.api.grpc:proto-google-iam-v1:1.0.14
com.google.api:api-common:1.10.4
com.google.api:gax-grpc:1.66.0
com.google.api:gax-httpjson:0.79.0
com.google.api:gax:1.66.0
com.google.apis:google-api-services-admin-directory:directory_v1-rev118-1.25.0
com.google.apis:google-api-services-appengine:v1-rev130-1.25.0
com.google.apis:google-api-services-bigquery:v2-rev20200916-1.30.10
@@ -80,17 +83,17 @@ com.google.apis:google-api-services-monitoring:v3-rev540-1.25.0
com.google.apis:google-api-services-pubsub:v1-rev20200713-1.30.10
com.google.apis:google-api-services-sheets:v4-rev612-1.25.0
com.google.apis:google-api-services-sqladmin:v1beta4-rev20210119-1.31.0
com.google.apis:google-api-services-storage:v1-rev20200927-1.30.10
com.google.apis:google-api-services-storage:v1-rev20210127-1.31.0
com.google.appengine.tools:appengine-gcs-client:0.8.1
com.google.appengine.tools:appengine-mapreduce:0.9
com.google.appengine.tools:appengine-pipeline:0.2.13
com.google.appengine:appengine-api-1.0-sdk:1.9.86
com.google.appengine:appengine-remote-api:1.9.86
com.google.appengine:appengine-testing:1.9.86
com.google.auth:google-auth-library-credentials:0.24.1
com.google.auth:google-auth-library-oauth2-http:0.24.1
com.google.auth:google-auth-library-credentials:0.26.0
com.google.auth:google-auth-library-oauth2-http:0.26.0
com.google.auto.service:auto-service-annotations:1.0-rc7
com.google.auto.value:auto-value-annotations:1.7.4
com.google.auto.value:auto-value-annotations:1.8.1
com.google.auto.value:auto-value:1.7.4
com.google.cloud.bigdataoss:gcsio:2.1.6
com.google.cloud.bigdataoss:util:2.1.6
@@ -101,31 +104,33 @@ com.google.cloud:google-cloud-bigquery:1.122.2
com.google.cloud:google-cloud-bigquerystorage:1.5.5
com.google.cloud:google-cloud-bigtable:1.14.0
com.google.cloud:google-cloud-core-grpc:1.93.9
com.google.cloud:google-cloud-core-http:1.93.9
com.google.cloud:google-cloud-core:1.93.9
com.google.cloud:google-cloud-core-http:1.94.1
com.google.cloud:google-cloud-core:1.94.3
com.google.cloud:google-cloud-pubsub:1.110.0
com.google.cloud:google-cloud-pubsublite:0.7.0
com.google.cloud:google-cloud-secretmanager:1.4.0
com.google.cloud:google-cloud-spanner:2.0.2
com.google.cloud:google-cloud-storage:1.113.12
com.google.cloud:google-cloud-tasks:1.33.2
com.google.code.findbugs:jsr305:3.0.2
com.google.code.gson:gson:2.8.6
com.google.code.gson:gson:2.8.7
com.google.common.html.types:types:1.0.6
com.google.dagger:dagger:2.33
com.google.errorprone:error_prone_annotations:2.5.1
com.google.errorprone:error_prone_annotations:2.7.1
com.google.escapevelocity:escapevelocity:0.9.1
com.google.flogger:flogger-system-backend:0.5.1
com.google.flogger:flogger:0.5.1
com.google.flogger:google-extensions:0.5.1
com.google.guava:failureaccess:1.0.1
com.google.guava:guava:30.1-jre
com.google.guava:guava:30.1.1-jre
com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava
com.google.gwt:gwt-user:2.9.0
com.google.http-client:google-http-client-apache-v2:1.39.0
com.google.http-client:google-http-client-appengine:1.39.0
com.google.http-client:google-http-client-gson:1.39.0
com.google.http-client:google-http-client-gson:1.39.2
com.google.http-client:google-http-client-jackson2:1.39.0
com.google.http-client:google-http-client-protobuf:1.33.0
com.google.http-client:google-http-client:1.39.0
com.google.http-client:google-http-client:1.39.2
com.google.inject.extensions:guice-multibindings:4.1.0
com.google.inject:guice:4.1.0
com.google.j2objc:j2objc-annotations:1.3
@@ -137,8 +142,8 @@ com.google.oauth-client:google-oauth-client-java6:1.31.4
com.google.oauth-client:google-oauth-client-jetty:1.31.4
com.google.oauth-client:google-oauth-client-servlet:1.31.4
com.google.oauth-client:google-oauth-client:1.31.4
com.google.protobuf:protobuf-java-util:3.15.2
com.google.protobuf:protobuf-java:3.15.2
com.google.protobuf:protobuf-java-util:3.17.3
com.google.protobuf:protobuf-java:3.17.3
com.google.re2j:re2j:1.6
com.google.template:soy:2021-02-01
com.googlecode.charts4j:charts4j:1.3
@@ -157,17 +162,17 @@ guru.nidi:graphviz-java-all-j2v8:0.17.0
guru.nidi:graphviz-java:0.17.0
io.dropwizard.metrics:metrics-core:3.2.6
io.github.classgraph:classgraph:4.8.65
io.grpc:grpc-alts:1.36.0
io.grpc:grpc-api:1.36.0
io.grpc:grpc-auth:1.36.0
io.grpc:grpc-context:1.36.0
io.grpc:grpc-core:1.36.0
io.grpc:grpc-grpclb:1.36.0
io.grpc:grpc-netty-shaded:1.36.0
io.grpc:grpc-alts:1.39.0
io.grpc:grpc-api:1.39.0
io.grpc:grpc-auth:1.39.0
io.grpc:grpc-context:1.39.0
io.grpc:grpc-core:1.39.0
io.grpc:grpc-grpclb:1.39.0
io.grpc:grpc-netty-shaded:1.39.0
io.grpc:grpc-netty:1.32.2
io.grpc:grpc-protobuf-lite:1.36.0
io.grpc:grpc-protobuf:1.36.0
io.grpc:grpc-stub:1.36.0
io.grpc:grpc-protobuf-lite:1.39.0
io.grpc:grpc-protobuf:1.39.0
io.grpc:grpc-stub:1.39.0
io.netty:netty-buffer:4.1.51.Final
io.netty:netty-codec-http2:4.1.51.Final
io.netty:netty-codec-http:4.1.51.Final
@@ -231,7 +236,7 @@ org.bouncycastle:bcpg-jdk15on:1.61
org.bouncycastle:bcpkix-jdk15on:1.61
org.bouncycastle:bcprov-jdk15on:1.61
org.checkerframework:checker-compat-qual:2.5.5
org.checkerframework:checker-qual:3.7.0
org.checkerframework:checker-qual:3.8.0
org.codehaus.jackson:jackson-core-asl:1.9.13
org.codehaus.jackson:jackson-mapper-asl:1.9.13
org.codehaus.mojo:animal-sniffer-annotations:1.20
@@ -270,7 +275,7 @@ org.testcontainers:database-commons:1.15.2
org.testcontainers:jdbc:1.15.2
org.testcontainers:postgresql:1.15.2
org.testcontainers:testcontainers:1.15.2
org.threeten:threetenbp:1.5.0
org.threeten:threetenbp:1.5.1
org.tukaani:xz:1.5
org.w3c.css:sac:1.3
org.webjars.npm:viz.js-for-graphviz-java:2.1.3

View File

@@ -58,12 +58,15 @@ com.google.api.grpc:proto-google-cloud-secretmanager-v1beta1:1.4.0
com.google.api.grpc:proto-google-cloud-spanner-admin-database-v1:2.0.2
com.google.api.grpc:proto-google-cloud-spanner-admin-instance-v1:2.0.2
com.google.api.grpc:proto-google-cloud-spanner-v1:2.0.2
com.google.api.grpc:proto-google-common-protos:2.1.0
com.google.api.grpc:proto-google-iam-v1:1.0.9
com.google.api:api-common:1.10.1
com.google.api:gax-grpc:1.62.0
com.google.api:gax-httpjson:0.76.1
com.google.api:gax:1.62.0
com.google.api.grpc:proto-google-cloud-tasks-v2:1.33.2
com.google.api.grpc:proto-google-cloud-tasks-v2beta2:0.89.2
com.google.api.grpc:proto-google-cloud-tasks-v2beta3:0.89.2
com.google.api.grpc:proto-google-common-protos:2.3.2
com.google.api.grpc:proto-google-iam-v1:1.0.14
com.google.api:api-common:1.10.4
com.google.api:gax-grpc:1.66.0
com.google.api:gax-httpjson:0.79.0
com.google.api:gax:1.66.0
com.google.apis:google-api-services-admin-directory:directory_v1-rev118-1.25.0
com.google.apis:google-api-services-appengine:v1-rev130-1.25.0
com.google.apis:google-api-services-bigquery:v2-rev20200916-1.30.10
@@ -80,17 +83,17 @@ com.google.apis:google-api-services-monitoring:v3-rev540-1.25.0
com.google.apis:google-api-services-pubsub:v1-rev20200713-1.30.10
com.google.apis:google-api-services-sheets:v4-rev612-1.25.0
com.google.apis:google-api-services-sqladmin:v1beta4-rev20210119-1.31.0
com.google.apis:google-api-services-storage:v1-rev20200927-1.30.10
com.google.apis:google-api-services-storage:v1-rev20210127-1.31.0
com.google.appengine.tools:appengine-gcs-client:0.8.1
com.google.appengine.tools:appengine-mapreduce:0.9
com.google.appengine.tools:appengine-pipeline:0.2.13
com.google.appengine:appengine-api-1.0-sdk:1.9.86
com.google.appengine:appengine-remote-api:1.9.86
com.google.appengine:appengine-testing:1.9.86
com.google.auth:google-auth-library-credentials:0.24.1
com.google.auth:google-auth-library-oauth2-http:0.24.1
com.google.auth:google-auth-library-credentials:0.26.0
com.google.auth:google-auth-library-oauth2-http:0.26.0
com.google.auto.service:auto-service-annotations:1.0-rc7
com.google.auto.value:auto-value-annotations:1.7.4
com.google.auto.value:auto-value-annotations:1.8.1
com.google.auto.value:auto-value:1.7.4
com.google.cloud.bigdataoss:gcsio:2.1.6
com.google.cloud.bigdataoss:util:2.1.6
@@ -101,31 +104,33 @@ com.google.cloud:google-cloud-bigquery:1.122.2
com.google.cloud:google-cloud-bigquerystorage:1.5.5
com.google.cloud:google-cloud-bigtable:1.14.0
com.google.cloud:google-cloud-core-grpc:1.93.9
com.google.cloud:google-cloud-core-http:1.93.9
com.google.cloud:google-cloud-core:1.93.9
com.google.cloud:google-cloud-core-http:1.94.1
com.google.cloud:google-cloud-core:1.94.3
com.google.cloud:google-cloud-pubsub:1.110.0
com.google.cloud:google-cloud-pubsublite:0.7.0
com.google.cloud:google-cloud-secretmanager:1.4.0
com.google.cloud:google-cloud-spanner:2.0.2
com.google.cloud:google-cloud-storage:1.113.12
com.google.cloud:google-cloud-tasks:1.33.2
com.google.code.findbugs:jsr305:3.0.2
com.google.code.gson:gson:2.8.6
com.google.code.gson:gson:2.8.7
com.google.common.html.types:types:1.0.6
com.google.dagger:dagger:2.33
com.google.errorprone:error_prone_annotations:2.5.1
com.google.errorprone:error_prone_annotations:2.7.1
com.google.escapevelocity:escapevelocity:0.9.1
com.google.flogger:flogger-system-backend:0.5.1
com.google.flogger:flogger:0.5.1
com.google.flogger:google-extensions:0.5.1
com.google.guava:failureaccess:1.0.1
com.google.guava:guava:30.1-jre
com.google.guava:guava:30.1.1-jre
com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava
com.google.gwt:gwt-user:2.9.0
com.google.http-client:google-http-client-apache-v2:1.39.0
com.google.http-client:google-http-client-appengine:1.39.0
com.google.http-client:google-http-client-gson:1.39.0
com.google.http-client:google-http-client-gson:1.39.2
com.google.http-client:google-http-client-jackson2:1.39.0
com.google.http-client:google-http-client-protobuf:1.33.0
com.google.http-client:google-http-client:1.39.0
com.google.http-client:google-http-client:1.39.2
com.google.inject.extensions:guice-multibindings:4.1.0
com.google.inject:guice:4.1.0
com.google.j2objc:j2objc-annotations:1.3
@@ -137,8 +142,8 @@ com.google.oauth-client:google-oauth-client-java6:1.31.4
com.google.oauth-client:google-oauth-client-jetty:1.31.4
com.google.oauth-client:google-oauth-client-servlet:1.31.4
com.google.oauth-client:google-oauth-client:1.31.4
com.google.protobuf:protobuf-java-util:3.15.2
com.google.protobuf:protobuf-java:3.15.2
com.google.protobuf:protobuf-java-util:3.17.3
com.google.protobuf:protobuf-java:3.17.3
com.google.re2j:re2j:1.6
com.google.template:soy:2021-02-01
com.googlecode.charts4j:charts4j:1.3
@@ -157,17 +162,17 @@ guru.nidi:graphviz-java-all-j2v8:0.17.0
guru.nidi:graphviz-java:0.17.0
io.dropwizard.metrics:metrics-core:3.2.6
io.github.classgraph:classgraph:4.8.65
io.grpc:grpc-alts:1.36.0
io.grpc:grpc-api:1.36.0
io.grpc:grpc-auth:1.36.0
io.grpc:grpc-context:1.36.0
io.grpc:grpc-core:1.36.0
io.grpc:grpc-grpclb:1.36.0
io.grpc:grpc-netty-shaded:1.36.0
io.grpc:grpc-alts:1.39.0
io.grpc:grpc-api:1.39.0
io.grpc:grpc-auth:1.39.0
io.grpc:grpc-context:1.39.0
io.grpc:grpc-core:1.39.0
io.grpc:grpc-grpclb:1.39.0
io.grpc:grpc-netty-shaded:1.39.0
io.grpc:grpc-netty:1.32.2
io.grpc:grpc-protobuf-lite:1.36.0
io.grpc:grpc-protobuf:1.36.0
io.grpc:grpc-stub:1.36.0
io.grpc:grpc-protobuf-lite:1.39.0
io.grpc:grpc-protobuf:1.39.0
io.grpc:grpc-stub:1.39.0
io.netty:netty-buffer:4.1.51.Final
io.netty:netty-codec-http2:4.1.51.Final
io.netty:netty-codec-http:4.1.51.Final
@@ -231,7 +236,7 @@ org.bouncycastle:bcpg-jdk15on:1.61
org.bouncycastle:bcpkix-jdk15on:1.61
org.bouncycastle:bcprov-jdk15on:1.61
org.checkerframework:checker-compat-qual:2.5.5
org.checkerframework:checker-qual:3.7.0
org.checkerframework:checker-qual:3.8.0
org.codehaus.jackson:jackson-core-asl:1.9.13
org.codehaus.jackson:jackson-mapper-asl:1.9.13
org.codehaus.mojo:animal-sniffer-annotations:1.20
@@ -270,7 +275,7 @@ org.testcontainers:database-commons:1.15.2
org.testcontainers:jdbc:1.15.2
org.testcontainers:postgresql:1.15.2
org.testcontainers:testcontainers:1.15.2
org.threeten:threetenbp:1.5.0
org.threeten:threetenbp:1.5.1
org.tukaani:xz:1.5
org.w3c.css:sac:1.3
org.webjars.npm:viz.js-for-graphviz-java:2.1.3

View File

@@ -58,12 +58,15 @@ com.google.api.grpc:proto-google-cloud-secretmanager-v1beta1:1.4.0
com.google.api.grpc:proto-google-cloud-spanner-admin-database-v1:2.0.2
com.google.api.grpc:proto-google-cloud-spanner-admin-instance-v1:2.0.2
com.google.api.grpc:proto-google-cloud-spanner-v1:2.0.2
com.google.api.grpc:proto-google-common-protos:2.1.0
com.google.api.grpc:proto-google-iam-v1:1.0.9
com.google.api:api-common:1.10.1
com.google.api:gax-grpc:1.62.0
com.google.api:gax-httpjson:0.76.1
com.google.api:gax:1.62.0
com.google.api.grpc:proto-google-cloud-tasks-v2:1.33.2
com.google.api.grpc:proto-google-cloud-tasks-v2beta2:0.89.2
com.google.api.grpc:proto-google-cloud-tasks-v2beta3:0.89.2
com.google.api.grpc:proto-google-common-protos:2.3.2
com.google.api.grpc:proto-google-iam-v1:1.0.14
com.google.api:api-common:1.10.4
com.google.api:gax-grpc:1.66.0
com.google.api:gax-httpjson:0.79.0
com.google.api:gax:1.66.0
com.google.apis:google-api-services-admin-directory:directory_v1-rev118-1.25.0
com.google.apis:google-api-services-appengine:v1-rev130-1.25.0
com.google.apis:google-api-services-bigquery:v2-rev20200916-1.30.10
@@ -80,17 +83,17 @@ com.google.apis:google-api-services-monitoring:v3-rev540-1.25.0
com.google.apis:google-api-services-pubsub:v1-rev20200713-1.30.10
com.google.apis:google-api-services-sheets:v4-rev612-1.25.0
com.google.apis:google-api-services-sqladmin:v1beta4-rev20210119-1.31.0
com.google.apis:google-api-services-storage:v1-rev20200927-1.30.10
com.google.apis:google-api-services-storage:v1-rev20210127-1.31.0
com.google.appengine.tools:appengine-gcs-client:0.8.1
com.google.appengine.tools:appengine-mapreduce:0.9
com.google.appengine.tools:appengine-pipeline:0.2.13
com.google.appengine:appengine-api-1.0-sdk:1.9.86
com.google.appengine:appengine-remote-api:1.9.86
com.google.appengine:appengine-testing:1.9.86
com.google.auth:google-auth-library-credentials:0.24.1
com.google.auth:google-auth-library-oauth2-http:0.24.1
com.google.auth:google-auth-library-credentials:0.26.0
com.google.auth:google-auth-library-oauth2-http:0.26.0
com.google.auto.service:auto-service-annotations:1.0-rc7
com.google.auto.value:auto-value-annotations:1.7.4
com.google.auto.value:auto-value-annotations:1.8.1
com.google.auto.value:auto-value:1.7.4
com.google.cloud.bigdataoss:gcsio:2.1.6
com.google.cloud.bigdataoss:util:2.1.6
@@ -102,31 +105,33 @@ com.google.cloud:google-cloud-bigquery:1.122.2
com.google.cloud:google-cloud-bigquerystorage:1.5.5
com.google.cloud:google-cloud-bigtable:1.14.0
com.google.cloud:google-cloud-core-grpc:1.93.9
com.google.cloud:google-cloud-core-http:1.93.9
com.google.cloud:google-cloud-core:1.93.9
com.google.cloud:google-cloud-core-http:1.94.1
com.google.cloud:google-cloud-core:1.94.3
com.google.cloud:google-cloud-pubsub:1.110.0
com.google.cloud:google-cloud-pubsublite:0.7.0
com.google.cloud:google-cloud-secretmanager:1.4.0
com.google.cloud:google-cloud-spanner:2.0.2
com.google.cloud:google-cloud-storage:1.113.12
com.google.cloud:google-cloud-tasks:1.33.2
com.google.code.findbugs:jsr305:3.0.2
com.google.code.gson:gson:2.8.6
com.google.code.gson:gson:2.8.7
com.google.common.html.types:types:1.0.6
com.google.dagger:dagger:2.33
com.google.errorprone:error_prone_annotations:2.5.1
com.google.errorprone:error_prone_annotations:2.7.1
com.google.escapevelocity:escapevelocity:0.9.1
com.google.flogger:flogger-system-backend:0.5.1
com.google.flogger:flogger:0.5.1
com.google.flogger:google-extensions:0.5.1
com.google.guava:failureaccess:1.0.1
com.google.guava:guava:30.1-jre
com.google.guava:guava:30.1.1-jre
com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava
com.google.gwt:gwt-user:2.9.0
com.google.http-client:google-http-client-apache-v2:1.39.0
com.google.http-client:google-http-client-appengine:1.39.0
com.google.http-client:google-http-client-gson:1.39.0
com.google.http-client:google-http-client-gson:1.39.2
com.google.http-client:google-http-client-jackson2:1.39.0
com.google.http-client:google-http-client-protobuf:1.33.0
com.google.http-client:google-http-client:1.39.0
com.google.http-client:google-http-client:1.39.2
com.google.inject.extensions:guice-multibindings:4.1.0
com.google.inject:guice:4.1.0
com.google.j2objc:j2objc-annotations:1.3
@@ -138,8 +143,8 @@ com.google.oauth-client:google-oauth-client-java6:1.31.4
com.google.oauth-client:google-oauth-client-jetty:1.31.4
com.google.oauth-client:google-oauth-client-servlet:1.31.4
com.google.oauth-client:google-oauth-client:1.31.4
com.google.protobuf:protobuf-java-util:3.15.2
com.google.protobuf:protobuf-java:3.15.2
com.google.protobuf:protobuf-java-util:3.17.3
com.google.protobuf:protobuf-java:3.17.3
com.google.re2j:re2j:1.6
com.google.template:soy:2021-02-01
com.googlecode.charts4j:charts4j:1.3
@@ -158,17 +163,17 @@ guru.nidi:graphviz-java-all-j2v8:0.17.0
guru.nidi:graphviz-java:0.17.0
io.dropwizard.metrics:metrics-core:3.2.6
io.github.classgraph:classgraph:4.8.65
io.grpc:grpc-alts:1.36.0
io.grpc:grpc-api:1.36.0
io.grpc:grpc-auth:1.36.0
io.grpc:grpc-context:1.36.0
io.grpc:grpc-core:1.36.0
io.grpc:grpc-grpclb:1.36.0
io.grpc:grpc-netty-shaded:1.36.0
io.grpc:grpc-alts:1.39.0
io.grpc:grpc-api:1.39.0
io.grpc:grpc-auth:1.39.0
io.grpc:grpc-context:1.39.0
io.grpc:grpc-core:1.39.0
io.grpc:grpc-grpclb:1.39.0
io.grpc:grpc-netty-shaded:1.39.0
io.grpc:grpc-netty:1.32.2
io.grpc:grpc-protobuf-lite:1.36.0
io.grpc:grpc-protobuf:1.36.0
io.grpc:grpc-stub:1.36.0
io.grpc:grpc-protobuf-lite:1.39.0
io.grpc:grpc-protobuf:1.39.0
io.grpc:grpc-stub:1.39.0
io.netty:netty-buffer:4.1.51.Final
io.netty:netty-codec-http2:4.1.51.Final
io.netty:netty-codec-http:4.1.51.Final
@@ -231,7 +236,7 @@ org.bouncycastle:bcpg-jdk15on:1.61
org.bouncycastle:bcpkix-jdk15on:1.61
org.bouncycastle:bcprov-jdk15on:1.61
org.checkerframework:checker-compat-qual:2.5.5
org.checkerframework:checker-qual:3.7.0
org.checkerframework:checker-qual:3.8.0
org.codehaus.jackson:jackson-core-asl:1.9.13
org.codehaus.jackson:jackson-mapper-asl:1.9.13
org.codehaus.mojo:animal-sniffer-annotations:1.20
@@ -271,7 +276,7 @@ org.testcontainers:database-commons:1.15.2
org.testcontainers:jdbc:1.15.2
org.testcontainers:postgresql:1.15.2
org.testcontainers:testcontainers:1.15.2
org.threeten:threetenbp:1.5.0
org.threeten:threetenbp:1.5.1
org.tukaani:xz:1.5
org.w3c.css:sac:1.3
org.webjars.npm:viz.js-for-graphviz-java:2.1.3

View File

@@ -12,14 +12,14 @@ com.google.dagger:dagger-producers:2.33
com.google.dagger:dagger-spi:2.33
com.google.dagger:dagger:2.33
com.google.errorprone:error_prone_annotation:2.3.4
com.google.errorprone:error_prone_annotations:2.3.4
com.google.errorprone:error_prone_annotations:2.5.1
com.google.errorprone:error_prone_check_api:2.3.4
com.google.errorprone:error_prone_core:2.3.4
com.google.errorprone:error_prone_type_annotations:2.3.4
com.google.errorprone:javac-shaded:9-dev-r4023-3
com.google.googlejavaformat:google-java-format:1.5
com.google.guava:failureaccess:1.0.1
com.google.guava:guava:30.1-jre
com.google.guava:guava:30.1.1-jre
com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava
com.google.j2objc:j2objc-annotations:1.3
com.google.protobuf:protobuf-java:3.4.0
@@ -30,7 +30,7 @@ javax.inject:javax.inject:1
javax.persistence:javax.persistence-api:2.2
net.ltgt.gradle.incap:incap:0.2
org.checkerframework:checker-compat-qual:2.5.3
org.checkerframework:checker-qual:3.5.0
org.checkerframework:checker-qual:3.8.0
org.checkerframework:dataflow:3.0.0
org.checkerframework:javacutil:3.0.0
org.jetbrains.kotlin:kotlin-stdlib-common:1.4.20

View File

@@ -6,10 +6,10 @@ aopalliance:aopalliance:1.0
args4j:args4j:2.0.23
cglib:cglib-nodep:2.2
com.beust:jcommander:1.60
com.fasterxml.jackson.core:jackson-annotations:2.12.1
com.fasterxml.jackson.core:jackson-core:2.12.1
com.fasterxml.jackson.core:jackson-databind:2.12.1
com.fasterxml.jackson:jackson-bom:2.12.1
com.fasterxml.jackson.core:jackson-annotations:2.12.3
com.fasterxml.jackson.core:jackson-core:2.12.3
com.fasterxml.jackson.core:jackson-databind:2.12.3
com.fasterxml.jackson:jackson-bom:2.12.3
com.fasterxml:classmate:1.5.1
com.github.docker-java:docker-java-api:3.2.7
com.github.docker-java:docker-java-transport-zerodep:3.2.7
@@ -28,7 +28,7 @@ com.google.api-client:google-api-client-appengine:1.31.3
com.google.api-client:google-api-client-jackson2:1.30.10
com.google.api-client:google-api-client-java6:1.31.3
com.google.api-client:google-api-client-servlet:1.31.3
com.google.api-client:google-api-client:1.31.3
com.google.api-client:google-api-client:1.32.1
com.google.api.grpc:grpc-google-cloud-bigquerystorage-v1:1.5.5
com.google.api.grpc:grpc-google-cloud-bigquerystorage-v1beta1:0.105.5
com.google.api.grpc:grpc-google-cloud-bigquerystorage-v1beta2:0.105.5
@@ -54,12 +54,15 @@ com.google.api.grpc:proto-google-cloud-secretmanager-v1beta1:1.4.0
com.google.api.grpc:proto-google-cloud-spanner-admin-database-v1:2.0.2
com.google.api.grpc:proto-google-cloud-spanner-admin-instance-v1:2.0.2
com.google.api.grpc:proto-google-cloud-spanner-v1:2.0.2
com.google.api.grpc:proto-google-common-protos:2.1.0
com.google.api.grpc:proto-google-iam-v1:1.0.9
com.google.api:api-common:1.10.1
com.google.api:gax-grpc:1.62.0
com.google.api:gax-httpjson:0.76.1
com.google.api:gax:1.62.0
com.google.api.grpc:proto-google-cloud-tasks-v2:1.33.2
com.google.api.grpc:proto-google-cloud-tasks-v2beta2:0.89.2
com.google.api.grpc:proto-google-cloud-tasks-v2beta3:0.89.2
com.google.api.grpc:proto-google-common-protos:2.3.2
com.google.api.grpc:proto-google-iam-v1:1.0.14
com.google.api:api-common:1.10.4
com.google.api:gax-grpc:1.66.0
com.google.api:gax-httpjson:0.83.0
com.google.api:gax:1.66.0
com.google.apis:google-api-services-admin-directory:directory_v1-rev118-1.25.0
com.google.apis:google-api-services-appengine:v1-rev130-1.25.0
com.google.apis:google-api-services-bigquery:v2-rev20200916-1.30.10
@@ -76,7 +79,7 @@ com.google.apis:google-api-services-monitoring:v3-rev540-1.25.0
com.google.apis:google-api-services-pubsub:v1-rev20200713-1.30.10
com.google.apis:google-api-services-sheets:v4-rev612-1.25.0
com.google.apis:google-api-services-sqladmin:v1beta4-rev20210119-1.31.0
com.google.apis:google-api-services-storage:v1-rev20200927-1.30.10
com.google.apis:google-api-services-storage:v1-rev20210127-1.32.1
com.google.appengine.tools:appengine-gcs-client:0.8.1
com.google.appengine.tools:appengine-mapreduce:0.9
com.google.appengine.tools:appengine-pipeline:0.2.13
@@ -84,10 +87,10 @@ com.google.appengine:appengine-api-1.0-sdk:1.9.86
com.google.appengine:appengine-api-stubs:1.9.86
com.google.appengine:appengine-remote-api:1.9.86
com.google.appengine:appengine-testing:1.9.86
com.google.auth:google-auth-library-credentials:0.24.1
com.google.auth:google-auth-library-oauth2-http:0.24.1
com.google.auth:google-auth-library-credentials:0.26.0
com.google.auth:google-auth-library-oauth2-http:0.26.0
com.google.auto.service:auto-service-annotations:1.0-rc7
com.google.auto.value:auto-value-annotations:1.7.4
com.google.auto.value:auto-value-annotations:1.8.1
com.google.auto.value:auto-value:1.7.4
com.google.cloud.bigdataoss:gcsio:2.1.6
com.google.cloud.bigdataoss:util:2.1.6
@@ -98,32 +101,35 @@ com.google.cloud:google-cloud-bigquery:1.122.2
com.google.cloud:google-cloud-bigquerystorage:1.5.5
com.google.cloud:google-cloud-bigtable:1.14.0
com.google.cloud:google-cloud-core-grpc:1.93.9
com.google.cloud:google-cloud-core-http:1.93.9
com.google.cloud:google-cloud-core:1.93.9
com.google.cloud:google-cloud-core-http:1.95.4
com.google.cloud:google-cloud-core:1.95.4
com.google.cloud:google-cloud-nio:0.123.4
com.google.cloud:google-cloud-pubsub:1.110.0
com.google.cloud:google-cloud-pubsublite:0.7.0
com.google.cloud:google-cloud-secretmanager:1.4.0
com.google.cloud:google-cloud-spanner:2.0.2
com.google.cloud:google-cloud-storage:1.118.0
com.google.cloud:google-cloud-tasks:1.33.2
com.google.code.findbugs:jsr305:3.0.2
com.google.code.gson:gson:2.8.6
com.google.code.gson:gson:2.8.7
com.google.common.html.types:types:1.0.6
com.google.dagger:dagger:2.33
com.google.errorprone:error_prone_annotations:2.5.1
com.google.errorprone:error_prone_annotations:2.7.1
com.google.escapevelocity:escapevelocity:0.9.1
com.google.flogger:flogger-system-backend:0.5.1
com.google.flogger:flogger:0.5.1
com.google.flogger:google-extensions:0.5.1
com.google.guava:failureaccess:1.0.1
com.google.guava:guava-testlib:30.1-jre
com.google.guava:guava:30.1-jre
com.google.guava:guava-testlib:30.1.1-jre
com.google.guava:guava:30.1.1-jre
com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava
com.google.gwt:gwt-user:2.9.0
com.google.http-client:google-http-client-apache-v2:1.39.0
com.google.http-client:google-http-client-appengine:1.39.0
com.google.http-client:google-http-client-gson:1.39.0
com.google.http-client:google-http-client-jackson2:1.39.0
com.google.http-client:google-http-client-apache-v2:1.39.2
com.google.http-client:google-http-client-appengine:1.39.2
com.google.http-client:google-http-client-gson:1.39.2
com.google.http-client:google-http-client-jackson2:1.39.2
com.google.http-client:google-http-client-protobuf:1.33.0
com.google.http-client:google-http-client:1.39.0
com.google.http-client:google-http-client:1.39.2
com.google.inject.extensions:guice-multibindings:4.1.0
com.google.inject:guice:4.1.0
com.google.j2objc:j2objc-annotations:1.3
@@ -135,9 +141,9 @@ com.google.oauth-client:google-oauth-client-appengine:1.31.4
com.google.oauth-client:google-oauth-client-java6:1.31.4
com.google.oauth-client:google-oauth-client-jetty:1.31.4
com.google.oauth-client:google-oauth-client-servlet:1.31.4
com.google.oauth-client:google-oauth-client:1.31.4
com.google.protobuf:protobuf-java-util:3.15.2
com.google.protobuf:protobuf-java:3.15.2
com.google.oauth-client:google-oauth-client:1.31.5
com.google.protobuf:protobuf-java-util:3.17.3
com.google.protobuf:protobuf-java:3.17.3
com.google.re2j:re2j:1.6
com.google.template:soy:2021-02-01
com.google.truth.extensions:truth-java8-extension:1.1.2
@@ -158,17 +164,17 @@ commons-logging:commons-logging:1.2
dnsjava:dnsjava:3.3.1
io.dropwizard.metrics:metrics-core:3.2.6
io.github.classgraph:classgraph:4.8.102
io.grpc:grpc-alts:1.36.0
io.grpc:grpc-api:1.36.0
io.grpc:grpc-auth:1.36.0
io.grpc:grpc-context:1.36.0
io.grpc:grpc-core:1.36.0
io.grpc:grpc-grpclb:1.36.0
io.grpc:grpc-netty-shaded:1.36.0
io.grpc:grpc-alts:1.39.0
io.grpc:grpc-api:1.39.0
io.grpc:grpc-auth:1.39.0
io.grpc:grpc-context:1.39.0
io.grpc:grpc-core:1.39.0
io.grpc:grpc-grpclb:1.39.0
io.grpc:grpc-netty-shaded:1.39.0
io.grpc:grpc-netty:1.32.2
io.grpc:grpc-protobuf-lite:1.36.0
io.grpc:grpc-protobuf:1.36.0
io.grpc:grpc-stub:1.36.0
io.grpc:grpc-protobuf-lite:1.39.0
io.grpc:grpc-protobuf:1.39.0
io.grpc:grpc-stub:1.39.0
io.netty:netty-buffer:4.1.51.Final
io.netty:netty-codec-http2:4.1.51.Final
io.netty:netty-codec-http:4.1.51.Final
@@ -199,7 +205,7 @@ javax.validation:validation-api:1.0.0.GA
javax.xml.bind:jaxb-api:2.3.1
jline:jline:1.0
joda-time:joda-time:2.10.5
junit:junit:4.13.1
junit:junit:4.13.2
net.bytebuddy:byte-buddy-agent:1.10.19
net.bytebuddy:byte-buddy:1.10.19
net.java.dev.jna:jna:5.5.0
@@ -307,7 +313,7 @@ org.testcontainers:junit-jupiter:1.15.2
org.testcontainers:postgresql:1.15.2
org.testcontainers:selenium:1.15.2
org.testcontainers:testcontainers:1.15.2
org.threeten:threetenbp:1.5.0
org.threeten:threetenbp:1.5.1
org.tukaani:xz:1.5
org.w3c.css:sac:1.3
org.xerial.snappy:snappy-java:1.1.4

View File

@@ -6,10 +6,10 @@ aopalliance:aopalliance:1.0
args4j:args4j:2.0.23
cglib:cglib-nodep:2.2
com.beust:jcommander:1.60
com.fasterxml.jackson.core:jackson-annotations:2.12.1
com.fasterxml.jackson.core:jackson-core:2.12.1
com.fasterxml.jackson.core:jackson-databind:2.12.1
com.fasterxml.jackson:jackson-bom:2.12.1
com.fasterxml.jackson.core:jackson-annotations:2.12.3
com.fasterxml.jackson.core:jackson-core:2.12.3
com.fasterxml.jackson.core:jackson-databind:2.12.3
com.fasterxml.jackson:jackson-bom:2.12.3
com.fasterxml:classmate:1.5.1
com.github.docker-java:docker-java-api:3.2.7
com.github.docker-java:docker-java-transport-zerodep:3.2.7
@@ -27,7 +27,7 @@ com.google.api-client:google-api-client-appengine:1.31.3
com.google.api-client:google-api-client-jackson2:1.30.10
com.google.api-client:google-api-client-java6:1.31.3
com.google.api-client:google-api-client-servlet:1.31.3
com.google.api-client:google-api-client:1.31.3
com.google.api-client:google-api-client:1.32.1
com.google.api.grpc:grpc-google-cloud-bigquerystorage-v1:1.5.5
com.google.api.grpc:grpc-google-cloud-bigquerystorage-v1beta1:0.105.5
com.google.api.grpc:grpc-google-cloud-bigquerystorage-v1beta2:0.105.5
@@ -53,12 +53,15 @@ com.google.api.grpc:proto-google-cloud-secretmanager-v1beta1:1.4.0
com.google.api.grpc:proto-google-cloud-spanner-admin-database-v1:2.0.2
com.google.api.grpc:proto-google-cloud-spanner-admin-instance-v1:2.0.2
com.google.api.grpc:proto-google-cloud-spanner-v1:2.0.2
com.google.api.grpc:proto-google-common-protos:2.1.0
com.google.api.grpc:proto-google-iam-v1:1.0.9
com.google.api:api-common:1.10.1
com.google.api:gax-grpc:1.62.0
com.google.api:gax-httpjson:0.76.1
com.google.api:gax:1.62.0
com.google.api.grpc:proto-google-cloud-tasks-v2:1.33.2
com.google.api.grpc:proto-google-cloud-tasks-v2beta2:0.89.2
com.google.api.grpc:proto-google-cloud-tasks-v2beta3:0.89.2
com.google.api.grpc:proto-google-common-protos:2.3.2
com.google.api.grpc:proto-google-iam-v1:1.0.14
com.google.api:api-common:1.10.4
com.google.api:gax-grpc:1.66.0
com.google.api:gax-httpjson:0.83.0
com.google.api:gax:1.66.0
com.google.apis:google-api-services-admin-directory:directory_v1-rev118-1.25.0
com.google.apis:google-api-services-appengine:v1-rev130-1.25.0
com.google.apis:google-api-services-bigquery:v2-rev20200916-1.30.10
@@ -75,7 +78,7 @@ com.google.apis:google-api-services-monitoring:v3-rev540-1.25.0
com.google.apis:google-api-services-pubsub:v1-rev20200713-1.30.10
com.google.apis:google-api-services-sheets:v4-rev612-1.25.0
com.google.apis:google-api-services-sqladmin:v1beta4-rev20210119-1.31.0
com.google.apis:google-api-services-storage:v1-rev20200927-1.30.10
com.google.apis:google-api-services-storage:v1-rev20210127-1.32.1
com.google.appengine.tools:appengine-gcs-client:0.8.1
com.google.appengine.tools:appengine-mapreduce:0.9
com.google.appengine.tools:appengine-pipeline:0.2.13
@@ -83,10 +86,10 @@ com.google.appengine:appengine-api-1.0-sdk:1.9.86
com.google.appengine:appengine-api-stubs:1.9.86
com.google.appengine:appengine-remote-api:1.9.86
com.google.appengine:appengine-testing:1.9.86
com.google.auth:google-auth-library-credentials:0.24.1
com.google.auth:google-auth-library-oauth2-http:0.24.1
com.google.auth:google-auth-library-credentials:0.26.0
com.google.auth:google-auth-library-oauth2-http:0.26.0
com.google.auto.service:auto-service-annotations:1.0-rc7
com.google.auto.value:auto-value-annotations:1.7.4
com.google.auto.value:auto-value-annotations:1.8.1
com.google.auto.value:auto-value:1.7.4
com.google.cloud.bigdataoss:gcsio:2.1.6
com.google.cloud.bigdataoss:util:2.1.6
@@ -97,31 +100,34 @@ com.google.cloud:google-cloud-bigquery:1.122.2
com.google.cloud:google-cloud-bigquerystorage:1.5.5
com.google.cloud:google-cloud-bigtable:1.14.0
com.google.cloud:google-cloud-core-grpc:1.93.9
com.google.cloud:google-cloud-core-http:1.93.9
com.google.cloud:google-cloud-core:1.93.9
com.google.cloud:google-cloud-core-http:1.95.4
com.google.cloud:google-cloud-core:1.95.4
com.google.cloud:google-cloud-nio:0.123.4
com.google.cloud:google-cloud-pubsub:1.110.0
com.google.cloud:google-cloud-pubsublite:0.7.0
com.google.cloud:google-cloud-secretmanager:1.4.0
com.google.cloud:google-cloud-spanner:2.0.2
com.google.cloud:google-cloud-storage:1.118.0
com.google.cloud:google-cloud-tasks:1.33.2
com.google.code.findbugs:jsr305:3.0.2
com.google.code.gson:gson:2.8.6
com.google.code.gson:gson:2.8.7
com.google.common.html.types:types:1.0.6
com.google.dagger:dagger:2.33
com.google.errorprone:error_prone_annotations:2.5.1
com.google.errorprone:error_prone_annotations:2.7.1
com.google.escapevelocity:escapevelocity:0.9.1
com.google.flogger:flogger:0.5.1
com.google.flogger:google-extensions:0.5.1
com.google.guava:failureaccess:1.0.1
com.google.guava:guava-testlib:30.1-jre
com.google.guava:guava:30.1-jre
com.google.guava:guava-testlib:30.1.1-jre
com.google.guava:guava:30.1.1-jre
com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava
com.google.gwt:gwt-user:2.9.0
com.google.http-client:google-http-client-apache-v2:1.39.0
com.google.http-client:google-http-client-appengine:1.39.0
com.google.http-client:google-http-client-gson:1.39.0
com.google.http-client:google-http-client-jackson2:1.39.0
com.google.http-client:google-http-client-apache-v2:1.39.2
com.google.http-client:google-http-client-appengine:1.39.2
com.google.http-client:google-http-client-gson:1.39.2
com.google.http-client:google-http-client-jackson2:1.39.2
com.google.http-client:google-http-client-protobuf:1.33.0
com.google.http-client:google-http-client:1.39.0
com.google.http-client:google-http-client:1.39.2
com.google.inject.extensions:guice-multibindings:4.1.0
com.google.inject:guice:4.1.0
com.google.j2objc:j2objc-annotations:1.3
@@ -133,9 +139,9 @@ com.google.oauth-client:google-oauth-client-appengine:1.31.4
com.google.oauth-client:google-oauth-client-java6:1.31.4
com.google.oauth-client:google-oauth-client-jetty:1.31.4
com.google.oauth-client:google-oauth-client-servlet:1.31.4
com.google.oauth-client:google-oauth-client:1.31.4
com.google.protobuf:protobuf-java-util:3.14.0
com.google.protobuf:protobuf-java:3.15.2
com.google.oauth-client:google-oauth-client:1.31.5
com.google.protobuf:protobuf-java-util:3.17.3
com.google.protobuf:protobuf-java:3.17.3
com.google.re2j:re2j:1.6
com.google.template:soy:2021-02-01
com.google.truth.extensions:truth-java8-extension:1.1.2
@@ -156,17 +162,17 @@ commons-logging:commons-logging:1.2
dnsjava:dnsjava:3.3.1
io.dropwizard.metrics:metrics-core:3.2.6
io.github.classgraph:classgraph:4.8.102
io.grpc:grpc-alts:1.36.0
io.grpc:grpc-api:1.36.0
io.grpc:grpc-auth:1.36.0
io.grpc:grpc-context:1.36.0
io.grpc:grpc-core:1.36.0
io.grpc:grpc-grpclb:1.36.0
io.grpc:grpc-netty-shaded:1.36.0
io.grpc:grpc-alts:1.39.0
io.grpc:grpc-api:1.39.0
io.grpc:grpc-auth:1.39.0
io.grpc:grpc-context:1.39.0
io.grpc:grpc-core:1.39.0
io.grpc:grpc-grpclb:1.39.0
io.grpc:grpc-netty-shaded:1.39.0
io.grpc:grpc-netty:1.32.2
io.grpc:grpc-protobuf-lite:1.36.0
io.grpc:grpc-protobuf:1.36.0
io.grpc:grpc-stub:1.36.0
io.grpc:grpc-protobuf-lite:1.39.0
io.grpc:grpc-protobuf:1.39.0
io.grpc:grpc-stub:1.39.0
io.netty:netty-buffer:4.1.51.Final
io.netty:netty-codec-http2:4.1.51.Final
io.netty:netty-codec-http:4.1.51.Final
@@ -194,7 +200,7 @@ javax.validation:validation-api:1.0.0.GA
javax.xml.bind:jaxb-api:2.3.1
jline:jline:1.0
joda-time:joda-time:2.10.5
junit:junit:4.13.1
junit:junit:4.13.2
net.bytebuddy:byte-buddy-agent:1.10.19
net.bytebuddy:byte-buddy:1.10.19
net.java.dev.jna:jna:5.5.0
@@ -301,7 +307,7 @@ org.testcontainers:junit-jupiter:1.15.2
org.testcontainers:postgresql:1.15.2
org.testcontainers:selenium:1.15.2
org.testcontainers:testcontainers:1.15.2
org.threeten:threetenbp:1.5.0
org.threeten:threetenbp:1.5.1
org.tukaani:xz:1.5
org.w3c.css:sac:1.3
org.xerial.snappy:snappy-java:1.1.4

View File

@@ -10,10 +10,10 @@ com.eclipsesource.j2v8:j2v8_linux_x86_64:4.6.0
com.eclipsesource.j2v8:j2v8_macosx_x86_64:4.6.0
com.eclipsesource.j2v8:j2v8_win32_x86:4.6.0
com.eclipsesource.j2v8:j2v8_win32_x86_64:4.6.0
com.fasterxml.jackson.core:jackson-annotations:2.12.1
com.fasterxml.jackson.core:jackson-core:2.12.1
com.fasterxml.jackson.core:jackson-databind:2.12.1
com.fasterxml.jackson:jackson-bom:2.12.1
com.fasterxml.jackson.core:jackson-annotations:2.12.3
com.fasterxml.jackson.core:jackson-core:2.12.3
com.fasterxml.jackson.core:jackson-databind:2.12.3
com.fasterxml.jackson:jackson-bom:2.12.3
com.fasterxml:classmate:1.5.1
com.github.docker-java:docker-java-api:3.2.7
com.github.docker-java:docker-java-transport-zerodep:3.2.7
@@ -32,7 +32,7 @@ com.google.api-client:google-api-client-appengine:1.31.3
com.google.api-client:google-api-client-jackson2:1.30.10
com.google.api-client:google-api-client-java6:1.31.3
com.google.api-client:google-api-client-servlet:1.31.3
com.google.api-client:google-api-client:1.31.3
com.google.api-client:google-api-client:1.32.1
com.google.api.grpc:grpc-google-cloud-bigquerystorage-v1:1.5.5
com.google.api.grpc:grpc-google-cloud-bigquerystorage-v1beta1:0.105.5
com.google.api.grpc:grpc-google-cloud-bigquerystorage-v1beta2:0.105.5
@@ -58,12 +58,15 @@ com.google.api.grpc:proto-google-cloud-secretmanager-v1beta1:1.4.0
com.google.api.grpc:proto-google-cloud-spanner-admin-database-v1:2.0.2
com.google.api.grpc:proto-google-cloud-spanner-admin-instance-v1:2.0.2
com.google.api.grpc:proto-google-cloud-spanner-v1:2.0.2
com.google.api.grpc:proto-google-common-protos:2.1.0
com.google.api.grpc:proto-google-iam-v1:1.0.9
com.google.api:api-common:1.10.1
com.google.api:gax-grpc:1.62.0
com.google.api:gax-httpjson:0.76.1
com.google.api:gax:1.62.0
com.google.api.grpc:proto-google-cloud-tasks-v2:1.33.2
com.google.api.grpc:proto-google-cloud-tasks-v2beta2:0.89.2
com.google.api.grpc:proto-google-cloud-tasks-v2beta3:0.89.2
com.google.api.grpc:proto-google-common-protos:2.3.2
com.google.api.grpc:proto-google-iam-v1:1.0.14
com.google.api:api-common:1.10.4
com.google.api:gax-grpc:1.66.0
com.google.api:gax-httpjson:0.83.0
com.google.api:gax:1.66.0
com.google.apis:google-api-services-admin-directory:directory_v1-rev118-1.25.0
com.google.apis:google-api-services-appengine:v1-rev130-1.25.0
com.google.apis:google-api-services-bigquery:v2-rev20200916-1.30.10
@@ -80,7 +83,7 @@ com.google.apis:google-api-services-monitoring:v3-rev540-1.25.0
com.google.apis:google-api-services-pubsub:v1-rev20200713-1.30.10
com.google.apis:google-api-services-sheets:v4-rev612-1.25.0
com.google.apis:google-api-services-sqladmin:v1beta4-rev20210119-1.31.0
com.google.apis:google-api-services-storage:v1-rev20200927-1.30.10
com.google.apis:google-api-services-storage:v1-rev20210127-1.32.1
com.google.appengine.tools:appengine-gcs-client:0.8.1
com.google.appengine.tools:appengine-mapreduce:0.9
com.google.appengine.tools:appengine-pipeline:0.2.13
@@ -88,10 +91,10 @@ com.google.appengine:appengine-api-1.0-sdk:1.9.86
com.google.appengine:appengine-api-stubs:1.9.86
com.google.appengine:appengine-remote-api:1.9.86
com.google.appengine:appengine-testing:1.9.86
com.google.auth:google-auth-library-credentials:0.24.1
com.google.auth:google-auth-library-oauth2-http:0.24.1
com.google.auth:google-auth-library-credentials:0.26.0
com.google.auth:google-auth-library-oauth2-http:0.26.0
com.google.auto.service:auto-service-annotations:1.0-rc7
com.google.auto.value:auto-value-annotations:1.7.4
com.google.auto.value:auto-value-annotations:1.8.1
com.google.auto.value:auto-value:1.7.4
com.google.cloud.bigdataoss:gcsio:2.1.6
com.google.cloud.bigdataoss:util:2.1.6
@@ -103,32 +106,35 @@ com.google.cloud:google-cloud-bigquery:1.122.2
com.google.cloud:google-cloud-bigquerystorage:1.5.5
com.google.cloud:google-cloud-bigtable:1.14.0
com.google.cloud:google-cloud-core-grpc:1.93.9
com.google.cloud:google-cloud-core-http:1.93.9
com.google.cloud:google-cloud-core:1.93.9
com.google.cloud:google-cloud-core-http:1.95.4
com.google.cloud:google-cloud-core:1.95.4
com.google.cloud:google-cloud-nio:0.123.4
com.google.cloud:google-cloud-pubsub:1.110.0
com.google.cloud:google-cloud-pubsublite:0.7.0
com.google.cloud:google-cloud-secretmanager:1.4.0
com.google.cloud:google-cloud-spanner:2.0.2
com.google.cloud:google-cloud-storage:1.118.0
com.google.cloud:google-cloud-tasks:1.33.2
com.google.code.findbugs:jsr305:3.0.2
com.google.code.gson:gson:2.8.6
com.google.code.gson:gson:2.8.7
com.google.common.html.types:types:1.0.6
com.google.dagger:dagger:2.33
com.google.errorprone:error_prone_annotations:2.5.1
com.google.errorprone:error_prone_annotations:2.7.1
com.google.escapevelocity:escapevelocity:0.9.1
com.google.flogger:flogger-system-backend:0.5.1
com.google.flogger:flogger:0.5.1
com.google.flogger:google-extensions:0.5.1
com.google.guava:failureaccess:1.0.1
com.google.guava:guava-testlib:30.1-jre
com.google.guava:guava:30.1-jre
com.google.guava:guava-testlib:30.1.1-jre
com.google.guava:guava:30.1.1-jre
com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava
com.google.gwt:gwt-user:2.9.0
com.google.http-client:google-http-client-apache-v2:1.39.0
com.google.http-client:google-http-client-appengine:1.39.0
com.google.http-client:google-http-client-gson:1.39.0
com.google.http-client:google-http-client-jackson2:1.39.0
com.google.http-client:google-http-client-apache-v2:1.39.2
com.google.http-client:google-http-client-appengine:1.39.2
com.google.http-client:google-http-client-gson:1.39.2
com.google.http-client:google-http-client-jackson2:1.39.2
com.google.http-client:google-http-client-protobuf:1.33.0
com.google.http-client:google-http-client:1.39.0
com.google.http-client:google-http-client:1.39.2
com.google.inject.extensions:guice-multibindings:4.1.0
com.google.inject:guice:4.1.0
com.google.j2objc:j2objc-annotations:1.3
@@ -140,9 +146,9 @@ com.google.oauth-client:google-oauth-client-appengine:1.31.4
com.google.oauth-client:google-oauth-client-java6:1.31.4
com.google.oauth-client:google-oauth-client-jetty:1.31.4
com.google.oauth-client:google-oauth-client-servlet:1.31.4
com.google.oauth-client:google-oauth-client:1.31.4
com.google.protobuf:protobuf-java-util:3.15.2
com.google.protobuf:protobuf-java:3.15.2
com.google.oauth-client:google-oauth-client:1.31.5
com.google.protobuf:protobuf-java-util:3.17.3
com.google.protobuf:protobuf-java:3.17.3
com.google.re2j:re2j:1.6
com.google.template:soy:2021-02-01
com.google.truth.extensions:truth-java8-extension:1.1.2
@@ -167,17 +173,17 @@ guru.nidi:graphviz-java:0.17.0
io.dropwizard.metrics:metrics-core:3.2.6
io.github.classgraph:classgraph:4.8.102
io.github.java-diff-utils:java-diff-utils:4.9
io.grpc:grpc-alts:1.36.0
io.grpc:grpc-api:1.36.0
io.grpc:grpc-auth:1.36.0
io.grpc:grpc-context:1.36.0
io.grpc:grpc-core:1.36.0
io.grpc:grpc-grpclb:1.36.0
io.grpc:grpc-netty-shaded:1.36.0
io.grpc:grpc-alts:1.39.0
io.grpc:grpc-api:1.39.0
io.grpc:grpc-auth:1.39.0
io.grpc:grpc-context:1.39.0
io.grpc:grpc-core:1.39.0
io.grpc:grpc-grpclb:1.39.0
io.grpc:grpc-netty-shaded:1.39.0
io.grpc:grpc-netty:1.32.2
io.grpc:grpc-protobuf-lite:1.36.0
io.grpc:grpc-protobuf:1.36.0
io.grpc:grpc-stub:1.36.0
io.grpc:grpc-protobuf-lite:1.39.0
io.grpc:grpc-protobuf:1.39.0
io.grpc:grpc-stub:1.39.0
io.netty:netty-buffer:4.1.51.Final
io.netty:netty-codec-http2:4.1.51.Final
io.netty:netty-codec-http:4.1.51.Final
@@ -208,7 +214,7 @@ javax.validation:validation-api:1.0.0.GA
javax.xml.bind:jaxb-api:2.3.1
jline:jline:1.0
joda-time:joda-time:2.10.5
junit:junit:4.13.1
junit:junit:4.13.2
net.arnx:nashorn-promise:0.1.1
net.bytebuddy:byte-buddy-agent:1.10.19
net.bytebuddy:byte-buddy:1.10.19
@@ -320,7 +326,7 @@ org.testcontainers:junit-jupiter:1.15.2
org.testcontainers:postgresql:1.15.2
org.testcontainers:selenium:1.15.2
org.testcontainers:testcontainers:1.15.2
org.threeten:threetenbp:1.5.0
org.threeten:threetenbp:1.5.1
org.tukaani:xz:1.5
org.w3c.css:sac:1.3
org.webjars.npm:viz.js-for-graphviz-java:2.1.3

View File

@@ -10,10 +10,10 @@ com.eclipsesource.j2v8:j2v8_linux_x86_64:4.6.0
com.eclipsesource.j2v8:j2v8_macosx_x86_64:4.6.0
com.eclipsesource.j2v8:j2v8_win32_x86:4.6.0
com.eclipsesource.j2v8:j2v8_win32_x86_64:4.6.0
com.fasterxml.jackson.core:jackson-annotations:2.12.1
com.fasterxml.jackson.core:jackson-core:2.12.1
com.fasterxml.jackson.core:jackson-databind:2.12.1
com.fasterxml.jackson:jackson-bom:2.12.1
com.fasterxml.jackson.core:jackson-annotations:2.12.3
com.fasterxml.jackson.core:jackson-core:2.12.3
com.fasterxml.jackson.core:jackson-databind:2.12.3
com.fasterxml.jackson:jackson-bom:2.12.3
com.fasterxml:classmate:1.5.1
com.github.docker-java:docker-java-api:3.2.7
com.github.docker-java:docker-java-transport-zerodep:3.2.7
@@ -32,7 +32,7 @@ com.google.api-client:google-api-client-appengine:1.31.3
com.google.api-client:google-api-client-jackson2:1.30.10
com.google.api-client:google-api-client-java6:1.31.3
com.google.api-client:google-api-client-servlet:1.31.3
com.google.api-client:google-api-client:1.31.3
com.google.api-client:google-api-client:1.32.1
com.google.api.grpc:grpc-google-cloud-bigquerystorage-v1:1.5.5
com.google.api.grpc:grpc-google-cloud-bigquerystorage-v1beta1:0.105.5
com.google.api.grpc:grpc-google-cloud-bigquerystorage-v1beta2:0.105.5
@@ -58,12 +58,15 @@ com.google.api.grpc:proto-google-cloud-secretmanager-v1beta1:1.4.0
com.google.api.grpc:proto-google-cloud-spanner-admin-database-v1:2.0.2
com.google.api.grpc:proto-google-cloud-spanner-admin-instance-v1:2.0.2
com.google.api.grpc:proto-google-cloud-spanner-v1:2.0.2
com.google.api.grpc:proto-google-common-protos:2.1.0
com.google.api.grpc:proto-google-iam-v1:1.0.9
com.google.api:api-common:1.10.1
com.google.api:gax-grpc:1.62.0
com.google.api:gax-httpjson:0.76.1
com.google.api:gax:1.62.0
com.google.api.grpc:proto-google-cloud-tasks-v2:1.33.2
com.google.api.grpc:proto-google-cloud-tasks-v2beta2:0.89.2
com.google.api.grpc:proto-google-cloud-tasks-v2beta3:0.89.2
com.google.api.grpc:proto-google-common-protos:2.3.2
com.google.api.grpc:proto-google-iam-v1:1.0.14
com.google.api:api-common:1.10.4
com.google.api:gax-grpc:1.66.0
com.google.api:gax-httpjson:0.83.0
com.google.api:gax:1.66.0
com.google.apis:google-api-services-admin-directory:directory_v1-rev118-1.25.0
com.google.apis:google-api-services-appengine:v1-rev130-1.25.0
com.google.apis:google-api-services-bigquery:v2-rev20200916-1.30.10
@@ -80,7 +83,7 @@ com.google.apis:google-api-services-monitoring:v3-rev540-1.25.0
com.google.apis:google-api-services-pubsub:v1-rev20200713-1.30.10
com.google.apis:google-api-services-sheets:v4-rev612-1.25.0
com.google.apis:google-api-services-sqladmin:v1beta4-rev20210119-1.31.0
com.google.apis:google-api-services-storage:v1-rev20200927-1.30.10
com.google.apis:google-api-services-storage:v1-rev20210127-1.32.1
com.google.appengine.tools:appengine-gcs-client:0.8.1
com.google.appengine.tools:appengine-mapreduce:0.9
com.google.appengine.tools:appengine-pipeline:0.2.13
@@ -88,10 +91,10 @@ com.google.appengine:appengine-api-1.0-sdk:1.9.86
com.google.appengine:appengine-api-stubs:1.9.86
com.google.appengine:appengine-remote-api:1.9.86
com.google.appengine:appengine-testing:1.9.86
com.google.auth:google-auth-library-credentials:0.24.1
com.google.auth:google-auth-library-oauth2-http:0.24.1
com.google.auth:google-auth-library-credentials:0.26.0
com.google.auth:google-auth-library-oauth2-http:0.26.0
com.google.auto.service:auto-service-annotations:1.0-rc7
com.google.auto.value:auto-value-annotations:1.7.4
com.google.auto.value:auto-value-annotations:1.8.1
com.google.auto.value:auto-value:1.7.4
com.google.cloud.bigdataoss:gcsio:2.1.6
com.google.cloud.bigdataoss:util:2.1.6
@@ -103,32 +106,35 @@ com.google.cloud:google-cloud-bigquery:1.122.2
com.google.cloud:google-cloud-bigquerystorage:1.5.5
com.google.cloud:google-cloud-bigtable:1.14.0
com.google.cloud:google-cloud-core-grpc:1.93.9
com.google.cloud:google-cloud-core-http:1.93.9
com.google.cloud:google-cloud-core:1.93.9
com.google.cloud:google-cloud-core-http:1.95.4
com.google.cloud:google-cloud-core:1.95.4
com.google.cloud:google-cloud-nio:0.123.4
com.google.cloud:google-cloud-pubsub:1.110.0
com.google.cloud:google-cloud-pubsublite:0.7.0
com.google.cloud:google-cloud-secretmanager:1.4.0
com.google.cloud:google-cloud-spanner:2.0.2
com.google.cloud:google-cloud-storage:1.118.0
com.google.cloud:google-cloud-tasks:1.33.2
com.google.code.findbugs:jsr305:3.0.2
com.google.code.gson:gson:2.8.6
com.google.code.gson:gson:2.8.7
com.google.common.html.types:types:1.0.6
com.google.dagger:dagger:2.33
com.google.errorprone:error_prone_annotations:2.5.1
com.google.errorprone:error_prone_annotations:2.7.1
com.google.escapevelocity:escapevelocity:0.9.1
com.google.flogger:flogger-system-backend:0.5.1
com.google.flogger:flogger:0.5.1
com.google.flogger:google-extensions:0.5.1
com.google.guava:failureaccess:1.0.1
com.google.guava:guava-testlib:30.1-jre
com.google.guava:guava:30.1-jre
com.google.guava:guava-testlib:30.1.1-jre
com.google.guava:guava:30.1.1-jre
com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava
com.google.gwt:gwt-user:2.9.0
com.google.http-client:google-http-client-apache-v2:1.39.0
com.google.http-client:google-http-client-appengine:1.39.0
com.google.http-client:google-http-client-gson:1.39.0
com.google.http-client:google-http-client-jackson2:1.39.0
com.google.http-client:google-http-client-apache-v2:1.39.2
com.google.http-client:google-http-client-appengine:1.39.2
com.google.http-client:google-http-client-gson:1.39.2
com.google.http-client:google-http-client-jackson2:1.39.2
com.google.http-client:google-http-client-protobuf:1.33.0
com.google.http-client:google-http-client:1.39.0
com.google.http-client:google-http-client:1.39.2
com.google.inject.extensions:guice-multibindings:4.1.0
com.google.inject:guice:4.1.0
com.google.j2objc:j2objc-annotations:1.3
@@ -140,9 +146,9 @@ com.google.oauth-client:google-oauth-client-appengine:1.31.4
com.google.oauth-client:google-oauth-client-java6:1.31.4
com.google.oauth-client:google-oauth-client-jetty:1.31.4
com.google.oauth-client:google-oauth-client-servlet:1.31.4
com.google.oauth-client:google-oauth-client:1.31.4
com.google.protobuf:protobuf-java-util:3.15.2
com.google.protobuf:protobuf-java:3.15.2
com.google.oauth-client:google-oauth-client:1.31.5
com.google.protobuf:protobuf-java-util:3.17.3
com.google.protobuf:protobuf-java:3.17.3
com.google.re2j:re2j:1.6
com.google.template:soy:2021-02-01
com.google.truth.extensions:truth-java8-extension:1.1.2
@@ -167,17 +173,17 @@ guru.nidi:graphviz-java:0.17.0
io.dropwizard.metrics:metrics-core:3.2.6
io.github.classgraph:classgraph:4.8.102
io.github.java-diff-utils:java-diff-utils:4.9
io.grpc:grpc-alts:1.36.0
io.grpc:grpc-api:1.36.0
io.grpc:grpc-auth:1.36.0
io.grpc:grpc-context:1.36.0
io.grpc:grpc-core:1.36.0
io.grpc:grpc-grpclb:1.36.0
io.grpc:grpc-netty-shaded:1.36.0
io.grpc:grpc-alts:1.39.0
io.grpc:grpc-api:1.39.0
io.grpc:grpc-auth:1.39.0
io.grpc:grpc-context:1.39.0
io.grpc:grpc-core:1.39.0
io.grpc:grpc-grpclb:1.39.0
io.grpc:grpc-netty-shaded:1.39.0
io.grpc:grpc-netty:1.32.2
io.grpc:grpc-protobuf-lite:1.36.0
io.grpc:grpc-protobuf:1.36.0
io.grpc:grpc-stub:1.36.0
io.grpc:grpc-protobuf-lite:1.39.0
io.grpc:grpc-protobuf:1.39.0
io.grpc:grpc-stub:1.39.0
io.netty:netty-buffer:4.1.51.Final
io.netty:netty-codec-http2:4.1.51.Final
io.netty:netty-codec-http:4.1.51.Final
@@ -208,7 +214,7 @@ javax.validation:validation-api:1.0.0.GA
javax.xml.bind:jaxb-api:2.3.1
jline:jline:1.0
joda-time:joda-time:2.10.5
junit:junit:4.13.1
junit:junit:4.13.2
net.arnx:nashorn-promise:0.1.1
net.bytebuddy:byte-buddy-agent:1.10.19
net.bytebuddy:byte-buddy:1.10.19
@@ -321,7 +327,7 @@ org.testcontainers:junit-jupiter:1.15.2
org.testcontainers:postgresql:1.15.2
org.testcontainers:selenium:1.15.2
org.testcontainers:testcontainers:1.15.2
org.threeten:threetenbp:1.5.0
org.threeten:threetenbp:1.5.1
org.tukaani:xz:1.5
org.w3c.css:sac:1.3
org.webjars.npm:viz.js-for-graphviz-java:2.1.3

View File

@@ -18,8 +18,10 @@ import static com.google.appengine.api.ThreadManager.currentRequestThreadFactory
import static com.google.common.util.concurrent.MoreExecutors.listeningDecorator;
import static google.registry.backup.ExportCommitLogDiffAction.LOWER_CHECKPOINT_TIME_PARAM;
import static google.registry.backup.ExportCommitLogDiffAction.UPPER_CHECKPOINT_TIME_PARAM;
import static google.registry.backup.RestoreCommitLogsAction.BUCKET_OVERRIDE_PARAM;
import static google.registry.backup.RestoreCommitLogsAction.FROM_TIME_PARAM;
import static google.registry.backup.RestoreCommitLogsAction.TO_TIME_PARAM;
import static google.registry.request.RequestParameters.extractOptionalParameter;
import static google.registry.request.RequestParameters.extractRequiredDatetimeParameter;
import static google.registry.request.RequestParameters.extractRequiredParameter;
import static java.util.concurrent.Executors.newFixedThreadPool;
@@ -32,6 +34,9 @@ import google.registry.cron.CommitLogFanoutAction;
import google.registry.request.HttpException.BadRequestException;
import google.registry.request.Parameter;
import java.lang.annotation.Documented;
import java.util.Optional;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import javax.inject.Qualifier;
import javax.servlet.http.HttpServletRequest;
import org.joda.time.DateTime;
@@ -75,6 +80,12 @@ public final class BackupModule {
return extractRequiredDatetimeParameter(req, UPPER_CHECKPOINT_TIME_PARAM);
}
@Provides
@Parameter(BUCKET_OVERRIDE_PARAM)
static Optional<String> provideBucketOverride(HttpServletRequest req) {
return extractOptionalParameter(req, BUCKET_OVERRIDE_PARAM);
}
@Provides
@Parameter(FROM_TIME_PARAM)
static DateTime provideFromTime(HttpServletRequest req) {
@@ -92,4 +103,9 @@ public final class BackupModule {
static ListeningExecutorService provideListeningExecutorService() {
return listeningDecorator(newFixedThreadPool(NUM_THREADS, currentRequestThreadFactory()));
}
@Provides
static ScheduledExecutorService provideScheduledExecutorService() {
return Executors.newSingleThreadScheduledExecutor();
}
}

View File

@@ -55,10 +55,9 @@ public final class CommitLogImports {
* represents the changes in one transaction. The {@code CommitLogManifest} contains deleted
* entity keys, whereas each {@code CommitLogMutation} contains one whole entity.
*/
public static ImmutableList<ImmutableList<VersionedEntity>> loadEntitiesByTransaction(
static ImmutableList<ImmutableList<VersionedEntity>> loadEntitiesByTransaction(
InputStream inputStream) {
try (AppEngineEnvironment appEngineEnvironment = new AppEngineEnvironment();
InputStream input = new BufferedInputStream(inputStream)) {
try (InputStream input = new BufferedInputStream(inputStream)) {
Iterator<ImmutableObject> commitLogs = createDeserializingIterator(input, false);
checkState(commitLogs.hasNext());
checkState(commitLogs.next() instanceof CommitLogCheckpoint);
@@ -105,7 +104,7 @@ public final class CommitLogImports {
* represents the changes in one transaction. The {@code CommitLogManifest} contains deleted
* entity keys, whereas each {@code CommitLogMutation} contains one whole entity.
*/
public static ImmutableList<VersionedEntity> loadEntities(InputStream inputStream) {
static ImmutableList<VersionedEntity> loadEntities(InputStream inputStream) {
return loadEntitiesByTransaction(inputStream).stream()
.flatMap(ImmutableList::stream)
.collect(toImmutableList());

View File

@@ -93,7 +93,7 @@ public final class DeleteOldCommitLogsAction implements Runnable {
public void run() {
DateTime deletionThreshold = clock.nowUtc().minus(maxAge);
logger.atInfo().log(
"Processing asynchronous deletion of unreferenced CommitLogManifests older than %s",
"Processing asynchronous deletion of unreferenced CommitLogManifests older than %s.",
deletionThreshold);
mrRunner
@@ -208,7 +208,7 @@ public final class DeleteOldCommitLogsAction implements Runnable {
getContext().incrementCounter("EPP resources missing pre-threshold revision (SEE LOGS)");
logger.atSevere().log(
"EPP resource missing old enough revision: "
+ "%s (created on %s) has %d revisions between %s and %s, while threshold is %s",
+ "%s (created on %s) has %d revisions between %s and %s, while threshold is %s.",
Key.create(eppResource),
eppResource.getCreationTime(),
eppResource.getRevisions().size(),

View File

@@ -28,18 +28,17 @@ import static google.registry.model.ofy.CommitLogBucket.getBucketKey;
import static google.registry.model.ofy.ObjectifyService.auditedOfy;
import static google.registry.util.DateTimeUtils.START_OF_TIME;
import static google.registry.util.DateTimeUtils.isAtOrAfter;
import static java.nio.channels.Channels.newOutputStream;
import static java.util.Comparator.comparingLong;
import com.google.appengine.tools.cloudstorage.GcsFileOptions;
import com.google.appengine.tools.cloudstorage.GcsFilename;
import com.google.appengine.tools.cloudstorage.GcsService;
import com.google.cloud.storage.BlobId;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Streams;
import com.google.common.flogger.FluentLogger;
import com.googlecode.objectify.Key;
import google.registry.config.RegistryConfig.Config;
import google.registry.gcs.GcsUtils;
import google.registry.model.ImmutableObject;
import google.registry.model.ofy.CommitLogBucket;
import google.registry.model.ofy.CommitLogCheckpoint;
@@ -74,7 +73,8 @@ public final class ExportCommitLogDiffAction implements Runnable {
public static final String DIFF_FILE_PREFIX = "commit_diff_until_";
@Inject GcsService gcsService;
@Inject GcsUtils gcsUtils;
@Inject @Config("commitLogGcsBucket") String gcsBucket;
@Inject @Config("commitLogDiffExportBatchSize") int batchSize;
@Inject @Parameter(LOWER_CHECKPOINT_TIME_PARAM) DateTime lowerCheckpointTime;
@@ -100,15 +100,15 @@ public final class ExportCommitLogDiffAction implements Runnable {
// Load the keys of all the manifests to include in this diff.
List<Key<CommitLogManifest>> sortedKeys = loadAllDiffKeys(lowerCheckpoint, upperCheckpoint);
logger.atInfo().log("Found %d manifests to export", sortedKeys.size());
logger.atInfo().log("Found %d manifests to export.", sortedKeys.size());
// Open an output channel to GCS, wrapped in a stream for convenience.
try (OutputStream gcsStream = newOutputStream(gcsService.createOrReplace(
new GcsFilename(gcsBucket, DIFF_FILE_PREFIX + upperCheckpointTime),
new GcsFileOptions.Builder()
.addUserMetadata(LOWER_BOUND_CHECKPOINT, lowerCheckpointTime.toString())
.addUserMetadata(UPPER_BOUND_CHECKPOINT, upperCheckpointTime.toString())
.addUserMetadata(NUM_TRANSACTIONS, Integer.toString(sortedKeys.size()))
.build()))) {
try (OutputStream gcsStream =
gcsUtils.openOutputStream(
BlobId.of(gcsBucket, DIFF_FILE_PREFIX + upperCheckpointTime),
ImmutableMap.of(
LOWER_BOUND_CHECKPOINT, lowerCheckpointTime.toString(),
UPPER_BOUND_CHECKPOINT, upperCheckpointTime.toString(),
NUM_TRANSACTIONS, Integer.toString(sortedKeys.size())))) {
// Export the upper checkpoint itself.
serializeEntity(upperCheckpoint, gcsStream);
// If there are no manifests to export, stop early, now that we've written out the file with
@@ -124,7 +124,7 @@ public final class ExportCommitLogDiffAction implements Runnable {
for (int i = 0; i < keyChunks.size(); i++) {
// Force the async load to finish.
Collection<CommitLogManifest> chunkValues = nextChunkToExport.values();
logger.atInfo().log("Loaded %d manifests", chunkValues.size());
logger.atInfo().log("Loaded %d manifests.", chunkValues.size());
// Since there is no hard bound on how much data this might be, take care not to let the
// Objectify session cache fill up and potentially run out of memory. This is the only safe
// point to do this since at this point there is no async load in progress.
@@ -134,12 +134,12 @@ public final class ExportCommitLogDiffAction implements Runnable {
nextChunkToExport = auditedOfy().load().keys(keyChunks.get(i + 1));
}
exportChunk(gcsStream, chunkValues);
logger.atInfo().log("Exported %d manifests", chunkValues.size());
logger.atInfo().log("Exported %d manifests.", chunkValues.size());
}
} catch (IOException e) {
throw new RuntimeException(e);
}
logger.atInfo().log("Exported %d manifests in total", sortedKeys.size());
logger.atInfo().log("Exported %d total manifests.", sortedKeys.size());
}
/**

View File

@@ -15,30 +15,32 @@
package google.registry.backup;
import static com.google.common.base.Preconditions.checkState;
import static com.google.common.collect.ImmutableList.toImmutableList;
import static google.registry.backup.BackupUtils.GcsMetadataKeys.LOWER_BOUND_CHECKPOINT;
import static google.registry.backup.ExportCommitLogDiffAction.DIFF_FILE_PREFIX;
import static google.registry.util.DateTimeUtils.START_OF_TIME;
import static google.registry.util.DateTimeUtils.isBeforeOrAt;
import static google.registry.util.DateTimeUtils.latestOf;
import com.google.appengine.tools.cloudstorage.GcsFileMetadata;
import com.google.appengine.tools.cloudstorage.GcsFilename;
import com.google.appengine.tools.cloudstorage.GcsService;
import com.google.appengine.tools.cloudstorage.ListItem;
import com.google.appengine.tools.cloudstorage.ListOptions;
import com.google.cloud.storage.BlobId;
import com.google.cloud.storage.BlobInfo;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableList;
import com.google.common.flogger.FluentLogger;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.ListeningExecutorService;
import com.google.common.util.concurrent.UncheckedExecutionException;
import google.registry.backup.BackupModule.Backups;
import google.registry.config.RegistryConfig.Config;
import google.registry.gcs.GcsUtils;
import java.io.IOException;
import java.util.Iterator;
import java.time.Duration;
import java.util.Map;
import java.util.TreeMap;
import java.util.concurrent.ScheduledExecutorService;
import javax.annotation.Nullable;
import javax.inject.Inject;
import javax.inject.Provider;
import org.joda.time.DateTime;
/** Utility class to list commit logs diff files stored on GCS. */
@@ -46,48 +48,65 @@ class GcsDiffFileLister {
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
@Inject GcsService gcsService;
@Inject @Config("commitLogGcsBucket") String gcsBucket;
@Inject @Backups ListeningExecutorService executor;
@Inject GcsDiffFileLister() {}
/** Timeout for retrieving per-file information from GCS. */
private static final Duration FILE_INFO_TIMEOUT_DURATION = Duration.ofMinutes(1);
@Inject GcsUtils gcsUtils;
@Inject @Backups Provider<ListeningExecutorService> executorProvider;
@Inject ScheduledExecutorService scheduledExecutorService;
@Inject
GcsDiffFileLister() {}
/**
* Traverses the sequence of diff files backwards from checkpointTime and inserts the file
* metadata into "sequence". Returns true if a complete sequence was discovered, false if one or
* metadata into "sequence". Returns true if a complete sequence was discovered, false if one or
* more files are missing.
*
* @throws UncheckedExecutionException wrapping a {@link java.util.concurrent.TimeoutException} if
* the GCS call fails to finish within one minute, or wrapping any other exception if
* something else goes wrong.
*/
private boolean constructDiffSequence(
Map<DateTime, ListenableFuture<GcsFileMetadata>> upperBoundTimesToMetadata,
String gcsBucket,
Map<DateTime, ListenableFuture<BlobInfo>> upperBoundTimesToBlobInfo,
DateTime fromTime,
DateTime lastTime,
TreeMap<DateTime, GcsFileMetadata> sequence) {
TreeMap<DateTime, BlobInfo> sequence) {
DateTime checkpointTime = lastTime;
while (isBeforeOrAt(fromTime, checkpointTime)) {
GcsFileMetadata metadata;
if (upperBoundTimesToMetadata.containsKey(checkpointTime)) {
metadata = Futures.getUnchecked(upperBoundTimesToMetadata.get(checkpointTime));
BlobInfo blobInfo;
if (upperBoundTimesToBlobInfo.containsKey(checkpointTime)) {
blobInfo =
Futures.getUnchecked(
Futures.withTimeout(
upperBoundTimesToBlobInfo.get(checkpointTime),
FILE_INFO_TIMEOUT_DURATION,
scheduledExecutorService));
} else {
String filename = DIFF_FILE_PREFIX + checkpointTime;
logger.atInfo().log("Patching GCS list; discovered file: %s", filename);
metadata = getMetadata(filename);
blobInfo = getBlobInfo(gcsBucket, filename);
// If we hit a gap, quit.
if (metadata == null) {
if (blobInfo == null) {
logger.atInfo().log(
"Gap discovered in sequence terminating at %s, missing file: %s",
sequence.lastKey(), filename);
logger.atInfo().log("Found sequence from %s to %s", checkpointTime, lastTime);
logger.atInfo().log("Found sequence from %s to %s.", checkpointTime, lastTime);
return false;
}
}
sequence.put(checkpointTime, metadata);
checkpointTime = getLowerBoundTime(metadata);
sequence.put(checkpointTime, blobInfo);
checkpointTime = getLowerBoundTime(blobInfo);
}
logger.atInfo().log("Found sequence from %s to %s", checkpointTime, lastTime);
logger.atInfo().log("Found sequence from %s to %s.", checkpointTime, lastTime);
return true;
}
ImmutableList<GcsFileMetadata> listDiffFiles(DateTime fromTime, @Nullable DateTime toTime) {
ImmutableList<BlobInfo> listDiffFiles(
String gcsBucket, DateTime fromTime, @Nullable DateTime toTime) {
logger.atInfo().log("Requested restore from time: %s", fromTime);
if (toTime != null) {
logger.atInfo().log(" Until time: %s", toTime);
@@ -95,70 +114,78 @@ class GcsDiffFileLister {
// List all of the diff files on GCS and build a map from each file's upper checkpoint time
// (extracted from the filename) to its asynchronously-loaded metadata, keeping only files with
// an upper checkpoint time > fromTime.
TreeMap<DateTime, ListenableFuture<GcsFileMetadata>> upperBoundTimesToMetadata
= new TreeMap<>();
Iterator<ListItem> listItems;
TreeMap<DateTime, ListenableFuture<BlobInfo>> upperBoundTimesToBlobInfo = new TreeMap<>();
String commitLogDiffPrefix = getCommitLogDiffPrefix(fromTime, toTime);
ImmutableList<String> filenames;
try {
// TODO(b/23554360): Use a smarter prefixing strategy to speed this up.
listItems = gcsService.list(
gcsBucket,
new ListOptions.Builder().setPrefix(DIFF_FILE_PREFIX).build());
filenames =
gcsUtils.listFolderObjects(gcsBucket, commitLogDiffPrefix).stream()
.map(s -> commitLogDiffPrefix + s)
.collect(toImmutableList());
} catch (IOException e) {
throw new RuntimeException(e);
}
DateTime lastUpperBoundTime = START_OF_TIME;
while (listItems.hasNext()) {
final String filename = listItems.next().getName();
DateTime upperBoundTime = DateTime.parse(filename.substring(DIFF_FILE_PREFIX.length()));
if (isInRange(upperBoundTime, fromTime, toTime)) {
upperBoundTimesToMetadata.put(upperBoundTime, executor.submit(() -> getMetadata(filename)));
lastUpperBoundTime = latestOf(upperBoundTime, lastUpperBoundTime);
}
}
if (upperBoundTimesToMetadata.isEmpty()) {
logger.atInfo().log("No files found");
return ImmutableList.of();
}
// Reconstruct the sequence of files by traversing backwards from "lastUpperBoundTime" (i.e. the
// last file that we found) and finding its previous file until we either run out of files or
// get to one that precedes "fromTime".
//
// GCS file listing is eventually consistent, so it's possible that we are missing a file. The
// metadata of a file is sufficient to identify the preceding file, so if we start from the
// last file and work backwards we can verify that we have no holes in our chain (although we
// may be missing files at the end).
TreeMap<DateTime, GcsFileMetadata> sequence = new TreeMap<>();
logger.atInfo().log("Restoring until: %s", lastUpperBoundTime);
boolean inconsistentFileSet = !constructDiffSequence(
upperBoundTimesToMetadata, fromTime, lastUpperBoundTime, sequence);
// Verify that all of the elements in the original set are represented in the sequence. If we
// find anything that's not represented, construct a sequence for it.
boolean checkForMoreExtraDiffs = true; // Always loop at least once.
while (checkForMoreExtraDiffs) {
checkForMoreExtraDiffs = false;
for (DateTime key : upperBoundTimesToMetadata.descendingKeySet()) {
if (!isInRange(key, fromTime, toTime)) {
break;
}
if (!sequence.containsKey(key)) {
constructDiffSequence(upperBoundTimesToMetadata, fromTime, key, sequence);
checkForMoreExtraDiffs = true;
inconsistentFileSet = true;
break;
TreeMap<DateTime, BlobInfo> sequence = new TreeMap<>();
ListeningExecutorService executor = executorProvider.get();
try {
for (String filename : filenames) {
String strippedFilename = filename.replaceFirst(DIFF_FILE_PREFIX, "");
DateTime upperBoundTime = DateTime.parse(strippedFilename);
if (isInRange(upperBoundTime, fromTime, toTime)) {
upperBoundTimesToBlobInfo.put(
upperBoundTime, executor.submit(() -> getBlobInfo(gcsBucket, filename)));
lastUpperBoundTime = latestOf(upperBoundTime, lastUpperBoundTime);
}
}
}
if (upperBoundTimesToBlobInfo.isEmpty()) {
logger.atInfo().log("No files found.");
return ImmutableList.of();
}
checkState(
!inconsistentFileSet,
"Unable to compute commit diff history, there are either gaps or forks in the history "
+ "file set. Check log for details.");
// Reconstruct the sequence of files by traversing backwards from "lastUpperBoundTime" (i.e.
// the last file that we found) and finding its previous file until we either run out of files
// or get to one that precedes "fromTime".
//
// GCS file listing is eventually consistent, so it's possible that we are missing a file. The
// metadata of a file is sufficient to identify the preceding file, so if we start from the
// last file and work backwards we can verify that we have no holes in our chain (although we
// may be missing files at the end).
logger.atInfo().log("Restoring until: %s", lastUpperBoundTime);
boolean inconsistentFileSet =
!constructDiffSequence(
gcsBucket, upperBoundTimesToBlobInfo, fromTime, lastUpperBoundTime, sequence);
// Verify that all of the elements in the original set are represented in the sequence. If we
// find anything that's not represented, construct a sequence for it.
boolean checkForMoreExtraDiffs = true; // Always loop at least once.
while (checkForMoreExtraDiffs) {
checkForMoreExtraDiffs = false;
for (DateTime key : upperBoundTimesToBlobInfo.descendingKeySet()) {
if (!isInRange(key, fromTime, toTime)) {
break;
}
if (!sequence.containsKey(key)) {
constructDiffSequence(gcsBucket, upperBoundTimesToBlobInfo, fromTime, key, sequence);
checkForMoreExtraDiffs = true;
inconsistentFileSet = true;
break;
}
}
}
checkState(
!inconsistentFileSet,
"Unable to compute commit diff history, there are either gaps or forks in the history "
+ "file set. Check log for details.");
} finally {
executor.shutdown();
}
logger.atInfo().log(
"Actual restore from time: %s", getLowerBoundTime(sequence.firstEntry().getValue()));
logger.atInfo().log("Found %d files to restore", sequence.size());
logger.atInfo().log("Found %d files to restore.", sequence.size());
return ImmutableList.copyOf(sequence.values());
}
@@ -171,15 +198,43 @@ class GcsDiffFileLister {
return isBeforeOrAt(start, time) && (end == null || isBeforeOrAt(time, end));
}
private DateTime getLowerBoundTime(GcsFileMetadata metadata) {
return DateTime.parse(metadata.getOptions().getUserMetadata().get(LOWER_BOUND_CHECKPOINT));
private DateTime getLowerBoundTime(BlobInfo blobInfo) {
return DateTime.parse(blobInfo.getMetadata().get(LOWER_BOUND_CHECKPOINT));
}
private GcsFileMetadata getMetadata(String filename) {
try {
return gcsService.getMetadata(new GcsFilename(gcsBucket, filename));
} catch (IOException e) {
throw new RuntimeException(e);
private BlobInfo getBlobInfo(String gcsBucket, String filename) {
return gcsUtils.getBlobInfo(BlobId.of(gcsBucket, filename));
}
/**
* Returns a prefix guaranteed to cover all commit log diff files in the given range.
*
* <p>The listObjects call can be fairly slow if we search over many thousands or tens of
* thousands of files, so we restrict the search space. The commit logs have a file format of
* "commit_diff_until_2021-05-11T06:48:00.070Z" so we can often filter down as far as the hour.
*
* <p>Here, we get the longest prefix possible based on which fields (year, month, day, hour) the
* times in question have in common.
*/
@VisibleForTesting
static String getCommitLogDiffPrefix(DateTime from, @Nullable DateTime to) {
StringBuilder result = new StringBuilder(DIFF_FILE_PREFIX);
if (to == null || from.getYear() != to.getYear()) {
return result.toString();
}
result.append(from.getYear()).append('-');
if (from.getMonthOfYear() != to.getMonthOfYear()) {
return result.toString();
}
result.append(String.format("%02d-", from.getMonthOfYear()));
if (from.getDayOfMonth() != to.getDayOfMonth()) {
return result.toString();
}
result.append(String.format("%02dT", from.getDayOfMonth()));
if (from.getHourOfDay() != to.getHourOfDay()) {
return result.toString();
}
result.append(String.format("%02d:", from.getHourOfDay()));
return result.toString();
}
}

View File

@@ -20,19 +20,28 @@ import static google.registry.backup.RestoreCommitLogsAction.DRY_RUN_PARAM;
import static google.registry.model.ofy.EntityWritePriorities.getEntityPriority;
import static google.registry.model.ofy.ObjectifyService.auditedOfy;
import static google.registry.persistence.transaction.TransactionManagerFactory.jpaTm;
import static google.registry.util.DateTimeUtils.isAtOrAfter;
import static google.registry.util.DateTimeUtils.isBeforeOrAt;
import static javax.servlet.http.HttpServletResponse.SC_NO_CONTENT;
import static javax.servlet.http.HttpServletResponse.SC_OK;
import static org.joda.time.Duration.standardHours;
import com.google.appengine.api.datastore.Entity;
import com.google.appengine.api.datastore.Key;
import com.google.appengine.tools.cloudstorage.GcsFileMetadata;
import com.google.appengine.tools.cloudstorage.GcsService;
import com.google.common.base.Joiner;
import com.google.cloud.storage.BlobInfo;
import com.google.common.collect.ImmutableList;
import com.google.common.flogger.FluentLogger;
import google.registry.config.RegistryConfig.Config;
import google.registry.gcs.GcsUtils;
import google.registry.model.UpdateAutoTimestamp;
import google.registry.model.common.DatabaseMigrationStateSchedule;
import google.registry.model.common.DatabaseMigrationStateSchedule.MigrationState;
import google.registry.model.common.DatabaseMigrationStateSchedule.ReplayDirection;
import google.registry.model.replay.DatastoreEntity;
import google.registry.model.replay.DatastoreOnlyEntity;
import google.registry.model.replay.NonReplicatedEntity;
import google.registry.model.replay.ReplaySpecializer;
import google.registry.model.replay.SqlReplayCheckpoint;
import google.registry.model.server.Lock;
import google.registry.model.translators.VKeyTranslatorFactory;
import google.registry.persistence.VKey;
@@ -41,21 +50,16 @@ import google.registry.request.Action.Method;
import google.registry.request.Parameter;
import google.registry.request.Response;
import google.registry.request.auth.Auth;
import google.registry.schema.replay.DatastoreEntity;
import google.registry.schema.replay.DatastoreOnlyEntity;
import google.registry.schema.replay.NonReplicatedEntity;
import google.registry.schema.replay.ReplaySpecializer;
import google.registry.schema.replay.SqlReplayCheckpoint;
import google.registry.util.Clock;
import google.registry.util.RequestStatusChecker;
import java.io.IOException;
import java.io.InputStream;
import java.nio.channels.Channels;
import java.util.Optional;
import javax.inject.Inject;
import javax.servlet.http.HttpServletResponse;
import org.joda.time.DateTime;
import org.joda.time.Duration;
import org.joda.time.Seconds;
/** Action that replays commit logs to Cloud SQL to keep it up to date. */
@Action(
@@ -69,19 +73,22 @@ public class ReplayCommitLogsToSqlAction implements Runnable {
static final String PATH = "/_dr/task/replayCommitLogsToSql";
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
private static final int BLOCK_SIZE =
1024 * 1024; // Buffer 1mb at a time, for no particular reason.
private static final Duration LEASE_LENGTH = standardHours(1);
// Stop / pause where we are if we've been replaying for more than five minutes to avoid GAE
// request timeouts
private static final Duration REPLAY_TIMEOUT_DURATION = Duration.standardMinutes(5);
@Inject GcsService gcsService;
@Inject GcsUtils gcsUtils;
@Inject Response response;
@Inject RequestStatusChecker requestStatusChecker;
@Inject GcsDiffFileLister diffLister;
@Inject Clock clock;
@Inject
@Config("commitLogGcsBucket")
String gcsBucket;
/** If true, will exit after logging the commit log files that would otherwise be replayed. */
@Inject
@Parameter(DRY_RUN_PARAM)
@@ -92,7 +99,8 @@ public class ReplayCommitLogsToSqlAction implements Runnable {
@Override
public void run() {
MigrationState state = DatabaseMigrationStateSchedule.getValueAtTime(clock.nowUtc());
DateTime startTime = clock.nowUtc();
MigrationState state = DatabaseMigrationStateSchedule.getValueAtTime(startTime);
if (!state.getReplayDirection().equals(ReplayDirection.DATASTORE_TO_SQL)) {
String message =
String.format(
@@ -117,26 +125,15 @@ public class ReplayCommitLogsToSqlAction implements Runnable {
}
try {
logger.atInfo().log("Beginning replay of commit logs.");
ImmutableList<GcsFileMetadata> commitLogFiles = getFilesToReplay();
String resultMessage;
if (dryRun) {
response.setStatus(HttpServletResponse.SC_OK);
ImmutableList<String> filenames =
commitLogFiles.stream()
.limit(10)
.map(file -> file.getFilename().getObjectName())
.collect(toImmutableList());
String dryRunMessage =
"Running in dry-run mode; would have processed %d files. They are (limit 10):\n"
+ Joiner.on('\n').join(filenames);
response.setPayload(dryRunMessage);
logger.atInfo().log(dryRunMessage);
resultMessage = executeDryRun();
} else {
replayFiles(commitLogFiles);
response.setStatus(HttpServletResponse.SC_OK);
String message = "ReplayCommitLogsToSqlAction completed successfully.";
response.setPayload(message);
logger.atInfo().log(message);
resultMessage = replayFiles(startTime);
}
response.setStatus(SC_OK);
response.setPayload(resultMessage);
logger.atInfo().log(resultMessage);
} catch (Throwable t) {
String message = "Errored out replaying files.";
logger.atSevere().withCause(t).log(message);
@@ -147,60 +144,100 @@ public class ReplayCommitLogsToSqlAction implements Runnable {
}
}
private ImmutableList<GcsFileMetadata> getFilesToReplay() {
private String executeDryRun() {
// Start at the first millisecond we haven't seen yet
DateTime fromTime = jpaTm().transact(() -> SqlReplayCheckpoint.get().plusMillis(1));
logger.atInfo().log("Starting replay from: %s.", fromTime);
// If there's an inconsistent file set, this will throw IllegalStateException and the job
// will try later -- this is likely because an export hasn't finished yet.
ImmutableList<GcsFileMetadata> commitLogFiles =
diffLister.listDiffFiles(fromTime, /* current time */ null);
logger.atInfo().log("Found %d new commit log files to process.", commitLogFiles.size());
return commitLogFiles;
DateTime searchStartTime = jpaTm().transact(() -> SqlReplayCheckpoint.get().plusMillis(1));
// Search through the end of the hour
DateTime searchEndTime =
searchStartTime.withMinuteOfHour(59).withSecondOfMinute(59).withMillisOfSecond(999);
ImmutableList<String> fileBatch =
diffLister.listDiffFiles(gcsBucket, searchStartTime, searchEndTime).stream()
.map(BlobInfo::getName)
.collect(toImmutableList());
return String.format(
"Running in dry-run mode, the first set of commit log files processed would be from "
+ "searching from %s to %s and would contain %d file(s). They are (limit 10): \n%s",
searchStartTime,
searchEndTime,
fileBatch.size(),
fileBatch.stream().limit(10).collect(toImmutableList()));
}
private void replayFiles(ImmutableList<GcsFileMetadata> commitLogFiles) {
DateTime replayTimeoutTime = clock.nowUtc().plus(REPLAY_TIMEOUT_DURATION);
int processedFiles = 0;
for (GcsFileMetadata metadata : commitLogFiles) {
// One transaction per GCS file
jpaTm().transact(() -> processFile(metadata));
processedFiles++;
if (clock.nowUtc().isAfter(replayTimeoutTime)) {
logger.atInfo().log(
"Reached max execution time after replaying %d files, leaving %d files for next run.",
processedFiles, commitLogFiles.size() - processedFiles);
return;
private String replayFiles(DateTime startTime) {
DateTime replayTimeoutTime = startTime.plus(REPLAY_TIMEOUT_DURATION);
DateTime searchStartTime = jpaTm().transact(() -> SqlReplayCheckpoint.get().plusMillis(1));
int filesProcessed = 0;
int transactionsProcessed = 0;
// Starting from one millisecond after the last file we processed, search for and import files
// one hour at a time until we catch up to the current time or we hit the replay timeout (in
// which case the next run will pick up from where we leave off).
//
// We use hour-long batches because GCS supports filename prefix-based searches.
while (true) {
if (isAtOrAfter(clock.nowUtc(), replayTimeoutTime)) {
return createResponseString(
"Reached max execution time", startTime, filesProcessed, transactionsProcessed);
}
if (isBeforeOrAt(clock.nowUtc(), searchStartTime)) {
return createResponseString(
"Caught up to current time", startTime, filesProcessed, transactionsProcessed);
}
// Search through the end of the hour
DateTime searchEndTime =
searchStartTime.withMinuteOfHour(59).withSecondOfMinute(59).withMillisOfSecond(999);
ImmutableList<BlobInfo> fileBatch =
diffLister.listDiffFiles(gcsBucket, searchStartTime, searchEndTime);
if (fileBatch.isEmpty()) {
logger.atInfo().log(
"No remaining files found in hour %s, continuing search in the next hour.",
searchStartTime.toString("yyyy-MM-dd HH"));
}
for (BlobInfo file : fileBatch) {
transactionsProcessed += processFile(file);
filesProcessed++;
if (clock.nowUtc().isAfter(replayTimeoutTime)) {
return createResponseString(
"Reached max execution time", startTime, filesProcessed, transactionsProcessed);
}
}
searchStartTime = searchEndTime.plusMillis(1);
}
logger.atInfo().log("Replayed %d commit log files to SQL successfully.", processedFiles);
}
private void processFile(GcsFileMetadata metadata) {
logger.atInfo().log(
"Processing commit log file %s of size %d B.",
metadata.getFilename(), metadata.getLength());
try (InputStream input =
Channels.newInputStream(
gcsService.openPrefetchingReadChannel(metadata.getFilename(), 0, BLOCK_SIZE))) {
private String createResponseString(
String msg, DateTime startTime, int filesProcessed, int transactionsProcessed) {
double tps =
(double) transactionsProcessed
/ (double) Seconds.secondsBetween(startTime, clock.nowUtc()).getSeconds();
return String.format(
"%s after replaying %d file(s) containing %d total transaction(s) (%.2f tx/s).",
msg, filesProcessed, transactionsProcessed, tps);
}
/**
* Replays the commit logs in the given commit log file and returns the number of transactions
* committed.
*/
private int processFile(BlobInfo metadata) {
try (InputStream input = gcsUtils.openInputStream(metadata.getBlobId())) {
// Load and process the Datastore transactions one at a time
ImmutableList<ImmutableList<VersionedEntity>> allTransactions =
CommitLogImports.loadEntitiesByTransaction(input);
logger.atInfo().log(
"Replaying %d transactions from commit log file %s.",
allTransactions.size(), metadata.getFilename());
allTransactions.forEach(this::replayTransaction);
try (UpdateAutoTimestamp.DisableAutoUpdateResource disabler =
UpdateAutoTimestamp.disableAutoUpdate()) {
allTransactions.forEach(
transaction -> jpaTm().transact(() -> replayTransaction(transaction)));
}
// if we succeeded, set the last-seen time
DateTime checkpoint =
DateTime.parse(
metadata.getFilename().getObjectName().substring(DIFF_FILE_PREFIX.length()));
SqlReplayCheckpoint.set(checkpoint);
DateTime checkpoint = DateTime.parse(metadata.getName().substring(DIFF_FILE_PREFIX.length()));
jpaTm().transact(() -> SqlReplayCheckpoint.set(checkpoint));
logger.atInfo().log(
"Replayed %d transactions from commit log file %s.",
allTransactions.size(), metadata.getFilename());
"Replayed %d transactions from commit log file %s with size %d B.",
allTransactions.size(), metadata.getName(), metadata.getSize());
return allTransactions.size();
} catch (IOException e) {
throw new RuntimeException(
"Errored out while replaying commit log file " + metadata.getFilename(), e);
"Errored out while replaying commit log file " + metadata.getName(), e);
}
}
@@ -219,20 +256,25 @@ public class ReplayCommitLogsToSqlAction implements Runnable {
private void handleEntityPut(Entity entity) {
Object ofyPojo = auditedOfy().toPojo(entity);
if (ofyPojo instanceof DatastoreEntity) {
DatastoreEntity datastoreEntity = (DatastoreEntity) ofyPojo;
datastoreEntity
.toSqlEntity()
.ifPresent(
sqlEntity -> {
ReplaySpecializer.beforeSqlSave(sqlEntity);
jpaTm().put(sqlEntity);
});
} else {
// this should never happen, but we shouldn't fail on it
logger.atSevere().log(
"%s does not implement DatastoreEntity, which is necessary for SQL replay.",
ofyPojo.getClass());
try {
if (ofyPojo instanceof DatastoreEntity) {
DatastoreEntity datastoreEntity = (DatastoreEntity) ofyPojo;
datastoreEntity
.toSqlEntity()
.ifPresent(
sqlEntity -> {
sqlEntity.beforeSqlSaveOnReplay();
jpaTm().putIgnoringReadOnly(sqlEntity);
});
} else {
// this should never happen, but we shouldn't fail on it
logger.atSevere().log(
"%s does not implement DatastoreEntity, which is necessary for SQL replay.",
ofyPojo.getClass());
}
} catch (Throwable t) {
logger.atSevere().log("Error when replaying object %s.", ofyPojo);
throw t;
}
}
@@ -248,13 +290,18 @@ public class ReplayCommitLogsToSqlAction implements Runnable {
"Skipping SQL delete for kind %s since it is not convertible.", key.getKind());
return;
}
Class<?> entityClass = entityVKey.getKind();
// Delete the key iff the class represents a JPA entity that is replicated
if (!NonReplicatedEntity.class.isAssignableFrom(entityClass)
&& !DatastoreOnlyEntity.class.isAssignableFrom(entityClass)
&& entityClass.getAnnotation(javax.persistence.Entity.class) != null) {
ReplaySpecializer.beforeSqlDelete(entityVKey);
jpaTm().delete(entityVKey);
try {
Class<?> entityClass = entityVKey.getKind();
// Delete the key iff the class represents a JPA entity that is replicated
if (!NonReplicatedEntity.class.isAssignableFrom(entityClass)
&& !DatastoreOnlyEntity.class.isAssignableFrom(entityClass)
&& entityClass.getAnnotation(javax.persistence.Entity.class) != null) {
ReplaySpecializer.beforeSqlDelete(entityVKey);
jpaTm().deleteIgnoringReadOnly(entityVKey);
}
} catch (Throwable t) {
logger.atSevere().log("Error when deleting key %s.", entityVKey);
throw t;
}
}

View File

@@ -23,9 +23,9 @@ import static google.registry.model.ofy.ObjectifyService.auditedOfy;
import com.google.appengine.api.datastore.DatastoreService;
import com.google.appengine.api.datastore.Entity;
import com.google.appengine.api.datastore.EntityTranslator;
import com.google.appengine.tools.cloudstorage.GcsFileMetadata;
import com.google.appengine.tools.cloudstorage.GcsService;
import com.google.cloud.storage.BlobInfo;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Lists;
import com.google.common.collect.PeekingIterator;
import com.google.common.collect.Streams;
@@ -33,7 +33,9 @@ import com.google.common.flogger.FluentLogger;
import com.googlecode.objectify.Key;
import com.googlecode.objectify.Result;
import com.googlecode.objectify.util.ResultNow;
import google.registry.config.RegistryConfig.Config;
import google.registry.config.RegistryEnvironment;
import google.registry.gcs.GcsUtils;
import google.registry.model.ImmutableObject;
import google.registry.model.ofy.CommitLogBucket;
import google.registry.model.ofy.CommitLogCheckpoint;
@@ -46,10 +48,10 @@ import google.registry.request.auth.Auth;
import google.registry.util.Retrier;
import java.io.IOException;
import java.io.InputStream;
import java.nio.channels.Channels;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Stream;
import javax.inject.Inject;
@@ -66,43 +68,55 @@ public class RestoreCommitLogsAction implements Runnable {
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
static final int BLOCK_SIZE = 1024 * 1024; // Buffer 1mb at a time, for no particular reason.
public static final String PATH = "/_dr/task/restoreCommitLogs";
static final String DRY_RUN_PARAM = "dryRun";
static final String FROM_TIME_PARAM = "fromTime";
static final String TO_TIME_PARAM = "toTime";
static final String BUCKET_OVERRIDE_PARAM = "gcsBucket";
private static final ImmutableSet<RegistryEnvironment> FORBIDDEN_ENVIRONMENTS =
ImmutableSet.of(RegistryEnvironment.PRODUCTION, RegistryEnvironment.SANDBOX);
@Inject GcsUtils gcsUtils;
@Inject GcsService gcsService;
@Inject @Parameter(DRY_RUN_PARAM) boolean dryRun;
@Inject @Parameter(FROM_TIME_PARAM) DateTime fromTime;
@Inject @Parameter(TO_TIME_PARAM) DateTime toTime;
@Inject
@Parameter(BUCKET_OVERRIDE_PARAM)
Optional<String> gcsBucketOverride;
@Inject DatastoreService datastoreService;
@Inject GcsDiffFileLister diffLister;
@Inject
@Config("commitLogGcsBucket")
String defaultGcsBucket;
@Inject Retrier retrier;
@Inject RestoreCommitLogsAction() {}
@Override
public void run() {
checkArgument(
RegistryEnvironment.get() == RegistryEnvironment.ALPHA
|| RegistryEnvironment.get() == RegistryEnvironment.CRASH
|| RegistryEnvironment.get() == RegistryEnvironment.UNITTEST,
"DO NOT RUN ANYWHERE ELSE EXCEPT ALPHA, CRASH OR TESTS.");
!FORBIDDEN_ENVIRONMENTS.contains(RegistryEnvironment.get()),
"DO NOT RUN IN PRODUCTION OR SANDBOX.");
if (dryRun) {
logger.atInfo().log("Running in dryRun mode");
logger.atInfo().log("Running in dry-run mode.");
}
List<GcsFileMetadata> diffFiles = diffLister.listDiffFiles(fromTime, toTime);
String gcsBucket = gcsBucketOverride.orElse(defaultGcsBucket);
logger.atInfo().log("Restoring from %s.", gcsBucket);
List<BlobInfo> diffFiles = diffLister.listDiffFiles(gcsBucket, fromTime, toTime);
if (diffFiles.isEmpty()) {
logger.atInfo().log("Nothing to restore");
logger.atInfo().log("Nothing to restore.");
return;
}
Map<Integer, DateTime> bucketTimestamps = new HashMap<>();
CommitLogCheckpoint lastCheckpoint = null;
for (GcsFileMetadata metadata : diffFiles) {
logger.atInfo().log("Restoring: %s", metadata.getFilename().getObjectName());
try (InputStream input = Channels.newInputStream(
gcsService.openPrefetchingReadChannel(metadata.getFilename(), 0, BLOCK_SIZE))) {
for (BlobInfo metadata : diffFiles) {
logger.atInfo().log("Restoring: %s", metadata.getName());
try (InputStream input = gcsUtils.openInputStream(metadata.getBlobId())) {
PeekingIterator<ImmutableObject> commitLogs =
peekingIterator(createDeserializingIterator(input, true));
lastCheckpoint = (CommitLogCheckpoint) commitLogs.next();
@@ -129,7 +143,7 @@ public class RestoreCommitLogsAction implements Runnable {
.build()),
Stream.of(CommitLogCheckpointRoot.create(lastCheckpoint.getCheckpointTime())))
.collect(toImmutableList()));
logger.atInfo().log("Restore complete");
logger.atInfo().log("Restore complete.");
}
/**

View File

@@ -105,29 +105,29 @@ public abstract class VersionedEntity implements Serializable {
* VersionedEntity VersionedEntities}. See {@link CommitLogImports#loadEntities} for more
* information.
*/
public static Stream<VersionedEntity> fromManifest(CommitLogManifest manifest) {
static Stream<VersionedEntity> fromManifest(CommitLogManifest manifest) {
long commitTimeMillis = manifest.getCommitTime().getMillis();
return manifest.getDeletions().stream()
.map(com.googlecode.objectify.Key::getRaw)
.map(key -> builder().commitTimeMills(commitTimeMillis).key(key).build());
.map(key -> newBuilder().commitTimeMills(commitTimeMillis).key(key).build());
}
/* Converts a {@link CommitLogMutation} to a {@link VersionedEntity}. */
public static VersionedEntity fromMutation(CommitLogMutation mutation) {
static VersionedEntity fromMutation(CommitLogMutation mutation) {
return from(
com.googlecode.objectify.Key.create(mutation).getParent().getId(),
mutation.getEntityProtoBytes());
}
public static VersionedEntity from(long commitTimeMillis, byte[] entityProtoBytes) {
return builder()
return newBuilder()
.entityProtoBytes(entityProtoBytes)
.key(EntityTranslator.createFromPbBytes(entityProtoBytes).getKey())
.commitTimeMills(commitTimeMillis)
.build();
}
static Builder builder() {
private static Builder newBuilder() {
return new AutoValue_VersionedEntity.Builder();
}
@@ -142,7 +142,7 @@ public abstract class VersionedEntity implements Serializable {
public abstract VersionedEntity build();
public Builder entityProtoBytes(byte[] bytes) {
Builder entityProtoBytes(byte[] bytes) {
return entityProtoBytes(new ImmutableBytes(bytes));
}
}

View File

@@ -28,10 +28,10 @@ import com.googlecode.objectify.Key;
import google.registry.config.RegistryConfig.Config;
import google.registry.model.EppResource;
import google.registry.model.ImmutableObject;
import google.registry.model.domain.RegistryLock;
import google.registry.model.eppcommon.Trid;
import google.registry.model.host.HostResource;
import google.registry.persistence.VKey;
import google.registry.schema.domain.RegistryLock;
import google.registry.util.AppEngineServiceUtils;
import google.registry.util.Retrier;
import javax.inject.Inject;
@@ -126,18 +126,18 @@ public final class AsyncTaskEnqueuer {
public void enqueueAsyncDelete(
EppResource resourceToDelete,
DateTime now,
String requestingClientId,
String requestingRegistrarId,
Trid trid,
boolean isSuperuser) {
Key<EppResource> resourceKey = Key.create(resourceToDelete);
logger.atInfo().log(
"Enqueuing async deletion of %s on behalf of registrar %s.",
resourceKey, requestingClientId);
resourceKey, requestingRegistrarId);
TaskOptions task =
TaskOptions.Builder.withMethod(Method.PULL)
.countdownMillis(asyncDeleteDelay.getMillis())
.param(PARAM_RESOURCE_KEY, resourceKey.getString())
.param(PARAM_REQUESTING_CLIENT_ID, requestingClientId)
.param(PARAM_REQUESTING_CLIENT_ID, requestingRegistrarId)
.param(PARAM_SERVER_TRANSACTION_ID, trid.getServerTransactionId())
.param(PARAM_IS_SUPERUSER, Boolean.toString(isSuperuser))
.param(PARAM_REQUESTED_TIME, now.toString());

View File

@@ -24,6 +24,7 @@ import static google.registry.batch.AsyncTaskEnqueuer.QUEUE_ASYNC_HOST_RENAME;
import static google.registry.request.RequestParameters.extractIntParameter;
import static google.registry.request.RequestParameters.extractLongParameter;
import static google.registry.request.RequestParameters.extractOptionalBooleanParameter;
import static google.registry.request.RequestParameters.extractOptionalDatetimeParameter;
import static google.registry.request.RequestParameters.extractOptionalIntParameter;
import static google.registry.request.RequestParameters.extractOptionalParameter;
import static google.registry.request.RequestParameters.extractRequiredDatetimeParameter;
@@ -106,6 +107,13 @@ public class BatchModule {
return extractIntParameter(req, RelockDomainAction.PREVIOUS_ATTEMPTS_PARAM);
}
@Provides
@Parameter(ExpandRecurringBillingEventsAction.PARAM_CURSOR_TIME)
static Optional<DateTime> provideCursorTime(HttpServletRequest req) {
return extractOptionalDatetimeParameter(
req, ExpandRecurringBillingEventsAction.PARAM_CURSOR_TIME);
}
@Provides
@Named(QUEUE_ASYNC_ACTIONS)
static Queue provideAsyncActionsPushQueue() {

View File

@@ -311,7 +311,7 @@ public class DeleteContactsAndHostsAction implements Runnable {
@Override
public void reduce(final DeletionRequest deletionRequest, ReducerInput<Boolean> values) {
final boolean hasNoActiveReferences = !Iterators.contains(values, true);
logger.atInfo().log("Processing async deletion request for %s", deletionRequest.key());
logger.atInfo().log("Processing async deletion request for %s.", deletionRequest.key());
DeletionResult result =
tm()
.transactNew(
@@ -343,15 +343,15 @@ public class DeleteContactsAndHostsAction implements Runnable {
}
// Contacts and external hosts have a direct client id. For subordinate hosts it needs to be
// read off of the superordinate domain.
String resourceClientId = resource.getPersistedCurrentSponsorClientId();
String resourceRegistrarId = resource.getPersistedCurrentSponsorRegistrarId();
if (resource instanceof HostResource && ((HostResource) resource).isSubordinate()) {
resourceClientId =
resourceRegistrarId =
tm().loadByKey(((HostResource) resource).getSuperordinateDomain())
.cloneProjectedAtTime(now)
.getCurrentSponsorClientId();
.getCurrentSponsorRegistrarId();
}
boolean requestedByCurrentOwner =
resourceClientId.equals(deletionRequest.requestingClientId());
resourceRegistrarId.equals(deletionRequest.requestingClientId());
boolean deleteAllowed =
hasNoActiveReferences && (requestedByCurrentOwner || deletionRequest.isSuperuser());
@@ -371,14 +371,14 @@ public class DeleteContactsAndHostsAction implements Runnable {
HistoryEntry historyEntry =
HistoryEntry.createBuilderForResource(resource)
.setClientId(deletionRequest.requestingClientId())
.setRegistrarId(deletionRequest.requestingClientId())
.setModificationTime(now)
.setType(getHistoryEntryType(resource, deleteAllowed))
.build();
PollMessage.OneTime pollMessage =
new PollMessage.OneTime.Builder()
.setClientId(deletionRequest.requestingClientId())
.setRegistrarId(deletionRequest.requestingClientId())
.setMsg(pollMessageText)
.setParent(historyEntry)
.setEventTime(now)
@@ -605,12 +605,12 @@ public class DeleteContactsAndHostsAction implements Runnable {
static boolean doesResourceStateAllowDeletion(EppResource resource, DateTime now) {
Key<EppResource> key = Key.create(resource);
if (isDeleted(resource, now)) {
logger.atWarning().log("Cannot asynchronously delete %s because it is already deleted", key);
logger.atWarning().log("Cannot asynchronously delete %s because it is already deleted.", key);
return false;
}
if (!resource.getStatusValues().contains(PENDING_DELETE)) {
logger.atWarning().log(
"Cannot asynchronously delete %s because it is not in PENDING_DELETE", key);
"Cannot asynchronously delete %s because it is not in PENDING_DELETE.", key);
return false;
}
return true;

View File

@@ -167,7 +167,7 @@ public class DeleteExpiredDomainsAction implements Runnable {
/** Runs the actual domain delete flow and returns whether the deletion was successful. */
private boolean runDomainDeleteFlow(DomainBase domain) {
logger.atInfo().log("Attempting to delete domain %s", domain.getDomainName());
logger.atInfo().log("Attempting to delete domain '%s'.", domain.getDomainName());
// Create a new transaction that the flow's execution will be enlisted in that loads the domain
// transactionally. This way we can ensure that nothing else has modified the domain in question
// in the intervening period since the query above found it.
@@ -203,7 +203,7 @@ public class DeleteExpiredDomainsAction implements Runnable {
if (eppOutput.isPresent()) {
if (eppOutput.get().isSuccess()) {
logger.atInfo().log("Successfully deleted domain %s", domain.getDomainName());
logger.atInfo().log("Successfully deleted domain '%s'.", domain.getDomainName());
} else {
logger.atSevere().log(
"Failed to delete domain %s; EPP response:\n\n%s",

View File

@@ -135,14 +135,14 @@ public class DeleteLoadTestDataAction implements Runnable {
}
private void deleteContact(ContactResource contact) {
if (!LOAD_TEST_REGISTRARS.contains(contact.getPersistedCurrentSponsorClientId())) {
if (!LOAD_TEST_REGISTRARS.contains(contact.getPersistedCurrentSponsorRegistrarId())) {
return;
}
// We cannot remove contacts from domains in the general case, so we cannot delete contacts
// that are linked to domains (since it would break the foreign keys)
if (EppResourceUtils.isLinked(contact.createVKey(), clock.nowUtc())) {
logger.atWarning().log(
"Cannot delete contact with repo ID %s since it is referenced from a domain",
"Cannot delete contact with repo ID %s since it is referenced from a domain.",
contact.getRepoId());
return;
}
@@ -150,7 +150,7 @@ public class DeleteLoadTestDataAction implements Runnable {
}
private void deleteHost(HostResource host) {
if (!LOAD_TEST_REGISTRARS.contains(host.getPersistedCurrentSponsorClientId())) {
if (!LOAD_TEST_REGISTRARS.contains(host.getPersistedCurrentSponsorRegistrarId())) {
return;
}
VKey<HostResource> hostVKey = host.createVKey();
@@ -177,7 +177,7 @@ public class DeleteLoadTestDataAction implements Runnable {
HistoryEntryDao.loadHistoryObjectsForResource(eppResource.createVKey());
if (isDryRun) {
logger.atInfo().log(
"Would delete repo ID %s along with %d history objects",
"Would delete repo ID %s along with %d history objects.",
eppResource.getRepoId(), historyObjects.size());
} else {
historyObjects.forEach(tm()::delete);
@@ -198,7 +198,7 @@ public class DeleteLoadTestDataAction implements Runnable {
@Override
public final void map(EppResource resource) {
if (LOAD_TEST_REGISTRARS.contains(resource.getPersistedCurrentSponsorClientId())) {
if (LOAD_TEST_REGISTRARS.contains(resource.getPersistedCurrentSponsorRegistrarId())) {
deleteResource(resource);
getContext()
.incrementCounter(

View File

@@ -21,8 +21,9 @@ import static google.registry.config.RegistryEnvironment.PRODUCTION;
import static google.registry.mapreduce.MapreduceRunner.PARAM_DRY_RUN;
import static google.registry.model.ResourceTransferUtils.updateForeignKeyIndexDeletionTime;
import static google.registry.model.ofy.ObjectifyService.auditedOfy;
import static google.registry.model.registry.Registries.getTldsOfType;
import static google.registry.model.reporting.HistoryEntry.Type.DOMAIN_DELETE;
import static google.registry.model.tld.Registries.getTldsOfType;
import static google.registry.persistence.transaction.TransactionManagerFactory.jpaTm;
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
import static google.registry.request.Action.Method.POST;
import static google.registry.request.RequestParameters.PARAM_TLDS;
@@ -42,27 +43,31 @@ import google.registry.config.RegistryEnvironment;
import google.registry.dns.DnsQueue;
import google.registry.mapreduce.MapreduceRunner;
import google.registry.mapreduce.inputs.EppResourceInputs;
import google.registry.model.CreateAutoTimestamp;
import google.registry.model.EppResourceUtils;
import google.registry.model.domain.DomainBase;
import google.registry.model.domain.DomainHistory;
import google.registry.model.index.EppResourceIndex;
import google.registry.model.index.ForeignKeyIndex;
import google.registry.model.registry.Registry;
import google.registry.model.registry.Registry.TldType;
import google.registry.model.tld.Registry;
import google.registry.model.tld.Registry.TldType;
import google.registry.request.Action;
import google.registry.request.Parameter;
import google.registry.request.Response;
import google.registry.request.auth.Auth;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import javax.inject.Inject;
import org.hibernate.CacheMode;
import org.hibernate.ScrollMode;
import org.hibernate.ScrollableResults;
import org.hibernate.query.Query;
import org.joda.time.DateTime;
import org.joda.time.Duration;
/**
* Deletes all prober DomainBases and their subordinate history entries, poll messages, and
* billing events, along with their ForeignKeyDomainIndex and EppResourceIndex entities.
*
* <p>See: https://www.youtube.com/watch?v=xuuv0syoHnM
* Deletes all prober DomainBases and their subordinate history entries, poll messages, and billing
* events, along with their ForeignKeyDomainIndex and EppResourceIndex entities.
*/
@Action(
service = Action.Service.BACKEND,
@@ -73,10 +78,51 @@ public class DeleteProberDataAction implements Runnable {
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
/**
* The maximum amount of time we allow a prober domain to be in use.
*
* <p>In practice, the prober's connection will time out well before this duration. This includes
* a decent buffer.
*/
private static final Duration DOMAIN_USED_DURATION = Duration.standardHours(1);
/**
* The minimum amount of time we want a domain to be "soft deleted".
*
* <p>The domain has to remain soft deleted for at least enough time for the DNS task to run and
* remove it from DNS itself. This is probably on the order of minutes.
*/
private static final Duration SOFT_DELETE_DELAY = Duration.standardHours(1);
private static final DnsQueue dnsQueue = DnsQueue.create();
// Domains to delete must:
// 1. Be in one of the prober TLDs
// 2. Not be a nic domain
// 3. Have no subordinate hosts
// 4. Not still be used (within an hour of creation time)
// 5. Either be active (creationTime <= now < deletionTime) or have been deleted a while ago (this
// prevents accidental double-map with the same key from immediately deleting active domains)
//
// Note: creationTime must be compared to a Java object (CreateAutoTimestamp) but deletionTime can
// be compared directly to the SQL timestamp (it's a DateTime)
private static final String DOMAIN_QUERY_STRING =
"FROM Domain d WHERE d.tld IN :tlds AND d.fullyQualifiedDomainName NOT LIKE 'nic.%' AND"
+ " (d.subordinateHosts IS EMPTY OR d.subordinateHosts IS NULL) AND d.creationTime <"
+ " :creationTimeCutoff AND ((d.creationTime <= :nowAutoTimestamp AND d.deletionTime >"
+ " current_timestamp()) OR d.deletionTime < :nowMinusSoftDeleteDelay) ORDER BY d.repoId";
/** Number of domains to retrieve and delete per SQL transaction. */
private static final int BATCH_SIZE = 1000;
@Inject @Parameter(PARAM_DRY_RUN) boolean isDryRun;
/** List of TLDs to work on. If empty - will work on all TLDs that end with .test. */
@Inject @Parameter(PARAM_TLDS) ImmutableSet<String> tlds;
@Inject @Config("registryAdminClientId") String registryAdminClientId;
@Inject
@Config("registryAdminClientId")
String registryAdminRegistrarId;
@Inject MapreduceRunner mrRunner;
@Inject Response response;
@Inject DeleteProberDataAction() {}
@@ -84,25 +130,14 @@ public class DeleteProberDataAction implements Runnable {
@Override
public void run() {
checkState(
!Strings.isNullOrEmpty(registryAdminClientId),
!Strings.isNullOrEmpty(registryAdminRegistrarId),
"Registry admin client ID must be configured for prober data deletion to work");
mrRunner
.setJobName("Delete prober data")
.setModuleName("backend")
.runMapOnly(
new DeleteProberDataMapper(getProberRoidSuffixes(), isDryRun, registryAdminClientId),
ImmutableList.of(EppResourceInputs.createKeyInput(DomainBase.class)))
.sendLinkToMapreduceConsole(response);
}
private ImmutableSet<String> getProberRoidSuffixes() {
checkArgument(
!PRODUCTION.equals(RegistryEnvironment.get())
|| tlds.stream().allMatch(tld -> tld.endsWith(".test")),
"On production, can only work on TLDs that end with .test");
ImmutableSet<String> deletableTlds =
getTldsOfType(TldType.TEST)
.stream()
getTldsOfType(TldType.TEST).stream()
.filter(tld -> tlds.isEmpty() ? tld.endsWith(".test") : tlds.contains(tld))
.collect(toImmutableSet());
checkArgument(
@@ -110,10 +145,161 @@ public class DeleteProberDataAction implements Runnable {
"If tlds are given, they must all exist and be TEST tlds. Given: %s, not found: %s",
tlds,
Sets.difference(tlds, deletableTlds));
return deletableTlds
.stream()
.map(tld -> Registry.get(tld).getRoidSuffix())
.collect(toImmutableSet());
ImmutableSet<String> proberRoidSuffixes =
deletableTlds.stream()
.map(tld -> Registry.get(tld).getRoidSuffix())
.collect(toImmutableSet());
if (tm().isOfy()) {
mrRunner
.setJobName("Delete prober data")
.setModuleName("backend")
.runMapOnly(
new DeleteProberDataMapper(proberRoidSuffixes, isDryRun, registryAdminRegistrarId),
ImmutableList.of(EppResourceInputs.createKeyInput(DomainBase.class)))
.sendLinkToMapreduceConsole(response);
} else {
runSqlJob(deletableTlds);
}
}
private void runSqlJob(ImmutableSet<String> deletableTlds) {
AtomicInteger softDeletedDomains = new AtomicInteger();
AtomicInteger hardDeletedDomains = new AtomicInteger();
jpaTm().transact(() -> processDomains(deletableTlds, softDeletedDomains, hardDeletedDomains));
logger.atInfo().log(
"%s %d domains.",
isDryRun ? "Would have soft-deleted" : "Soft-deleted", softDeletedDomains.get());
logger.atInfo().log(
"%s %d domains.",
isDryRun ? "Would have hard-deleted" : "Hard-deleted", hardDeletedDomains.get());
}
private void processDomains(
ImmutableSet<String> deletableTlds,
AtomicInteger softDeletedDomains,
AtomicInteger hardDeletedDomains) {
DateTime now = tm().getTransactionTime();
// Scroll through domains, soft-deleting as necessary (very few will be soft-deleted) and
// keeping track of which domains to hard-delete (there can be many, so we batch them up)
ScrollableResults scrollableResult =
jpaTm()
.query(DOMAIN_QUERY_STRING, DomainBase.class)
.setParameter("tlds", deletableTlds)
.setParameter(
"creationTimeCutoff", CreateAutoTimestamp.create(now.minus(DOMAIN_USED_DURATION)))
.setParameter("nowMinusSoftDeleteDelay", now.minus(SOFT_DELETE_DELAY))
.setParameter("nowAutoTimestamp", CreateAutoTimestamp.create(now))
.unwrap(Query.class)
.setCacheMode(CacheMode.IGNORE)
.scroll(ScrollMode.FORWARD_ONLY);
ImmutableList.Builder<String> domainRepoIdsToHardDelete = new ImmutableList.Builder<>();
ImmutableList.Builder<String> hostNamesToHardDelete = new ImmutableList.Builder<>();
for (int i = 1; scrollableResult.next(); i = (i + 1) % BATCH_SIZE) {
DomainBase domain = (DomainBase) scrollableResult.get(0);
processDomain(
domain,
domainRepoIdsToHardDelete,
hostNamesToHardDelete,
softDeletedDomains,
hardDeletedDomains);
// Batch the deletion and DB flush + session clearing so we don't OOM
if (i == 0) {
hardDeleteDomainsAndHosts(domainRepoIdsToHardDelete.build(), hostNamesToHardDelete.build());
domainRepoIdsToHardDelete = new ImmutableList.Builder<>();
hostNamesToHardDelete = new ImmutableList.Builder<>();
jpaTm().getEntityManager().flush();
jpaTm().getEntityManager().clear();
}
}
// process the remainder
hardDeleteDomainsAndHosts(domainRepoIdsToHardDelete.build(), hostNamesToHardDelete.build());
}
private void processDomain(
DomainBase domain,
ImmutableList.Builder<String> domainRepoIdsToHardDelete,
ImmutableList.Builder<String> hostNamesToHardDelete,
AtomicInteger softDeletedDomains,
AtomicInteger hardDeletedDomains) {
// If the domain is still active, that means that the prober encountered a failure and did not
// successfully soft-delete the domain (thus leaving its DNS entry published). We soft-delete
// it now so that the DNS entry can be handled. The domain will then be hard-deleted the next
// time the job is run.
if (EppResourceUtils.isActive(domain, tm().getTransactionTime())) {
if (isDryRun) {
logger.atInfo().log(
"Would soft-delete the active domain: %s (%s).",
domain.getDomainName(), domain.getRepoId());
} else {
softDeleteDomain(domain, registryAdminRegistrarId, dnsQueue);
}
softDeletedDomains.incrementAndGet();
} else {
if (isDryRun) {
logger.atInfo().log(
"Would hard-delete the non-active domain: %s (%s) and its dependents.",
domain.getDomainName(), domain.getRepoId());
} else {
domainRepoIdsToHardDelete.add(domain.getRepoId());
hostNamesToHardDelete.addAll(domain.getSubordinateHosts());
}
hardDeletedDomains.incrementAndGet();
}
}
private void hardDeleteDomainsAndHosts(
ImmutableList<String> domainRepoIds, ImmutableList<String> hostNames) {
jpaTm()
.query("DELETE FROM Host WHERE fullyQualifiedHostName IN :hostNames")
.setParameter("hostNames", hostNames)
.executeUpdate();
jpaTm()
.query("DELETE FROM BillingEvent WHERE domainRepoId IN :repoIds")
.setParameter("repoIds", domainRepoIds)
.executeUpdate();
jpaTm()
.query("DELETE FROM BillingRecurrence WHERE domainRepoId IN :repoIds")
.setParameter("repoIds", domainRepoIds)
.executeUpdate();
jpaTm()
.query("DELETE FROM BillingCancellation WHERE domainRepoId IN :repoIds")
.setParameter("repoIds", domainRepoIds)
.executeUpdate();
jpaTm()
.query("DELETE FROM DomainHistory WHERE domainRepoId IN :repoIds")
.setParameter("repoIds", domainRepoIds)
.executeUpdate();
jpaTm()
.query("DELETE FROM PollMessage WHERE domainRepoId IN :repoIds")
.setParameter("repoIds", domainRepoIds)
.executeUpdate();
jpaTm()
.query("DELETE FROM Domain WHERE repoId IN :repoIds")
.setParameter("repoIds", domainRepoIds)
.executeUpdate();
}
// Take a DNS queue + admin registrar id as input so that it can be called from the mapper as well
private static void softDeleteDomain(
DomainBase domain, String registryAdminRegistrarId, DnsQueue localDnsQueue) {
DomainBase deletedDomain =
domain.asBuilder().setDeletionTime(tm().getTransactionTime()).setStatusValues(null).build();
DomainHistory historyEntry =
new DomainHistory.Builder()
.setDomain(domain)
.setType(DOMAIN_DELETE)
.setModificationTime(tm().getTransactionTime())
.setBySuperuser(true)
.setReason("Deletion of prober data")
.setRegistrarId(registryAdminRegistrarId)
.build();
// Note that we don't bother handling grace periods, billing events, pending transfers, poll
// messages, or auto-renews because those will all be hard-deleted the next time the job runs
// anyway.
tm().putAllWithoutBackup(ImmutableList.of(deletedDomain, historyEntry));
// updating foreign keys is a no-op in SQL
updateForeignKeyIndexDeletionTime(deletedDomain);
localDnsQueue.addDomainRefreshTask(deletedDomain.getDomainName());
}
/** Provides the map method that runs for each existing DomainBase entity. */
@@ -122,32 +308,17 @@ public class DeleteProberDataAction implements Runnable {
private static final DnsQueue dnsQueue = DnsQueue.create();
private static final long serialVersionUID = -7724537393697576369L;
/**
* The maximum amount of time we allow a prober domain to be in use.
*
* In practice, the prober's connection will time out well before this duration. This includes a
* decent buffer.
*
*/
private static final Duration DOMAIN_USED_DURATION = Duration.standardHours(1);
/**
* The minimum amount of time we want a domain to be "soft deleted".
*
* The domain has to remain soft deleted for at least enough time for the DNS task to run and
* remove it from DNS itself. This is probably on the order of minutes.
*/
private static final Duration SOFT_DELETE_DELAY = Duration.standardHours(1);
private final ImmutableSet<String> proberRoidSuffixes;
private final Boolean isDryRun;
private final String registryAdminClientId;
private final String registryAdminRegistrarId;
public DeleteProberDataMapper(
ImmutableSet<String> proberRoidSuffixes, Boolean isDryRun, String registryAdminClientId) {
ImmutableSet<String> proberRoidSuffixes,
Boolean isDryRun,
String registryAdminRegistrarId) {
this.proberRoidSuffixes = proberRoidSuffixes;
this.isDryRun = isDryRun;
this.registryAdminClientId = registryAdminClientId;
this.registryAdminRegistrarId = registryAdminRegistrarId;
}
@Override
@@ -160,7 +331,7 @@ public class DeleteProberDataAction implements Runnable {
getContext().incrementCounter("skipped, non-prober data");
}
} catch (Throwable t) {
logger.atSevere().withCause(t).log("Error while deleting prober data for key %s", key);
logger.atSevere().withCause(t).log("Error while deleting prober data for key %s.", key);
getContext().incrementCounter(String.format("error, kind %s", key.getKind()));
}
}
@@ -201,9 +372,9 @@ public class DeleteProberDataAction implements Runnable {
if (EppResourceUtils.isActive(domain, now)) {
if (isDryRun) {
logger.atInfo().log(
"Would soft-delete the active domain: %s (%s)", domainName, domainKey);
"Would soft-delete the active domain: %s (%s).", domainName, domainKey);
} else {
softDeleteDomain(domain);
tm().transact(() -> softDeleteDomain(domain, registryAdminRegistrarId, dnsQueue));
}
getContext().incrementCounter("domains soft-deleted");
return;
@@ -223,8 +394,7 @@ public class DeleteProberDataAction implements Runnable {
tm().transact(
() -> {
// This ancestor query selects all descendant HistoryEntries, BillingEvents,
// PollMessages,
// and TLD-specific entities, as well as the domain itself.
// PollMessages, and TLD-specific entities, as well as the domain itself.
List<Key<Object>> domainAndDependentKeys =
auditedOfy().load().ancestor(domainKey).keys().list();
ImmutableSet<Key<?>> allKeys =
@@ -243,32 +413,5 @@ public class DeleteProberDataAction implements Runnable {
getContext().incrementCounter("domains hard-deleted");
getContext().incrementCounter("total entities hard-deleted", entitiesDeleted);
}
private void softDeleteDomain(final DomainBase domain) {
tm().transactNew(
() -> {
DomainBase deletedDomain =
domain
.asBuilder()
.setDeletionTime(tm().getTransactionTime())
.setStatusValues(null)
.build();
DomainHistory historyEntry =
new DomainHistory.Builder()
.setDomain(domain)
.setType(DOMAIN_DELETE)
.setModificationTime(tm().getTransactionTime())
.setBySuperuser(true)
.setReason("Deletion of prober data")
.setClientId(registryAdminClientId)
.build();
// Note that we don't bother handling grace periods, billing events, pending
// transfers, poll messages, or auto-renews because these will all be hard-deleted
// the next time the mapreduce runs anyway.
tm().putAll(deletedDomain, historyEntry);
updateForeignKeyIndexDeletionTime(deletedDomain);
dnsQueue.addDomainRefreshTask(deletedDomain.getDomainName());
});
}
}
}

View File

@@ -52,9 +52,9 @@ import google.registry.model.common.Cursor;
import google.registry.model.domain.DomainBase;
import google.registry.model.domain.DomainHistory;
import google.registry.model.domain.Period;
import google.registry.model.registry.Registry;
import google.registry.model.reporting.DomainTransactionRecord;
import google.registry.model.reporting.DomainTransactionRecord.TransactionReportField;
import google.registry.model.tld.Registry;
import google.registry.persistence.VKey;
import google.registry.request.Action;
import google.registry.request.Parameter;
@@ -148,9 +148,9 @@ public class ExpandRecurringBillingEventsAction implements Runnable {
.reduce(0, Integer::sum);
if (!isDryRun) {
logger.atInfo().log("Saved OneTime billing events", numBillingEventsSaved);
logger.atInfo().log("Saved OneTime billing events.", numBillingEventsSaved);
} else {
logger.atInfo().log("Generated OneTime billing events (dry run)", numBillingEventsSaved);
logger.atInfo().log("Generated OneTime billing events (dry run).", numBillingEventsSaved);
}
logger.atInfo().log(
"Recurring event expansion %s complete for billing event range [%s, %s).",
@@ -324,7 +324,7 @@ public class ExpandRecurringBillingEventsAction implements Runnable {
DomainHistory historyEntry =
new DomainHistory.Builder()
.setBySuperuser(false)
.setClientId(recurring.getClientId())
.setRegistrarId(recurring.getRegistrarId())
.setModificationTime(tm().getTransactionTime())
.setDomain(tm().loadByKey(domainKey))
.setPeriod(Period.create(1, YEARS))
@@ -354,7 +354,7 @@ public class ExpandRecurringBillingEventsAction implements Runnable {
syntheticOneTimesBuilder.add(
new OneTime.Builder()
.setBillingTime(billingTime)
.setClientId(recurring.getClientId())
.setRegistrarId(recurring.getRegistrarId())
.setCost(renewCost)
.setEventTime(eventTime)
.setFlags(union(recurring.getFlags(), Flag.SYNTHETIC))

View File

@@ -173,7 +173,7 @@ public class RefreshDnsOnHostRenameAction implements Runnable {
retrier.callWithRetry(
() -> dnsQueue.addDomainRefreshTask(domainName),
TransientFailureException.class);
logger.atInfo().log("Enqueued DNS refresh for domain %s.", domainName);
logger.atInfo().log("Enqueued DNS refresh for domain '%s'.", domainName);
});
deleteTasksWithRetry(
refreshRequests,

View File

@@ -28,16 +28,16 @@ import com.google.common.flogger.FluentLogger;
import com.google.common.net.MediaType;
import google.registry.config.RegistryConfig.Config;
import google.registry.model.domain.DomainBase;
import google.registry.model.domain.RegistryLock;
import google.registry.model.eppcommon.StatusValue;
import google.registry.model.registrar.Registrar;
import google.registry.model.registrar.RegistrarContact;
import google.registry.model.registry.RegistryLockDao;
import google.registry.model.tld.RegistryLockDao;
import google.registry.persistence.VKey;
import google.registry.request.Action;
import google.registry.request.Parameter;
import google.registry.request.Response;
import google.registry.request.auth.Auth;
import google.registry.schema.domain.RegistryLock;
import google.registry.tools.DomainLockUtils;
import google.registry.util.DateTimeUtils;
import google.registry.util.EmailMessage;
@@ -203,11 +203,11 @@ public class RelockDomainAction implements Runnable {
"Domain %s has a pending transfer.",
domainName);
checkArgument(
domain.getCurrentSponsorClientId().equals(oldLock.getRegistrarId()),
domain.getCurrentSponsorRegistrarId().equals(oldLock.getRegistrarId()),
"Domain %s has been transferred from registrar %s to registrar %s since the unlock.",
domainName,
oldLock.getRegistrarId(),
domain.getCurrentSponsorClientId());
domain.getCurrentSponsorRegistrarId());
}
private void handleNonRetryableFailure(RegistryLock oldLock, Throwable t) {
@@ -293,7 +293,7 @@ public class RelockDomainAction implements Runnable {
private ImmutableSet<InternetAddress> getEmailRecipients(String registrarId) {
Registrar registrar =
Registrar.loadByClientIdCached(registrarId)
Registrar.loadByRegistrarIdCached(registrarId)
.orElseThrow(
() ->
new IllegalStateException(String.format("Unknown registrar %s", registrarId)));
@@ -313,7 +313,7 @@ public class RelockDomainAction implements Runnable {
builder.add(new InternetAddress(registryLockEmailAddress));
} catch (AddressException e) {
// This shouldn't stop any other emails going out, so swallow it
logger.atWarning().log("Invalid email address %s", registryLockEmailAddress);
logger.atWarning().log("Invalid email address '%s'.", registryLockEmailAddress);
}
}
return builder.build();

View File

@@ -0,0 +1,347 @@
// Copyright 2021 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.batch;
import static com.google.common.collect.ImmutableList.toImmutableList;
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
import static google.registry.util.PreconditionsUtils.checkArgumentNotNull;
import static org.apache.http.HttpStatus.SC_INTERNAL_SERVER_ERROR;
import static org.apache.http.HttpStatus.SC_OK;
import static org.joda.time.DateTimeZone.UTC;
import com.google.auto.value.AutoValue;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSortedSet;
import com.google.common.collect.Streams;
import com.google.common.flogger.FluentLogger;
import com.google.common.net.MediaType;
import google.registry.config.RegistryConfig.Config;
import google.registry.flows.certs.CertificateChecker;
import google.registry.model.registrar.Registrar;
import google.registry.model.registrar.RegistrarContact;
import google.registry.model.registrar.RegistrarContact.Type;
import google.registry.request.Action;
import google.registry.request.Response;
import google.registry.request.auth.Auth;
import google.registry.util.EmailMessage;
import google.registry.util.SendEmailService;
import java.util.Date;
import java.util.Optional;
import javax.inject.Inject;
import javax.mail.internet.AddressException;
import javax.mail.internet.InternetAddress;
import org.joda.time.DateTime;
import org.joda.time.Duration;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;
/** An action that sends notification emails to registrars whose certificates are expiring soon. */
@Action(
service = Action.Service.BACKEND,
path = SendExpiringCertificateNotificationEmailAction.PATH,
auth = Auth.AUTH_INTERNAL_OR_ADMIN)
public class SendExpiringCertificateNotificationEmailAction implements Runnable {
public static final String PATH = "/_dr/task/sendExpiringCertificateNotificationEmail";
/**
* Used as an offset when storing the last notification email sent date.
*
* <p>This is used to handle edges cases when the update happens in between the day switch. For
* instance,if the job starts at 2:00 am every day and it finishes at 2:03 of the same day, then
* next day at 2am, the date difference will be less than a day, which will lead to the date
* difference between two successive email sent date being the expected email interval days + 1;
*/
protected static final Duration UPDATE_TIME_OFFSET = Duration.standardMinutes(10);
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormat.forPattern("yyyy-MM-dd");
private final CertificateChecker certificateChecker;
private final String expirationWarningEmailBodyText;
private final SendEmailService sendEmailService;
private final String expirationWarningEmailSubjectText;
private final InternetAddress gSuiteOutgoingEmailAddress;
private final Response response;
@Inject
public SendExpiringCertificateNotificationEmailAction(
@Config("expirationWarningEmailBodyText") String expirationWarningEmailBodyText,
@Config("expirationWarningEmailSubjectText") String expirationWarningEmailSubjectText,
@Config("gSuiteOutgoingEmailAddress") InternetAddress gSuiteOutgoingEmailAddress,
SendEmailService sendEmailService,
CertificateChecker certificateChecker,
Response response) {
this.certificateChecker = certificateChecker;
this.expirationWarningEmailSubjectText = expirationWarningEmailSubjectText;
this.sendEmailService = sendEmailService;
this.gSuiteOutgoingEmailAddress = gSuiteOutgoingEmailAddress;
this.expirationWarningEmailBodyText = expirationWarningEmailBodyText;
this.response = response;
}
@Override
public void run() {
response.setContentType(MediaType.PLAIN_TEXT_UTF_8);
try {
int numEmailsSent = sendNotificationEmails();
String message =
String.format(
"Done. Sent %d expiring certificate notification emails in total.", numEmailsSent);
logger.atInfo().log(message);
response.setStatus(SC_OK);
response.setPayload(message);
} catch (Exception e) {
logger.atWarning().withCause(e).log(
"Exception thrown when sending expiring certificate notification emails.");
response.setStatus(SC_INTERNAL_SERVER_ERROR);
response.setPayload(String.format("Exception thrown with cause: %s", e));
}
}
/**
* Returns a list of registrars that should receive expiring notification emails. There are two
* certificates that should be considered (the main certificate and failOver certificate). The
* registrars should receive notifications if one of the certificate checks returns true.
*/
@VisibleForTesting
ImmutableList<RegistrarInfo> getRegistrarsWithExpiringCertificates() {
logger.atInfo().log(
"Getting a list of registrars that should receive expiring notification emails.");
return Streams.stream(Registrar.loadAllCached())
.map(
registrar ->
RegistrarInfo.create(
registrar,
registrar.getClientCertificate().isPresent()
&& certificateChecker.shouldReceiveExpiringNotification(
registrar.getLastExpiringCertNotificationSentDate(),
registrar.getClientCertificate().get()),
registrar.getFailoverClientCertificate().isPresent()
&& certificateChecker.shouldReceiveExpiringNotification(
registrar.getLastExpiringFailoverCertNotificationSentDate(),
registrar.getFailoverClientCertificate().get())))
.filter(
registrarInfo ->
registrarInfo.isCertExpiring() || registrarInfo.isFailOverCertExpiring())
.collect(toImmutableList());
}
/**
* Sends a notification email to the registrar regarding the expiring certificate and returns true
* if it's sent successfully.
*/
@VisibleForTesting
boolean sendNotificationEmail(
Registrar registrar,
DateTime lastExpiringCertNotificationSentDate,
CertificateType certificateType,
Optional<String> certificate) {
if (!certificate.isPresent()
|| !certificateChecker.shouldReceiveExpiringNotification(
lastExpiringCertNotificationSentDate, certificate.get())) {
return false;
}
try {
ImmutableSet<InternetAddress> recipients = getEmailAddresses(registrar, Type.TECH);
Date expirationDate = certificateChecker.getCertificate(certificate.get()).getNotAfter();
logger.atInfo().log(
"Registrar %s should receive an email that its %s SSL certificate will expire on %s.",
registrar.getRegistrarName(),
certificateType.getDisplayName(),
expirationDate.toString());
if (recipients.isEmpty()) {
logger.atWarning().log(
"Registrar %s contains no email addresses to receive notification email.",
registrar.getRegistrarName());
return false;
}
sendEmailService.sendEmail(
EmailMessage.newBuilder()
.setFrom(gSuiteOutgoingEmailAddress)
.setSubject(expirationWarningEmailSubjectText)
.setBody(
getEmailBody(
registrar.getRegistrarName(),
certificateType,
expirationDate,
registrar.getRegistrarId()))
.setRecipients(recipients)
.setCcs(getEmailAddresses(registrar, Type.ADMIN))
.build());
/*
* A duration time offset is used here to ensure that date comparison between two
* successive dates is always greater than 1 day. This date is set as last updated date,
* for applicable certificate.
*/
updateLastNotificationSentDate(
registrar,
DateTime.now(UTC).minusMinutes((int) UPDATE_TIME_OFFSET.getStandardMinutes()),
certificateType);
return true;
} catch (Exception e) {
throw new RuntimeException(
String.format(
"Failed to send expiring certificate notification email to registrar %s.",
registrar.getRegistrarName()));
}
}
/** Updates the last notification sent date in database. */
@VisibleForTesting
void updateLastNotificationSentDate(
Registrar registrar, DateTime now, CertificateType certificateType) {
try {
tm().transact(
() -> {
Registrar.Builder newRegistrar = tm().loadByEntity(registrar).asBuilder();
switch (certificateType) {
case PRIMARY:
newRegistrar.setLastExpiringCertNotificationSentDate(now);
tm().put(newRegistrar.build());
logger.atInfo().log(
"Updated last notification email sent date to %s for %s certificate of "
+ "registrar %s.",
DATE_FORMATTER.print(now),
certificateType.getDisplayName(),
registrar.getRegistrarName());
break;
case FAILOVER:
newRegistrar.setLastExpiringFailoverCertNotificationSentDate(now);
tm().put(newRegistrar.build());
logger.atInfo().log(
"Updated last notification email sent date to %s for %s certificate of "
+ "registrar %s.",
DATE_FORMATTER.print(now),
certificateType.getDisplayName(),
registrar.getRegistrarName());
break;
default:
throw new IllegalArgumentException(
String.format(
"Unsupported certificate type: %s being passed in when updating "
+ "the last notification sent date to registrar %s.",
certificateType.toString(), registrar.getRegistrarName()));
}
});
} catch (Exception e) {
throw new RuntimeException(
String.format(
"Failed to update the last notification sent date to Registrar %s for the %s "
+ "certificate.",
registrar.getRegistrarName(), certificateType.getDisplayName()));
}
}
/** Sends notification emails to registrars with expiring certificates. */
@VisibleForTesting
int sendNotificationEmails() {
int emailsSent = 0;
for (RegistrarInfo registrarInfo : getRegistrarsWithExpiringCertificates()) {
Registrar registrar = registrarInfo.registrar();
if (registrarInfo.isCertExpiring()) {
sendNotificationEmail(
registrar,
registrar.getLastExpiringCertNotificationSentDate(),
CertificateType.PRIMARY,
registrar.getClientCertificate());
emailsSent++;
}
if (registrarInfo.isFailOverCertExpiring()) {
sendNotificationEmail(
registrar,
registrar.getLastExpiringFailoverCertNotificationSentDate(),
CertificateType.FAILOVER,
registrar.getFailoverClientCertificate());
emailsSent++;
}
}
return emailsSent;
}
/** Returns a list of email addresses of the registrar that should receive a notification email */
@VisibleForTesting
ImmutableSet<InternetAddress> getEmailAddresses(Registrar registrar, Type contactType) {
ImmutableSortedSet<RegistrarContact> contacts = registrar.getContactsOfType(contactType);
ImmutableSet.Builder<InternetAddress> recipientEmails = new ImmutableSet.Builder<>();
for (RegistrarContact contact : contacts) {
try {
recipientEmails.add(new InternetAddress(contact.getEmailAddress()));
} catch (AddressException e) {
logger.atWarning().withCause(e).log(
"Registrar Contact email address %s of Registrar %s is invalid; skipping.",
contact.getEmailAddress(), registrar.getRegistrarName());
}
}
return recipientEmails.build();
}
/**
* Generates email content by taking registrar name, certificate type and expiration date as
* parameters.
*/
@VisibleForTesting
@SuppressWarnings("lgtm[java/dereferenced-value-may-be-null]")
String getEmailBody(
String registrarName, CertificateType type, Date expirationDate, String registrarId) {
checkArgumentNotNull(expirationDate, "Expiration date cannot be null");
checkArgumentNotNull(type, "Certificate type cannot be null");
checkArgumentNotNull(registrarId, "Registrar Id cannot be null");
return String.format(
expirationWarningEmailBodyText,
registrarName,
type.getDisplayName(),
DATE_FORMATTER.print(new DateTime(expirationDate)),
registrarId);
}
/**
* Certificate types for X509Certificate.
*
* <p><b>Note:</b> These types are only used to indicate the type of expiring certificate in
* notification emails.
*/
protected enum CertificateType {
PRIMARY("primary"),
FAILOVER("fail-over");
private final String displayName;
CertificateType(String displayName) {
this.displayName = displayName;
}
public String getDisplayName() {
return displayName;
}
}
@AutoValue
public abstract static class RegistrarInfo {
static RegistrarInfo create(
Registrar registrar, boolean isCertExpiring, boolean isFailOverCertExpiring) {
return new AutoValue_SendExpiringCertificateNotificationEmailAction_RegistrarInfo(
registrar, isCertExpiring, isFailOverCertExpiring);
}
public abstract Registrar registrar();
public abstract boolean isCertExpiring();
public abstract boolean isFailOverCertExpiring();
}
}

View File

@@ -22,7 +22,7 @@ import static javax.servlet.http.HttpServletResponse.SC_OK;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.flogger.FluentLogger;
import google.registry.config.RegistryConfig.Config;
import google.registry.config.RegistryEnvironment;
import google.registry.persistence.PersistenceModule.SchemaManagerConnection;
import google.registry.request.Action;
import google.registry.request.Response;
@@ -48,22 +48,18 @@ import javax.inject.Inject;
public class WipeOutCloudSqlAction implements Runnable {
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
// As a short-lived class, hardcode allowed projects here instead of using config files.
private static final ImmutableSet<String> ALLOWED_PROJECTS =
ImmutableSet.of("domain-registry-qa");
private static final ImmutableSet<RegistryEnvironment> FORBIDDEN_ENVIRONMENTS =
ImmutableSet.of(RegistryEnvironment.PRODUCTION, RegistryEnvironment.SANDBOX);
private final String projectId;
private final Supplier<Connection> connectionSupplier;
private final Response response;
private final Retrier retrier;
@Inject
WipeOutCloudSqlAction(
@Config("projectId") String projectId,
@SchemaManagerConnection Supplier<Connection> connectionSupplier,
Response response,
Retrier retrier) {
this.projectId = projectId;
this.connectionSupplier = connectionSupplier;
this.response = response;
this.retrier = retrier;
@@ -73,9 +69,9 @@ public class WipeOutCloudSqlAction implements Runnable {
public void run() {
response.setContentType(PLAIN_TEXT_UTF_8);
if (!ALLOWED_PROJECTS.contains(projectId)) {
if (FORBIDDEN_ENVIRONMENTS.contains(RegistryEnvironment.get())) {
response.setStatus(SC_FORBIDDEN);
response.setPayload("Wipeout is not allowed in " + projectId);
response.setPayload("Wipeout is not allowed in " + RegistryEnvironment.get());
return;
}
@@ -90,11 +86,11 @@ public class WipeOutCloudSqlAction implements Runnable {
},
e -> !(e instanceof SQLException));
response.setStatus(SC_OK);
response.setPayload("Wiped out Cloud SQL in " + projectId);
response.setPayload("Wiped out Cloud SQL in " + RegistryEnvironment.get());
} catch (RuntimeException e) {
logger.atSevere().withCause(e).log("Failed to wipe out Cloud SQL data.");
response.setStatus(SC_INTERNAL_SERVER_ERROR);
response.setPayload("Failed to wipe out Cloud SQL in " + projectId);
response.setPayload("Failed to wipe out Cloud SQL in " + RegistryEnvironment.get());
}
}

View File

@@ -0,0 +1,138 @@
// Copyright 2021 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.batch;
import static google.registry.persistence.transaction.TransactionManagerFactory.jpaTm;
import static org.apache.http.HttpStatus.SC_INTERNAL_SERVER_ERROR;
import static org.apache.http.HttpStatus.SC_OK;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.flogger.FluentLogger;
import com.google.common.net.MediaType;
import google.registry.config.RegistryConfig.Config;
import google.registry.model.contact.ContactHistory;
import google.registry.request.Action;
import google.registry.request.Action.Service;
import google.registry.request.Response;
import google.registry.request.auth.Auth;
import google.registry.util.Clock;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Stream;
import javax.inject.Inject;
import org.joda.time.DateTime;
/**
* An action that wipes out Personal Identifiable Information (PII) fields of {@link ContactHistory}
* entities.
*
* <p>ContactHistory entities should be retained in the database for only certain amount of time.
* This periodic wipe out action only applies to SQL.
*/
@Action(
service = Service.BACKEND,
path = WipeOutContactHistoryPiiAction.PATH,
auth = Auth.AUTH_INTERNAL_OR_ADMIN)
public class WipeOutContactHistoryPiiAction implements Runnable {
public static final String PATH = "/_dr/task/wipeOutContactHistoryPii";
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
private final Clock clock;
private final Response response;
private final int minMonthsBeforeWipeOut;
private final int wipeOutQueryBatchSize;
@Inject
public WipeOutContactHistoryPiiAction(
Clock clock,
@Config("minMonthsBeforeWipeOut") int minMonthsBeforeWipeOut,
@Config("wipeOutQueryBatchSize") int wipeOutQueryBatchSize,
Response response) {
this.clock = clock;
this.response = response;
this.minMonthsBeforeWipeOut = minMonthsBeforeWipeOut;
this.wipeOutQueryBatchSize = wipeOutQueryBatchSize;
}
@Override
public void run() {
response.setContentType(MediaType.PLAIN_TEXT_UTF_8);
try {
int totalNumOfWipedEntities = 0;
DateTime wipeOutTime = clock.nowUtc().minusMonths(minMonthsBeforeWipeOut);
logger.atInfo().log(
"About to wipe out all PII of contact history entities prior to %s.", wipeOutTime);
int numOfWipedEntities = 0;
do {
numOfWipedEntities =
jpaTm()
.transact(
() ->
wipeOutContactHistoryData(
getNextContactHistoryEntitiesWithPiiBatch(wipeOutTime)));
totalNumOfWipedEntities += numOfWipedEntities;
} while (numOfWipedEntities > 0);
String msg =
String.format(
"Done. Wiped out PII of %d ContactHistory entities in total.",
totalNumOfWipedEntities);
logger.atInfo().log(msg);
response.setPayload(msg);
response.setStatus(SC_OK);
} catch (Exception e) {
logger.atSevere().withCause(e).log(
"Exception thrown during the process of wiping out contact history PII.");
response.setStatus(SC_INTERNAL_SERVER_ERROR);
response.setPayload(
String.format(
"Exception thrown during the process of wiping out contact history PII with cause"
+ ": %s",
e));
}
}
/**
* Returns a stream of up to {@link #wipeOutQueryBatchSize} {@link ContactHistory} entities
* containing PII that are prior to @param wipeOutTime.
*/
@VisibleForTesting
Stream<ContactHistory> getNextContactHistoryEntitiesWithPiiBatch(DateTime wipeOutTime) {
// email is one of the required fields in EPP, meaning it's initially not null.
// Therefore, checking if it's null is one way to avoid processing contact history entities
// that have been processed previously. Refer to RFC 5733 for more information.
return jpaTm()
.query(
"FROM ContactHistory WHERE modificationTime < :wipeOutTime " + "AND email IS NOT NULL",
ContactHistory.class)
.setParameter("wipeOutTime", wipeOutTime)
.setMaxResults(wipeOutQueryBatchSize)
.getResultStream();
}
/** Wipes out the PII of each of the {@link ContactHistory} entities in the stream. */
@VisibleForTesting
int wipeOutContactHistoryData(Stream<ContactHistory> contactHistoryEntities) {
AtomicInteger numOfEntities = new AtomicInteger(0);
contactHistoryEntities.forEach(
contactHistoryEntity -> {
jpaTm().update(contactHistoryEntity.asBuilder().wipeOutPii().build());
numOfEntities.incrementAndGet();
});
logger.atInfo().log(
"Wiped out all PII fields of %d ContactHistory entities.", numOfEntities.get());
return numOfEntities.get();
}
}

View File

@@ -28,6 +28,7 @@ import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.flogger.FluentLogger;
import google.registry.config.RegistryConfig.Config;
import google.registry.config.RegistryEnvironment;
import google.registry.request.Action;
import google.registry.request.Response;
import google.registry.request.auth.Auth;
@@ -49,9 +50,8 @@ public class WipeoutDatastoreAction implements Runnable {
private static final String PIPELINE_NAME = "bulk_delete_datastore_pipeline";
// As a short-lived class, hardcode allowed projects here instead of using config files.
private static final ImmutableSet<String> ALLOWED_PROJECTS =
ImmutableSet.of("domain-registry-qa");
private static final ImmutableSet<RegistryEnvironment> FORBIDDEN_ENVIRONMENTS =
ImmutableSet.of(RegistryEnvironment.PRODUCTION, RegistryEnvironment.SANDBOX);
private final String projectId;
private final String jobRegion;
@@ -80,9 +80,9 @@ public class WipeoutDatastoreAction implements Runnable {
public void run() {
response.setContentType(PLAIN_TEXT_UTF_8);
if (!ALLOWED_PROJECTS.contains(projectId)) {
if (FORBIDDEN_ENVIRONMENTS.contains(RegistryEnvironment.get())) {
response.setStatus(SC_FORBIDDEN);
response.setPayload("Wipeout is not allowed in " + projectId);
response.setPayload("Wipeout is not allowed in " + RegistryEnvironment.get());
return;
}
@@ -92,7 +92,12 @@ public class WipeoutDatastoreAction implements Runnable {
.setJobName(createJobName("bulk-delete-datastore-", clock))
.setContainerSpecGcsPath(
String.format("%s/%s_metadata.json", stagingBucketUrl, PIPELINE_NAME))
.setParameters(ImmutableMap.of("kindsToDelete", "*"));
.setParameters(
ImmutableMap.of(
"kindsToDelete",
"*",
"registryEnvironment",
RegistryEnvironment.get().name()));
LaunchFlexTemplateResponse launchResponse =
dataflow
.projects()

View File

@@ -17,7 +17,6 @@ package google.registry.beam.common;
import static com.google.common.base.Verify.verify;
import static google.registry.persistence.transaction.TransactionManagerFactory.jpaTm;
import google.registry.backup.AppEngineEnvironment;
import google.registry.model.contact.ContactResource;
import google.registry.persistence.transaction.CriteriaQueryBuilder;
import google.registry.persistence.transaction.JpaTransactionManager;
@@ -59,18 +58,16 @@ public class JpaDemoPipeline implements Serializable {
public void processElement() {
// AppEngineEnvironment is needed as long as JPA entity classes still depends
// on Objectify.
try (AppEngineEnvironment allowOfyEntity = new AppEngineEnvironment()) {
int result =
(Integer)
jpaTm()
.transact(
() ->
jpaTm()
.getEntityManager()
.createNativeQuery("select 1;")
.getSingleResult());
verify(result == 1, "Expecting 1, got %s.", result);
}
int result =
(Integer)
jpaTm()
.transact(
() ->
jpaTm()
.getEntityManager()
.createNativeQuery("select 1;")
.getSingleResult());
verify(result == 1, "Expecting 1, got %s.", result);
counter.inc();
}
}));

View File

@@ -20,9 +20,10 @@ import static org.apache.beam.sdk.values.TypeDescriptors.integers;
import com.google.auto.value.AutoValue;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Streams;
import google.registry.backup.AppEngineEnvironment;
import google.registry.beam.common.RegistryQuery.CriteriaQuerySupplier;
import google.registry.model.ofy.ObjectifyService;
import google.registry.model.UpdateAutoTimestamp;
import google.registry.model.UpdateAutoTimestamp.DisableAutoUpdateResource;
import google.registry.model.replay.SqlEntity;
import google.registry.persistence.transaction.JpaTransactionManager;
import google.registry.persistence.transaction.TransactionManagerFactory;
import java.io.Serializable;
@@ -208,14 +209,9 @@ public final class RegistryJpaIO {
@ProcessElement
public void processElement(OutputReceiver<T> outputReceiver) {
// AppEngineEnvironment is need for handling VKeys, which involve Ofy keys. Unlike
// SqlBatchWriter, it is unnecessary to initialize ObjectifyService in this class.
try (AppEngineEnvironment env = new AppEngineEnvironment()) {
// TODO(b/187210388): JpaTransactionManager should support non-transactional query.
jpaTm()
.transactNoRetry(
() -> query.stream().map(resultMapper::apply).forEach(outputReceiver::output));
}
jpaTm()
.transactNoRetry(
() -> query.stream().map(resultMapper::apply).forEach(outputReceiver::output));
}
}
}
@@ -273,6 +269,12 @@ public final class RegistryJpaIO {
public abstract SerializableFunction<T, Object> jpaConverter();
/**
* Signal to the writer that the {@link UpdateAutoTimestamp} property should be allowed to
* manipulate its value before persistence. The default value is {@code true}.
*/
abstract boolean withUpdateAutoTimestamp();
public Write<T> withName(String name) {
return toBuilder().name(name).build();
}
@@ -293,6 +295,10 @@ public final class RegistryJpaIO {
return toBuilder().jpaConverter(jpaConverter).build();
}
public Write<T> disableUpdateAutoTimestamp() {
return toBuilder().withUpdateAutoTimestamp(false).build();
}
abstract Builder<T> toBuilder();
@Override
@@ -309,7 +315,7 @@ public final class RegistryJpaIO {
GroupIntoBatches.<Integer, T>ofSize(batchSize()).withShardedKey())
.apply(
"Write in batch for " + name(),
ParDo.of(new SqlBatchWriter<>(name(), jpaConverter())));
ParDo.of(new SqlBatchWriter<>(name(), jpaConverter(), withUpdateAutoTimestamp())));
}
static <T> Builder<T> builder() {
@@ -317,7 +323,8 @@ public final class RegistryJpaIO {
.name(DEFAULT_NAME)
.batchSize(DEFAULT_BATCH_SIZE)
.shards(DEFAULT_SHARDS)
.jpaConverter(x -> x);
.jpaConverter(x -> x)
.withUpdateAutoTimestamp(true);
}
@AutoValue.Builder
@@ -331,6 +338,8 @@ public final class RegistryJpaIO {
abstract Builder<T> jpaConverter(SerializableFunction<T, Object> jpaConverter);
abstract Builder<T> withUpdateAutoTimestamp(boolean withUpdateAutoTimestamp);
abstract Write<T> build();
}
}
@@ -339,37 +348,38 @@ public final class RegistryJpaIO {
private static class SqlBatchWriter<T> extends DoFn<KV<ShardedKey<Integer>, Iterable<T>>, Void> {
private final Counter counter;
private final SerializableFunction<T, Object> jpaConverter;
private final boolean withAutoTimestamp;
SqlBatchWriter(String type, SerializableFunction<T, Object> jpaConverter) {
SqlBatchWriter(
String type, SerializableFunction<T, Object> jpaConverter, boolean withAutoTimestamp) {
counter = Metrics.counter("SQL_WRITE", type);
this.jpaConverter = jpaConverter;
}
@Setup
public void setup() {
// AppEngineEnvironment is needed as long as Objectify keys are still involved in the handling
// of SQL entities (e.g., in VKeys). ObjectifyService needs to be initialized when conversion
// between Ofy entity and Datastore entity is needed.
try (AppEngineEnvironment env = new AppEngineEnvironment()) {
ObjectifyService.initOfy();
}
this.withAutoTimestamp = withAutoTimestamp;
}
@ProcessElement
public void processElement(@Element KV<ShardedKey<Integer>, Iterable<T>> kv) {
try (AppEngineEnvironment env = new AppEngineEnvironment()) {
ImmutableList<Object> ofyEntities =
Streams.stream(kv.getValue())
.map(this.jpaConverter::apply)
// TODO(b/177340730): post migration delete the line below.
.filter(Objects::nonNull)
.collect(ImmutableList.toImmutableList());
try {
jpaTm().transact(() -> jpaTm().putAll(ofyEntities));
counter.inc(ofyEntities.size());
} catch (RuntimeException e) {
processSingly(ofyEntities);
}
if (withAutoTimestamp) {
actuallyProcessElement(kv);
return;
}
try (DisableAutoUpdateResource disable = UpdateAutoTimestamp.disableAutoUpdate()) {
actuallyProcessElement(kv);
}
}
private void actuallyProcessElement(@Element KV<ShardedKey<Integer>, Iterable<T>> kv) {
ImmutableList<Object> entities =
Streams.stream(kv.getValue())
.map(this.jpaConverter::apply)
// TODO(b/177340730): post migration delete the line below.
.filter(Objects::nonNull)
.collect(ImmutableList.toImmutableList());
try {
jpaTm().transact(() -> jpaTm().putAll(entities));
counter.inc(entities.size());
} catch (RuntimeException e) {
processSingly(entities);
}
}
@@ -377,19 +387,22 @@ public final class RegistryJpaIO {
* Writes entities in a failed batch one by one to identify the first bad entity and throws a
* {@link RuntimeException} on it.
*/
private void processSingly(ImmutableList<Object> ofyEntities) {
for (Object ofyEntity : ofyEntities) {
private void processSingly(ImmutableList<Object> entities) {
for (Object entity : entities) {
try {
jpaTm().transact(() -> jpaTm().put(ofyEntity));
jpaTm().transact(() -> jpaTm().put(entity));
counter.inc();
} catch (RuntimeException e) {
throw new RuntimeException(toOfyKey(ofyEntity).toString(), e);
throw new RuntimeException(toEntityKeyString(entity), e);
}
}
}
private com.googlecode.objectify.Key<?> toOfyKey(Object ofyEntity) {
return com.googlecode.objectify.Key.create(ofyEntity);
private String toEntityKeyString(Object entity) {
if (entity instanceof SqlEntity) {
return ((SqlEntity) entity).getPrimaryKeyString();
}
return "Non-SqlEntity: " + String.valueOf(entity);
}
}
}

View File

@@ -34,7 +34,6 @@ import org.apache.beam.sdk.options.Description;
public interface RegistryPipelineOptions extends GcpOptions {
@Description("The Registry environment.")
@Nullable
RegistryEnvironment getRegistryEnvironment();
void setRegistryEnvironment(RegistryEnvironment environment);

View File

@@ -20,6 +20,8 @@ import com.google.auto.service.AutoService;
import com.google.common.flogger.FluentLogger;
import dagger.Lazy;
import google.registry.config.RegistryEnvironment;
import google.registry.config.SystemPropertySetter;
import google.registry.model.AppEngineEnvironment;
import google.registry.persistence.transaction.JpaTransactionManager;
import google.registry.persistence.transaction.TransactionManagerFactory;
import org.apache.beam.sdk.harness.JvmInitializer;
@@ -35,18 +37,26 @@ import org.apache.beam.sdk.options.PipelineOptions;
@AutoService(JvmInitializer.class)
public class RegistryPipelineWorkerInitializer implements JvmInitializer {
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
public static final String PROPERTY = "google.registry.beam";
@Override
public void beforeProcessing(PipelineOptions options) {
RegistryPipelineOptions registryOptions = options.as(RegistryPipelineOptions.class);
RegistryEnvironment environment = registryOptions.getRegistryEnvironment();
if (environment == null || environment.equals(RegistryEnvironment.UNITTEST)) {
return;
throw new RuntimeException(
"A registry environment must be specified in the pipeline options.");
}
logger.atInfo().log("Setting up RegistryEnvironment: %s", environment);
logger.atInfo().log("Setting up RegistryEnvironment %s.", environment);
environment.setup();
Lazy<JpaTransactionManager> transactionManagerLazy =
toRegistryPipelineComponent(registryOptions).getJpaTransactionManager();
TransactionManagerFactory.setJpaTmOnBeamWorker(transactionManagerLazy::get);
// Masquarade all threads as App Engine threads so we can create Ofy keys in the pipeline. Also
// loads all ofy entities.
new AppEngineEnvironment("Beam").setEnvironmentForAllThreads();
// Set the system property so that we can call IdService.allocateId() without access to
// datastore.
SystemPropertySetter.PRODUCTION_IMPL.setProperty(PROPERTY, "true");
}
}

View File

@@ -16,6 +16,7 @@ package google.registry.beam.common;
import static google.registry.persistence.transaction.TransactionManagerFactory.jpaTm;
import google.registry.persistence.transaction.JpaTransactionManager;
import java.io.Serializable;
import java.util.Map;
import java.util.function.Supplier;
@@ -28,6 +29,15 @@ import javax.persistence.criteria.CriteriaQuery;
/** Interface for query instances used by {@link RegistryJpaIO.Read}. */
public interface RegistryQuery<T> extends Serializable {
/**
* Number of JPA entities to fetch in each batch during a query.
*
* <p>With Hibernate, for result streaming to work, a query's fetchSize property must be set to a
* non-zero value.
*/
int QUERY_FETCH_SIZE = 1000;
Stream<T> stream();
interface CriteriaQuerySupplier<T> extends Supplier<CriteriaQuery<T>>, Serializable {}
@@ -49,6 +59,7 @@ public interface RegistryQuery<T> extends Serializable {
if (parameters != null) {
parameters.forEach(query::setParameter);
}
JpaTransactionManager.setQueryFetchSize(query, QUERY_FETCH_SIZE);
@SuppressWarnings("unchecked")
Stream<T> resultStream = query.getResultStream();
return nativeQuery ? resultStream : resultStream.map(e -> detach(entityManager, e));
@@ -64,11 +75,14 @@ public interface RegistryQuery<T> extends Serializable {
static <T> RegistryQuery<T> createQuery(
String jpql, @Nullable Map<String, Object> parameters, Class<T> clazz) {
return () -> {
TypedQuery<T> query = jpaTm().query(jpql, clazz);
// TODO(b/193662898): switch to jpaTm().query() when it can properly detach loaded entities.
EntityManager entityManager = jpaTm().getEntityManager();
TypedQuery<T> query = entityManager.createQuery(jpql, clazz);
if (parameters != null) {
parameters.forEach(query::setParameter);
}
return query.getResultStream();
JpaTransactionManager.setQueryFetchSize(query, QUERY_FETCH_SIZE);
return query.getResultStream().map(e -> detach(entityManager, e));
};
}
@@ -82,7 +96,13 @@ public interface RegistryQuery<T> extends Serializable {
* @param <T> Type of each row in the result set.
*/
static <T> RegistryQuery<T> createQuery(CriteriaQuerySupplier<T> criteriaQuery) {
return () -> jpaTm().query(criteriaQuery.get()).getResultStream();
return () -> {
// TODO(b/193662898): switch to jpaTm().query() when it can properly detach loaded entities.
EntityManager entityManager = jpaTm().getEntityManager();
TypedQuery<T> query = entityManager.createQuery(criteriaQuery.get());
JpaTransactionManager.setQueryFetchSize(query, QUERY_FETCH_SIZE);
return query.getResultStream().map(e -> detach(entityManager, e));
};
}
/**
@@ -108,7 +128,10 @@ public interface RegistryQuery<T> extends Serializable {
return;
}
try {
entityManager.detach(object);
// TODO(b/193662898): choose detach() or clear() based on the type of transaction.
// For context, EntityManager.detach() does not remove all metadata about loaded entities.
// See b/193925312 or https://hibernate.atlassian.net/browse/HHH-14735 for details.
entityManager.clear();
} catch (IllegalArgumentException e) {
// Not an entity. Do nothing.
}

View File

@@ -25,6 +25,7 @@ import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSortedSet;
import com.google.common.flogger.FluentLogger;
import com.google.datastore.v1.Entity;
import google.registry.config.RegistryEnvironment;
import java.util.Iterator;
import java.util.Map;
import org.apache.beam.sdk.Pipeline;
@@ -308,6 +309,11 @@ public class BulkDeleteDatastorePipeline {
public interface BulkDeletePipelineOptions extends GcpOptions {
@Description("The Registry environment.")
RegistryEnvironment getRegistryEnvironment();
void setRegistryEnvironment(RegistryEnvironment environment);
@Description(
"The Datastore KINDs to be deleted. The format may be:\n"
+ "\t- The list of kinds to be deleted as a comma-separated string, or\n"

View File

@@ -169,14 +169,15 @@ public class DatastoreV1 {
int numSplits;
try {
long estimatedSizeBytes = getEstimatedSizeBytes(datastore, query, namespace);
logger.atInfo().log("Estimated size bytes for the query is: %s", estimatedSizeBytes);
logger.atInfo().log("Estimated size for the query is %d bytes.", estimatedSizeBytes);
numSplits =
(int)
Math.min(
NUM_QUERY_SPLITS_MAX,
Math.round(((double) estimatedSizeBytes) / DEFAULT_BUNDLE_SIZE_BYTES));
} catch (Exception e) {
logger.atWarning().log("Failed the fetch estimatedSizeBytes for query: %s", query, e);
logger.atWarning().withCause(e).log(
"Failed the fetch estimatedSizeBytes for query: %s", query);
// Fallback in case estimated size is unavailable.
numSplits = NUM_QUERY_SPLITS_MIN;
}
@@ -215,7 +216,7 @@ public class DatastoreV1 {
private static Entity getLatestTableStats(
String ourKind, @Nullable String namespace, Datastore datastore) throws DatastoreException {
long latestTimestamp = queryLatestStatisticsTimestamp(datastore, namespace);
logger.atInfo().log("Latest stats timestamp for kind %s is %s", ourKind, latestTimestamp);
logger.atInfo().log("Latest stats timestamp for kind %s is %s.", ourKind, latestTimestamp);
Query.Builder queryBuilder = Query.newBuilder();
if (Strings.isNullOrEmpty(namespace)) {
@@ -234,7 +235,7 @@ public class DatastoreV1 {
long now = System.currentTimeMillis();
RunQueryResponse response = datastore.runQuery(request);
logger.atFine().log(
"Query for per-kind statistics took %sms", System.currentTimeMillis() - now);
"Query for per-kind statistics took %d ms.", System.currentTimeMillis() - now);
QueryResultBatch batch = response.getBatch();
if (batch.getEntityResultsCount() == 0) {
@@ -330,7 +331,7 @@ public class DatastoreV1 {
logger.atWarning().log(
"Failed to translate Gql query '%s': %s", gqlQueryWithZeroLimit, e.getMessage());
logger.atWarning().log(
"User query might have a limit already set, so trying without zero limit");
"User query might have a limit already set, so trying without zero limit.");
// Retry without the zero limit.
return translateGqlQuery(gql, datastore, namespace);
} else {
@@ -514,10 +515,10 @@ public class DatastoreV1 {
@ProcessElement
public void processElement(ProcessContext c) throws Exception {
String gqlQuery = c.element();
logger.atInfo().log("User query: '%s'", gqlQuery);
logger.atInfo().log("User query: '%s'.", gqlQuery);
Query query =
translateGqlQueryWithLimitCheck(gqlQuery, datastore, v1Options.getNamespace());
logger.atInfo().log("User gql query translated to Query(%s)", query);
logger.atInfo().log("User gql query translated to Query(%s).", query);
c.output(query);
}
}
@@ -573,7 +574,7 @@ public class DatastoreV1 {
estimatedNumSplits = numSplits;
}
logger.atInfo().log("Splitting the query into %s splits", estimatedNumSplits);
logger.atInfo().log("Splitting the query into %d splits.", estimatedNumSplits);
List<Query> querySplits;
try {
querySplits =
@@ -647,7 +648,7 @@ public class DatastoreV1 {
throw exception;
}
if (!BackOffUtils.next(sleeper, backoff)) {
logger.atSevere().log("Aborting after %s retries.", MAX_RETRIES);
logger.atSevere().log("Aborting after %d retries.", MAX_RETRIES);
throw exception;
}
}

View File

@@ -1,50 +0,0 @@
// 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 static com.google.common.base.Preconditions.checkArgument;
import com.google.api.services.cloudkms.v1.model.DecryptRequest;
import com.google.common.base.Strings;
import google.registry.config.RegistryConfig.Config;
import google.registry.keyring.kms.KmsConnection;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import javax.inject.Inject;
/**
* Decrypts data using Cloud KMS, with the same crypto key with which Cloud SQL credential files on
* GCS was encrypted. See {@link BackupPaths#getCloudSQLCredentialFilePatterns} for more
* information.
*/
public class CloudSqlCredentialDecryptor {
private static final String CRYPTO_KEY_NAME = "nomulus-tool-key";
private final KmsConnection kmsConnection;
@Inject
CloudSqlCredentialDecryptor(@Config("beamKmsConnection") KmsConnection kmsConnection) {
this.kmsConnection = kmsConnection;
}
public String decrypt(String data) {
checkArgument(!Strings.isNullOrEmpty(data), "Null or empty data.");
byte[] ciphertext = Base64.getDecoder().decode(data);
// Re-encode for Cloud KMS JSON REST API, invoked through kmsConnection.
String urlSafeCipherText = new DecryptRequest().encodeCiphertext(ciphertext).getCiphertext();
return new String(
kmsConnection.decrypt(CRYPTO_KEY_NAME, urlSafeCipherText), StandardCharsets.UTF_8);
}
}

View File

@@ -20,11 +20,11 @@ import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.googlecode.objectify.Key;
import google.registry.backup.AppEngineEnvironment;
import google.registry.backup.VersionedEntity;
import google.registry.beam.common.RegistryJpaIO;
import google.registry.beam.initsql.Transforms.RemoveDomainBaseForeignKeys;
import google.registry.model.billing.BillingEvent;
import google.registry.model.common.Cursor;
import google.registry.model.contact.ContactResource;
import google.registry.model.domain.DomainBase;
import google.registry.model.domain.token.AllocationToken;
@@ -32,8 +32,8 @@ import google.registry.model.host.HostResource;
import google.registry.model.poll.PollMessage;
import google.registry.model.registrar.Registrar;
import google.registry.model.registrar.RegistrarContact;
import google.registry.model.registry.Registry;
import google.registry.model.reporting.HistoryEntry;
import google.registry.model.tld.Registry;
import google.registry.persistence.PersistenceModule.TransactionIsolationLevel;
import java.io.Serializable;
import java.util.Collection;
@@ -62,6 +62,7 @@ import org.joda.time.DateTime;
* <ol>
* <li>{@link Registry}: Assumes that {@code PremiumList} and {@code ReservedList} have been set
* up in the SQL database.
* <li>{@link Cursor}: Logically can depend on {@code Registry}, but without foreign key.
* <li>{@link Registrar}: Logically depends on {@code Registry}, Foreign key not modeled yet.
* <li>{@link ContactResource}: references {@code Registrar}
* <li>{@link RegistrarContact}: references {@code Registrar}.
@@ -101,7 +102,11 @@ public class InitSqlPipeline implements Serializable {
*/
private static final ImmutableList<Class<?>> PHASE_ONE_ORDERED =
ImmutableList.of(
Registry.class, Registrar.class, ContactResource.class, RegistrarContact.class);
Registry.class,
Cursor.class,
Registrar.class,
ContactResource.class,
RegistrarContact.class);
/**
* Datastore kinds to be written to the SQL database after the cleansed version of {@link
@@ -219,13 +224,12 @@ public class InitSqlPipeline implements Serializable {
.withName(transformId)
.withBatchSize(options.getSqlWriteBatchSize())
.withShards(options.getSqlWriteShards())
.withJpaConverter(Transforms::convertVersionedEntityToSqlEntity));
.withJpaConverter(Transforms::convertVersionedEntityToSqlEntity)
.disableUpdateAutoTimestamp());
}
private static ImmutableList<String> toKindStrings(Collection<Class<?>> entityClasses) {
try (AppEngineEnvironment env = new AppEngineEnvironment()) {
return entityClasses.stream().map(Key::getKind).collect(ImmutableList.toImmutableList());
}
return entityClasses.stream().map(Key::getKind).collect(ImmutableList.toImmutableList());
}
public static void main(String[] args) {

View File

@@ -1,45 +0,0 @@
// 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 com.google.auto.value.AutoValue;
import java.util.Optional;
/**
* Information needed to connect to a database, including JDBC URL, user name, password, and in the
* case of Cloud SQL, the database instance's name.
*/
@AutoValue
abstract class SqlAccessInfo {
abstract String jdbcUrl();
abstract String user();
abstract String password();
abstract Optional<String> cloudSqlInstanceName();
public static SqlAccessInfo createCloudSqlAccessInfo(
String sqlInstanceName, String username, String password) {
return new AutoValue_SqlAccessInfo(
"jdbc:postgresql://google/postgres", username, password, Optional.of(sqlInstanceName));
}
public static SqlAccessInfo createLocalSqlAccessInfo(
String jdbcUrl, String username, String password) {
return new AutoValue_SqlAccessInfo(jdbcUrl, username, password, Optional.empty());
}
}

View File

@@ -22,6 +22,7 @@ import static google.registry.beam.initsql.BackupPaths.getExportFilePatterns;
import static google.registry.model.ofy.ObjectifyService.auditedOfy;
import static google.registry.util.DateTimeUtils.START_OF_TIME;
import static google.registry.util.DateTimeUtils.isBeforeOrAt;
import static google.registry.util.DomainNameUtils.canonicalizeDomainName;
import static java.util.Comparator.comparing;
import static org.apache.beam.sdk.values.TypeDescriptors.kvs;
import static org.apache.beam.sdk.values.TypeDescriptors.strings;
@@ -39,9 +40,9 @@ import google.registry.backup.VersionedEntity;
import google.registry.model.billing.BillingEvent.Flag;
import google.registry.model.billing.BillingEvent.Reason;
import google.registry.model.domain.DomainBase;
import google.registry.model.replay.DatastoreAndSqlEntity;
import google.registry.model.replay.SqlEntity;
import google.registry.model.reporting.HistoryEntry;
import google.registry.schema.replay.DatastoreAndSqlEntity;
import google.registry.schema.replay.SqlEntity;
import google.registry.tools.LevelDbLogReader;
import java.io.Serializable;
import java.util.Collection;
@@ -277,7 +278,7 @@ public final class Transforms {
// Prober contacts referencing phantom registrars. They and their associated history entries can
// be safely ignored.
private static final ImmutableSet IGNORED_CONTACTS =
private static final ImmutableSet<String> IGNORED_CONTACTS =
ImmutableSet.of(
"1_WJ0TEST-GOOGLE", "1_WJ1TEST-GOOGLE", "1_WJ2TEST-GOOGLE", "1_WJ3TEST-GOOGLE");
@@ -320,7 +321,8 @@ public final class Transforms {
return true;
}
private static Entity repairBadData(Entity entity) {
@VisibleForTesting
static Entity repairBadData(Entity entity) {
if (entity.getKind().equals("Cancellation")
&& Objects.equals(entity.getProperty("reason"), "AUTO_RENEW")) {
// AUTO_RENEW has been moved from 'reason' to flags. Change reason to RENEW and add the
@@ -328,6 +330,15 @@ public final class Transforms {
// instead of append. See b/185954992.
entity.setUnindexedProperty("reason", Reason.RENEW.name());
entity.setUnindexedProperty("flags", ImmutableList.of(Flag.AUTO_RENEW.name()));
} else if (entity.getKind().equals("DomainBase")) {
// Canonicalize old domain/host names from 2016 and earlier before we were enforcing this.
entity.setIndexedProperty(
"fullyQualifiedDomainName",
canonicalizeDomainName((String) entity.getProperty("fullyQualifiedDomainName")));
} else if (entity.getKind().equals("HostResource")) {
entity.setIndexedProperty(
"fullyQualifiedHostName",
canonicalizeDomainName((String) entity.getProperty("fullyQualifiedHostName")));
}
return entity;
}
@@ -365,7 +376,8 @@ public final class Transforms {
* Returns a {@link PTransform} that produces a {@link PCollection} containing all elements in the
* given {@link Iterable}.
*/
static PTransform<PBegin, PCollection<String>> toStringPCollection(Iterable<String> strings) {
private static PTransform<PBegin, PCollection<String>> toStringPCollection(
Iterable<String> strings) {
return Create.of(strings).withCoder(StringUtf8Coder.of());
}
@@ -373,7 +385,7 @@ public final class Transforms {
* Returns a {@link PTransform} from file {@link Metadata} to {@link VersionedEntity} using
* caller-provided {@code transformer}.
*/
static PTransform<PCollection<Metadata>, PCollection<VersionedEntity>> processFiles(
private static PTransform<PCollection<Metadata>, PCollection<VersionedEntity>> processFiles(
DoFn<ReadableFile, VersionedEntity> transformer) {
return new PTransform<PCollection<Metadata>, PCollection<VersionedEntity>>() {
@Override
@@ -389,7 +401,7 @@ public final class Transforms {
private final DateTime fromTime;
private final DateTime toTime;
public FilterCommitLogFileByTime(DateTime fromTime, DateTime toTime) {
FilterCommitLogFileByTime(DateTime fromTime, DateTime toTime) {
checkNotNull(fromTime, "fromTime");
checkNotNull(toTime, "toTime");
checkArgument(

View File

@@ -14,18 +14,29 @@
package google.registry.beam.invoicing;
import static com.google.common.collect.ImmutableSet.toImmutableSet;
import static google.registry.beam.BeamUtils.getQueryFromFile;
import static org.apache.beam.sdk.values.TypeDescriptors.strings;
import google.registry.beam.common.RegistryJpaIO;
import google.registry.beam.common.RegistryJpaIO.Read;
import google.registry.beam.invoicing.BillingEvent.InvoiceGroupingKey;
import google.registry.beam.invoicing.BillingEvent.InvoiceGroupingKey.InvoiceGroupingKeyCoder;
import google.registry.model.billing.BillingEvent.Flag;
import google.registry.model.registrar.Registrar;
import google.registry.persistence.PersistenceModule.TransactionIsolationLevel;
import google.registry.reporting.billing.BillingModule;
import google.registry.util.DateTimeUtils;
import google.registry.util.DomainNameUtils;
import google.registry.util.SqlTemplate;
import java.io.Serializable;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.YearMonth;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.Optional;
import java.util.regex.Pattern;
import org.apache.beam.sdk.Pipeline;
import org.apache.beam.sdk.PipelineResult;
import org.apache.beam.sdk.coders.SerializableCoder;
@@ -58,6 +69,9 @@ public class InvoicingPipeline implements Serializable {
private static final DateTimeFormatter TIMESTAMP_FORMATTER =
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSSSSS");
private static final Pattern SQL_COMMENT_REGEX =
Pattern.compile("^\\s*--.*\\n", Pattern.MULTILINE);
private final InvoicingPipelineOptions options;
InvoicingPipeline(InvoicingPipelineOptions options) {
@@ -71,21 +85,60 @@ public class InvoicingPipeline implements Serializable {
}
void setupPipeline(Pipeline pipeline) {
options.setIsolationOverride(TransactionIsolationLevel.TRANSACTION_READ_COMMITTED);
PCollection<BillingEvent> billingEvents =
pipeline.apply(
"Read BillingEvents from Bigquery",
BigQueryIO.read(BillingEvent::parseFromRecord)
.fromQuery(makeQuery(options.getYearMonth(), options.getProject()))
.withCoder(SerializableCoder.of(BillingEvent.class))
.usingStandardSql()
.withoutValidation()
.withTemplateCompatibility());
options.getDatabase().equals("DATASTORE")
? readFromBigQuery(options, pipeline)
: readFromCloudSql(options, pipeline);
saveInvoiceCsv(billingEvents, options);
saveDetailedCsv(billingEvents, options);
}
static PCollection<BillingEvent> readFromBigQuery(
InvoicingPipelineOptions options, Pipeline pipeline) {
return pipeline.apply(
"Read BillingEvents from Bigquery",
BigQueryIO.read(BillingEvent::parseFromRecord)
.fromQuery(makeQuery(options.getYearMonth(), options.getProject()))
.withCoder(SerializableCoder.of(BillingEvent.class))
.usingStandardSql()
.withoutValidation()
.withTemplateCompatibility());
}
static PCollection<BillingEvent> readFromCloudSql(
InvoicingPipelineOptions options, Pipeline pipeline) {
Read<Object[], BillingEvent> read =
RegistryJpaIO.read(
makeCloudSqlQuery(options.getYearMonth()), false, InvoicingPipeline::parseRow);
return pipeline.apply("Read BillingEvents from Cloud SQL", read);
}
private static BillingEvent parseRow(Object[] row) {
google.registry.model.billing.BillingEvent.OneTime oneTime =
(google.registry.model.billing.BillingEvent.OneTime) row[0];
Registrar registrar = (Registrar) row[1];
return BillingEvent.create(
oneTime.getId(),
DateTimeUtils.toZonedDateTime(oneTime.getBillingTime(), ZoneId.of("UTC")),
DateTimeUtils.toZonedDateTime(oneTime.getEventTime(), ZoneId.of("UTC")),
registrar.getRegistrarId(),
registrar.getBillingIdentifier().toString(),
registrar.getPoNumber().orElse(""),
DomainNameUtils.getTldFromDomainName(oneTime.getTargetId()),
oneTime.getReason().toString(),
oneTime.getTargetId(),
oneTime.getDomainRepoId(),
Optional.ofNullable(oneTime.getPeriodYears()).orElse(0),
oneTime.getCost().getCurrencyUnit().toString(),
oneTime.getCost().getAmount().doubleValue(),
String.join(
" ", oneTime.getFlags().stream().map(Flag::toString).collect(toImmutableSet())));
}
/** Transform that converts a {@code BillingEvent} into an invoice CSV row. */
private static class GenerateInvoiceRows
extends PTransform<PCollection<BillingEvent>, PCollection<String>> {
@@ -171,6 +224,21 @@ public class InvoicingPipeline implements Serializable {
.build();
}
/** Create the Cloud SQL query for a given yearMonth at runtime. */
static String makeCloudSqlQuery(String yearMonth) {
YearMonth endMonth = YearMonth.parse(yearMonth).plusMonths(1);
String queryWithComments =
SqlTemplate.create(
getQueryFromFile(InvoicingPipeline.class, "cloud_sql_billing_events.sql"))
.put("FIRST_TIMESTAMP_OF_MONTH", yearMonth.concat("-01"))
.put(
"LAST_TIMESTAMP_OF_MONTH",
String.format("%d-%d-01", endMonth.getYear(), endMonth.getMonthValue()))
.build();
// Remove the comments from the query string
return SQL_COMMENT_REGEX.matcher(queryWithComments).replaceAll("");
}
public static void main(String[] args) {
PipelineOptionsFactory.register(InvoicingPipelineOptions.class);
InvoicingPipelineOptions options =

View File

@@ -30,6 +30,11 @@ public interface InvoicingPipelineOptions extends RegistryPipelineOptions {
void setInvoiceFilePrefix(String value);
@Description("The database to read data from.")
String getDatabase();
void setDatabase(String value);
@Description("The GCS bucket URL for invoices and detailed reports to be uploaded.")
String getBillingBucketUrl();

View File

@@ -0,0 +1,327 @@
// Copyright 2021 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.rde;
import static com.google.common.base.Preconditions.checkState;
import static com.google.common.base.Verify.verify;
import static google.registry.model.common.Cursor.getCursorTimeOrStartOfTime;
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
import static google.registry.persistence.transaction.TransactionManagerUtil.transactIfJpaTm;
import static google.registry.rde.RdeModule.BRDA_QUEUE;
import static google.registry.rde.RdeModule.RDE_UPLOAD_QUEUE;
import static java.nio.charset.StandardCharsets.UTF_8;
import com.google.auto.value.AutoValue;
import com.google.cloud.storage.BlobId;
import com.google.common.collect.ImmutableMultimap;
import com.google.common.flogger.FluentLogger;
import google.registry.gcs.GcsUtils;
import google.registry.keyring.api.PgpHelper;
import google.registry.model.common.Cursor;
import google.registry.model.rde.RdeMode;
import google.registry.model.rde.RdeNamingUtils;
import google.registry.model.rde.RdeRevision;
import google.registry.model.tld.Registry;
import google.registry.rde.BrdaCopyAction;
import google.registry.rde.DepositFragment;
import google.registry.rde.Ghostryde;
import google.registry.rde.PendingDeposit;
import google.registry.rde.RdeCounter;
import google.registry.rde.RdeMarshaller;
import google.registry.rde.RdeModule;
import google.registry.rde.RdeResourceType;
import google.registry.rde.RdeUploadAction;
import google.registry.rde.RdeUtil;
import google.registry.request.Action.Service;
import google.registry.request.RequestParameters;
import google.registry.tldconfig.idn.IdnTableEnum;
import google.registry.util.CloudTasksUtils;
import google.registry.xjc.rdeheader.XjcRdeHeader;
import google.registry.xjc.rdeheader.XjcRdeHeaderElement;
import google.registry.xml.ValidationMode;
import google.registry.xml.XmlException;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.security.Security;
import java.util.Optional;
import org.apache.beam.sdk.options.PipelineOptions;
import org.apache.beam.sdk.transforms.DoFn;
import org.apache.beam.sdk.transforms.PTransform;
import org.apache.beam.sdk.transforms.ParDo;
import org.apache.beam.sdk.values.KV;
import org.apache.beam.sdk.values.PCollection;
import org.apache.beam.sdk.values.PDone;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.openpgp.PGPPublicKey;
import org.joda.time.DateTime;
public class RdeIO {
@AutoValue
abstract static class Write
extends PTransform<PCollection<KV<PendingDeposit, Iterable<DepositFragment>>>, PDone> {
abstract GcsUtils gcsUtils();
abstract CloudTasksUtils cloudTasksUtils();
abstract String rdeBucket();
// It's OK to return a primitive array because we are only using it to construct the
// PGPPublicKey, which is not serializable.
@SuppressWarnings("mutable")
abstract byte[] stagingKeyBytes();
abstract ValidationMode validationMode();
static Builder builder() {
return new AutoValue_RdeIO_Write.Builder();
}
@AutoValue.Builder
abstract static class Builder {
abstract Builder setGcsUtils(GcsUtils value);
abstract Builder setCloudTasksUtils(CloudTasksUtils value);
abstract Builder setRdeBucket(String value);
abstract Builder setStagingKeyBytes(byte[] value);
abstract Builder setValidationMode(ValidationMode value);
abstract Write build();
}
@Override
public PDone expand(PCollection<KV<PendingDeposit, Iterable<DepositFragment>>> input) {
input
.apply(
"Write to GCS",
ParDo.of(new RdeWriter(gcsUtils(), rdeBucket(), stagingKeyBytes(), validationMode())))
.apply("Update cursors", ParDo.of(new CursorUpdater()))
.apply("Enqueue upload action", ParDo.of(new UploadEnqueuer(cloudTasksUtils())));
return PDone.in(input.getPipeline());
}
}
private static class RdeWriter
extends DoFn<KV<PendingDeposit, Iterable<DepositFragment>>, KV<PendingDeposit, Integer>> {
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
private final GcsUtils gcsUtils;
private final String rdeBucket;
private final byte[] stagingKeyBytes;
private final RdeMarshaller marshaller;
protected RdeWriter(
GcsUtils gcsUtils,
String rdeBucket,
byte[] stagingKeyBytes,
ValidationMode validationMode) {
this.gcsUtils = gcsUtils;
this.rdeBucket = rdeBucket;
this.stagingKeyBytes = stagingKeyBytes;
this.marshaller = new RdeMarshaller(validationMode);
}
@Setup
public void setup() {
Security.addProvider(new BouncyCastleProvider());
}
@ProcessElement
public void processElement(
@Element KV<PendingDeposit, Iterable<DepositFragment>> kv,
PipelineOptions options,
OutputReceiver<KV<PendingDeposit, Integer>> outputReceiver) {
PGPPublicKey stagingKey = PgpHelper.loadPublicKeyBytes(stagingKeyBytes);
PendingDeposit key = kv.getKey();
Iterable<DepositFragment> fragments = kv.getValue();
RdeCounter counter = new RdeCounter();
// Determine some basic things about the deposit.
final RdeMode mode = key.mode();
final String tld = key.tld();
final DateTime watermark = key.watermark();
final int revision =
Optional.ofNullable(key.revision())
.orElseGet(() -> RdeRevision.getNextRevision(tld, watermark, mode));
String id = RdeUtil.timestampToId(watermark);
String prefix = options.getJobName();
String basename = RdeNamingUtils.makeRydeFilename(tld, watermark, mode, 1, revision);
if (key.manual()) {
checkState(key.directoryWithTrailingSlash() != null, "Manual subdirectory not specified");
prefix = prefix + "/manual/" + key.directoryWithTrailingSlash() + basename;
} else {
prefix = prefix + "/" + basename;
}
BlobId xmlFilename = BlobId.of(rdeBucket, prefix + ".xml.ghostryde");
// This file will contain the byte length (ASCII) of the raw unencrypted XML.
//
// This is necessary because RdeUploadAction creates a tar file which requires that the length
// be outputted. We don't want to have to decrypt the entire ghostryde file to determine the
// length, so we just save it separately.
BlobId xmlLengthFilename = BlobId.of(rdeBucket, prefix + ".xml.length");
BlobId reportFilename = BlobId.of(rdeBucket, prefix + "-report.xml.ghostryde");
// These variables will be populated as we write the deposit XML and used for other files.
boolean failed = false;
XjcRdeHeader header;
// Write a gigantic XML file to GCS. We'll start by opening encrypted out/err file handles.
logger.atInfo().log("Writing files '%s' and '%s'.", xmlFilename, xmlLengthFilename);
try (OutputStream gcsOutput = gcsUtils.openOutputStream(xmlFilename);
OutputStream lengthOutput = gcsUtils.openOutputStream(xmlLengthFilename);
OutputStream ghostrydeEncoder = Ghostryde.encoder(gcsOutput, stagingKey, lengthOutput);
Writer output = new OutputStreamWriter(ghostrydeEncoder, UTF_8)) {
// Output the top portion of the XML document.
output.write(marshaller.makeHeader(id, watermark, RdeResourceType.getUris(mode), revision));
// Output XML fragments while counting them.
for (DepositFragment fragment : fragments) {
if (!fragment.xml().isEmpty()) {
output.write(fragment.xml());
counter.increment(fragment.type());
}
if (!fragment.error().isEmpty()) {
failed = true;
logger.atSevere().log("Fragment error: %s", fragment.error());
}
}
// Don't write the IDN elements for BRDA.
if (mode == RdeMode.FULL) {
for (IdnTableEnum idn : IdnTableEnum.values()) {
output.write(marshaller.marshalIdn(idn.getTable()));
counter.increment(RdeResourceType.IDN);
}
}
// Output XML that says how many resources were emitted.
header = counter.makeHeader(tld, mode);
output.write(marshaller.marshalOrDie(new XjcRdeHeaderElement(header)));
// Output the bottom of the XML document.
output.write(marshaller.makeFooter());
} catch (IOException e) {
throw new RuntimeException(e);
}
// If an entity was broken, abort after writing as much logs/deposit data as possible.
verify(!failed, "RDE staging failed for TLD %s", tld);
// Write a tiny XML file to GCS containing some information about the deposit.
//
// This will be sent to ICANN once we're done uploading the big XML to the escrow provider.
if (mode == RdeMode.FULL) {
logger.atInfo().log("Writing file '%s'.", reportFilename);
try (OutputStream gcsOutput = gcsUtils.openOutputStream(reportFilename);
OutputStream ghostrydeEncoder = Ghostryde.encoder(gcsOutput, stagingKey)) {
counter.makeReport(id, watermark, header, revision).marshal(ghostrydeEncoder, UTF_8);
} catch (IOException | XmlException e) {
throw new RuntimeException(e);
}
}
// Now that we're done, output roll the cursor forward.
if (key.manual()) {
logger.atInfo().log("Manual operation; not advancing cursor or enqueuing upload task.");
} else {
outputReceiver.output(KV.of(key, revision));
}
}
}
private static class CursorUpdater extends DoFn<KV<PendingDeposit, Integer>, PendingDeposit> {
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
@ProcessElement
public void processElement(
@Element KV<PendingDeposit, Integer> input, OutputReceiver<PendingDeposit> outputReceiver) {
tm().transact(
() -> {
PendingDeposit key = input.getKey();
int revision = input.getValue();
Registry registry = Registry.get(key.tld());
Optional<Cursor> cursor =
transactIfJpaTm(
() ->
tm().loadByKeyIfPresent(
Cursor.createVKey(key.cursor(), registry.getTldStr())));
DateTime position = getCursorTimeOrStartOfTime(cursor);
checkState(key.interval() != null, "Interval must be present");
DateTime newPosition = key.watermark().plus(key.interval());
if (!position.isBefore(newPosition)) {
logger.atWarning().log("Cursor has already been rolled forward.");
return;
}
verify(
position.equals(key.watermark()),
"Partial ordering of RDE deposits broken: %s %s",
position,
key);
tm().put(Cursor.create(key.cursor(), newPosition, registry));
logger.atInfo().log(
"Rolled forward %s on %s cursor to %s.", key.cursor(), key.tld(), newPosition);
RdeRevision.saveRevision(key.tld(), key.watermark(), key.mode(), revision);
});
outputReceiver.output(input.getKey());
}
}
private static class UploadEnqueuer extends DoFn<PendingDeposit, Void> {
private final CloudTasksUtils cloudTasksUtils;
private UploadEnqueuer(CloudTasksUtils cloudTasksUtils) {
this.cloudTasksUtils = cloudTasksUtils;
}
@ProcessElement
public void processElement(@Element PendingDeposit input, PipelineOptions options) {
if (input.mode() == RdeMode.FULL) {
cloudTasksUtils.enqueue(
RDE_UPLOAD_QUEUE,
CloudTasksUtils.createPostTask(
RdeUploadAction.PATH,
Service.BACKEND.getServiceId(),
ImmutableMultimap.of(
RequestParameters.PARAM_TLD,
input.tld(),
RdeModule.PARAM_PREFIX,
options.getJobName() + '/')));
} else {
cloudTasksUtils.enqueue(
BRDA_QUEUE,
CloudTasksUtils.createPostTask(
BrdaCopyAction.PATH,
Service.BACKEND.getServiceId(),
ImmutableMultimap.of(
RequestParameters.PARAM_TLD,
input.tld(),
RdeModule.PARAM_WATERMARK,
input.watermark().toString(),
RdeModule.PARAM_PREFIX,
options.getJobName() + '/')));
}
}
}
}

View File

@@ -18,14 +18,19 @@ import static com.google.common.collect.ImmutableSet.toImmutableSet;
import static google.registry.model.EppResourceUtils.loadAtPointInTimeAsync;
import static google.registry.persistence.transaction.TransactionManagerFactory.jpaTm;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSetMultimap;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.google.common.io.BaseEncoding;
import dagger.BindsInstance;
import dagger.Component;
import google.registry.beam.common.RegistryJpaIO;
import google.registry.config.CloudTasksUtilsModule;
import google.registry.config.CredentialModule;
import google.registry.config.RegistryConfig.ConfigModule;
import google.registry.gcs.GcsUtils;
import google.registry.model.EppResource;
import google.registry.model.contact.ContactResource;
import google.registry.model.domain.DomainBase;
@@ -33,12 +38,15 @@ import google.registry.model.host.HostResource;
import google.registry.model.rde.RdeMode;
import google.registry.model.registrar.Registrar;
import google.registry.model.registrar.Registrar.Type;
import google.registry.persistence.PersistenceModule.TransactionIsolationLevel;
import google.registry.persistence.VKey;
import google.registry.rde.DepositFragment;
import google.registry.rde.PendingDeposit;
import google.registry.rde.PendingDeposit.PendingDepositCoder;
import google.registry.rde.RdeFragmenter;
import google.registry.rde.RdeMarshaller;
import google.registry.util.CloudTasksUtils;
import google.registry.util.UtilsModule;
import google.registry.xml.ValidationMode;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
@@ -50,6 +58,8 @@ import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.function.Supplier;
import javax.inject.Inject;
import javax.inject.Singleton;
import javax.persistence.Entity;
import org.apache.beam.sdk.Pipeline;
import org.apache.beam.sdk.PipelineResult;
@@ -58,7 +68,7 @@ import org.apache.beam.sdk.coders.SerializableCoder;
import org.apache.beam.sdk.options.PipelineOptionsFactory;
import org.apache.beam.sdk.transforms.FlatMapElements;
import org.apache.beam.sdk.transforms.Flatten;
import org.apache.beam.sdk.transforms.Reshuffle;
import org.apache.beam.sdk.transforms.GroupByKey;
import org.apache.beam.sdk.values.KV;
import org.apache.beam.sdk.values.PCollection;
import org.apache.beam.sdk.values.PCollectionList;
@@ -76,10 +86,16 @@ import org.joda.time.DateTime;
* @see <a href="https://cloud.google.com/dataflow/docs/guides/templates/using-flex-templates">Using
* Flex Templates</a>
*/
@Singleton
public class RdePipeline implements Serializable {
private final RdeMarshaller marshaller;
private final transient RdePipelineOptions options;
private final ValidationMode mode;
private final ImmutableSetMultimap<String, PendingDeposit> pendings;
private final String rdeBucket;
private final byte[] stagingKeyBytes;
private final GcsUtils gcsUtils;
private final CloudTasksUtils cloudTasksUtils;
// Registrars to be excluded from data escrow. Not including the sandbox-only OTE type so that
// if sneaks into production we would get an extra signal.
@@ -97,31 +113,45 @@ public class RdePipeline implements Serializable {
+ (clazz.equals(DomainBase.class) ? " AND tld in (:tlds)" : "");
}
RdePipeline(RdePipelineOptions options) throws IOException, ClassNotFoundException {
this.marshaller = new RdeMarshaller(ValidationMode.valueOf(options.getValidationMode()));
@Inject
RdePipeline(RdePipelineOptions options, GcsUtils gcsUtils, CloudTasksUtils cloudTasksUtils) {
this.options = options;
this.mode = ValidationMode.valueOf(options.getValidationMode());
this.pendings = decodePendings(options.getPendings());
}
@VisibleForTesting
PipelineResult run(Pipeline pipeline) {
createFragments(pipeline);
return pipeline.run();
this.rdeBucket = options.getRdeStagingBucket();
this.stagingKeyBytes = BaseEncoding.base64Url().decode(options.getStagingKey());
this.gcsUtils = gcsUtils;
this.cloudTasksUtils = cloudTasksUtils;
}
PipelineResult run() {
return run(Pipeline.create());
Pipeline pipeline = Pipeline.create(options);
PCollection<KV<PendingDeposit, Iterable<DepositFragment>>> fragments =
createFragments(pipeline);
persistData(fragments);
return pipeline.run();
}
PCollection<KV<PendingDeposit, DepositFragment>> createFragments(Pipeline pipeline) {
PCollection<KV<PendingDeposit, DepositFragment>> fragments =
PCollectionList.of(processRegistrars(pipeline))
.and(processNonRegistrarEntities(pipeline, DomainBase.class))
.and(processNonRegistrarEntities(pipeline, ContactResource.class))
.and(processNonRegistrarEntities(pipeline, HostResource.class))
.apply(Flatten.pCollections())
.setCoder(
KvCoder.of(PendingDepositCoder.of(), SerializableCoder.of(DepositFragment.class)));
return fragments;
PCollection<KV<PendingDeposit, Iterable<DepositFragment>>> createFragments(Pipeline pipeline) {
return PCollectionList.of(processRegistrars(pipeline))
.and(processNonRegistrarEntities(pipeline, DomainBase.class))
.and(processNonRegistrarEntities(pipeline, ContactResource.class))
.and(processNonRegistrarEntities(pipeline, HostResource.class))
.apply(Flatten.pCollections())
.setCoder(KvCoder.of(PendingDepositCoder.of(), SerializableCoder.of(DepositFragment.class)))
.apply("Group by PendingDeposit", GroupByKey.create());
}
void persistData(PCollection<KV<PendingDeposit, Iterable<DepositFragment>>> input) {
input.apply(
"Write to GCS, update cursors, and enqueue upload tasks",
RdeIO.Write.builder()
.setRdeBucket(rdeBucket)
.setGcsUtils(gcsUtils)
.setCloudTasksUtils(cloudTasksUtils)
.setValidationMode(mode)
.setStagingKeyBytes(stagingKeyBytes)
.build());
}
PCollection<KV<PendingDeposit, DepositFragment>> processRegistrars(Pipeline pipeline) {
@@ -144,25 +174,21 @@ public class RdePipeline implements Serializable {
.via(
(VKey<Registrar> key) -> {
Registrar registrar = jpaTm().transact(() -> jpaTm().loadByKey(key));
DepositFragment fragment = marshaller.marshalRegistrar(registrar);
DepositFragment fragment =
new RdeMarshaller(mode).marshalRegistrar(registrar);
return pendings.values().stream()
.map(pending -> KV.of(pending, fragment))
.collect(toImmutableSet());
}));
}
@SuppressWarnings("deprecation") // Reshuffle is still recommended by Dataflow.
<T extends EppResource>
PCollection<KV<PendingDeposit, DepositFragment>> processNonRegistrarEntities(
Pipeline pipeline, Class<T> clazz) {
return createInputs(pipeline, clazz)
.apply("Marshal " + clazz.getSimpleName() + " into DepositFragment", mapToFragments(clazz))
.setCoder(KvCoder.of(PendingDepositCoder.of(), SerializableCoder.of(DepositFragment.class)))
.apply(
"Reshuffle KV<PendingDeposit, DepositFragment> of "
+ clazz.getSimpleName()
+ " to prevent fusion",
Reshuffle.of());
.setCoder(
KvCoder.of(PendingDepositCoder.of(), SerializableCoder.of(DepositFragment.class)));
}
<T extends EppResource> PCollection<VKey<T>> createInputs(Pipeline pipeline, Class<T> clazz) {
@@ -176,7 +202,7 @@ public class RdePipeline implements Serializable {
String.class,
// TODO: consider adding coders for entities and pass them directly instead of using
// VKeys.
x -> VKey.create(clazz, x)));
x -> VKey.createSql(clazz, x)));
}
<T extends EppResource>
@@ -205,7 +231,8 @@ public class RdePipeline implements Serializable {
Maps.asMap(dates, input -> loadAtPointInTimeAsync(resource, input)));
// Convert resource to an XML fragment for each watermark/mode pair lazily and cache
// the result.
RdeFragmenter fragmenter = new RdeFragmenter(resourceAtTimes, marshaller);
RdeFragmenter fragmenter =
new RdeFragmenter(resourceAtTimes, new RdeMarshaller(mode));
List<KV<PendingDeposit, DepositFragment>> results = new ArrayList<>();
for (String tld : tlds) {
for (PendingDeposit pending : pendings.get(tld)) {
@@ -228,13 +255,14 @@ public class RdePipeline implements Serializable {
* the original TLD to pending deposit map.
*/
@SuppressWarnings("unchecked")
static ImmutableSetMultimap<String, PendingDeposit> decodePendings(String encodedPending)
throws IOException, ClassNotFoundException {
static ImmutableSetMultimap<String, PendingDeposit> decodePendings(String encodedPending) {
try (ObjectInputStream ois =
new ObjectInputStream(
new ByteArrayInputStream(
BaseEncoding.base64Url().omitPadding().decode(encodedPending)))) {
return (ImmutableSetMultimap<String, PendingDeposit>) ois.readObject();
} catch (IOException | ClassNotFoundException e) {
throw new IllegalArgumentException("Unable to parse encoded pending deposit map.", e);
}
}
@@ -254,7 +282,33 @@ public class RdePipeline implements Serializable {
public static void main(String[] args) throws IOException, ClassNotFoundException {
PipelineOptionsFactory.register(RdePipelineOptions.class);
RdePipelineOptions options = PipelineOptionsFactory.fromArgs(args).as(RdePipelineOptions.class);
new RdePipeline(options).run();
RdePipelineOptions options =
PipelineOptionsFactory.fromArgs(args).withValidation().as(RdePipelineOptions.class);
// RegistryPipelineWorkerInitializer only initializes before pipeline executions, after the
// main() function constructed the graph. We need the registry environment set up so that we
// can create a CloudTasksUtils which uses the environment-dependent config file.
options.getRegistryEnvironment().setup();
options.setIsolationOverride(TransactionIsolationLevel.TRANSACTION_READ_COMMITTED);
DaggerRdePipeline_RdePipelineComponent.builder().options(options).build().rdePipeline().run();
}
@Singleton
@Component(
modules = {
CredentialModule.class,
ConfigModule.class,
CloudTasksUtilsModule.class,
UtilsModule.class
})
interface RdePipelineComponent {
RdePipeline rdePipeline();
@Component.Builder
interface Builder {
@BindsInstance
Builder options(RdePipelineOptions options);
RdePipelineComponent build();
}
}
}

View File

@@ -29,4 +29,14 @@ public interface RdePipelineOptions extends RegistryPipelineOptions {
String getValidationMode();
void setValidationMode(String value);
@Description("The GCS bucket where the encrypted RDE deposits will be uploaded to.")
String getRdeStagingBucket();
void setRdeStagingBucket(String value);
@Description("The Base64-encoded PGP public key to encrypt the deposits.")
String getStagingKey();
void setStagingKey(String value);
}

View File

@@ -25,7 +25,7 @@ import org.apache.avro.generic.GenericRecord;
import org.apache.beam.sdk.io.gcp.bigquery.SchemaAndRecord;
/**
* A POJO representing a single subdomain, parsed from a {@code SchemaAndRecord}.
* A POJO representing a domain name and associated info, parsed from a {@code SchemaAndRecord}.
*
* <p>This is a trivially serializable class that allows Beam to transform the results of a Bigquery
* query into a standard Java representation, giving us the type guarantees and ease of manipulation
@@ -33,28 +33,31 @@ import org.apache.beam.sdk.io.gcp.bigquery.SchemaAndRecord;
* function.
*/
@AutoValue
public abstract class Subdomain implements Serializable {
public abstract class DomainNameInfo implements Serializable {
private static final ImmutableList<String> FIELD_NAMES =
ImmutableList.of("domainName", "domainRepoId", "registrarId", "registrarEmailAddress");
/** Returns the fully qualified domain name. */
abstract String domainName();
/** Returns the domain repo ID (the primary key of the domain table). */
abstract String domainRepoId();
/** Returns the registrar ID of the associated registrar for this domain. */
abstract String registrarId();
/** Returns the email address of the registrar associated with this domain. */
abstract String registrarEmailAddress();
/**
* Constructs a {@link Subdomain} from an Apache Avro {@code SchemaAndRecord}.
* Constructs a {@link DomainNameInfo} from an Apache Avro {@code SchemaAndRecord}.
*
* @see <a
* href=http://avro.apache.org/docs/1.7.7/api/java/org/apache/avro/generic/GenericData.Record.html>
* Apache AVRO GenericRecord</a>
*/
static Subdomain parseFromRecord(SchemaAndRecord schemaAndRecord) {
static DomainNameInfo parseFromRecord(SchemaAndRecord schemaAndRecord) {
checkFieldsNotNull(FIELD_NAMES, schemaAndRecord);
GenericRecord record = schemaAndRecord.getRecord();
return create(
@@ -65,18 +68,15 @@ public abstract class Subdomain implements Serializable {
}
/**
* Creates a concrete {@link Subdomain}.
* Creates a concrete {@link DomainNameInfo}.
*
* <p>This should only be used outside this class for testing- instances of {@link Subdomain}
* <p>This should only be used outside this class for testing- instances of {@link DomainNameInfo}
* should otherwise come from {@link #parseFromRecord}.
*/
@VisibleForTesting
static Subdomain create(
String domainName,
String domainRepoId,
String registrarId,
String registrarEmailAddress) {
return new AutoValue_Subdomain(
static DomainNameInfo create(
String domainName, String domainRepoId, String registrarId, String registrarEmailAddress) {
return new AutoValue_DomainNameInfo(
domainName, domainRepoId, registrarId, registrarEmailAddress);
}
}

View File

@@ -55,13 +55,14 @@ public class SafeBrowsingTransforms {
"https://safebrowsing.googleapis.com/v4/threatMatches:find";
/**
* {@link DoFn} mapping a {@link Subdomain} to its evaluation report from SafeBrowsing.
* {@link DoFn} mapping a {@link DomainNameInfo} to its evaluation report from SafeBrowsing.
*
* <p>Refer to the Lookup API documentation for the request/response format and other details.
*
* @see <a href=https://developers.google.com/safe-browsing/v4/lookup-api>Lookup API</a>
*/
static class EvaluateSafeBrowsingFn extends DoFn<Subdomain, KV<Subdomain, ThreatMatch>> {
static class EvaluateSafeBrowsingFn
extends DoFn<DomainNameInfo, KV<DomainNameInfo, ThreatMatch>> {
/**
* Max number of urls we can check in a single query.
@@ -74,10 +75,11 @@ public class SafeBrowsingTransforms {
private final String apiKey;
/**
* Maps a subdomain's {@code fullyQualifiedDomainName} to its corresponding {@link Subdomain} to
* facilitate batching SafeBrowsing API requests.
* Maps a domain name's {@code fullyQualifiedDomainName} to its corresponding {@link
* DomainNameInfo} to facilitate batching SafeBrowsing API requests.
*/
private final Map<String, Subdomain> subdomainBuffer = new LinkedHashMap<>(BATCH_SIZE);
private final Map<String, DomainNameInfo> domainNameInfoBuffer =
new LinkedHashMap<>(BATCH_SIZE);
/**
* Provides the HTTP client we use to interact with the SafeBrowsing API.
@@ -119,37 +121,38 @@ public class SafeBrowsingTransforms {
closeableHttpClientSupplier = clientSupplier;
}
/** Evaluates any buffered {@link Subdomain} objects upon completing the bundle. */
/** Evaluates any buffered {@link DomainNameInfo} objects upon completing the bundle. */
@FinishBundle
public void finishBundle(FinishBundleContext context) {
if (!subdomainBuffer.isEmpty()) {
ImmutableSet<KV<Subdomain, ThreatMatch>> results = evaluateAndFlush();
if (!domainNameInfoBuffer.isEmpty()) {
ImmutableSet<KV<DomainNameInfo, ThreatMatch>> results = evaluateAndFlush();
results.forEach((kv) -> context.output(kv, Instant.now(), GlobalWindow.INSTANCE));
}
}
/**
* Buffers {@link Subdomain} objects until we reach the batch size, then bulk-evaluate the URLs
* with the SafeBrowsing API.
* Buffers {@link DomainNameInfo} objects until we reach the batch size, then bulk-evaluate the
* URLs with the SafeBrowsing API.
*/
@ProcessElement
public void processElement(ProcessContext context) {
Subdomain subdomain = context.element();
subdomainBuffer.put(subdomain.domainName(), subdomain);
if (subdomainBuffer.size() >= BATCH_SIZE) {
ImmutableSet<KV<Subdomain, ThreatMatch>> results = evaluateAndFlush();
DomainNameInfo domainNameInfo = context.element();
domainNameInfoBuffer.put(domainNameInfo.domainName(), domainNameInfo);
if (domainNameInfoBuffer.size() >= BATCH_SIZE) {
ImmutableSet<KV<DomainNameInfo, ThreatMatch>> results = evaluateAndFlush();
results.forEach(context::output);
}
}
/**
* Evaluates all {@link Subdomain} objects in the buffer and returns a list of key-value pairs
* from {@link Subdomain} to its SafeBrowsing report.
* Evaluates all {@link DomainNameInfo} objects in the buffer and returns a list of key-value
* pairs from {@link DomainNameInfo} to its SafeBrowsing report.
*
* <p>If a {@link Subdomain} is safe according to the API, it will not emit a report.
* <p>If a {@link DomainNameInfo} is safe according to the API, it will not emit a report.
*/
private ImmutableSet<KV<Subdomain, ThreatMatch>> evaluateAndFlush() {
ImmutableSet.Builder<KV<Subdomain, ThreatMatch>> resultBuilder = new ImmutableSet.Builder<>();
private ImmutableSet<KV<DomainNameInfo, ThreatMatch>> evaluateAndFlush() {
ImmutableSet.Builder<KV<DomainNameInfo, ThreatMatch>> resultBuilder =
new ImmutableSet.Builder<>();
try {
URIBuilder uriBuilder = new URIBuilder(SAFE_BROWSING_URL);
// Add the API key param
@@ -174,7 +177,7 @@ public class SafeBrowsingTransforms {
throw new RuntimeException("Caught parsing exception, failing pipeline.", e);
} finally {
// Flush the buffer
subdomainBuffer.clear();
domainNameInfoBuffer.clear();
}
return resultBuilder.build();
}
@@ -183,7 +186,7 @@ public class SafeBrowsingTransforms {
private JSONObject createRequestBody() throws JSONException {
// Accumulate all domain names to evaluate.
JSONArray threatArray = new JSONArray();
for (String fullyQualifiedDomainName : subdomainBuffer.keySet()) {
for (String fullyQualifiedDomainName : domainNameInfoBuffer.keySet()) {
threatArray.put(new JSONObject().put("url", fullyQualifiedDomainName));
}
// Construct the JSON request body
@@ -211,11 +214,11 @@ public class SafeBrowsingTransforms {
*/
private void processResponse(
CloseableHttpResponse response,
ImmutableSet.Builder<KV<Subdomain, ThreatMatch>> resultBuilder)
ImmutableSet.Builder<KV<DomainNameInfo, ThreatMatch>> resultBuilder)
throws JSONException, IOException {
int statusCode = response.getStatusLine().getStatusCode();
if (statusCode != SC_OK) {
logger.atWarning().log("Got unexpected status code %s from response", statusCode);
logger.atWarning().log("Got unexpected status code %s from response.", statusCode);
} else {
// Unpack the response body
JSONObject responseBody =
@@ -224,18 +227,19 @@ public class SafeBrowsingTransforms {
new InputStreamReader(response.getEntity().getContent(), UTF_8)));
logger.atInfo().log("Got response: %s", responseBody.toString());
if (responseBody.length() == 0) {
logger.atInfo().log("Response was empty, no threats detected");
logger.atInfo().log("Response was empty, no threats detected.");
} else {
// Emit all Subdomains with their API results.
// Emit all DomainNameInfos with their API results.
JSONArray threatMatches = responseBody.getJSONArray("matches");
for (int i = 0; i < threatMatches.length(); i++) {
JSONObject match = threatMatches.getJSONObject(i);
String url = match.getJSONObject("threat").getString("url");
Subdomain subdomain = subdomainBuffer.get(url);
DomainNameInfo domainNameInfo = domainNameInfoBuffer.get(url);
resultBuilder.add(
KV.of(
subdomain,
ThreatMatch.create(match.getString("threatType"), subdomain.domainName())));
domainNameInfo,
ThreatMatch.create(
match.getString("threatType"), domainNameInfo.domainName())));
}
}
}

View File

@@ -16,6 +16,7 @@ package google.registry.beam.spec11;
import static com.google.common.base.Preconditions.checkArgument;
import static google.registry.beam.BeamUtils.getQueryFromFile;
import static google.registry.persistence.transaction.TransactionManagerFactory.jpaTm;
import com.google.auto.value.AutoValue;
import com.google.common.collect.ImmutableSet;
@@ -29,6 +30,8 @@ import google.registry.config.RegistryConfig.ConfigModule;
import google.registry.model.domain.DomainBase;
import google.registry.model.reporting.Spec11ThreatMatch;
import google.registry.model.reporting.Spec11ThreatMatch.ThreatType;
import google.registry.persistence.PersistenceModule.TransactionIsolationLevel;
import google.registry.persistence.VKey;
import google.registry.util.Retrier;
import google.registry.util.SqlTemplate;
import google.registry.util.UtilsModule;
@@ -40,6 +43,7 @@ import org.apache.beam.sdk.coders.SerializableCoder;
import org.apache.beam.sdk.io.TextIO;
import org.apache.beam.sdk.io.gcp.bigquery.BigQueryIO;
import org.apache.beam.sdk.options.PipelineOptionsFactory;
import org.apache.beam.sdk.transforms.DoFn;
import org.apache.beam.sdk.transforms.GroupByKey;
import org.apache.beam.sdk.transforms.MapElements;
import org.apache.beam.sdk.transforms.ParDo;
@@ -98,86 +102,107 @@ public class Spec11Pipeline implements Serializable {
}
void setupPipeline(Pipeline pipeline) {
PCollection<Subdomain> domains =
options.setIsolationOverride(TransactionIsolationLevel.TRANSACTION_READ_COMMITTED);
PCollection<DomainNameInfo> domains =
options.getDatabase().equals("DATASTORE")
? readFromBigQuery(options, pipeline)
: readFromCloudSql(pipeline);
PCollection<KV<Subdomain, ThreatMatch>> threatMatches =
PCollection<KV<DomainNameInfo, ThreatMatch>> threatMatches =
domains.apply("Run through SafeBrowsing API", ParDo.of(safeBrowsingFn));
saveToSql(threatMatches, options);
saveToGcs(threatMatches, options);
}
static PCollection<Subdomain> readFromCloudSql(Pipeline pipeline) {
Read<Object[], Subdomain> read =
static PCollection<DomainNameInfo> readFromCloudSql(Pipeline pipeline) {
Read<Object[], KV<String, String>> read =
RegistryJpaIO.read(
"select d, r.emailAddress from Domain d join Registrar r on"
+ " d.currentSponsorClientId = r.clientIdentifier where r.type = 'REAL'"
+ " and d.deletionTime > now()",
"select d.repoId, r.emailAddress from Domain d join Registrar r on"
+ " d.currentSponsorClientId = r.clientIdentifier where r.type = 'REAL' and"
+ " d.deletionTime > now()",
false,
Spec11Pipeline::parseRow);
return pipeline.apply("Read active domains from Cloud SQL", read);
return pipeline
.apply("Read active domains from Cloud SQL", read)
.apply(
"Build DomainNameInfo",
ParDo.of(
new DoFn<KV<String, String>, DomainNameInfo>() {
@ProcessElement
public void processElement(
@Element KV<String, String> input, OutputReceiver<DomainNameInfo> output) {
DomainBase domainBase =
jpaTm()
.transact(
() ->
jpaTm()
.loadByKey(
VKey.createSql(DomainBase.class, input.getKey())));
String emailAddress = input.getValue();
if (emailAddress == null) {
emailAddress = "";
}
DomainNameInfo domainNameInfo =
DomainNameInfo.create(
domainBase.getDomainName(),
domainBase.getRepoId(),
domainBase.getCurrentSponsorRegistrarId(),
emailAddress);
output.output(domainNameInfo);
}
}));
}
static PCollection<Subdomain> readFromBigQuery(Spec11PipelineOptions options, Pipeline pipeline) {
static PCollection<DomainNameInfo> readFromBigQuery(
Spec11PipelineOptions options, Pipeline pipeline) {
return pipeline.apply(
"Read active domains from BigQuery",
BigQueryIO.read(Subdomain::parseFromRecord)
BigQueryIO.read(DomainNameInfo::parseFromRecord)
.fromQuery(
SqlTemplate.create(getQueryFromFile(Spec11Pipeline.class, "subdomains.sql"))
SqlTemplate.create(getQueryFromFile(Spec11Pipeline.class, "domain_name_infos.sql"))
.put("PROJECT_ID", options.getProject())
.put("DATASTORE_EXPORT_DATASET", "latest_datastore_export")
.put("REGISTRAR_TABLE", "Registrar")
.put("DOMAIN_BASE_TABLE", "DomainBase")
.build())
.withCoder(SerializableCoder.of(Subdomain.class))
.withCoder(SerializableCoder.of(DomainNameInfo.class))
.usingStandardSql()
.withoutValidation()
.withTemplateCompatibility());
}
private static Subdomain parseRow(Object[] row) {
DomainBase domainBase = (DomainBase) row[0];
String emailAddress = (String) row[1];
if (emailAddress == null) {
emailAddress = "";
}
return Subdomain.create(
domainBase.getDomainName(),
domainBase.getRepoId(),
domainBase.getCurrentSponsorClientId(),
emailAddress);
private static KV<String, String> parseRow(Object[] row) {
return KV.of((String) row[0], (String) row[1]);
}
static void saveToSql(
PCollection<KV<Subdomain, ThreatMatch>> threatMatches, Spec11PipelineOptions options) {
PCollection<KV<DomainNameInfo, ThreatMatch>> threatMatches, Spec11PipelineOptions options) {
String transformId = "Spec11 Threat Matches";
LocalDate date = LocalDate.parse(options.getDate(), ISODateTimeFormat.date());
threatMatches.apply(
"Write to Sql: " + transformId,
RegistryJpaIO.<KV<Subdomain, ThreatMatch>>write()
RegistryJpaIO.<KV<DomainNameInfo, ThreatMatch>>write()
.withName(transformId)
.withBatchSize(options.getSqlWriteBatchSize())
.withShards(options.getSqlWriteShards())
.withJpaConverter(
(kv) -> {
Subdomain subdomain = kv.getKey();
DomainNameInfo domainNameInfo = kv.getKey();
return new Spec11ThreatMatch.Builder()
.setThreatTypes(
ImmutableSet.of(ThreatType.valueOf(kv.getValue().threatType())))
.setCheckDate(date)
.setDomainName(subdomain.domainName())
.setDomainRepoId(subdomain.domainRepoId())
.setRegistrarId(subdomain.registrarId())
.setDomainName(domainNameInfo.domainName())
.setDomainRepoId(domainNameInfo.domainRepoId())
.setRegistrarId(domainNameInfo.registrarId())
.build();
}));
}
static void saveToGcs(
PCollection<KV<Subdomain, ThreatMatch>> threatMatches, Spec11PipelineOptions options) {
PCollection<KV<DomainNameInfo, ThreatMatch>> threatMatches, Spec11PipelineOptions options) {
threatMatches
.apply(
"Map registrar ID to email/ThreatMatch pair",
@@ -185,7 +210,7 @@ public class Spec11Pipeline implements Serializable {
TypeDescriptors.kvs(
TypeDescriptors.strings(), TypeDescriptor.of(EmailAndThreatMatch.class)))
.via(
(KV<Subdomain, ThreatMatch> kv) ->
(KV<DomainNameInfo, ThreatMatch> kv) ->
KV.of(
kv.getKey().registrarId(),
EmailAndThreatMatch.create(
@@ -196,15 +221,15 @@ public class Spec11Pipeline implements Serializable {
MapElements.into(TypeDescriptors.strings())
.via(
(KV<String, Iterable<EmailAndThreatMatch>> kv) -> {
String clientId = kv.getKey();
String registrarId = kv.getKey();
checkArgument(
kv.getValue().iterator().hasNext(),
String.format(
"Registrar with ID %s had no corresponding threats", clientId));
"Registrar with ID %s had no corresponding threats", registrarId));
String email = kv.getValue().iterator().next().email();
JSONObject output = new JSONObject();
try {
output.put(REGISTRAR_CLIENT_ID_FIELD, clientId);
output.put(REGISTRAR_CLIENT_ID_FIELD, registrarId);
output.put(REGISTRAR_EMAIL_FIELD, email);
JSONArray threatMatchArray = new JSONArray();
for (EmailAndThreatMatch emailAndThreatMatch : kv.getValue()) {
@@ -228,7 +253,7 @@ public class Spec11Pipeline implements Serializable {
options.getReportingBucketUrl(),
getSpec11ReportFilePath(LocalDate.parse(options.getDate()))))
.withoutSharding()
.withHeader("Map from registrar email / name to detected subdomain threats:"));
.withHeader("Map from registrar email / name to detected domain name threats:"));
}
public static void main(String[] args) {

View File

@@ -632,7 +632,7 @@ public class BigqueryConnection implements AutoCloseable {
private static String summarizeCompletedJob(Job job) {
JobStatistics stats = job.getStatistics();
return String.format(
"Job took %,.3f seconds after a %,.3f second delay and processed %,d bytes (%s)",
"Job took %,.3f seconds after a %,.3f second delay and processed %,d bytes (%s).",
(stats.getEndTime() - stats.getStartTime()) / 1000.0,
(stats.getStartTime() - stats.getCreationTime()) / 1000.0,
stats.getTotalBytesProcessed(),
@@ -706,17 +706,17 @@ public class BigqueryConnection implements AutoCloseable {
}
/**
* Helper that creates a dataset with this name if it doesn't already exist, and returns true
* if creation took place.
* Helper that creates a dataset with this name if it doesn't already exist, and returns true if
* creation took place.
*/
public boolean createDatasetIfNeeded(String datasetName) throws IOException {
private boolean createDatasetIfNeeded(String datasetName) throws IOException {
if (!checkDatasetExists(datasetName)) {
bigquery.datasets()
.insert(getProjectId(), new Dataset().setDatasetReference(new DatasetReference()
.setProjectId(getProjectId())
.setDatasetId(datasetName)))
.execute();
logger.atInfo().log("Created dataset: %s:%s\n", getProjectId(), datasetName);
logger.atInfo().log("Created dataset: %s: %s.", getProjectId(), datasetName);
return true;
}
return false;
@@ -732,9 +732,8 @@ public class BigqueryConnection implements AutoCloseable {
.setDefaultDataset(getDataset())
.setDestinationTable(table))));
} catch (BigqueryJobFailureException e) {
if (e.getReason().equals("duplicate")) {
// Table already exists.
} else {
if (!e.getReason().equals("duplicate")) {
// Throw if it failed for any reason other than table already existing.
throw e;
}
}

View File

@@ -116,7 +116,7 @@ public class CheckedBigquery {
.setTableReference(table))
.execute();
logger.atInfo().log(
"Created BigQuery table %s:%s.%s",
"Created BigQuery table %s:%s.%s.",
table.getProjectId(), table.getDatasetId(), table.getTableId());
} catch (IOException e) {
// Swallow errors about a table that exists, and throw any other ones.

View File

@@ -0,0 +1,79 @@
// Copyright 2021 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.config;
import com.google.api.gax.core.FixedCredentialsProvider;
import com.google.cloud.tasks.v2.CloudTasksClient;
import com.google.cloud.tasks.v2.CloudTasksSettings;
import dagger.Module;
import dagger.Provides;
import google.registry.config.CredentialModule.DefaultCredential;
import google.registry.config.RegistryConfig.Config;
import google.registry.util.CloudTasksUtils;
import google.registry.util.CloudTasksUtils.GcpCloudTasksClient;
import google.registry.util.CloudTasksUtils.SerializableCloudTasksClient;
import google.registry.util.GoogleCredentialsBundle;
import google.registry.util.Retrier;
import java.io.IOException;
import java.io.Serializable;
import java.util.function.Supplier;
import javax.inject.Singleton;
/**
* A {@link Module} that provides {@link CloudTasksUtils}.
*
* <p>The class itself cannot be annotated as {@code Inject} because its requested dependencies use
* the {@link Config} qualifier which is not available in the {@code util} package.
*/
@Module
public abstract class CloudTasksUtilsModule {
@Singleton
@Provides
public static CloudTasksUtils provideCloudTasksUtils(
@Config("projectId") String projectId,
@Config("locationId") String locationId,
SerializableCloudTasksClient client,
Retrier retrier) {
return new CloudTasksUtils(retrier, projectId, locationId, client);
}
// Provides a supplier instead of using a Dagger @Provider because the latter is not serializable.
@Provides
public static Supplier<CloudTasksClient> provideCloudTasksClientSupplier(
@DefaultCredential GoogleCredentialsBundle credentials) {
return (Supplier<CloudTasksClient> & Serializable)
() -> {
CloudTasksClient client;
try {
client =
CloudTasksClient.create(
CloudTasksSettings.newBuilder()
.setCredentialsProvider(
FixedCredentialsProvider.create(credentials.getGoogleCredentials()))
.build());
} catch (IOException e) {
throw new RuntimeException(e);
}
return client;
};
}
@Provides
public static SerializableCloudTasksClient provideSerializableCloudTasksClient(
final Supplier<CloudTasksClient> clientSupplier) {
return new GcpCloudTasksClient(clientSupplier);
}
}

View File

@@ -60,7 +60,7 @@ import org.joda.time.Duration;
* <p>This class does not represent the total configuration of the Nomulus service. It's <b>only
* meant for settings that need to be configured <i>once</i></b>. Settings which may be subject to
* change in the future, should instead be retrieved from Datastore. The {@link
* google.registry.model.registry.Registry Registry} class is one such example of this.
* google.registry.model.tld.Registry Registry} class is one such example of this.
*
* <p>Note: Only settings that are actually configurable belong in this file. It's not a catch-all
* for anything widely used throughout the code base.
@@ -105,6 +105,12 @@ public final class RegistryConfig {
return config.appEngine.projectId;
}
@Provides
@Config("locationId")
public static String provideLocationId(RegistryConfigSettings config) {
return config.appEngine.locationId;
}
/**
* The filename of the logo to be displayed in the header of the registrar console.
*
@@ -416,17 +422,6 @@ public final class RegistryConfig {
return Optional.ofNullable(config.cloudDns.servicePath);
}
/**
* Returns size of Google Cloud Storage client connection buffer in bytes.
*
* @see google.registry.gcs.GcsUtils
*/
@Provides
@Config("gcsBufferSize")
public static int provideGcsBufferSize() {
return 1024 * 1024;
}
/**
* Returns the email address of the admin account on the G Suite app used to perform
* administrative actions.
@@ -1143,8 +1138,8 @@ public final class RegistryConfig {
}
/**
* Returns the clientId of the registrar that admins are automatically logged in as if they
* aren't otherwise associated with one.
* Returns the ID of the registrar that admins are automatically logged in as if they aren't
* otherwise associated with one.
*/
@Provides
@Config("registryAdminClientId")
@@ -1278,21 +1273,51 @@ public final class RegistryConfig {
@Provides
@Config("expirationWarningDays")
public static int provideDaysToExpiration(RegistryConfigSettings config) {
public static int provideExpirationWarningDays(RegistryConfigSettings config) {
return config.sslCertificateValidation.expirationWarningDays;
}
@Provides
@Config("expirationWarningIntervalDays")
public static int provideExpirationWarningIntervalDays(RegistryConfigSettings config) {
return config.sslCertificateValidation.expirationWarningIntervalDays;
}
@Provides
@Config("minimumRsaKeyLength")
public static int provideMinimumRsaKeyLength(RegistryConfigSettings config) {
return config.sslCertificateValidation.minimumRsaKeyLength;
}
@Provides
@Config("expirationWarningEmailBodyText")
public static String provideExpirationWarningEmailBodyText(RegistryConfigSettings config) {
return config.sslCertificateValidation.expirationWarningEmailBodyText;
}
@Provides
@Config("expirationWarningEmailSubjectText")
public static String provideExpirationWarningEmailSubjectText(RegistryConfigSettings config) {
return config.sslCertificateValidation.expirationWarningEmailSubjectText;
}
@Provides
@Config("allowedEcdsaCurves")
public static ImmutableSet<String> provideAllowedEcdsaCurves(RegistryConfigSettings config) {
return ImmutableSet.copyOf(config.sslCertificateValidation.allowedEcdsaCurves);
}
@Provides
@Config("minMonthsBeforeWipeOut")
public static int provideMinMonthsBeforeWipeOut(RegistryConfigSettings config) {
return config.contactHistory.minMonthsBeforeWipeOut;
}
@Provides
@Config("wipeOutQueryBatchSize")
public static int provideWipeOutQueryBatchSize(RegistryConfigSettings config) {
return config.contactHistory.wipeOutQueryBatchSize;
}
}
/** Returns the App Engine project ID, which is based off the environment name. */
@@ -1387,8 +1412,8 @@ public final class RegistryConfig {
/**
* Returns the amount of time a domain label list should be cached in memory before expiring.
*
* @see google.registry.model.registry.label.ReservedList
* @see google.registry.model.registry.label.PremiumList
* @see google.registry.model.tld.label.ReservedList
* @see google.registry.model.tld.label.PremiumList
*/
public static Duration getDomainLabelListCacheDuration() {
return Duration.standardSeconds(CONFIG_SETTINGS.get().caching.domainLabelCachingSeconds);
@@ -1487,21 +1512,6 @@ public final class RegistryConfig {
return CONFIG_SETTINGS.get().hibernate.hikariIdleTimeout;
}
/**
* Returns whether to replicate cloud SQL transactions to datastore.
*
* <p>If true, all cloud SQL transactions will be persisted as TransactionEntity objects in the
* Transaction table and replayed against datastore in a cron job.
*/
public static boolean getCloudSqlReplicateTransactions() {
return CONFIG_SETTINGS.get().cloudSql.replicateTransactions;
}
@VisibleForTesting
public static void overrideCloudSqlReplicateTransactions(boolean replicateTransactions) {
CONFIG_SETTINGS.get().cloudSql.replicateTransactions = replicateTransactions;
}
/** Returns the roid suffix to be used for the roids of all contacts and hosts. */
public static String getContactAndHostRoidSuffix() {
return CONFIG_SETTINGS.get().registryPolicy.contactAndHostRoidSuffix;

View File

@@ -41,10 +41,12 @@ public class RegistryConfigSettings {
public Keyring keyring;
public RegistryTool registryTool;
public SslCertificateValidation sslCertificateValidation;
public ContactHistory contactHistory;
/** Configuration options that apply to the entire App Engine project. */
public static class AppEngine {
public String projectId;
public String locationId;
public boolean isLocal;
public String defaultServiceUrl;
public String backendServiceUrl;
@@ -126,7 +128,6 @@ public class RegistryConfigSettings {
// TODO(05012021): remove username field after it is removed from all yaml files.
public String username;
public String instanceConnectionName;
public boolean replicateTransactions;
}
/** Configuration for Apache Beam (Cloud Dataflow). */
@@ -228,7 +229,16 @@ public class RegistryConfigSettings {
public static class SslCertificateValidation {
public Map<String, Integer> maxValidityDaysSchedule;
public int expirationWarningDays;
public int expirationWarningIntervalDays;
public int minimumRsaKeyLength;
public Set<String> allowedEcdsaCurves;
public String expirationWarningEmailBodyText;
public String expirationWarningEmailSubjectText;
}
/** Configuration for contact history. */
public static class ContactHistory {
public int minMonthsBeforeWipeOut;
public int wipeOutQueryBatchSize;
}
}

View File

@@ -8,6 +8,10 @@
appEngine:
# Globally unique App Engine project ID
projectId: registry-project-id
# Location of the App engine project, note that us-central1 and europe-west1 are special in that
# they are used without the trailing number in App Engine commands and Google Cloud Console.
# See: https://cloud.google.com/appengine/docs/locations
locationId: registry-location-id
# whether to use local/test credentials when connecting to the servers
isLocal: true
@@ -227,9 +231,6 @@ cloudSql:
jdbcUrl: jdbc:postgresql://localhost
# This name is used by Cloud SQL when connecting to the database.
instanceConnectionName: project-id:region:instance-id
# Set this to true to replicate cloud SQL transactions to datastore in the
# background.
replicateTransactions: false
cloudDns:
# Set both properties to null in Production.
@@ -441,6 +442,13 @@ registryTool:
# OAuth client secret used by the tool.
clientSecret: YOUR_CLIENT_SECRET
# Configuration options for handling contact history.
contactHistory:
# The number of months that a ContactHistory entity should be stored in the database.
minMonthsBeforeWipeOut: 18
# The batch size for querying ContactHistory table in the database.
wipeOutQueryBatchSize: 500
# Configuration options for checking SSL certificates.
sslCertificateValidation:
# A map specifying the maximum amount of days the certificate can be valid.
@@ -452,6 +460,39 @@ sslCertificateValidation:
# The number of days before a certificate expires that indicates the
# certificate is nearing expiration and warnings should be sent.
expirationWarningDays: 30
# The minimum number of days between two successive expiring notification emails.
expirationWarningIntervalDays: 15
# Text for expiring certificate notification email subject.
expirationWarningEmailSubjectText: "[Important] Expiring SSL certificate for Google Registry EPP connection"
# Text for expiring certificate notification email body that accepts 3 parameters:
# registrar name, certificate type, and expiration date, respectively.
expirationWarningEmailBodyText: |
Dear %1$s,
We would like to inform you that your %2$s SSL certificate will expire at %3$s. Please take note that using expired certificates will prevent successful Registry login.
Kindly update your production account certificate within the support console using the following steps:
1. Navigate to support.registry.google and login using your %4$s@registry.google credentials.
* If this is your first time logging in, you will be prompted to reset your password, so please keep your new password safe.
* If you are already logged in with some other Google account(s) but not your %4$s@registry.google account, you need to click on
“Add Account” and login using your %4$s@registry.google credentials.
2. Select “Settings > Security” from the left navigation bar.
3. Click “Edit” on the top left corner.
4. Enter your full certificate string (including lines -----BEGIN CERTIFICATE----- and -----END CERTIFICATE-----) in the box.
5. Click “Save”. If there are validation issues with the form, you will be prompted to fix them and click “Save” again.
A failover SSL certificate can also be added in order to prevent connection issues once your main certificate expires. Connecting with either of the certificates will work with our production EPP server.
Further information about our EPP connection requirements can be found in section 9.2 in the updated Technical Guide in your Google Drive folder.
Note that account certificate changes take a few minutes to become effective and that the existing connections will remain unaffected by the change.
If you also would like to update your OT&E account certificate, please send an email from your primary or technical contact to registry-support@google.com and include the full certificate string (including lines -----BEGIN CERTIFICATE----- and -----END CERTIFICATE-----).
Regards,
Google Registry
# The minimum number of bits an RSA key must contain.
minimumRsaKeyLength: 2048
# The ECDSA curves that are allowed for public keys.

View File

@@ -14,14 +14,10 @@
package google.registry.cron;
import static com.google.appengine.api.taskqueue.QueueFactory.getQueue;
import static com.google.appengine.api.taskqueue.TaskOptions.Builder.withUrl;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Predicates.in;
import static com.google.common.base.Predicates.not;
import static com.google.common.base.Strings.nullToEmpty;
import static com.google.common.collect.ImmutableSet.toImmutableSet;
import static com.google.common.collect.Iterables.getFirst;
import static com.google.common.collect.Multimaps.filterKeys;
import static com.google.common.net.MediaType.PLAIN_TEXT_UTF_8;
import static google.registry.cron.CronModule.ENDPOINT_PARAM;
@@ -31,26 +27,29 @@ import static google.registry.cron.CronModule.FOR_EACH_TEST_TLD_PARAM;
import static google.registry.cron.CronModule.JITTER_SECONDS_PARAM;
import static google.registry.cron.CronModule.QUEUE_PARAM;
import static google.registry.cron.CronModule.RUN_IN_EMPTY_PARAM;
import static google.registry.model.registry.Registries.getTldsOfType;
import static google.registry.model.registry.Registry.TldType.REAL;
import static google.registry.model.registry.Registry.TldType.TEST;
import static google.registry.model.tld.Registries.getTldsOfType;
import static google.registry.model.tld.Registry.TldType.REAL;
import static google.registry.model.tld.Registry.TldType.TEST;
import static java.util.concurrent.TimeUnit.SECONDS;
import com.google.appengine.api.taskqueue.Queue;
import com.google.appengine.api.taskqueue.TaskHandle;
import com.google.appengine.api.taskqueue.TaskOptions;
import com.google.cloud.tasks.v2.Task;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.ImmutableListMultimap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Multimap;
import com.google.common.collect.Streams;
import com.google.common.flogger.FluentLogger;
import com.google.protobuf.Timestamp;
import google.registry.request.Action;
import google.registry.request.Action.Service;
import google.registry.request.Parameter;
import google.registry.request.ParameterMap;
import google.registry.request.RequestParameters;
import google.registry.request.Response;
import google.registry.request.auth.Auth;
import google.registry.util.TaskQueueUtils;
import google.registry.util.Clock;
import google.registry.util.CloudTasksUtils;
import java.time.Instant;
import java.util.Optional;
import java.util.Random;
import java.util.stream.Stream;
@@ -105,7 +104,8 @@ public final class TldFanoutAction implements Runnable {
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
@Inject TaskQueueUtils taskQueueUtils;
@Inject Clock clock;
@Inject CloudTasksUtils cloudTasksUtils;
@Inject Response response;
@Inject @Parameter(ENDPOINT_PARAM) String endpoint;
@Inject @Parameter(QUEUE_PARAM) String queue;
@@ -115,7 +115,9 @@ public final class TldFanoutAction implements Runnable {
@Inject @Parameter(EXCLUDE_PARAM) ImmutableSet<String> excludes;
@Inject @Parameter(JITTER_SECONDS_PARAM) Optional<Integer> jitterSeconds;
@Inject @ParameterMap ImmutableListMultimap<String, String> params;
@Inject TldFanoutAction() {}
@Inject
TldFanoutAction() {}
@Override
public void run() {
@@ -126,8 +128,7 @@ public final class TldFanoutAction implements Runnable {
runInEmpty || forEachTestTld || forEachRealTld,
"At least one of runInEmpty, forEachTestTld, forEachRealTld must be given");
checkArgument(
!(runInEmpty && !excludes.isEmpty()),
"Can't specify 'exclude' with 'runInEmpty'");
!(runInEmpty && !excludes.isEmpty()), "Can't specify 'exclude' with 'runInEmpty'");
ImmutableSet<String> tlds =
runInEmpty
? ImmutableSet.of("")
@@ -137,42 +138,49 @@ public final class TldFanoutAction implements Runnable {
.filter(not(in(excludes)))
.collect(toImmutableSet());
Multimap<String, String> flowThruParams = filterKeys(params, not(in(CONTROL_PARAMS)));
Queue taskQueue = getQueue(queue);
StringBuilder outputPayload =
new StringBuilder(
String.format("OK: Launched the following %d tasks in queue %s\n", tlds.size(), queue));
logger.atInfo().log("Launching %d tasks in queue %s", tlds.size(), queue);
logger.atInfo().log("Launching %d tasks in queue %s.", tlds.size(), queue);
if (tlds.isEmpty()) {
logger.atWarning().log("No TLDs to fan-out!");
}
for (String tld : tlds) {
TaskOptions taskOptions = createTaskOptions(tld, flowThruParams);
TaskHandle taskHandle = taskQueueUtils.enqueue(taskQueue, taskOptions);
Task task = createTask(tld, flowThruParams);
Task createdTask = cloudTasksUtils.enqueue(queue, task);
outputPayload.append(
String.format(
"- Task: '%s', tld: '%s', endpoint: '%s'\n",
taskHandle.getName(), tld, taskOptions.getUrl()));
createdTask.getName(), tld, createdTask.getAppEngineHttpRequest().getRelativeUri()));
logger.atInfo().log(
"Task: '%s', tld: '%s', endpoint: '%s'", taskHandle.getName(), tld, taskOptions.getUrl());
"Task: '%s', tld: '%s', endpoint: '%s'.",
createdTask.getName(), tld, createdTask.getAppEngineHttpRequest().getRelativeUri());
}
response.setContentType(PLAIN_TEXT_UTF_8);
response.setPayload(outputPayload.toString());
}
private TaskOptions createTaskOptions(String tld, Multimap<String, String> params) {
TaskOptions options =
withUrl(endpoint)
.countdownMillis(
jitterSeconds
.map(seconds -> random.nextInt((int) SECONDS.toMillis(seconds)))
.orElse(0));
private Task createTask(String tld, Multimap<String, String> params) {
if (!tld.isEmpty()) {
options.param(RequestParameters.PARAM_TLD, tld);
params = ArrayListMultimap.create(params);
params.put(RequestParameters.PARAM_TLD, tld);
}
for (String param : params.keySet()) {
// TaskOptions.param() does not accept null values.
options.param(param, nullToEmpty(getFirst(params.get(param), null)));
}
return options;
Instant scheduleTime =
Instant.ofEpochMilli(
clock
.nowUtc()
.plusMillis(
jitterSeconds
.map(seconds -> random.nextInt((int) SECONDS.toMillis(seconds)))
.orElse(0))
.getMillis());
return Task.newBuilder(
CloudTasksUtils.createPostTask(endpoint, Service.BACKEND.toString(), params))
.setScheduleTime(
Timestamp.newBuilder()
.setSeconds(scheduleTime.getEpochSecond())
.setNanos(scheduleTime.getNano())
.build())
.build();
}
}

View File

@@ -20,7 +20,7 @@ import static google.registry.dns.DnsConstants.DNS_PULL_QUEUE_NAME;
import static google.registry.dns.DnsConstants.DNS_TARGET_CREATE_TIME_PARAM;
import static google.registry.dns.DnsConstants.DNS_TARGET_NAME_PARAM;
import static google.registry.dns.DnsConstants.DNS_TARGET_TYPE_PARAM;
import static google.registry.model.registry.Registries.assertTldExists;
import static google.registry.model.tld.Registries.assertTldExists;
import static google.registry.request.RequestParameters.PARAM_TLD;
import static google.registry.util.DomainNameUtils.getTldFromDomainName;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
@@ -38,7 +38,7 @@ import com.google.common.flogger.FluentLogger;
import com.google.common.net.InternetDomainName;
import com.google.common.util.concurrent.RateLimiter;
import google.registry.dns.DnsConstants.TargetType;
import google.registry.model.registry.Registries;
import google.registry.model.tld.Registries;
import google.registry.util.Clock;
import google.registry.util.NonFinalForTesting;
import google.registry.util.SystemClock;
@@ -107,7 +107,7 @@ public class DnsQueue {
private TaskHandle addToQueue(
TargetType targetType, String targetName, String tld, Duration countdown) {
logger.atInfo().log(
"Adding task type=%s, target=%s, tld=%s to pull queue %s (%d tasks currently on queue)",
"Adding task type=%s, target=%s, tld=%s to pull queue %s (%d tasks currently on queue).",
targetType, targetName, tld, DNS_PULL_QUEUE_NAME, queue.fetchStatistics().getNumTasks());
return queue.add(
TaskOptions.Builder.withDefaults()
@@ -166,7 +166,7 @@ public class DnsQueue {
"There are %d tasks in the DNS queue '%s'.", numTasks, DNS_PULL_QUEUE_NAME);
return queue.leaseTasks(leaseDuration.getMillis(), MILLISECONDS, leaseTasksBatchSize);
} catch (TransientFailureException | DeadlineExceededException e) {
logger.atSevere().withCause(e).log("Failed leasing tasks too fast");
logger.atSevere().withCause(e).log("Failed leasing tasks too fast.");
return ImmutableList.of();
}
}
@@ -176,7 +176,7 @@ public class DnsQueue {
try {
queue.deleteTask(tasks);
} catch (TransientFailureException | DeadlineExceededException e) {
logger.atSevere().withCause(e).log("Failed deleting tasks too fast");
logger.atSevere().withCause(e).log("Failed deleting tasks too fast.");
}
}
}

View File

@@ -19,7 +19,7 @@ import static com.google.common.base.Preconditions.checkState;
import com.google.common.collect.ImmutableMap;
import com.google.common.flogger.FluentLogger;
import google.registry.dns.writer.DnsWriter;
import google.registry.model.registry.Registry;
import google.registry.model.tld.Registry;
import java.util.Map;
import javax.inject.Inject;

View File

@@ -32,7 +32,7 @@ import google.registry.dns.DnsMetrics.ActionStatus;
import google.registry.dns.DnsMetrics.CommitStatus;
import google.registry.dns.DnsMetrics.PublishStatus;
import google.registry.dns.writer.DnsWriter;
import google.registry.model.registry.Registry;
import google.registry.model.tld.Registry;
import google.registry.request.Action;
import google.registry.request.HttpException.ServiceUnavailableException;
import google.registry.request.Parameter;
@@ -104,7 +104,7 @@ public final class PublishDnsUpdatesAction implements Runnable, Callable<Void> {
new Duration(enqueuedTime, now));
logger.atInfo().log(
"publishDnsWriter latency statistics: TLD: %s, dnsWriter: %s, actionStatus: %s, "
+ "numItems: %d, timeSinceCreation: %s, timeInQueue: %s",
+ "numItems: %d, timeSinceCreation: %s, timeInQueue: %s.",
tld,
dnsWriter,
status,
@@ -144,7 +144,7 @@ public final class PublishDnsUpdatesAction implements Runnable, Callable<Void> {
/** Adds all the domains and hosts in the batch back to the queue to be processed later. */
private void requeueBatch() {
logger.atInfo().log("Requeueing batch for retry");
logger.atInfo().log("Requeueing batch for retry.");
for (String domain : nullToEmpty(domains)) {
dnsQueue.addDomainRefreshTask(domain);
}
@@ -158,14 +158,14 @@ public final class PublishDnsUpdatesAction implements Runnable, Callable<Void> {
// LockIndex should always be within [1, numPublishLocks]
if (lockIndex > numPublishLocks || lockIndex <= 0) {
logger.atSevere().log(
"Lock index should be within [1,%d], got %d instead", numPublishLocks, lockIndex);
"Lock index should be within [1,%d], got %d instead.", numPublishLocks, lockIndex);
return false;
}
// Check if the Registry object's num locks has changed since this task was batched
int registryNumPublishLocks = Registry.get(tld).getNumDnsPublishLocks();
if (registryNumPublishLocks != numPublishLocks) {
logger.atWarning().log(
"Registry numDnsPublishLocks %d out of sync with parameter %d",
"Registry numDnsPublishLocks %d out of sync with parameter %d.",
registryNumPublishLocks, numPublishLocks);
return false;
}
@@ -179,7 +179,7 @@ public final class PublishDnsUpdatesAction implements Runnable, Callable<Void> {
DnsWriter writer = dnsWriterProxy.getByClassNameForTld(dnsWriter, tld);
if (writer == null) {
logger.atWarning().log("Couldn't get writer %s for TLD %s", dnsWriter, tld);
logger.atWarning().log("Couldn't get writer %s for TLD %s.", dnsWriter, tld);
recordActionResult(ActionStatus.BAD_WRITER);
requeueBatch();
return;
@@ -190,11 +190,11 @@ public final class PublishDnsUpdatesAction implements Runnable, Callable<Void> {
for (String domain : nullToEmpty(domains)) {
if (!DomainNameUtils.isUnder(
InternetDomainName.from(domain), InternetDomainName.from(tld))) {
logger.atSevere().log("%s: skipping domain %s not under tld", tld, domain);
logger.atSevere().log("%s: skipping domain %s not under TLD.", tld, domain);
domainsRejected += 1;
} else {
writer.publishDomain(domain);
logger.atInfo().log("%s: published domain %s", tld, domain);
logger.atInfo().log("%s: published domain %s.", tld, domain);
domainsPublished += 1;
}
}
@@ -206,11 +206,11 @@ public final class PublishDnsUpdatesAction implements Runnable, Callable<Void> {
for (String host : nullToEmpty(hosts)) {
if (!DomainNameUtils.isUnder(
InternetDomainName.from(host), InternetDomainName.from(tld))) {
logger.atSevere().log("%s: skipping host %s not under tld", tld, host);
logger.atSevere().log("%s: skipping host %s not under TLD.", tld, host);
hostsRejected += 1;
} else {
writer.publishHost(host);
logger.atInfo().log("%s: published host %s", tld, host);
logger.atInfo().log("%s: published host %s.", tld, host);
hostsPublished += 1;
}
}
@@ -233,7 +233,7 @@ public final class PublishDnsUpdatesAction implements Runnable, Callable<Void> {
tld, dnsWriter, commitStatus, duration, domainsPublished, hostsPublished);
logger.atInfo().log(
"writer.commit() statistics: TLD: %s, dnsWriter: %s, commitStatus: %s, duration: %s, "
+ "domainsPublished: %d, domainsRejected: %d, hostsPublished: %d, hostsRejected: %d",
+ "domainsPublished: %d, domainsRejected: %d, hostsPublished: %d, hostsRejected: %d.",
tld,
dnsWriter,
commitStatus,

View File

@@ -47,8 +47,8 @@ import com.google.common.hash.HashFunction;
import com.google.common.hash.Hashing;
import google.registry.config.RegistryConfig.Config;
import google.registry.dns.DnsConstants.TargetType;
import google.registry.model.registry.Registries;
import google.registry.model.registry.Registry;
import google.registry.model.tld.Registries;
import google.registry.model.tld.Registry;
import google.registry.request.Action;
import google.registry.request.Parameter;
import google.registry.request.auth.Auth;

View File

@@ -31,12 +31,12 @@ package google.registry.dns.writer;
public interface DnsWriter {
/**
* Loads {@code domainName} from Datastore and publishes its NS/DS records to the DNS server.
* Loads {@code domainName} from the database and publishes its NS/DS records to the DNS server.
* Replaces existing records for the exact name supplied with an NS record for each name server
* and a DS record for each delegation signer stored in the registry for the supplied domain name.
* If the domain is deleted or is in a "non-publish" state then any existing records are deleted.
*
* This must NOT actually perform any action, instead it should stage the action so that it's
* <p>This must NOT actually perform any action, instead it should stage the action so that it's
* performed when {@link #commit()} is called.
*
* @param domainName the fully qualified domain name, with no trailing dot
@@ -44,14 +44,14 @@ public interface DnsWriter {
void publishDomain(String domainName);
/**
* Loads {@code hostName} from Datastore and publishes its A/AAAA glue records to the DNS server,
* if it is used as an in-bailiwick nameserver. Orphaned glue records are prohibited. Replaces
* existing records for the exact name supplied, with an A or AAAA record (as appropriate) for
* each address stored in the registry, for the supplied host name. If the host is deleted then
* the existing records are deleted. Assumes that this method will only be called for in-bailiwick
* hosts. The registry does not have addresses for other hosts.
* Loads {@code hostName} from the database and publishes its A/AAAA glue records to the DNS
* server, if it is used as an in-bailiwick nameserver. Orphaned glue records are prohibited.
* Replaces existing records for the exact name supplied, with an A or AAAA record (as
* appropriate) for each address stored in the registry, for the supplied host name. If the host
* is deleted then the existing records are deleted. Assumes that this method will only be called
* for in-bailiwick hosts. The registry does not have addresses for other hosts.
*
* This must NOT actually perform any action, instead it should stage the action so that it's
* <p>This must NOT actually perform any action, instead it should stage the action so that it's
* performed when {@link #commit()} is called.
*
* @param hostName the fully qualified host name, with no trailing dot

Some files were not shown because too many files have changed in this diff Show More