Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -1245,13 +1245,14 @@ private ObjectNode findByNameInArray(final ArrayNode groupsArray, final String g
private String resolveFolderUidByTitle(final String title) {
if (title == null || title.isBlank()) return null;
try {
final String url = baseUrl + "/api/search?type=dash-folder&query="
final String url = baseUrlNow() + "/api/search?type=dash-folder&query="
+ java.net.URLEncoder.encode(title, java.nio.charset.StandardCharsets.UTF_8);
HttpRequest.Builder rb = HttpRequest.newBuilder()
.uri(URI.create(url))
.timeout(java.time.Duration.ofMillis(readTimeoutMs))
.header("Accept", "application/json");
if (bearer != null) rb.header("Authorization", "Bearer " + bearer);
final String b = bearerNow();
if (b != null) rb.header("Authorization", "Bearer " + b);

final java.net.http.HttpResponse<String> res =
http.send(rb.GET().build(), java.net.http.HttpResponse.BodyHandlers.ofString());
Expand Down Expand Up @@ -1481,17 +1482,19 @@ private String resolveNamespaceByGroupName(final String groupName) {

private RulerRulesResponse.Group fetchRulerGroupDirect(final String namespace, final String groupName) {
final String encodedFolder = URLEncoder.encode(namespace, StandardCharsets.UTF_8);
final String base = baseUrlNow();
final String[] urls = new String[] {
baseUrl + "/api/ruler/grafana/api/v1/rules/" + encodedFolder + "?subtype=cortex",
baseUrl + "/api/ruler/grafana/api/v1/rules/" + encodedFolder
base + "/api/ruler/grafana/api/v1/rules/" + encodedFolder + "?subtype=cortex",
base + "/api/ruler/grafana/api/v1/rules/" + encodedFolder
};
for (String url : urls) {
try {
final HttpRequest.Builder rb = HttpRequest.newBuilder()
.uri(URI.create(url))
.timeout(Duration.ofMillis(readTimeoutMs))
.header("Accept", "application/json");
if (bearer != null) rb.header("Authorization", "Bearer " + bearer);
final String b = bearerNow();
if (b != null) rb.header("Authorization", "Bearer " + b);
final HttpResponse<String> res = http.send(rb.GET().build(), HttpResponse.BodyHandlers.ofString());
if (res.statusCode() < 200 || res.statusCode() >= 300) {
LOG.warn("[Ruler] GET " + url + " -> " + res.statusCode());
Expand Down Expand Up @@ -1559,18 +1562,19 @@ private boolean trySend(final String url,
final int sc = res.statusCode();
if (sc >= 200 && sc < 300) {
// 200/202 모두 성공 처리
LOG.info("[Ruler][send] {} {} -> {}");
LOG.info("[Ruler][send] " + method + " " + url + " -> " + sc);
return true;
}
LOG.warn("[Ruler][send] {} {} -> {} (preview: {})"
);
LOG.warn("[Ruler][send] " + method + " " + url + " -> " + sc
+ " (preview: " + trimBody(res.body(), 600) + ")");
return false;

} catch (IllegalArgumentException iae) { // URI.create 등
LOG.warn("[Ruler][send] invalid URI for {}: {} ({})");
LOG.warn("[Ruler][send] invalid URI for " + method + ": " + url
+ " (" + iae.getMessage() + ")");
return false;
} catch (Exception e) {
LOG.warn("[Ruler][send] {} {} failed: {}");
LOG.warn("[Ruler][send] " + method + " " + url + " failed: " + e.getMessage(), e);
return false;
}
}
Expand Down Expand Up @@ -1630,7 +1634,7 @@ private ObjectNode fetchRulerGroupRawFlexible(final String nsRaw,
final String nsKeyForUrl,
final String folderUid,
final String groupName) {
final String base = baseUrl; // baseUrlNow() 쓰는 구조면 그걸로 교체해도 됨
final String base = baseUrlNow();
final java.util.LinkedHashSet<String> nsSet = buildNsCandidates(nsRaw, nsKeyForUrl, folderUid);
final String encGroup = encodePathSegment(groupName);

Expand Down Expand Up @@ -1658,7 +1662,8 @@ private ObjectNode fetchRulerGroupRawFlexible(final String nsRaw,
.uri(URI.create(url))
.timeout(java.time.Duration.ofMillis(readTimeoutMs))
.header("Accept", "application/json");
if (bearer != null) rb.header("Authorization", "Bearer " + bearer);
final String b = bearerNow();
if (b != null) rb.header("Authorization", "Bearer " + b);

final HttpResponse<String> res = http.send(rb.GET().build(), HttpResponse.BodyHandlers.ofString());
final int sc = res.statusCode();
Expand Down Expand Up @@ -1740,13 +1745,13 @@ private static String textOrNull(final JsonNode n) {

private ObjectNode fetchRulerNamespaceRaw(final String nsKey) {
try {
final String url = baseUrl + "/api/ruler/grafana/api/v1/rules/" + encodePathSegment(nsKey);
final HttpRequest req = HttpRequest.newBuilder(URI.create(url))
final String url = baseUrlNow() + "/api/ruler/grafana/api/v1/rules/" + encodePathSegment(nsKey);
final HttpRequest.Builder rb = HttpRequest.newBuilder(URI.create(url))
.timeout(Duration.ofMillis(Math.max(readTimeoutMs, 10000)))
.header("Authorization", "Bearer " + bearer)
.header("Accept", "application/json")
.GET()
.build();
.header("Accept", "application/json");
final String b = bearerNow();
if (b != null) rb.header("Authorization", "Bearer " + b);
final HttpRequest req = rb.GET().build();
final HttpResponse<String> res = http.send(req, HttpResponse.BodyHandlers.ofString());

if (res.statusCode() / 100 == 2) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -627,6 +627,13 @@ public WallAlertRuleResponse updateWallAlertRuleThreshold(final UpdateWallAlertR
ok = wallApiClient.updateRuleThreshold(m.namespace, m.group, m.title, op, newThreshold, null);
}

if (!ok) {
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR,
"임계값 업데이트에 실패했습니다. Wall Ruler API 응답 로그를 확인해 주세요.");
}

invalidateRulesCache();

final WallAlertRuleResponse resp = new WallAlertRuleResponse();
resp.setObjectName("wallalertrule");
resp.setId(id != null && !id.isBlank() ? id : (m.group + ":" + m.title));
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
// 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.cloudstack.wallAlerts.client;

import java.lang.reflect.Method;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;

public class WallApiClientImplTest {

private final ObjectMapper objectMapper = new ObjectMapper();
private WallApiClientImpl client;
private Method mutateThresholdRaw;

@Before
public void setUp() throws Exception {
client = new WallApiClientImpl("http://localhost", null, 1000, 1000);
mutateThresholdRaw = WallApiClientImpl.class.getDeclaredMethod("mutateThresholdRaw",
ObjectNode.class, String.class, String.class, Double.class, Double.class);
mutateThresholdRaw.setAccessible(true);
}

@Test
public void updatesEvaluatorReferencedByCondition() throws Exception {
final ObjectNode group = groupWithData("[{\"refId\":\"C\",\"model\":{\"conditions\":["
+ "{\"evaluator\":{\"type\":\"lt\",\"params\":[60]}}]}}]");

Assert.assertTrue(mutate(group, "gt", 75.0, null));
Assert.assertEquals("gt", group.at("/rules/0/grafana_alert/data/0/model/conditions/0/evaluator/type").asText());
Assert.assertEquals(75.0,
group.at("/rules/0/grafana_alert/data/0/model/conditions/0/evaluator/params/0").asDouble(), 0.0);
}

private ObjectNode groupWithData(final String data) throws Exception {
final String group = "{\"name\":\"WALL_Every_1m\",\"rules\":[{\"title\":\"host cpu\","
+ "\"grafana_alert\":{\"title\":\"host cpu\",\"condition\":\"C\",\"data\":" + data + "}}]}";
return (ObjectNode) objectMapper.readTree(group);
}

private boolean mutate(final ObjectNode group, final String operator,
final Double threshold, final Double threshold2) throws Exception {
return (Boolean) mutateThresholdRaw.invoke(client, group, "host cpu", operator, threshold, threshold2);
}
}
8 changes: 0 additions & 8 deletions ui/src/views/infra/WallThresholdEditor.vue
Original file line number Diff line number Diff line change
Expand Up @@ -412,16 +412,8 @@ export default {
if (isRange) {
payload.threshold = lower
payload.threshold2 = upper
payload.lower = lower
payload.upper = upper
payload.thresholdUpper = upper
} else {
payload.threshold = lower
payload.lower = lower
// 상한 키들을 명시적으로 비워서 “되돌림” 방지
payload.threshold2 = null
payload.upper = null
payload.thresholdUpper = null
}

return payload
Expand Down
Loading