Skip to content
This repository has been archived by the owner on Aug 2, 2022. It is now read-only.

Commit

Permalink
Common changes needed to support dynamic en/disabling of config overr…
Browse files Browse the repository at this point in the history
…ides (#294)

* Common changes needed to support dynamic en/disabling of config overrides

* Separate serialize/deserialize methods into own helper

* Add licence header to new files

* Update licence year to 2020

* Add javadoc explaining one element array

* Synchronize serialize and deserialize methods
  • Loading branch information
ktkrg authored Jul 29, 2020
1 parent d61da52 commit f1c0f03
Show file tree
Hide file tree
Showing 13 changed files with 354 additions and 8 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
/*
* Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* or in the "license" file accompanying this file. This file 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 com.amazon.opendistro.elasticsearch.performanceanalyzer.config.overrides;

import java.util.ArrayList;
import java.util.List;

/**
* POJO for config overrides. The class contains two sets of overrides, one for enabling and one
* for disabling.
*/
public class ConfigOverrides {
private Overrides enable;
private Overrides disable;

public ConfigOverrides() {
this.enable = new Overrides();
this.disable = new Overrides();
}

public Overrides getEnable() {
return enable;
}

public void setEnable(Overrides enable) {
this.enable = enable;
}

public Overrides getDisable() {
return disable;
}

public void setDisable(Overrides disable) {
this.disable = disable;
}

/**
* Class containing the overridable attributes of the system. Currently, overriding the
* enabled/disabled state for RCAs, deciders, and actions are supported. More attributes can
* be added as needed.
*/
public static class Overrides {
private List<String> rcas;
private List<String> deciders;
private List<String> actions;

public Overrides() {
this.rcas = new ArrayList<>();
this.deciders = new ArrayList<>();
this.actions = new ArrayList<>();
}

public List<String> getRcas() {
return rcas;
}

public void setRcas(List<String> rcas) {
this.rcas = rcas;
}

public List<String> getDeciders() {
return deciders;
}

public void setDeciders(List<String> deciders) {
this.deciders = deciders;
}

public List<String> getActions() {
return actions;
}

public void setActions(List<String> actions) {
this.actions = actions;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
/*
* Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* or in the "license" file accompanying this file. This file 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 com.amazon.opendistro.elasticsearch.performanceanalyzer.config.overrides;

import com.amazon.opendistro.elasticsearch.performanceanalyzer.util.JsonConverter;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.IOException;
import java.security.AccessController;
import java.security.PrivilegedAction;

/**
* Class that helps with operations concerning {@link ConfigOverrides}s
*/
public class ConfigOverridesHelper {

private static final ObjectMapper MAPPER = new ObjectMapper();

/**
* Serializes a {@link ConfigOverrides} instance to its JSON representation.
*
* @param overrides The {@link ConfigOverrides} instance.
* @return String in JSON format representing the serialized equivalent.
* @throws IOException if conversion runs into an IOException.
*/
public static synchronized String serialize(final ConfigOverrides overrides) throws IOException {
// We can't use a local variable to set the exception generated inside the lambda as the
// local variable is not effectively final(because we'll end up mutating the reference).
// In order to fish the exception out, we need to create a wrapper and set the exception
// there instead for the caller to get the value.
final IOException[] exception = new IOException[1];
final String serializedOverrides = AccessController.doPrivileged((PrivilegedAction<String>) () -> {
try {
return MAPPER.writeValueAsString(overrides);
} catch (IOException e) {
exception[0] = e;
}
return "";
});

if (serializedOverrides.isEmpty() && exception[0] != null) {
throw exception[0];
}

return serializedOverrides;
}

/**
* Deserializes a JSON representation of the config overrides into a {@link ConfigOverrides} instance.
*
* @param overrides The JSON string representing config overrides.
* @return A {@link ConfigOverrides} instance if the JSON is valid.
* @throws IOException if conversion runs into an IOException.
*/
public static synchronized ConfigOverrides deserialize(final String overrides) throws IOException {
// We can't use a local variable to set the exception generated inside the lambda as the
// local variable is not effectively final(because we'll end up mutating the reference).
// In order to fish the exception out, we need to create a wrapper and set the exception
// there instead for the caller to get the value.
final IOException[] exception = new IOException[1];
final ConfigOverrides configOverrides = AccessController.doPrivileged((PrivilegedAction<ConfigOverrides>) () -> {
try {
return MAPPER.readValue(overrides, ConfigOverrides.class);
} catch (IOException ioe) {
exception[0] = ioe;
}
return null;
});

if (configOverrides == null && exception[0] != null) {
// re throw the exception that was consumed while deserializing.
throw exception[0];
}

return configOverrides;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/*
* Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* or in the "license" file accompanying this file. This file 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 com.amazon.opendistro.elasticsearch.performanceanalyzer.config.overrides;

/**
* Class responsible for holding the latest config overrides across the cluster.
*/
public class ConfigOverridesWrapper {

private volatile ConfigOverrides currentClusterConfigOverrides;
private volatile long lastUpdatedTimestamp;

public ConfigOverrides getCurrentClusterConfigOverrides() {
return currentClusterConfigOverrides;
}

/**
* Sets a new ConfigOverrides instance as the current cluster config overrides instance.
*
* @param configOverrides the ConfigOverrides instance.
*/
public void setCurrentClusterConfigOverrides(final ConfigOverrides configOverrides) {
this.currentClusterConfigOverrides = configOverrides;
}

public long getLastUpdatedTimestamp() {
return lastUpdatedTimestamp;
}

public void setLastUpdatedTimestamp(long lastUpdatedTimestamp) {
this.lastUpdatedTimestamp = lastUpdatedTimestamp;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -62,27 +62,35 @@ public void finalizeProcessing() {}
@Override
public void processEvent(Event event) {
String[] lines = event.value.split(System.lineSeparator());
if (lines.length < 2) {
// We expect at-least 2 lines as the first line is always timestamp
if (lines.length < 4) {
// We expect at-least 4 lines as the first line is always timestamp,
// the second line is the list of overridden rca conf values,
// the third line is the timestamp of when the last override was set,
// and there must be at least one ElasticSearch node in a cluster.
LOG.error(
"ClusterDetails contain less items than expected. " + "Expected 2, found: {}",
"ClusterDetails contain less items than expected. " + "Expected 4, found: {}",
event.value);
return;
}

// An example node_metrics data is something like this for a two node cluster:
// {"current_time":1566414001749}
// {"overrides": {"enabled": {}, "disabled": {}}
// {"lastOverrideTimestamp":1566414001749}
// {"ID":"4sqG_APMQuaQwEW17_6zwg","HOST_ADDRESS":"10.212.73.121"}
// {"ID":"OVH94mKXT5ibeqvDoAyTeg","HOST_ADDRESS":"10.212.78.83"}
//
// The line 0 is timestamp that can be skipped. So we allocated size of
// the array is one less than the list.



final List<NodeDetails> tmpNodesDetails = new ArrayList<>();

// Just to keep track of duplicate node ids.
Set<String> ids = new HashSet<>();

for (int i = 1; i < lines.length; ++i) {
for (int i = 3; i < lines.length; ++i) {
NodeDetails nodeDetails = new NodeDetails(lines[i]);

// Include nodeIds we haven't seen so far.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
/*
* Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* or in the "license" file accompanying this file. This file 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 com.amazon.opendistro.elasticsearch.performanceanalyzer.config.overrides;

import static org.junit.Assert.assertEquals;

import com.amazon.opendistro.elasticsearch.performanceanalyzer.util.JsonConverter;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collections;
import org.junit.Before;
import org.junit.Test;

public class ConfigOverridesHelperTests {
private ConfigOverridesWrapper testConfigOverridesWrapper;
private final ConfigOverrides validTestOverrides = buildValidConfigOverrides();
private final String validTestOverrideJson = JsonConverter
.writeValueAsString(validTestOverrides);

@Before
public void setUp() {
testConfigOverridesWrapper = new ConfigOverridesWrapper();
testConfigOverridesWrapper.setCurrentClusterConfigOverrides(validTestOverrides);
}

@Test
public void testSerializeSuccess() throws IOException {
String serializedOverrides = ConfigOverridesHelper.serialize(validTestOverrides);

assertEquals(validTestOverrideJson, serializedOverrides);
}

@Test
public void testDeserializeSuccess() throws IOException {
ConfigOverrides deserializedOverrides =
ConfigOverridesHelper.deserialize(validTestOverrideJson);

assertEquals(validTestOverrides.getEnable().getRcas(), deserializedOverrides.getEnable().getRcas());
assertEquals(validTestOverrides.getEnable().getDeciders(), deserializedOverrides.getEnable().getDeciders());
assertEquals(validTestOverrides.getEnable().getActions(), deserializedOverrides.getEnable().getActions());

assertEquals(validTestOverrides.getDisable().getRcas(), deserializedOverrides.getDisable().getRcas());
assertEquals(validTestOverrides.getDisable().getDeciders(), deserializedOverrides.getDisable().getDeciders());
assertEquals(validTestOverrides.getDisable().getActions(), deserializedOverrides.getDisable().getActions());
}

@Test(expected = IOException.class)
public void testDeserializeIOException() throws IOException {
String nonJsonString = "Not a JSON string.";
ConfigOverridesHelper.deserialize(nonJsonString);
}

private ConfigOverrides buildValidConfigOverrides() {
ConfigOverrides overrides = new ConfigOverrides();
overrides.getDisable().setRcas(Arrays.asList("rca1", "rca2"));
overrides.getDisable().setActions(Arrays.asList("action1", "action2"));
overrides.getEnable().setRcas(Arrays.asList("rca3", "rca4"));
overrides.getEnable().setDeciders(Collections.singletonList("decider1"));

return overrides;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import javax.swing.JSeparator;
import org.jooq.tools.json.JSONObject;
import org.junit.After;
import org.junit.Assert;
Expand Down Expand Up @@ -374,9 +375,13 @@ public void testHandlers() throws IOException {
}

private void setMyIp(String ip, AllMetrics.NodeRole nodeRole) {
final String separator = System.lineSeparator();
JSONObject jtime = new JSONObject();
jtime.put("current_time", 1566414001749L);

JSONObject jOverrides = new JSONObject();
JSONObject jOverridesTimeStamp = new JSONObject();

JSONObject jNode = new JSONObject();
jNode.put(AllMetrics.NodeDetailColumns.ID.toString(), "4sqG_APMQuaQwEW17_6zwg");
jNode.put(AllMetrics.NodeDetailColumns.HOST_ADDRESS.toString(), ip);
Expand All @@ -385,8 +390,16 @@ private void setMyIp(String ip, AllMetrics.NodeRole nodeRole) {
nodeRole == AllMetrics.NodeRole.ELECTED_MASTER ? true : false);

ClusterDetailsEventProcessor eventProcessor = new ClusterDetailsEventProcessor();
StringBuilder nodeDetails = new StringBuilder();
nodeDetails.append(jtime);
nodeDetails.append(separator);
nodeDetails.append(jOverrides);
nodeDetails.append(separator);
nodeDetails.append(jOverridesTimeStamp);
nodeDetails.append(separator);
nodeDetails.append(jNode.toString());
eventProcessor.processEvent(
new Event("", jtime.toString() + System.lineSeparator() + jNode.toString(), 0));
new Event("", nodeDetails.toString(), 0));
rcaController.getAppContext().setClusterDetailsEventProcessor(eventProcessor);
}

Expand Down
Loading

0 comments on commit f1c0f03

Please sign in to comment.