aboutsummaryrefslogtreecommitdiffstats
path: root/clustercontroller-core/src/main/java/com/yahoo/vespa/clustercontroller/core/restapiv2/ClusterControllerStateRestAPI.java
blob: b4a41e49c636d24430cd3798100645ccd5689d7e (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
// Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.clustercontroller.core.restapiv2;

import com.yahoo.vespa.clustercontroller.core.RemoteClusterControllerTaskScheduler;
import com.yahoo.vespa.clustercontroller.core.restapiv2.requests.ClusterListRequest;
import com.yahoo.vespa.clustercontroller.core.restapiv2.requests.ClusterStateRequest;
import com.yahoo.vespa.clustercontroller.core.restapiv2.requests.NodeStateRequest;
import com.yahoo.vespa.clustercontroller.core.restapiv2.requests.ServiceStateRequest;
import com.yahoo.vespa.clustercontroller.core.restapiv2.requests.SetNodeStateRequest;
import com.yahoo.vespa.clustercontroller.core.restapiv2.requests.SetNodeStatesForClusterRequest;
import com.yahoo.vespa.clustercontroller.utils.staterestapi.StateRestAPI;
import com.yahoo.vespa.clustercontroller.utils.staterestapi.errors.InternalFailure;
import com.yahoo.vespa.clustercontroller.utils.staterestapi.errors.OtherMasterException;
import com.yahoo.vespa.clustercontroller.utils.staterestapi.errors.StateRestApiException;
import com.yahoo.vespa.clustercontroller.utils.staterestapi.requests.SetUnitStateRequest;
import com.yahoo.vespa.clustercontroller.utils.staterestapi.requests.UnitStateRequest;
import com.yahoo.vespa.clustercontroller.utils.staterestapi.response.SetResponse;
import com.yahoo.vespa.clustercontroller.utils.staterestapi.response.UnitResponse;
import java.util.Map;
import java.util.logging.Logger;

public class ClusterControllerStateRestAPI implements StateRestAPI {

    private static final Logger log = Logger.getLogger(ClusterControllerStateRestAPI.class.getName());

    public interface FleetControllerResolver {
        Map<String, RemoteClusterControllerTaskScheduler> getFleetControllers();
    }

    public record Socket(String hostname, int port) {
        @Override public String toString() { return hostname + ":" + port; }
    }

    private final FleetControllerResolver fleetControllerResolver;
    private final Map<Integer, Socket> clusterControllerSockets;

    public ClusterControllerStateRestAPI(FleetControllerResolver resolver,
                                         Map<Integer, Socket> clusterControllerSockets)
    {
        fleetControllerResolver = resolver;
        this.clusterControllerSockets = clusterControllerSockets;
    }

    @Override
    public UnitResponse getState(final UnitStateRequest request) throws StateRestApiException {
        log.finest("Got getState() request");
        UnitPathResolver<UnitResponse> resolver = new UnitPathResolver<>(fleetControllerResolver.getFleetControllers());
        Request<? extends UnitResponse> req = resolver.visit(
                request.getUnitPath(), new UnitPathResolver.Visitor<>()
        {
            @Override
            public Request<? extends UnitResponse> visitGlobal() {
                return new ClusterListRequest(request.getRecursiveLevels(), fleetControllerResolver);
            }
            @Override
            public Request<? extends UnitResponse> visitCluster(Id.Cluster id)  {
                return new ClusterStateRequest(id, request.getRecursiveLevels());
            }
            @Override
            public Request<? extends UnitResponse> visitService(Id.Service id)  {
                return new ServiceStateRequest(id, request.getRecursiveLevels());
            }
            @Override
            public Request<? extends UnitResponse> visitNode(Id.Node id)  {
                return new NodeStateRequest(id);
            }
        });
        if (req instanceof ClusterListRequest) {
            log.fine("Got cluster list request");
            req.doRemoteFleetControllerTask(null);
            req.notifyCompleted();
            log.finest("Completed processing cluster list request");
        } else {
            log.fine("Scheduling state request: " + req.getClass().toString());
            resolver.resolveFleetController(request.getUnitPath()).schedule(req);
            log.finest("Scheduled state request: " + req.getClass());
            req.waitForCompletion();
            log.finest("Completed processing state request: " + req.getClass());
        }
        try {
            return req.getResult();
        } catch (OtherMasterIndexException e) {
            createAndThrowOtherMasterException(e.getMasterIndex());
            throw new RuntimeException("Should not get here");
        }
    }

    @Override
    public SetResponse setUnitState(final SetUnitStateRequest request) throws StateRestApiException {
        UnitPathResolver<SetResponse> resolver = new UnitPathResolver<>(fleetControllerResolver.getFleetControllers());
        Request<? extends SetResponse> req = resolver.visit(request.getUnitPath(),
                new UnitPathResolver.AbstractVisitor<>(request.getUnitPath(),
                                                       "State can only be set at cluster or node level")
        {
            @Override
            public Request<? extends SetResponse> visitCluster(Id.Cluster id) {
                return new SetNodeStatesForClusterRequest(request);
            }
            @Override
            public Request<? extends SetResponse> visitNode(Id.Node id) {
                return new SetNodeStateRequest(id, request);
            }
        });
        resolver.resolveFleetController(request.getUnitPath()).schedule(req);
        req.waitForCompletion();
        try{
            return req.getResult();
        } catch (OtherMasterIndexException e) {
            createAndThrowOtherMasterException(e.getMasterIndex());
            throw new RuntimeException("Should not get here");
        }
    }

    // Will always throw an exception.
    private void createAndThrowOtherMasterException(int master) throws StateRestApiException {
        Socket s = clusterControllerSockets.get(master);
        // TODO: Consider changing return status code of this call to 503.
        if (s == null) throw new InternalFailure(
                "Cannot create redirect response to master at index " + master
              + ", as we failed to get correct config to detect running cluster controllers.");
        throw new OtherMasterException(s.hostname, s.port);
    }
}