1
0
mirror of https://github.com/google/nomulus synced 2026-06-09 16:33:02 +00:00

Compare commits

...

20 Commits

Author SHA1 Message Date
Lai Jiang
c584de9f72 Respect certificate validity period (#391)
Client SSL handler already performs the necessary validation. Only tests are
added.

Server SSL handler does not currently check for the validity period of
the client certificate as the insecure trust manager is used. This PR
added the check but does not actually terminate the connection yet. It
will log the expired certificates so that we can contact the registrars
to update them.

Once we are certain that all certificates are updated, we can turn off
dryrun mode.

We should also consider checking if the certificate has too long a
validity period as it defeats the purpose of using regularly updated
certificates to deprecate insecure cipher suites.
2019-11-27 16:08:38 -05:00
Shicong Huang
9be5091c84 Add entity for reserved list (#381)
This PR added the Cloud SQL entity for reserved list.
2019-11-26 16:51:41 -05:00
Michael Muller
28499d23a0 Print filenames that need to be reformatted (#386)
* Print filenames that need to be reformatted

Print the names of all java files that need reformatting during the check and
reformat operations.
2019-11-26 13:20:27 -05:00
Ben McIlwain
961d7e88f4 Use Maps.transformEntries() utility method to improve Map composition (#387)
* Use Maps.transformEntries() utility method to improve Map composition
2019-11-26 12:20:00 -05:00
Weimin Yu
215de62fa7 Stop publish Cloud SQL schema jar to maven repo (#383)
* Stop publish Cloud SQL schema jar to maven repo

The original purpose of the maven publication is for
use in server/schema compatibility tests. A commandline
flag can direct a test run to use different versions of
the schema jar. However, this won't work due to dependency
locking.
2019-11-25 18:23:02 -05:00
Lai Jiang
05d56fe1a2 Remove SSL initializer from the prober (#378)
The prober now uses the common SSL initializer in the networking
subproject.

Also changed both initializers to take an ImmutableList of certificates
other than an array of those, for better immutability.

I have no idea where these lockfile changes are coming from. They seem
to be pure noise as far as code review is concerned.
2019-11-22 17:46:06 -05:00
sarahcaseybot
e318f47fc6 Add a cursor for tracking monthly uploads of ICANN report (#343)
* Add a cursor for tracking monthly uploads of the transaction report to ICANN

* Add cursors to track activity, transaction, and manifest report uploads.

* Address comments

* Add @Nullable annotation to manifestCursor

* Add lock and batch load cursors.

* Add string formatting, autovalue CursorInfo object, and handling for null cursors

* Add some helper functions for loadCursors and restructure to require less round trips to the database

* Switch new cursors to be created with cursorTime at first of next month
2019-11-22 17:40:31 -05:00
Lai Jiang
cc5f62587e Make dev project configurable (#371)
* Make dev project configurable

We should not hardcode our dev project in the public config file.

* Remove the use of .ext when using external properties

They are only needed when defining properties.
2019-11-22 16:20:07 -05:00
Lai Jiang
02846bcbdd No-op: Use nicer HCL2 syntax. (#384)
Generated with perl -pi -e 's/\"\$\{([a-zA-Z0-9._-]*)\}\"/$1/g' $(find ./ -name '*.tf')

Copied from cl/282012376.
2019-11-22 16:08:56 -05:00
Ben McIlwain
c920f709ef Update the Registries cache to leverage/populate the Registry cache (#382)
* Update the Registries cache to leverage/populate the Registry cache

This is accomplished by also providing a loadAll() method on the Registry cache
that can be used to load an entire batch of Registry objects at once.

This improves efficiency, because now, any operation on Registries that loads
all the Registry entities (getTlds(), getTldsOfType(), and getTldEntities()), is
plumbed through the Registry cache, therefore loading it from that cache if it
exists and only hitting the DB if not. If not, this populates the Registry cache
upon loading, so that subsequent calls to Registry.get() will now hit the cache
instead of the DB.

To give a concrete example, the following code:

    for (String tld : Registries.getTlds()) {
      // ...
      doSomethingWith(Registry.get(tld));
      // ...
    }

is now much more efficient, because the initial call to Registries.getTlds()
populates all the entities in cache, and the subsequent individual calls to
Registry.get(tld) now retrieve them from the cache. Prior to this change,
Registries.getTlds() did not populate the Registry cache, and each subsequent
Registry.get(tld) had the potential to trigger an individual round-trip to the
DB, which is obviously bad for performance.
2019-11-22 14:47:09 -05:00
Ben McIlwain
ad9daac1ab Update premium and reserved list management docs (#380)
* Update premium and reserved list management docs

They were a little bit out of date.
2019-11-21 16:44:45 -05:00
Weimin Yu
9f0e24132a Break circular dependency between core and util (#379)
* Break circular dependency between core and util

Created a new :common project and moved a minimum
number of classes to break the circular dependency
between the two projects. This gets rid of the
gradle lint dependency warnings.

Also separated api classes and testing helpers into
separate source sets in :common so that testing
classes may be restricted to test configurations.
2019-11-21 15:36:55 -05:00
Shicong Huang
98414cb7cb Add a test to verify generated schema (#377) 2019-11-21 13:37:37 -05:00
Lai Jiang
6af1896362 Refactor common code used by the proxy and the prober (#375) 2019-11-20 12:42:44 -05:00
Weimin Yu
68887d427f Allow schema-loading from arbitrary url in tests (#374)
* Allow schema-loading from arbitrary url in tests

Server/Schema compatibility tests must be able to load different versions
of the SQL schema. This change allows test runners to override the
schema location using a system property.

Note: due to dependency-locking, we cannot manipulate the dependencies
closure in the build script to load different schema jars. The jars
must not be on the classpath.
2019-11-20 12:22:48 -05:00
Ben McIlwain
8a06ef09c0 Add a new method to load all Registry entities of a given type (#373)
* Add a new method to load all Registry entities of a given type

This is useful for things that need to know more than just the TLD strings
themselves, which is all Registries currently provides.
2019-11-18 17:40:42 -05:00
Shicong Huang
9e0368b77c Enable JpaTransactionManager in all environment (#352)
* Enable JpaTransactionManager in all environment

* Refactor to have a single place to create JpaTm
2019-11-18 14:53:49 -05:00
Weimin Yu
05c45da07a Use psql 11 docker image in all tests (#372)
* Use psql 11 docker image in all tests
2019-11-18 14:08:58 -05:00
Weimin Yu
365c5da942 Require explict tag when starting psql docker (#368)
* Require explict tag when starting psql docker

Defined a util class to return docker tag of desired PSQL version.
Class is defined in ':db' and shared by ':db' and ':core'. Used
an artifact declaration to exclude unnecesary compile dependencies.

Added a presubmit check for instantiations without explicit tag.
2019-11-18 11:33:26 -05:00
Weimin Yu
2cc2571375 Update schema deployment doc and flyway tool (#363)
* Update schema deployment doc and flyway tool

Disabled Flyway Gradle tasks with side effects on Cloud SQL
instances. They can still be used on local databases.

Also switched Flyway Gradle tasks to get credentials from
new locations (in domain-registry-dev).

Updated README file on schema push process. Also reformatted
the entire file.
2019-11-15 11:44:21 -05:00
166 changed files with 3406 additions and 4209 deletions

View File

@@ -0,0 +1,3 @@
# This is a Gradle generated file for dependency locking.
# Manual edits can break the build and are not advised.
# This file is expected to be part of source control.

View File

@@ -0,0 +1,3 @@
# This is a Gradle generated file for dependency locking.
# Manual edits can break the build and are not advised.
# This file is expected to be part of source control.

View File

@@ -0,0 +1,61 @@
# This is a Gradle generated file for dependency locking.
# Manual edits can break the build and are not advised.
# This file is expected to be part of source control.
aopalliance:aopalliance:1.0
args4j:args4j:2.0.23
com.fasterxml.jackson.core:jackson-core:2.9.9
com.google.api-client:google-api-client:1.27.0
com.google.api.grpc:proto-google-common-protos:1.12.0
com.google.api.grpc:proto-google-iam-v1:0.12.0
com.google.api:api-common:1.7.0
com.google.api:gax-httpjson:0.52.1
com.google.api:gax:1.35.1
com.google.apis:google-api-services-storage:v1-rev20181013-1.27.0
com.google.auth:google-auth-library-credentials:0.16.1
com.google.auth:google-auth-library-oauth2-http:0.16.1
com.google.auto.value:auto-value-annotations:1.6.3
com.google.cloud:google-cloud-core-http:1.59.0
com.google.cloud:google-cloud-core:1.59.0
com.google.cloud:google-cloud-storage:1.59.0
com.google.code.findbugs:jsr305:3.0.2
com.google.code.gson:gson:2.7
com.google.common.html.types:types:1.0.4
com.google.errorprone:error_prone_annotations:2.3.2
com.google.guava:failureaccess:1.0.1
com.google.guava:guava:28.1-jre
com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava
com.google.gwt:gwt-user:2.8.0-beta1
com.google.http-client:google-http-client-appengine:1.27.0
com.google.http-client:google-http-client-jackson2:1.30.1
com.google.http-client:google-http-client:1.30.1
com.google.inject.extensions:guice-multibindings:4.1.0
com.google.inject:guice:4.1.0
com.google.j2objc:j2objc-annotations:1.3
com.google.oauth-client:google-oauth-client:1.27.0
com.google.protobuf:protobuf-java-util:3.6.1
com.google.protobuf:protobuf-java:3.6.1
com.google.template:soy:2018-03-14
com.ibm.icu:icu4j:57.1
commons-codec:commons-codec:1.11
commons-logging:commons-logging:1.2
io.grpc:grpc-context:1.19.0
io.opencensus:opencensus-api:0.21.0
io.opencensus:opencensus-contrib-http-util:0.21.0
javax.annotation:javax.annotation-api:1.2
javax.annotation:jsr250-api:1.0
javax.inject:javax.inject:1
javax.validation:validation-api:1.0.0.GA
joda-time:joda-time:2.9.2
org.apache.commons:commons-lang3:3.8.1
org.apache.commons:commons-text:1.6
org.apache.httpcomponents:httpclient:4.5.8
org.apache.httpcomponents:httpcore:4.4.11
org.checkerframework:checker-qual:2.8.1
org.codehaus.mojo:animal-sniffer-annotations:1.18
org.json:json:20160212
org.ow2.asm:asm-analysis:6.0
org.ow2.asm:asm-commons:6.0
org.ow2.asm:asm-tree:6.0
org.ow2.asm:asm-util:6.0
org.ow2.asm:asm:6.0
org.threeten:threetenbp:1.3.3

View File

@@ -0,0 +1,24 @@
# This is a Gradle generated file for dependency locking.
# Manual edits can break the build and are not advised.
# This file is expected to be part of source control.
com.github.kevinstern:software-and-algorithms:1.0
com.github.stephenc.jcip:jcip-annotations:1.0-1
com.google.auto:auto-common:0.10
com.google.code.findbugs:jFormatString:3.0.0
com.google.code.findbugs:jsr305:3.0.2
com.google.errorprone:error_prone_annotation:2.3.3
com.google.errorprone:error_prone_annotations:2.3.3
com.google.errorprone:error_prone_check_api:2.3.3
com.google.errorprone:error_prone_core:2.3.3
com.google.errorprone:error_prone_type_annotations:2.3.3
com.google.guava:failureaccess:1.0.1
com.google.guava:guava:27.0.1-jre
com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava
com.google.j2objc:j2objc-annotations:1.1
com.google.protobuf:protobuf-java:3.4.0
com.googlecode.java-diff-utils:diffutils:1.3.0
org.checkerframework:checker-qual:2.5.3
org.checkerframework:dataflow:2.5.3
org.checkerframework:javacutil:2.5.3
org.codehaus.mojo:animal-sniffer-annotations:1.17
org.pcollections:pcollections:2.1.2

View File

@@ -0,0 +1,3 @@
# This is a Gradle generated file for dependency locking.
# Manual edits can break the build and are not advised.
# This file is expected to be part of source control.

7
common/README.md Normal file
View File

@@ -0,0 +1,7 @@
## Summary
This project holds some of the general-purpose utility classes that do not rely
on the registry domain model.
This is an intermediate step in untangling the circular dependencies
between :core and :util subprojects.

52
common/build.gradle Normal file
View File

@@ -0,0 +1,52 @@
// Copyright 2019 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.
sourceSets {
testing {
java {
compileClasspath += main.output
runtimeClasspath += main.output
}
}
}
configurations {
testingCompile.extendsFrom compile
testingRuntime.extendsFrom runtime
// All testing util classes. Other projects may declare dependency as:
// testCompile project(path: 'common', configuration: 'testing')
testing
}
task testingJar(type: Jar) {
archiveBaseName = 'testing'
from sourceSets.testing.output
}
artifacts {
testing testingJar
}
dependencies {
def deps = rootProject.dependencyMap
compile deps['com.google.code.findbugs:jsr305']
compile deps['com.google.guava:guava']
compile deps['javax.inject:javax.inject']
compile deps['joda-time:joda-time']
testingCompile deps['com.google.flogger:flogger']
testingRuntime deps['com.google.flogger:flogger-system-backend']
}

View File

@@ -0,0 +1,24 @@
# This is a Gradle generated file for dependency locking.
# Manual edits can break the build and are not advised.
# This file is expected to be part of source control.
com.github.kevinstern:software-and-algorithms:1.0
com.github.stephenc.jcip:jcip-annotations:1.0-1
com.google.auto:auto-common:0.10
com.google.code.findbugs:jFormatString:3.0.0
com.google.code.findbugs:jsr305:3.0.2
com.google.errorprone:error_prone_annotation:2.3.3
com.google.errorprone:error_prone_annotations:2.3.3
com.google.errorprone:error_prone_check_api:2.3.3
com.google.errorprone:error_prone_core:2.3.3
com.google.errorprone:error_prone_type_annotations:2.3.3
com.google.guava:failureaccess:1.0.1
com.google.guava:guava:27.0.1-jre
com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava
com.google.j2objc:j2objc-annotations:1.1
com.google.protobuf:protobuf-java:3.4.0
com.googlecode.java-diff-utils:diffutils:1.3.0
org.checkerframework:checker-qual:2.5.3
org.checkerframework:dataflow:2.5.3
org.checkerframework:javacutil:2.5.3
org.codehaus.mojo:animal-sniffer-annotations:1.17
org.pcollections:pcollections:2.1.2

View File

@@ -0,0 +1,3 @@
# This is a Gradle generated file for dependency locking.
# Manual edits can break the build and are not advised.
# This file is expected to be part of source control.

View File

@@ -0,0 +1,3 @@
# This is a Gradle generated file for dependency locking.
# Manual edits can break the build and are not advised.
# This file is expected to be part of source control.

View File

@@ -0,0 +1,3 @@
# This is a Gradle generated file for dependency locking.
# Manual edits can break the build and are not advised.
# This file is expected to be part of source control.

View File

@@ -0,0 +1,18 @@
# This is a Gradle generated file for dependency locking.
# Manual edits can break the build and are not advised.
# This file is expected to be part of source control.
antlr:antlr:2.7.7
com.google.code.findbugs:jsr305:3.0.2
com.google.errorprone:error_prone_annotations:2.3.2
com.google.guava:failureaccess:1.0.1
com.google.guava:guava:28.0-jre
com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava
com.google.j2objc:j2objc-annotations:1.3
com.puppycrawl.tools:checkstyle:8.24
commons-beanutils:commons-beanutils:1.9.4
commons-collections:commons-collections:3.2.2
info.picocli:picocli:4.0.3
net.sf.saxon:Saxon-HE:9.9.1-4
org.antlr:antlr4-runtime:4.7.2
org.checkerframework:checker-qual:2.8.1
org.codehaus.mojo:animal-sniffer-annotations:1.17

View File

@@ -0,0 +1,13 @@
# This is a Gradle generated file for dependency locking.
# 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.2
com.google.guava:failureaccess:1.0.1
com.google.guava:guava:28.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:2.8.1
org.codehaus.mojo:animal-sniffer-annotations:1.18

View File

@@ -0,0 +1,13 @@
# This is a Gradle generated file for dependency locking.
# 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.2
com.google.guava:failureaccess:1.0.1
com.google.guava:guava:28.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:2.8.1
org.codehaus.mojo:animal-sniffer-annotations:1.18

View File

@@ -0,0 +1,3 @@
# This is a Gradle generated file for dependency locking.
# Manual edits can break the build and are not advised.
# This file is expected to be part of source control.

View File

@@ -0,0 +1,13 @@
# This is a Gradle generated file for dependency locking.
# 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.2
com.google.guava:failureaccess:1.0.1
com.google.guava:guava:28.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:2.8.1
org.codehaus.mojo:animal-sniffer-annotations:1.18

View File

@@ -0,0 +1,24 @@
# This is a Gradle generated file for dependency locking.
# Manual edits can break the build and are not advised.
# This file is expected to be part of source control.
com.github.kevinstern:software-and-algorithms:1.0
com.github.stephenc.jcip:jcip-annotations:1.0-1
com.google.auto:auto-common:0.10
com.google.code.findbugs:jFormatString:3.0.0
com.google.code.findbugs:jsr305:3.0.2
com.google.errorprone:error_prone_annotation:2.3.3
com.google.errorprone:error_prone_annotations:2.3.3
com.google.errorprone:error_prone_check_api:2.3.3
com.google.errorprone:error_prone_core:2.3.3
com.google.errorprone:error_prone_type_annotations:2.3.3
com.google.guava:failureaccess:1.0.1
com.google.guava:guava:27.0.1-jre
com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava
com.google.j2objc:j2objc-annotations:1.1
com.google.protobuf:protobuf-java:3.4.0
com.googlecode.java-diff-utils:diffutils:1.3.0
org.checkerframework:checker-qual:2.5.3
org.checkerframework:dataflow:2.5.3
org.checkerframework:javacutil:2.5.3
org.codehaus.mojo:animal-sniffer-annotations:1.17
org.pcollections:pcollections:2.1.2

View File

@@ -0,0 +1,4 @@
# This is a Gradle generated file for dependency locking.
# Manual edits can break the build and are not advised.
# This file is expected to be part of source control.
com.google.errorprone:javac:9+181-r4173-1

View File

@@ -0,0 +1,4 @@
# This is a Gradle generated file for dependency locking.
# Manual edits can break the build and are not advised.
# This file is expected to be part of source control.
org.jacoco:org.jacoco.agent:0.8.5

View File

@@ -0,0 +1,11 @@
# This is a Gradle generated file for dependency locking.
# Manual edits can break the build and are not advised.
# This file is expected to be part of source control.
org.jacoco:org.jacoco.agent:0.8.5
org.jacoco:org.jacoco.ant:0.8.5
org.jacoco:org.jacoco.core:0.8.5
org.jacoco:org.jacoco.report:0.8.5
org.ow2.asm:asm-analysis:7.2
org.ow2.asm:asm-commons:7.2
org.ow2.asm:asm-tree:7.2
org.ow2.asm:asm:7.2

View File

@@ -0,0 +1,13 @@
# This is a Gradle generated file for dependency locking.
# 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.2
com.google.guava:failureaccess:1.0.1
com.google.guava:guava:28.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:2.8.1
org.codehaus.mojo:animal-sniffer-annotations:1.18

View File

@@ -0,0 +1,13 @@
# This is a Gradle generated file for dependency locking.
# 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.2
com.google.guava:failureaccess:1.0.1
com.google.guava:guava:28.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:2.8.1
org.codehaus.mojo:animal-sniffer-annotations:1.18

View File

@@ -0,0 +1,24 @@
# This is a Gradle generated file for dependency locking.
# Manual edits can break the build and are not advised.
# This file is expected to be part of source control.
com.github.kevinstern:software-and-algorithms:1.0
com.github.stephenc.jcip:jcip-annotations:1.0-1
com.google.auto:auto-common:0.10
com.google.code.findbugs:jFormatString:3.0.0
com.google.code.findbugs:jsr305:3.0.2
com.google.errorprone:error_prone_annotation:2.3.3
com.google.errorprone:error_prone_annotations:2.3.3
com.google.errorprone:error_prone_check_api:2.3.3
com.google.errorprone:error_prone_core:2.3.3
com.google.errorprone:error_prone_type_annotations:2.3.3
com.google.guava:failureaccess:1.0.1
com.google.guava:guava:27.0.1-jre
com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava
com.google.j2objc:j2objc-annotations:1.1
com.google.protobuf:protobuf-java:3.4.0
com.googlecode.java-diff-utils:diffutils:1.3.0
org.checkerframework:checker-qual:2.5.3
org.checkerframework:dataflow:2.5.3
org.checkerframework:javacutil:2.5.3
org.codehaus.mojo:animal-sniffer-annotations:1.17
org.pcollections:pcollections:2.1.2

View File

@@ -0,0 +1,3 @@
# This is a Gradle generated file for dependency locking.
# Manual edits can break the build and are not advised.
# This file is expected to be part of source control.

View File

@@ -0,0 +1,13 @@
# This is a Gradle generated file for dependency locking.
# 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.2
com.google.guava:failureaccess:1.0.1
com.google.guava:guava:28.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:2.8.1
org.codehaus.mojo:animal-sniffer-annotations:1.18

View File

@@ -0,0 +1,13 @@
# This is a Gradle generated file for dependency locking.
# 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.2
com.google.guava:failureaccess:1.0.1
com.google.guava:guava:28.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:2.8.1
org.codehaus.mojo:animal-sniffer-annotations:1.18

View File

@@ -0,0 +1,3 @@
# This is a Gradle generated file for dependency locking.
# Manual edits can break the build and are not advised.
# This file is expected to be part of source control.

View File

@@ -0,0 +1,13 @@
# This is a Gradle generated file for dependency locking.
# 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.2
com.google.guava:failureaccess:1.0.1
com.google.guava:guava:28.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:2.8.1
org.codehaus.mojo:animal-sniffer-annotations:1.18

View File

@@ -0,0 +1,13 @@
# This is a Gradle generated file for dependency locking.
# 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.2
com.google.guava:failureaccess:1.0.1
com.google.guava:guava:28.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:2.8.1
org.codehaus.mojo:animal-sniffer-annotations:1.18

View File

@@ -0,0 +1,3 @@
# This is a Gradle generated file for dependency locking.
# Manual edits can break the build and are not advised.
# This file is expected to be part of source control.

View File

@@ -0,0 +1,24 @@
# This is a Gradle generated file for dependency locking.
# Manual edits can break the build and are not advised.
# This file is expected to be part of source control.
com.github.kevinstern:software-and-algorithms:1.0
com.github.stephenc.jcip:jcip-annotations:1.0-1
com.google.auto:auto-common:0.10
com.google.code.findbugs:jFormatString:3.0.0
com.google.code.findbugs:jsr305:3.0.2
com.google.errorprone:error_prone_annotation:2.3.3
com.google.errorprone:error_prone_annotations:2.3.3
com.google.errorprone:error_prone_check_api:2.3.3
com.google.errorprone:error_prone_core:2.3.3
com.google.errorprone:error_prone_type_annotations:2.3.3
com.google.guava:failureaccess:1.0.1
com.google.guava:guava:27.0.1-jre
com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava
com.google.j2objc:j2objc-annotations:1.1
com.google.protobuf:protobuf-java:3.4.0
com.googlecode.java-diff-utils:diffutils:1.3.0
org.checkerframework:checker-qual:2.5.3
org.checkerframework:dataflow:2.5.3
org.checkerframework:javacutil:2.5.3
org.codehaus.mojo:animal-sniffer-annotations:1.17
org.pcollections:pcollections:2.1.2

View File

@@ -0,0 +1,3 @@
# This is a Gradle generated file for dependency locking.
# Manual edits can break the build and are not advised.
# This file is expected to be part of source control.

View File

@@ -0,0 +1,14 @@
# This is a Gradle generated file for dependency locking.
# 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.2
com.google.flogger:flogger:0.1
com.google.guava:failureaccess:1.0.1
com.google.guava:guava:28.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:2.8.1
org.codehaus.mojo:animal-sniffer-annotations:1.18

View File

@@ -0,0 +1,14 @@
# This is a Gradle generated file for dependency locking.
# 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.2
com.google.flogger:flogger:0.1
com.google.guava:failureaccess:1.0.1
com.google.guava:guava:28.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:2.8.1
org.codehaus.mojo:animal-sniffer-annotations:1.18

View File

@@ -0,0 +1,3 @@
# This is a Gradle generated file for dependency locking.
# Manual edits can break the build and are not advised.
# This file is expected to be part of source control.

View File

@@ -0,0 +1,15 @@
# This is a Gradle generated file for dependency locking.
# 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.2
com.google.flogger:flogger-system-backend:0.1
com.google.flogger:flogger:0.1
com.google.guava:failureaccess:1.0.1
com.google.guava:guava:28.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:2.8.1
org.codehaus.mojo:animal-sniffer-annotations:1.18

View File

@@ -0,0 +1,15 @@
# This is a Gradle generated file for dependency locking.
# 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.2
com.google.flogger:flogger-system-backend:0.1
com.google.flogger:flogger:0.1
com.google.guava:failureaccess:1.0.1
com.google.guava:guava:28.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:2.8.1
org.codehaus.mojo:animal-sniffer-annotations:1.18

View File

@@ -99,6 +99,12 @@ PRESUBMITS = {
"System.(out|err).println is only allowed in tools/ packages. Please "
"use a logger instead.",
# PostgreSQLContainer instantiation must specify docker tag
PresubmitCheck(
r"[\s\S]*new\s+PostgreSQLContainer(<[\s\S]*>)?\(\s*\)[\s\S]*",
"java", {}):
"PostgreSQLContainer instantiation must specify docker tag.",
# Various Soy linting checks
PresubmitCheck(
r".* (/\*)?\* {?@param ",

View File

@@ -241,8 +241,12 @@ dependencies {
compile deps['com.google.appengine:appengine-api-1.0-sdk']
// Known issue: nebula-lint misses inherited dependency.
compile project(':common')
testCompile project(path: ':common', configuration: 'testing')
compile project(':third_party')
compile project(':util')
// Import NomulusPostreSql from ':db' for compile but exclude dependencies.
compile project(path: ':db', configuration: 'compileApi')
testRuntime project(':db')
// Include auto-value in compile until nebula-lint understands
@@ -658,6 +662,8 @@ task outcastTest(type: FilteringTest) {
// Dedicated test suite for schema-dependent tests.
task sqlIntegrationTest(type: FilteringTest) {
systemProperties project.getProperties().subMap('sql_schema_resource_root')
excludeTestCases = false
tests = ['google/registry/schema/integration/SqlIntegrationTestSuite.*']
}

View File

@@ -19,6 +19,7 @@ import static com.google.common.base.Predicates.equalTo;
import static com.google.common.base.Predicates.in;
import static com.google.common.base.Predicates.not;
import static com.google.common.base.Strings.emptyToNull;
import static com.google.common.collect.ImmutableMap.toImmutableMap;
import static com.google.common.collect.ImmutableSet.toImmutableSet;
import static com.google.common.collect.Maps.filterValues;
import static google.registry.model.CacheUtils.memoizeWithShortExpiration;
@@ -31,9 +32,12 @@ import com.google.common.base.Joiner;
import com.google.common.base.Supplier;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Maps;
import com.google.common.collect.Streams;
import com.google.common.net.InternetDomainName;
import com.googlecode.objectify.Key;
import google.registry.model.registry.Registry.TldType;
import java.util.Map;
import java.util.Optional;
/** Utilities for finding and listing {@link Registry} entities. */
@@ -53,16 +57,21 @@ public final class Registries {
private static Supplier<ImmutableMap<String, TldType>> createFreshCache() {
return memoizeWithShortExpiration(
() ->
tm()
.doTransactionless(
tm().doTransactionless(
() -> {
ImmutableMap.Builder<String, TldType> builder =
new ImmutableMap.Builder<>();
for (Registry registry :
ofy().load().type(Registry.class).ancestor(getCrossTldKey())) {
builder.put(registry.getTldStr(), registry.getTldType());
}
return builder.build();
ImmutableSet<String> tlds =
ofy()
.load()
.type(Registry.class)
.ancestor(getCrossTldKey())
.keys()
.list()
.stream()
.map(Key::getName)
.collect(toImmutableSet());
return Registry.getAll(tlds).stream()
.map(e -> Maps.immutableEntry(e.getTldStr(), e.getTldType()))
.collect(toImmutableMap(Map.Entry::getKey, Map.Entry::getValue));
}));
}
@@ -80,6 +89,11 @@ public final class Registries {
return ImmutableSet.copyOf(filterValues(cache.get(), equalTo(type)).keySet());
}
/** Returns the Registry entities themselves of the given type loaded fresh from Datastore. */
public static ImmutableSet<Registry> getTldEntitiesOfType(TldType type) {
return Registry.getAll(filterValues(cache.get(), equalTo(type)).keySet());
}
/** Pass-through check that the specified TLD exists, otherwise throw an IAE. */
public static String assertTldExists(String tld) {
checkArgument(

View File

@@ -18,6 +18,8 @@ import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Predicates.equalTo;
import static com.google.common.base.Predicates.not;
import static com.google.common.collect.ImmutableSet.toImmutableSet;
import static com.google.common.collect.Maps.toMap;
import static google.registry.config.RegistryConfig.getSingletonCacheRefreshDuration;
import static google.registry.model.common.EntityGroupRoot.getCrossTldKey;
import static google.registry.model.ofy.ObjectifyService.ofy;
@@ -30,12 +32,15 @@ import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static org.joda.money.CurrencyUnit.USD;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Joiner;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSortedMap;
import com.google.common.collect.Iterables;
import com.google.common.collect.Maps;
import com.google.common.collect.Ordering;
import com.google.common.collect.Range;
import com.google.common.net.InternetDomainName;
@@ -58,8 +63,10 @@ import google.registry.model.domain.fee.Fee;
import google.registry.model.registry.label.PremiumList;
import google.registry.model.registry.label.ReservedList;
import google.registry.util.Idn;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.function.Predicate;
import java.util.regex.Pattern;
import javax.annotation.Nullable;
@@ -201,6 +208,25 @@ public class Registry extends ImmutableObject implements Buildable {
return registry;
}
/** Returns the registry entities for the given TLD strings, throwing if any don't exist. */
static ImmutableSet<Registry> getAll(Set<String> tlds) {
try {
ImmutableMap<String, Optional<Registry>> registries = CACHE.getAll(tlds);
ImmutableSet<String> missingRegistries =
registries.entrySet().stream()
.filter(e -> !e.getValue().isPresent())
.map(Map.Entry::getKey)
.collect(toImmutableSet());
if (missingRegistries.isEmpty()) {
return registries.values().stream().map(Optional::get).collect(toImmutableSet());
} else {
throw new RegistryNotFoundException(missingRegistries);
}
} catch (ExecutionException e) {
throw new RuntimeException("Unexpected error retrieving TLDs " + tlds, e);
}
}
/**
* Invalidates the cache entry.
*
@@ -220,15 +246,27 @@ public class Registry extends ImmutableObject implements Buildable {
new CacheLoader<String, Optional<Registry>>() {
@Override
public Optional<Registry> load(final String tld) {
// Enter a transactionless context briefly; we don't want to enroll every TLD in a
// transaction that might be wrapping this call.
// Enter a transaction-less context briefly; we don't want to enroll every TLD in
// a transaction that might be wrapping this call.
return Optional.ofNullable(
tm()
.doTransactionless(
() -> ofy()
.load()
.key(Key.create(getCrossTldKey(), Registry.class, tld))
.now()));
tm().doTransactionless(
() ->
ofy()
.load()
.key(Key.create(getCrossTldKey(), Registry.class, tld))
.now()));
}
@Override
public Map<String, Optional<Registry>> loadAll(Iterable<? extends String> tlds) {
ImmutableMap<String, Key<Registry>> keysMap =
toMap(
ImmutableSet.copyOf(tlds),
tld -> Key.create(getCrossTldKey(), Registry.class, tld));
Map<Key<Registry>, Registry> entities =
tm().doTransactionless(() -> ofy().load().keys(keysMap.values()));
return Maps.transformEntries(
keysMap, (k, v) -> Optional.ofNullable(entities.getOrDefault(v, null)));
}
});
@@ -883,10 +921,14 @@ public class Registry extends ImmutableObject implements Buildable {
}
}
/** Exception to throw when no Registry is found for a given tld. */
/** Exception to throw when no Registry entity is found for given TLD string(s). */
public static class RegistryNotFoundException extends RuntimeException {
RegistryNotFoundException(ImmutableSet<String> tlds) {
super("No registry object(s) found for " + Joiner.on(", ").join(tlds));
}
RegistryNotFoundException(String tld) {
super("No registry object found for " + tld);
this(ImmutableSet.of(tld));
}
}
}

View File

@@ -14,16 +14,13 @@
package google.registry.model.transaction;
import static google.registry.config.RegistryEnvironment.ALPHA;
import static google.registry.config.RegistryEnvironment.CRASH;
import static google.registry.config.RegistryEnvironment.SANDBOX;
import com.google.appengine.api.utils.SystemProperty;
import com.google.appengine.api.utils.SystemProperty.Environment.Value;
import com.google.common.annotations.VisibleForTesting;
import google.registry.config.RegistryEnvironment;
import google.registry.model.ofy.DatastoreTransactionManager;
import google.registry.persistence.DaggerPersistenceComponent;
import google.registry.tools.RegistryToolEnvironment;
/** Factory class to create {@link TransactionManager} instance. */
// TODO: Rename this to PersistenceFactory and move to persistence package.
@@ -35,8 +32,11 @@ public class TransactionManagerFactory {
private TransactionManagerFactory() {}
private static JpaTransactionManager createJpaTransactionManager() {
if (shouldEnableJpaTm() && isInAppEngine()) {
if (isInAppEngine()) {
return DaggerPersistenceComponent.create().appEngineJpaTransactionManager();
} else if (RegistryToolEnvironment.isInRegistryTool()
&& RegistryToolEnvironment.isJpaTmEnabled()) {
return DaggerPersistenceComponent.create().nomulusToolJpaTransactionManager();
} else {
return DummyJpaTransactionManager.create();
}
@@ -49,23 +49,6 @@ public class TransactionManagerFactory {
return new DatastoreTransactionManager(null);
}
/**
* Sets jpaTm to the implementation for Nomulus tool. Note that this method should be only used by
* {@link google.registry.tools.RegistryCli} to initialize jpaTm.
*/
public static void initForTool() {
if (shouldEnableJpaTm()) {
jpaTm = DaggerPersistenceComponent.create().nomulusToolJpaTransactionManager();
}
}
// TODO(shicong): Enable JpaTm for all environments and remove this function
private static boolean shouldEnableJpaTm() {
return RegistryEnvironment.get() == ALPHA
|| RegistryEnvironment.get() == CRASH
|| RegistryEnvironment.get() == SANDBOX;
}
/**
* This function uses App Engine API to determine if the current runtime environment is App
* Engine.

View File

@@ -15,34 +15,51 @@
package google.registry.reporting.icann;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.collect.ImmutableMap.toImmutableMap;
import static com.google.common.net.MediaType.PLAIN_TEXT_UTF_8;
import static google.registry.model.common.Cursor.getCursorTimeOrStartOfTime;
import static google.registry.model.ofy.ObjectifyService.ofy;
import static google.registry.model.transaction.TransactionManagerFactory.tm;
import static google.registry.reporting.icann.IcannReportingModule.MANIFEST_FILE_NAME;
import static google.registry.reporting.icann.IcannReportingModule.PARAM_SUBDIR;
import static google.registry.request.Action.Method.POST;
import static java.nio.charset.StandardCharsets.UTF_8;
import static javax.servlet.http.HttpServletResponse.SC_OK;
import com.google.appengine.tools.cloudstorage.GcsFilename;
import com.google.common.base.Splitter;
import com.google.common.collect.ImmutableList;
import com.google.auto.value.AutoValue;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.flogger.FluentLogger;
import com.google.common.io.ByteStreams;
import com.googlecode.objectify.Key;
import google.registry.config.RegistryConfig.Config;
import google.registry.gcs.GcsUtils;
import google.registry.model.common.Cursor;
import google.registry.model.common.Cursor.CursorType;
import google.registry.model.registry.Registries;
import google.registry.model.registry.Registry;
import google.registry.model.registry.Registry.TldType;
import google.registry.request.Action;
import google.registry.request.HttpException.ServiceUnavailableException;
import google.registry.request.Parameter;
import google.registry.request.Response;
import google.registry.request.auth.Auth;
import google.registry.request.lock.LockHandler;
import google.registry.util.Clock;
import google.registry.util.EmailMessage;
import google.registry.util.Retrier;
import google.registry.util.SendEmailService;
import java.io.IOException;
import java.io.InputStream;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import javax.annotation.Nullable;
import javax.inject.Inject;
import javax.mail.internet.InternetAddress;
import org.joda.time.DateTime;
import org.joda.time.Duration;
/**
* Action that uploads the monthly activity/transactions reports from GCS to ICANN via an HTTP PUT.
@@ -81,44 +98,175 @@ public final class IcannReportingUploadAction implements Runnable {
@Inject @Config("gSuiteOutgoingEmailAddress") InternetAddress sender;
@Inject @Config("alertRecipientEmailAddress") InternetAddress recipient;
@Inject SendEmailService emailService;
@Inject Clock clock;
@Inject LockHandler lockHandler;
@Inject
IcannReportingUploadAction() {}
@Override
public void run() {
String reportBucketname = String.format("%s/%s", reportingBucket, subdir);
ImmutableList<String> manifestedFiles = getManifestedFiles(reportBucketname);
ImmutableMap.Builder<String, Boolean> reportSummaryBuilder = new ImmutableMap.Builder<>();
// Report on all manifested files
for (String reportFilename : manifestedFiles) {
logger.atInfo().log(
"Reading ICANN report %s from bucket %s", reportFilename, reportBucketname);
final GcsFilename gcsFilename = new GcsFilename(reportBucketname, reportFilename);
verifyFileExists(gcsFilename);
boolean success = false;
try {
success =
retrier.callWithRetry(
() -> {
final byte[] payload = readBytesFromGcs(gcsFilename);
return icannReporter.send(payload, reportFilename);
},
IcannReportingUploadAction::isUploadFailureRetryable);
} catch (RuntimeException e) {
logger.atWarning().withCause(e).log("Upload to %s failed.", gcsFilename);
}
reportSummaryBuilder.put(reportFilename, success);
Callable<Void> lockRunner =
() -> {
ImmutableMap.Builder<String, Boolean> reportSummaryBuilder = new ImmutableMap.Builder<>();
ImmutableMap<Cursor, CursorInfo> cursors = loadCursors();
// If cursor time is before now, upload the corresponding report
cursors.entrySet().stream()
.filter(entry -> getCursorTimeOrStartOfTime(entry.getKey()).isBefore(clock.nowUtc()))
.forEach(
entry -> {
DateTime cursorTime = getCursorTimeOrStartOfTime(entry.getKey());
uploadReport(
cursorTime,
entry.getValue().getType(),
entry.getValue().getTld(),
reportSummaryBuilder);
});
// Send email of which reports were uploaded
emailUploadResults(reportSummaryBuilder.build());
response.setStatus(SC_OK);
response.setContentType(PLAIN_TEXT_UTF_8);
return null;
};
String lockname = "IcannReportingUploadAction";
if (!lockHandler.executeWithLocks(lockRunner, null, Duration.standardHours(2), lockname)) {
throw new ServiceUnavailableException("Lock for IcannReportingUploadAction already in use");
}
emailUploadResults(reportSummaryBuilder.build());
response.setStatus(SC_OK);
response.setContentType(PLAIN_TEXT_UTF_8);
response.setPayload(
String.format("OK, attempted uploading %d reports", manifestedFiles.size()));
}
/** Uploads the report and rolls forward the cursor for that report. */
private void uploadReport(
DateTime cursorTime,
CursorType cursorType,
String tldStr,
ImmutableMap.Builder<String, Boolean> reportSummaryBuilder) {
String reportBucketname = String.format("%s/%s", reportingBucket, subdir);
String filename = getFileName(cursorType, cursorTime, tldStr);
final GcsFilename gcsFilename = new GcsFilename(reportBucketname, filename);
logger.atInfo().log("Reading ICANN report %s from bucket %s", filename, reportBucketname);
// Check that the report exists
try {
verifyFileExists(gcsFilename);
} catch (IllegalArgumentException e) {
String logMessage =
String.format(
"Could not upload %s report for %s because file %s did not exist.",
cursorType, tldStr, filename);
if (clock.nowUtc().dayOfMonth().get() == 1) {
logger.atInfo().withCause(e).log(logMessage + " This report may not have been staged yet.");
} else {
logger.atSevere().withCause(e).log(logMessage);
}
reportSummaryBuilder.put(filename, false);
return;
}
// Upload the report
boolean success = false;
try {
success =
retrier.callWithRetry(
() -> {
final byte[] payload = readBytesFromGcs(gcsFilename);
return icannReporter.send(payload, filename);
},
IcannReportingUploadAction::isUploadFailureRetryable);
} catch (RuntimeException e) {
logger.atWarning().withCause(e).log("Upload to %s failed", gcsFilename);
}
reportSummaryBuilder.put(filename, success);
// Set cursor to first day of next month if the upload succeeded
if (success) {
Cursor newCursor;
if (cursorType.equals(CursorType.ICANN_UPLOAD_MANIFEST)) {
newCursor =
Cursor.createGlobal(
cursorType, cursorTime.withTimeAtStartOfDay().withDayOfMonth(1).plusMonths(1));
} else {
newCursor =
Cursor.create(
cursorType,
cursorTime.withTimeAtStartOfDay().withDayOfMonth(1).plusMonths(1),
Registry.get(tldStr));
}
tm().transact(() -> ofy().save().entity(newCursor));
}
}
private String getFileName(CursorType cursorType, DateTime cursorTime, String tld) {
if (cursorType.equals(CursorType.ICANN_UPLOAD_MANIFEST)) {
return MANIFEST_FILE_NAME;
}
return String.format(
"%s%s%d%02d.csv",
tld,
(cursorType.equals(CursorType.ICANN_UPLOAD_ACTIVITY) ? "-activity-" : "-transactions-"),
cursorTime.year().get(),
cursorTime.monthOfYear().get());
}
/** Returns a map of each cursor to the CursorType and tld. */
private ImmutableMap<Cursor, CursorInfo> loadCursors() {
ImmutableSet<Registry> registries = Registries.getTldEntitiesOfType(TldType.REAL);
Map<Key<Cursor>, Registry> activityKeyMap =
loadKeyMap(registries, CursorType.ICANN_UPLOAD_ACTIVITY);
Map<Key<Cursor>, Registry> transactionKeyMap =
loadKeyMap(registries, CursorType.ICANN_UPLOAD_TX);
ImmutableSet.Builder<Key<Cursor>> keys = new ImmutableSet.Builder<>();
keys.addAll(activityKeyMap.keySet());
keys.addAll(transactionKeyMap.keySet());
keys.add(Cursor.createGlobalKey(CursorType.ICANN_UPLOAD_MANIFEST));
Map<Key<Cursor>, Cursor> cursorMap = ofy().load().keys(keys.build());
ImmutableMap.Builder<Cursor, CursorInfo> cursors = new ImmutableMap.Builder<>();
defaultNullCursorsToNextMonthAndAddToMap(
activityKeyMap, CursorType.ICANN_UPLOAD_ACTIVITY, cursorMap, cursors);
defaultNullCursorsToNextMonthAndAddToMap(
transactionKeyMap, CursorType.ICANN_UPLOAD_TX, cursorMap, cursors);
Cursor manifestCursor =
cursorMap.getOrDefault(
Cursor.createGlobalKey(CursorType.ICANN_UPLOAD_MANIFEST),
Cursor.createGlobal(CursorType.ICANN_UPLOAD_MANIFEST, clock.nowUtc().minusDays(1)));
cursors.put(manifestCursor, CursorInfo.create(CursorType.ICANN_UPLOAD_MANIFEST, null));
return cursors.build();
}
private Map<Key<Cursor>, Registry> loadKeyMap(
ImmutableSet<Registry> registries, CursorType type) {
return registries.stream().collect(toImmutableMap(r -> Cursor.createKey(type, r), r -> r));
}
/**
* Populate the cursors map with the Cursor and CursorInfo for each key in the keyMap. If the key
* from the keyMap does not have an existing cursor, create a new cursor with a default cursorTime
* of the first of next month.
*/
private void defaultNullCursorsToNextMonthAndAddToMap(
Map<Key<Cursor>, Registry> keyMap,
CursorType type,
Map<Key<Cursor>, Cursor> cursorMap,
ImmutableMap.Builder<Cursor, CursorInfo> cursors) {
keyMap.forEach(
(key, registry) -> {
// Cursor time is defaulted to the first of next month since a new tld will not yet have a
// report staged for upload.
Cursor cursor =
cursorMap.getOrDefault(
key, Cursor.create(type, clock.nowUtc().minusDays(1), registry));
cursors.put(cursor, CursorInfo.create(type, registry.getTldStr()));
});
}
/** Don't retry when reports are already uploaded or can't be uploaded. */
private static final String ICANN_UPLOAD_PERMANENT_ERROR_MESSAGE =
"A report for that month already exists, the cut-off date already passed.";
"A report for that month already exists, the cut-off date already passed";
/** Don't retry when the IP address isn't whitelisted, as retries go through the same IP. */
private static final Pattern ICANN_UPLOAD_WHITELIST_ERROR =
@@ -146,18 +294,6 @@ public final class IcannReportingUploadAction implements Runnable {
emailService.sendEmail(EmailMessage.create(subject, body, recipient, sender));
}
private ImmutableList<String> getManifestedFiles(String reportBucketname) {
GcsFilename manifestFilename = new GcsFilename(reportBucketname, MANIFEST_FILE_NAME);
verifyFileExists(manifestFilename);
return retrier.callWithRetry(
() ->
ImmutableList.copyOf(
Splitter.on('\n')
.omitEmptyStrings()
.split(new String(readBytesFromGcs(manifestFilename), UTF_8))),
IOException.class);
}
private byte[] readBytesFromGcs(GcsFilename reportFilename) throws IOException {
try (InputStream gcsInput = gcsUtils.openInputStream(reportFilename)) {
return ByteStreams.toByteArray(gcsInput);
@@ -171,4 +307,16 @@ public final class IcannReportingUploadAction implements Runnable {
gcsFilename.getObjectName(),
gcsFilename.getBucketName());
}
@AutoValue
abstract static class CursorInfo {
static CursorInfo create(CursorType type, @Nullable String tld) {
return new AutoValue_IcannReportingUploadAction_CursorInfo(type, tld);
}
public abstract CursorType getType();
@Nullable
abstract String getTld();
}
}

View File

@@ -0,0 +1,147 @@
// Copyright 2019 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.schema.tld;
import static com.google.common.base.Preconditions.checkState;
import com.google.common.collect.ImmutableMap;
import google.registry.model.CreateAutoTimestamp;
import google.registry.model.ImmutableObject;
import google.registry.model.registry.label.ReservationType;
import java.util.Map;
import javax.annotation.Nullable;
import javax.persistence.CollectionTable;
import javax.persistence.Column;
import javax.persistence.ElementCollection;
import javax.persistence.Embeddable;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Index;
import javax.persistence.JoinColumn;
import javax.persistence.MapKeyColumn;
import javax.persistence.Table;
import org.joda.time.DateTime;
/**
* A list of reserved domain labels that are blocked from being registered for various reasons.
*
* <p>Note that the primary key of this entity is {@link #revisionId}, which is auto-generated by
* the database. So, if a retry of insertion happens after the previous attempt unexpectedly
* succeeds, we will end up with having two exact same reserved lists that differ only by
* revisionId. This is fine though, because we only use the list with the highest revisionId.
*/
@Entity
@Table(indexes = {@Index(columnList = "name", name = "reservedlist_name_idx")})
public class ReservedList extends ImmutableObject {
@Column(nullable = false)
private String name;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(nullable = false)
private Long revisionId;
@Column(nullable = false)
private CreateAutoTimestamp creationTimestamp = CreateAutoTimestamp.create(null);
@Column(nullable = false)
private Boolean shouldPublish;
@ElementCollection
@CollectionTable(
name = "ReservedEntry",
joinColumns = @JoinColumn(name = "revisionId", referencedColumnName = "revisionId"))
@MapKeyColumn(name = "domainLabel")
private Map<String, ReservedEntry> labelsToReservations;
@Embeddable
public static class ReservedEntry extends ImmutableObject {
@Column(nullable = false)
private ReservationType reservationType;
@Column(nullable = true)
private String comment;
private ReservedEntry(ReservationType reservationType, @Nullable String comment) {
this.reservationType = reservationType;
this.comment = comment;
}
// Hibernate requires this default constructor.
private ReservedEntry() {}
/** Constructs a {@link ReservedEntry} object. */
public static ReservedEntry create(ReservationType reservationType, @Nullable String comment) {
return new ReservedEntry(reservationType, comment);
}
/** Returns the reservation type for this entry. */
public ReservationType getReservationType() {
return reservationType;
}
/** Returns the comment for this entry. Retruns null if there is no comment. */
public String getComment() {
return comment;
}
}
private ReservedList(
String name, Boolean shouldPublish, Map<String, ReservedEntry> labelsToReservations) {
this.name = name;
this.shouldPublish = shouldPublish;
this.labelsToReservations = labelsToReservations;
}
// Hibernate requires this default constructor.
private ReservedList() {}
/** Constructs a {@link ReservedList} object. */
public static ReservedList create(
String name, Boolean shouldPublish, Map<String, ReservedEntry> labelsToReservations) {
return new ReservedList(name, shouldPublish, labelsToReservations);
}
/** Returns the name of the reserved list. */
public String getName() {
return name;
}
/** Returns the ID of this revision, or throws if null. */
public Long getRevisionId() {
checkState(
revisionId != null,
"revisionId is null because this object has not been persisted to the database yet");
return revisionId;
}
/** Returns the creation time of this revision of the reserved list. */
public DateTime getCreationTimestamp() {
return creationTimestamp.getTimestamp();
}
/** Returns a {@link Map} of domain labels to {@link ReservedEntry}. */
public ImmutableMap<String, ReservedEntry> getLabelsToReservations() {
return ImmutableMap.copyOf(labelsToReservations);
}
/** Returns true if the reserved list should be published. */
public Boolean getShouldPublish() {
return shouldPublish;
}
}

View File

@@ -20,6 +20,7 @@ import com.beust.jcommander.Parameter;
import com.beust.jcommander.Parameters;
import com.google.common.annotations.VisibleForTesting;
import google.registry.persistence.HibernateSchemaExporter;
import google.registry.persistence.NomulusPostgreSql;
import google.registry.persistence.PersistenceXmlUtility;
import java.io.File;
import java.io.IOException;
@@ -83,7 +84,7 @@ public class GenerateSqlSchemaCommand implements Command {
// Start the container and store the address information.
postgresContainer =
new PostgreSQLContainer()
new PostgreSQLContainer(NomulusPostgreSql.getDockerTag())
.withDatabaseName(DB_NAME)
.withUsername(DB_USERNAME)
.withPassword(DB_PASSWORD);

View File

@@ -31,7 +31,6 @@ import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Iterables;
import google.registry.config.RegistryConfig;
import google.registry.model.ofy.ObjectifyService;
import google.registry.model.transaction.TransactionManagerFactory;
import google.registry.tools.AuthModule.LoginRequiredException;
import google.registry.tools.params.ParameterFactory;
import java.io.ByteArrayInputStream;
@@ -237,7 +236,7 @@ final class RegistryCli implements AutoCloseable, CommandRunner {
}
if (command instanceof CommandWithCloudSql) {
TransactionManagerFactory.initForTool();
RegistryToolEnvironment.get().enableJpaTm();
}
command.run();

View File

@@ -25,7 +25,7 @@ import google.registry.config.RegistryEnvironment;
import google.registry.config.SystemPropertySetter;
/** Enum of production environments, used for the {@code --environment} flag. */
enum RegistryToolEnvironment {
public enum RegistryToolEnvironment {
PRODUCTION(RegistryEnvironment.PRODUCTION),
ALPHA(RegistryEnvironment.ALPHA),
CRASH(RegistryEnvironment.CRASH),
@@ -39,6 +39,7 @@ enum RegistryToolEnvironment {
private static final ImmutableList<String> FLAGS = ImmutableList.of("-e", "--environment");
private static RegistryToolEnvironment instance;
private static boolean isJpaTmEnabled = false;
private final RegistryEnvironment actualEnvironment;
private final ImmutableMap<String, String> extraProperties;
@@ -72,7 +73,7 @@ enum RegistryToolEnvironment {
*
* <p>This should be called after {@link #parseFromArgs(String[])}.
*/
static RegistryToolEnvironment get() {
public static RegistryToolEnvironment get() {
checkState(instance != null, "No RegistryToolEnvironment has been set up");
return instance;
}
@@ -98,6 +99,26 @@ enum RegistryToolEnvironment {
}
}
/** Returns true if the RegistryToolEnvironment is set up. */
public static boolean isInRegistryTool() {
return instance != null;
}
/**
* Sets the flag to indicate that the running command needs JpaTransactionManager to be enabled.
*/
public static void enableJpaTm() {
isJpaTmEnabled = true;
}
/**
* Returns true if the JpaTransactionManager is enabled. Note that JpaTm is actually enabled in
* {@link google.registry.model.transaction.TransactionManagerFactory} by reading this flag.
*/
public static boolean isJpaTmEnabled() {
return isJpaTmEnabled;
}
/** Extracts value from command-line arguments associated with any {@code flags}. */
private static String getFlagValue(String[] args, Iterable<String> flags) {
for (String flag : flags) {

View File

@@ -24,6 +24,7 @@
<class>google.registry.schema.tmch.ClaimsList</class>
<class>google.registry.model.transfer.BaseTransferObject</class>
<class>google.registry.schema.tld.PremiumList</class>
<class>google.registry.schema.tld.ReservedList</class>
<class>google.registry.model.domain.secdns.DelegationSignerData</class>
<class>google.registry.model.domain.DesignatedContact</class>
<class>google.registry.model.domain.DomainBase</class>

View File

@@ -48,10 +48,7 @@ import org.junit.runners.JUnit4;
@RunWith(JUnit4.class)
public class ExportReservedTermsActionTest {
@Rule
public final AppEngineRule appEngine = AppEngineRule.builder()
.withDatastore()
.build();
@Rule public final AppEngineRule appEngine = AppEngineRule.builder().withDatastore().build();
private final DriveConnection driveConnection = mock(DriveConnection.class);
private final Response response = mock(Response.class);
@@ -133,6 +130,6 @@ public class ExportReservedTermsActionTest {
assertThat(thrown)
.hasCauseThat()
.hasMessageThat()
.isEqualTo("No registry object found for fakeTld");
.isEqualTo("No registry object(s) found for fakeTld");
}
}

View File

@@ -17,9 +17,12 @@ package google.registry.model.registry;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth8.assertThat;
import static google.registry.testing.DatastoreHelper.createTlds;
import static google.registry.testing.DatastoreHelper.newRegistry;
import static google.registry.testing.DatastoreHelper.persistResource;
import static google.registry.testing.JUnitBackports.assertThrows;
import com.google.common.net.InternetDomainName;
import google.registry.model.registry.Registry.TldType;
import google.registry.testing.AppEngineRule;
import org.junit.Rule;
import org.junit.Test;
@@ -32,6 +35,7 @@ public class RegistriesTest {
@Rule
public final AppEngineRule appEngine = AppEngineRule.builder().withDatastore().build();
private void initTestTlds() {
createTlds("foo", "a.b.c"); // Test a multipart tld.
}
@@ -42,6 +46,16 @@ public class RegistriesTest {
assertThat(Registries.getTlds()).containsExactly("foo", "a.b.c");
}
@Test
public void test_getTldEntities() {
initTestTlds();
persistResource(newRegistry("testtld", "TESTTLD").asBuilder().setTldType(TldType.TEST).build());
assertThat(Registries.getTldEntitiesOfType(TldType.REAL))
.containsExactly(Registry.get("foo"), Registry.get("a.b.c"));
assertThat(Registries.getTldEntitiesOfType(TldType.TEST))
.containsExactly(Registry.get("testtld"));
}
@Test
public void testGetTlds_withNoRegistriesPersisted_returnsEmptySet() {
assertThat(Registries.getTlds()).isEmpty();

View File

@@ -51,6 +51,7 @@ import org.junit.Test;
/** Unit tests for {@link Registry}. */
public class RegistryTest extends EntityTestCase {
Registry registry;
@Before
@@ -146,6 +147,19 @@ public class RegistryTest extends EntityTestCase {
assertThat(registry.getReservedLists()).isEmpty();
}
@Test
public void testGetAll() {
createTld("foo");
assertThat(Registry.getAll(ImmutableSet.of("foo", "tld")))
.containsExactlyElementsIn(
ofy()
.load()
.keys(
Key.create(getCrossTldKey(), Registry.class, "foo"),
Key.create(getCrossTldKey(), Registry.class, "tld"))
.values());
}
@Test
public void testSetReservedLists() {
ReservedList rl5 = persistReservedList(

View File

@@ -18,6 +18,7 @@ import static com.google.common.truth.Truth.assertThat;
import static org.joda.time.DateTimeZone.UTC;
import static org.testcontainers.containers.PostgreSQLContainer.POSTGRESQL_PORT;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Charsets;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
@@ -25,12 +26,14 @@ import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Maps;
import com.google.common.io.Resources;
import google.registry.persistence.HibernateSchemaExporter;
import google.registry.persistence.NomulusPostgreSql;
import google.registry.persistence.PersistenceModule;
import google.registry.persistence.PersistenceXmlUtility;
import google.registry.testing.FakeClock;
import java.io.File;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.sql.Connection;
@@ -42,6 +45,7 @@ import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Properties;
import javax.persistence.EntityManagerFactory;
import org.hibernate.cfg.Environment;
@@ -59,6 +63,19 @@ import org.testcontainers.containers.PostgreSQLContainer;
* TransactionManagerFactory} with the {@link JpaTransactionManagerImpl} generated by the rule
* itself, so that all SQL queries will be sent to the database instance created by {@link
* PostgreSQLContainer} to achieve test purpose.
*
* <p>The location of the Nomulus golden schema may be overridden with the {@code
* "sql_schema_resource_root} system property. This feature is needed by the server/schema
* compatibility tests, which need to use different versions of the schema off the classpath.
*
* <p>If defined, the value of the {@code "sql_schema_resource_root} should be an URL string that
* points to the jar or resource root directory. Here are some examples:
*
* <ul>
* <li>Absolute path to local directory: [file://]/path/to/resources
* <li>Absolute path to local jar: [file://]/path/to/schema.jar
* <li>URL to remote jar: https://host/path/to/schema.jar
* </ul>
*/
public class JpaTransactionManagerRule extends ExternalResource {
private static final String GOLDEN_SCHEMA_SQL_PATH = "sql/schema/nomulus.golden.sql";
@@ -67,6 +84,10 @@ public class JpaTransactionManagerRule extends ExternalResource {
private static final String MANAGEMENT_DB_NAME = "management";
private static final String POSTGRES_DB_NAME = "postgres";
// Name of the optional property that specifies the root path of the golden schema.
@VisibleForTesting
static final String GOLDEN_SCHEMA_RESOURCE_ROOT_PROP = "sql_schema_resource_root";
private final DateTime now = DateTime.now(UTC);
private final FakeClock clock = new FakeClock(now);
private final String initScriptPath;
@@ -74,6 +95,9 @@ public class JpaTransactionManagerRule extends ExternalResource {
private final ImmutableMap userProperties;
private static final JdbcDatabaseContainer database = create();
private static final long ACTIVE_CONNECTIONS_BASELINE =
getActiveConnectionCountByUser(database.getUsername());
;
private static final HibernateSchemaExporter exporter =
HibernateSchemaExporter.create(
database.getJdbcUrl(), database.getUsername(), database.getPassword());
@@ -90,7 +114,9 @@ public class JpaTransactionManagerRule extends ExternalResource {
}
private static JdbcDatabaseContainer create() {
PostgreSQLContainer container = new PostgreSQLContainer().withDatabaseName(MANAGEMENT_DB_NAME);
PostgreSQLContainer container =
new PostgreSQLContainer(NomulusPostgreSql.getDockerTag())
.withDatabaseName(MANAGEMENT_DB_NAME);
container.start();
Runtime.getRuntime().addShutdownHook(new Thread(() -> container.close()));
return container;
@@ -99,7 +125,7 @@ public class JpaTransactionManagerRule extends ExternalResource {
@Override
public void before() throws Exception {
executeSql(MANAGEMENT_DB_NAME, readSqlInClassPath(DB_CLEANUP_SQL_PATH));
executeSql(POSTGRES_DB_NAME, readSqlInClassPath(initScriptPath));
executeSql(POSTGRES_DB_NAME, readInitialScript());
if (!extraEntityClasses.isEmpty()) {
File tempSqlFile = File.createTempFile("tempSqlFile", ".sql");
tempSqlFile.deleteOnExit();
@@ -140,27 +166,28 @@ public class JpaTransactionManagerRule extends ExternalResource {
assertNormalActiveConnection();
}
/**
* This function throws exception if it detects connection leak by checking the metadata table
* pg_stat_activity.
*/
private void assertNormalActiveConnection() {
private static long getActiveConnectionCountByUser(String userName) {
try (Connection conn = createConnection(POSTGRES_DB_NAME);
Statement statement = conn.createStatement()) {
ResultSet rs =
statement.executeQuery(
"SELECT COUNT(1) FROM pg_stat_activity WHERE usename = '"
+ database.getUsername()
+ "'");
"SELECT COUNT(1) FROM pg_stat_activity WHERE usename = '" + userName + "'");
rs.next();
long activeConns = rs.getLong(1);
// There should be only 1 active connection which is executing this query
assertThat(activeConns).isEqualTo(1L);
return rs.getLong(1);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* This function throws exception if it detects connection leak by checking the metadata table
* pg_stat_activity.
*/
private void assertNormalActiveConnection() {
assertThat(getActiveConnectionCountByUser(database.getUsername()))
.isEqualTo(ACTIVE_CONNECTIONS_BASELINE);
}
private static String readSqlInClassPath(String sqlScriptPath) {
try {
return Resources.toString(Resources.getResource(sqlScriptPath), Charsets.UTF_8);
@@ -169,6 +196,38 @@ public class JpaTransactionManagerRule extends ExternalResource {
}
}
@VisibleForTesting
Optional<String> getInitScriptUrlOverride() {
String schemaRootPath = System.getProperty(GOLDEN_SCHEMA_RESOURCE_ROOT_PROP, "").trim();
if (schemaRootPath.isEmpty() || !initScriptPath.equals(GOLDEN_SCHEMA_SQL_PATH)) {
return Optional.empty();
}
if (schemaRootPath.startsWith("/")) {
schemaRootPath = "file://" + schemaRootPath;
}
if (schemaRootPath.endsWith(".jar") && !schemaRootPath.startsWith("jar:")) {
schemaRootPath = "jar:" + schemaRootPath;
}
if (schemaRootPath.endsWith(".jar")) {
schemaRootPath += "!/" + GOLDEN_SCHEMA_SQL_PATH;
} else {
schemaRootPath += "/" + GOLDEN_SCHEMA_SQL_PATH;
}
return Optional.of(schemaRootPath);
}
private String readInitialScript() {
Optional<String> schemaUrlOverride = getInitScriptUrlOverride();
if (!schemaUrlOverride.isPresent()) {
return readSqlInClassPath(initScriptPath);
}
try {
return Resources.toString(new URL(schemaUrlOverride.get()), StandardCharsets.UTF_8);
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}
private void executeSql(String dbName, String sqlScript) {
try (Connection conn = createConnection(dbName);
Statement statement = conn.createStatement()) {
@@ -178,7 +237,7 @@ public class JpaTransactionManagerRule extends ExternalResource {
}
}
private String getJdbcUrlFor(String dbName) {
private static String getJdbcUrlFor(String dbName) {
// Disable Postgres driver use of java.util.logging to reduce noise at startup time
return "jdbc:postgresql://"
+ database.getContainerIpAddress()
@@ -189,7 +248,7 @@ public class JpaTransactionManagerRule extends ExternalResource {
+ "?loggerLevel=OFF";
}
private Connection createConnection(String dbName) {
private static Connection createConnection(String dbName) {
final Properties info = new Properties();
info.put("user", database.getUsername());
info.put("password", database.getPassword());

View File

@@ -15,10 +15,13 @@
package google.registry.model.transaction;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth8.assertThat;
import static google.registry.model.transaction.JpaTransactionManagerRule.GOLDEN_SCHEMA_RESOURCE_ROOT_PROP;
import static google.registry.model.transaction.TransactionManagerFactory.jpaTm;
import static google.registry.testing.JUnitBackports.assertThrows;
import google.registry.model.ImmutableObject;
import google.registry.testing.SystemPropertyRule;
import java.util.List;
import javax.persistence.Entity;
import javax.persistence.Id;
@@ -38,6 +41,8 @@ public class JpaTransactionManagerRuleTest {
.withEntityClass(TestEntity.class)
.build();
@Rule public final SystemPropertyRule systemPropertyRule = new SystemPropertyRule();
@Test
public void verifiesRuleWorks() {
assertThrows(
@@ -73,6 +78,57 @@ public class JpaTransactionManagerRuleTest {
assertThat(retrieved).isEqualTo(original);
}
@Test
public void testInitScriptUrl_noOverride() {
assertThat(jpaTmRule.getInitScriptUrlOverride()).isEmpty();
}
@Test
public void testInitScriptUrl_localDir_noProtocol() {
systemPropertyRule.setProperty(GOLDEN_SCHEMA_RESOURCE_ROOT_PROP, "/path/to/resources");
assertThat(jpaTmRule.getInitScriptUrlOverride())
.hasValue("file:///path/to/resources/sql/schema/nomulus.golden.sql");
}
@Test
public void testInitScriptUrl_localDir_hasProtocol() {
systemPropertyRule.setProperty(GOLDEN_SCHEMA_RESOURCE_ROOT_PROP, "file:///path/to/resources");
assertThat(jpaTmRule.getInitScriptUrlOverride())
.hasValue("file:///path/to/resources/sql/schema/nomulus.golden.sql");
}
@Test
public void testInitScriptUrl_localJar_noProtocol() {
systemPropertyRule.setProperty(
GOLDEN_SCHEMA_RESOURCE_ROOT_PROP, "/path/to/resources/schema.jar");
assertThat(jpaTmRule.getInitScriptUrlOverride())
.hasValue("jar:file:///path/to/resources/schema.jar!/sql/schema/nomulus.golden.sql");
}
@Test
public void testInitScriptUrl_localJar_hasPartialProtocol() {
systemPropertyRule.setProperty(
GOLDEN_SCHEMA_RESOURCE_ROOT_PROP, "file:///path/to/resources/schema.jar");
assertThat(jpaTmRule.getInitScriptUrlOverride())
.hasValue("jar:file:///path/to/resources/schema.jar!/sql/schema/nomulus.golden.sql");
}
@Test
public void testInitScriptUrl_localJar_hasFullProtocol() {
systemPropertyRule.setProperty(
GOLDEN_SCHEMA_RESOURCE_ROOT_PROP, "jar:file:///path/to/resources/schema.jar");
assertThat(jpaTmRule.getInitScriptUrlOverride())
.hasValue("jar:file:///path/to/resources/schema.jar!/sql/schema/nomulus.golden.sql");
}
@Test
public void testInitScriptUrl_remoteJar() {
systemPropertyRule.setProperty(
GOLDEN_SCHEMA_RESOURCE_ROOT_PROP, "http://host/path/to/resources/schema.jar");
assertThat(jpaTmRule.getInitScriptUrlOverride())
.hasValue("jar:http://host/path/to/resources/schema.jar!/sql/schema/nomulus.golden.sql");
}
@Entity(name = "TestEntity") // Specify name to avoid nested class naming issues.
static class TestEntity extends ImmutableObject {
@Id String key;

View File

@@ -36,7 +36,10 @@ import org.testcontainers.containers.PostgreSQLContainer;
/** Unit tests for {@link HibernateSchemaExporter}. */
@RunWith(JUnit4.class)
public class HibernateSchemaExporterTest {
@ClassRule public static final PostgreSQLContainer database = new PostgreSQLContainer();
@ClassRule
public static final PostgreSQLContainer database =
new PostgreSQLContainer(NomulusPostgreSql.getDockerTag());
private static HibernateSchemaExporter exporter;
@Rule public final TemporaryFolder tempFolder = new TemporaryFolder();

View File

@@ -29,7 +29,8 @@ import org.testcontainers.containers.PostgreSQLContainer;
/** Unit tests for {@link PersistenceModule}. */
@RunWith(JUnit4.class)
public class PersistenceModuleTest {
@Rule public PostgreSQLContainer database = new PostgreSQLContainer();
@Rule
public PostgreSQLContainer database = new PostgreSQLContainer(NomulusPostgreSql.getDockerTag());
private EntityManagerFactory emf;

View File

@@ -15,8 +15,12 @@
package google.registry.reporting.icann;
import static com.google.common.truth.Truth.assertThat;
import static google.registry.model.ofy.ObjectifyService.ofy;
import static google.registry.testing.DatastoreHelper.createTlds;
import static google.registry.testing.DatastoreHelper.persistResource;
import static google.registry.testing.GcsTestingUtils.writeGcsFile;
import static google.registry.testing.JUnitBackports.assertThrows;
import static google.registry.testing.LogsSubject.assertAboutLogs;
import static java.nio.charset.StandardCharsets.UTF_8;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
@@ -27,16 +31,25 @@ import static org.mockito.Mockito.when;
import com.google.appengine.tools.cloudstorage.GcsFilename;
import com.google.appengine.tools.cloudstorage.GcsService;
import com.google.appengine.tools.cloudstorage.GcsServiceFactory;
import com.google.common.testing.TestLogHandler;
import google.registry.gcs.GcsUtils;
import google.registry.model.common.Cursor;
import google.registry.model.common.Cursor.CursorType;
import google.registry.model.registry.Registry;
import google.registry.request.HttpException.ServiceUnavailableException;
import google.registry.testing.AppEngineRule;
import google.registry.testing.FakeClock;
import google.registry.testing.FakeLockHandler;
import google.registry.testing.FakeResponse;
import google.registry.testing.FakeSleeper;
import google.registry.util.EmailMessage;
import google.registry.util.Retrier;
import google.registry.util.SendEmailService;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.mail.internet.InternetAddress;
import org.joda.time.DateTime;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -52,105 +65,175 @@ public class IcannReportingUploadActionTest {
private static final byte[] PAYLOAD_SUCCESS = "test,csv\n13,37".getBytes(UTF_8);
private static final byte[] PAYLOAD_FAIL = "ahah,csv\n12,34".getBytes(UTF_8);
private static final byte[] MANIFEST_PAYLOAD =
"test-transactions-201706.csv\na-activity-201706.csv\n".getBytes(UTF_8);
"tld-transactions-200606.csv\ntld-activity-200606.csv\nfoo-transactions-200606.csv\nfoo-activity-200606.csv\n"
.getBytes(UTF_8);
private final IcannHttpReporter mockReporter = mock(IcannHttpReporter.class);
private final SendEmailService emailService = mock(SendEmailService.class);
private final FakeResponse response = new FakeResponse();
private final GcsService gcsService = GcsServiceFactory.createGcsService();
private final TestLogHandler logHandler = new TestLogHandler();
private final Logger loggerToIntercept =
Logger.getLogger(IcannReportingUploadAction.class.getCanonicalName());
private final FakeClock clock = new FakeClock(DateTime.parse("2000-01-01TZ"));
private IcannReportingUploadAction createAction() throws Exception {
IcannReportingUploadAction action = new IcannReportingUploadAction();
action.icannReporter = mockReporter;
action.gcsUtils = new GcsUtils(gcsService, 1024);
action.retrier = new Retrier(new FakeSleeper(new FakeClock()), 3);
action.subdir = "icann/monthly/2017-06";
action.subdir = "icann/monthly/2006-06";
action.reportingBucket = "basin";
action.emailService = emailService;
action.sender = new InternetAddress("sender@example.com");
action.recipient = new InternetAddress("recipient@example.com");
action.response = response;
action.clock = clock;
action.lockHandler = new FakeLockHandler(true);
return action;
}
@Before
public void before() throws Exception {
createTlds("tld", "foo");
writeGcsFile(
gcsService,
new GcsFilename("basin/icann/monthly/2017-06", "test-transactions-201706.csv"),
new GcsFilename("basin/icann/monthly/2006-06", "tld-transactions-200606.csv"),
PAYLOAD_SUCCESS);
writeGcsFile(
gcsService,
new GcsFilename("basin/icann/monthly/2017-06", "a-activity-201706.csv"),
new GcsFilename("basin/icann/monthly/2006-06", "tld-activity-200606.csv"),
PAYLOAD_FAIL);
writeGcsFile(
gcsService,
new GcsFilename("basin/icann/monthly/2017-06", "MANIFEST.txt"),
new GcsFilename("basin/icann/monthly/2006-06", "foo-transactions-200606.csv"),
PAYLOAD_SUCCESS);
writeGcsFile(
gcsService,
new GcsFilename("basin/icann/monthly/2006-06", "foo-activity-200606.csv"),
PAYLOAD_SUCCESS);
writeGcsFile(
gcsService,
new GcsFilename("basin/icann/monthly/2006-06", "MANIFEST.txt"),
MANIFEST_PAYLOAD);
when(mockReporter.send(PAYLOAD_SUCCESS, "test-transactions-201706.csv")).thenReturn(true);
when(mockReporter.send(PAYLOAD_FAIL, "a-activity-201706.csv")).thenReturn(false);
when(mockReporter.send(PAYLOAD_SUCCESS, "tld-transactions-200606.csv")).thenReturn(true);
when(mockReporter.send(PAYLOAD_SUCCESS, "foo-transactions-200606.csv")).thenReturn(true);
when(mockReporter.send(PAYLOAD_FAIL, "tld-activity-200606.csv")).thenReturn(false);
when(mockReporter.send(PAYLOAD_SUCCESS, "foo-activity-200606.csv")).thenReturn(true);
when(mockReporter.send(MANIFEST_PAYLOAD, "MANIFEST.txt")).thenReturn(true);
clock.setTo(DateTime.parse("2006-06-06T00:30:00Z"));
persistResource(
Cursor.create(
CursorType.ICANN_UPLOAD_ACTIVITY, DateTime.parse("2006-06-06TZ"), Registry.get("tld")));
persistResource(
Cursor.create(
CursorType.ICANN_UPLOAD_TX, DateTime.parse("2006-06-06TZ"), Registry.get("tld")));
persistResource(
Cursor.createGlobal(CursorType.ICANN_UPLOAD_MANIFEST, DateTime.parse("2006-07-06TZ")));
persistResource(
Cursor.create(
CursorType.ICANN_UPLOAD_ACTIVITY, DateTime.parse("2006-06-06TZ"), Registry.get("foo")));
persistResource(
Cursor.create(
CursorType.ICANN_UPLOAD_TX, DateTime.parse("2006-06-06TZ"), Registry.get("foo")));
loggerToIntercept.addHandler(logHandler);
}
@Test
public void testSuccess() throws Exception {
IcannReportingUploadAction action = createAction();
action.run();
verify(mockReporter).send(PAYLOAD_SUCCESS, "test-transactions-201706.csv");
verify(mockReporter).send(PAYLOAD_FAIL, "a-activity-201706.csv");
verify(mockReporter).send(PAYLOAD_SUCCESS, "foo-activity-200606.csv");
verify(mockReporter).send(PAYLOAD_FAIL, "tld-activity-200606.csv");
verify(mockReporter).send(PAYLOAD_SUCCESS, "foo-transactions-200606.csv");
verify(mockReporter).send(PAYLOAD_SUCCESS, "tld-transactions-200606.csv");
verifyNoMoreInteractions(mockReporter);
assertThat(((FakeResponse) action.response).getPayload())
.isEqualTo("OK, attempted uploading 2 reports");
verify(emailService)
.sendEmail(
EmailMessage.create(
"ICANN Monthly report upload summary: 1/2 succeeded",
"ICANN Monthly report upload summary: 3/4 succeeded",
"Report Filename - Upload status:\n"
+ "test-transactions-201706.csv - SUCCESS\n"
+ "a-activity-201706.csv - FAILURE",
+ "foo-activity-200606.csv - SUCCESS\n"
+ "tld-activity-200606.csv - FAILURE\n"
+ "foo-transactions-200606.csv - SUCCESS\n"
+ "tld-transactions-200606.csv - SUCCESS",
new InternetAddress("recipient@example.com"),
new InternetAddress("sender@example.com")));
}
@Test
public void testSuccess_WithRetry() throws Exception {
public void testSuccess_advancesCursor() throws Exception {
writeGcsFile(
gcsService,
new GcsFilename("basin/icann/monthly/2006-06", "tld-activity-200606.csv"),
PAYLOAD_SUCCESS);
when(mockReporter.send(PAYLOAD_SUCCESS, "tld-activity-200606.csv")).thenReturn(true);
IcannReportingUploadAction action = createAction();
when(mockReporter.send(PAYLOAD_SUCCESS, "test-transactions-201706.csv"))
action.run();
ofy().clearSessionCache();
Cursor cursor =
ofy()
.load()
.key(Cursor.createKey(CursorType.ICANN_UPLOAD_ACTIVITY, Registry.get("tld")))
.now();
assertThat(cursor.getCursorTime()).isEqualTo(DateTime.parse("2006-07-01TZ"));
}
@Test
public void testSuccess_noUploadsNeeded() throws Exception {
clock.setTo(DateTime.parse("2006-5-01T00:30:00Z"));
IcannReportingUploadAction action = createAction();
action.run();
ofy().clearSessionCache();
verifyNoMoreInteractions(mockReporter);
verify(emailService)
.sendEmail(
EmailMessage.create(
"ICANN Monthly report upload summary: 0/0 succeeded",
"Report Filename - Upload status:\n",
new InternetAddress("recipient@example.com"),
new InternetAddress("sender@example.com")));
}
@Test
public void testSuccess_uploadManifest() throws Exception {
persistResource(
Cursor.createGlobal(CursorType.ICANN_UPLOAD_MANIFEST, DateTime.parse("2006-06-06TZ")));
IcannReportingUploadAction action = createAction();
action.run();
ofy().clearSessionCache();
Cursor cursor =
ofy().load().key(Cursor.createGlobalKey(CursorType.ICANN_UPLOAD_MANIFEST)).now();
assertThat(cursor.getCursorTime()).isEqualTo(DateTime.parse("2006-07-01TZ"));
verify(mockReporter).send(PAYLOAD_FAIL, "tld-activity-200606.csv");
verify(mockReporter).send(PAYLOAD_SUCCESS, "foo-activity-200606.csv");
verify(mockReporter).send(PAYLOAD_SUCCESS, "foo-transactions-200606.csv");
verify(mockReporter).send(PAYLOAD_SUCCESS, "tld-transactions-200606.csv");
verify(mockReporter).send(MANIFEST_PAYLOAD, "MANIFEST.txt");
verifyNoMoreInteractions(mockReporter);
}
@Test
public void testSuccess_withRetry() throws Exception {
IcannReportingUploadAction action = createAction();
when(mockReporter.send(PAYLOAD_SUCCESS, "tld-transactions-200606.csv"))
.thenThrow(new IOException("Expected exception."))
.thenReturn(true);
action.run();
verify(mockReporter, times(2)).send(PAYLOAD_SUCCESS, "test-transactions-201706.csv");
verify(mockReporter).send(PAYLOAD_FAIL, "a-activity-201706.csv");
verify(mockReporter).send(PAYLOAD_SUCCESS, "foo-activity-200606.csv");
verify(mockReporter).send(PAYLOAD_FAIL, "tld-activity-200606.csv");
verify(mockReporter).send(PAYLOAD_SUCCESS, "foo-transactions-200606.csv");
verify(mockReporter, times(2)).send(PAYLOAD_SUCCESS, "tld-transactions-200606.csv");
verifyNoMoreInteractions(mockReporter);
assertThat(((FakeResponse) action.response).getPayload())
.isEqualTo("OK, attempted uploading 2 reports");
verify(emailService)
.sendEmail(
EmailMessage.create(
"ICANN Monthly report upload summary: 1/2 succeeded",
"ICANN Monthly report upload summary: 3/4 succeeded",
"Report Filename - Upload status:\n"
+ "test-transactions-201706.csv - SUCCESS\n"
+ "a-activity-201706.csv - FAILURE",
new InternetAddress("recipient@example.com"),
new InternetAddress("sender@example.com")));
}
@Test
public void testFailure_firstUnrecoverable_stillAttemptsUploadingBoth() throws Exception {
IcannReportingUploadAction action = createAction();
when(mockReporter.send(PAYLOAD_SUCCESS, "test-transactions-201706.csv"))
.thenThrow(new IOException("Expected exception"));
action.run();
verify(mockReporter, times(3)).send(PAYLOAD_SUCCESS, "test-transactions-201706.csv");
verify(mockReporter).send(PAYLOAD_FAIL, "a-activity-201706.csv");
verifyNoMoreInteractions(mockReporter);
assertThat(((FakeResponse) action.response).getPayload())
.isEqualTo("OK, attempted uploading 2 reports");
verify(emailService)
.sendEmail(
EmailMessage.create(
"ICANN Monthly report upload summary: 0/2 succeeded",
"Report Filename - Upload status:\n"
+ "test-transactions-201706.csv - FAILURE\n"
+ "a-activity-201706.csv - FAILURE",
+ "foo-activity-200606.csv - SUCCESS\n"
+ "tld-activity-200606.csv - FAILURE\n"
+ "foo-transactions-200606.csv - SUCCESS\n"
+ "tld-transactions-200606.csv - SUCCESS",
new InternetAddress("recipient@example.com"),
new InternetAddress("sender@example.com")));
}
@@ -169,38 +252,149 @@ public class IcannReportingUploadActionTest {
new IOException("Your IP address 25.147.130.158 is not allowed to connect"));
}
@Test
public void testFailure_cursorIsNotAdvancedForward() throws Exception {
runTest_nonRetryableException(
new IOException("Your IP address 25.147.130.158 is not allowed to connect"));
ofy().clearSessionCache();
Cursor cursor =
ofy()
.load()
.key(Cursor.createKey(CursorType.ICANN_UPLOAD_ACTIVITY, Registry.get("tld")))
.now();
assertThat(cursor.getCursorTime()).isEqualTo(DateTime.parse("2006-06-06TZ"));
}
@Test
public void testNotRunIfCursorDateIsAfterToday() throws Exception {
clock.setTo(DateTime.parse("2006-05-01T00:30:00Z"));
IcannReportingUploadAction action = createAction();
action.run();
ofy().clearSessionCache();
Cursor cursor =
ofy()
.load()
.key(Cursor.createKey(CursorType.ICANN_UPLOAD_ACTIVITY, Registry.get("foo")))
.now();
assertThat(cursor.getCursorTime()).isEqualTo(DateTime.parse("2006-06-06TZ"));
verifyNoMoreInteractions(mockReporter);
}
private void runTest_nonRetryableException(Exception nonRetryableException) throws Exception {
IcannReportingUploadAction action = createAction();
when(mockReporter.send(PAYLOAD_FAIL, "a-activity-201706.csv"))
when(mockReporter.send(PAYLOAD_FAIL, "tld-activity-200606.csv"))
.thenThrow(nonRetryableException)
.thenThrow(
new AssertionError(
"This should never be thrown because the previous exception isn't retryable"));
action.run();
verify(mockReporter, times(1)).send(PAYLOAD_FAIL, "a-activity-201706.csv");
verify(mockReporter).send(PAYLOAD_SUCCESS, "test-transactions-201706.csv");
verify(mockReporter).send(PAYLOAD_SUCCESS, "foo-activity-200606.csv");
verify(mockReporter, times(1)).send(PAYLOAD_FAIL, "tld-activity-200606.csv");
verify(mockReporter).send(PAYLOAD_SUCCESS, "foo-transactions-200606.csv");
verify(mockReporter).send(PAYLOAD_SUCCESS, "tld-transactions-200606.csv");
verifyNoMoreInteractions(mockReporter);
assertThat(((FakeResponse) action.response).getPayload())
.isEqualTo("OK, attempted uploading 2 reports");
verify(emailService)
.sendEmail(
EmailMessage.create(
"ICANN Monthly report upload summary: 1/2 succeeded",
"ICANN Monthly report upload summary: 3/4 succeeded",
"Report Filename - Upload status:\n"
+ "test-transactions-201706.csv - SUCCESS\n"
+ "a-activity-201706.csv - FAILURE",
+ "foo-activity-200606.csv - SUCCESS\n"
+ "tld-activity-200606.csv - FAILURE\n"
+ "foo-transactions-200606.csv - SUCCESS\n"
+ "tld-transactions-200606.csv - SUCCESS",
new InternetAddress("recipient@example.com"),
new InternetAddress("sender@example.com")));
}
@Test
public void testFail_FileNotFound() throws Exception {
public void testFail_fileNotFound() throws Exception {
IcannReportingUploadAction action = createAction();
action.subdir = "somewhere/else";
IllegalArgumentException thrown = assertThrows(IllegalArgumentException.class, action::run);
action.run();
assertAboutLogs()
.that(logHandler)
.hasLogAtLevelWithMessage(
Level.SEVERE,
"Could not upload ICANN_UPLOAD_ACTIVITY report for foo because file"
+ " foo-activity-200606.csv did not exist");
}
@Test
public void testWarning_fileNotStagedYet() throws Exception {
persistResource(
Cursor.create(
CursorType.ICANN_UPLOAD_ACTIVITY, DateTime.parse("2006-07-01TZ"), Registry.get("foo")));
clock.setTo(DateTime.parse("2006-07-01T00:30:00Z"));
IcannReportingUploadAction action = createAction();
action.subdir = "icann/monthly/2006-07";
action.run();
assertAboutLogs()
.that(logHandler)
.hasLogAtLevelWithMessage(
Level.INFO,
"Could not upload ICANN_UPLOAD_ACTIVITY report for foo because file"
+ " foo-activity-200607.csv did not exist. This report may not have been staged"
+ " yet.");
}
@Test
public void testFailure_lockIsntAvailable() throws Exception {
IcannReportingUploadAction action = createAction();
action.lockHandler = new FakeLockHandler(false);
ServiceUnavailableException thrown =
assertThrows(ServiceUnavailableException.class, () -> action.run());
assertThat(thrown)
.hasMessageThat()
.isEqualTo("Object MANIFEST.txt in bucket basin/somewhere/else not found");
.contains("Lock for IcannReportingUploadAction already in use");
}
@Test
public void testSuccess_nullCursors() throws Exception {
createTlds("new");
writeGcsFile(
gcsService,
new GcsFilename("basin/icann/monthly/2006-06", "new-transactions-200606.csv"),
PAYLOAD_SUCCESS);
writeGcsFile(
gcsService,
new GcsFilename("basin/icann/monthly/2006-06", "new-activity-200606.csv"),
PAYLOAD_SUCCESS);
when(mockReporter.send(PAYLOAD_SUCCESS, "new-transactions-200606.csv")).thenReturn(true);
when(mockReporter.send(PAYLOAD_SUCCESS, "new-activity-200606.csv")).thenReturn(true);
IcannReportingUploadAction action = createAction();
action.run();
verify(mockReporter).send(PAYLOAD_SUCCESS, "foo-activity-200606.csv");
verify(mockReporter).send(PAYLOAD_FAIL, "tld-activity-200606.csv");
verify(mockReporter).send(PAYLOAD_SUCCESS, "new-activity-200606.csv");
verify(mockReporter).send(PAYLOAD_SUCCESS, "foo-transactions-200606.csv");
verify(mockReporter).send(PAYLOAD_SUCCESS, "tld-transactions-200606.csv");
verify(mockReporter).send(PAYLOAD_SUCCESS, "new-transactions-200606.csv");
verifyNoMoreInteractions(mockReporter);
verify(emailService)
.sendEmail(
EmailMessage.create(
"ICANN Monthly report upload summary: 5/6 succeeded",
"Report Filename - Upload status:\n"
+ "foo-activity-200606.csv - SUCCESS\n"
+ "new-activity-200606.csv - SUCCESS\n"
+ "tld-activity-200606.csv - FAILURE\n"
+ "foo-transactions-200606.csv - SUCCESS\n"
+ "new-transactions-200606.csv - SUCCESS\n"
+ "tld-transactions-200606.csv - SUCCESS",
new InternetAddress("recipient@example.com"),
new InternetAddress("sender@example.com")));
Cursor newActivityCursor =
ofy()
.load()
.key(Cursor.createKey(CursorType.ICANN_UPLOAD_ACTIVITY, Registry.get("new")))
.now();
assertThat(newActivityCursor.getCursorTime()).isEqualTo(DateTime.parse("2006-07-01TZ"));
Cursor newTransactionCursor =
ofy().load().key(Cursor.createKey(CursorType.ICANN_UPLOAD_TX, Registry.get("new"))).now();
assertThat(newTransactionCursor.getCursorTime()).isEqualTo(DateTime.parse("2006-07-01TZ"));
}
}

View File

@@ -0,0 +1,53 @@
// Copyright 2019 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.schema.tld;
import static com.google.common.truth.Truth.assertThat;
import com.google.common.collect.ImmutableMap;
import google.registry.model.registry.label.ReservationType;
import google.registry.schema.tld.ReservedList.ReservedEntry;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
/** Unit tests for {@link ReservedList} */
@RunWith(JUnit4.class)
public class ReservedListTest {
@Test
public void verifyConstructorAndGetters_workCorrectly() {
ReservedList reservedList =
ReservedList.create(
"app",
false,
ImmutableMap.of(
"book",
ReservedEntry.create(ReservationType.ALLOWED_IN_SUNRISE, null),
"music",
ReservedEntry.create(
ReservationType.RESERVED_FOR_ANCHOR_TENANT, "reserved for anchor tenant")));
assertThat(reservedList.getName()).isEqualTo("app");
assertThat(reservedList.getShouldPublish()).isFalse();
assertThat(reservedList.getLabelsToReservations())
.containsExactly(
"book",
ReservedEntry.create(ReservationType.ALLOWED_IN_SUNRISE, null),
"music",
ReservedEntry.create(
ReservationType.RESERVED_FOR_ANCHOR_TENANT, "reserved for anchor tenant"));
}
}

View File

@@ -17,7 +17,10 @@ package google.registry.tools;
import static com.google.common.truth.Truth.assertThat;
import static java.nio.charset.StandardCharsets.UTF_8;
import com.google.common.base.Charsets;
import com.google.common.io.Files;
import com.google.common.io.Resources;
import google.registry.persistence.NomulusPostgreSql;
import java.io.File;
import org.junit.Before;
import org.junit.ClassRule;
@@ -37,8 +40,9 @@ public class GenerateSqlSchemaCommandTest extends CommandTestCase<GenerateSqlSch
@Rule public TemporaryFolder tmp = new TemporaryFolder();
@ClassRule public static PostgreSQLContainer postgres =
new PostgreSQLContainer()
@ClassRule
public static PostgreSQLContainer postgres =
new PostgreSQLContainer(NomulusPostgreSql.getDockerTag())
.withDatabaseName("postgres")
.withUsername("postgres")
.withPassword("domain-registry");
@@ -84,4 +88,14 @@ public class GenerateSqlSchemaCommandTest extends CommandTestCase<GenerateSqlSch
"--start_postgresql", "--out_file=" + tmp.getRoot() + File.separatorChar + "schema.sql");
assertThat(new File(tmp.getRoot(), "schema.sql").exists()).isTrue();
}
@Test
public void validateGeneratedSchemaIsSameAsSchemaInFile() throws Exception {
runCommand(
"--start_postgresql", "--out_file=" + tmp.getRoot() + File.separatorChar + "schema.sql");
assertThat(Files.readLines(new File(tmp.getRoot(), "schema.sql"), Charsets.UTF_8))
.isEqualTo(
Resources.readLines(
Resources.getResource("sql/schema/db-schema.sql.generated"), Charsets.UTF_8));
}
}

View File

@@ -9,16 +9,16 @@ Nomulus uses the 'postgres' database in the 'public' schema. The following
users/roles are defined:
* postgres: the initial user is used for admin and schema deployment.
* In Cloud SQL, we do not control superusers. The initial 'postgres' user
is a regular user with create-role/create-db privileges. Therefore,
it is not possible to separate admin user and schema-deployment user.
* In Cloud SQL, we do not control superusers. The initial 'postgres' user
is a regular user with create-role/create-db privileges. Therefore, it
is not possible to separate admin user and schema-deployment user.
* readwrite is a role with read-write privileges on all data tables and
sequences. However, it does not have write access to admin tables. Nor
can it create new tables.
sequences. However, it does not have write access to admin tables. Nor can
it create new tables.
* The Registry server user is granted this role.
* readonly is a role with SELECT privileges on all tables.
* Reporting job user and individual human readers may be granted
this role.
* Reporting job user and individual human readers may be granted this
role.
### Schema DDL Scripts
@@ -33,23 +33,29 @@ Below are the steps to submit a schema change:
`core/src/main/resources/META-INF/persistence.xml` so they'll be picked up.
2. Run the `nomulus generate_sql_schema` command to generate a new version of
`db-schema.sql.generated`. The full command line to do this is:
`./gradlew registryTool --args="-e localhost generate_sql_schema --start_postgresql -o /path/to/nomulus/db/src/main/resources/sql/schema/db-schema.sql.generated"`
3. Write an incremental DDL script that changes the existing schema to your
new one. The generated SQL file from the previous step should help. New
create table statements can be used as is, whereas alter table statements
should be written to change any existing tables.
`./gradlew registryTool --args="-e localhost generate_sql_schema
--start_postgresql -o
/path/to/nomulus/db/src/main/resources/sql/schema/db-schema.sql.generated"`
3. Write an incremental DDL script that changes the existing schema to your new
one. The generated SQL file from the previous step should help. New create
table statements can be used as is, whereas alter table statements should be
written to change any existing tables.
This script should be stored in a new file in the
`db/src/main/resources/sql/flyway` folder using the naming pattern
`V{id}__{description text}.sql`, where `{id}` is the next highest number
following the existing scripts in that folder. Note the double underscore in
the naming pattern.
4. Run the `:db:test` task from the Gradle root project. The SchemaTest will
fail because the new schema does not match the golden file.
5. Copy `db/build/resources/test/testcontainer/mount/dump.txt` to the golden file
`db/src/main/resources/sql/schema/nomulus.golden.sql`. Diff it against the
old version and verify that all changes are expected.
5. Copy `db/build/resources/test/testcontainer/mount/dump.txt` to the golden
file `db/src/main/resources/sql/schema/nomulus.golden.sql`. Diff it against
the old version and verify that all changes are expected.
6. Re-run the `:db:test` task. This time all tests should pass.
Relevant files (under db/src/main/resources/sql/schema/):
@@ -66,23 +72,63 @@ example, when adding a new column to a table, we would deploy the change before
adding it to the relevant ORM class. Therefore, for a short time the golden file
will contain the new column while the generated one does not.
### Non-production Schema Push
### Schema Push
To manage schema in a non-production environment, use the 'flywayMigration'
task. You will need Cloud SDK and login once.
Currently Cloud SQL schema is released with the Nomulus server, and shares the
server release's tag (e.g., nomulus-20191101-RC00). Automatic schema push
process (to apply new changes in a released schema to the databases) has not
been set up yet, and new schema may be pushed manually on demand.
Presubmit and continuous-integration tests are being implemented to ensure
server/schema compatibility. Before the tests are activated, please look for
breaking changes before deploying a schema.
Released schema may be deployed using Cloud Build. Use the root project
directory as working directory, run the following shell snippets:
```shell
# Tags exist as folder names under gs://domain-registry-dev-deploy.
SCHEMA_TAG=
# Recognized environments are alpha, crash, sandbox and production
SQL_ENV=
# Deploy on cloud build. The --project is optional if domain-registry-dev
# is already your default project.
gcloud builds submit --config=release/cloudbuild-schema-deploy.yaml \
--substitutions=TAG_NAME=${SCHEMA_TAG},_ENV=${SQL_ENV} \
--project domain-registry-dev
# Verify by checking Flyway Schema History:
./gradlew :db:flywayInfo -PdbServer=${SQL_ENV}
```
#### Glass Breaking
If you need to deploy a schema off-cycle, try making a release first, then
deploy that release schema to Cloud SQL.
TODO(weiminyu): elaborate on different ways to push schema without a full
release.
#### Notes On Flyway
Please note: to run Flyway commands, you need Cloud SDK and need to log in once.
```shell
# One time login
gcloud auth login
# Deploy the current schema to alpha
gradlew :db:flywayMigrate -PdbServer=alpha
# Delete the entire schema in alpha
gradlew :db:flywayClean -PdbServer=alpha
```
The flywayMigrate task is idempotent. Repeated runs will not introduce problems.
The Flyway-based Cloud Build schema push process is safe in common scenarios:
* Repeatedly deploying the latest schema is safe. All duplicate runs become
NOP.
* Accidentally deploying a past schema is safe. Flyway will not undo
incremental changes not reflected in the deployed schema.
* Concurrent deployment runs are safe. Flyway locks its own metadata table,
serializing deployment runs without affecting normal accesses.
#### Schema Push to Local Database
The Flyway tasks may also be used to deploy to local instances, e.g, your own
test instance. E.g.,
@@ -95,7 +141,3 @@ gradlew :db:flywayMigrate -PdbServer=192.168.9.2 -PdbPassword=domain-registry
gradlew :db:flywayMigrate -PdbServer=192.168.9.2:5432 -PdbUser=postgres \
-PdbPassword=domain-registry
```
### Production Schema Deployment
Schema deployment to production and sandbox is under development.

View File

@@ -31,24 +31,8 @@ ext {
def dbServer = findProperty(dbServerProperty).toString().toLowerCase()
def dbName = findProperty(dbNameProperty)
reconfirmRestrictedDbEnv = {
if (!restrictedDbEnv.contains(dbServer)) {
return
}
// For restricted environments, ask the user to type again to confirm.
// The following statement uses Gradle internal API to get around the
// missing console bug when Gradle Daemon is in use. Another option is
// to use the ant.input task. For details please refer to
// https://github.com/gradle/gradle/issues/1251.
def dbServerAgain = services.get(UserInputHandler.class).askQuestion(
"""\
Are you sure? Operating on ${dbServer} from desktop is unsafe.
Please type '${dbServer}' again to proceed: """.stripIndent(),
'').trim()
if (dbServer != dbServerAgain) {
throw new RuntimeException(
"Failed to confirm for restricted database environment. Operation aborted.")
}
isCloudSql = {
return allDbEnv.contains(dbServer)
}
getAccessInfoByHostPort = { hostAndPort ->
@@ -87,18 +71,14 @@ ext {
// production. The role parameter may be superuser. (More roles will be added
// later).
getCloudSqlCredential = { env, role ->
env = env == 'production' ? '' : "-${env}"
def keyProject = env == '-crash'
? 'domain-registry-crash-kms-keys'
: "domain-registry${env}-keys"
def command =
"""gsutil cp \
gs://domain-registry${env}-cloudsql-credentials/${role}_credential.enc - | \
gs://${rootProject.devProject}-deploy/cloudsql-credentials/${env}/${role}_credential.enc - | \
base64 -d | \
gcloud kms decrypt --location global --keyring nomulus \
--key sql-credentials-on-gcs-key --plaintext-file=- \
gcloud kms decrypt --location global --keyring nomulus-tool-keyring \
--key nomulus-tool-key --plaintext-file=- \
--ciphertext-file=- \
--project=${keyProject}"""
--project=${rootProject.devProject}"""
return execInBash(command, '/tmp')
}
@@ -112,8 +92,23 @@ task schemaJar(type: Jar) {
}
}
// Expose NomulusPostgreSql class to ':core' for compile, without leaking
// unnecessary dependencies to the release artifacts through ':core'.
// Jar is put in the 'compileApi' configuration.
task compileApiJar(type: Jar) {
archiveBaseName = 'compile'
from(sourceSets.main.output) {
include 'google/registry/persistence/NomulusPostgreSql**'
}
}
configurations {
compileApi
}
artifacts {
archives schemaJar
compileApi compileApiJar
}
publishing {
@@ -132,7 +127,6 @@ publishing {
}
}
flyway {
def accessInfo = project.ext.getJdbcAccessInfo()
@@ -144,13 +138,6 @@ flyway {
locations = [ "classpath:sql/flyway" ]
}
tasks.flywayMigrate.dependsOn(
tasks.create('confirmMigrateOnRestrictedDb') {
doLast {
project.ext.reconfirmRestrictedDbEnv()
}
})
dependencies {
def deps = rootProject.dependencyMap
@@ -170,7 +157,23 @@ dependencies {
testCompile project(':third_party')
}
// Ensure that resources are rebuilt before running Flyway tasks
tasks
.findAll { task -> task.group.equals('Flyway')}
.collect { task -> task.dependsOn('buildNeeded') }
flywayValidate.dependsOn('buildNeeded')
if (ext.isCloudSql()) {
// Disable dangerous Flyway tasks. Only allow info and validate.
tasks.findAll { task -> task.group.equals('Flyway')}.each {
if (it.name == 'flywayMigrate') {
it.doFirst {
throw new UnsupportedOperationException(
""" \
FlywayMigrate is disabled. See README.md for schema deployment
instructions.""".stripIndent())
}
} else if (it.name != 'flywayInfo' && it.name != 'flywayValidate') {
it.doFirst {
throw new UnsupportedOperationException(
"${it.name} from commandline is not allowed.")
}
}
}
}

View File

@@ -0,0 +1,3 @@
# This is a Gradle generated file for dependency locking.
# Manual edits can break the build and are not advised.
# This file is expected to be part of source control.

View File

@@ -1,4 +1,4 @@
// Copyright 2017 The Nomulus Authors. All Rights Reserved.
// Copyright 2019 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.
@@ -11,22 +11,17 @@
// 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.persistence;
package google.registry.util;
/** Information about Nomulus' Cloud SQL PostgreSql instance. */
public class NomulusPostgreSql {
import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
/** The current PostgreSql version in Cloud SQL. */
// TODO(weiminyu): setup periodic checks to detect version changes in Cloud SQL.
private static final String TARGET_VERSION = "11.5";
/** A simple clock that always returns a fixed time. */
public class FixedClock implements Clock {
private final DateTime nowUtc;
public FixedClock(long millisSinceEpoch) {
this.nowUtc = new DateTime(millisSinceEpoch, DateTimeZone.UTC);
}
@Override
public DateTime nowUtc() {
return nowUtc;
/** Returns the docker image tag of the targeted Postgresql server version. */
public static String getDockerTag() {
return "postgres:" + TARGET_VERSION;
}
}

View File

@@ -0,0 +1,36 @@
-- Copyright 2019 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.
create table "ReservedEntry" (
revision_id int8 not null,
comment text,
reservation_type int4 not null,
domain_label text not null,
primary key (revision_id, domain_label)
);
create table "ReservedList" (
revision_id bigserial not null,
creation_timestamp timestamptz not null,
name text not null,
should_publish boolean not null,
primary key (revision_id)
);
create index reservedlist_name_idx on "ReservedList" (name);
alter table if exists "ReservedEntry"
add constraint FKgq03rk0bt1hb915dnyvd3vnfc
foreign key (revision_id)
references "ReservedList";

View File

@@ -44,9 +44,9 @@
repo_id text not null,
creation_client_id text,
current_sponsor_client_id text,
deletion_time bytea,
deletion_time timestamptz,
last_epp_update_client_id text,
last_epp_update_time bytea,
last_epp_update_time timestamptz,
revisions bytea,
auth_info_repo_id text,
auth_info_value text,
@@ -55,12 +55,12 @@
delete_poll_message bytea,
fully_qualified_domain_name text,
idn_table_name text,
last_transfer_time bytea,
launch_notice_accepted_time bytea,
launch_notice_expiration_time bytea,
last_transfer_time timestamptz,
launch_notice_accepted_time timestamptz,
launch_notice_expiration_time timestamptz,
launch_notice_tcn_id text,
launch_notice_validator_id text,
registration_expiration_time bytea,
registration_expiration_time timestamptz,
smd_id text,
tld text,
transfer_data_server_approve_autorenrew_event bytea,
@@ -70,11 +70,11 @@
value int4,
client_transaction_id text,
server_transaction_id text,
transfer_data_registration_expiration_time bytea,
transfer_data_registration_expiration_time timestamptz,
gaining_client_id text,
losing_client_id text,
pending_transfer_expiration_time bytea,
transfer_request_time bytea,
pending_transfer_expiration_time timestamptz,
transfer_request_time timestamptz,
transfer_status int4,
primary key (repo_id)
);
@@ -117,7 +117,7 @@
billing_event_one_time bytea,
billing_event_recurring bytea,
client_id text,
expiration_time bytea,
expiration_time timestamptz,
type int4,
primary key (id)
);
@@ -152,6 +152,22 @@
primary key (revision_id)
);
create table "ReservedEntry" (
revision_id int8 not null,
comment text,
reservation_type int4 not null,
domain_label text not null,
primary key (revision_id, domain_label)
);
create table "ReservedList" (
revision_id bigserial not null,
creation_timestamp timestamptz not null,
name text not null,
should_publish boolean not null,
primary key (revision_id)
);
alter table if exists "Domain_DelegationSignerData"
add constraint UK_2yp55erx1i51pa7gnb8bu7tjn unique (ds_data_key_tag);
@@ -166,6 +182,7 @@ create index idx_registry_lock_registrar_id on "RegistryLock" (registrar_id);
alter table if exists "RegistryLock"
add constraint idx_registry_lock_repo_id_revision_id unique (repo_id, revision_id);
create index reservedlist_name_idx on "ReservedList" (name);
alter table if exists "ClaimsEntry"
add constraint FK6sc6at5hedffc0nhdcab6ivuq
@@ -221,3 +238,8 @@ create index idx_registry_lock_registrar_id on "RegistryLock" (registrar_id);
add constraint FKo0gw90lpo1tuee56l0nb6y6g5
foreign key (revision_id)
references "PremiumList";
alter table if exists "ReservedEntry"
add constraint FKgq03rk0bt1hb915dnyvd3vnfc
foreign key (revision_id)
references "ReservedList";

View File

@@ -2,8 +2,8 @@
-- PostgreSQL database dump
--
-- Dumped from database version 9.6.12
-- Dumped by pg_dump version 9.6.12
-- Dumped from database version 11.5 (Debian 11.5-3.pgdg90+1)
-- Dumped by pg_dump version 11.5 (Debian 11.5-3.pgdg90+1)
SET statement_timeout = 0;
SET lock_timeout = 0;
@@ -12,23 +12,10 @@ SET client_encoding = 'UTF8';
SET standard_conforming_strings = on;
SELECT pg_catalog.set_config('search_path', '', false);
SET check_function_bodies = false;
SET xmloption = content;
SET client_min_messages = warning;
SET row_security = off;
--
-- Name: plpgsql; Type: EXTENSION; Schema: -; Owner: -
--
CREATE EXTENSION IF NOT EXISTS plpgsql WITH SCHEMA pg_catalog;
--
-- Name: EXTENSION plpgsql; Type: COMMENT; Schema: -; Owner: -
--
COMMENT ON EXTENSION plpgsql IS 'PL/pgSQL procedural language';
SET default_tablespace = '';
SET default_with_oids = false;
@@ -154,6 +141,49 @@ CREATE SEQUENCE public."RegistryLock_revision_id_seq"
ALTER SEQUENCE public."RegistryLock_revision_id_seq" OWNED BY public."RegistryLock".revision_id;
--
-- Name: ReservedEntry; Type: TABLE; Schema: public; Owner: -
--
CREATE TABLE public."ReservedEntry" (
revision_id bigint NOT NULL,
comment text,
reservation_type integer NOT NULL,
domain_label text NOT NULL
);
--
-- Name: ReservedList; Type: TABLE; Schema: public; Owner: -
--
CREATE TABLE public."ReservedList" (
revision_id bigint NOT NULL,
creation_timestamp timestamp with time zone NOT NULL,
name text NOT NULL,
should_publish boolean NOT NULL
);
--
-- Name: ReservedList_revision_id_seq; Type: SEQUENCE; Schema: public; Owner: -
--
CREATE SEQUENCE public."ReservedList_revision_id_seq"
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
--
-- Name: ReservedList_revision_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: -
--
ALTER SEQUENCE public."ReservedList_revision_id_seq" OWNED BY public."ReservedList".revision_id;
--
-- Name: ClaimsList revision_id; Type: DEFAULT; Schema: public; Owner: -
--
@@ -175,6 +205,13 @@ ALTER TABLE ONLY public."PremiumList" ALTER COLUMN revision_id SET DEFAULT nextv
ALTER TABLE ONLY public."RegistryLock" ALTER COLUMN revision_id SET DEFAULT nextval('public."RegistryLock_revision_id_seq"'::regclass);
--
-- Name: ReservedList revision_id; Type: DEFAULT; Schema: public; Owner: -
--
ALTER TABLE ONLY public."ReservedList" ALTER COLUMN revision_id SET DEFAULT nextval('public."ReservedList_revision_id_seq"'::regclass);
--
-- Name: ClaimsEntry ClaimsEntry_pkey; Type: CONSTRAINT; Schema: public; Owner: -
--
@@ -215,6 +252,22 @@ ALTER TABLE ONLY public."RegistryLock"
ADD CONSTRAINT "RegistryLock_pkey" PRIMARY KEY (revision_id);
--
-- Name: ReservedEntry ReservedEntry_pkey; Type: CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY public."ReservedEntry"
ADD CONSTRAINT "ReservedEntry_pkey" PRIMARY KEY (revision_id, domain_label);
--
-- Name: ReservedList ReservedList_pkey; Type: CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY public."ReservedList"
ADD CONSTRAINT "ReservedList_pkey" PRIMARY KEY (revision_id);
--
-- Name: RegistryLock idx_registry_lock_repo_id_revision_id; Type: CONSTRAINT; Schema: public; Owner: -
--
@@ -244,6 +297,13 @@ CREATE INDEX idx_registry_lock_verification_code ON public."RegistryLock" USING
CREATE INDEX premiumlist_name_idx ON public."PremiumList" USING btree (name);
--
-- Name: reservedlist_name_idx; Type: INDEX; Schema: public; Owner: -
--
CREATE INDEX reservedlist_name_idx ON public."ReservedList" USING btree (name);
--
-- Name: ClaimsEntry fk6sc6at5hedffc0nhdcab6ivuq; Type: FK CONSTRAINT; Schema: public; Owner: -
--
@@ -252,6 +312,14 @@ ALTER TABLE ONLY public."ClaimsEntry"
ADD CONSTRAINT fk6sc6at5hedffc0nhdcab6ivuq FOREIGN KEY (revision_id) REFERENCES public."ClaimsList"(revision_id);
--
-- Name: ReservedEntry fkgq03rk0bt1hb915dnyvd3vnfc; Type: FK CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY public."ReservedEntry"
ADD CONSTRAINT fkgq03rk0bt1hb915dnyvd3vnfc FOREIGN KEY (revision_id) REFERENCES public."ReservedList"(revision_id);
--
-- Name: PremiumEntry fko0gw90lpo1tuee56l0nb6y6g5; Type: FK CONSTRAINT; Schema: public; Owner: -
--

View File

@@ -19,6 +19,7 @@ import static google.registry.testing.TextDiffSubject.assertThat;
import com.google.common.base.Joiner;
import com.google.common.io.Resources;
import google.registry.persistence.NomulusPostgreSql;
import java.io.File;
import java.net.URL;
import java.nio.charset.StandardCharsets;
@@ -54,7 +55,7 @@ public class SchemaTest {
*/
@Rule
public PostgreSQLContainer sqlContainer =
new PostgreSQLContainer<>("postgres:9.6.12")
new PostgreSQLContainer<>(NomulusPostgreSql.getDockerTag())
.withClasspathResourceMapping(
MOUNTED_RESOURCE_PATH, CONTAINER_MOUNT_POINT, BindMode.READ_WRITE);

View File

@@ -31,8 +31,8 @@ reconstruct a premium list .txt file from the premium list that is loaded into
Datastore (though in principle it would be easy to do by writing a tool to do
so), so don't lose those .txt files.
An example premium list can be found at
`java/google/registry/model/registry/config/files/premium/example.txt`.
The nomulus repository contains an
[example premium list file](https://github.com/google/nomulus/blob/master/core/src/main/java/google/registry/config/files/premium/example.txt).
## Creating a premium list
@@ -52,6 +52,9 @@ that the convention of naming premium lists after the TLD they are intended to
be used for is enforced unless the override parameter `-o` is passed, which
allows premium lists to be created with any name.
You're not done yet! After creating the premium list you must the apply it to
one or more TLDs (see below) for it to actually be used.
## Updating a premium list
If the premium list already exists and you want to update it with new prices
@@ -66,6 +69,10 @@ Perform this command? (y/N): y
Successfully saved premium list exampletld
```
If this premium list is already applied to a TLD, then changes will take up to
60 minutes to take effect (depending on how you've configured the relevant
caching interval; 60 minutes is the default).
## Applying a premium list to a TLD
Separate from the management of the contents of individual premium lists, a
@@ -92,9 +99,9 @@ all other information about a TLD). It is used as follows:
```shell
$ nomulus -e {ENVIRONMENT} get_tld exampletld
[ ... snip ... ]
[ ... snip output ... ]
premiumList=Key<?>(EntityGroupRoot("cross-tld")/PremiumList("exampletld"))
[ ... snip ... ]
[ ... snip output ... ]
```
## Listing all available premium lists
@@ -107,3 +114,22 @@ $ nomulus -e {ENVIRONMENT} list_premium_lists
exampletld
someotherlist
```
## Verifying premium list updates
To verify that the changes have actually been applied, you can run a domain
check on a modified entry using the `nomulus check_domain` command and verify
that the domain now has the correct price.
```shell
$ nomulus -e production check_domain {domain_name}
[ ... snip output ... ]
```
**Note that the list can be cached for up to 60 minutes, so the old value may
still be returned for a little while**. If it is urgent that the new pricing
changes be applied, and it's OK to potentially interrupt client connections,
then you can use the App Engine web console to kill instances of the `default`
service, as the cache is per-instance. Once you've killed all the existing
instances (don't kill them all at once!), all of the newly spun up instances
will now be using the new values you've configured.

View File

@@ -65,14 +65,19 @@ acmecorp,RESERVED_FOR_ANCHOR_TENANT
internaldomain,NAMESERVER_RESTRICTED,ns1.internal.tld:ns1.internal.tld
```
# Reserved list file name format
There are two types of reserved lists: Those that are intended to apply to a
specifc TLD, and are thus prefixed with the name of the TLD followed by an
specific TLD, and are thus prefixed with the name of the TLD followed by an
underscore, and those that can be applied to any TLD, and are prefixed with
`common_`. For example, a list of reserved labels on the TLD `exampletld` might
be named `exampletld_blocked-names.txt`, whereas a similar list intended to
apply to multiple TLDs might be named `common_blocked-names.txt`. Note that
these naming conventions are enforced by the tooling used to create and apply
reserved lists (see subsequent sections).
reserved lists (see subsequent sections). The two naming patterns are thus:
* `common_list-name.txt`
* `tldname_list-name.txt`
## Creating a reserved list
@@ -82,17 +87,20 @@ purposes of this example, we are creating a common reserved list named
"common_bad-words".
```shell
$ nomulus -e {ENVIRONMENT} create_reserved_list -n common_bad-words \
-i common_bad-words.txt
$ nomulus -e {ENVIRONMENT} create_reserved_list -i common_bad-words.txt
[ ... snip long confirmation prompt ... ]
Perform this command? (y/N): y
Updated 1 entities.
```
`-n` is the name of the reserved list to create, and `-i` is the input file
containing the list. Note that if `-n` is omitted, the list name is inferred
from the input of the filename minus its file extension. It is recommended to
store all lists such that the filename and list name are identical.
Note that `-i` is the input file containing the list. You can optionally specify
the name of the reserved list using `-n`, but when it's omitted as above the
list name is inferred from the name of the filename (minus the file extension).
For ease of tracking track of things, it is recommended to store all lists such
that the filename and list name are identical.
You're not done yet! After creating the reserved list you must the apply it to
one or more TLDs (see below) for it to actually be used.
## Updating a reserved list
@@ -101,15 +109,14 @@ file containing the reserved list entries, then pass it as input to the
`update_reserved_list` command as follows:
```shell
$ nomulus -e {ENVIRONMENT} update_reserved_list -n common_bad-words \
-i common_bad-words.txt
$ nomulus -e {ENVIRONMENT} update_reserved_list -i common_bad-words.txt
[ ... snip diff of changes to list entries ... ]
Perform this command? (y/N): y
Updated 1 entities.
```
Note that, like the create command, the name of the list is inferred from the
filename if the `-n` parameter is omitted.
filename unless you specify the `-n` parameter (not generally recommended).
## Applying a reserved list to a TLD
@@ -149,9 +156,9 @@ purposes here. It is used as follows:
```shell
$ nomulus -e {ENVIRONMENT} get_tld exampletld
[ ... snip ... ]
[ ... snip output ... ]
reservedLists=[Key<?>(EntityGroupRoot("cross-tld")/ReservedList("common_bad-words"))]
[ ... snip ... ]
[ ... snip output ... ]
```
## Listing all available reserved lists
@@ -165,3 +172,22 @@ $ nomulus -e {ENVIRONMENT} list_reserved_lists
common_bad-words
exampletld_some-other-list
```
## Verifying reserved list updates
To verify that the changes have actually been applied, you can run a domain
check on a modified entry using the `nomulus check_domain` command and verify
that the domain now has the correct reservation status.
```shell
$ nomulus -e production check_domain {domain_name}
[ ... snip output ... ]
```
**Note that the list can be cached for up to 60 minutes, so changes may not
take place immediately**. If it is urgent that the new changes be applied, and
it's OK to potentially interrupt client connections, then you can use the App
Engine web console to kill instances of the `default` service, as the cache is
per-instance. Once you've killed all the existing instances (don't kill them all
at once!), all of the newly spun up instances will now be using the new values
you've configured.

View File

@@ -41,6 +41,16 @@ where:
SCRIPT_DIR="$(realpath $(dirname $0))"
function showNoncompliantFiles() {
local forkPoint="$1"
local message="$2"
git diff -U0 ${forkPoint} | \
${SCRIPT_DIR}/google-java-format-diff.py -p1 | \
awk -v "message=$message" \
'/\+\+\+ ([^ ]*)/ { print message $2 }' 1>&2
}
function callGoogleJavaFormatDiff() {
local forkPoint
forkPoint=$(git merge-base --fork-point origin/master)
@@ -48,10 +58,12 @@ function callGoogleJavaFormatDiff() {
local callResult
case "$1" in
"check")
showNoncompliantFiles "$forkPoint" "\033[1mNeeds formatting: "
callResult=$(git diff -U0 ${forkPoint} | \
${SCRIPT_DIR}/google-java-format-diff.py -p1 | wc -l)
;;
"format")
showNoncompliantFiles "$forkPoint" "\033[1mReformatting: "
callResult=$(git diff -U0 ${forkPoint} | \
${SCRIPT_DIR}/google-java-format-diff.py -p1 -i)
;;
@@ -60,6 +72,7 @@ function callGoogleJavaFormatDiff() {
${SCRIPT_DIR}/google-java-format-diff.py -p1)
;;
esac
echo -e "\033[0m" 1>&2
echo "${callResult}"
exit 0
}

View File

@@ -94,8 +94,8 @@ task initCoverageMinimums {
].asImmutable()
rootProject.ext.getMinCoverage = { key ->
if (rootProject.ext.coverageMinimums.containsKey(key)) {
return rootProject.ext.coverageMinimums.get(key)
if (rootProject.coverageMinimums.containsKey(key)) {
return rootProject.coverageMinimums.get(key)
}
return 0.0
}
@@ -117,7 +117,7 @@ jacocoTestCoverageVerification {
// or MISSEDRATIO
// - The 'minimum' threshold, given as a fraction or a percentage (including '%')
limit {
minimum = rootProject.ext.getMinCoverage(project.getName())
minimum = rootProject.getMinCoverage(project.getName())
}
}
}

47
networking/build.gradle Normal file
View File

@@ -0,0 +1,47 @@
// Copyright 2019 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.
dependencies {
def deps = rootProject.dependencyMap
compile deps['com.google.flogger:flogger']
compile deps['com.google.guava:guava']
compile deps['io.netty:netty-buffer']
compile deps['io.netty:netty-codec']
compile deps['io.netty:netty-codec-http']
compile deps['io.netty:netty-common']
compile deps['io.netty:netty-handler']
compile deps['io.netty:netty-transport']
compile deps['javax.inject:javax.inject']
compile project(':util')
runtime deps['com.google.flogger:flogger-system-backend']
runtime deps['io.netty:netty-tcnative-boringssl-static']
testCompile deps['com.google.truth:truth']
testCompile deps['junit:junit']
testCompile deps['org.bouncycastle:bcpkix-jdk15on']
testCompile deps['org.bouncycastle:bcprov-jdk15on']
testCompile project(':third_party')
}
// Make testing artifacts available to be depended up on by other projects.
task testJar(type: Jar) {
classifier = 'test'
from sourceSets.test.output
}
artifacts {
testRuntime testJar
}

View File

@@ -0,0 +1,24 @@
# This is a Gradle generated file for dependency locking.
# Manual edits can break the build and are not advised.
# This file is expected to be part of source control.
com.github.kevinstern:software-and-algorithms:1.0
com.github.stephenc.jcip:jcip-annotations:1.0-1
com.google.auto:auto-common:0.10
com.google.code.findbugs:jFormatString:3.0.0
com.google.code.findbugs:jsr305:3.0.2
com.google.errorprone:error_prone_annotation:2.3.3
com.google.errorprone:error_prone_annotations:2.3.3
com.google.errorprone:error_prone_check_api:2.3.3
com.google.errorprone:error_prone_core:2.3.3
com.google.errorprone:error_prone_type_annotations:2.3.3
com.google.guava:failureaccess:1.0.1
com.google.guava:guava:27.0.1-jre
com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava
com.google.j2objc:j2objc-annotations:1.1
com.google.protobuf:protobuf-java:3.4.0
com.googlecode.java-diff-utils:diffutils:1.3.0
org.checkerframework:checker-qual:2.5.3
org.checkerframework:dataflow:2.5.3
org.checkerframework:javacutil:2.5.3
org.codehaus.mojo:animal-sniffer-annotations:1.17
org.pcollections:pcollections:2.1.2

View File

@@ -0,0 +1,3 @@
# This is a Gradle generated file for dependency locking.
# Manual edits can break the build and are not advised.
# This file is expected to be part of source control.

View File

@@ -0,0 +1,3 @@
# This is a Gradle generated file for dependency locking.
# Manual edits can break the build and are not advised.
# This file is expected to be part of source control.

View File

@@ -0,0 +1,3 @@
# This is a Gradle generated file for dependency locking.
# Manual edits can break the build and are not advised.
# This file is expected to be part of source control.

View File

@@ -0,0 +1,18 @@
# This is a Gradle generated file for dependency locking.
# Manual edits can break the build and are not advised.
# This file is expected to be part of source control.
antlr:antlr:2.7.7
com.google.code.findbugs:jsr305:3.0.2
com.google.errorprone:error_prone_annotations:2.3.2
com.google.guava:failureaccess:1.0.1
com.google.guava:guava:28.0-jre
com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava
com.google.j2objc:j2objc-annotations:1.3
com.puppycrawl.tools:checkstyle:8.24
commons-beanutils:commons-beanutils:1.9.4
commons-collections:commons-collections:3.2.2
info.picocli:picocli:4.0.3
net.sf.saxon:Saxon-HE:9.9.1-4
org.antlr:antlr4-runtime:4.7.2
org.checkerframework:checker-qual:2.8.1
org.codehaus.mojo:animal-sniffer-annotations:1.17

View File

@@ -0,0 +1,46 @@
# This is a Gradle generated file for dependency locking.
# Manual edits can break the build and are not advised.
# This file is expected to be part of source control.
com.fasterxml.jackson.core:jackson-core:2.9.9
com.google.api-client:google-api-client:1.29.2
com.google.appengine:appengine-api-1.0-sdk:1.9.48
com.google.appengine:appengine-testing:1.9.58
com.google.auth:google-auth-library-credentials:0.16.1
com.google.auth:google-auth-library-oauth2-http:0.16.1
com.google.auto.value:auto-value-annotations:1.6.3
com.google.auto.value:auto-value:1.6.3
com.google.code.findbugs:jsr305:3.0.2
com.google.dagger:dagger:2.21
com.google.errorprone:error_prone_annotations:2.3.2
com.google.flogger:flogger:0.1
com.google.guava:failureaccess:1.0.1
com.google.guava:guava:28.1-jre
com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava
com.google.http-client:google-http-client-jackson2:1.30.1
com.google.http-client:google-http-client:1.30.1
com.google.j2objc:j2objc-annotations:1.3
com.google.oauth-client:google-oauth-client:1.29.2
com.google.re2j:re2j:1.1
com.ibm.icu:icu4j:57.1
commons-codec:commons-codec:1.11
commons-logging:commons-logging:1.2
io.grpc:grpc-context:1.19.0
io.netty:netty-buffer:4.1.31.Final
io.netty:netty-codec-http:4.1.31.Final
io.netty:netty-codec:4.1.31.Final
io.netty:netty-common:4.1.31.Final
io.netty:netty-handler:4.1.31.Final
io.netty:netty-resolver:4.1.31.Final
io.netty:netty-transport:4.1.31.Final
io.opencensus:opencensus-api:0.21.0
io.opencensus:opencensus-contrib-http-util:0.21.0
javax.activation:activation:1.1
javax.inject:javax.inject:1
javax.mail:mail:1.4
javax.xml.bind:jaxb-api:2.3.0
joda-time:joda-time:2.9.2
org.apache.httpcomponents:httpclient:4.5.8
org.apache.httpcomponents:httpcore:4.4.11
org.checkerframework:checker-qual:2.8.1
org.codehaus.mojo:animal-sniffer-annotations:1.18
org.yaml:snakeyaml:1.17

View File

@@ -0,0 +1,46 @@
# This is a Gradle generated file for dependency locking.
# Manual edits can break the build and are not advised.
# This file is expected to be part of source control.
com.fasterxml.jackson.core:jackson-core:2.9.9
com.google.api-client:google-api-client:1.29.2
com.google.appengine:appengine-api-1.0-sdk:1.9.48
com.google.appengine:appengine-testing:1.9.58
com.google.auth:google-auth-library-credentials:0.16.1
com.google.auth:google-auth-library-oauth2-http:0.16.1
com.google.auto.value:auto-value-annotations:1.6.3
com.google.auto.value:auto-value:1.6.3
com.google.code.findbugs:jsr305:3.0.2
com.google.dagger:dagger:2.21
com.google.errorprone:error_prone_annotations:2.3.2
com.google.flogger:flogger:0.1
com.google.guava:failureaccess:1.0.1
com.google.guava:guava:28.1-jre
com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava
com.google.http-client:google-http-client-jackson2:1.30.1
com.google.http-client:google-http-client:1.30.1
com.google.j2objc:j2objc-annotations:1.3
com.google.oauth-client:google-oauth-client:1.29.2
com.google.re2j:re2j:1.1
com.ibm.icu:icu4j:57.1
commons-codec:commons-codec:1.11
commons-logging:commons-logging:1.2
io.grpc:grpc-context:1.19.0
io.netty:netty-buffer:4.1.31.Final
io.netty:netty-codec-http:4.1.31.Final
io.netty:netty-codec:4.1.31.Final
io.netty:netty-common:4.1.31.Final
io.netty:netty-handler:4.1.31.Final
io.netty:netty-resolver:4.1.31.Final
io.netty:netty-transport:4.1.31.Final
io.opencensus:opencensus-api:0.21.0
io.opencensus:opencensus-contrib-http-util:0.21.0
javax.activation:activation:1.1
javax.inject:javax.inject:1
javax.mail:mail:1.4
javax.xml.bind:jaxb-api:2.3.0
joda-time:joda-time:2.9.2
org.apache.httpcomponents:httpclient:4.5.8
org.apache.httpcomponents:httpcore:4.4.11
org.checkerframework:checker-qual:2.8.1
org.codehaus.mojo:animal-sniffer-annotations:1.18
org.yaml:snakeyaml:1.17

View File

@@ -0,0 +1,3 @@
# This is a Gradle generated file for dependency locking.
# Manual edits can break the build and are not advised.
# This file is expected to be part of source control.

View File

@@ -0,0 +1,48 @@
# This is a Gradle generated file for dependency locking.
# Manual edits can break the build and are not advised.
# This file is expected to be part of source control.
com.fasterxml.jackson.core:jackson-core:2.9.9
com.google.api-client:google-api-client:1.29.2
com.google.appengine:appengine-api-1.0-sdk:1.9.48
com.google.appengine:appengine-testing:1.9.58
com.google.auth:google-auth-library-credentials:0.16.1
com.google.auth:google-auth-library-oauth2-http:0.16.1
com.google.auto.value:auto-value-annotations:1.6.3
com.google.auto.value:auto-value:1.6.3
com.google.code.findbugs:jsr305:3.0.2
com.google.dagger:dagger:2.21
com.google.errorprone:error_prone_annotations:2.3.2
com.google.flogger:flogger-system-backend:0.1
com.google.flogger:flogger:0.1
com.google.guava:failureaccess:1.0.1
com.google.guava:guava:28.1-jre
com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava
com.google.http-client:google-http-client-jackson2:1.30.1
com.google.http-client:google-http-client:1.30.1
com.google.j2objc:j2objc-annotations:1.3
com.google.oauth-client:google-oauth-client:1.29.2
com.google.re2j:re2j:1.1
com.ibm.icu:icu4j:57.1
commons-codec:commons-codec:1.11
commons-logging:commons-logging:1.2
io.grpc:grpc-context:1.19.0
io.netty:netty-buffer:4.1.31.Final
io.netty:netty-codec-http:4.1.31.Final
io.netty:netty-codec:4.1.31.Final
io.netty:netty-common:4.1.31.Final
io.netty:netty-handler:4.1.31.Final
io.netty:netty-resolver:4.1.31.Final
io.netty:netty-tcnative-boringssl-static:2.0.22.Final
io.netty:netty-transport:4.1.31.Final
io.opencensus:opencensus-api:0.21.0
io.opencensus:opencensus-contrib-http-util:0.21.0
javax.activation:activation:1.1
javax.inject:javax.inject:1
javax.mail:mail:1.4
javax.xml.bind:jaxb-api:2.3.0
joda-time:joda-time:2.9.2
org.apache.httpcomponents:httpclient:4.5.8
org.apache.httpcomponents:httpcore:4.4.11
org.checkerframework:checker-qual:2.8.1
org.codehaus.mojo:animal-sniffer-annotations:1.18
org.yaml:snakeyaml:1.17

View File

@@ -0,0 +1,24 @@
# This is a Gradle generated file for dependency locking.
# Manual edits can break the build and are not advised.
# This file is expected to be part of source control.
com.github.kevinstern:software-and-algorithms:1.0
com.github.stephenc.jcip:jcip-annotations:1.0-1
com.google.auto:auto-common:0.10
com.google.code.findbugs:jFormatString:3.0.0
com.google.code.findbugs:jsr305:3.0.2
com.google.errorprone:error_prone_annotation:2.3.3
com.google.errorprone:error_prone_annotations:2.3.3
com.google.errorprone:error_prone_check_api:2.3.3
com.google.errorprone:error_prone_core:2.3.3
com.google.errorprone:error_prone_type_annotations:2.3.3
com.google.guava:failureaccess:1.0.1
com.google.guava:guava:27.0.1-jre
com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava
com.google.j2objc:j2objc-annotations:1.1
com.google.protobuf:protobuf-java:3.4.0
com.googlecode.java-diff-utils:diffutils:1.3.0
org.checkerframework:checker-qual:2.5.3
org.checkerframework:dataflow:2.5.3
org.checkerframework:javacutil:2.5.3
org.codehaus.mojo:animal-sniffer-annotations:1.17
org.pcollections:pcollections:2.1.2

View File

@@ -0,0 +1,4 @@
# This is a Gradle generated file for dependency locking.
# Manual edits can break the build and are not advised.
# This file is expected to be part of source control.
com.google.errorprone:javac:9+181-r4173-1

View File

@@ -0,0 +1,4 @@
# This is a Gradle generated file for dependency locking.
# Manual edits can break the build and are not advised.
# This file is expected to be part of source control.
org.jacoco:org.jacoco.agent:0.8.5

View File

@@ -0,0 +1,11 @@
# This is a Gradle generated file for dependency locking.
# Manual edits can break the build and are not advised.
# This file is expected to be part of source control.
org.jacoco:org.jacoco.agent:0.8.5
org.jacoco:org.jacoco.ant:0.8.5
org.jacoco:org.jacoco.core:0.8.5
org.jacoco:org.jacoco.report:0.8.5
org.ow2.asm:asm-analysis:7.2
org.ow2.asm:asm-commons:7.2
org.ow2.asm:asm-tree:7.2
org.ow2.asm:asm:7.2

View File

@@ -0,0 +1,48 @@
# This is a Gradle generated file for dependency locking.
# Manual edits can break the build and are not advised.
# This file is expected to be part of source control.
com.fasterxml.jackson.core:jackson-core:2.9.9
com.google.api-client:google-api-client:1.29.2
com.google.appengine:appengine-api-1.0-sdk:1.9.48
com.google.appengine:appengine-testing:1.9.58
com.google.auth:google-auth-library-credentials:0.16.1
com.google.auth:google-auth-library-oauth2-http:0.16.1
com.google.auto.value:auto-value-annotations:1.6.3
com.google.auto.value:auto-value:1.6.3
com.google.code.findbugs:jsr305:3.0.2
com.google.dagger:dagger:2.21
com.google.errorprone:error_prone_annotations:2.3.2
com.google.flogger:flogger-system-backend:0.1
com.google.flogger:flogger:0.1
com.google.guava:failureaccess:1.0.1
com.google.guava:guava:28.1-jre
com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava
com.google.http-client:google-http-client-jackson2:1.30.1
com.google.http-client:google-http-client:1.30.1
com.google.j2objc:j2objc-annotations:1.3
com.google.oauth-client:google-oauth-client:1.29.2
com.google.re2j:re2j:1.1
com.ibm.icu:icu4j:57.1
commons-codec:commons-codec:1.11
commons-logging:commons-logging:1.2
io.grpc:grpc-context:1.19.0
io.netty:netty-buffer:4.1.31.Final
io.netty:netty-codec-http:4.1.31.Final
io.netty:netty-codec:4.1.31.Final
io.netty:netty-common:4.1.31.Final
io.netty:netty-handler:4.1.31.Final
io.netty:netty-resolver:4.1.31.Final
io.netty:netty-tcnative-boringssl-static:2.0.22.Final
io.netty:netty-transport:4.1.31.Final
io.opencensus:opencensus-api:0.21.0
io.opencensus:opencensus-contrib-http-util:0.21.0
javax.activation:activation:1.1
javax.inject:javax.inject:1
javax.mail:mail:1.4
javax.xml.bind:jaxb-api:2.3.0
joda-time:joda-time:2.9.2
org.apache.httpcomponents:httpclient:4.5.8
org.apache.httpcomponents:httpcore:4.4.11
org.checkerframework:checker-qual:2.8.1
org.codehaus.mojo:animal-sniffer-annotations:1.18
org.yaml:snakeyaml:1.17

View File

@@ -0,0 +1,48 @@
# This is a Gradle generated file for dependency locking.
# Manual edits can break the build and are not advised.
# This file is expected to be part of source control.
com.fasterxml.jackson.core:jackson-core:2.9.9
com.google.api-client:google-api-client:1.29.2
com.google.appengine:appengine-api-1.0-sdk:1.9.48
com.google.appengine:appengine-testing:1.9.58
com.google.auth:google-auth-library-credentials:0.16.1
com.google.auth:google-auth-library-oauth2-http:0.16.1
com.google.auto.value:auto-value-annotations:1.6.3
com.google.auto.value:auto-value:1.6.3
com.google.code.findbugs:jsr305:3.0.2
com.google.dagger:dagger:2.21
com.google.errorprone:error_prone_annotations:2.3.2
com.google.flogger:flogger-system-backend:0.1
com.google.flogger:flogger:0.1
com.google.guava:failureaccess:1.0.1
com.google.guava:guava:28.1-jre
com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava
com.google.http-client:google-http-client-jackson2:1.30.1
com.google.http-client:google-http-client:1.30.1
com.google.j2objc:j2objc-annotations:1.3
com.google.oauth-client:google-oauth-client:1.29.2
com.google.re2j:re2j:1.1
com.ibm.icu:icu4j:57.1
commons-codec:commons-codec:1.11
commons-logging:commons-logging:1.2
io.grpc:grpc-context:1.19.0
io.netty:netty-buffer:4.1.31.Final
io.netty:netty-codec-http:4.1.31.Final
io.netty:netty-codec:4.1.31.Final
io.netty:netty-common:4.1.31.Final
io.netty:netty-handler:4.1.31.Final
io.netty:netty-resolver:4.1.31.Final
io.netty:netty-tcnative-boringssl-static:2.0.22.Final
io.netty:netty-transport:4.1.31.Final
io.opencensus:opencensus-api:0.21.0
io.opencensus:opencensus-contrib-http-util:0.21.0
javax.activation:activation:1.1
javax.inject:javax.inject:1
javax.mail:mail:1.4
javax.xml.bind:jaxb-api:2.3.0
joda-time:joda-time:2.9.2
org.apache.httpcomponents:httpclient:4.5.8
org.apache.httpcomponents:httpcore:4.4.11
org.checkerframework:checker-qual:2.8.1
org.codehaus.mojo:animal-sniffer-annotations:1.18
org.yaml:snakeyaml:1.17

View File

@@ -0,0 +1,24 @@
# This is a Gradle generated file for dependency locking.
# Manual edits can break the build and are not advised.
# This file is expected to be part of source control.
com.github.kevinstern:software-and-algorithms:1.0
com.github.stephenc.jcip:jcip-annotations:1.0-1
com.google.auto:auto-common:0.10
com.google.code.findbugs:jFormatString:3.0.0
com.google.code.findbugs:jsr305:3.0.2
com.google.errorprone:error_prone_annotation:2.3.3
com.google.errorprone:error_prone_annotations:2.3.3
com.google.errorprone:error_prone_check_api:2.3.3
com.google.errorprone:error_prone_core:2.3.3
com.google.errorprone:error_prone_type_annotations:2.3.3
com.google.guava:failureaccess:1.0.1
com.google.guava:guava:27.0.1-jre
com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava
com.google.j2objc:j2objc-annotations:1.1
com.google.protobuf:protobuf-java:3.4.0
com.googlecode.java-diff-utils:diffutils:1.3.0
org.checkerframework:checker-qual:2.5.3
org.checkerframework:dataflow:2.5.3
org.checkerframework:javacutil:2.5.3
org.codehaus.mojo:animal-sniffer-annotations:1.17
org.pcollections:pcollections:2.1.2

View File

@@ -0,0 +1,3 @@
# This is a Gradle generated file for dependency locking.
# Manual edits can break the build and are not advised.
# This file is expected to be part of source control.

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