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

com.yahoo.vespa.model.container.ContainerCluster Maven / Gradle / Ivy

// Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.model.container;

import com.yahoo.cloud.config.ClusterInfoConfig;
import com.yahoo.cloud.config.ConfigserverConfig;
import com.yahoo.cloud.config.CuratorConfig;
import com.yahoo.component.ComponentId;
import com.yahoo.config.application.api.ApplicationMetaData;
import com.yahoo.config.docproc.DocprocConfig;
import com.yahoo.config.docproc.SchemamappingConfig;
import com.yahoo.config.model.ApplicationConfigProducerRoot;
import com.yahoo.config.model.deploy.DeployState;
import com.yahoo.config.model.producer.AnyConfigProducer;
import com.yahoo.config.model.producer.TreeConfigProducer;
import com.yahoo.config.provision.ClusterSpec;
import com.yahoo.config.provision.Zone;
import com.yahoo.container.ComponentsConfig;
import com.yahoo.container.QrSearchersConfig;
import com.yahoo.container.bundle.BundleInstantiationSpecification;
import com.yahoo.container.core.ApplicationMetadataConfig;
import com.yahoo.container.core.document.ContainerDocumentConfig;
import com.yahoo.container.di.config.PlatformBundlesConfig;
import com.yahoo.container.jdisc.JdiscBindingsConfig;
import com.yahoo.container.jdisc.config.HealthMonitorConfig;
import com.yahoo.container.jdisc.state.StateHandler;
import com.yahoo.container.logging.AccessLog;
import com.yahoo.container.usability.BindingsOverviewHandler;
import com.yahoo.document.config.DocumentmanagerConfig;
import com.yahoo.jdisc.http.server.jetty.VoidRequestLog;
import com.yahoo.osgi.provider.model.ComponentModel;
import com.yahoo.prelude.semantics.SemanticRulesConfig;
import com.yahoo.search.config.IndexInfoConfig;
import com.yahoo.search.config.QrStartConfig;
import com.yahoo.search.config.SchemaInfoConfig;
import com.yahoo.search.pagetemplates.PageTemplatesConfig;
import com.yahoo.search.query.profile.config.QueryProfilesConfig;
import com.yahoo.vespa.configdefinition.IlscriptsConfig;
import com.yahoo.vespa.model.PortsMeta;
import com.yahoo.vespa.model.Service;
import com.yahoo.vespa.model.VespaModel;
import com.yahoo.vespa.model.admin.Admin;
import com.yahoo.vespa.model.admin.monitoring.Monitoring;
import com.yahoo.vespa.model.clients.ContainerDocumentApi;
import com.yahoo.vespa.model.container.component.AccessLogComponent;
import com.yahoo.vespa.model.container.component.BindingPattern;
import com.yahoo.vespa.model.container.component.Component;
import com.yahoo.vespa.model.container.component.ComponentGroup;
import com.yahoo.vespa.model.container.component.ComponentsConfigGenerator;
import com.yahoo.vespa.model.container.component.DiscBindingsConfigGenerator;
import com.yahoo.vespa.model.container.component.FileStatusHandlerComponent;
import com.yahoo.vespa.model.container.component.Handler;
import com.yahoo.vespa.model.container.component.SimpleComponent;
import com.yahoo.vespa.model.container.component.SystemBindingPattern;
import com.yahoo.vespa.model.container.component.chain.ProcessingHandler;
import com.yahoo.vespa.model.container.configserver.ConfigserverCluster;
import com.yahoo.vespa.model.container.docproc.ContainerDocproc;
import com.yahoo.vespa.model.container.docproc.DocprocChains;
import com.yahoo.vespa.model.container.http.Client;
import com.yahoo.vespa.model.container.http.Http;
import com.yahoo.vespa.model.container.processing.ProcessingChains;
import com.yahoo.vespa.model.container.search.ContainerSearch;
import com.yahoo.vespa.model.container.search.searchchain.SearchChains;
import com.yahoo.vespa.model.content.Content;
import com.yahoo.vespa.model.search.SearchCluster;

import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.OptionalDouble;
import java.util.OptionalInt;
import java.util.Set;
import java.util.TreeSet;

