diff options
Diffstat (limited to 'configserver/src/main/java/com/yahoo')
3 files changed, 169 insertions, 67 deletions
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/ApplicationRepository.java b/configserver/src/main/java/com/yahoo/vespa/config/server/ApplicationRepository.java index 02ca4ce14c4..80194337daa 100644 --- a/configserver/src/main/java/com/yahoo/vespa/config/server/ApplicationRepository.java +++ b/configserver/src/main/java/com/yahoo/vespa/config/server/ApplicationRepository.java @@ -111,6 +111,8 @@ import java.util.stream.Collectors; import static com.yahoo.config.model.api.container.ContainerServiceType.CONTAINER; import static com.yahoo.config.model.api.container.ContainerServiceType.LOGSERVER_CONTAINER; +import static com.yahoo.vespa.config.server.application.ConfigConvergenceChecker.ServiceResponse; +import static com.yahoo.vespa.config.server.application.ConfigConvergenceChecker.ServiceListResponse; import static com.yahoo.vespa.config.server.filedistribution.FileDistributionUtil.fileReferenceExistsOnDisk; import static com.yahoo.vespa.config.server.filedistribution.FileDistributionUtil.getFileReferencesOnDisk; import static com.yahoo.vespa.config.server.tenant.TenantRepository.HOSTED_VESPA_TENANT; @@ -737,16 +739,22 @@ public class ApplicationRepository implements com.yahoo.config.provision.Deploye // ---------------- Convergence ---------------------------------------------------------------- - public HttpResponse checkServiceForConfigConvergence(ApplicationId applicationId, String hostAndPort, URI uri, - Duration timeout, Optional<Version> vespaVersion) { - return convergeChecker.getServiceConfigGenerationResponse(getApplication(applicationId, vespaVersion), hostAndPort, uri, timeout); + public ServiceResponse checkServiceForConfigConvergence(ApplicationId applicationId, + String hostAndPort, + Duration timeout, + Optional<Version> vespaVersion) { + return convergeChecker.getServiceConfigGeneration(getApplication(applicationId, vespaVersion), hostAndPort, timeout); } - public HttpResponse servicesToCheckForConfigConvergence(ApplicationId applicationId, URI uri, - Duration timeoutPerService, Optional<Version> vespaVersion) { - return convergeChecker.getServiceConfigGenerationsResponse(getApplication(applicationId, vespaVersion), uri, timeoutPerService); + public ServiceListResponse servicesToCheckForConfigConvergence(ApplicationId applicationId, + URI uri, + Duration timeoutPerService, + Optional<Version> vespaVersion) { + return convergeChecker.getServiceConfigGenerations(getApplication(applicationId, vespaVersion), uri, timeoutPerService); } + public ConfigConvergenceChecker configConvergenceChecker() { return convergeChecker; } + // ---------------- Logs ---------------------------------------------------------------- public HttpResponse getLogs(ApplicationId applicationId, Optional<String> hostname, String apiParams) { diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/application/ConfigConvergenceChecker.java b/configserver/src/main/java/com/yahoo/vespa/config/server/application/ConfigConvergenceChecker.java index 24744a1b3b2..ad14cf4aab6 100644 --- a/configserver/src/main/java/com/yahoo/vespa/config/server/application/ConfigConvergenceChecker.java +++ b/configserver/src/main/java/com/yahoo/vespa/config/server/application/ConfigConvergenceChecker.java @@ -10,8 +10,6 @@ import com.yahoo.concurrent.DaemonThreadFactory; import com.yahoo.config.model.api.HostInfo; import com.yahoo.config.model.api.PortInfo; import com.yahoo.config.model.api.ServiceInfo; -import com.yahoo.slime.Cursor; -import com.yahoo.vespa.config.server.http.JSONResponse; import org.apache.hc.client5.http.async.methods.SimpleHttpRequest; import org.apache.hc.client5.http.async.methods.SimpleHttpResponse; import org.apache.hc.client5.http.async.methods.SimpleRequestBuilder; @@ -92,27 +90,26 @@ public class ConfigConvergenceChecker extends AbstractComponent { } /** Check all services in given application. Returns the minimum current generation of all services */ - public JSONResponse getServiceConfigGenerationsResponse(Application application, URI requestUrl, Duration timeoutPerService) { + public ServiceListResponse getServiceConfigGenerations(Application application, URI uri, Duration timeoutPerService) { Map<ServiceInfo, Long> currentGenerations = getServiceConfigGenerations(application, timeoutPerService); long currentGeneration = currentGenerations.values().stream().mapToLong(Long::longValue).min().orElse(-1); - return new ServiceListResponse(200, currentGenerations, requestUrl, application.getApplicationGeneration(), - currentGeneration); + return new ServiceListResponse(currentGenerations, uri, application.getApplicationGeneration(), currentGeneration); } /** Check service identified by host and port in given application */ - public JSONResponse getServiceConfigGenerationResponse(Application application, String hostAndPortToCheck, URI requestUrl, Duration timeout) { + public ServiceResponse getServiceConfigGeneration(Application application, String hostAndPortToCheck, Duration timeout) { Long wantedGeneration = application.getApplicationGeneration(); try (CloseableHttpAsyncClient client = createHttpClient()) { client.start(); if ( ! hostInApplication(application, hostAndPortToCheck)) - return ServiceResponse.createHostNotFoundInAppResponse(requestUrl, hostAndPortToCheck, wantedGeneration); + return new ServiceResponse(ServiceResponse.Status.hostNotFound, wantedGeneration); long currentGeneration = getServiceGeneration(client, URI.create("http://" + hostAndPortToCheck), timeout).get(); boolean converged = currentGeneration >= wantedGeneration; - return ServiceResponse.createOkResponse(requestUrl, hostAndPortToCheck, wantedGeneration, currentGeneration, converged); + return new ServiceResponse(ServiceResponse.Status.ok, wantedGeneration, currentGeneration, converged); } catch (InterruptedException | ExecutionException | CancellationException e) { // e.g. if we cannot connect to the service to find generation - return ServiceResponse.createNotFoundResponse(requestUrl, hostAndPortToCheck, wantedGeneration, e.getMessage()); + return new ServiceResponse(ServiceResponse.Status.notFound, wantedGeneration, e.getMessage()); } catch (Exception e) { - return ServiceResponse.createErrorResponse(requestUrl, hostAndPortToCheck, wantedGeneration, e.getMessage()); + return new ServiceResponse(ServiceResponse.Status.error, wantedGeneration, e.getMessage()); } } @@ -192,7 +189,7 @@ public class ConfigConvergenceChecker extends AbstractComponent { return false; } - private static Optional<Integer> getStatePort(ServiceInfo service) { + public static Optional<Integer> getStatePort(ServiceInfo service) { return service.getPorts().stream() .filter(port -> port.getTags().contains("state")) .map(PortInfo::getPort) @@ -249,63 +246,70 @@ public class ConfigConvergenceChecker extends AbstractComponent { .build(); } - private static class ServiceListResponse extends JSONResponse { - - // Pre-condition: servicesToCheck has a state port - private ServiceListResponse(int status, Map<ServiceInfo, Long> servicesToCheck, URI uri, long wantedGeneration, - long currentGeneration) { - super(status); - Cursor serviceArray = object.setArray("services"); - servicesToCheck.forEach((service, generation) -> { - Cursor serviceObject = serviceArray.addObject(); - String hostName = service.getHostName(); - int statePort = getStatePort(service).get(); - serviceObject.setString("host", hostName); - serviceObject.setLong("port", statePort); - serviceObject.setString("type", service.getServiceType()); - serviceObject.setString("url", uri.toString() + "/" + hostName + ":" + statePort); - serviceObject.setLong("currentGeneration", generation); - }); - object.setString("url", uri.toString()); - object.setLong("currentGeneration", currentGeneration); - object.setLong("wantedGeneration", wantedGeneration); - object.setBool("converged", currentGeneration >= wantedGeneration); + public static class ServiceResponse { + + public enum Status { ok, notFound, hostNotFound, error } + + public final Status status; + public final Long wantedGeneration; + public final Long currentGeneration; + public final boolean converged; + public final Optional<String> errorMessage; + + public ServiceResponse(Status status, Long wantedGeneration) { + this(status, wantedGeneration, 0L); } - } - private static class ServiceResponse extends JSONResponse { + public ServiceResponse(Status status, Long wantedGeneration, Long currentGeneration) { + this(status, wantedGeneration, currentGeneration, false); + } - private ServiceResponse(int status, URI uri, String hostname, Long wantedGeneration) { - super(status); - object.setString("url", uri.toString()); - object.setString("host", hostname); - object.setLong("wantedGeneration", wantedGeneration); + public ServiceResponse(Status status, Long wantedGeneration, Long currentGeneration, boolean converged) { + this(status, wantedGeneration, currentGeneration, converged, Optional.empty()); } - static ServiceResponse createOkResponse(URI uri, String hostname, Long wantedGeneration, Long currentGeneration, boolean converged) { - ServiceResponse serviceResponse = new ServiceResponse(200, uri, hostname, wantedGeneration); - serviceResponse.object.setBool("converged", converged); - serviceResponse.object.setLong("currentGeneration", currentGeneration); - return serviceResponse; + public ServiceResponse(Status status, Long wantedGeneration, String errorMessage) { + this(status, wantedGeneration, 0L, false, Optional.ofNullable(errorMessage)); } - static ServiceResponse createHostNotFoundInAppResponse(URI uri, String hostname, Long wantedGeneration) { - ServiceResponse serviceResponse = new ServiceResponse(410, uri, hostname, wantedGeneration); - serviceResponse.object.setString("problem", "Host:port (service) no longer part of application, refetch list of services."); - return serviceResponse; + private ServiceResponse(Status status, Long wantedGeneration, Long currentGeneration, boolean converged, Optional<String> errorMessage) { + this.status = status; + this.wantedGeneration = wantedGeneration; + this.currentGeneration = currentGeneration; + this.converged = converged; + this.errorMessage = errorMessage; } - static ServiceResponse createErrorResponse(URI uri, String hostname, Long wantedGeneration, String error) { - ServiceResponse serviceResponse = new ServiceResponse(500, uri, hostname, wantedGeneration); - serviceResponse.object.setString("error", error); - return serviceResponse; + } + + public static class ServiceListResponse { + + public final List<Service> services = new ArrayList<>(); + public final URI uri; + public final long wantedGeneration; + public final long currentGeneration; + + public ServiceListResponse(Map<ServiceInfo, Long> services, URI uri, long wantedGeneration, long currentGeneration) { + services.forEach((key, value) -> this.services.add(new Service(key, value))); + this.uri = uri; + this.wantedGeneration = wantedGeneration; + this.currentGeneration = currentGeneration; } - static ServiceResponse createNotFoundResponse(URI uri, String hostname, Long wantedGeneration, String error) { - ServiceResponse serviceResponse = new ServiceResponse(404, uri, hostname, wantedGeneration); - serviceResponse.object.setString("error", error); - return serviceResponse; + public List<Service> services() { return services; } + + public static class Service { + + public final ServiceInfo serviceInfo; + public final Long currentGeneration; + + public Service(ServiceInfo serviceInfo, Long currentGeneration) { + this.serviceInfo = serviceInfo; + this.currentGeneration = currentGeneration; + } + } + } } diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/http/v2/ApplicationHandler.java b/configserver/src/main/java/com/yahoo/vespa/config/server/http/v2/ApplicationHandler.java index 4dda141491c..0131517818d 100644 --- a/configserver/src/main/java/com/yahoo/vespa/config/server/http/v2/ApplicationHandler.java +++ b/configserver/src/main/java/com/yahoo/vespa/config/server/http/v2/ApplicationHandler.java @@ -5,6 +5,7 @@ import com.google.inject.Inject; import com.yahoo.component.Version; import com.yahoo.config.application.api.ApplicationFile; import com.yahoo.config.model.api.Model; +import com.yahoo.config.model.api.ServiceInfo; import com.yahoo.config.provision.ApplicationId; import com.yahoo.config.provision.HostFilter; import com.yahoo.config.provision.InstanceName; @@ -16,13 +17,15 @@ import com.yahoo.jdisc.Response; import com.yahoo.restapi.ErrorResponse; import com.yahoo.restapi.MessageResponse; import com.yahoo.restapi.Path; +import com.yahoo.slime.Cursor; import com.yahoo.slime.SlimeUtils; import com.yahoo.text.StringUtilities; import com.yahoo.vespa.config.server.ApplicationRepository; -import com.yahoo.vespa.config.server.application.ApplicationReindexing; +import com.yahoo.vespa.config.server.application.ConfigConvergenceChecker; import com.yahoo.vespa.config.server.http.ContentHandler; import com.yahoo.vespa.config.server.http.ContentRequest; import com.yahoo.vespa.config.server.http.HttpHandler; +import com.yahoo.vespa.config.server.http.JSONResponse; import com.yahoo.vespa.config.server.http.NotFoundException; import com.yahoo.vespa.config.server.http.v2.request.ApplicationContentRequest; import com.yahoo.vespa.config.server.http.v2.response.ApplicationSuspendedResponse; @@ -33,6 +36,7 @@ import com.yahoo.vespa.config.server.http.v2.response.ReindexingResponse; import com.yahoo.vespa.config.server.tenant.Tenant; import java.io.IOException; +import java.net.URI; import java.time.Duration; import java.time.Instant; import java.util.Map; @@ -43,8 +47,11 @@ import java.util.StringJoiner; import java.util.TreeMap; import java.util.TreeSet; +import static com.yahoo.vespa.config.server.application.ConfigConvergenceChecker.ServiceListResponse; +import static com.yahoo.vespa.config.server.application.ConfigConvergenceChecker.ServiceResponse; import static com.yahoo.yolean.Exceptions.uncheck; + /** * Operations on applications (delete, wait for config convergence, restart, application content etc.) * @@ -108,13 +115,21 @@ public class ApplicationHandler extends HttpHandler { } private HttpResponse listServiceConverge(ApplicationId applicationId, HttpRequest request) { - return applicationRepository.servicesToCheckForConfigConvergence(applicationId, request.getUri(), - getTimeoutFromRequest(request), getVespaVersionFromRequest(request)); + ServiceListResponse response = + applicationRepository.servicesToCheckForConfigConvergence(applicationId, + request.getUri(), + getTimeoutFromRequest(request), + getVespaVersionFromRequest(request)); + return new HttpServiceListResponse(response); } private HttpResponse checkServiceConverge(ApplicationId applicationId, String hostAndPort, HttpRequest request) { - return applicationRepository.checkServiceForConfigConvergence(applicationId, hostAndPort, request.getUri(), - getTimeoutFromRequest(request), getVespaVersionFromRequest(request)); + ServiceResponse response = + applicationRepository.checkServiceForConfigConvergence(applicationId, + hostAndPort, + getTimeoutFromRequest(request), + getVespaVersionFromRequest(request)); + return HttpServiceResponse.createResponse(response, hostAndPort, request.getUri()); } private HttpResponse serviceStatusPage(ApplicationId applicationId, String service, String hostname, String pathSuffix) { @@ -301,4 +316,79 @@ public class ApplicationHandler extends HttpHandler { .map(Version::fromString); } + static class HttpServiceResponse extends JSONResponse { + + public static HttpServiceResponse createResponse(ConfigConvergenceChecker.ServiceResponse serviceResponse, String hostAndPort, URI uri) { + switch (serviceResponse.status) { + case ok: + return createOkResponse(uri, hostAndPort, serviceResponse.wantedGeneration, serviceResponse.currentGeneration, serviceResponse.converged); + case hostNotFound: + return createHostNotFoundInAppResponse(uri, hostAndPort, serviceResponse.wantedGeneration); + case notFound: + return createNotFoundResponse(uri, hostAndPort, serviceResponse.wantedGeneration, serviceResponse.errorMessage.orElse("")); + case error: + return createErrorResponse(uri, hostAndPort, serviceResponse.wantedGeneration, serviceResponse.errorMessage.orElse("")); + default: + throw new IllegalArgumentException("Unknown status " + serviceResponse.status); + } + } + + private HttpServiceResponse(int status, URI uri, String hostname, Long wantedGeneration) { + super(status); + object.setString("url", uri.toString()); + object.setString("host", hostname); + object.setLong("wantedGeneration", wantedGeneration); + } + + private static HttpServiceResponse createOkResponse(URI uri, String hostname, Long wantedGeneration, Long currentGeneration, boolean converged) { + HttpServiceResponse serviceResponse = new HttpServiceResponse(200, uri, hostname, wantedGeneration); + serviceResponse.object.setBool("converged", converged); + serviceResponse.object.setLong("currentGeneration", currentGeneration); + return serviceResponse; + } + + private static HttpServiceResponse createHostNotFoundInAppResponse(URI uri, String hostname, Long wantedGeneration) { + HttpServiceResponse serviceResponse = new HttpServiceResponse(410, uri, hostname, wantedGeneration); + serviceResponse.object.setString("problem", "Host:port (service) no longer part of application, refetch list of services."); + return serviceResponse; + } + + private static HttpServiceResponse createErrorResponse(URI uri, String hostname, Long wantedGeneration, String error) { + HttpServiceResponse serviceResponse = new HttpServiceResponse(500, uri, hostname, wantedGeneration); + serviceResponse.object.setString("error", error); + return serviceResponse; + } + + private static HttpServiceResponse createNotFoundResponse(URI uri, String hostname, Long wantedGeneration, String error) { + HttpServiceResponse serviceResponse = new HttpServiceResponse(404, uri, hostname, wantedGeneration); + serviceResponse.object.setString("error", error); + return serviceResponse; + } + + } + + static class HttpServiceListResponse extends JSONResponse { + + // Pre-condition: servicesToCheck has a state port + public HttpServiceListResponse(ConfigConvergenceChecker.ServiceListResponse response) { + super(200); + Cursor serviceArray = object.setArray("services"); + response.services().forEach((service) -> { + ServiceInfo serviceInfo = service.serviceInfo; + Cursor serviceObject = serviceArray.addObject(); + String hostName = serviceInfo.getHostName(); + int statePort = ConfigConvergenceChecker.getStatePort(serviceInfo).get(); + serviceObject.setString("host", hostName); + serviceObject.setLong("port", statePort); + serviceObject.setString("type", serviceInfo.getServiceType()); + serviceObject.setString("url", response.uri.toString() + "/" + hostName + ":" + statePort); + serviceObject.setLong("currentGeneration", service.currentGeneration); + }); + object.setString("url", response.uri.toString()); + object.setLong("currentGeneration", response.currentGeneration); + object.setLong("wantedGeneration", response.wantedGeneration); + object.setBool("converged", response.currentGeneration >= response.wantedGeneration); + } + } + } |