1 /*
2  * Copyright 2021 The gRPC Authors
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package io.grpc.xds;
18 
19 import com.google.auto.value.AutoValue;
20 import com.google.common.collect.ImmutableMap;
21 import com.google.protobuf.Any;
22 import com.google.protobuf.InvalidProtocolBufferException;
23 import com.google.protobuf.Message;
24 import io.grpc.internal.JsonParser;
25 import io.grpc.internal.JsonUtil;
26 import java.io.IOException;
27 import java.util.Map;
28 
29 /** The ClusterSpecifierPlugin for RouteLookup policy. */
30 final class RouteLookupServiceClusterSpecifierPlugin implements ClusterSpecifierPlugin {
31 
32   static final RouteLookupServiceClusterSpecifierPlugin INSTANCE =
33       new RouteLookupServiceClusterSpecifierPlugin();
34 
35   private static final String TYPE_URL =
36       "type.googleapis.com/grpc.lookup.v1.RouteLookupClusterSpecifier";
37 
RouteLookupServiceClusterSpecifierPlugin()38   private RouteLookupServiceClusterSpecifierPlugin() {}
39 
40   @Override
typeUrls()41   public String[] typeUrls() {
42     return new String[] {
43         TYPE_URL,
44     };
45   }
46 
47   @Override
48   @SuppressWarnings("unchecked")
parsePlugin(Message rawProtoMessage)49   public ConfigOrError<RlsPluginConfig> parsePlugin(Message rawProtoMessage) {
50     if (!(rawProtoMessage instanceof Any)) {
51       return ConfigOrError.fromError("Invalid config type: " + rawProtoMessage.getClass());
52     }
53     try {
54       Any anyMessage = (Any) rawProtoMessage;
55       Class<? extends Message> protoClass;
56       try {
57         protoClass =
58             (Class<? extends Message>)
59                 Class.forName("io.grpc.lookup.v1.RouteLookupClusterSpecifier");
60       } catch (ClassNotFoundException e) {
61         return ConfigOrError.fromError("Dependency for 'io.grpc:grpc-rls' is missing: " + e);
62       }
63       Message configProto;
64       try {
65         configProto = anyMessage.unpack(protoClass);
66       } catch (InvalidProtocolBufferException e) {
67         return ConfigOrError.fromError("Invalid proto: " + e);
68       }
69       String jsonString = MessagePrinter.print(configProto);
70       try {
71         Map<String, ?> jsonMap = (Map<String, ?>) JsonParser.parse(jsonString);
72         Map<String, ?> config = JsonUtil.getObject(jsonMap, "routeLookupConfig");
73         return ConfigOrError.fromConfig(RlsPluginConfig.create(config));
74       } catch (IOException e) {
75         return ConfigOrError.fromError(
76             "Unable to parse RouteLookupClusterSpecifier: " + jsonString);
77       }
78     } catch (RuntimeException e) {
79       return ConfigOrError.fromError("Error parsing RouteLookupConfig: " + e);
80     }
81   }
82 
83   @AutoValue
84   abstract static class RlsPluginConfig implements PluginConfig {
85 
config()86     abstract ImmutableMap<String, ?> config();
87 
create(Map<String, ?> config)88     static RlsPluginConfig create(Map<String, ?> config) {
89       return new AutoValue_RouteLookupServiceClusterSpecifierPlugin_RlsPluginConfig(
90           ImmutableMap.copyOf(config));
91     }
92 
93     @Override
typeUrl()94     public String typeUrl() {
95       return TYPE_URL;
96     }
97   }
98 }
99