1
0
mirror of https://github.com/google/nomulus synced 2026-07-06 08:06:33 +00:00

Compare commits

...

11 Commits

Author SHA1 Message Date
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
Lai Jiang 3f2a42ab8d Expose EPP via saidcar proxy (#2680) 2025-02-19 18:57:25 +00:00
Lai Jiang b73e342820 Update PostgreSQL version in builder image and tests (#2667) 2025-02-18 17:34:41 +00:00
Lai Jiang df7fec7a3e Update RDAP TOS link (#2678) 2025-02-18 17:00:26 +00:00
Lai Jiang 6f7ae1eabc Redirect HTTP to HTTPS (#2679)
This opens up port 80 on the load balancer IP and upgrades all HTTP
request to HTTPS.

TESTED=tested on alpha.
2025-02-18 16:57:18 +00:00
Lai Jiang eb978ebbd5 Let nomulus tool connect to sandbox GKE by default (#2674) 2025-02-16 18:10:03 +00:00
Pavlo Tkach 95831bc8b7 Add suspend / unsuspend to the console (#2675) 2025-02-14 20:41:19 +00:00
Lai Jiang 538260521b Update Nomulus deployment script (#2677)
We only deploy to the us-central1 cluster in order to minimize database
locality issue.
2025-02-14 17:31:18 +00:00
Pavlo Tkach 612708f0a8 Fix console user creation role param (#2676) 2025-02-14 13:51:06 +00:00
38 changed files with 702 additions and 212 deletions
@@ -24,7 +24,11 @@
</div>
} @else {
<mat-menu #actions="matMenu">
<ng-template matMenuContent let-domainName="domainName">
<ng-template
matMenuContent
let-domainName="domainName"
let-domain="domain"
>
<button
mat-menu-item
(click)="openRegistryLock(domainName)"
@@ -33,6 +37,24 @@
<mat-icon>key</mat-icon>
<span>Registry Lock</span>
</button>
<button
mat-menu-item
(click)="onSuspendClick(domainName)"
[elementId]="getElementIdForSuspendUnsuspend()"
[disabled]="isDomainUnsuspendable(domain)"
>
<mat-icon>lock_clock</mat-icon>
<span>Suspend</span>
</button>
<button
mat-menu-item
(click)="onUnsuspendClick(domainName)"
[elementId]="getElementIdForSuspendUnsuspend()"
[disabled]="!isDomainUnsuspendable(domain)"
>
<mat-icon>lock_open</mat-icon>
<span>Unsuspend</span>
</button>
</ng-template>
</mat-menu>
<div
@@ -170,7 +192,10 @@
<button
mat-icon-button
[matMenuTriggerFor]="actions"
[matMenuTriggerData]="{ domainName: element.domainName }"
[matMenuTriggerData]="{
domainName: element.domainName,
domain: element
}"
aria-label="Domain actions"
>
<mat-icon>more_horiz</mat-icon>
@@ -14,13 +14,17 @@
import { SelectionModel } from '@angular/cdk/collections';
import { HttpErrorResponse, HttpStatusCode } from '@angular/common/http';
import { Component, ViewChild, effect, Inject } from '@angular/core';
import { Component, effect, Inject, ViewChild } from '@angular/core';
import { MatPaginator, PageEvent } from '@angular/material/paginator';
import { MatSnackBar } from '@angular/material/snack-bar';
import { MatTableDataSource } from '@angular/material/table';
import { Subject, debounceTime, take, filter } from 'rxjs';
import { debounceTime, filter, Subject, take } from 'rxjs';
import { RegistrarService } from '../registrar/registrar.service';
import { Domain, DomainListService } from './domainList.service';
import {
BULK_ACTION_NAME,
Domain,
DomainListService,
} from './domainList.service';
import { RegistryLockComponent } from './registryLock.component';
import { RegistryLockService } from './registryLock.service';
import {
@@ -62,11 +66,17 @@ export class ResponseDialogComponent {
}
}
enum Operation {
deleting = 'deleting',
suspending = 'suspending',
unsuspending = 'unsuspending',
}
@Component({
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%">
@@ -75,8 +85,8 @@ export class ResponseDialogComponent {
</mat-dialog-content>
<mat-dialog-actions>
<button mat-button (click)="onCancel()">Cancel</button>
<button mat-button color="warn" (click)="onDelete()" [disabled]="!reason">
Delete
<button mat-button color="warn" (click)="onSave()" [disabled]="!reason">
Save
</button>
</mat-dialog-actions>
`,
@@ -84,14 +94,13 @@ export class ResponseDialogComponent {
})
export class ReasonDialogComponent {
reason: string = '';
constructor(
public dialogRef: MatDialogRef<ReasonDialogComponent>,
@Inject(MAT_DIALOG_DATA)
public data: { operation: 'deleting' | 'suspending' }
public data: { operation: Operation }
) {}
onDelete(): void {
onSave(): void {
this.dialogRef.close(this.reason);
}
@@ -108,6 +117,13 @@ export class ReasonDialogComponent {
})
export class DomainListComponent {
public static PATH = 'domain-list';
private static SUSPENDED_STATUSES = [
'SERVER_RENEW_PROHIBITED',
'SERVER_TRANSFER_PROHIBITED',
'SERVER_UPDATE_PROHIBITED',
'SERVER_DELETE_PROHIBITED',
'SERVER_HOLD',
];
private readonly DEBOUNCE_MS = 500;
isAllSelected = false;
@@ -258,19 +274,30 @@ export class DomainListComponent {
return RESTRICTED_ELEMENTS.BULK_DELETE;
}
getElementIdForSuspendUnsuspend() {
return RESTRICTED_ELEMENTS.SUSPEND;
}
getOperationMessage(domain: string) {
if (this.operationResult && this.operationResult[domain])
return this.operationResult[domain].message;
return '';
}
isDomainUnsuspendable(domain: Domain) {
return DomainListComponent.SUSPENDED_STATUSES.every((s) =>
domain.statuses.includes(s)
);
}
sendDeleteRequest(reason: string) {
this.isLoading = true;
this.domainListService
.deleteDomains(
this.selection.selected,
.bulkDomainAction(
this.selection.selected.map((d) => d.domainName),
reason,
this.registrarService.registrarId()
this.registrarService.registrarId(),
BULK_ACTION_NAME.DELETE
)
.pipe(take(1))
.subscribe({
@@ -294,15 +321,17 @@ export class DomainListComponent {
this.operationResult = result;
this.reloadData();
},
error: (err: HttpErrorResponse) =>
this._snackBar.open(err.error || err.message),
error: (err: HttpErrorResponse) => {
this.isLoading = false;
this._snackBar.open(err.error || err.message);
},
});
}
deleteSelectedDomains() {
const dialogRef = this.dialog.open(ReasonDialogComponent, {
data: {
operation: 'deleting',
operation: Operation.deleting,
},
});
@@ -314,4 +343,77 @@ export class DomainListComponent {
)
.subscribe(this.sendDeleteRequest.bind(this));
}
sendSuspendUnsuspendRequest(
domainName: string,
reason: string,
actionName: BULK_ACTION_NAME
) {
this.isLoading = true;
this.domainListService
.bulkDomainAction(
[domainName],
reason,
this.registrarService.registrarId(),
actionName
)
.pipe(take(1))
.subscribe({
next: (result: DomainData) => {
this.isLoading = false;
if (result[domainName].responseCode.toString().startsWith('2')) {
this._snackBar.open(result[domainName].message);
} else {
this.reloadData();
}
},
error: (err: HttpErrorResponse) => {
this.isLoading = false;
this._snackBar.open(err.error || err.message);
},
});
}
onSuspendClick(domainName: string) {
const dialogRef = this.dialog.open(ReasonDialogComponent, {
data: {
operation: Operation.suspending,
},
});
dialogRef
.afterClosed()
.pipe(
take(1),
filter((reason) => !!reason)
)
.subscribe((reason) => {
this.sendSuspendUnsuspendRequest(
domainName,
reason,
BULK_ACTION_NAME.SUSPEND
);
});
}
onUnsuspendClick(domainName: string) {
const dialogRef = this.dialog.open(ReasonDialogComponent, {
data: {
operation: Operation.unsuspending,
},
});
dialogRef
.afterClosed()
.pipe(
take(1),
filter((reason) => !!reason)
)
.subscribe((reason) => {
this.sendSuspendUnsuspendRequest(
domainName,
reason,
BULK_ACTION_NAME.UNSUSPEND
);
});
}
}
@@ -35,6 +35,12 @@ export interface DomainListResult {
totalResults: number;
}
export enum BULK_ACTION_NAME {
DELETE = 'DELETE',
SUSPEND = 'SUSPEND',
UNSUSPEND = 'UNSUSPEND',
}
@Injectable({
providedIn: 'root',
})
@@ -71,11 +77,16 @@ export class DomainListService {
);
}
deleteDomains(domains: Domain[], reason: string, registrarId: string) {
bulkDomainAction(
domains: string[],
reason: string,
registrarId: string,
actionName: BULK_ACTION_NAME
) {
return this.backendService.bulkDomainAction(
domains.map((d) => d.domainName),
domains,
reason,
'DELETE',
actionName,
registrarId
);
}
@@ -20,6 +20,7 @@ export enum RESTRICTED_ELEMENTS {
OTE,
USERS,
BULK_DELETE,
SUSPEND,
}
export const DISABLED_ELEMENTS_PER_ROLE = {
@@ -28,6 +29,7 @@ export const DISABLED_ELEMENTS_PER_ROLE = {
RESTRICTED_ELEMENTS.OTE,
RESTRICTED_ELEMENTS.USERS,
RESTRICTED_ELEMENTS.BULK_DELETE,
RESTRICTED_ELEMENTS.SUSPEND,
],
SUPPORT_LEAD: [RESTRICTED_ELEMENTS.USERS],
SUPPORT_AGENT: [RESTRICTED_ELEMENTS.USERS],
@@ -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() {
@@ -70,7 +70,7 @@ import google.registry.rdap.RdapObjectClasses.RdapRegistrarEntity;
import google.registry.rdap.RdapObjectClasses.SecureDns;
import google.registry.rdap.RdapObjectClasses.Vcard;
import google.registry.rdap.RdapObjectClasses.VcardArray;
import google.registry.request.FullServletPath;
import google.registry.request.RequestServerName;
import google.registry.util.Clock;
import jakarta.persistence.Entity;
import java.net.Inet4Address;
@@ -114,7 +114,7 @@ public class RdapJsonFormatter {
@Nullable
String rdapTosStaticUrl;
@Inject @FullServletPath String fullServletPath;
@Inject @RequestServerName String serverName;
@Inject RdapAuthorization rdapAuthorization;
@Inject Clock clock;
@@ -267,7 +267,7 @@ public class RdapJsonFormatter {
.setDescription(rdapTos)
.addLink(selfLink);
if (rdapTosStaticUrl != null) {
URI htmlBaseURI = URI.create(fullServletPath);
URI htmlBaseURI = URI.create("https//:" + serverName + "/rdap/");
URI htmlUri = htmlBaseURI.resolve(rdapTosStaticUrl);
noticeBuilder.addLink(
Link.builder()
@@ -1071,7 +1071,7 @@ public class RdapJsonFormatter {
/** Create a link relative to the RDAP server endpoint. */
String makeRdapServletRelativeUrl(String part, String... moreParts) {
return makeServerRelativeUrl(fullServletPath, part, moreParts);
return makeServerRelativeUrl("https://" + serverName + "/rdap/", part, moreParts);
}
/** Create a link relative to some base server */
@@ -151,17 +151,9 @@ public final class RequestModule {
* query string.
*/
@Provides
@FullServletPath
static String provideFullServletPath(HttpServletRequest req) {
// Include the port only if it differs from the default for the scheme.
if (("http".equals(req.getScheme()) && (req.getServerPort() == 80))
|| ("https".equals(req.getScheme()) && (req.getServerPort() == 443))) {
return String.format("%s://%s%s", req.getScheme(), req.getServerName(), req.getServletPath());
} else {
return String.format(
"%s://%s:%d%s",
req.getScheme(), req.getServerName(), req.getServerPort(), req.getServletPath());
}
@RequestServerName
static String provideServerName(HttpServletRequest req) {
return req.getServerName();
}
@Provides
@@ -20,11 +20,11 @@ import java.lang.annotation.RetentionPolicy;
import javax.inject.Qualifier;
/**
* Dagger qualifier for the HTTP servlet path, prepended with scheme, host and port.
* Dagger qualifier for the server name of the HTTP request.
*
* <p>See {@link jakarta.servlet.http.HttpServletRequest#getServletPath}
* <p>See {@link jakarta.servlet.http.HttpServletRequest#getServerName()}
*/
@Qualifier
@Documented
@Retention(RetentionPolicy.RUNTIME)
public @interface FullServletPath {}
public @interface RequestServerName {}
@@ -44,7 +44,10 @@ final class RegistryCli implements CommandRunner {
private static final ImmutableSet<RegistryToolEnvironment> DEFAULT_GKE_ENVIRONMENTS =
ImmutableSet.of(
RegistryToolEnvironment.ALPHA, RegistryToolEnvironment.CRASH, RegistryToolEnvironment.QA);
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.
@@ -225,7 +225,8 @@ public class ConsoleUsersAction extends ConsoleApiAction {
UserRoles userRoles =
new UserRoles.Builder()
.setRegistrarRoles(ImmutableMap.of(registrarId, ACCOUNT_MANAGER))
.setRegistrarRoles(
ImmutableMap.of(registrarId, RegistrarRole.valueOf(userData.get().role)))
.build();
User.Builder builder = new User.Builder().setUserRoles(userRoles).setEmailAddress(newEmail);
@@ -0,0 +1,71 @@
// Copyright 2025 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.ui.server.console.domains;
import com.google.common.collect.ImmutableMap;
import com.google.gson.JsonElement;
import google.registry.model.console.ConsolePermission;
/** An action that will unsuspend the given domain, removing all 5 server*Prohibited statuses. */
public class ConsoleBulkDomainUnsuspendActionType implements ConsoleDomainActionType {
private static final String DOMAIN_SUSPEND_XML =
"""
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<epp
xmlns="urn:ietf:params:xml:ns:epp-1.0">
<command>
<update>
<domain:update
xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
<domain:name>%DOMAIN_NAME%</domain:name>
<domain:add></domain:add>
<domain:rem>
<domain:status s="serverDeleteProhibited" lang="en"></domain:status>
<domain:status s="serverHold" lang="en"></domain:status>
<domain:status s="serverRenewProhibited" lang="en"></domain:status>
<domain:status s="serverTransferProhibited" lang="en"></domain:status>
<domain:status s="serverUpdateProhibited" lang="en"></domain:status>
</domain:rem>
</domain:update>
</update>
<extension>
<metadata:metadata
xmlns:metadata="urn:google:params:xml:ns:metadata-1.0">
<metadata:reason>Console unsuspension: %REASON%</metadata:reason>
<metadata:requestedByRegistrar>false</metadata:requestedByRegistrar>
</metadata:metadata>
</extension>
<clTRID>RegistryConsole</clTRID>
</command>
</epp>""";
private final String reason;
public ConsoleBulkDomainUnsuspendActionType(JsonElement jsonElement) {
this.reason = jsonElement.getAsJsonObject().get("reason").getAsString();
}
@Override
public String getXmlContentsToRun(String domainName) {
return ConsoleDomainActionType.fillSubstitutions(
DOMAIN_SUSPEND_XML, ImmutableMap.of("DOMAIN_NAME", domainName, "REASON", reason));
}
@Override
public ConsolePermission getNecessaryPermission() {
return ConsolePermission.SUSPEND_DOMAIN;
}
}
@@ -31,7 +31,8 @@ public interface ConsoleDomainActionType {
enum BulkAction {
DELETE(ConsoleBulkDomainDeleteActionType.class),
SUSPEND(ConsoleBulkDomainSuspendActionType.class);
SUSPEND(ConsoleBulkDomainSuspendActionType.class),
UNSUSPEND(ConsoleBulkDomainUnsuspendActionType.class);
private final Class<? extends ConsoleDomainActionType> actionClass;
@@ -406,26 +406,6 @@ class RdapJsonFormatterTest {
.isEqualTo(loadJson("rdapjson_registrant_logged_out.json"));
}
@Test
void testRegistrant_baseHasNoTrailingSlash() {
// First, make sure we have a trailing slash at the end by default!
// This test tries to change the default state, if the default doesn't have a /, then this test
// doesn't help.
assertThat(rdapJsonFormatter.fullServletPath).endsWith("/");
rdapJsonFormatter.fullServletPath =
rdapJsonFormatter.fullServletPath.substring(
0, rdapJsonFormatter.fullServletPath.length() - 1);
assertAboutJson()
.that(
rdapJsonFormatter
.createRdapContactEntity(
contactRegistrant,
ImmutableSet.of(RdapEntity.Role.REGISTRANT),
OutputDataType.FULL)
.toJson())
.isEqualTo(loadJson("rdapjson_registrant.json"));
}
@Test
void testAdmin() {
assertAboutJson()
@@ -150,7 +150,7 @@ class RdapTestHelper {
static RdapJsonFormatter getTestRdapJsonFormatter(Clock clock) {
RdapJsonFormatter rdapJsonFormatter = new RdapJsonFormatter();
rdapJsonFormatter.rdapAuthorization = RdapAuthorization.PUBLIC_AUTHORIZATION;
rdapJsonFormatter.fullServletPath = "https://example.tld/rdap/";
rdapJsonFormatter.serverName = "example.tld";
rdapJsonFormatter.clock = clock;
rdapJsonFormatter.rdapTos =
ImmutableList.of(
@@ -30,6 +30,7 @@ import static org.mockito.Mockito.when;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSortedMap;
import com.google.gson.Gson;
import com.google.gson.JsonElement;
@@ -61,6 +62,14 @@ public class ConsoleBulkDomainActionTest {
private static final Gson GSON = GsonUtils.provideGson();
private static ImmutableSet<StatusValue> serverSuspensionStatuses =
ImmutableSet.of(
StatusValue.SERVER_RENEW_PROHIBITED,
StatusValue.SERVER_TRANSFER_PROHIBITED,
StatusValue.SERVER_UPDATE_PROHIBITED,
StatusValue.SERVER_DELETE_PROHIBITED,
StatusValue.SERVER_HOLD);
private final FakeClock clock = new FakeClock(DateTime.parse("2024-05-13T00:00:00.000Z"));
@RegisterExtension
@@ -135,12 +144,34 @@ public class ConsoleBulkDomainActionTest {
"""
{"example.tld":{"message":"Command completed successfully","responseCode":1000}}""");
assertThat(loadByEntity(domain).getStatusValues())
.containsAtLeast(
StatusValue.SERVER_RENEW_PROHIBITED,
StatusValue.SERVER_TRANSFER_PROHIBITED,
StatusValue.SERVER_UPDATE_PROHIBITED,
StatusValue.SERVER_DELETE_PROHIBITED,
StatusValue.SERVER_HOLD);
.containsAtLeastElementsIn(serverSuspensionStatuses);
}
@Test
void testSuccess_unsuspend() throws Exception {
User adminUser =
persistResource(
new User.Builder()
.setEmailAddress("email@email.com")
.setUserRoles(
new UserRoles.Builder().setGlobalRole(GlobalRole.FTE).setIsAdmin(true).build())
.build());
persistResource(domain.asBuilder().addStatusValues(serverSuspensionStatuses).build());
ConsoleBulkDomainAction action =
createAction(
"UNSUSPEND",
GSON.toJsonTree(
ImmutableMap.of("domainList", ImmutableList.of("example.tld"), "reason", "test")),
adminUser);
assertThat(loadByEntity(domain).getStatusValues())
.containsAtLeastElementsIn(serverSuspensionStatuses);
action.run();
assertThat(fakeResponse.getStatus()).isEqualTo(SC_OK);
assertThat(fakeResponse.getPayload())
.isEqualTo(
"""
{"example.tld":{"message":"Command completed successfully","responseCode":1000}}""");
assertThat(loadByEntity(domain).getStatusValues()).containsNoneIn(serverSuspensionStatuses);
}
@Test
Binary file not shown.

Before

Width:  |  Height:  |  Size: 129 KiB

After

Width:  |  Height:  |  Size: 130 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 132 KiB

After

Width:  |  Height:  |  Size: 132 KiB

@@ -23,7 +23,7 @@ public class NomulusPostgreSql {
/** 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.21-alpine";
private static final String TARGET_VERSION = "17-alpine";
/**
* Returns the docker image of the targeted Postgresql server version.
@@ -257,11 +257,11 @@ td.section {
<tbody>
<tr>
<td class="property_name">generated by</td>
<td class="property_value">SchemaCrawler 16.23.2</td>
<td class="property_value">SchemaCrawler 16.25.2</td>
</tr>
<tr>
<td class="property_name">generated on</td>
<td class="property_value">2024-12-09 22:31:09</td>
<td class="property_value">2025-02-16 20:10:13</td>
</tr>
<tr>
<td class="property_name">last flyway file</td>
@@ -278,9 +278,9 @@ td.section {
</title>
<polygon fill="white" stroke="transparent" points="-4,4 -4,-3577.64 4860,-3577.64 4860,4 -4,4" />
<text text-anchor="start" x="4616" y="-29.8" font-family="Helvetica,sans-Serif" font-size="14.00">generated by</text>
<text text-anchor="start" x="4699" y="-29.8" font-family="Helvetica,sans-Serif" font-size="14.00">SchemaCrawler 16.23.2</text>
<text text-anchor="start" x="4699" y="-29.8" font-family="Helvetica,sans-Serif" font-size="14.00">SchemaCrawler 16.25.2</text>
<text text-anchor="start" x="4615" y="-10.8" font-family="Helvetica,sans-Serif" font-size="14.00">generated on</text>
<text text-anchor="start" x="4699" y="-10.8" font-family="Helvetica,sans-Serif" font-size="14.00">2024-12-09 22:31:09</text>
<text text-anchor="start" x="4699" y="-10.8" font-family="Helvetica,sans-Serif" font-size="14.00">2025-02-16 20:10:13</text>
<polygon fill="none" stroke="#888888" points="4612,-4 4612,-44 4848,-44 4848,-4 4612,-4" /> <!-- allocationtoken_a08ccbef -->
<g id="node1" class="node">
<title>
@@ -2716,6 +2716,11 @@ td.section {
<td class="minwidth">recurrence_last_expansion</td>
<td class="minwidth">timestamptz not null</td>
</tr>
<tr>
<td class="spacer"></td>
<td class="minwidth"></td>
<td class="minwidth">default '2021-05-31 20:00:00-04'::timestamp with time zone</td>
</tr>
<tr>
<td colspan="3"></td>
</tr>
@@ -2825,6 +2830,11 @@ td.section {
<td class="minwidth"><b><i>job_id</i></b></td>
<td class="minwidth">bigserial not null</td>
</tr>
<tr>
<td class="spacer"></td>
<td class="minwidth"></td>
<td class="minwidth">default nextval('"BsaDomainRefresh_job_id_seq"'::regclass)</td>
</tr>
<tr>
<td class="spacer"></td>
<td class="minwidth"></td>
@@ -2861,6 +2871,11 @@ td.section {
<td class="minwidth"><b><i>job_id</i></b></td>
<td class="minwidth">bigserial not null</td>
</tr>
<tr>
<td class="spacer"></td>
<td class="minwidth"></td>
<td class="minwidth">default nextval('"BsaDownload_job_id_seq"'::regclass)</td>
</tr>
<tr>
<td class="spacer"></td>
<td class="minwidth"></td>
@@ -3069,6 +3084,11 @@ td.section {
<td class="minwidth"><b><i>revision_id</i></b></td>
<td class="minwidth">bigserial not null</td>
</tr>
<tr>
<td class="spacer"></td>
<td class="minwidth"></td>
<td class="minwidth">default nextval('"ClaimsList_revision_id_seq"'::regclass)</td>
</tr>
<tr>
<td class="spacer"></td>
<td class="minwidth"></td>
@@ -3639,6 +3659,11 @@ td.section {
<td class="minwidth"><b><i>id</i></b></td>
<td class="minwidth">bigserial not null</td>
</tr>
<tr>
<td class="spacer"></td>
<td class="minwidth"></td>
<td class="minwidth">default nextval('"DnsRefreshRequest_id_seq"'::regclass)</td>
</tr>
<tr>
<td class="spacer"></td>
<td class="minwidth"></td>
@@ -3785,6 +3810,11 @@ td.section {
<td class="minwidth">lordn_phase</td>
<td class="minwidth">text not null</td>
</tr>
<tr>
<td class="spacer"></td>
<td class="minwidth"></td>
<td class="minwidth">default 'NONE'::text</td>
</tr>
<tr>
<td colspan="3"></td>
</tr>
@@ -4334,6 +4364,11 @@ td.section {
<td class="minwidth"><b><i>id</i></b></td>
<td class="minwidth">bigserial not null</td>
</tr>
<tr>
<td class="spacer"></td>
<td class="minwidth"></td>
<td class="minwidth">default nextval('"DomainTransactionRecord_id_seq"'::regclass)</td>
</tr>
<tr>
<td class="spacer"></td>
<td class="minwidth"></td>
@@ -4895,6 +4930,11 @@ td.section {
<td class="minwidth"><b><i>package_promotion_id</i></b></td>
<td class="minwidth">bigserial not null</td>
</tr>
<tr>
<td class="spacer"></td>
<td class="minwidth"></td>
<td class="minwidth">default nextval('"Package_promotion_id_seq"'::regclass)</td>
</tr>
<tr>
<td class="spacer"></td>
<td class="minwidth"></td>
@@ -5188,6 +5228,11 @@ td.section {
<td class="minwidth"><b><i>revision_id</i></b></td>
<td class="minwidth">bigserial not null</td>
</tr>
<tr>
<td class="spacer"></td>
<td class="minwidth"></td>
<td class="minwidth">default nextval('"PremiumList_revision_id_seq"'::regclass)</td>
</tr>
<tr>
<td class="spacer"></td>
<td class="minwidth"></td>
@@ -5873,6 +5918,11 @@ td.section {
<td class="minwidth"><b><i>revision_id</i></b></td>
<td class="minwidth">bigserial not null</td>
</tr>
<tr>
<td class="spacer"></td>
<td class="minwidth"></td>
<td class="minwidth">default nextval('"RegistryLock_revision_id_seq"'::regclass)</td>
</tr>
<tr>
<td class="spacer"></td>
<td class="minwidth"></td>
@@ -6006,6 +6056,11 @@ td.section {
<td class="minwidth"><b><i>revision_id</i></b></td>
<td class="minwidth">bigserial not null</td>
</tr>
<tr>
<td class="spacer"></td>
<td class="minwidth"></td>
<td class="minwidth">default nextval('"ReservedList_revision_id_seq"'::regclass)</td>
</tr>
<tr>
<td class="spacer"></td>
<td class="minwidth"></td>
@@ -6065,6 +6120,11 @@ td.section {
<td class="minwidth"><b><i>id</i></b></td>
<td class="minwidth">int8 not null</td>
</tr>
<tr>
<td class="spacer"></td>
<td class="minwidth"></td>
<td class="minwidth">default 1</td>
</tr>
<tr>
<td colspan="3"></td>
</tr>
@@ -6155,6 +6215,11 @@ td.section {
<td class="minwidth"><b><i>revision_id</i></b></td>
<td class="minwidth">bigserial not null</td>
</tr>
<tr>
<td class="spacer"></td>
<td class="minwidth"></td>
<td class="minwidth">default nextval('"SignedMarkRevocationList_revision_id_seq"'::regclass)</td>
</tr>
<tr>
<td class="spacer"></td>
<td class="minwidth"></td>
@@ -6209,6 +6274,11 @@ td.section {
<td class="minwidth"><b><i>id</i></b></td>
<td class="minwidth">bigserial not null</td>
</tr>
<tr>
<td class="spacer"></td>
<td class="minwidth"></td>
<td class="minwidth">default nextval('"SafeBrowsingThreat_id_seq"'::regclass)</td>
</tr>
<tr>
<td class="spacer"></td>
<td class="minwidth"></td>
@@ -6321,6 +6391,11 @@ td.section {
<td class="minwidth"><b><i>id</i></b></td>
<td class="minwidth">int8 not null</td>
</tr>
<tr>
<td class="spacer"></td>
<td class="minwidth"></td>
<td class="minwidth">default 1</td>
</tr>
<tr>
<td colspan="3"></td>
</tr>
@@ -257,11 +257,11 @@ td.section {
<tbody>
<tr>
<td class="property_name">generated by</td>
<td class="property_value">SchemaCrawler 16.23.2</td>
<td class="property_value">SchemaCrawler 16.25.2</td>
</tr>
<tr>
<td class="property_name">generated on</td>
<td class="property_value">2024-12-09 22:31:07</td>
<td class="property_value">2025-02-16 20:10:10</td>
</tr>
<tr>
<td class="property_name">last flyway file</td>
@@ -278,9 +278,9 @@ td.section {
</title>
<polygon fill="white" stroke="transparent" points="-4,4 -4,-7933.5 5565,-7933.5 5565,4 -4,4" />
<text text-anchor="start" x="5321" y="-29.8" font-family="Helvetica,sans-Serif" font-size="14.00">generated by</text>
<text text-anchor="start" x="5404" y="-29.8" font-family="Helvetica,sans-Serif" font-size="14.00">SchemaCrawler 16.23.2</text>
<text text-anchor="start" x="5404" y="-29.8" font-family="Helvetica,sans-Serif" font-size="14.00">SchemaCrawler 16.25.2</text>
<text text-anchor="start" x="5320" y="-10.8" font-family="Helvetica,sans-Serif" font-size="14.00">generated on</text>
<text text-anchor="start" x="5404" y="-10.8" font-family="Helvetica,sans-Serif" font-size="14.00">2024-12-09 22:31:07</text>
<text text-anchor="start" x="5404" y="-10.8" font-family="Helvetica,sans-Serif" font-size="14.00">2025-02-16 20:10:10</text>
<polygon fill="none" stroke="#888888" points="5317,-4 5317,-44 5553,-44 5553,-4 5317,-4" /> <!-- allocationtoken_a08ccbef -->
<g id="node1" class="node">
<title>
@@ -3986,11 +3986,21 @@ td.section {
<td class="minwidth">renewal_price_behavior</td>
<td class="minwidth">text not null</td>
</tr>
<tr>
<td class="spacer"></td>
<td class="minwidth"></td>
<td class="minwidth">default 'DEFAULT'::text</td>
</tr>
<tr>
<td class="spacer"></td>
<td class="minwidth">registration_behavior</td>
<td class="minwidth">text not null</td>
</tr>
<tr>
<td class="spacer"></td>
<td class="minwidth"></td>
<td class="minwidth">default 'DEFAULT'::text</td>
</tr>
<tr>
<td class="spacer"></td>
<td class="minwidth">allowed_epp_actions</td>
@@ -4755,6 +4765,11 @@ td.section {
<td class="minwidth">renewal_price_behavior</td>
<td class="minwidth">text not null</td>
</tr>
<tr>
<td class="spacer"></td>
<td class="minwidth"></td>
<td class="minwidth">default 'DEFAULT'::text</td>
</tr>
<tr>
<td class="spacer"></td>
<td class="minwidth">renewal_price_currency</td>
@@ -4770,6 +4785,11 @@ td.section {
<td class="minwidth">recurrence_last_expansion</td>
<td class="minwidth">timestamptz not null</td>
</tr>
<tr>
<td class="spacer"></td>
<td class="minwidth"></td>
<td class="minwidth">default '2021-05-31 20:00:00-04'::timestamp with time zone</td>
</tr>
<tr>
<td colspan="3"></td>
</tr>
@@ -4986,6 +5006,11 @@ td.section {
<td class="minwidth"><b><i>job_id</i></b></td>
<td class="minwidth">bigserial not null</td>
</tr>
<tr>
<td class="spacer"></td>
<td class="minwidth"></td>
<td class="minwidth">default nextval('"BsaDomainRefresh_job_id_seq"'::regclass)</td>
</tr>
<tr>
<td class="spacer"></td>
<td class="minwidth"></td>
@@ -5055,6 +5080,11 @@ td.section {
<td class="minwidth"><b><i>job_id</i></b></td>
<td class="minwidth">bigserial not null</td>
</tr>
<tr>
<td class="spacer"></td>
<td class="minwidth"></td>
<td class="minwidth">default nextval('"BsaDownload_job_id_seq"'::regclass)</td>
</tr>
<tr>
<td class="spacer"></td>
<td class="minwidth"></td>
@@ -5392,6 +5422,11 @@ td.section {
<td class="minwidth"><b><i>revision_id</i></b></td>
<td class="minwidth">bigserial not null</td>
</tr>
<tr>
<td class="spacer"></td>
<td class="minwidth"></td>
<td class="minwidth">default nextval('"ClaimsList_revision_id_seq"'::regclass)</td>
</tr>
<tr>
<td class="spacer"></td>
<td class="minwidth"></td>
@@ -6866,6 +6901,11 @@ td.section {
<td class="minwidth"><b><i>id</i></b></td>
<td class="minwidth">bigserial not null</td>
</tr>
<tr>
<td class="spacer"></td>
<td class="minwidth"></td>
<td class="minwidth">default nextval('"DnsRefreshRequest_id_seq"'::regclass)</td>
</tr>
<tr>
<td class="spacer"></td>
<td class="minwidth"></td>
@@ -7224,6 +7264,11 @@ td.section {
<td class="minwidth">lordn_phase</td>
<td class="minwidth">text not null</td>
</tr>
<tr>
<td class="spacer"></td>
<td class="minwidth"></td>
<td class="minwidth">default 'NONE'::text</td>
</tr>
<tr>
<td class="spacer"></td>
<td class="minwidth">last_update_time_via_epp</td>
@@ -8154,6 +8199,11 @@ td.section {
<td class="minwidth">lordn_phase</td>
<td class="minwidth">text not null</td>
</tr>
<tr>
<td class="spacer"></td>
<td class="minwidth"></td>
<td class="minwidth">default 'NONE'::text</td>
</tr>
<tr>
<td class="spacer"></td>
<td class="minwidth">last_update_time_via_epp</td>
@@ -8494,6 +8544,11 @@ td.section {
<td class="minwidth"><b><i>id</i></b></td>
<td class="minwidth">bigserial not null</td>
</tr>
<tr>
<td class="spacer"></td>
<td class="minwidth"></td>
<td class="minwidth">default nextval('"DomainTransactionRecord_id_seq"'::regclass)</td>
</tr>
<tr>
<td class="spacer"></td>
<td class="minwidth"></td>
@@ -9622,6 +9677,11 @@ td.section {
<td class="minwidth"><b><i>package_promotion_id</i></b></td>
<td class="minwidth">bigserial not null</td>
</tr>
<tr>
<td class="spacer"></td>
<td class="minwidth"></td>
<td class="minwidth">default nextval('"Package_promotion_id_seq"'::regclass)</td>
</tr>
<tr>
<td class="spacer"></td>
<td class="minwidth"></td>
@@ -10154,6 +10214,11 @@ td.section {
<td class="minwidth"><b><i>revision_id</i></b></td>
<td class="minwidth">bigserial not null</td>
</tr>
<tr>
<td class="spacer"></td>
<td class="minwidth"></td>
<td class="minwidth">default nextval('"PremiumList_revision_id_seq"'::regclass)</td>
</tr>
<tr>
<td class="spacer"></td>
<td class="minwidth"></td>
@@ -11688,6 +11753,11 @@ td.section {
<td class="minwidth"><b><i>revision_id</i></b></td>
<td class="minwidth">bigserial not null</td>
</tr>
<tr>
<td class="spacer"></td>
<td class="minwidth"></td>
<td class="minwidth">default nextval('"RegistryLock_revision_id_seq"'::regclass)</td>
</tr>
<tr>
<td class="spacer"></td>
<td class="minwidth"></td>
@@ -11958,6 +12028,11 @@ td.section {
<td class="minwidth"><b><i>revision_id</i></b></td>
<td class="minwidth">bigserial not null</td>
</tr>
<tr>
<td class="spacer"></td>
<td class="minwidth"></td>
<td class="minwidth">default nextval('"ReservedList_revision_id_seq"'::regclass)</td>
</tr>
<tr>
<td class="spacer"></td>
<td class="minwidth"></td>
@@ -12057,6 +12132,11 @@ td.section {
<td class="minwidth"><b><i>id</i></b></td>
<td class="minwidth">int8 not null</td>
</tr>
<tr>
<td class="spacer"></td>
<td class="minwidth"></td>
<td class="minwidth">default 1</td>
</tr>
<tr>
<td colspan="3"></td>
</tr>
@@ -12193,6 +12273,11 @@ td.section {
<td class="minwidth"><b><i>revision_id</i></b></td>
<td class="minwidth">bigserial not null</td>
</tr>
<tr>
<td class="spacer"></td>
<td class="minwidth"></td>
<td class="minwidth">default nextval('"SignedMarkRevocationList_revision_id_seq"'::regclass)</td>
</tr>
<tr>
<td class="spacer"></td>
<td class="minwidth"></td>
@@ -12270,6 +12355,11 @@ td.section {
<td class="minwidth"><b><i>id</i></b></td>
<td class="minwidth">bigserial not null</td>
</tr>
<tr>
<td class="spacer"></td>
<td class="minwidth"></td>
<td class="minwidth">default nextval('"SafeBrowsingThreat_id_seq"'::regclass)</td>
</tr>
<tr>
<td class="spacer"></td>
<td class="minwidth"></td>
@@ -12595,6 +12685,11 @@ td.section {
<td class="minwidth">breakglass_mode</td>
<td class="minwidth">bool not null</td>
</tr>
<tr>
<td class="spacer"></td>
<td class="minwidth"></td>
<td class="minwidth">default false</td>
</tr>
<tr>
<td class="spacer"></td>
<td class="minwidth">bsa_enroll_start_time</td>
@@ -12699,6 +12794,11 @@ td.section {
<td class="minwidth"><b><i>id</i></b></td>
<td class="minwidth">int8 not null</td>
</tr>
<tr>
<td class="spacer"></td>
<td class="minwidth"></td>
<td class="minwidth">default 1</td>
</tr>
<tr>
<td colspan="3"></td>
</tr>
@@ -2,12 +2,13 @@
-- PostgreSQL database dump
--
-- Dumped from database version 11.21
-- Dumped by pg_dump version 11.21
-- Dumped from database version 17.3
-- Dumped by pg_dump version 17.3
SET statement_timeout = 0;
SET lock_timeout = 0;
SET idle_in_transaction_session_timeout = 0;
SET transaction_timeout = 0;
SET client_encoding = 'UTF8';
SET standard_conforming_strings = on;
SELECT pg_catalog.set_config('search_path', '', false);
@@ -32,7 +33,7 @@ COMMENT ON EXTENSION hstore IS 'data type for storing sets of (key, value) pairs
SET default_tablespace = '';
SET default_with_oids = false;
SET default_table_access_method = heap;
--
-- Name: AllocationToken; Type: TABLE; Schema: public; Owner: -
+40 -40
View File
@@ -29,48 +29,48 @@ environment=${1}
base_domain=${2}
project="domain-registry-"${environment}
current_context=$(kubectl config current-context)
line=$(gcloud container clusters list --project "${project}" | grep nomulus | grep main)
parts=(${line})
echo "Updating cluster ${parts[0]} in location ${parts[1]}..."
gcloud container fleet memberships get-credentials "${parts[0]}" --project "${project}"
for service in frontend backend pubapi console
do
sed s/GCP_PROJECT/"${project}"/g "./kubernetes/nomulus-${service}.yaml" | \
sed s/ENVIRONMENT/"${environment}"/g | \
sed s/PROXY_ENV/"${environment}"/g | \
sed s/EPP/"epp"/g | \
kubectl apply -f -
kubectl rollout restart deployment/${service}
# canary
sed s/GCP_PROJECT/"${project}"/g "./kubernetes/nomulus-${service}.yaml" | \
sed s/ENVIRONMENT/"${environment}"/g | \
sed s/PROXY_ENV/"${environment}_canary"/g | \
sed s/EPP/"epp-canary"/g | \
sed s/"${service}"/"${service}-canary"/g | \
kubectl apply -f -
kubectl rollout restart deployment/${service}-canary
done
kubectl apply -f "./kubernetes/gateway/nomulus-gateway.yaml"
kubectl apply -f "./kubernetes/gateway/nomulus-iap-${environment}.yaml"
for service in frontend backend console pubapi
do
sed s/BASE_DOMAIN/"${base_domain}"/g "./kubernetes/gateway/nomulus-route-${service}.yaml" | \
kubectl apply -f -
sed s/SERVICE/"${service}"/g "./kubernetes/gateway/nomulus-backend-policy-${environment}.yaml" | \
kubectl apply -f -
sed s/SERVICE/"${service}-canary"/g "./kubernetes/gateway/nomulus-backend-policy-${environment}.yaml" | \
kubectl apply -f -
done
# Restart proxies
while read line
do
parts=(${line})
echo "Updating cluster ${parts[0]} in location ${parts[1]}..."
gcloud container fleet memberships get-credentials "${parts[0]}" --project "${project}"
for service in frontend backend pubapi console
do
sed s/GCP_PROJECT/"${project}"/g "./kubernetes/nomulus-${service}.yaml" | \
sed s/ENVIRONMENT/"${environment}"/g | \
sed s/PROXY_ENV/"${environment}"/g | \
sed s/EPP/"epp"/g | \
sed s/WHOIS/"whois"/g | \
kubectl apply -f -
# canary
sed s/GCP_PROJECT/"${project}"/g "./kubernetes/nomulus-${service}.yaml" | \
sed s/ENVIRONMENT/"${environment}"/g | \
sed s/PROXY_ENV/"${environment}_canary"/g | \
sed s/EPP/"epp-canary"/g | \
sed s/WHOIS/"whois-canary"/g | \
sed s/"${service}"/"${service}-canary"/g | \
kubectl apply -f -
done
# Kills all running pods, new pods created will be pulling the new image.
kubectl delete pods --all
# The multi-cluster gateway is only deployed to one cluster (the one in the US).
if [[ "${parts[1]}" == us-* ]]
then
kubectl apply -f "./kubernetes/gateway/nomulus-gateway.yaml"
for service in frontend backend console pubapi
do
sed s/BASE_DOMAIN/"${base_domain}"/g "./kubernetes/gateway/nomulus-route-${service}.yaml" | \
kubectl apply -f -
# Don't enable IAP on pubapi.
if [[ "${service}" == pubapi ]]
then
continue
fi
sed s/SERVICE/"${service}"/g "./kubernetes/gateway/nomulus-iap-${environment}.yaml" | \
kubectl apply -f -
sed s/SERVICE/"${service}-canary"/g "./kubernetes/gateway/nomulus-iap-${environment}.yaml" | \
kubectl apply -f -
done
fi
done < <(gcloud container clusters list --project "${project}" | grep nomulus)
gcloud container clusters get-credentials ${parts[0]} \
--project ${project} --location ${parts[1]}
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"
@@ -4,7 +4,18 @@ 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
port: 80
allowedRoutes:
kinds:
- kind: HTTPRoute
- name: https
protocol: HTTPS
port: 443
@@ -15,3 +26,19 @@ spec:
allowedRoutes:
kinds:
- kind: HTTPRoute
---
apiVersion: gateway.networking.k8s.io/v1beta1
kind: HTTPRoute
metadata:
name: redirect
spec:
parentRefs:
- kind: Gateway
name: nomulus
sectionName: http
rules:
- filters:
- type: RequestRedirect
requestRedirect:
scheme: https
@@ -6,6 +6,7 @@ spec:
parentRefs:
- kind: Gateway
name: nomulus
sectionName: https
hostnames:
- "backend.BASE_DOMAIN"
rules:
@@ -6,6 +6,7 @@ spec:
parentRefs:
- kind: Gateway
name: nomulus
sectionName: https
hostnames:
- "console.BASE_DOMAIN"
rules:
@@ -6,6 +6,7 @@ spec:
parentRefs:
- kind: Gateway
name: nomulus
sectionName: https
hostnames:
- "frontend.BASE_DOMAIN"
rules:
@@ -6,6 +6,7 @@ spec:
parentRefs:
- kind: Gateway
name: nomulus
sectionName: https
hostnames:
- "pubapi.BASE_DOMAIN"
rules:
+12 -3
View File
@@ -2,6 +2,8 @@ apiVersion: apps/v1
kind: Deployment
metadata:
name: backend
annotations:
tag: "latest"
spec:
selector:
matchLabels:
@@ -12,6 +14,8 @@ spec:
service: backend
spec:
serviceAccountName: nomulus
nodeSelector:
cloud.google.com/compute-class: "Performance"
containers:
- name: backend
image: gcr.io/GCP_PROJECT/nomulus
@@ -20,13 +24,18 @@ spec:
name: http
resources:
requests:
cpu: "500m"
cpu: "100m"
memory: "512Mi"
args: [ENVIRONMENT]
env:
- name: POD_ID
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: JETTY_WORKER_INSTANCE
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: NAMESPACE_ID
valueFrom:
fieldRef:
@@ -43,8 +52,8 @@ spec:
apiVersion: apps/v1
kind: Deployment
name: backend
minReplicas: 1
maxReplicas: 20
minReplicas: 2
maxReplicas: 5
metrics:
- type: Resource
resource:
+11 -2
View File
@@ -2,6 +2,8 @@ apiVersion: apps/v1
kind: Deployment
metadata:
name: console
annotations:
tag: "latest"
spec:
selector:
matchLabels:
@@ -12,6 +14,8 @@ spec:
service: console
spec:
serviceAccountName: nomulus
nodeSelector:
cloud.google.com/compute-class: "Performance"
containers:
- name: console
image: gcr.io/GCP_PROJECT/nomulus
@@ -20,13 +24,18 @@ spec:
name: http
resources:
requests:
cpu: "500m"
cpu: "100m"
memory: "512Mi"
args: [ENVIRONMENT]
env:
- name: POD_ID
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: JETTY_WORKER_INSTANCE
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: NAMESPACE_ID
valueFrom:
fieldRef:
@@ -44,7 +53,7 @@ spec:
kind: Deployment
name: console
minReplicas: 1
maxReplicas: 20
maxReplicas: 5
metrics:
- type: Resource
resource:
+14 -4
View File
@@ -2,6 +2,8 @@ apiVersion: apps/v1
kind: Deployment
metadata:
name: frontend
annotations:
tag: "latest"
spec:
selector:
matchLabels:
@@ -12,6 +14,8 @@ spec:
service: frontend
spec:
serviceAccountName: nomulus
nodeSelector:
cloud.google.com/compute-class: "Performance"
containers:
- name: frontend
image: gcr.io/GCP_PROJECT/nomulus
@@ -20,13 +24,18 @@ spec:
name: http
resources:
requests:
cpu: "500m"
cpu: "100m"
memory: "512Mi"
args: [ENVIRONMENT]
env:
- name: POD_ID
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: JETTY_WORKER_INSTANCE
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: NAMESPACE_ID
valueFrom:
fieldRef:
@@ -40,7 +49,8 @@ spec:
name: epp
resources:
requests:
cpu: "500m"
cpu: "100m"
memory: "512Mi"
args: [--env, PROXY_ENV, --log, --local]
env:
- name: POD_ID
@@ -71,8 +81,8 @@ spec:
apiVersion: apps/v1
kind: Deployment
name: frontend
minReplicas: 1
maxReplicas: 20
minReplicas: 15
maxReplicas: 15
metrics:
- type: Resource
resource:
+12 -53
View File
@@ -2,6 +2,8 @@ apiVersion: apps/v1
kind: Deployment
metadata:
name: pubapi
annotations:
tag: "latest"
spec:
selector:
matchLabels:
@@ -12,6 +14,8 @@ spec:
service: pubapi
spec:
serviceAccountName: nomulus
nodeSelector:
cloud.google.com/compute-class: "Performance"
containers:
- name: pubapi
image: gcr.io/GCP_PROJECT/nomulus
@@ -20,43 +24,24 @@ spec:
name: http
resources:
requests:
cpu: "500m"
cpu: "100m"
memory: "512Mi"
args: [ENVIRONMENT]
env:
- name: POD_ID
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: JETTY_WORKER_INSTANCE
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: NAMESPACE_ID
valueFrom:
fieldRef:
fieldPath: metadata.namespace
- name: CONTAINER_NAME
value: pubapi
- name: WHOIS
image: gcr.io/GCP_PROJECT/proxy
ports:
- containerPort: 30001
name: whois
- containerPort: 30010
name: http-whois
- containerPort: 30011
name: https-whois
resources:
requests:
cpu: "500m"
args: [ --env, PROXY_ENV, --log, --local ]
env:
- name: POD_ID
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: NAMESPACE_ID
valueFrom:
fieldRef:
fieldPath: metadata.namespace
- name: CONTAINER_NAME
value: WHOIS
---
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
@@ -67,8 +52,8 @@ spec:
apiVersion: apps/v1
kind: Deployment
name: pubapi
minReplicas: 1
maxReplicas: 20
minReplicas: 5
maxReplicas: 15
metrics:
- type: Resource
resource:
@@ -89,32 +74,6 @@ spec:
targetPort: http
name: http
---
apiVersion: v1
kind: Service
metadata:
name: WHOIS
annotations:
cloud.google.com/l4-rbs: enabled
networking.gke.io/weighted-load-balancing: pods-per-node
spec:
type: LoadBalancer
# Traffic is directly delivered to a node, preserving the original source IP.
externalTrafficPolicy: Local
ipFamilies: [IPv4, IPv6]
ipFamilyPolicy: RequireDualStack
selector:
service: pubapi
ports:
- port: 43
targetPort: whois
name: whois
- port: 80
targetPort: http-whois
name: http-whois
- port: 443
targetPort: https-whois
name: https-whois
---
apiVersion: net.gke.io/v1
kind: ServiceExport
metadata:
+1 -1
View File
@@ -28,7 +28,7 @@ COPY go.sum ./
COPY go.mod ./
RUN go build -o /deployCloudSchedulerAndQueue
FROM marketplace.gcr.io/google/ubuntu2204
FROM marketplace.gcr.io/google/ubuntu2404
ENV DEBIAN_FRONTEND=noninteractive LANG=en_US.UTF-8
# Add script for cloud scheduler and cloud tasks deployment
COPY --from=deployCloudSchedulerAndQueueBuilder /deployCloudSchedulerAndQueue /usr/local/bin/deployCloudSchedulerAndQueue
+3 -5
View File
@@ -43,10 +43,6 @@ apt-get install openjdk-21-jdk-headless -y
# Install Python
apt-get install python3 -y
# As of March 2021 python3 is at v3.6. Get pip then install dataclasses
# (introduced in 3.7) for nom_build
apt-get install python3-pip -y
python3 -m pip install dataclasses
# Install Node
apt-get install npm -y
@@ -57,11 +53,13 @@ npm install -g n
for i in {1..5}; do n 22.7.0 && break || sleep 15; done
# Install gp_dump
apt-get install postgresql-client-11 procps -y
apt-get install postgresql-client-17 procps -y
# Install gcloud
apt-get install google-cloud-cli -y
apt-get install google-cloud-sdk-app-engine-java -y
apt-get install kubectl -y
apt-get install google-cloud-cli-gke-gcloud-auth-plugin -y
# Install git
apt-get install git -y
+1
View File
@@ -198,6 +198,7 @@ artifacts:
- 'release/cloudbuild-delete-*.yaml'
- 'release/cloudbuild-schema-deploy-*.yaml'
- 'release/cloudbuild-schema-verify-*.yaml'
- 'release/cloudbuild-restart-proxies-*.yaml'
- 'jetty/kubernetes/*.yaml'
- 'jetty/kubernetes/gateway/*.yaml'
# The images are already uploaded, but we still need to include them there so that
+19 -13
View File
@@ -88,6 +88,7 @@ steps:
sed -i s/builder:latest/builder@$builder_digest/g release/cloudbuild-schema-deploy.yaml
sed -i s/builder:latest/builder@$builder_digest/g release/cloudbuild-schema-verify.yaml
sed -i s/builder:latest/builder@$builder_digest/g release/cloudbuild-delete.yaml
sed -i s/builder:latest/builder@$builder_digest/g release/cloudbuild-restart-proxies.yaml
sed -i s/GCP_PROJECT/${PROJECT_ID}/ proxy/kubernetes/proxy-*.yaml
sed -i s/'$${TAG_NAME}'/${TAG_NAME}/g release/cloudbuild-sync-and-tag.yaml
sed -i s/'$${TAG_NAME}'/${TAG_NAME}/g release/cloudbuild-deploy.yaml
@@ -99,6 +100,11 @@ steps:
> release/cloudbuild-deploy-gke-${environment}.yaml
sed s/'$${_ENV}'/${environment}/g release/cloudbuild-delete.yaml \
> release/cloudbuild-delete-${environment}.yaml
sed s/'$${_ENV}'/${environment}/g release/cloudbuild-restart-proxies.yaml \
> release/cloudbuild-restart-proxies-${environment}.yaml
sed s/'$${_ENV}'/${environment}/g release/cloudbuild-restart-proxies.yaml | \
sed s/proxy-deployment/proxy-deployment-canary/g \
> release/cloudbuild-restart-proxies-${environment}-canary.yaml
done
# Build and upload the schema_deployer image.
- name: 'gcr.io/cloud-builders/docker'
@@ -178,14 +184,14 @@ steps:
base_domain=$(grep baseDomain \
./core/src/main/java/google/registry/config/files/nomulus-config-${env}.yaml | \
awk '{print $2}')
for service in frontend backend pubapi console
for service in frontend backend pubapi console
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 | \
sed s/WHOIS/whois/g > ./jetty/kubernetes/nomulus-${env}-${service}.yaml
sed s/PROXY_ENV/"${env}"/g | \
sed s/EPP/"epp"/g > ./jetty/kubernetes/nomulus-${env}-${service}.yaml
# Proxy '--log' flag does not work on production.
if [ ${env} == production ]
then
@@ -198,10 +204,10 @@ 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 | \
sed s/WHOIS/whois-canary/g | \
sed s/PROXY_ENV/"${env}_canary"/g | \
sed s/EPP/"epp-canary"/g | \
sed s/${service}/${service}-canary/g \
> ./jetty/kubernetes/nomulus-${env}-${service}-canary.yaml
# Proxy '--log' flag does not work on production.
@@ -218,11 +224,11 @@ steps:
sed s/BASE_DOMAIN/${base_domain}/g \
./jetty/kubernetes/gateway/nomulus-route-${service}.yaml \
> ./jetty/kubernetes/gateway/nomulus-route-${env}-${service}.yaml
# IAP
sed s/SERVICE/${service}/g ./jetty/kubernetes/gateway/nomulus-iap-${env}.yaml \
> ./jetty/kubernetes/gateway/nomulus-iap-${env}-${service}.yaml
sed s/SERVICE/${service}-canary/g ./jetty/kubernetes/gateway/nomulus-iap-${env}.yaml \
> ./jetty/kubernetes/gateway/nomulus-iap-${env}-${service}-canary.yaml
# GCP backend policy
sed s/SERVICE/${service}/g ./jetty/kubernetes/gateway/nomulus-backend-policy-${env}.yaml \
> ./jetty/kubernetes/gateway/nomulus-backend-policy-${env}-${service}.yaml
sed s/SERVICE/${service}-canary/g ./jetty/kubernetes/gateway/nomulus-backend-policy-${env}.yaml \
> ./jetty/kubernetes/gateway/nomulus-backend-policy-${env}-${service}-canary.yaml
done
done
# Upload the Gradle binary to GCS if it does not exist and point URL in Gradle wrapper to it.
@@ -270,7 +276,7 @@ steps:
$(gcloud auth list --format='get(account)' --filter=active)
git add .
git commit -m "Release commit for tag ${TAG_NAME}"
git push -o nokeycheck origin master
git push -o nokeycheck origin master
git tag ${TAG_NAME}
git push -o nokeycheck origin ${TAG_NAME}
timeout: 3600s
+54
View File
@@ -0,0 +1,54 @@
# This will do rolling restarts of all proxies. This forces the client to reconnect
# and resets the sessions.
#
# To manually trigger a build on GCB, run:
# gcloud builds submit --config=cloudbuild-restart-proxies.yaml \
# --substitutions=_ENV=[ENV] ..
#
# To trigger a build automatically, follow the instructions below and add a trigger:
# https://cloud.google.com/cloud-build/docs/running-builds/automate-builds
#
# Note: to work around the issue in Spinnaker's 'Deployment Manifest' stage,
# variable references must avoid the ${var} format. Valid formats include
# $var or ${"${var}"}. This file uses the former. Since TAG_NAME and _ENV are
# expanded in the copies sent to Spinnaker, we preserve the brackets around
# them for safe pattern matching during release.
# See https://github.com/spinnaker/spinnaker/issues/3028 for more information.
steps:
# Pull the credential for nomulus tool.
- name: 'gcr.io/$PROJECT_ID/builder:latest'
entrypoint: /bin/bash
args:
- -c
- |
set -e
gcloud secrets versions access latest \
--secret nomulus-tool-cloudbuild-credential > tool-credential.json
# Do rolling restarts of all proxies in all environments.
- name: 'gcr.io/$PROJECT_ID/builder:latest'
entrypoint: /bin/bash
args:
- -c
- |
if [ ${_ENV} == production ]
then
project_id="domain-registry"
else
project_id="domain-registry-${_ENV}"
fi
gcloud auth activate-service-account --key-file=tool-credential.json
while read line
do
name=$(echo $line | awk '{print $1}')
location=$(echo $line | awk '{print $2}')
echo $name $region
echo "Updating cluster $name in location $location..."
gcloud container clusters get-credentials $name \
--project $project_id --location $location
kubectl rollout restart deployment/proxy-deployment
done < <(gcloud container clusters list --project $project_id | grep proxy-cluster)
timeout: 3600s
options:
machineType: 'N1_HIGHCPU_8'
@@ -3,3 +3,6 @@ COMMENT ON EXTENSION pgaudit IS 'provides auditing functionality';
SET default_with_oids = false;
CREATE EXTENSION IF NOT EXISTS pg_stat_statements WITH SCHEMA public;
COMMENT ON EXTENSION pg_stat_statements IS 'track execution statistics of all SQL statements executed';
SET transaction_timeout = 0;
SET default_table_access_method = heap;
SET default_with_oids = false;
@@ -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 "^[<>]"