All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.yahoo.vespa.filedistribution.status.FileDistributionStatusClient Maven / Gradle / Ivy

There is a newer version: 8.441.21
Show newest version
// Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.filedistribution.status;

import com.yahoo.json.Jackson;
import ai.vespa.util.http.hc5.VespaHttpClientBuilder;
import com.fasterxml.jackson.databind.JsonNode;
import io.airlift.airline.Command;
import io.airlift.airline.HelpOption;
import io.airlift.airline.Option;
import io.airlift.airline.SingleCommand;
import org.apache.hc.client5.http.classic.methods.HttpGet;
import org.apache.hc.client5.http.config.RequestConfig;
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse;
import org.apache.hc.core5.http.HttpEntity;
import org.apache.hc.core5.http.ParseException;
import org.apache.hc.core5.http.io.entity.EntityUtils;
import org.apache.hc.core5.net.URIBuilder;
import org.apache.hc.core5.util.Timeout;

import javax.inject.Inject;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Map;

import static org.apache.hc.client5.http.config.RequestConfig.custom;

/**
 * Tool for getting file distribution status
 *
 * @author hmusum
 */
public class FileDistributionStatusClient {

    private static final String statusUnknown = "UNKNOWN";
    private static final String statusInProgress = "IN_PROGRESS";
    private static final String statusFinished = "FINISHED";

    private final String tenantName;
    private final String applicationName;
    private final String instanceName;
    private final String environment;
    private final String region;
    private final double timeout;
    private final boolean debug;

    FileDistributionStatusClient(CommandLineArguments arguments) {
        tenantName = arguments.getTenantName();
        applicationName = arguments.getApplicationName();
        instanceName = arguments.getInstanceName();
        environment = arguments.getEnvironment();
        region = arguments.getRegion();
        timeout = arguments.getTimeout();
        debug = arguments.getDebugFlag();
    }

    public static void main(String[] args) {
        try {
            new FileDistributionStatusClient(CommandLineArguments.build(args)).run();
        } catch (Exception e) {
            System.err.println(e.getMessage());
            System.exit(1);
        }
    }

    public void run() {
        String json = doHttpRequest();
        System.out.println(parseAndGenerateOutput(json));
    }

    @SuppressWarnings("deprecation")
    private String doHttpRequest() {
        Timeout timeoutInMillis = Timeout.ofMilliseconds((long) (timeout * 1000));
        RequestConfig config = custom()
                .setConnectionRequestTimeout(timeoutInMillis)
                .setResponseTimeout(timeoutInMillis)
                .build();
        try (CloseableHttpClient httpClient =  VespaHttpClientBuilder.custom().connectTimeout(timeoutInMillis).buildClient()) {
            URI statusUri = createStatusApiUri();
            if (debug)
                System.out.println("URI:" + statusUri);
            HttpGet request = new HttpGet(statusUri);
            request.addHeader("Connection", "Close");
            request.setConfig(config);
            CloseableHttpResponse response = httpClient.execute(request);
            HttpEntity entity = response.getEntity();
            String content = EntityUtils.toString(entity);
            if (debug)
                System.out.println("response:" + content);
            if (response.getCode() == 200) {
                return content;
            } else {
                throw new RuntimeException("Failed to get status for request " + statusUri + ": " +
                        response.getCode() + ": " + content);
            }
        } catch (IOException | ParseException e) {
            throw new RuntimeException(e);
        }
    }

    String parseAndGenerateOutput(String json) {
        JsonNode jsonNode;
        try {
            jsonNode = Jackson.mapper().readTree(json);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        String status = jsonNode.get("status").asText();
        return switch (status) {
            case statusUnknown -> "File distribution status unknown: " + jsonNode.get("message").asText();
            case statusInProgress -> "File distribution in progress:\n" + inProgressOutput(jsonNode.get("hosts"));
            case statusFinished -> "File distribution finished";
            default -> throw new RuntimeException("Unknown status " + status);
        };
    }

    private URI createStatusApiUri() {
        String path = String.format("/application/v2/tenant/%s/application/%s/environment/%s/region/%s/instance/%s/filedistributionstatus",
                                    tenantName, applicationName, environment, region, instanceName);
        try {
            return new URIBuilder()
                    .setScheme("http")
                    .setHost("localhost")
                    .setPort(19071)
                    .setPath(path)
                    .addParameter("timeout", String.valueOf(timeout))
                    .build();
        } catch (URISyntaxException e) {
            throw new RuntimeException(e);
        }
    }

    private String inProgressOutput(JsonNode hosts) {
        ArrayList statusPerHost = new ArrayList<>();
        for (JsonNode host : hosts) {
            String status = host.get("status").asText();
            StringBuilder sb = new StringBuilder(host.get("hostname").asText()).append(": ").append(status);
            switch (status) {
                case statusUnknown:
                    sb.append(" (").append(host.get("message").asText()).append(")");
                    break;
                case statusInProgress:
                    JsonNode fileReferencesArray = host.get("fileReferences");
                    int finished = 0;
                    for (JsonNode element : fileReferencesArray) {
                        for (Iterator> it = element.fields(); it.hasNext(); ) {
                            Map.Entry fileReferenceStatus = it.next();
                            if (fileReferenceStatus.getValue().asDouble() == 1.0)
                                finished++;
                        }
                    }
                    sb.append(" (").append(finished).append(" of ").append(fileReferencesArray.size()).append(" finished)");
                    break;
                case statusFinished:
                    break; // Nothing to add
                default:
                    throw new RuntimeException("Unknown status " + status);
            }
            statusPerHost.add(sb.toString());
        }
        return String.join("\n", statusPerHost);
    }

    @Command(name = "vespa-status-filedistribution", description = "Tool for getting file distribution status.")
    public static class CommandLineArguments {

        static CommandLineArguments build(String[] args) {
            CommandLineArguments arguments = null;
            try {
                arguments = SingleCommand.singleCommand(CommandLineArguments.class).parse(args);
            } catch (Exception e) {
                System.err.println(e.getMessage());
                System.err.println("Use --help to show usage.\n");
                System.exit(1);
            }

            if (arguments.helpOption.showHelpIfRequested()) {
                System.exit(0);
            }

            if (arguments.getTenantName() == null) {
                System.err.println("'--tenant' not set.");
                System.exit(1);
            }

            if (arguments.getApplicationName() == null) {
                System.err.println("'--application' not set.");
                System.exit(1);
            }

            return arguments;
        }

        @Inject
        HelpOption helpOption;

        @Option(name = {"--tenant"},
                description = "tenant name")
        private String tenantNameArg;

        @Option(name = {"--application"},
                description = "application name")
        private String applicationNameArg;

        @Option(name = {"--instance"},
                description = "instance name")
        private String instanceNameArg = "default";

        @Option(name = {"--environment"},
                description = "environment name")
        private String environmentArg = "prod";

        @Option(name = {"--region"},
                description = "region name")
        private String regionArg = "default";

        @Option(name = {"--timeout"},
                description = "The timeout (in seconds).")
        private double timeoutArg = 5;

        @Option(name = {"--debug"},
                description = "Print debug log.")
        private boolean debugArg;

        public String getTenantName() { return tenantNameArg; }

        public String getApplicationName() { return applicationNameArg; }

        public String getInstanceName() { return instanceNameArg; }

        public String getEnvironment() { return environmentArg; }

        public String getRegion() { return regionArg; }

        public double getTimeout() { return timeoutArg; }

        public boolean getDebugFlag() { return debugArg; }
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy