Skip to content

Commit

Permalink
Add groovy config change listener for servers.
Browse files Browse the repository at this point in the history
  • Loading branch information
abhishekbafna committed Feb 10, 2025
1 parent 9c8adeb commit 9812e00
Show file tree
Hide file tree
Showing 5 changed files with 130 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -215,8 +215,7 @@ public SuccessResponse setGroovyStaticAnalysisConfig(String body) throws Excepti
_pinotHelixResourceManager.getHelixClusterName()).build();
Map<String, String> properties = new TreeMap<>();
GroovyStaticAnalyzerConfig groovyConfig = GroovyStaticAnalyzerConfig.fromJson(body);
properties.put(CommonConstants.GROOVY_STATIC_ANALYZER_CONFIG,
groovyConfig == null ? null : groovyConfig.toJson());
properties.put(CommonConstants.GROOVY_STATIC_ANALYZER_CONFIG, groovyConfig.toJson());
admin.setConfig(configScope, properties);
GroovyFunctionEvaluator.setConfig(groovyConfig);
return new SuccessResponse("Updated Groovy Static Analyzer config.");
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/**
* 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.pinot.segment.local.function;

import java.util.Map;
import java.util.Set;
import org.apache.pinot.spi.config.provider.PinotClusterConfigChangeListener;
import org.apache.pinot.spi.utils.CommonConstants;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;


public class GroovyConfigChangeListener implements PinotClusterConfigChangeListener {
private static final Logger LOGGER = LoggerFactory.getLogger(GroovyConfigChangeListener.class);

@Override
public void onChange(Set<String> changedConfigs, Map<String, String> clusterConfigs) {
if (changedConfigs.contains(CommonConstants.GROOVY_STATIC_ANALYZER_CONFIG)) {
try {
String configJson = clusterConfigs.get(CommonConstants.GROOVY_STATIC_ANALYZER_CONFIG);
LOGGER.info("Updating Groovy Static Analyzer configuration with latest config: {}", configJson);
GroovyStaticAnalyzerConfig config = GroovyStaticAnalyzerConfig.fromJson(configJson);
GroovyFunctionEvaluator.setConfig(config);
} catch (Exception e) {
LOGGER.error("Failed to update Groovy Static Analyzer configuration", e);
}
} else {
LOGGER.debug("Skip updating Groovy Static Analyzer configuration.");
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,7 @@ public String toString() {
return _expression;
}

private static GroovyStaticAnalyzerConfig getConfig() {
public static GroovyStaticAnalyzerConfig getConfig() {
synchronized (GroovyFunctionEvaluator.class) {
return _config;
}
Expand Down Expand Up @@ -243,7 +243,6 @@ public static void setConfig(GroovyStaticAnalyzerConfig config)
// object and whenever a GroovyFunctionEvaluator is created it makes a local copy of that address by reading
// the static variable atomically. So, if the static config variable changes it will have no effect on
// any currently running evaluators.
LOGGER.info("Updating Groovy Static Analyzer: {}", config.toJson());
_config = config;
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
/**
* 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.pinot.segment.local.function;

import com.fasterxml.jackson.core.JsonProcessingException;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.pinot.spi.utils.CommonConstants;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;

import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertNotNull;
import static org.testng.Assert.assertNull;


public class GroovyConfigChangeListenerTest {

@BeforeMethod
public void setUp()
throws JsonProcessingException {
assertNull(GroovyFunctionEvaluator.getConfig());
GroovyStaticAnalyzerConfig groovyConfig = new GroovyStaticAnalyzerConfig(
List.of("java.lang.Math"),
List.of("java.lang.Math"),
List.of("java.lang.Math"),
List.of("invoke", "execute"),
false);
GroovyFunctionEvaluator.setConfig(groovyConfig);
}

@AfterMethod
public void tearDown()
throws JsonProcessingException {
GroovyFunctionEvaluator.setConfig(null);
}

@Test
public void testGroovyConfigIsUpdated() {
Set<String> changedConfigs = Set.of(CommonConstants.GROOVY_STATIC_ANALYZER_CONFIG);
String updatedConfig = "{\"allowedReceivers\" : [\"java.lang.String\"]}";
Map<String, String> clusterConfigs = Map.of(CommonConstants.GROOVY_STATIC_ANALYZER_CONFIG, updatedConfig);
assertEquals(GroovyFunctionEvaluator.getConfig().getAllowedReceivers(), List.of("java.lang.Math"));
GroovyConfigChangeListener groovyConfigChangeListener = new GroovyConfigChangeListener();
groovyConfigChangeListener.onChange(changedConfigs, clusterConfigs);
assertNotNull(GroovyFunctionEvaluator.getConfig());
assertEquals(GroovyFunctionEvaluator.getConfig().getAllowedReceivers(), List.of("java.lang.String"));
}

@Test
public void testGroovyConfigIsUnchanged() {
Set<String> changedConfigs = Set.of("random.test.key");
Map<String, String> clusterConfigs = Map.of("random.test.key", "random.test.value");
GroovyConfigChangeListener groovyConfigChangeListener = new GroovyConfigChangeListener();
groovyConfigChangeListener.onChange(changedConfigs, clusterConfigs);
assertNotNull(GroovyFunctionEvaluator.getConfig());
assertEquals(GroovyFunctionEvaluator.getConfig().getAllowedReceivers(), List.of("java.lang.Math"));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@
import org.apache.pinot.core.query.scheduler.resources.ResourceManager;
import org.apache.pinot.core.transport.ListenerConfig;
import org.apache.pinot.core.util.ListenerConfigUtil;
import org.apache.pinot.segment.local.function.GroovyConfigChangeListener;
import org.apache.pinot.segment.local.function.GroovyFunctionEvaluator;
import org.apache.pinot.segment.local.realtime.impl.invertedindex.RealtimeLuceneIndexRefreshManager;
import org.apache.pinot.segment.local.realtime.impl.invertedindex.RealtimeLuceneTextIndexSearcherPool;
Expand Down Expand Up @@ -158,6 +159,7 @@ public abstract class BaseServerStarter implements ServiceStartable {
protected RealtimeLuceneIndexRefreshManager _realtimeLuceneTextIndexRefreshManager;
protected PinotEnvironmentProvider _pinotEnvironmentProvider;
protected SegmentPreprocessThrottler _segmentPreprocessThrottler;
protected GroovyConfigChangeListener _groovyConfigChangeListener;
protected DefaultClusterConfigChangeHandler _clusterConfigChangeHandler;
protected volatile boolean _isServerReadyToServeQueries = false;

Expand Down Expand Up @@ -690,6 +692,8 @@ public void start()
_adminApiApplication.start(_listenerConfigs);

// Initializing Groovy execution security
_groovyConfigChangeListener = new GroovyConfigChangeListener();
_clusterConfigChangeHandler.registerClusterConfigChangeListener(_groovyConfigChangeListener);
GroovyFunctionEvaluator.configureGroovySecurity(
_serverConf.getProperty(CommonConstants.GROOVY_STATIC_ANALYZER_CONFIG));
GroovyFunctionEvaluator.setMetrics(serverMetrics, ServerMeter.GROOVY_SECURITY_VIOLATIONS);
Expand Down

0 comments on commit 9812e00

Please sign in to comment.