aboutsummaryrefslogtreecommitdiffstats
path: root/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/routing/PreparedEndpoints.java
blob: 62dc8eab1c737126495b4600450c22ccea9dbd15 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
package com.yahoo.vespa.hosted.controller.routing;

import com.yahoo.vespa.hosted.controller.api.identifiers.DeploymentId;
import com.yahoo.vespa.hosted.controller.api.integration.certificates.EndpointCertificate;
import com.yahoo.vespa.hosted.controller.api.integration.configserver.ContainerEndpoint;
import com.yahoo.vespa.hosted.controller.application.AssignedRotation;
import com.yahoo.vespa.hosted.controller.application.Endpoint;
import com.yahoo.vespa.hosted.controller.application.EndpointId;
import com.yahoo.vespa.hosted.controller.application.EndpointList;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.OptionalInt;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * This represents the endpoints, and associated resources, that have been prepared for a deployment.
 *
 * @author mpolden
 */
public record PreparedEndpoints(DeploymentId deployment,
                                EndpointList endpoints,
                                List<AssignedRotation> rotations,
                                Optional<EndpointCertificate> certificate) {

    public PreparedEndpoints(DeploymentId deployment, EndpointList endpoints, List<AssignedRotation> rotations, Optional<EndpointCertificate> certificate) {
        this.deployment = Objects.requireNonNull(deployment);
        this.endpoints = Objects.requireNonNull(endpoints);
        this.rotations = List.copyOf(Objects.requireNonNull(rotations));
        this.certificate = Objects.requireNonNull(certificate);
    }

    /** Returns the endpoints contained in this as {@link com.yahoo.vespa.hosted.controller.api.integration.configserver.ContainerEndpoint} */
    public Set<ContainerEndpoint> containerEndpoints() {
        Map<EndpointId, AssignedRotation> rotationsByEndpointId = rotations.stream()
                                                                           .collect(Collectors.toMap(AssignedRotation::endpointId,
                                                                                                     Function.identity()));
        Set<ContainerEndpoint> containerEndpoints = new HashSet<>();
        endpoints.scope(Endpoint.Scope.zone).groupingBy(Endpoint::cluster).forEach((clusterId, clusterEndpoints) -> {
            clusterEndpoints.groupingBy(Endpoint::authMethod).forEach((authMethod, endpointsByAuthMethod) -> {
                containerEndpoints.add(new ContainerEndpoint(clusterId.value(),
                                                             asString(Endpoint.Scope.zone),
                                                             endpointsByAuthMethod.mapToList(Endpoint::dnsName),
                                                             OptionalInt.empty(),
                                                             endpointsByAuthMethod.first().get().routingMethod(),
                                                             authMethod));
            });
        });
        endpoints.scope(Endpoint.Scope.global).groupingBy(Endpoint::cluster).forEach((clusterId, clusterEndpoints) -> {
            for (var endpoint : clusterEndpoints) {
                List<String> names = new ArrayList<>(2);
                names.add(endpoint.dnsName());
                if (endpoint.requiresRotation()) {
                    EndpointId endpointId = EndpointId.of(endpoint.name());
                    AssignedRotation rotation = rotationsByEndpointId.get(endpointId);
                    if (rotation == null) {
                        throw new IllegalStateException(endpoint + " requires a rotation, but no rotation has been assigned to " + endpointId);
                    }
                    // Include the rotation ID as a valid name of this container endpoint
                    // (required by global routing health checks)
                    names.add(rotation.rotationId().asString());
                }
                containerEndpoints.add(new ContainerEndpoint(clusterId.value(),
                                                             asString(Endpoint.Scope.global),
                                                             names,
                                                             OptionalInt.empty(),
                                                             endpoint.routingMethod(),
                                                             endpoint.authMethod()));
            }
        });
        endpoints.scope(Endpoint.Scope.application).groupingBy(Endpoint::cluster).forEach((clusterId, clusterEndpoints) -> {
            for (var endpoint : clusterEndpoints) {
                Optional<Endpoint.Target> matchingTarget = endpoint.targets().stream()
                                                                   .filter(t -> t.routesTo(deployment))
                                                                   .findFirst();
                if (matchingTarget.isEmpty()) throw new IllegalStateException("No target found routing to " + deployment + " in " + endpoint);
                containerEndpoints.add(new ContainerEndpoint(clusterId.value(),
                                                             asString(Endpoint.Scope.application),
                                                             List.of(endpoint.dnsName()),
                                                             OptionalInt.of(matchingTarget.get().weight()),
                                                             endpoint.routingMethod(),
                                                             endpoint.authMethod()));
            }
        });
        return containerEndpoints;
    }

    private static String asString(Endpoint.Scope scope) {
        return switch (scope) {
            case application -> "application";
            case global -> "global";
            case weighted -> "weighted";
            case zone -> "zone";
        };
    }

}