diff options
author | Morten Tokle <mortent@verizonmedia.com> | 2021-05-19 22:38:55 +0200 |
---|---|---|
committer | Morten Tokle <mortent@verizonmedia.com> | 2021-05-20 09:49:02 +0200 |
commit | 0a8b5307acfdd5063412b41a1d7aa4297223ce4e (patch) | |
tree | f1ea43aaff290c452846fb145fd0ee81b62d5ee9 /configserver | |
parent | fc0711f7870b55ea77d18d87ec3e70b75e0de2e0 (diff) |
Support http multipart on prepareandactivate
Diffstat (limited to 'configserver')
6 files changed, 232 insertions, 14 deletions
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/http/SessionHandler.java b/configserver/src/main/java/com/yahoo/vespa/config/server/http/SessionHandler.java index fcac023eec3..540a6545383 100644 --- a/configserver/src/main/java/com/yahoo/vespa/config/server/http/SessionHandler.java +++ b/configserver/src/main/java/com/yahoo/vespa/config/server/http/SessionHandler.java @@ -61,7 +61,11 @@ public class SessionHandler extends HttpHandler { } public static TimeoutBudget getTimeoutBudget(HttpRequest request, Duration defaultTimeout) { - return new TimeoutBudget(Clock.systemUTC(), getRequestTimeout(request, defaultTimeout)); + return getTimeoutBudget(getRequestTimeout(request, defaultTimeout)); + } + + public static TimeoutBudget getTimeoutBudget(Duration requestTimeout) { + return new TimeoutBudget(Clock.systemUTC(), requestTimeout); } /** diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/http/v2/ApplicationApiHandler.java b/configserver/src/main/java/com/yahoo/vespa/config/server/http/v2/ApplicationApiHandler.java index 9ea96b97af3..91c926d8a5c 100644 --- a/configserver/src/main/java/com/yahoo/vespa/config/server/http/v2/ApplicationApiHandler.java +++ b/configserver/src/main/java/com/yahoo/vespa/config/server/http/v2/ApplicationApiHandler.java @@ -8,14 +8,25 @@ import com.yahoo.config.provision.Zone; import com.yahoo.container.jdisc.HttpRequest; import com.yahoo.container.jdisc.HttpResponse; import com.yahoo.jdisc.application.BindingMatch; +import com.yahoo.jdisc.http.HttpHeaders; import com.yahoo.vespa.config.server.ApplicationRepository; import com.yahoo.vespa.config.server.application.CompressedApplicationInputStream; +import com.yahoo.vespa.config.server.http.BadRequestException; import com.yahoo.vespa.config.server.http.SessionHandler; import com.yahoo.vespa.config.server.http.Utils; import com.yahoo.vespa.config.server.session.PrepareParams; import com.yahoo.vespa.config.server.tenant.TenantRepository; +import org.eclipse.jetty.http.MultiPartFormInputStream; +import javax.servlet.http.Part; +import java.io.IOException; +import java.nio.charset.StandardCharsets; import java.time.Duration; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.logging.Level; +import java.util.stream.Collectors; import static com.yahoo.vespa.config.server.application.CompressedApplicationInputStream.createFromCompressedStream; import static com.yahoo.vespa.config.server.http.Utils.checkThatTenantExists; @@ -32,6 +43,9 @@ public class ApplicationApiHandler extends SessionHandler { public final static String APPLICATION_X_GZIP = "application/x-gzip"; public final static String APPLICATION_ZIP = "application/zip"; + public final static String MULTIPART_FORM_DATA = "multipart/form-data"; + public final static String MULTIPART_PARAMS = "prepareParams"; + public final static String MULTIPART_APPLICATION_PACKAGE = "applicationPackage"; public final static String contentTypeHeader = "Content-Type"; private final TenantRepository tenantRepository; private final Duration zookeeperBarrierTimeout; @@ -50,10 +64,34 @@ public class ApplicationApiHandler extends SessionHandler { @Override protected HttpResponse handlePOST(HttpRequest request) { - validateDataAndHeader(request); + validateDataAndHeader(request, List.of(APPLICATION_X_GZIP, APPLICATION_ZIP, MULTIPART_FORM_DATA)); TenantName tenantName = validateTenant(request); - PrepareParams prepareParams = PrepareParams.fromHttpRequest(request, tenantName, zookeeperBarrierTimeout); - CompressedApplicationInputStream compressedStream = createFromCompressedStream(request.getData(), request.getHeader(contentTypeHeader)); + + PrepareParams prepareParams; + CompressedApplicationInputStream compressedStream; + boolean multipartRequest = Optional.ofNullable(request.getHeader(HttpHeaders.Names.CONTENT_TYPE)) + .map(val -> val.equalsIgnoreCase(MULTIPART_FORM_DATA)) + .orElse(false); + if(multipartRequest) { + try { + MultiPartFormInputStream multiPartFormInputStream = new MultiPartFormInputStream(request.getData(), request.getHeader(CONTENT_TYPE), /* config */null, /* contextTmpDir */null); + Map<String, Part> parts = multiPartFormInputStream.getParts().stream() + .collect(Collectors.toMap(Part::getName, p -> p)); + + byte[] params = parts.get(MULTIPART_PARAMS).getInputStream().readAllBytes(); + log.log(Level.FINE, "Deploy parameters: [{}]", new String(params, StandardCharsets.UTF_8)); + prepareParams = PrepareParams.fromJson(parts.get(MULTIPART_PARAMS).getInputStream().readAllBytes(), tenantName, zookeeperBarrierTimeout); + Part appPackagePart = parts.get(MULTIPART_APPLICATION_PACKAGE); + compressedStream = createFromCompressedStream(appPackagePart.getInputStream(), appPackagePart.getContentType()); + } catch (IOException e) { + log.log(Level.WARNING, "Unable to parse multipart in deploy", e); + throw new BadRequestException("Request contains invalid data"); + } + } else { + prepareParams = PrepareParams.fromHttpRequest(request, tenantName, zookeeperBarrierTimeout); + compressedStream = createFromCompressedStream(request.getData(), request.getHeader(contentTypeHeader)); + } + PrepareResult result = applicationRepository.deploy(compressedStream, prepareParams); return new SessionPrepareAndActivateResponse(result, request, prepareParams.getApplicationId(), zone); } diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/http/v2/SessionCreateHandler.java b/configserver/src/main/java/com/yahoo/vespa/config/server/http/v2/SessionCreateHandler.java index 732bad80e00..a3e82c51dfa 100644 --- a/configserver/src/main/java/com/yahoo/vespa/config/server/http/v2/SessionCreateHandler.java +++ b/configserver/src/main/java/com/yahoo/vespa/config/server/http/v2/SessionCreateHandler.java @@ -19,6 +19,8 @@ import com.yahoo.vespa.config.server.http.Utils; import java.net.URI; import java.time.Duration; +import java.util.List; +import java.util.stream.Collectors; /** * A handler that is able to create a session from an application package, @@ -55,7 +57,7 @@ public class SessionCreateHandler extends SessionHandler { logger = DeployHandlerLogger.forApplication(applicationId, verbose); sessionId = applicationRepository.createSessionFromExisting(applicationId, false, timeoutBudget); } else { - validateDataAndHeader(request); + validateDataAndHeader(request, List.of(ApplicationApiHandler.APPLICATION_ZIP, ApplicationApiHandler.APPLICATION_X_GZIP)); logger = DeployHandlerLogger.forTenant(tenantName, verbose); // TODO: Avoid using application id here at all ApplicationId applicationId = ApplicationId.from(tenantName, ApplicationName.defaultName(), InstanceName.defaultName()); @@ -84,16 +86,16 @@ public class SessionCreateHandler extends SessionHandler { .instanceName(match.group(6)).build(); } - static void validateDataAndHeader(HttpRequest request) { + static void validateDataAndHeader(HttpRequest request, List<String> supportedContentTypes) { if (request.getData() == null) { throw new BadRequestException("Request contains no data"); } String header = request.getHeader(ApplicationApiHandler.contentTypeHeader); if (header == null) { throw new BadRequestException("Request contains no " + ApplicationApiHandler.contentTypeHeader + " header"); - } else if (!(header.equals(ApplicationApiHandler.APPLICATION_X_GZIP) || header.equals(ApplicationApiHandler.APPLICATION_ZIP))) { - throw new BadRequestException("Request contains invalid " + ApplicationApiHandler.contentTypeHeader + " header, only '" + - ApplicationApiHandler.APPLICATION_X_GZIP + "' and '" + ApplicationApiHandler.APPLICATION_ZIP + "' are supported"); + } else if (!supportedContentTypes.contains(header)) { + throw new BadRequestException("Request contains invalid " + ApplicationApiHandler.contentTypeHeader + " header, only '[" + + String.join(", ", supportedContentTypes) + "' are supported"); } } } diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/session/PrepareParams.java b/configserver/src/main/java/com/yahoo/vespa/config/server/session/PrepareParams.java index 5a3e0311db9..c9f4b8164eb 100644 --- a/configserver/src/main/java/com/yahoo/vespa/config/server/session/PrepareParams.java +++ b/configserver/src/main/java/com/yahoo/vespa/config/server/session/PrepareParams.java @@ -11,6 +11,8 @@ import com.yahoo.config.provision.AthenzDomain; import com.yahoo.config.provision.DockerImage; import com.yahoo.config.provision.TenantName; import com.yahoo.container.jdisc.HttpRequest; +import com.yahoo.slime.Inspector; +import com.yahoo.slime.Slime; import com.yahoo.slime.SlimeUtils; import com.yahoo.vespa.config.server.TimeoutBudget; import com.yahoo.config.model.api.TenantSecretStore; @@ -21,9 +23,11 @@ import com.yahoo.vespa.config.server.tenant.TenantSecretStoreSerializer; import java.time.Clock; import java.time.Duration; +import java.util.Collections; import java.util.List; import java.util.Objects; import java.util.Optional; +import java.util.function.Function; /** * Parameters for preparing an application. Immutable. @@ -163,6 +167,16 @@ public final class PrepareParams { return this; } + public Builder containerEndpointList(List<ContainerEndpoint> endpoints) { + this.containerEndpoints = endpoints; + return this; + } + + public Builder endpointCertificateMetadata(EndpointCertificateMetadata endpointCertificateMetadata) { + this.endpointCertificateMetadata = Optional.ofNullable(endpointCertificateMetadata); + return this; + } + public Builder endpointCertificateMetadata(String serialized) { this.endpointCertificateMetadata = (serialized == null) ? Optional.empty() @@ -197,6 +211,11 @@ public final class PrepareParams { return this; } + public Builder quota(Quota quota) { + this.quota = Optional.ofNullable(quota); + return this; + } + public Builder quota(String serialized) { this.quota = (serialized == null) ? Optional.empty() @@ -253,6 +272,53 @@ public final class PrepareParams { .build(); } + public static PrepareParams fromJson(byte[] json, TenantName tenant, Duration barrierTimeout) { + Slime slime = SlimeUtils.jsonToSlime(json); + Inspector params = slime.get(); + + return new Builder().ignoreValidationErrors(params.field(IGNORE_VALIDATION_PARAM_NAME).asBool()) + .dryRun(params.field(DRY_RUN_PARAM_NAME).asBool()) + .verbose(params.field(VERBOSE_PARAM_NAME).asBool()) + .timeoutBudget(SessionHandler.getTimeoutBudget(getTimeout(params, barrierTimeout))) + .applicationId(createApplicationId(params, tenant)) + .vespaVersion(params.field(VESPA_VERSION_PARAM_NAME).asString()) + .containerEndpointList(deserialize(params.field(CONTAINER_ENDPOINTS_PARAM_NAME), ContainerEndpointSerializer::endpointListFromSlime, Collections.emptyList())) + .endpointCertificateMetadata(deserialize(params.field(ENDPOINT_CERTIFICATE_METADATA_PARAM_NAME), EndpointCertificateMetadataSerializer::fromSlime)) + .dockerImageRepository(SlimeUtils.optionalString(params.field(DOCKER_IMAGE_REPOSITORY)).orElse(null)) + .athenzDomain(SlimeUtils.optionalString(params.field(ATHENZ_DOMAIN)).orElse(null)) + .applicationRoles(ApplicationRoles.fromString(SlimeUtils.optionalString(params.field(APPLICATION_HOST_ROLE)).orElse(null), SlimeUtils.optionalString(params.field(APPLICATION_CONTAINER_ROLE)).orElse(null))) + .quota(deserialize(params.field(QUOTA_PARAM_NAME), Quota::fromSlime)) + .tenantSecretStores(SlimeUtils.optionalString(params.field(TENANT_SECRET_STORES_PARAM_NAME)).orElse(null)) + .force(params.field(FORCE_PARAM_NAME).asBool()) + .waitForResourcesInPrepare(params.field(WAIT_FOR_RESOURCES_IN_PREPARE).asBool()) + .build(); + } + + private static <T> T deserialize(Inspector field, Function<Inspector, T> mapper) { + return deserialize(field, mapper, null); + } + private static <T> T deserialize(Inspector field, Function<Inspector, T> mapper, T defaultValue) { + return field.valid() + ? mapper.apply(field) + : defaultValue; + } + + private static Duration getTimeout(Inspector params, Duration defaultTimeout) { + if(params.field("timeout").valid()) { + return Duration.ofSeconds(params.field("timeout").asLong()); + } else { + return defaultTimeout; + } + } + + private static ApplicationId createApplicationId(Inspector params, TenantName tenant) { + return new ApplicationId.Builder() + .tenant(tenant) + .applicationName(SlimeUtils.optionalString(params.field(APPLICATION_NAME_PARAM_NAME)).orElse("default")) + .instanceName(SlimeUtils.optionalString(params.field(INSTANCE_PARAM_NAME)).orElse("default")) + .build(); + } + private static ApplicationId createApplicationId(HttpRequest request, TenantName tenant) { return new ApplicationId.Builder() .tenant(tenant) @@ -268,7 +334,7 @@ public final class PrepareParams { private static Optional<String> getProperty(HttpRequest request, String propertyName) { return Optional.ofNullable(request.getProperty(propertyName)); } - + public String getApplicationName() { return applicationId.application().value(); } diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/tenant/ContainerEndpointSerializer.java b/configserver/src/main/java/com/yahoo/vespa/config/server/tenant/ContainerEndpointSerializer.java index 6ec0ba693f5..15fe04932dd 100644 --- a/configserver/src/main/java/com/yahoo/vespa/config/server/tenant/ContainerEndpointSerializer.java +++ b/configserver/src/main/java/com/yahoo/vespa/config/server/tenant/ContainerEndpointSerializer.java @@ -54,6 +54,9 @@ public class ContainerEndpointSerializer { public static List<ContainerEndpoint> endpointListFromSlime(Slime slime) { final var inspector = slime.get(); + return endpointListFromSlime(inspector); + } + public static List<ContainerEndpoint> endpointListFromSlime(Inspector inspector) { final var endpoints = new ArrayList<ContainerEndpoint>(); inspector.traverse((ArrayTraverser) (idx, endpointInspector) -> { diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/session/PrepareParamsTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/session/PrepareParamsTest.java index 941f2726b0e..04984f85a64 100644 --- a/configserver/src/test/java/com/yahoo/vespa/config/server/session/PrepareParamsTest.java +++ b/configserver/src/test/java/com/yahoo/vespa/config/server/session/PrepareParamsTest.java @@ -3,16 +3,32 @@ package com.yahoo.vespa.config.server.session; import com.yahoo.config.model.api.ApplicationRoles; import com.yahoo.config.model.api.ContainerEndpoint; +import com.yahoo.config.model.api.EndpointCertificateMetadata; import com.yahoo.config.provision.ApplicationId; import com.yahoo.config.provision.TenantName; import com.yahoo.container.jdisc.HttpRequest; +import com.yahoo.slime.ArrayInserter; +import com.yahoo.slime.Cursor; +import com.yahoo.slime.Injector; +import com.yahoo.slime.Inspector; +import com.yahoo.slime.ObjectInserter; +import com.yahoo.slime.ObjectSymbolInserter; +import com.yahoo.slime.Slime; +import com.yahoo.slime.SlimeInserter; +import com.yahoo.slime.SlimeUtils; +import com.yahoo.vespa.config.server.tenant.ContainerEndpointSerializer; +import com.yahoo.vespa.config.server.tenant.EndpointCertificateMetadataSerializer; import org.junit.Test; +import java.io.IOException; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; import java.time.Duration; import java.util.List; +import java.util.Map; +import java.util.Objects; import java.util.Optional; +import java.util.stream.Collectors; import static org.hamcrest.CoreMatchers.is; import static org.junit.Assert.assertEquals; @@ -26,13 +42,22 @@ import static org.junit.Assert.assertTrue; public class PrepareParamsTest { private static final String vespaVersion = "6.37.49"; - private static final String request = "http://foo:19071/application/v2/tenant/foo/application/bar?" + + private static final String baseRequest = "http://foo:19071/application/v2/tenant/foo/application/bar"; + private static final String request = baseRequest + "?" + PrepareParams.DRY_RUN_PARAM_NAME + "=true&" + PrepareParams.VERBOSE_PARAM_NAME+ "=true&" + PrepareParams.IGNORE_VALIDATION_PARAM_NAME + "=false&" + PrepareParams.APPLICATION_NAME_PARAM_NAME + "=baz&" + PrepareParams.VESPA_VERSION_PARAM_NAME + "=" + vespaVersion; + private static final String json = "{\n" + + "\"" + PrepareParams.DRY_RUN_PARAM_NAME + "\": true,\n" + + "\"" + PrepareParams.VERBOSE_PARAM_NAME+ "\": true,\n" + + "\"" + PrepareParams.IGNORE_VALIDATION_PARAM_NAME + "\": false,\n" + + "\"" + PrepareParams.APPLICATION_NAME_PARAM_NAME + "\":\"baz\",\n" + + "\"" + PrepareParams.VESPA_VERSION_PARAM_NAME + "\":\"" + vespaVersion + "\"\n" + + "}"; + @Test public void testCorrectParsing() { PrepareParams prepareParams = createParams("http://foo:19071/application/v2/", TenantName.defaultName()); @@ -47,7 +72,7 @@ public class PrepareParamsTest { } @Test - public void testCorrectParsingWithContainerEndpoints() { + public void testCorrectParsingWithContainerEndpoints() throws IOException { var endpoints = List.of(new ContainerEndpoint("qrs1", List.of("c1.example.com", "c2.example.com")), @@ -69,10 +94,16 @@ public class PrepareParamsTest { var prepareParams = createParams(request + "&" + PrepareParams.CONTAINER_ENDPOINTS_PARAM_NAME + "=" + encoded, TenantName.from("foo")); assertEquals(endpoints, prepareParams.containerEndpoints()); + + // Verify using json object + var slime = SlimeUtils.jsonToSlime(json); + new Injector().inject(ContainerEndpointSerializer.endpointListToSlime(endpoints).get(), new ObjectInserter(slime.get(), PrepareParams.CONTAINER_ENDPOINTS_PARAM_NAME)); + PrepareParams prepareParamsJson = PrepareParams.fromJson(SlimeUtils.toJsonBytes(slime), TenantName.from("foo"), Duration.ofSeconds(60)); + assertPrepareParamsEqual(prepareParams, prepareParamsJson); } @Test - public void testCorrectParsingWithApplicationRoles() { + public void testCorrectParsingWithApplicationRoles() throws IOException { String req = request + "&" + PrepareParams.APPLICATION_HOST_ROLE + "=hostRole&" + PrepareParams.APPLICATION_CONTAINER_ROLE + "=containerRole"; @@ -82,15 +113,89 @@ public class PrepareParamsTest { assertTrue(applicationRoles.isPresent()); assertEquals("hostRole", applicationRoles.get().applicationHostRole()); assertEquals("containerRole", applicationRoles.get().applicationContainerRole()); + + // Verify using json object + var slime = SlimeUtils.jsonToSlime(json); + var cursor = slime.get(); + cursor.setString(PrepareParams.APPLICATION_HOST_ROLE, "hostRole"); + cursor.setString(PrepareParams.APPLICATION_CONTAINER_ROLE, "containerRole"); + + PrepareParams prepareParamsJson = PrepareParams.fromJson(SlimeUtils.toJsonBytes(slime), TenantName.from("foo"), Duration.ofSeconds(60)); + assertPrepareParamsEqual(prepareParams, prepareParamsJson); } @Test - public void testQuotaParsing() { + public void testQuotaParsing() throws IOException { var quotaParam = "{\"clusterSize\": 23, \"budget\": 23232323}"; var quotaEncoded = URLEncoder.encode(quotaParam, StandardCharsets.UTF_8); var prepareParams = createParams(request + "&" + PrepareParams.QUOTA_PARAM_NAME + "=" + quotaEncoded, TenantName.from("foo")); assertEquals(23, (int) prepareParams.quota().get().maxClusterSize().get()); assertEquals(23232323, (int) prepareParams.quota().get().budget().get()); + + // Verify using json object + var slime = SlimeUtils.jsonToSlime(json); + new Injector().inject(SlimeUtils.jsonToSlime(quotaParam).get(), new ObjectInserter(slime.get(), PrepareParams.QUOTA_PARAM_NAME)); + PrepareParams prepareParamsJson = PrepareParams.fromJson(SlimeUtils.toJsonBytes(slime), TenantName.from("foo"), Duration.ofSeconds(60)); + assertPrepareParamsEqual(prepareParams, prepareParamsJson); + } + + @Test + public void testEndpointCertificateParsing() throws IOException { + var certMeta = new EndpointCertificateMetadata("key", "cert", 3); + var slime = new Slime(); + EndpointCertificateMetadataSerializer.toSlime(certMeta, slime.setObject()); + String encoded = URLEncoder.encode(new String(SlimeUtils.toJsonBytes(slime), StandardCharsets.UTF_8), StandardCharsets.UTF_8); + var prepareParams = createParams(request + "&" + PrepareParams.ENDPOINT_CERTIFICATE_METADATA_PARAM_NAME + "=" + encoded, TenantName.from("foo")); + assertTrue(prepareParams.endpointCertificateMetadata().isPresent()); + assertEquals("key", prepareParams.endpointCertificateMetadata().get().keyName()); + assertEquals("cert", prepareParams.endpointCertificateMetadata().get().certName()); + assertEquals(3, prepareParams.endpointCertificateMetadata().get().version()); + + // Verify using json object + var root = SlimeUtils.jsonToSlime(json); + new Injector().inject(slime.get(), new ObjectInserter(root.get(), PrepareParams.ENDPOINT_CERTIFICATE_METADATA_PARAM_NAME)); + PrepareParams prepareParamsJson = PrepareParams.fromJson(SlimeUtils.toJsonBytes(root), TenantName.from("foo"), Duration.ofSeconds(60)); + assertPrepareParamsEqual(prepareParams, prepareParamsJson); + } + + @Test + public void compareEmptyUrlparamsVsJson() { + TenantName tenantName = TenantName.from("foo"); + Duration barrierTimeout = Duration.ofSeconds(60); + HttpRequest httpRequest = HttpRequest.createTestRequest(baseRequest, com.yahoo.jdisc.http.HttpRequest.Method.POST); + PrepareParams urlPrepareParams = PrepareParams.fromHttpRequest(httpRequest, tenantName, barrierTimeout); + PrepareParams jsonPrepareParams = PrepareParams.fromJson(new byte[0], tenantName, barrierTimeout); + + assertPrepareParamsEqual(urlPrepareParams, jsonPrepareParams); + } + + @Test + public void compareStandardUrlparamsVsJson() { + TenantName tenantName = TenantName.from("foo"); + Duration barrierTimeout = Duration.ofSeconds(60); + HttpRequest httpRequest = HttpRequest.createTestRequest(request, com.yahoo.jdisc.http.HttpRequest.Method.POST); + PrepareParams urlPrepareParams = PrepareParams.fromHttpRequest(httpRequest, tenantName, barrierTimeout); + PrepareParams jsonPrepareParams = PrepareParams.fromJson(json.getBytes(StandardCharsets.UTF_8), tenantName, barrierTimeout); + assertPrepareParamsEqual(urlPrepareParams, jsonPrepareParams); + } + + private void assertPrepareParamsEqual(PrepareParams urlParams, PrepareParams jsonParams) { + assertEquals(urlParams.ignoreValidationErrors(), jsonParams.ignoreValidationErrors()); + assertEquals(urlParams.isDryRun(), jsonParams.isDryRun()); + assertEquals(urlParams.isVerbose(), jsonParams.isVerbose()); + assertEquals(urlParams.isBootstrap(), jsonParams.isBootstrap()); + assertEquals(urlParams.force(), jsonParams.force()); + assertEquals(urlParams.waitForResourcesInPrepare(), jsonParams.waitForResourcesInPrepare()); + assertEquals(urlParams.getApplicationId(), jsonParams.getApplicationId()); + assertEquals(urlParams.getTimeoutBudget().timeout(), jsonParams.getTimeoutBudget().timeout()); + assertEquals(urlParams.vespaVersion(), jsonParams.vespaVersion()); + assertEquals(urlParams.containerEndpoints(), jsonParams.containerEndpoints()); + assertEquals(urlParams.endpointCertificateMetadata(), jsonParams.endpointCertificateMetadata()); + assertEquals(urlParams.dockerImageRepository(), jsonParams.dockerImageRepository()); + assertEquals(urlParams.athenzDomain(), jsonParams.athenzDomain()); + assertEquals(urlParams.applicationRoles(), jsonParams.applicationRoles()); + assertEquals(urlParams.quota(), jsonParams.quota()); + assertEquals(urlParams.tenantSecretStores(), jsonParams.tenantSecretStores()); } // Create PrepareParams from a request (based on uri and tenant name) |