1
0
mirror of https://github.com/google/nomulus synced 2026-05-22 07:41:50 +00:00

Compare commits

...

14 Commits

Author SHA1 Message Date
Pavlo Tkach
6c7bf5e5dd Enable Users and Domains actions, add email notification (#2700) 2025-02-28 21:57:49 +00:00
Pavlo Tkach
ea1e8d5cc5 Add console gzip compression to js,css and html files (#2696) 2025-02-27 22:52:10 +00:00
Lai Jiang
7fb846c5b0 Add headers to record WHOIS client IPs (#2695)
The headers can be used by Cloud Armor to perform IP-based rate
limiting.
2025-02-27 22:15:13 +00:00
Lai Jiang
5180095cb6 Reduce log level to info when no email is found from the OIDC token (#2694)
This can happen on public endpoints (in pubapi) where the service is
behind IAP but all users (including not-logged-in ones) are allowed. IAP
will add an OIDC token with no email field in the request header.
2025-02-26 22:17:45 +00:00
Lai Jiang
9fe64bf9ec Make ignoreLinesStartingWith varargs (#2691)
It still is a list, because we String::startsWith does not benefit from
the target being in a set.
2025-02-26 17:12:24 +00:00
Lai Jiang
0f3b62d5ce Change the sleep time between proxy rollout (#2689) 2025-02-26 04:48:52 +00:00
Ben McIlwain
bd4701647b Refactor logic out of domain create flow tests (#2688)
This removes logic from an inner helper method so that it becomes more clear
from callsites within each test exactly which behavior is expected from those
test conditions.
2025-02-25 19:54:56 +00:00
Lai Jiang
fb816d7a2c Make it possible to ignore comment lines when comparing schemas (#2690)
We now pin to postgreSQL v17 when running tests, which means that minor
version might increase without our intervention. This causes (at least)
the comment in the golden schema to change, and failing the test as a
result.

This PR adds the ability to strip lines that we deem as comment from the
comparison, so we don't have to do trivial upgrades to the gold schema
whenever there's minor version upgrade.
2025-02-25 16:58:26 +00:00
gbrodman
8fbf363195 Remove unused dummy PGP file (#2687)
This was previously used as a dummy value for testing / compilation but
it's not used any more.
2025-02-24 21:45:26 +00:00
Lai Jiang
397f800614 Connect to GKE by default from the tool (#2686) 2025-02-24 19:01:05 +00:00
Lai Jiang
bcf42bd287 Use static IPs for EPP endpoints (#2685)
These IPs are now provisioned by Terraform. Also delete the
get-endpoints.py script as it is no longer necessary.
2025-02-24 16:38:47 +00:00
Pavlo Tkach
ed95d19b93 Provide prompt for user deletion UI (#2684) 2025-02-21 20:30:03 +00:00
Lai Jiang
97fc2c0b66 Add an annotation to the deployment (#2683)
This allows us to easily tell which tag was deployed.

Also set the gateway to use named address so they are stable, and so
that we can attach an IPv6 record to it. Auto-provisioned addresses are
IPv4 only.
2025-02-21 16:30:32 +00:00
Weimin Yu
00728c40ba Abort schema verifier when pg_dump fails (#2681)
Failed pg_dump may not leave a file, failing the subsequent diffing and
causing the verifier to return success.

The verifier should abort in this case.
2025-02-20 17:35:47 +00:00
43 changed files with 200 additions and 338 deletions

View File

@@ -63,6 +63,7 @@ public class TextDiffSubject extends Subject {
private final ImmutableList<String> actual;
private DiffFormat diffFormat = DiffFormat.SIDE_BY_SIDE_MARKDOWN;
private ImmutableList<String> comments = ImmutableList.of();
protected TextDiffSubject(FailureMetadata metadata, List<String> actual) {
super(metadata, actual);
@@ -83,10 +84,22 @@ public class TextDiffSubject extends Subject {
return this;
}
/** If set, ignore lines that start with the given string. */
public TextDiffSubject ignoringLinesStartingWith(String... comments) {
this.comments = ImmutableList.copyOf(comments);
return this;
}
private ImmutableList<String> filterComments(List<String> lines) {
return lines.stream()
.filter(line -> comments.stream().noneMatch(line::startsWith))
.collect(ImmutableList.toImmutableList());
}
public void hasSameContentAs(List<String> expectedContent) {
checkNotNull(expectedContent, "expectedContent");
ImmutableList<String> expected = ImmutableList.copyOf(expectedContent);
if (expected.equals(actual)) {
ImmutableList<String> expected = filterComments(expectedContent);
if (filterComments(expected).equals(filterComments(actual))) {
return;
}
String diffString = diffFormat.generateDiff(expected, actual);

View File

@@ -76,7 +76,7 @@ enum Operation {
selector: 'app-reason-dialog',
template: `
<h2 mat-dialog-title>
Please provide a reason for {{ data.operation }} the domain(s):
Please provide the (EPP) reason for {{ data.operation }} the domain(s):
</h2>
<mat-dialog-content>
<mat-form-field appearance="outline" style="width:100%">

View File

@@ -27,12 +27,10 @@ export const DISABLED_ELEMENTS_PER_ROLE = {
NONE: [
RESTRICTED_ELEMENTS.REGISTRAR_ELEMENT,
RESTRICTED_ELEMENTS.OTE,
RESTRICTED_ELEMENTS.USERS,
RESTRICTED_ELEMENTS.BULK_DELETE,
RESTRICTED_ELEMENTS.SUSPEND,
],
SUPPORT_LEAD: [RESTRICTED_ELEMENTS.USERS],
SUPPORT_AGENT: [RESTRICTED_ELEMENTS.USERS],
SUPPORT_LEAD: [],
SUPPORT_AGENT: [],
};
@Directive({

View File

@@ -67,17 +67,24 @@ export class UserDetailsComponent {
}
deleteUser() {
this.isLoading = true;
this.usersService.deleteUser(this.userDetails()).subscribe({
error: (err) => {
this._snackBar.open(err.error || err.message);
this.isLoading = false;
},
complete: () => {
this.isLoading = false;
this.goBack();
},
});
if (
confirm(
'This will permanently delete the user ' +
this.userDetails().emailAddress
)
) {
this.isLoading = true;
this.usersService.deleteUser(this.userDetails()).subscribe({
error: (err) => {
this._snackBar.open(err.error || err.message);
this.isLoading = false;
},
complete: () => {
this.isLoading = false;
this.goBack();
},
});
}
}
goBack() {

View File

@@ -1,32 +0,0 @@
-----BEGIN PGP PRIVATE KEY BLOCK-----
lQHYBFo8aRIBBAC8MA2xQXYvEbeLV1iMo4GC3lRFYvrUCarenhwoWufCYH6dGien
/HhiB0eiDF672J4MtueHQ2M7UaGJgxAoQTG9c6O90vlmFFhPZ967U1MTdY/NLvDK
bQEGzjdaUC1T/O6kr0O4GHRAyNyHa39Q75Oaj8MNdPsTmT4tDy+aFO6kKwARAQAB
AAP9Gd59M12tUmEcGxKBwKuFVSkc6oDlvBosG/geJMoCS+0Z2pzK0MPbBJa9mSAc
MbRgXZ0TDLwNuwzIqO+UXARCQu1ln/NlCcSzQZd5S80Of6CSoFMdFEb0kcpFW3z9
rpZdIBpNNk2iyBro9+7JOLJgCUkZQX7jy2K4LM5eTJsnuMECANFBnrMUde43XBiT
gixOJ5zbekGIIGq4QeRc8fJUDUhkFMq1znNriu30bB0Ld4Btlxzyn56tx8DVgx1+
4anONuECAOY5nm2G9i46AUxQN3dB8IE0SMMHcRcz60eX68fke+1aYjdSQA/nf9hR
l2f+gX9+y3cPqo7bFZzrDNECRm3J2IsB/2444JDTnzyME99jRYeEZGM0BXMWZEoO
hLU7f2V8pdN1po6mZ5bZZv6LeTXWPCIqCuBxNHZAV/xH9oWmkpjnw8Sc77QpVGVz
dCBSZWdpc3RyeSA8dGVzdC1yZWdpc3RyeUBleGFtcGxlLmNvbT6IuAQTAQIAIgUC
WjxpEgIbAwYLCQgHAwIGFQgCCQoLBBYCAwECHgECF4AACgkQvIOfrLbgEN2NegP+
JV+i4DxPTv3jfRDcpGy8yDANiIoBFSyARpqMqg0+TfV/UypuyjFTGfnLuQv+osce
OKtPevH8gCc779+OqtqyNDcPooTG5K+eUYR77PYtfzsKk/Z/33tQtEJDb8WWn4G4
R9Nh51MOz1X17oe3ih9HNvMGIrOG9VeWPsTxjXAzBoidAdgEWjxpEgEEANiasS9p
bG53M3jeCwLX0PFgWgspMZl3QnU6bvaTsfMAHaklJ55Tj1wuaaQymHqNm6xElCN8
MK8exDQQvPZwYVQOuoP3cHriCslLGznB943URcuxXz6R7F7WixYUeVVpQ4J0+gFu
bR8PfThDCtHQyP+uYx9U+EVWIvuIZIchdjl9ABEBAAEAA/4xmt2sorthIf3g9pL1
e/jfKoZ8i1rPT1NiNvdeE217neFtEPP9i5vni76ISskGOgN2hH8bkE+y7zwWQ2YP
FyYGlvVcw2KjT7+SrAWCkgR6Y7hWib+RDcVGje+YH5MxGtBIX2W/zcOW5S9+nC3Q
Y3Tzc3YQxF8sOeaHvrEb1tJ9eQIA5ivEjt43GgZq0nxacKLhleXyA9Z/JmwDg15z
FCZCnPABmR72wpXzXe2gO18W3iiqwS/WFDbdSFwxDQ0lXSy8VQIA8Okv6Q2BNXEw
H0hufK8P7aHvuOI1ll4qTw6QkY+z5hRZAcmmID3boQJeJAmVbUissYKUNJudmiUJ
DPLQod+wiQIAtJWxlRgHvEHRjQS5tH13ERWLObBHdZcQvKcqdtTCZj1EVH7zVHpb
qBLggo7QwPJTC+UMf/f4nPd1U2O6zXv66p5liJ8EGAECAAkFAlo8aRICGwwACgkQ
vIOfrLbgEN141gP9GATYCoihm5igbZ0FL8YPPb5WvHpTEA4WgdIIUUCQ0TYJ2ZOC
dK0i3qbb1xRRBJq006qSiE4vqQ7fHO8HxmEWaPLlsPvebGm39PUuzVyWx8I2w+0/
qcxt5L2VVzbZFp6+Yoa+meRYsO77gAzUvqUG1yLWo6MD4pSUNYJA867BB/k=
=mkAP
-----END PGP PRIVATE KEY BLOCK-----

View File

@@ -106,7 +106,7 @@ public abstract class OidcTokenAuthenticationMechanism implements Authentication
String email = (String) token.getPayload().get("email");
if (email == null) {
logger.atWarning().log("No email address from the OIDC token:\n%s", token.getPayload());
logger.atInfo().log("No email address from the OIDC token:\n%s", token.getPayload());
return AuthResult.NOT_AUTHENTICATED;
}
Optional<User> maybeUser =

View File

@@ -25,7 +25,6 @@ import com.beust.jcommander.Parameters;
import com.beust.jcommander.ParametersDelegate;
import com.google.common.base.Throwables;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import google.registry.persistence.transaction.JpaTransactionManager;
import google.registry.persistence.transaction.TransactionManagerFactory;
@@ -42,13 +41,6 @@ import org.postgresql.util.PSQLException;
@Parameters(separators = " =", commandDescription = "Command-line interface to the registry")
final class RegistryCli implements CommandRunner {
private static final ImmutableSet<RegistryToolEnvironment> DEFAULT_GKE_ENVIRONMENTS =
ImmutableSet.of(
RegistryToolEnvironment.ALPHA,
RegistryToolEnvironment.CRASH,
RegistryToolEnvironment.QA,
RegistryToolEnvironment.SANDBOX);
// The environment parameter is parsed twice: once here, and once with {@link
// RegistryToolEnvironment#parseFromArgs} in the {@link RegistryTool#main} function.
//
@@ -78,9 +70,6 @@ final class RegistryCli implements CommandRunner {
+ "Beam pipelines")
private String sqlAccessInfoFile = null;
@Parameter(names = "--gke", description = "Whether to use GKE runtime, instead of GAE")
private boolean useGke = false;
@Parameter(names = "--gae", description = "Whether to use GAE runtime, instead of GKE")
private boolean useGae = false;
@@ -161,12 +150,6 @@ final class RegistryCli implements CommandRunner {
throw e;
}
checkState(!useGke || !useGae, "Cannot specify both --gke and --gae");
// Special logic to set the default based on the environment if neither --gae nor --gke is set.
if (!useGke && !useGae) {
useGke = DEFAULT_GKE_ENVIRONMENTS.contains(environment);
}
String parsedCommand = jcommander.getParsedCommand();
// Show the list of all commands either if requested or if no subcommand name was specified
// (which does not throw a ParameterException parse error above).
@@ -186,7 +169,7 @@ final class RegistryCli implements CommandRunner {
DaggerRegistryToolComponent.builder()
.credentialFilePath(credentialJson)
.sqlAccessInfoFile(sqlAccessInfoFile)
.useGke(useGke)
.useGke(!useGae)
.useCanary(useCanary)
.build();

View File

@@ -31,12 +31,14 @@ import com.google.api.services.directory.Directory;
import com.google.api.services.directory.model.UserName;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.gson.annotations.Expose;
import google.registry.config.RegistryConfig.Config;
import google.registry.model.console.ConsolePermission;
import google.registry.model.console.RegistrarRole;
import google.registry.model.console.User;
import google.registry.model.console.UserRoles;
import google.registry.model.registrar.Registrar;
import google.registry.persistence.VKey;
import google.registry.request.Action;
import google.registry.request.Action.GkeService;
@@ -44,6 +46,7 @@ import google.registry.request.HttpException.BadRequestException;
import google.registry.request.Parameter;
import google.registry.request.auth.Auth;
import google.registry.tools.IamClient;
import google.registry.util.DiffUtils;
import google.registry.util.StringGenerator;
import java.io.IOException;
import java.util.List;
@@ -96,24 +99,14 @@ public class ConsoleUsersAction extends ConsoleApiAction {
@Override
protected void postHandler(User user) {
// Temporary flag while testing
if (user.getUserRoles().isAdmin()) {
checkPermission(user, registrarId, ConsolePermission.MANAGE_USERS);
tm().transact(this::runPostInTransaction);
} else {
consoleApiParams.response().setStatus(SC_FORBIDDEN);
}
checkPermission(user, registrarId, ConsolePermission.MANAGE_USERS);
tm().transact(this::runPostInTransaction);
}
@Override
protected void putHandler(User user) {
// Temporary flag while testing
if (user.getUserRoles().isAdmin()) {
checkPermission(user, registrarId, ConsolePermission.MANAGE_USERS);
tm().transact(this::runUpdateInTransaction);
} else {
consoleApiParams.response().setStatus(SC_FORBIDDEN);
}
checkPermission(user, registrarId, ConsolePermission.MANAGE_USERS);
tm().transact(this::runUpdateInTransaction);
}
@Override
@@ -135,13 +128,8 @@ public class ConsoleUsersAction extends ConsoleApiAction {
@Override
protected void deleteHandler(User user) {
// Temporary flag while testing
if (user.getUserRoles().isAdmin()) {
checkPermission(user, registrarId, ConsolePermission.MANAGE_USERS);
tm().transact(this::runDeleteInTransaction);
} else {
consoleApiParams.response().setStatus(SC_FORBIDDEN);
}
checkPermission(user, registrarId, ConsolePermission.MANAGE_USERS);
tm().transact(this::runDeleteInTransaction);
}
private void runPostInTransaction() throws IOException {
@@ -163,6 +151,8 @@ public class ConsoleUsersAction extends ConsoleApiAction {
this.userData.get().emailAddress,
registrarId,
RegistrarRole.valueOf(this.userData.get().role));
sendConfirmationEmail(registrarId, this.userData.get().emailAddress, "Added existing user");
consoleApiParams.response().setStatus(SC_OK);
}
@@ -186,6 +176,7 @@ public class ConsoleUsersAction extends ConsoleApiAction {
VKey<User> key = VKey.create(User.class, email);
tm().delete(key);
User.revokeIapPermission(email, maybeGroupEmailAddress, cloudTasksUtils, null, iamClient);
sendConfirmationEmail(registrarId, email, "Deleted user");
}
consoleApiParams.response().setStatus(SC_OK);
@@ -232,7 +223,7 @@ public class ConsoleUsersAction extends ConsoleApiAction {
User.Builder builder = new User.Builder().setUserRoles(userRoles).setEmailAddress(newEmail);
tm().put(builder.build());
User.grantIapPermission(newEmail, maybeGroupEmailAddress, cloudTasksUtils, null, iamClient);
sendConfirmationEmail(registrarId, newEmail, "Created user");
consoleApiParams.response().setStatus(SC_CREATED);
consoleApiParams
.response()
@@ -251,6 +242,8 @@ public class ConsoleUsersAction extends ConsoleApiAction {
this.userData.get().emailAddress,
registrarId,
RegistrarRole.valueOf(this.userData.get().role));
sendConfirmationEmail(registrarId, this.userData.get().emailAddress, "Updated user");
consoleApiParams.response().setStatus(SC_OK);
}
@@ -315,6 +308,20 @@ public class ConsoleUsersAction extends ConsoleApiAction {
.collect(toImmutableList()));
}
private boolean sendConfirmationEmail(String registrarId, String emailAddress, String operation) {
Optional<Registrar> registrar = Registrar.loadByRegistrarId(registrarId);
if (registrar.isEmpty()) { // Shouldn't happen, but worth checking
setFailedResponse(
"Failed to send an email to registrar " + registrarId, SC_INTERNAL_SERVER_ERROR);
return false;
}
sendExternalUpdates(
ImmutableMap.of("Console users updated", new DiffUtils.DiffPair(operation, emailAddress)),
registrar.get(),
ImmutableSet.of());
return true;
}
public record UserData(
@Expose String emailAddress, @Expose String role, @Expose @Nullable String password) {}
}

View File

@@ -15,7 +15,6 @@
package google.registry.ui.server.console.domains;
import static com.google.common.collect.ImmutableMap.toImmutableMap;
import static jakarta.servlet.http.HttpServletResponse.SC_FORBIDDEN;
import static jakarta.servlet.http.HttpServletResponse.SC_OK;
import static java.nio.charset.StandardCharsets.UTF_8;
@@ -81,11 +80,6 @@ public class ConsoleBulkDomainAction extends ConsoleApiAction {
@Override
protected void postHandler(User user) {
// Temporary flag while testing
if (!user.getUserRoles().isAdmin()) {
consoleApiParams.response().setStatus(SC_FORBIDDEN);
return;
}
JsonElement jsonPayload =
optionalJsonPayload.orElseThrow(
() -> new IllegalArgumentException("Bulk action payload must be present"));

View File

@@ -161,8 +161,6 @@ import google.registry.model.common.FeatureFlag;
import google.registry.model.domain.Domain;
import google.registry.model.domain.DomainHistory;
import google.registry.model.domain.GracePeriod;
import google.registry.model.domain.fee.BaseFee.FeeType;
import google.registry.model.domain.fee.Fee;
import google.registry.model.domain.fee.FeeQueryCommandExtensionItem.CommandName;
import google.registry.model.domain.launch.LaunchNotice;
import google.registry.model.domain.rgp.GracePeriodStatus;
@@ -287,59 +285,45 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
persistContactsAndHosts("net"); // domain_create.xml uses hosts on "net".
}
private void assertSuccessfulCreate(String domainTld, ImmutableSet<Flag> expectedBillingFlags)
throws Exception {
assertSuccessfulCreate(domainTld, expectedBillingFlags, null, 24, null);
}
private void assertSuccessfulCreate(
String domainTld, ImmutableSet<BillingBase.Flag> expectedBillingFlags) throws Exception {
assertSuccessfulCreate(domainTld, expectedBillingFlags, null);
String domainTld, ImmutableSet<Flag> expectedBillingFlags, double createCost)
throws Exception {
assertSuccessfulCreate(domainTld, expectedBillingFlags, null, createCost, null);
}
private void assertSuccessfulCreate(
String domainTld, ImmutableSet<Flag> expectedBillingFlags, AllocationToken token)
throws Exception {
assertSuccessfulCreate(domainTld, expectedBillingFlags, token, 24, null);
}
private void assertSuccessfulCreate(
String domainTld,
ImmutableSet<BillingBase.Flag> expectedBillingFlags,
@Nullable AllocationToken allocationToken)
ImmutableSet<Flag> expectedBillingFlags,
AllocationToken token,
double createCost)
throws Exception {
assertSuccessfulCreate(domainTld, expectedBillingFlags, token, createCost, null);
}
private void assertSuccessfulCreate(
String domainTld,
ImmutableSet<Flag> expectedBillingFlags,
@Nullable AllocationToken token,
double createCost,
@Nullable Integer specifiedRenewCost)
throws Exception {
Domain domain = reloadResourceByForeignKey();
boolean isAnchorTenant = expectedBillingFlags.contains(ANCHOR_TENANT);
// Set up the creation cost.
boolean isDomainPremium = isDomainPremium(getUniqueIdFromCommand(), clock.nowUtc());
BigDecimal createCost = isDomainPremium ? BigDecimal.valueOf(200) : BigDecimal.valueOf(24);
if (isAnchorTenant) {
createCost = BigDecimal.ZERO;
}
if (expectedBillingFlags.contains(SUNRISE)) {
createCost =
createCost.multiply(
BigDecimal.valueOf(1 - RegistryConfig.getSunriseDomainCreateDiscount()));
}
if (allocationToken != null) {
if (allocationToken
.getRegistrationBehavior()
.equals(RegistrationBehavior.NONPREMIUM_CREATE)) {
createCost =
createCost.subtract(
BigDecimal.valueOf(isDomainPremium ? 87 : 0)); // premium is 100, standard 13
}
if (allocationToken.getRenewalPriceBehavior().equals(NONPREMIUM)) {
createCost =
createCost.subtract(
BigDecimal.valueOf(isDomainPremium ? 89 : 0)); // premium is 100, standard 11
}
if (allocationToken.getRenewalPriceBehavior().equals(SPECIFIED)) {
createCost =
createCost
.subtract(BigDecimal.valueOf(isDomainPremium ? 100 : 11))
.add(allocationToken.getRenewalPrice().get().getAmount());
}
}
FeesAndCredits feesAndCredits =
new FeesAndCredits.Builder()
.setCurrency(USD)
.addFeeOrCredit(
Fee.create(
createCost,
FeeType.CREATE,
isDomainPremium(getUniqueIdFromCommand(), clock.nowUtc())))
.build();
Money eapFee =
Money.of(
Tld.get(domainTld).getCurrency(),
@@ -362,7 +346,7 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
RenewalPriceBehavior expectedRenewalPriceBehavior =
isAnchorTenant
? RenewalPriceBehavior.NONPREMIUM
: Optional.ofNullable(allocationToken)
: Optional.ofNullable(token)
.map(AllocationToken::getRenewalPriceBehavior)
.orElse(RenewalPriceBehavior.DEFAULT);
// There should be one bill for the create and one for the recurrence autorenew event.
@@ -371,13 +355,13 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
.setReason(Reason.CREATE)
.setTargetId(getUniqueIdFromCommand())
.setRegistrarId("TheRegistrar")
.setCost(feesAndCredits.getCreateCost())
.setCost(Money.of(USD, BigDecimal.valueOf(createCost)))
.setPeriodYears(2)
.setEventTime(clock.nowUtc())
.setBillingTime(billingTime)
.setFlags(expectedBillingFlags)
.setDomainHistory(historyEntry)
.setAllocationToken(allocationToken == null ? null : allocationToken.createVKey())
.setAllocationToken(Optional.ofNullable(token).map(t -> t.createVKey()).orElse(null))
.build();
BillingRecurrence renewBillingEvent =
@@ -391,8 +375,8 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
.setDomainHistory(historyEntry)
.setRenewalPriceBehavior(expectedRenewalPriceBehavior)
.setRenewalPrice(
Optional.ofNullable(allocationToken)
.flatMap(AllocationToken::getRenewalPrice)
Optional.ofNullable(specifiedRenewCost)
.map(r -> Money.of(USD, BigDecimal.valueOf(r)))
.orElse(null))
.build();
@@ -480,7 +464,7 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
assertMutatingFlow(true);
runFlowAssertResponse(
CommitMode.LIVE, userPrivileges, loadFile(responseXmlFile, substitutions));
assertSuccessfulCreate(domainTld, ImmutableSet.of());
assertSuccessfulCreate(domainTld, ImmutableSet.of(), 24);
assertNoLordn();
}
@@ -886,10 +870,14 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
clock.nowUtc().plusDays(1),
Money.of(USD, 0)))
.build());
doSuccessfulTest(
"example",
"domain_create_response_premium_eap.xml",
ImmutableMap.of("DOMAIN", "rich.example"));
assertMutatingFlow(true);
runFlowAssertResponse(
CommitMode.LIVE,
UserPrivileges.NORMAL,
loadFile(
"domain_create_response_premium_eap.xml", ImmutableMap.of("DOMAIN", "rich.example")));
assertSuccessfulCreate("example", ImmutableSet.of(), 200);
assertNoLordn();
}
/**
@@ -1336,7 +1324,7 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
setEppInput("domain_create_anchor_allocationtoken.xml");
persistContactsAndHosts();
runFlowAssertResponse(loadFile("domain_create_anchor_response.xml"));
assertSuccessfulCreate("tld", ImmutableSet.of(ANCHOR_TENANT), allocationToken);
assertSuccessfulCreate("tld", ImmutableSet.of(ANCHOR_TENANT), allocationToken, 0);
assertNoLordn();
assertAllocationTokenWasRedeemed("abcDEF23456");
}
@@ -1350,7 +1338,7 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
.setTokenType(SINGLE_USE)
.setDomainName("resdom.tld")
.setRenewalPriceBehavior(SPECIFIED)
.setRenewalPrice(Money.of(USD, 0))
.setRenewalPrice(Money.of(USD, 1))
.build());
// Despite the domain being FULLY_BLOCKED, the non-superuser create succeeds the domain is also
// RESERVED_FOR_SPECIFIC_USE and the correct allocation token is passed.
@@ -1359,7 +1347,8 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
persistContactsAndHosts();
runFlowAssertResponse(
loadFile("domain_create_response.xml", ImmutableMap.of("DOMAIN", "resdom.tld")));
assertSuccessfulCreate("tld", ImmutableSet.of(RESERVED), allocationToken);
// $13 for the first year plus $1 renewal for the second year =
assertSuccessfulCreate("tld", ImmutableSet.of(RESERVED), allocationToken, 14, 1);
assertNoLordn();
assertAllocationTokenWasRedeemed("abc123");
}
@@ -1379,7 +1368,7 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
setEppInput("domain_create_anchor_allocationtoken.xml");
persistContactsAndHosts();
runFlowAssertResponse(loadFile("domain_create_anchor_response.xml"));
assertSuccessfulCreate("tld", ImmutableSet.of(ANCHOR_TENANT), allocationToken);
assertSuccessfulCreate("tld", ImmutableSet.of(ANCHOR_TENANT), allocationToken, 0);
assertNoLordn();
assertAllocationTokenWasRedeemed("abcDEF23456");
}
@@ -1402,7 +1391,7 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
clock.setTo(DateTime.parse("2009-08-16T09:00:00.0Z"));
persistContactsAndHosts();
runFlowAssertResponse(loadFile("domain_create_response_claims.xml"));
assertSuccessfulCreate("tld", ImmutableSet.of(ANCHOR_TENANT), allocationToken);
assertSuccessfulCreate("tld", ImmutableSet.of(ANCHOR_TENANT), allocationToken, 0);
assertDomainDnsRequests("example-one.tld");
assertClaimsLordn();
assertAllocationTokenWasRedeemed("abcDEF23456");
@@ -1415,7 +1404,7 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
persistContactsAndHosts();
runFlowAssertResponse(
loadFile("domain_create_response.xml", ImmutableMap.of("DOMAIN", "example.tld")));
assertSuccessfulCreate("tld", ImmutableSet.of(ANCHOR_TENANT));
assertSuccessfulCreate("tld", ImmutableSet.of(ANCHOR_TENANT), 0);
assertNoLordn();
}
@@ -1450,7 +1439,7 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
SMD_VALID_TIME.toString(),
"EXPIRATION_TIME",
SMD_VALID_TIME.plusYears(2).toString())));
assertSuccessfulCreate("tld", ImmutableSet.of(SUNRISE, ANCHOR_TENANT));
assertSuccessfulCreate("tld", ImmutableSet.of(SUNRISE, ANCHOR_TENANT), 0);
}
@Test
@@ -1482,7 +1471,7 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
SMD_VALID_TIME.toString(),
"EXPIRATION_TIME",
SMD_VALID_TIME.plusYears(2).toString())));
assertSuccessfulCreate("tld", ImmutableSet.of(ANCHOR_TENANT, SUNRISE), allocationToken);
assertSuccessfulCreate("tld", ImmutableSet.of(ANCHOR_TENANT, SUNRISE), allocationToken, 0);
assertDomainDnsRequests("test-validate.tld");
assertSunriseLordn();
assertAllocationTokenWasRedeemed("abcDEF23456");
@@ -1505,7 +1494,7 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
setEppInput("domain_create_anchor_allocationtoken.xml");
persistContactsAndHosts();
runFlowAssertResponse(loadFile("domain_create_anchor_response.xml"));
assertSuccessfulCreate("tld", ImmutableSet.of(ANCHOR_TENANT), allocationToken);
assertSuccessfulCreate("tld", ImmutableSet.of(ANCHOR_TENANT), allocationToken, 0);
assertNoLordn();
assertAllocationTokenWasRedeemed("abcDEF23456");
}
@@ -1929,7 +1918,7 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
loadFile(
"domain_create_response_premium.xml",
ImmutableMap.of("EXDATE", "2001-04-03T22:00:00.0Z", "FEE", "200.00")));
assertSuccessfulCreate("example", ImmutableSet.of());
assertSuccessfulCreate("example", ImmutableSet.of(), 200);
}
private BillingEvent runTest_defaultToken(String token) throws Exception {
@@ -2148,7 +2137,7 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
loadFile(
"domain_create_response_premium.xml",
ImmutableMap.of("EXDATE", "2001-04-03T22:00:00.0Z", "FEE", "200.00")));
assertSuccessfulCreate("example", ImmutableSet.of());
assertSuccessfulCreate("example", ImmutableSet.of(), 200);
}
@Test
@@ -2572,7 +2561,7 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
persistContactsAndHosts();
persistBsaLabel("anchor");
runFlowAssertResponse(loadFile("domain_create_anchor_response.xml"));
assertSuccessfulCreate("tld", ImmutableSet.of(ANCHOR_TENANT), allocationToken);
assertSuccessfulCreate("tld", ImmutableSet.of(ANCHOR_TENANT), allocationToken, 0);
assertNoLordn();
assertAllocationTokenWasRedeemed("abcDEF23456");
}
@@ -2738,7 +2727,7 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
SMD_VALID_TIME.toString(),
"EXPIRATION_TIME",
SMD_VALID_TIME.plusYears(2).toString())));
assertSuccessfulCreate("tld", ImmutableSet.of(SUNRISE));
assertSuccessfulCreate("tld", ImmutableSet.of(SUNRISE), 20.40);
assertSunriseLordn();
}
@@ -2761,7 +2750,7 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
SMD_VALID_TIME.toString(),
"EXPIRATION_TIME",
SMD_VALID_TIME.plusYears(2).toString())));
assertSuccessfulCreate("tld", ImmutableSet.of(SUNRISE));
assertSuccessfulCreate("tld", ImmutableSet.of(SUNRISE), 20.40);
assertSunriseLordn();
}
@@ -3245,7 +3234,7 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
"domain_create_allocationtoken.xml",
ImmutableMap.of("DOMAIN", "example.tld", "YEARS", "2"));
runFlow();
assertSuccessfulCreate("tld", ImmutableSet.of(ANCHOR_TENANT), token);
assertSuccessfulCreate("tld", ImmutableSet.of(ANCHOR_TENANT), token, 0);
}
@Test
@@ -3265,7 +3254,7 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
"domain_create_premium_allocationtoken.xml",
ImmutableMap.of("YEARS", "2", "FEE", "111.00"));
runFlow();
assertSuccessfulCreate("example", ImmutableSet.of(), token);
assertSuccessfulCreate("example", ImmutableSet.of(), token, 111);
}
@Test
@@ -3286,7 +3275,7 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
"domain_create_premium_allocationtoken.xml",
ImmutableMap.of("YEARS", "2", "FEE", "101.00"));
runFlow();
assertSuccessfulCreate("example", ImmutableSet.of(), token);
assertSuccessfulCreate("example", ImmutableSet.of(), token, 101, 1);
}
@Test
@@ -3438,7 +3427,7 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
ImmutableMap.of("DOMAIN", "example.tld", "YEARS", "2"));
persistContactsAndHosts();
runFlow();
assertSuccessfulCreate("tld", ImmutableSet.of(ANCHOR_TENANT), token);
assertSuccessfulCreate("tld", ImmutableSet.of(ANCHOR_TENANT), token, 0);
}
@Test
@@ -3492,7 +3481,7 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
"domain_create_allocationtoken.xml",
ImmutableMap.of("DOMAIN", "example.tld", "YEARS", "2"));
runFlow();
assertSuccessfulCreate("tld", ImmutableSet.of(ANCHOR_TENANT), token);
assertSuccessfulCreate("tld", ImmutableSet.of(ANCHOR_TENANT), token, 0);
}
@Test
@@ -3510,7 +3499,7 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
.build());
persistContactsAndHosts();
runFlow();
assertSuccessfulCreate("tld", ImmutableSet.of(SUNRISE, ANCHOR_TENANT), allocationToken);
assertSuccessfulCreate("tld", ImmutableSet.of(SUNRISE, ANCHOR_TENANT), allocationToken, 0);
}
@Test
@@ -3572,7 +3561,7 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
"domain_create_allocationtoken.xml",
ImmutableMap.of("DOMAIN", "example.tld", "YEARS", "2"));
runFlow();
assertSuccessfulCreate("tld", ImmutableSet.of(ANCHOR_TENANT), token);
assertSuccessfulCreate("tld", ImmutableSet.of(ANCHOR_TENANT), token, 0);
}
@Test
@@ -3599,7 +3588,7 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
setEppInput("domain_create_allocationtoken_claims.xml");
persistContactsAndHosts();
runFlow();
assertSuccessfulCreate("tld", ImmutableSet.of(ANCHOR_TENANT), allocationToken);
assertSuccessfulCreate("tld", ImmutableSet.of(ANCHOR_TENANT), allocationToken, 0);
}
@Test
@@ -3649,7 +3638,7 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
"domain_create_allocationtoken.xml",
ImmutableMap.of("DOMAIN", "example.tld", "YEARS", "2"));
runFlow();
assertSuccessfulCreate("tld", ImmutableSet.of(ANCHOR_TENANT), token);
assertSuccessfulCreate("tld", ImmutableSet.of(ANCHOR_TENANT), token, 0);
}
@Test
@@ -3663,7 +3652,7 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
setEppInput("domain_create_allocationtoken_claims.xml");
persistContactsAndHosts();
runFlow();
assertSuccessfulCreate("tld", ImmutableSet.of(ANCHOR_TENANT), allocationToken);
assertSuccessfulCreate("tld", ImmutableSet.of(ANCHOR_TENANT), allocationToken, 0);
}
@Test

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 51 KiB

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 KiB

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 130 KiB

After

Width:  |  Height:  |  Size: 134 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 132 KiB

After

Width:  |  Height:  |  Size: 136 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 KiB

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 46 KiB

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 68 KiB

After

Width:  |  Height:  |  Size: 69 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 43 KiB

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 43 KiB

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 72 KiB

After

Width:  |  Height:  |  Size: 73 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 37 KiB

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 53 KiB

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 72 KiB

After

Width:  |  Height:  |  Size: 73 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 73 KiB

After

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 74 KiB

After

Width:  |  Height:  |  Size: 75 KiB

View File

@@ -2,8 +2,8 @@
-- PostgreSQL database dump
--
-- Dumped from database version 17.3
-- Dumped by pg_dump version 17.3
-- Dumped from database version 17.4
-- Dumped by pg_dump version 17.4
SET statement_timeout = 0;
SET lock_timeout = 0;

View File

@@ -115,6 +115,7 @@ class SchemaTest {
Joiner.on(File.separatorChar).join(MOUNTED_RESOURCE_PATH, DUMP_OUTPUT_FILE));
assertThat(dumpedSchema)
.ignoringLinesStartingWith("--")
.hasSameContentAs(Resources.getResource("sql/schema/nomulus.golden.sql"));
}

View File

@@ -1,158 +0,0 @@
#! /bin/env python3
# Copyright 2024 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.
'''
A script that outputs the IP endpoints of various load balancers, to be run
after Nomulus is deployed.
'''
import ipaddress
import json
import subprocess
import sys
from dataclasses import dataclass
from ipaddress import IPv4Address
from ipaddress import IPv6Address
from operator import attrgetter
from operator import methodcaller
class PreserveContext:
def __enter__(self):
self._context = run_command('kubectl config current-context')
def __exit__(self, type, value, traceback):
run_command('kubectl config use-context ' + self._context)
class UseCluster(PreserveContext):
def __init__(self, cluster: str, region: str, project: str):
self._cluster = cluster
self._region = region
self._project = project
def __enter__(self):
super().__enter__()
cmd = (f'gcloud container fleet memberships get-credentials'
f' {self._cluster} --project {self._project}')
run_command(cmd)
def run_command(cmd: str, print_output=False) -> str:
proc = subprocess.run(cmd, text=True, shell=True, stdout=subprocess.PIPE,
stderr=subprocess.STDOUT)
if print_output:
print(proc.stdout)
return proc.stdout
def get_clusters(project: str) -> dict[str, str]:
cmd = f'gcloud container clusters list --project {project} --format=json'
content = json.loads(run_command(cmd))
res = {}
for item in content:
name = item['name']
region = item['location']
if not name.startswith('nomulus-cluster'):
continue
res[name] = region
return res
def get_endpoints(resource: str, service: str, jsonpath: str) -> list[
str]:
content = run_command(
f'kubectl get {resource}/{service} -o jsonpath={jsonpath}', )
return content.split()
def get_region_symbol(region: str) -> str:
if region.startswith('us'):
return 'amer'
if region.startswith('europe'):
return 'emea'
if region.startswith('asia'):
return 'apac'
return 'other'
@dataclass
class IP:
service: str
region: str
address: IPv4Address | IPv6Address
def is_ipv6(self) -> bool:
return self.address.version == 6
def __str__(self) -> str:
return f'{self.service} {self.region}: {self.address}'
def terraform_str(item) -> str:
res = ""
if (isinstance(item, dict)):
res += '{\n'
for key, value in item.items():
res += f'{key} = {terraform_str(value)}\n'
res += '}'
elif (isinstance(item, list)):
res += '['
for i, value in enumerate(item):
if i != 0:
res += ', '
res += terraform_str(value)
res += ']'
else:
res += f'"{item}"'
return res
if __name__ == '__main__':
if len(sys.argv) != 2:
raise ValueError('Usage: get-endpoints.py <project>')
project = sys.argv[1]
print(f'Project: {project}')
clusters = get_clusters(project)
ips = []
res = {}
for cluster, region in clusters.items():
with UseCluster(cluster, region, project):
for service in ['whois', 'whois-canary', 'epp', 'epp-canary']:
map_key = service.replace('-', '_')
for ip in get_endpoints('services', service,
'{.status.loadBalancer.ingress[*].ip}'):
ip = ipaddress.ip_address(ip)
if isinstance(ip, IPv4Address):
map_key_with_iptype = map_key + '_ipv4'
else:
map_key_with_iptype = map_key + '_ipv6'
if map_key_with_iptype not in res:
res[map_key_with_iptype] = {}
res[map_key_with_iptype][get_region_symbol(region)] = [ip]
ips.append(IP(service, get_region_symbol(region), ip))
if not region.startswith('us'):
continue
ip = get_endpoints('gateways.gateway.networking.k8s.io', 'nomulus',
'{.status.addresses[*].value}')[0]
print(f'nomulus: {ip}')
res['https_ip'] = ipaddress.ip_address(ip)
ips.sort(key=attrgetter('region'))
ips.sort(key=methodcaller('is_ipv6'))
ips.sort(key=attrgetter('service'))
for ip in ips:
print(ip)
print("Terraform friendly output:")
print(terraform_str(res))

View File

@@ -4,6 +4,11 @@ metadata:
name: nomulus
spec:
gatewayClassName: gke-l7-global-external-managed-mc
addresses:
- type: NamedAddress
value: nomulus-ipv4-address
- type: NamedAddress
value: nomulus-ipv6-address
listeners:
- name: http
protocol: HTTP

View File

@@ -2,6 +2,8 @@ apiVersion: apps/v1
kind: Deployment
metadata:
name: backend
annotations:
tag: "latest"
spec:
selector:
matchLabels:

View File

@@ -2,6 +2,8 @@ apiVersion: apps/v1
kind: Deployment
metadata:
name: console
annotations:
tag: "latest"
spec:
selector:
matchLabels:

View File

@@ -2,6 +2,8 @@ apiVersion: apps/v1
kind: Deployment
metadata:
name: frontend
annotations:
tag: "latest"
spec:
selector:
matchLabels:
@@ -108,6 +110,7 @@ metadata:
annotations:
cloud.google.com/l4-rbs: enabled
networking.gke.io/weighted-load-balancing: pods-per-node
networking.gke.io/load-balancer-ip-addresses: "EPP-ipv6-main,EPP-ipv4-main"
spec:
type: LoadBalancer
# Traffic is directly delivered to a node, preserving the original source IP.

View File

@@ -2,6 +2,8 @@ apiVersion: apps/v1
kind: Deployment
metadata:
name: pubapi
annotations:
tag: "latest"
spec:
selector:
matchLabels:

View File

@@ -0,0 +1,9 @@
# ---------------------------------------
# Module: gzip
# Enables gzip compression of the following mime types
# ---------------------------------------
--module=gzip
jetty.http.gzip.minGzipSize=200
jetty.http.gzip.includedMimeTypes=text/html,text/css,text/javascript,application/javascript,application/json,image/svg+xml

View File

@@ -40,7 +40,8 @@ do
kubectl apply -f -
kubectl apply -f "./kubernetes/proxy-service-canary.yaml" --force
fi
# Kills all running pods, new pods created will be pulling the new image.
kubectl delete pods --all
# Restart all running pods, new pods created will be pulling the new image.
kubectl rollout restart deployment/proxy-deployment
kubectl rollout restart deployment/proxy-deployment-canary
done < <(gcloud container clusters list --project ${project} | grep proxy-cluster)
kubectl config use-context "$current_context"

View File

@@ -15,8 +15,10 @@
package google.registry.proxy.handler;
import static com.google.common.base.Preconditions.checkArgument;
import static google.registry.proxy.handler.ProxyProtocolHandler.REMOTE_ADDRESS_KEY;
import google.registry.proxy.metric.FrontendMetrics;
import google.registry.util.ProxyHttpHeaders;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
@@ -30,6 +32,8 @@ import java.util.function.Supplier;
/** Handler that processes WHOIS protocol logic. */
public final class WhoisServiceHandler extends HttpsRelayServiceHandler {
private String clientAddress;
public WhoisServiceHandler(
String relayHost,
String relayPath,
@@ -45,6 +49,12 @@ public final class WhoisServiceHandler extends HttpsRelayServiceHandler {
super.channelActive(ctx);
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
clientAddress = ctx.channel().attr(REMOTE_ADDRESS_KEY).get();
super.channelRead(ctx, msg);
}
@Override
protected FullHttpRequest decodeFullHttpRequest(ByteBuf byteBuf) {
FullHttpRequest request = super.decodeFullHttpRequest(byteBuf);
@@ -52,6 +62,12 @@ public final class WhoisServiceHandler extends HttpsRelayServiceHandler {
.headers()
.set(HttpHeaderNames.CONTENT_TYPE, HttpHeaderValues.TEXT_PLAIN)
.set(HttpHeaderNames.ACCEPT, HttpHeaderValues.TEXT_PLAIN);
if (clientAddress != null) {
request
.headers()
.set(ProxyHttpHeaders.IP_ADDRESS, clientAddress)
.set(ProxyHttpHeaders.FALLBACK_IP_ADDRESS, clientAddress);
}
return request;
}

View File

@@ -188,6 +188,7 @@ steps:
do
# non-canary
sed s/GCP_PROJECT/${PROJECT_ID}/g ./jetty/kubernetes/nomulus-${service}.yaml | \
sed s/latest/${TAG_NAME}/g | \
sed s/ENVIRONMENT/${env}/g | \
sed s/PROXY_ENV/"${env}"/g | \
sed s/EPP/"epp"/g > ./jetty/kubernetes/nomulus-${env}-${service}.yaml
@@ -203,6 +204,7 @@ steps:
fi
# canary
sed s/GCP_PROJECT/${PROJECT_ID}/g ./jetty/kubernetes/nomulus-${service}.yaml | \
sed s/latest/${TAG_NAME}/g | \
sed s/ENVIRONMENT/${env}/g | \
sed s/PROXY_ENV/"${env}_canary"/g | \
sed s/EPP/"epp-canary"/g | \

View File

@@ -39,8 +39,18 @@ steps:
gcloud auth activate-service-account --key-file=tool-credential.json
first=true
t=0
while read line
do
# Sleep for t seconds for the rollout to stabilize.
if [[ -v first ]]
then
unset first
else
sleep $t
fi
name=$(echo $line | awk '{print $1}')
location=$(echo $line | awk '{print $2}')
echo $name $region
@@ -49,6 +59,6 @@ steps:
--project $project_id --location $location
kubectl rollout restart deployment/proxy-deployment
done < <(gcloud container clusters list --project $project_id | grep proxy-cluster)
timeout: 3600s
timeout: 7500s
options:
machineType: 'N1_HIGHCPU_8'

View File

@@ -69,6 +69,14 @@ PGPASSWORD=${db_password} pg_dump -h localhost -U "${db_user}" \
--exclude-table flyway_schema_history \
postgres
if [ $? -ne 0 ]; then
echo "Failed to dump schema."
exit 1
else
echo "Schema dumped."
fi
raw_diff=$(diff /schema/nomulus.golden.sql /schema/nomulus.actual.sql)
# Clean up the raw_diff:
# - Remove diff locations (e.g. "5,6c5,6): grep "^[<>]"