mirror of
https://github.com/google/nomulus.git
synced 2025-07-27 04:58:37 +02:00
Add CurlCommand option to connect to canary (#2060)
Add a --canary option (default to false) to the CurlCommand that allows connection to the canary endpoints. During canary analysis, only the DEFAULT-canary receives traffic. This new flag allows use to test other canary services manually using the curl command.
This commit is contained in:
parent
1b6b800345
commit
68e738d88c
4 changed files with 93 additions and 14 deletions
|
@ -75,6 +75,11 @@ class CurlCommand implements CommandWithConnection {
|
||||||
required = true)
|
required = true)
|
||||||
private Service service;
|
private Service service;
|
||||||
|
|
||||||
|
@Parameter(
|
||||||
|
names = {"--canary"},
|
||||||
|
description = "If set, use the canary end-point; otherwise use the regular end-point.")
|
||||||
|
private Boolean canary = Boolean.FALSE;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setConnection(ServiceConnection connection) {
|
public void setConnection(ServiceConnection connection) {
|
||||||
this.connection = connection;
|
this.connection = connection;
|
||||||
|
@ -90,7 +95,7 @@ class CurlCommand implements CommandWithConnection {
|
||||||
throw new IllegalArgumentException("You may not specify a body for a get method.");
|
throw new IllegalArgumentException("You may not specify a body for a get method.");
|
||||||
}
|
}
|
||||||
|
|
||||||
ServiceConnection connectionToService = connection.withService(service);
|
ServiceConnection connectionToService = connection.withService(service, canary);
|
||||||
String response =
|
String response =
|
||||||
(method == Method.GET)
|
(method == Method.GET)
|
||||||
? connectionToService.sendGetRequest(path, ImmutableMap.<String, String>of())
|
? connectionToService.sendGetRequest(path, ImmutableMap.<String, String>of())
|
||||||
|
|
|
@ -15,6 +15,8 @@
|
||||||
package google.registry.tools;
|
package google.registry.tools;
|
||||||
|
|
||||||
import static com.google.common.base.Preconditions.checkNotNull;
|
import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
|
import static com.google.common.base.Strings.isNullOrEmpty;
|
||||||
|
import static com.google.common.base.Verify.verify;
|
||||||
import static com.google.common.net.HttpHeaders.X_REQUESTED_WITH;
|
import static com.google.common.net.HttpHeaders.X_REQUESTED_WITH;
|
||||||
import static com.google.common.net.MediaType.JSON_UTF_8;
|
import static com.google.common.net.MediaType.JSON_UTF_8;
|
||||||
import static google.registry.security.JsonHttp.JSON_SAFETY_PREFIX;
|
import static google.registry.security.JsonHttp.JSON_SAFETY_PREFIX;
|
||||||
|
@ -26,6 +28,7 @@ import com.google.api.client.http.HttpHeaders;
|
||||||
import com.google.api.client.http.HttpRequest;
|
import com.google.api.client.http.HttpRequest;
|
||||||
import com.google.api.client.http.HttpRequestFactory;
|
import com.google.api.client.http.HttpRequestFactory;
|
||||||
import com.google.api.client.http.HttpResponse;
|
import com.google.api.client.http.HttpResponse;
|
||||||
|
import com.google.common.annotations.VisibleForTesting;
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
import com.google.common.collect.ImmutableMap;
|
import com.google.common.collect.ImmutableMap;
|
||||||
import com.google.common.io.CharStreams;
|
import com.google.common.io.CharStreams;
|
||||||
|
@ -36,6 +39,7 @@ import google.registry.config.RegistryConfig;
|
||||||
import google.registry.request.Action.Service;
|
import google.registry.request.Action.Service;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStreamReader;
|
import java.io.InputStreamReader;
|
||||||
|
import java.net.MalformedURLException;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
@ -55,20 +59,23 @@ public class ServiceConnection {
|
||||||
|
|
||||||
@Inject HttpRequestFactory requestFactory;
|
@Inject HttpRequestFactory requestFactory;
|
||||||
private final Service service;
|
private final Service service;
|
||||||
|
private final boolean useCanary;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
ServiceConnection() {
|
ServiceConnection() {
|
||||||
service = Service.TOOLS;
|
service = Service.TOOLS;
|
||||||
|
useCanary = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private ServiceConnection(Service service, HttpRequestFactory requestFactory) {
|
private ServiceConnection(Service service, HttpRequestFactory requestFactory, boolean useCanary) {
|
||||||
this.service = service;
|
this.service = service;
|
||||||
this.requestFactory = requestFactory;
|
this.requestFactory = requestFactory;
|
||||||
|
this.useCanary = useCanary;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns a copy of this connection that talks to a different service. */
|
/** Returns a copy of this connection that talks to a different service endpoint. */
|
||||||
public ServiceConnection withService(Service service) {
|
public ServiceConnection withService(Service service, boolean isCanary) {
|
||||||
return new ServiceConnection(service, requestFactory);
|
return new ServiceConnection(service, requestFactory, isCanary);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns the contents of the title tag in the given HTML, or null if not found. */
|
/** Returns the contents of the title tag in the given HTML, or null if not found. */
|
||||||
|
@ -85,7 +92,7 @@ public class ServiceConnection {
|
||||||
private String internalSend(
|
private String internalSend(
|
||||||
String endpoint, Map<String, ?> params, MediaType contentType, @Nullable byte[] payload)
|
String endpoint, Map<String, ?> params, MediaType contentType, @Nullable byte[] payload)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
GenericUrl url = new GenericUrl(String.format("%s%s", getServer(service), endpoint));
|
GenericUrl url = new GenericUrl(String.format("%s%s", getServer(), endpoint));
|
||||||
url.putAll(params);
|
url.putAll(params);
|
||||||
HttpRequest request =
|
HttpRequest request =
|
||||||
(payload != null)
|
(payload != null)
|
||||||
|
@ -120,6 +127,20 @@ public class ServiceConnection {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
URL getServer() {
|
||||||
|
URL url = getServer(service);
|
||||||
|
if (useCanary) {
|
||||||
|
verify(!isNullOrEmpty(url.getHost()), "Null host in url");
|
||||||
|
try {
|
||||||
|
return new URL(url.getProtocol(), "nomulus-dot-" + url.getHost(), url.getFile());
|
||||||
|
} catch (MalformedURLException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
|
||||||
public String sendPostRequest(
|
public String sendPostRequest(
|
||||||
String endpoint, Map<String, ?> params, MediaType contentType, byte[] payload)
|
String endpoint, Map<String, ?> params, MediaType contentType, byte[] payload)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
|
|
|
@ -22,6 +22,7 @@ import static google.registry.request.Action.Service.TOOLS;
|
||||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||||
import static org.mockito.ArgumentMatchers.any;
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
|
import static org.mockito.ArgumentMatchers.anyBoolean;
|
||||||
import static org.mockito.ArgumentMatchers.eq;
|
import static org.mockito.ArgumentMatchers.eq;
|
||||||
import static org.mockito.Mockito.verify;
|
import static org.mockito.Mockito.verify;
|
||||||
import static org.mockito.Mockito.verifyNoMoreInteractions;
|
import static org.mockito.Mockito.verifyNoMoreInteractions;
|
||||||
|
@ -29,6 +30,7 @@ import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableMap;
|
import com.google.common.collect.ImmutableMap;
|
||||||
import com.google.common.net.MediaType;
|
import com.google.common.net.MediaType;
|
||||||
|
import google.registry.request.Action.Service;
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.mockito.ArgumentCaptor;
|
import org.mockito.ArgumentCaptor;
|
||||||
|
@ -46,7 +48,7 @@ class CurlCommandTest extends CommandTestCase<CurlCommand> {
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
void beforeEach() {
|
void beforeEach() {
|
||||||
command.setConnection(connection);
|
command.setConnection(connection);
|
||||||
when(connection.withService(any())).thenReturn(connectionForService);
|
when(connection.withService(any(Service.class), anyBoolean())).thenReturn(connectionForService);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Captor ArgumentCaptor<ImmutableMap<String, String>> urlParamCaptor;
|
@Captor ArgumentCaptor<ImmutableMap<String, String>> urlParamCaptor;
|
||||||
|
@ -54,7 +56,7 @@ class CurlCommandTest extends CommandTestCase<CurlCommand> {
|
||||||
@Test
|
@Test
|
||||||
void testGetInvocation() throws Exception {
|
void testGetInvocation() throws Exception {
|
||||||
runCommand("--path=/foo/bar?a=1&b=2", "--service=TOOLS");
|
runCommand("--path=/foo/bar?a=1&b=2", "--service=TOOLS");
|
||||||
verify(connection).withService(TOOLS);
|
verify(connection).withService(eq(TOOLS), eq(false));
|
||||||
verifyNoMoreInteractions(connection);
|
verifyNoMoreInteractions(connection);
|
||||||
verify(connectionForService)
|
verify(connectionForService)
|
||||||
.sendGetRequest(eq("/foo/bar?a=1&b=2"), eq(ImmutableMap.<String, String>of()));
|
.sendGetRequest(eq("/foo/bar?a=1&b=2"), eq(ImmutableMap.<String, String>of()));
|
||||||
|
@ -63,7 +65,7 @@ class CurlCommandTest extends CommandTestCase<CurlCommand> {
|
||||||
@Test
|
@Test
|
||||||
void testExplicitGetInvocation() throws Exception {
|
void testExplicitGetInvocation() throws Exception {
|
||||||
runCommand("--path=/foo/bar?a=1&b=2", "--request=GET", "--service=BACKEND");
|
runCommand("--path=/foo/bar?a=1&b=2", "--request=GET", "--service=BACKEND");
|
||||||
verify(connection).withService(BACKEND);
|
verify(connection).withService(eq(BACKEND), eq(false));
|
||||||
verifyNoMoreInteractions(connection);
|
verifyNoMoreInteractions(connection);
|
||||||
verify(connectionForService)
|
verify(connectionForService)
|
||||||
.sendGetRequest(eq("/foo/bar?a=1&b=2"), eq(ImmutableMap.<String, String>of()));
|
.sendGetRequest(eq("/foo/bar?a=1&b=2"), eq(ImmutableMap.<String, String>of()));
|
||||||
|
@ -72,7 +74,7 @@ class CurlCommandTest extends CommandTestCase<CurlCommand> {
|
||||||
@Test
|
@Test
|
||||||
void testPostInvocation() throws Exception {
|
void testPostInvocation() throws Exception {
|
||||||
runCommand("--path=/foo/bar?a=1&b=2", "--data=some data", "--service=DEFAULT");
|
runCommand("--path=/foo/bar?a=1&b=2", "--data=some data", "--service=DEFAULT");
|
||||||
verify(connection).withService(DEFAULT);
|
verify(connection).withService(eq(DEFAULT), eq(false));
|
||||||
verifyNoMoreInteractions(connection);
|
verifyNoMoreInteractions(connection);
|
||||||
verify(connectionForService)
|
verify(connectionForService)
|
||||||
.sendPostRequest(
|
.sendPostRequest(
|
||||||
|
@ -89,7 +91,7 @@ class CurlCommandTest extends CommandTestCase<CurlCommand> {
|
||||||
"--data=some data",
|
"--data=some data",
|
||||||
"--service=DEFAULT",
|
"--service=DEFAULT",
|
||||||
"--content-type=application/json");
|
"--content-type=application/json");
|
||||||
verify(connection).withService(DEFAULT);
|
verify(connection).withService(eq(DEFAULT), eq(false));
|
||||||
verifyNoMoreInteractions(connection);
|
verifyNoMoreInteractions(connection);
|
||||||
verify(connectionForService)
|
verify(connectionForService)
|
||||||
.sendPostRequest(
|
.sendPostRequest(
|
||||||
|
@ -118,7 +120,7 @@ class CurlCommandTest extends CommandTestCase<CurlCommand> {
|
||||||
void testMultiDataPost() throws Exception {
|
void testMultiDataPost() throws Exception {
|
||||||
runCommand(
|
runCommand(
|
||||||
"--path=/foo/bar?a=1&b=2", "--data=first=100", "-d", "second=200", "--service=PUBAPI");
|
"--path=/foo/bar?a=1&b=2", "--data=first=100", "-d", "second=200", "--service=PUBAPI");
|
||||||
verify(connection).withService(PUBAPI);
|
verify(connection).withService(eq(PUBAPI), eq(false));
|
||||||
verifyNoMoreInteractions(connection);
|
verifyNoMoreInteractions(connection);
|
||||||
verify(connectionForService)
|
verify(connectionForService)
|
||||||
.sendPostRequest(
|
.sendPostRequest(
|
||||||
|
@ -132,7 +134,7 @@ class CurlCommandTest extends CommandTestCase<CurlCommand> {
|
||||||
void testDataDoesntSplit() throws Exception {
|
void testDataDoesntSplit() throws Exception {
|
||||||
runCommand(
|
runCommand(
|
||||||
"--path=/foo/bar?a=1&b=2", "--data=one,two", "--service=PUBAPI");
|
"--path=/foo/bar?a=1&b=2", "--data=one,two", "--service=PUBAPI");
|
||||||
verify(connection).withService(PUBAPI);
|
verify(connection).withService(eq(PUBAPI), eq(false));
|
||||||
verifyNoMoreInteractions(connection);
|
verifyNoMoreInteractions(connection);
|
||||||
verify(connectionForService)
|
verify(connectionForService)
|
||||||
.sendPostRequest(
|
.sendPostRequest(
|
||||||
|
@ -145,7 +147,20 @@ class CurlCommandTest extends CommandTestCase<CurlCommand> {
|
||||||
@Test
|
@Test
|
||||||
void testExplicitPostInvocation() throws Exception {
|
void testExplicitPostInvocation() throws Exception {
|
||||||
runCommand("--path=/foo/bar?a=1&b=2", "--request=POST", "--service=TOOLS");
|
runCommand("--path=/foo/bar?a=1&b=2", "--request=POST", "--service=TOOLS");
|
||||||
verify(connection).withService(TOOLS);
|
verify(connection).withService(eq(TOOLS), eq(false));
|
||||||
|
verifyNoMoreInteractions(connection);
|
||||||
|
verify(connectionForService)
|
||||||
|
.sendPostRequest(
|
||||||
|
eq("/foo/bar?a=1&b=2"),
|
||||||
|
eq(ImmutableMap.<String, String>of()),
|
||||||
|
eq(MediaType.PLAIN_TEXT_UTF_8),
|
||||||
|
eq("".getBytes(UTF_8)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testCanaryInvocation() throws Exception {
|
||||||
|
runCommand("--path=/foo/bar?a=1&b=2", "--request=POST", "--service=TOOLS", "--canary");
|
||||||
|
verify(connection).withService(eq(TOOLS), eq(true));
|
||||||
verifyNoMoreInteractions(connection);
|
verifyNoMoreInteractions(connection);
|
||||||
verify(connectionForService)
|
verify(connectionForService)
|
||||||
.sendPostRequest(
|
.sendPostRequest(
|
||||||
|
|
|
@ -0,0 +1,38 @@
|
||||||
|
// Copyright 2023 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.tools;
|
||||||
|
|
||||||
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
|
import static google.registry.request.Action.Service.DEFAULT;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
/** Unit tests for {@link google.registry.tools.ServiceConnection}. */
|
||||||
|
public class ServiceConnectionTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testServerUrl_notCanary() {
|
||||||
|
ServiceConnection connection = new ServiceConnection().withService(DEFAULT, false);
|
||||||
|
String serverUrl = connection.getServer().toString();
|
||||||
|
assertThat(serverUrl).isEqualTo("https://localhost"); // See default-config.yaml
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testServerUrl_canary() {
|
||||||
|
ServiceConnection connection = new ServiceConnection().withService(DEFAULT, true);
|
||||||
|
String serverUrl = connection.getServer().toString();
|
||||||
|
assertThat(serverUrl).isEqualTo("https://nomulus-dot-localhost");
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue