/
ConfigurationClient.java
168 lines (151 loc) · 6.65 KB
/
ConfigurationClient.java
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
160
161
162
163
164
165
166
167
168
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.ignite.cli.builtins.config;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.typesafe.config.ConfigFactory;
import com.typesafe.config.ConfigRenderOptions;
import java.io.IOException;
import java.io.PrintWriter;
import java.net.HttpURLConnection;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import javax.inject.Inject;
import javax.inject.Singleton;
import org.apache.ignite.cli.IgniteCliException;
import org.jetbrains.annotations.Nullable;
import picocli.CommandLine.Help.ColorScheme;
/**
* Client to get/put HOCON based configuration from/to Ignite server nodes.
*/
@Singleton
public class ConfigurationClient {
/** Url for getting configuration from REST endpoint of the node. */
private static final String GET_URL = "/management/v1/configuration/";
/** Url for patching configuration with REST endpoint of the node. */
private static final String PATCH_URL = "/management/v1/configuration/";
/** Http client. */
private final HttpClient httpClient;
/** Mapper serialize/deserialize json values during communication with node REST endpoint. */
private final ObjectMapper mapper;
/**
* Creates new configuration client.
*
* @param httpClient Http client.
*/
@Inject
public ConfigurationClient(HttpClient httpClient) {
this.httpClient = httpClient;
mapper = new ObjectMapper();
}
/**
* Gets server node configuration as a raw JSON string.
*
* @param host String representation of server node host.
* @param port Host REST port.
* @param rawHoconPath HOCON dot-delimited path of requested configuration.
* @param type Configuration type: {@code node} or {@code cluster}.
* @return JSON string with node configuration.
*/
//TODO: Fix in https://issues.apache.org/jira/browse/IGNITE-15306
public String get(
String host,
int port,
@Nullable String rawHoconPath,
String type
) {
var req = HttpRequest
.newBuilder()
.header("Content-Type", "application/json");
if (rawHoconPath == null) {
req.uri(URI.create("http://" + host + ":" + port + GET_URL + type + "/"));
} else {
req.uri(URI.create("http://" + host + ":" + port + GET_URL + type + "/" + rawHoconPath));
}
try {
HttpResponse<String> res =
httpClient.send(req.build(),
HttpResponse.BodyHandlers.ofString());
if (res.statusCode() == HttpURLConnection.HTTP_OK) {
return mapper.writerWithDefaultPrettyPrinter()
.writeValueAsString(mapper.readValue(res.body(), JsonNode.class));
} else {
throw error("Can't get configuration", res);
}
} catch (IOException | InterruptedException e) {
throw new IgniteCliException("Connection issues while trying to send http request");
}
}
/**
* Sets node configuration from JSON string with configs.
*
* @param host String representation of server node host.
* @param port Host REST port.
* @param rawHoconData Valid HOCON represented as a string.
* @param out PrintWriter for printing user messages.
* @param cs ColorScheme to enrich user messages.
* @param type Configuration type: {@code node} or {@code cluster}.
*/
//TODO: Fix in https://issues.apache.org/jira/browse/IGNITE-15306
public void set(String host, int port, String rawHoconData, PrintWriter out, ColorScheme cs, String type) {
var req = HttpRequest
.newBuilder()
.method("PATCH", HttpRequest.BodyPublishers.ofString(renderJsonFromHocon(rawHoconData)))
.header("Content-Type", "application/json")
.uri(URI.create("http://" + host + ":" + port + PATCH_URL + type + "/"))
.build();
try {
HttpResponse<String> res = httpClient.send(req, HttpResponse.BodyHandlers.ofString());
if (res.statusCode() == HttpURLConnection.HTTP_OK) {
out.println("Configuration was updated successfully.");
out.println();
out.println("Use the " + cs.commandText("ignite config get")
+ " command to view the updated configuration.");
} else {
throw error("Failed to set configuration", res);
}
} catch (IOException | InterruptedException e) {
throw new IgniteCliException("Connection issues while trying to send http request", e);
}
}
/**
* Prepares exception with message, enriched by HTTP response details.
*
* @param msg Base error message.
* @param res Http response, which cause the raising exce[tion.
* @return Exception with detailed message.
* @throws JsonProcessingException if response has incorrect error format.
*/
private IgniteCliException error(String msg, HttpResponse<String> res) throws JsonProcessingException {
var errorMsg = mapper.writerWithDefaultPrettyPrinter()
.writeValueAsString(mapper.readValue(res.body(), JsonNode.class));
return new IgniteCliException(msg + System.lineSeparator().repeat(2) + errorMsg);
}
/**
* Produces JSON representation of any valid HOCON string.
*
* @param rawHoconData HOCON string.
* @return JSON representation of HOCON string.
*/
private static String renderJsonFromHocon(String rawHoconData) {
return ConfigFactory.parseString(rawHoconData)
.root().render(ConfigRenderOptions.concise());
}
}