1
0
mirror of https://github.com/google/nomulus synced 2026-07-13 19:42:36 +00:00

Compare commits

..

7 Commits

Author SHA1 Message Date
gbrodman fd7820759d Use token's renewalPrice if renewalBehavior is SPECIFIED (#2502)
Previous PRs and token changes (see b/332928676) have made it so that
SPECIFIED renewalPriceBehavior tokens must have a renewal price. As
such, we can now use that renewalPrice when creating domains with
SPECIFIED tokens.
2024-08-15 19:06:32 +00:00
sarahcaseybot 69359bb1e6 Add QPS and incomplete connections metrics to load test client (#2487)
* Add QPS and incomplete connections metrics to load test client

* Add a failed request count

* Add todos

* Reuse contact

* Add bugs to todos

* small fix

* Clarify QPS
2024-08-14 18:14:17 +00:00
gbrodman 35b602a76e Remove User ID field from SQL (#2523)
This will fail tests until the corresponding PR in Java is deployed.
2024-08-14 17:51:15 +00:00
dependabot[bot] 82002d1f75 Bump axios in /console-webapp in the npm_and_yarn group (#2532)
Bumps the npm_and_yarn group in /console-webapp with 1 update: [axios](https://github.com/axios/axios).


Updates `axios` from 1.7.2 to 1.7.4
- [Release notes](https://github.com/axios/axios/releases)
- [Changelog](https://github.com/axios/axios/blob/v1.x/CHANGELOG.md)
- [Commits](https://github.com/axios/axios/compare/v1.7.2...v1.7.4)

---
updated-dependencies:
- dependency-name: axios
  dependency-type: indirect
  dependency-group: npm_and_yarn
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-08-14 15:44:14 +00:00
Lai Jiang 2fd9b062df Make Nomulus work on GKE with external load balancer (#2527)
This will create a multi-cluster external load balancer exposing HTTP
traffic to nomulus running in clusters in the fleet.
2024-08-14 14:32:39 +00:00
Ben McIlwain ec3804e87e Make domain update flow handle null auth data (#2530)
It's valid for the auth data to be null (although it only happens 10 times
across our entire registry), so the domain update flow should not fail out with
a NullPointerException when the existing state of the data is null and the
update isn't adding that data either.

BUG=http://b/359264787
2024-08-13 18:19:44 +00:00
Pavlo Tkach d0d28cc7e6 Fix console contact delete button not working (#2528) 2024-08-09 16:42:39 +00:00
24 changed files with 2761 additions and 2655 deletions
+3 -3
View File
@@ -6451,9 +6451,9 @@
}
},
"node_modules/axios": {
"version": "1.7.2",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.7.2.tgz",
"integrity": "sha512-2A8QhOMrbomlDuiLeK9XibIBzuHeRcqqNOHp0Cyp5EoJ1IFDh+XZH3A6BkXtv0K4gFGCI0Y4BM7B1wOEi0Rmgw==",
"version": "1.7.4",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.7.4.tgz",
"integrity": "sha512-DukmaFRnY6AzAALSH4J2M3k6PkaC+MfaAGdEERRWcC9q3/TWQwLpHR8ZRLKTdQ3aBDL64EdluRDjJqKw+BPZEw==",
"dev": true,
"dependencies": {
"follow-redirects": "^1.15.6",
@@ -18,7 +18,11 @@
<mat-icon>edit</mat-icon>
Edit
</button>
<button mat-icon-button aria-label="Delete Contact">
<button
mat-icon-button
aria-label="Delete Contact"
(click)="deleteContact()"
>
<mat-icon>delete</mat-icon>
</button>
}
@@ -50,6 +50,9 @@ export class ContactDetailsComponent {
error: (err: HttpErrorResponse) => {
this._snackBar.open(err.error);
},
complete: () => {
this.goBack();
},
});
}
}
@@ -365,7 +365,7 @@ public final class DomainCreateFlow implements MutatingFlow {
createAutorenewBillingEvent(
domainHistoryId,
registrationExpirationTime,
getRenewalPriceInfo(isAnchorTenant, allocationToken, feesAndCredits));
getRenewalPriceInfo(isAnchorTenant, allocationToken));
PollMessage.Autorenew autorenewPollMessage =
createAutorenewPollMessage(domainHistoryId, registrationExpirationTime);
ImmutableSet.Builder<ImmutableObject> entitiesToSave = new ImmutableSet.Builder<>();
@@ -688,9 +688,7 @@ public final class DomainCreateFlow implements MutatingFlow {
* AllocationToken} is 'SPECIFIED'.
*/
static RenewalPriceInfo getRenewalPriceInfo(
boolean isAnchorTenant,
Optional<AllocationToken> allocationToken,
FeesAndCredits feesAndCredits) {
boolean isAnchorTenant, Optional<AllocationToken> allocationToken) {
if (isAnchorTenant) {
allocationToken.ifPresent(
token ->
@@ -701,7 +699,7 @@ public final class DomainCreateFlow implements MutatingFlow {
} else if (allocationToken.isPresent()
&& allocationToken.get().getRenewalPriceBehavior() == RenewalPriceBehavior.SPECIFIED) {
return RenewalPriceInfo.create(
RenewalPriceBehavior.SPECIFIED, feesAndCredits.getCreateCost());
RenewalPriceBehavior.SPECIFIED, allocationToken.get().getRenewalPrice().get());
} else {
return RenewalPriceInfo.create(RenewalPriceBehavior.DEFAULT, null);
}
@@ -14,7 +14,6 @@
package google.registry.flows.domain;
import static com.google.common.base.MoreObjects.firstNonNull;
import static com.google.common.collect.ImmutableSet.toImmutableSet;
import static com.google.common.collect.ImmutableSortedSet.toImmutableSortedSet;
import static com.google.common.collect.Sets.symmetricDifference;
@@ -282,7 +281,7 @@ public final class DomainUpdateFlow implements MutatingFlow {
.removeContacts(remove.getContacts())
.addContacts(add.getContacts())
.setRegistrant(determineUpdatedRegistrant(change, domain))
.setAuthInfo(firstNonNull(change.getAuthInfo(), domain.getAuthInfo()));
.setAuthInfo(Optional.ofNullable(change.getAuthInfo()).orElse(domain.getAuthInfo()));
if (!add.getNameservers().isEmpty()) {
domainBuilder.addNameservers(add.getNameservers().stream().collect(toImmutableSet()));
@@ -144,6 +144,7 @@ public class DomainBase extends EppResource
@AttributeOverride(name = "pw.value", column = @Column(name = "auth_info_value")),
@AttributeOverride(name = "pw.repoId", column = @Column(name = "auth_info_repo_id")),
})
@Nullable
DomainAuthInfo authInfo;
/** Data used to construct DS records for this domain. */
@@ -620,6 +621,7 @@ public class DomainBase extends EppResource
return getAllContacts(true);
}
@Nullable
public DomainAuthInfo getAuthInfo() {
return authInfo;
}
@@ -16,6 +16,7 @@ package google.registry.model.eppcommon;
import google.registry.model.ImmutableObject;
import google.registry.model.UnsafeSerializable;
import javax.annotation.Nullable;
import javax.persistence.Embeddable;
import javax.persistence.Embedded;
import javax.persistence.MappedSuperclass;
@@ -49,10 +50,12 @@ public abstract class AuthInfo extends ImmutableObject implements UnsafeSerializ
public static class PasswordAuth extends ImmutableObject implements UnsafeSerializable {
@XmlValue
@XmlJavaTypeAdapter(NormalizedStringAdapter.class)
@Nullable
String value;
@XmlAttribute(name = "roid")
@XmlJavaTypeAdapter(CollapsedStringAdapter.class)
@Nullable
String repoId;
public String getValue() {
@@ -63,14 +66,14 @@ public abstract class AuthInfo extends ImmutableObject implements UnsafeSerializ
return repoId;
}
public static PasswordAuth create(String value, String repoId) {
public static PasswordAuth create(@Nullable String value, @Nullable String repoId) {
PasswordAuth instance = new PasswordAuth();
instance.value = value;
instance.repoId = repoId;
return instance;
}
public static PasswordAuth create(String value) {
public static PasswordAuth create(@Nullable String value) {
return create(value, null);
}
}
@@ -344,8 +344,7 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
.and()
.hasPeriodYears(2);
RenewalPriceInfo renewalPriceInfo =
DomainCreateFlow.getRenewalPriceInfo(
isAnchorTenant, Optional.ofNullable(allocationToken), feesAndCredits);
DomainCreateFlow.getRenewalPriceInfo(isAnchorTenant, Optional.ofNullable(allocationToken));
// There should be one bill for the create and one for the recurrence autorenew event.
BillingEvent createBillingEvent =
new BillingEvent.Builder()
@@ -3189,53 +3188,25 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
@Test
void testGetRenewalPriceInfo_isAnchorTenantWithoutToken_returnsNonPremiumAndNullPrice() {
assertThat(
DomainCreateFlow.getRenewalPriceInfo(
true,
Optional.empty(),
new FeesAndCredits.Builder()
.setCurrency(USD)
.addFeeOrCredit(Fee.create(BigDecimal.valueOf(0), FeeType.CREATE, false))
.build()))
assertThat(DomainCreateFlow.getRenewalPriceInfo(true, Optional.empty()))
.isEqualTo(RenewalPriceInfo.create(NONPREMIUM, null));
}
@Test
void testGetRenewalPriceInfo_isAnchorTenantWithDefaultToken_returnsNonPremiumAndNullPrice() {
assertThat(
DomainCreateFlow.getRenewalPriceInfo(
true,
Optional.of(allocationToken),
new FeesAndCredits.Builder()
.setCurrency(USD)
.addFeeOrCredit(Fee.create(BigDecimal.valueOf(0), FeeType.CREATE, false))
.build()))
assertThat(DomainCreateFlow.getRenewalPriceInfo(true, Optional.of(allocationToken)))
.isEqualTo(RenewalPriceInfo.create(NONPREMIUM, null));
}
@Test
void testGetRenewalPriceInfo_isNotAnchorTenantWithDefaultToken_returnsDefaultAndNullPrice() {
assertThat(
DomainCreateFlow.getRenewalPriceInfo(
false,
Optional.of(allocationToken),
new FeesAndCredits.Builder()
.setCurrency(USD)
.addFeeOrCredit(Fee.create(BigDecimal.valueOf(100), FeeType.CREATE, false))
.build()))
assertThat(DomainCreateFlow.getRenewalPriceInfo(false, Optional.of(allocationToken)))
.isEqualTo(RenewalPriceInfo.create(DEFAULT, null));
}
@Test
void testGetRenewalPriceInfo_isNotAnchorTenantWithoutToken_returnsDefaultAndNullPrice() {
assertThat(
DomainCreateFlow.getRenewalPriceInfo(
false,
Optional.empty(),
new FeesAndCredits.Builder()
.setCurrency(USD)
.addFeeOrCredit(Fee.create(BigDecimal.valueOf(100), FeeType.CREATE, false))
.build()))
assertThat(DomainCreateFlow.getRenewalPriceInfo(false, Optional.empty()))
.isEqualTo(RenewalPriceInfo.create(DEFAULT, null));
}
@@ -3248,17 +3219,10 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
.setToken("abc123")
.setTokenType(SINGLE_USE)
.setRenewalPriceBehavior(SPECIFIED)
.setRenewalPrice(Money.of(USD, 0))
.setRenewalPrice(Money.of(USD, 5))
.build());
assertThat(
DomainCreateFlow.getRenewalPriceInfo(
false,
Optional.of(token),
new FeesAndCredits.Builder()
.setCurrency(USD)
.addFeeOrCredit(Fee.create(BigDecimal.valueOf(100), FeeType.CREATE, false))
.build()))
.isEqualTo(RenewalPriceInfo.create(SPECIFIED, Money.of(USD, 100)));
assertThat(DomainCreateFlow.getRenewalPriceInfo(false, Optional.of(token)))
.isEqualTo(RenewalPriceInfo.create(SPECIFIED, Money.of(USD, 5)));
}
@Test
@@ -3276,11 +3240,7 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
.setTokenType(SINGLE_USE)
.setRenewalPriceBehavior(SPECIFIED)
.setRenewalPrice(Money.of(USD, 0))
.build())),
new FeesAndCredits.Builder()
.setCurrency(USD)
.addFeeOrCredit(Fee.create(BigDecimal.valueOf(0), FeeType.CREATE, true))
.build()));
.build()))));
assertThat(thrown)
.hasMessageThat()
.isEqualTo("Renewal price behavior cannot be SPECIFIED for anchor tenant");
@@ -3300,11 +3260,7 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
.setToken("abc123")
.setTokenType(SINGLE_USE)
.setRenewalPriceBehavior(RenewalPriceBehavior.valueOf("INVALID"))
.build())),
new FeesAndCredits.Builder()
.setCurrency(USD)
.addFeeOrCredit(Fee.create(BigDecimal.valueOf(0), FeeType.CREATE, true))
.build()));
.build()))));
assertThat(thrown)
.hasMessageThat()
.isEqualTo(
@@ -104,8 +104,10 @@ import google.registry.model.contact.Contact;
import google.registry.model.domain.DesignatedContact;
import google.registry.model.domain.DesignatedContact.Type;
import google.registry.model.domain.Domain;
import google.registry.model.domain.DomainAuthInfo;
import google.registry.model.domain.DomainHistory;
import google.registry.model.domain.secdns.DomainDsData;
import google.registry.model.eppcommon.AuthInfo.PasswordAuth;
import google.registry.model.eppcommon.Trid;
import google.registry.model.host.Host;
import google.registry.model.poll.PendingActionNotificationResponse.DomainPendingActionNotificationResponse;
@@ -264,6 +266,38 @@ class DomainUpdateFlowTest extends ResourceFlowTestCase<DomainUpdateFlow, Domain
doSuccessfulTest("generic_success_response_no_cltrid.xml");
}
@Test
void testSuccess_noExistingAuthData() throws Exception {
setEppInput("domain_update_no_auth_change.xml");
persistReferencedEntities();
persistDomain();
persistResource(
reloadResourceByForeignKey()
.asBuilder()
.setAuthInfo(DomainAuthInfo.create(PasswordAuth.create(null)))
.build());
assertMutatingFlow(true);
runFlowAssertResponse(loadFile("generic_success_response.xml"));
Domain domain = reloadResourceByForeignKey();
// Check that the domain was updated. These values came from the xml.
assertAboutDomains()
.that(domain)
.hasStatusValue(CLIENT_HOLD)
.and()
.hasAuthInfoPwd(null)
.and()
.hasOneHistoryEntryEachOfTypes(DOMAIN_CREATE, DOMAIN_UPDATE)
.and()
.hasLastEppUpdateTime(clock.nowUtc())
.and()
.hasLastEppUpdateRegistrarId("TheRegistrar")
.and()
.hasNoAutorenewEndTime();
assertNoBillingEvents();
assertDomainDnsRequests("example.tld");
assertLastHistoryContainsResource(reloadResourceByForeignKey());
}
@Test
void testSuccess_cachingDisabled() throws Exception {
boolean origIsCachingEnabled = RegistryConfig.isEppResourceCachingEnabled();
@@ -29,6 +29,7 @@ import google.registry.model.eppcommon.AuthInfo;
import google.registry.testing.TruthChainer.And;
import google.registry.tmch.LordnTaskUtils.LordnPhase;
import java.util.Set;
import javax.annotation.Nullable;
import org.joda.time.DateTime;
/** Truth subject for asserting things about {@link Domain} instances. */
@@ -65,7 +66,7 @@ public final class DomainSubject extends AbstractEppResourceSubject<Domain, Doma
return hasValue(lordnPhase, actual.getLordnPhase(), "lordnPhase");
}
public And<DomainSubject> hasAuthInfoPwd(String pw) {
public And<DomainSubject> hasAuthInfoPwd(@Nullable String pw) {
AuthInfo authInfo = actual.getAuthInfo();
return hasValue(pw, authInfo == null ? null : authInfo.getPw().getValue(), "has auth info pw");
}
@@ -0,0 +1,26 @@
<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>example.tld</domain:name>
<domain:add>
<domain:ns>
<domain:hostObj>ns2.example.foo</domain:hostObj>
</domain:ns>
<domain:contact type="tech">mak21</domain:contact>
<domain:status s="clientHold"
lang="en">Payment overdue.</domain:status>
</domain:add>
<domain:rem>
<domain:ns>
<domain:hostObj>ns1.example.foo</domain:hostObj>
</domain:ns>
<domain:contact type="tech">sh8013</domain:contact>
<domain:status s="clientUpdateProhibited"/>
</domain:rem>
</domain:update>
</update>
<clTRID>ABC-12345</clTRID>
</command>
</epp>
@@ -261,11 +261,11 @@ td.section {
</tr>
<tr>
<td class="property_name">generated on</td>
<td class="property_value">2024-08-01 19:19:25</td>
<td class="property_value">2024-08-14 16:47:59</td>
</tr>
<tr>
<td class="property_name">last flyway file</td>
<td id="lastFlywayFile" class="property_value">V176__drop_login_email_address_column_from_registrar_poc.sql</td>
<td id="lastFlywayFile" class="property_value">V178__drop_user_id_history.sql</td>
</tr>
</tbody>
</table>
@@ -280,7 +280,7 @@ td.section {
<text text-anchor="start" x="3795" y="-29.8" font-family="Helvetica,sans-Serif" font-size="14.00">generated by</text>
<text text-anchor="start" x="3878" y="-29.8" font-family="Helvetica,sans-Serif" font-size="14.00">SchemaCrawler 16.21.4</text>
<text text-anchor="start" x="3794" y="-10.8" font-family="Helvetica,sans-Serif" font-size="14.00">generated on</text>
<text text-anchor="start" x="3878" y="-10.8" font-family="Helvetica,sans-Serif" font-size="14.00">2024-08-01 19:19:25</text>
<text text-anchor="start" x="3878" y="-10.8" font-family="Helvetica,sans-Serif" font-size="14.00">2024-08-14 16:47:59</text>
<polygon fill="none" stroke="#888888" points="3791,-4 3791,-44 4027,-44 4027,-4 3791,-4" /> <!-- allocationtoken_a08ccbef -->
<g id="node1" class="node">
<title>
File diff suppressed because it is too large Load Diff
+2
View File
@@ -174,3 +174,5 @@ V173__create_feature_flag_table.sql
V174__user_pkey.sql
V175__user_update_history_id.sql
V176__drop_login_email_address_column_from_registrar_poc.sql
V177__drop_user_id.sql
V178__drop_user_id_history.sql
@@ -0,0 +1,15 @@
-- 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.
ALTER TABLE "User" DROP COLUMN id;
@@ -0,0 +1,15 @@
-- 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.
ALTER TABLE "UserUpdateHistory" DROP COLUMN user_id;
@@ -1316,7 +1316,6 @@ CREATE TABLE public."TmchCrl" (
--
CREATE TABLE public."User" (
id bigint,
email_address text NOT NULL,
registry_lock_password_hash text,
registry_lock_password_salt text,
@@ -1339,7 +1338,6 @@ CREATE TABLE public."UserUpdateHistory" (
history_request_body text,
history_type text NOT NULL,
history_url text NOT NULL,
user_id bigint,
email_address text NOT NULL,
registry_lock_password_hash text,
registry_lock_password_salt text,
@@ -1352,25 +1350,6 @@ CREATE TABLE public."UserUpdateHistory" (
);
--
-- Name: User_id_seq; Type: SEQUENCE; Schema: public; Owner: -
--
CREATE SEQUENCE public."User_id_seq"
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
--
-- Name: User_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: -
--
ALTER SEQUENCE public."User_id_seq" OWNED BY public."User".id;
--
-- Name: project_wide_unique_id_seq; Type: SEQUENCE; Schema: public; Owner: -
--
@@ -1460,13 +1439,6 @@ ALTER TABLE ONLY public."SignedMarkRevocationList" ALTER COLUMN revision_id SET
ALTER TABLE ONLY public."Spec11ThreatMatch" ALTER COLUMN id SET DEFAULT nextval('public."SafeBrowsingThreat_id_seq"'::regclass);
--
-- Name: User id; Type: DEFAULT; Schema: public; Owner: -
--
ALTER TABLE ONLY public."User" ALTER COLUMN id SET DEFAULT nextval('public."User_id_seq"'::regclass);
--
-- Name: AllocationToken AllocationToken_pkey; Type: CONSTRAINT; Schema: public; Owner: -
--
+8 -4
View File
@@ -31,12 +31,16 @@ do
echo "Updating cluster ${parts[0]} in location ${parts[1]}..."
gcloud container clusters get-credentials "${parts[0]}" \
--project "${project}" --location "${parts[1]}"
sed s/GCP_PROJECT/${project}/g "./kubernetes/nomulus-deployment.yaml" | \
sed s/ENVIRONMENT/${environment}/g | \
sed s/GCP_PROJECT/"${project}"/g "./kubernetes/nomulus-deployment.yaml" | \
sed s/ENVIRONMENT/"${environment}"/g | \
kubectl apply -f -
kubectl apply -f "./kubernetes/nomulus-service.yaml"
#kubectl apply -f "./kubernetes/nomulus-gateway.yaml"
# Kills all running pods, new pods created will be pulling the new image.
kubectl delete pods --all
done < <(gcloud container clusters list --project ${project} | grep nomulus)
# The multi-cluster gateway is only deployed to one cluster (the one in the US).
if [[ "${parts[1]}" == us-* ]]
then
kubectl apply -f "./kubernetes/nomulus-gateway.yaml"
fi
done < <(gcloud container clusters list --project "${project}" | grep nomulus)
kubectl config use-context "$current_context"
+15
View File
@@ -28,4 +28,19 @@ spec:
kind: ServiceImport
name: nomulus
port: 80
---
apiVersion: networking.gke.io/v1
kind: HealthCheckPolicy
metadata:
name: nomulus
spec:
default:
config:
type: HTTP
httpHealthCheck:
requestPath: /healthz/
targetRef:
group: net.gke.io
kind: ServiceImport
name: nomulus
+5 -5
View File
@@ -15,8 +15,8 @@ spec:
- port: 700
targetPort: epp
name: epp
#---
#kind: ServiceExport
#apiVersion: net.gke.io/v1
#metadata:
# name: nomulus
---
kind: ServiceExport
apiVersion: net.gke.io/v1
metadata:
name: nomulus
@@ -0,0 +1,5 @@
<!DOCTYPE html>
<html lang="en">
<title>Health Check</title>
<body>Health check successful.</body>
</html>
@@ -77,6 +77,8 @@ import org.joda.time.DateTime;
@SuppressWarnings("FutureReturnValueIgnored")
public class EppClient implements Runnable {
// TODO(b/354014191): Add loadtest handling for other EPP flows
static {
Security.addProvider(new BouncyCastleProvider());
}
@@ -164,8 +166,9 @@ public class EppClient implements Runnable {
@Parameter(
names = {"--tld"},
description = "TLD to create domains on.")
private String tld = "test";
description = "TLD to create domains on.",
required = true)
private String tld = "loadtest";
@Parameter(
names = {"--force_terminate", "-ft"},
@@ -193,14 +196,17 @@ public class EppClient implements Runnable {
ImmutableList.Builder<String> templatesList = ImmutableList.builder();
ImmutableList.Builder<String> inputList = ImmutableList.builder();
templatesList.add(readStringFromFile(LOGIN_FILE));
String randomContactString = generateRandomString(5);
if (domainCreates > 0) {
templatesList.add(
readStringFromFile(CONTACT_CREATE_FILE)
.replace("@@RANDOM_CONTACT@@", randomContactString));
}
for (int i = 0; i < domainCreates; i++) {
String randomString = generateRandomString(5);
templatesList.add(
readStringFromFile(CONTACT_CREATE_FILE)
.replace("@@REPEAT_NUMBER@@", String.valueOf(i))
.replace("@@RANDOM@@", randomString));
templatesList.add(
readStringFromFile(DOMAIN_CREATE_FILE)
.replace("@@RANDOM_CONTACT@@", randomContactString)
.replace("@@REPEAT_NUMBER@@", String.valueOf(i))
.replace("@@RANDOM@@", randomString));
}
@@ -329,9 +335,14 @@ public class EppClient implements Runnable {
List<ChannelFuture> channelFutures = new ArrayList<>();
// Three requests: hello (from the proxy), login and logout plus additional configured EPP
// requests.
int requestPerConnection = 3 + (domainCreates * 2);
// Three requests: hello (from the proxy), login and logout
int requestsPerConnection = 3;
// If testing domain creates, add the number of domain creates per connection plus the one
// contact created for each connection
if (domainCreates > 0) {
requestsPerConnection += domainCreates + 1;
}
for (int i = 0; i < connections; i++) {
bootstrap.attr(CHANNEL_NUMBER, i);
@@ -347,6 +358,11 @@ public class EppClient implements Runnable {
}
LinkedHashSet<Integer> killedConnections = new LinkedHashSet<>();
LinkedHashSet<Integer> incompleteConnections = new LinkedHashSet<>();
List<Long> requestDurations = new ArrayList<>();
ZonedDateTime startTime = null;
ZonedDateTime endTime = null;
int failedRequests = 0;
// Wait for all channels to close.
for (ChannelFuture channelFuture : channelFutures) {
@@ -365,10 +381,47 @@ public class EppClient implements Runnable {
}
}
for (ChannelFuture channelFuture : channelFutures) {
Channel channel = channelFuture.channel();
int channelNumber = channel.attr(CHANNEL_NUMBER).get();
int responsesReceived = channel.attr(RESPONSE_RECEIVED).get().size();
for (int i = 0; i < responsesReceived; i++) {
requestDurations.add(
Duration.between(
channel.attr(REQUEST_SENT).get().get(i),
channel.attr(RESPONSE_RECEIVED).get().get(i))
.toMillis());
}
if (responsesReceived != requestsPerConnection) {
incompleteConnections.add(channelNumber);
failedRequests += (requestsPerConnection - channel.attr(REQUEST_SENT).get().size());
}
if (responsesReceived > 0) {
startTime = updateStartTime(channelFutures, channelNumber, startTime);
endTime = updateEndTime(channelFutures, channelNumber, endTime);
}
}
if (!incompleteConnections.isEmpty()) {
System.out.printf("%d incomplete connections: ", incompleteConnections.size());
for (int channelNumber : incompleteConnections) {
System.out.printf("Channel %d not finished\n", channelNumber);
}
System.out.print("\n");
}
System.out.println();
System.out.println("====== SUMMARY ======");
System.out.printf("Number of connections: %d\n", connections);
System.out.printf("Number of requests per connection: %d\n", requestPerConnection);
System.out.printf("Number of requests per connection: %d\n", requestsPerConnection);
if (startTime != null && endTime != null) {
System.out.printf(
"Average Response QPS: %.2f\n",
(double) requestDurations.size()
* 1000.0
/ Duration.between(startTime, endTime).toMillis());
}
System.out.printf("Number of incomplete connections: %d\n", incompleteConnections.size());
if (!killedConnections.isEmpty()) {
System.out.printf("Force killed connections (%d): ", killedConnections.size());
for (int channelNumber : killedConnections) {
@@ -376,6 +429,7 @@ public class EppClient implements Runnable {
}
System.out.print("\n");
}
System.out.printf("Number of Failed Requests: %d\n", failedRequests);
eventLoopGroup.shutdownGracefully();
@@ -388,4 +442,25 @@ public class EppClient implements Runnable {
throw new RuntimeException(e);
}
}
private ZonedDateTime updateStartTime(
List<ChannelFuture> channelFutures, int channelNumber, ZonedDateTime startTime) {
ZonedDateTime channelStartTime =
channelFutures.get(channelNumber).channel().attr(REQUEST_SENT).get().getFirst();
if (startTime == null || startTime.isAfter(channelStartTime)) {
return channelStartTime;
}
return startTime;
}
private ZonedDateTime updateEndTime(
List<ChannelFuture> channelFutures, int channelNumber, ZonedDateTime endTime) {
ZonedDateTime channelEndTime =
channelFutures.get(channelNumber).channel().attr(RESPONSE_RECEIVED).get().getLast();
if (endTime == null || endTime.isBefore(channelEndTime)) {
return channelEndTime;
}
return endTime;
}
}
@@ -4,7 +4,7 @@
<create>
<contact:create
xmlns:contact="urn:ietf:params:xml:ns:contact-1.0">
<contact:id>@@RANDOM@@-@@CHANNEL_NUMBER@@-@@REPEAT_NUMBER@@</contact:id>
<contact:id>@@RANDOM_CONTACT@@-@@CHANNEL_NUMBER@@</contact:id>
<contact:postalInfo type="int">
<contact:name>John Doe</contact:name>
<contact:org>Example, Inc.</contact:org>
@@ -9,9 +9,9 @@
<domain:ns>
<domain:hostObj>ns1.domain.com</domain:hostObj>
</domain:ns>
<domain:registrant>@@RANDOM@@-@@CHANNEL_NUMBER@@-@@REPEAT_NUMBER@@</domain:registrant>
<domain:contact type="admin">@@RANDOM@@-@@CHANNEL_NUMBER@@-@@REPEAT_NUMBER@@</domain:contact>
<domain:contact type="tech">@@RANDOM@@-@@CHANNEL_NUMBER@@-@@REPEAT_NUMBER@@</domain:contact>
<domain:registrant>@@RANDOM_CONTACT@@-@@CHANNEL_NUMBER@@</domain:registrant>
<domain:contact type="admin">@@RANDOM_CONTACT@@-@@CHANNEL_NUMBER@@</domain:contact>
<domain:contact type="tech">@@RANDOM_CONTACT@@-@@CHANNEL_NUMBER@@</domain:contact>
<domain:authInfo>
<domain:pw>somepassword</domain:pw>
</domain:authInfo>