aboutsummaryrefslogtreecommitdiffstats
path: root/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/routing/RoutingPolicy.java
blob: 3d43e42af273a0b71fbe96d314d020fd3095eae8 (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
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.hosted.controller.routing;

import ai.vespa.http.DomainName;
import com.google.common.collect.ImmutableSortedSet;
import com.yahoo.config.provision.SystemName;
import com.yahoo.config.provision.zone.RoutingMethod;
import com.yahoo.text.Text;
import com.yahoo.vespa.hosted.controller.api.identifiers.DeploymentId;
import com.yahoo.vespa.hosted.controller.api.integration.zone.ZoneRegistry;
import com.yahoo.vespa.hosted.controller.application.Endpoint;
import com.yahoo.vespa.hosted.controller.application.Endpoint.Port;
import com.yahoo.vespa.hosted.controller.application.EndpointId;

import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;

/**
 * Represents the DNS routing policy for a {@link com.yahoo.vespa.hosted.controller.application.Deployment}.
 *
 * @author mortent
 * @author mpolden
 */
public record RoutingPolicy(RoutingPolicyId id,
                            Optional<DomainName> canonicalName,
                            Optional<String> ipAddress,
                            Optional<String> dnsZone,
                            Set<EndpointId> instanceEndpoints,
                            Set<EndpointId> applicationEndpoints,
                            Status status) {

    /** DO NOT USE. Public for serialization purposes */
    public RoutingPolicy(RoutingPolicyId id, Optional<DomainName> canonicalName, Optional<String> ipAddress, Optional<String> dnsZone,
                         Set<EndpointId> instanceEndpoints, Set<EndpointId> applicationEndpoints, Status status) {
        this.id = Objects.requireNonNull(id, "id must be non-null");
        this.canonicalName = Objects.requireNonNull(canonicalName, "canonicalName must be non-null");
        this.ipAddress = Objects.requireNonNull(ipAddress, "ipAddress must be non-null");
        this.dnsZone = Objects.requireNonNull(dnsZone, "dnsZone must be non-null");
        this.instanceEndpoints = ImmutableSortedSet.copyOf(Objects.requireNonNull(instanceEndpoints, "instanceEndpoints must be non-null"));
        this.applicationEndpoints = ImmutableSortedSet.copyOf(Objects.requireNonNull(applicationEndpoints, "applicationEndpoints must be non-null"));
        this.status = Objects.requireNonNull(status, "status must be non-null");

        if (canonicalName.isEmpty() == ipAddress.isEmpty())
            throw new IllegalArgumentException("Exactly 1 of canonicalName=%s and ipAddress=%s must be set".formatted(
                    canonicalName.map(DomainName::value).orElse("<empty>"), ipAddress.orElse("<empty>")));
    }

    /** The ID of this */
    public RoutingPolicyId id() {
        return id;
    }

    /** The canonical name for the load balancer this applies to (rhs of a CNAME or ALIAS record) */
    public Optional<DomainName> canonicalName() {
        return canonicalName;
    }

    /** The IP address for the load balancer this applies to (rhs of an A or DIRECT record) */
    public Optional<String> ipAddress() {
        return ipAddress;
    }

    /** DNS zone for the load balancer this applies to, if any. Used when creating ALIAS records. */
    public Optional<String> dnsZone() {
        return dnsZone;
    }

    /** The instance-level endpoints this participates in */
    public Set<EndpointId> instanceEndpoints() {
        return instanceEndpoints;
    }

    /** The application-level endpoints  this participates in */
    public Set<EndpointId> applicationEndpoints() {
        return applicationEndpoints;
    }

    /** Returns the status of this */
    public Status status() {
        return status;
    }

    /** Returns whether this policy applies to given deployment */
    public boolean appliesTo(DeploymentId deployment) {
        return id.owner().equals(deployment.applicationId()) &&
               id.zone().equals(deployment.zoneId());
    }

    /** Returns a copy of this with status set to given status */
    public RoutingPolicy with(Status status) {
        return new RoutingPolicy(id, canonicalName, ipAddress, dnsZone, instanceEndpoints, applicationEndpoints, status);
    }

    /** Returns the zone endpoints of this */
    public List<Endpoint> zoneEndpointsIn(SystemName system, RoutingMethod routingMethod) {
        DeploymentId deployment = new DeploymentId(id.owner(), id.zone());
        return List.of(endpoint(routingMethod).target(id.cluster(), deployment).in(system));
    }

    /** Returns the region endpoint of this */
    public Endpoint regionEndpointIn(SystemName system, RoutingMethod routingMethod) {
        return endpoint(routingMethod).targetRegion(id.cluster(), id.zone()).in(system);
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        RoutingPolicy that = (RoutingPolicy) o;
        return id.equals(that.id);
    }

    @Override
    public int hashCode() {
        return Objects.hash(id);
    }

    @Override
    public String toString() {
        return Text.format("%s [instance endpoints: %s, application endpoints: %s%s], %s owned by %s, in %s", canonicalName,
                           instanceEndpoints, applicationEndpoints,
                           dnsZone.map(z -> ", DNS zone: " + z).orElse(""), id.cluster(), id.owner().toShortString(),
                           id.zone().value());
    }

    private Endpoint.EndpointBuilder endpoint(RoutingMethod routingMethod) {
        return Endpoint.of(id.owner())
                       .on(Port.fromRoutingMethod(routingMethod))
                       .routingMethod(routingMethod);
    }

    /** The status of a routing policy */
    public record Status(boolean active, RoutingStatus routingStatus) {

        /** DO NOT USE. Public for serialization purposes */
        public Status {
            Objects.requireNonNull(routingStatus, "routingStatus must be non-null");
        }

        /** Returns whether this is considered active according to the load balancer status */
        public boolean isActive() {
            return active;
        }

        /** Return status of routing */
        public RoutingStatus routingStatus() {
            return routingStatus;
        }

        /** Returns a copy of this with routing status changed */
        public Status with(RoutingStatus routingStatus) {
            return new Status(active, routingStatus);
        }

    }

}