import static com.yahoo.vespa.model.container.component.AccessLogComponent.AccessLogType.jsonAccessLog;
import static com.yahoo.vespa.model.container.component.chain.ProcessingHandler.PROCESSING_HANDLER_CLASS;

/**
 * Parent class for all container cluster types.
 *
 * @author gjoranv
 * @author Einar M R Rosenvinge
 * @author Tony Vaagenes
 */
public abstract class ContainerCluster
        extends TreeConfigProducer
        implements
        ComponentsConfig.Producer,
        JdiscBindingsConfig.Producer,
        DocumentmanagerConfig.Producer,
        ContainerDocumentConfig.Producer,
        HealthMonitorConfig.Producer,
        ApplicationMetadataConfig.Producer,
        PlatformBundlesConfig.Producer,
        IndexInfoConfig.Producer,
        IlscriptsConfig.Producer,
        SchemamappingConfig.Producer,
        QrSearchersConfig.Producer,
        QrStartConfig.Producer,
        QueryProfilesConfig.Producer,
        PageTemplatesConfig.Producer,
        SemanticRulesConfig.Producer,
        DocprocConfig.Producer,
        ClusterInfoConfig.Producer,
        ConfigserverConfig.Producer,
        CuratorConfig.Producer,
        SchemaInfoConfig.Producer
{

    /**
     * URI prefix used for internal, usually programmatic, APIs. URIs using this
     * prefix should never be considered available for direct use by customers, and
     * normal compatibility concerns only applies to libraries using the URIs in
     * question, not contents served from the URIs themselves.
     */
    public static final String RESERVED_URI_PREFIX = "/reserved-for-internal-use";

    public static final String APPLICATION_STATUS_HANDLER_CLASS = "com.yahoo.container.handler.observability.ApplicationStatusHandler";
    public static final String BINDINGS_OVERVIEW_HANDLER_CLASS = BindingsOverviewHandler.class.getName();
    public static final String LOG_HANDLER_CLASS = com.yahoo.container.handler.LogHandler.class.getName();
    public static final String G1GC = "-XX:+UseG1GC -XX:MaxTenuringThreshold=15";
    public static final String PARALLEL_GC = "-XX:+UseParallelGC -XX:MaxTenuringThreshold=15 -XX:NewRatio=1";

    public static final String STATE_HANDLER_CLASS = "com.yahoo.container.jdisc.state.StateHandler";
    public static final BindingPattern STATE_HANDLER_BINDING_1 = SystemBindingPattern.fromHttpPath(StateHandler.STATE_API_ROOT);
    public static final BindingPattern STATE_HANDLER_BINDING_2 = SystemBindingPattern.fromHttpPath(StateHandler.STATE_API_ROOT + "/*");

    public static final String ROOT_HANDLER_PATH = "/";
    public static final BindingPattern ROOT_HANDLER_BINDING = SystemBindingPattern.fromHttpPath(ROOT_HANDLER_PATH);

    public static final BindingPattern VIP_HANDLER_BINDING = SystemBindingPattern.fromHttpPath("/status.html");

    private final String name;

    protected List containers = new ArrayList<>();

    private Http http;
    private ProcessingChains processingChains;
    private ContainerSearch containerSearch;
    private ContainerDocproc containerDocproc;
    private ContainerDocumentApi containerDocumentApi;
    private SecretStore secretStore;
    private final ContainerThreadpool defaultHandlerThreadpool;

    private boolean rpcServerEnabled = true;
    private boolean httpServerEnabled = true;

    private final Set platformBundles = new TreeSet<>(); // Ensure stable ordering

    private final ComponentGroup> componentGroup;
    protected final boolean isHostedVespa;
    private final boolean zooKeeperLocalhostAffinity;
    private final String compressionType;

    private final Map concreteDocumentTypes = new LinkedHashMap<>();

    private ApplicationMetaData applicationMetaData = null;

    /** The zone this is deployed in, or the default zone if not on hosted Vespa */
    private Zone zone;

    private String hostClusterId = null;
    private String jvmGCOptions = null;

    private volatile boolean deferChangesUntilRestart = false;
    private boolean clientsLegacyMode;
    private List clients = List.of();

    public ContainerCluster(TreeConfigProducer parent, String configSubId, String clusterId, DeployState deployState, boolean zooKeeperLocalhostAffinity) {
        this(parent, configSubId, clusterId, deployState, zooKeeperLocalhostAffinity, 1);
    }

    public ContainerCluster(TreeConfigProducer parent, String configSubId, String clusterId, DeployState deployState, boolean zooKeeperLocalhostAffinity, int defaultPoolNumThreads) {
        super(parent, configSubId);
        this.name = clusterId;
        this.isHostedVespa = stateIsHosted(deployState);
        this.zone = (deployState != null) ? deployState.zone() : Zone.defaultZone();
        this.zooKeeperLocalhostAffinity = zooKeeperLocalhostAffinity;
        this.compressionType = deployState.featureFlags().logFileCompressionAlgorithm("zstd");

        componentGroup = new ComponentGroup<>(this, "component");

        addCommonVespaBundles();
        addSimpleComponent(VoidRequestLog.class);
        addComponent(new DefaultThreadpoolProvider(this, defaultPoolNumThreads));
        defaultHandlerThreadpool = new Handler.DefaultHandlerThreadpool(deployState, null);
        addComponent(defaultHandlerThreadpool);
        addSimpleComponent(com.yahoo.concurrent.classlock.ClassLocking.class);
        addSimpleComponent("com.yahoo.container.jdisc.metric.MetricConsumerProviderProvider");
        addSimpleComponent("com.yahoo.container.jdisc.metric.MetricProvider");
        addSimpleComponent("com.yahoo.container.jdisc.metric.MetricUpdater");
        addSimpleComponent(com.yahoo.container.jdisc.ThreadedHttpRequestHandler.Context.class);
        addSimpleComponent(com.yahoo.metrics.simple.MetricManager.class.getName());
        addSimpleComponent(com.yahoo.metrics.simple.jdisc.JdiscMetricsFactory.class.getName());
        addSimpleComponent("com.yahoo.container.jdisc.state.StateMonitor");
        addSimpleComponent("com.yahoo.container.jdisc.ContainerThreadFactory");
        addSimpleComponent("com.yahoo.container.handler.VipStatus");
        addSimpleComponent(com.yahoo.container.handler.ClustersStatus.class.getName());
        addSimpleComponent("com.yahoo.container.jdisc.DisabledConnectionLogProvider");
        addSimpleComponent(com.yahoo.jdisc.http.server.jetty.Janitor.class);
    }

    protected abstract boolean messageBusEnabled();

    public ClusterSpec.Id id() { return ClusterSpec.Id.from(getName()); }

    public void setZone(Zone zone) {
        this.zone = zone;
    }
    public Zone getZone() {
        return zone;
    }

    protected Optional getAdmin() {
        var parent = getParent();
        if (parent != null) {
            var r = parent.getRoot();
            if (r instanceof VespaModel model) {
                return Optional.ofNullable(model.getAdmin());
            }
        }
        return Optional.empty();
    }

    public void addDefaultHandlersWithVip() {
        addDefaultHandlersExceptStatus();
        addVipHandler();
    }

    public final void addDefaultHandlersExceptStatus() {
        addDefaultRootHandler();
        addMetricStateHandler();
        addApplicationStatusHandler();
    }

    public void addMetricStateHandler() {
        Handler stateHandler = new Handler(
                new ComponentModel(STATE_HANDLER_CLASS, null, null, null));
        stateHandler.addServerBindings(STATE_HANDLER_BINDING_1, STATE_HANDLER_BINDING_2);
        addComponent(stateHandler);
    }

    public void addDefaultRootHandler() {
        Handler handler = new Handler(
                new ComponentModel(BundleInstantiationSpecification.fromStrings(
                        BINDINGS_OVERVIEW_HANDLER_CLASS, null, null), null));  // null bundle, as the handler is in container-disc
        handler.addServerBindings(ROOT_HANDLER_BINDING);
        addComponent(handler);
    }

    public void addApplicationStatusHandler() {
        Handler statusHandler = new Handler(
                new ComponentModel(BundleInstantiationSpecification.fromStrings(
                        APPLICATION_STATUS_HANDLER_CLASS, null, null), null));  // null bundle, as the handler is in container-disc
        statusHandler.addServerBindings(SystemBindingPattern.fromHttpPath("/ApplicationStatus"));
        addComponent(statusHandler);
    }

    public void addVipHandler() {
        Handler vipHandler = Handler.fromClassName(FileStatusHandlerComponent.CLASS);
        vipHandler.addServerBindings(VIP_HANDLER_BINDING);
        addComponent(vipHandler);
    }

    public final void addComponent(Component component) {
        componentGroup.addComponent(component);
        if (component instanceof Handler handler) {
            ensureHandlerHasThreadpool(handler);
        }
    }

    private void ensureHandlerHasThreadpool(Handler handler) {
        if (! handler.hasCustomThreadPool) {
            handler.inject(defaultHandlerThreadpool);
        }
    }

    public final void addSimpleComponent(String idSpec, String classSpec, String bundleSpec) {
        addComponent(new SimpleComponent(new ComponentModel(idSpec, classSpec, bundleSpec)));
    }

    /**
     * Removes a component by id
     *
     * @return the removed component, or null if it was not present
     */
    @SuppressWarnings("unused") // Used from other repositories
    public Component removeComponent(ComponentId componentId) {
        return componentGroup.removeComponent(componentId);
    }

    public void removeSimpleComponent(Class clazz) {
        removeComponent(new SimpleComponent(clazz.getName()).getComponentId());
    }

    public void addSimpleComponent(Class clazz) {
        addSimpleComponent(clazz.getName());
    }

    protected void addSimpleComponent(String className) {
        addComponent(new SimpleComponent(className));
    }

    public void prepare(DeployState deployState) {
        applicationMetaData = deployState.getApplicationPackage().getMetaData();
        doPrepare(deployState);
    }

    protected void doPrepare(DeployState deployState) {
        wireLogctlSpecs();
    }

    private void wireLogctlSpecs() {
        getAdmin().ifPresent(admin -> {
            for (var c : getContainers()) {
                c.setLogctlSpecs(admin.getLogctlSpecs());
            }});
    }

    public String getName() {
        return name;
    }

    public List getContainers() {
        return Collections.unmodifiableList(containers);
    }

    public void addContainer(CONTAINER container) {
        container.setOwner(this);
        container.setClusterName(name);
        container.setProp("clustername", name)
                 .setProp("index", this.containers.size())
                 .setProp("clustertype", "container");
        containers.add(container);
    }

    public void addContainers(Collection containers) {
        containers.forEach(this::addContainer);
    }

    public void setProcessingChains(ProcessingChains processingChains, BindingPattern... serverBindings) {
        if (this.processingChains != null)
            throw new IllegalStateException("ProcessingChains should only be set once.");

        this.processingChains = processingChains;

        ProcessingHandler processingHandler = new ProcessingHandler<>(
                processingChains,
                BundleInstantiationSpecification.fromStrings(PROCESSING_HANDLER_CLASS, null, null));

        for (BindingPattern binding: serverBindings)
            processingHandler.addServerBindings(binding);

        addComponent(processingHandler);
    }

    ProcessingChains getProcessingChains() {
        return processingChains;
    }

    public SearchChains getSearchChains() {
        if (containerSearch == null)
            throw new IllegalArgumentException("Search components not found in container cluster '" + getSubId() +
                                               "': Add  to the cluster in services.xml");
        return containerSearch.getChains();
    }

    public ContainerSearch getSearch() {
        return containerSearch;
    }

    public void setSearch(ContainerSearch containerSearch) {
        this.containerSearch = containerSearch;
    }

    public void setHttp(Http http) {
        this.http = http;
        addChild(http);
    }

    public Http getHttp() {
        return http;
    }

    public void setClients(boolean legacyMode, List clients) {
        clientsLegacyMode = legacyMode;
        this.clients = clients;
    }

    public List getClients() {
        return clients;
    }

    public boolean clientsLegacyMode() { return clientsLegacyMode; }

    public ContainerDocproc getDocproc() {
        return containerDocproc;
    }

    public void setDocproc(ContainerDocproc containerDocproc) {
        this.containerDocproc = containerDocproc;
    }

    public void setDocumentApi(ContainerDocumentApi containerDocumentApi) {
        this.containerDocumentApi = containerDocumentApi;
    }

    public DocprocChains getDocprocChains() {
        if (containerDocproc == null)
            throw new IllegalArgumentException("Document processing components not found in container cluster '" + getSubId() +
                                            "': Add  to the cluster in services.xml");
        return containerDocproc.getChains();
    }

    public Collection getHandlers() {
        return componentGroup.getComponents(Handler.class);
    }

    public void setSecretStore(SecretStore secretStore) {
        this.secretStore = secretStore;
    }

    public Optional getSecretStore() {
        return Optional.ofNullable(secretStore);
    }

    public Map> getComponentsMap() {
        return componentGroup.getComponentMap();
    }

    /** Returns all components in this cluster (generic, handlers, chained) */
    public Collection> getAllComponents() {
        List> allComponents = new ArrayList<>();
        recursivelyFindAllComponents(allComponents, this);
        // We need consistent ordering
        Collections.sort(allComponents);
        return Collections.unmodifiableCollection(allComponents);
    }

    private void recursivelyFindAllComponents(Collection> allComponents, TreeConfigProducer current) {
        for (var child: current.getChildren().values()) {
            if (child instanceof Component)
                allComponents.add((Component) child);
            
            if (child instanceof TreeConfigProducer t && !(child instanceof Container))
                recursivelyFindAllComponents(allComponents, t);
        }
    }

    @Override
    public void getConfig(ComponentsConfig.Builder builder) {
        builder.setApplyOnRestart(getDeferChangesUntilRestart()); //  Sufficient to set on one config
        builder.components.addAll(ComponentsConfigGenerator.generate(getAllComponents()));
        builder.components(new ComponentsConfig.Components.Builder().id("com.yahoo.container.core.config.HandlersConfigurerDi$RegistriesHack"));
    }

    @Override
    public void getConfig(JdiscBindingsConfig.Builder builder) {
        builder.handlers.putAll(DiscBindingsConfigGenerator.generate(getHandlers()));
    }

    @Override
    public void getConfig(DocumentmanagerConfig.Builder builder) {
        if (containerDocumentApi != null)
            builder.ignoreundefinedfields(containerDocumentApi.ignoreUndefinedFields());
    }

    @Override
    public void getConfig(ContainerDocumentConfig.Builder builder) {
        for (Map.Entry e : concreteDocumentTypes.entrySet()) {
            ContainerDocumentConfig.Doctype.Builder dtb = new ContainerDocumentConfig.Doctype.Builder();
            dtb.type(e.getKey());
            dtb.factorycomponent(e.getValue());
            builder.doctype(dtb);
        }
    }

    @Override
    public void getConfig(HealthMonitorConfig.Builder builder) {
        Monitoring monitoring = getMonitoringService();
        if (monitoring != null) {
            builder.snapshot_interval(monitoring.getIntervalSeconds());
        }
    }

    @Override
    public void getConfig(ApplicationMetadataConfig.Builder builder) {
        if (applicationMetaData != null)
            builder.name(applicationMetaData.getApplicationId().application().value()).
                    path(applicationMetaData.getDeployPath()).
                    timestamp(applicationMetaData.getDeployTimestamp()).
                    checksum(applicationMetaData.getChecksum()).
                    generation(applicationMetaData.getGeneration());
    }

    /**
     * Adds the Vespa bundles that are necessary for most container types.
     * Note that some of these can be removed later by the individual cluster types.
     */
    public void addCommonVespaBundles() {
        PlatformBundles.COMMON_VESPA_BUNDLES.forEach(this::addPlatformBundle);
        PlatformBundles.VESPA_SECURITY_BUNDLES.forEach(this::addPlatformBundle);
        PlatformBundles.VESPA_ZK_BUNDLES.forEach(this::addPlatformBundle);
    }

    /**
     * Add all search/docproc/feed related platform bundles.
     * These are only required for application configured containers as the platform bundle set is not allowed to change
     * between config generations. For standalone container platform bundles can be added on features enabled as an
     * update of application package requires restart.
     */
    public void addAllPlatformBundles() {
        ContainerDocumentApi.addVespaClientContainerBundle(this);
        addSearchAndDocprocBundles();
    }

    public void addSearchAndDocprocBundles() { PlatformBundles.SEARCH_AND_DOCPROC_BUNDLES.forEach(this::addPlatformBundle); }

    /**
     * Adds a bundle present at a known location at the target container nodes.
     * Note that the set of platform bundles cannot change during the jdisc container's lifetime.
     *
     * @param bundlePath usually an absolute path, e.g. '$VESPA_HOME/lib/jars/foo.jar'
     */
    public final void addPlatformBundle(Path bundlePath) {
        if (! unnecessaryPlatformBundles().contains(bundlePath)) {
            platformBundles.add(bundlePath);
        } else {
            log.fine(() -> "Not installing bundle " + bundlePath + " for cluster " + getName());
        }
    }

    /**
     * Implement in subclasses to avoid installing unnecessary bundles, see {@link PlatformBundles}
     * Should only return constant values, as there is no guarantee for when this is called.
     */
    protected Set unnecessaryPlatformBundles() { return Set.of(); }

    @Override
    public void getConfig(PlatformBundlesConfig.Builder builder) {
        platformBundles.stream()
                .map(Path::toString)
                .forEach(builder::bundlePaths);
    }

    @Override
    public void getConfig(QrSearchersConfig.Builder builder) {
        if (containerSearch != null) containerSearch.getConfig(builder);
    }

    @Override
    public void getConfig(QrStartConfig.Builder builder) {
        builder.jvm
                .verbosegc(false)
                .availableProcessors(1)
                .compressedClassSpaceSize(32)
                .minHeapsize(32)
                .heapsize(256)
                .heapSizeAsPercentageOfPhysicalMemory(0)
                .gcopts(Objects.requireNonNullElse(jvmGCOptions, G1GC));
    }

    @Override
    public void getConfig(DocprocConfig.Builder builder) {
        if (containerDocproc != null) containerDocproc.getConfig(builder);
    }

    @Override
    public void getConfig(PageTemplatesConfig.Builder builder) {
        if (containerSearch != null) containerSearch.getConfig(builder);
    }

    @Override
    public void getConfig(SemanticRulesConfig.Builder builder) {
        if (containerSearch != null) containerSearch.getConfig(builder);
    }

    @Override
    public void getConfig(QueryProfilesConfig.Builder builder) {
        if (containerSearch != null) containerSearch.getConfig(builder);
    }

    @Override
    public void getConfig(SchemamappingConfig.Builder builder) {
        if (containerDocproc != null) containerDocproc.getConfig(builder);
    }

    @Override
    public void getConfig(IndexInfoConfig.Builder builder) {
        if (containerSearch != null) containerSearch.getConfig(builder);
    }

    @Override
    public void getConfig(SchemaInfoConfig.Builder builder) {
        if (containerSearch != null) containerSearch.getConfig(builder);
    }

    public void initialize(Map clusterMap) {
        if (containerSearch != null) containerSearch.connectSearchClusters(clusterMap);
    }

    public void addAccessLog(String clusterName) {
        addAccessLog(Optional.ofNullable(clusterName));
    }

    public void addAccessLog(String fileNamePattern, String symlinkName) {
        removeSimpleComponent(VoidRequestLog.class);
        addSimpleComponent(AccessLog.class);
        addComponent(new AccessLogComponent(jsonAccessLog, compressionType, fileNamePattern, null, true, true, symlinkName, 1024, 256 * 1024));
    }

    protected void addAccessLog(Optional clusterName) {
        removeSimpleComponent(VoidRequestLog.class);
        addSimpleComponent(AccessLog.class);
        addComponent(new AccessLogComponent(this, jsonAccessLog, compressionType, clusterName, isHostedVespa));
    }


    @Override
    public void getConfig(IlscriptsConfig.Builder builder) {
        for (SearchCluster searchCluster : Content.getSearchClusters(getRoot().configModelRepo())) {
            searchCluster.getConfig(builder);
        }
    }

    @Override
    public void getConfig(ClusterInfoConfig.Builder builder) {
        builder.clusterId(name);
        builder.nodeCount(containers.size());
        containers.forEach(c -> builder.nodeIndices(c.index()));

        for (Service service : getDescendantServices()) {
            builder.services.add(new ClusterInfoConfig.Services.Builder()
                                         .index(Integer.parseInt(service.getServicePropertyString("index", "99999")))
                                         .hostname(service.getHostName())
                                         .ports(getPorts(service)));
        }
    }

    /**
     * Returns a config server config containing the right zone settings (and defaults for the rest).
     * This is useful to allow applications to find out in which zone they are running by having the Zone
     * object (which is constructed from this config) injected.
     */
    @Override
    public void getConfig(ConfigserverConfig.Builder builder) {
        builder.system(zone.system().value());
        builder.environment(zone.environment().value());
        builder.region(zone.region().value());
        builder.cloud(zone.cloud().name().value());
    }

    @Override
    public void getConfig(CuratorConfig.Builder builder) {
        if (getParent() instanceof ConfigserverCluster) return; // Produces its own config
        for (var container : containers) {
            builder.server(new CuratorConfig.Server.Builder().hostname(container.getHostResource().getHostname()));
        }
        builder.zookeeperLocalhostAffinity(zooKeeperLocalhostAffinity);
    }

    private List getPorts(Service service) {
        List builders = new ArrayList<>();
        PortsMeta portsMeta = service.getPortsMeta();
        for (int i = 0; i < portsMeta.getNumPorts(); i++) {
            builders.add(new ClusterInfoConfig.Services.Ports.Builder()
                                 .number(service.getRelativePort(i))
                                 .tags(ApplicationConfigProducerRoot.getPortTags(portsMeta, i))
            );
        }
        return builders;
    }

    public boolean isHostedVespa() {
        return isHostedVespa;
    }

    public Map concreteDocumentTypes() { return concreteDocumentTypes; }

    public void setHostClusterId(String clusterId) { hostClusterId = clusterId; }

    /**
     * Returns the id of the content cluster which hosts this container cluster, if any.
     * This is only set with hosted clusters where this container cluster is set up to run on the nodes
     * of a content cluster.
     */
    public Optional getHostClusterId() { return Optional.ofNullable(hostClusterId); }

    public void setJvmGCOptions(String opts) { this.jvmGCOptions = opts; }

    public Optional getJvmGCOptions() { return Optional.ofNullable(jvmGCOptions); }

    public final void setRpcServerEnabled(boolean rpcServerEnabled) { this.rpcServerEnabled = rpcServerEnabled; }

    boolean rpcServerEnabled() { return rpcServerEnabled; }

    boolean httpServerEnabled() { return httpServerEnabled; }

    public void setHttpServerEnabled(boolean httpServerEnabled) { this.httpServerEnabled = httpServerEnabled; }

    @Override
    public String toString() {
        return "container cluster '" + getName() + "'";
    }

    /**
     * Mark whether the config emitted by this cluster currently should be applied by clients already running with
     * a previous generation of it only by restarting the consuming processes.
     */
    public void setDeferChangesUntilRestart(boolean deferChangesUntilRestart) {
        this.deferChangesUntilRestart = deferChangesUntilRestart;
    }

    public boolean getDeferChangesUntilRestart() { return deferChangesUntilRestart; }

    /**
     * Returns the percentage of host physical memory this application has specified for nodes in this cluster,
     * or empty if this is not specified by the application.
     */
    public record JvmMemoryPercentage(int ofContainerAvailable, OptionalInt ofContainerTotal, OptionalDouble asAbsoluteGb) { // optionalInt pctOfTotal < int pctOfAvailable
        static JvmMemoryPercentage of(int percentageOfAvailable) { return new JvmMemoryPercentage(percentageOfAvailable, OptionalInt.empty(), OptionalDouble.empty()); }
        static JvmMemoryPercentage of(int percentageOfAvailable, int percentageOfTotal, double absoluteMemoryGb) {
            return new JvmMemoryPercentage(percentageOfAvailable, OptionalInt.of(percentageOfTotal), OptionalDouble.of(absoluteMemoryGb));
        }
    }
    public Optional getMemoryPercentage() { return Optional.empty(); }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy