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

com.transferwise.envoy.xds.sotw.SotwDiscoveryService Maven / Gradle / Ivy

The newest version!
package com.transferwise.envoy.xds.sotw;

import com.google.protobuf.Message;
import com.transferwise.envoy.xds.NodeConfig;
import com.transferwise.envoy.xds.api.IncrementalConfigBuilder;
import com.transferwise.envoy.xds.AbstractDiscoveryService;
import com.transferwise.envoy.xds.TypeUrl;
import com.transferwise.envoy.xds.api.IncrementalConfigBuilder.NamedMessage;
import io.envoyproxy.envoy.service.discovery.v3.DiscoveryRequest;
import io.envoyproxy.envoy.service.discovery.v3.DiscoveryResponse;
import io.grpc.stub.StreamObserver;
import lombok.extern.slf4j.Slf4j;

import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Predicate;

@Slf4j
class SotwDiscoveryService extends AbstractDiscoveryService {

    private final StreamObserver responseObserver;

    private final SubManager subManager;
    private final VersionManager versionManager;

    private String awaitingVersion = null;

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

    SotwDiscoveryService(TypeUrl myTypeUrl, StreamObserver responseObserver, SubManager subManager, VersionManager versionManager, IncrementalConfigBuilder configBuilder, NodeConfig nodeConfig) {
        super(myTypeUrl, configBuilder, nodeConfig);
        this.responseObserver = responseObserver;
        this.subManager = subManager;
        this.versionManager = versionManager;
    }

    private boolean applyCurrentState() {
        return switch (getCurrentSubState()) {
            case PRE -> applyPre().isPresent();
            case POST -> applyPost().isPresent();
            default -> applyPre().isPresent() || applyPost().isPresent();
        };
    }

    private void processSubUpdate(Predicate subChanged) {
        // Remove state for anything we're no longer subscribed to.
        boolean hasUpdates = resourceState.keySet().removeIf(n -> !subManager.isSubscribedTo(n));

        IncrementalConfigBuilder.Resources resources = getResources(subChanged);
        if (!resources.getResources().isEmpty()) {
            hasUpdates = true;
            resources.getResources().forEach(msg -> resourceState.put(msg.getName(), msg));
        }
        hasUpdates = applyCurrentState() || hasUpdates;
        if (hasUpdates) {
            pushResources(resourceState.values());
        }
    }

    @Override
    protected void processRequest(DiscoveryRequest value) {
        log.debug("DiscoveryRequest: V={},R={},N={},T={}", value.getVersionInfo(), value.getResourceNamesList(), value.getResponseNonce(), value.getTypeUrl());
        if (!versionManager.processUpdate(value.getResponseNonce(), value.getVersionInfo())) {
            // Stale or otherwise invalid
            return;
        }
        subManager.processResourceListChange(value.getResourceNamesList()).ifPresent(this::processSubUpdate);
    }

    @Override
    public boolean awaitingAck() {
        if (awaitingVersion == null) {
            return false;
        }

        return !versionManager.hasAcceptedVersion(awaitingVersion);
    }

    @Override
    protected Predicate subFilter() {
        return subManager::isSubscribedTo;
    }

    @Override
    protected boolean updateState(IncrementalConfigBuilder.Response response) {
        if (response.isNoop()) {
            // Nothing to do :)
            return false;
        }
        response.getAddAndUpdates().forEach(msg ->
                resourceState.put(msg.getName(), msg)
        );
        response.getRemoves().forEach(name ->
                resourceState.remove(name)
        );
        return true;
    }

    @Override
    protected void pushNewState(IncrementalConfigBuilder.Response response) {
        pushResources(resourceState.values());
    }

    private void pushResources(Collection> resources) {
        String version = versionManager.getNext();
        DiscoveryResponse.Builder responseBuilder = DiscoveryResponse.newBuilder();
        log.debug("Sending {} of {}", version, getTypeUrl());
        List names = new ArrayList<>(resources.size());
        for (IncrementalConfigBuilder.NamedMessage namedMessage : resources) {
            responseBuilder.addResources(pack(namedMessage.getMessage()));
            names.add(namedMessage.getName());
        }
        responseBuilder.setTypeUrl(getTypeUrl().getTypeUrl());
        responseBuilder.setVersionInfo(version);
        responseBuilder.setNonce(versionManager.pushedVersion(version));

        DiscoveryResponse discoveryResponse = responseBuilder.build();
        log.debug("DiscoveryResponse: V={},R={},N={},T={}", discoveryResponse.getVersionInfo(), names, discoveryResponse.getNonce(), discoveryResponse.getTypeUrl());

        awaitingVersion = version;
        responseObserver.onNext(discoveryResponse);
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy