mirror of https://github.com/halo-dev/halo
refactor: initialize default config value by settings after plugin installation (#3161)
#### What type of PR is this? /kind improvement /area core /milestone 2.2.x #### What this PR does / why we need it: 插件安装后根据配置的 settingName 读取默认值创建 ConfigMap 如果配置了 settingName 而没有配置 configMapName 则会自动填充为 uuid 并创建一个 ConfigMap #### Which issue(s) this PR fixes: Fixes #3160 #### Special notes for your reviewer: /cc @halo-dev/sig-halo #### Does this PR introduce a user-facing change? ```release-note 插件安装后自动初始化 Setting 的默认值 ```pull/3142/head^2
parent
2241c08371
commit
ca4e93d4bb
|
@ -18,9 +18,14 @@ import org.pf4j.PluginRuntimeException;
|
||||||
import org.pf4j.PluginState;
|
import org.pf4j.PluginState;
|
||||||
import org.pf4j.PluginWrapper;
|
import org.pf4j.PluginWrapper;
|
||||||
import org.pf4j.RuntimeMode;
|
import org.pf4j.RuntimeMode;
|
||||||
|
import org.springframework.context.ApplicationEventPublisher;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
import org.springframework.util.Assert;
|
||||||
import run.halo.app.core.extension.Plugin;
|
import run.halo.app.core.extension.Plugin;
|
||||||
import run.halo.app.core.extension.ReverseProxy;
|
import run.halo.app.core.extension.ReverseProxy;
|
||||||
|
import run.halo.app.core.extension.Setting;
|
||||||
|
import run.halo.app.core.extension.theme.SettingUtils;
|
||||||
|
import run.halo.app.extension.ConfigMap;
|
||||||
import run.halo.app.extension.ExtensionClient;
|
import run.halo.app.extension.ExtensionClient;
|
||||||
import run.halo.app.extension.Metadata;
|
import run.halo.app.extension.Metadata;
|
||||||
import run.halo.app.extension.controller.Controller;
|
import run.halo.app.extension.controller.Controller;
|
||||||
|
@ -32,6 +37,7 @@ import run.halo.app.infra.utils.PathUtils;
|
||||||
import run.halo.app.plugin.HaloPluginManager;
|
import run.halo.app.plugin.HaloPluginManager;
|
||||||
import run.halo.app.plugin.PluginConst;
|
import run.halo.app.plugin.PluginConst;
|
||||||
import run.halo.app.plugin.PluginStartingError;
|
import run.halo.app.plugin.PluginStartingError;
|
||||||
|
import run.halo.app.plugin.event.PluginCreatedEvent;
|
||||||
import run.halo.app.plugin.resources.BundleResourceUtils;
|
import run.halo.app.plugin.resources.BundleResourceUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -47,6 +53,7 @@ public class PluginReconciler implements Reconciler<Request> {
|
||||||
private static final String FINALIZER_NAME = "plugin-protection";
|
private static final String FINALIZER_NAME = "plugin-protection";
|
||||||
private final ExtensionClient client;
|
private final ExtensionClient client;
|
||||||
private final HaloPluginManager haloPluginManager;
|
private final HaloPluginManager haloPluginManager;
|
||||||
|
private final ApplicationEventPublisher eventPublisher;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Result reconcile(Request request) {
|
public Result reconcile(Request request) {
|
||||||
|
@ -146,6 +153,8 @@ public class PluginReconciler implements Reconciler<Request> {
|
||||||
plugin.statusNonNull().setLastStartTime(Instant.now());
|
plugin.statusNonNull().setLastStartTime(Instant.now());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
settingDefaultConfig(plugin);
|
||||||
|
|
||||||
Plugin.PluginStatus status = plugin.statusNonNull();
|
Plugin.PluginStatus status = plugin.statusNonNull();
|
||||||
String jsBundlePath =
|
String jsBundlePath =
|
||||||
BundleResourceUtils.getJsBundlePath(haloPluginManager, pluginName);
|
BundleResourceUtils.getJsBundlePath(haloPluginManager, pluginName);
|
||||||
|
@ -257,6 +266,9 @@ public class PluginReconciler implements Reconciler<Request> {
|
||||||
}
|
}
|
||||||
newFinalizers.add(FINALIZER_NAME);
|
newFinalizers.add(FINALIZER_NAME);
|
||||||
client.update(plugin);
|
client.update(plugin);
|
||||||
|
|
||||||
|
eventPublisher.publishEvent(
|
||||||
|
new PluginCreatedEvent(this, plugin.getMetadata().getName()));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -321,6 +333,35 @@ public class PluginReconciler implements Reconciler<Request> {
|
||||||
}, () -> client.create(reverseProxy));
|
}, () -> client.create(reverseProxy));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void settingDefaultConfig(Plugin plugin) {
|
||||||
|
Assert.notNull(plugin, "The plugin must not be null.");
|
||||||
|
if (StringUtils.isBlank(plugin.getSpec().getSettingName())) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final String configMapNameToUse = plugin.getSpec().getConfigMapName();
|
||||||
|
if (StringUtils.isBlank(configMapNameToUse)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean existConfigMap = client.fetch(ConfigMap.class, configMapNameToUse)
|
||||||
|
.isPresent();
|
||||||
|
if (existConfigMap) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
client.fetch(Setting.class, plugin.getSpec().getSettingName())
|
||||||
|
.ifPresent(setting -> {
|
||||||
|
var data = SettingUtils.settingDefinedDefaultValueMap(setting);
|
||||||
|
// Create with or without default value
|
||||||
|
ConfigMap configMap = new ConfigMap();
|
||||||
|
configMap.setMetadata(new Metadata());
|
||||||
|
configMap.getMetadata().setName(configMapNameToUse);
|
||||||
|
configMap.setData(data);
|
||||||
|
client.create(configMap);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
static String initialReverseProxyName(String pluginName) {
|
static String initialReverseProxyName(String pluginName) {
|
||||||
return pluginName + "-system-generated-reverse-proxy";
|
return pluginName + "-system-generated-reverse-proxy";
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,107 @@
|
||||||
|
package run.halo.app.plugin;
|
||||||
|
|
||||||
|
import io.micrometer.common.util.StringUtils;
|
||||||
|
import java.time.Duration;
|
||||||
|
import java.time.Instant;
|
||||||
|
import java.util.UUID;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.context.SmartLifecycle;
|
||||||
|
import org.springframework.context.event.EventListener;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
import reactor.core.Exceptions;
|
||||||
|
import reactor.core.publisher.Mono;
|
||||||
|
import run.halo.app.core.extension.Plugin;
|
||||||
|
import run.halo.app.extension.ReactiveExtensionClient;
|
||||||
|
import run.halo.app.extension.controller.Controller;
|
||||||
|
import run.halo.app.extension.controller.ControllerBuilder;
|
||||||
|
import run.halo.app.extension.controller.DefaultController;
|
||||||
|
import run.halo.app.extension.controller.DefaultDelayQueue;
|
||||||
|
import run.halo.app.extension.controller.Reconciler;
|
||||||
|
import run.halo.app.extension.controller.RequestQueue;
|
||||||
|
import run.halo.app.plugin.event.PluginCreatedEvent;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Plugin event reconciler.
|
||||||
|
* If other plugin events need to be reconciled, consider sharing this reconciler.
|
||||||
|
*
|
||||||
|
* @author guqing
|
||||||
|
* @since 2.2.0
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@Component
|
||||||
|
public class PluginCreatedEventReconciler
|
||||||
|
implements Reconciler<PluginCreatedEvent>, SmartLifecycle {
|
||||||
|
|
||||||
|
private final RequestQueue<PluginCreatedEvent> pluginEventQueue;
|
||||||
|
|
||||||
|
private final ReactiveExtensionClient client;
|
||||||
|
|
||||||
|
private final Controller pluginEventController;
|
||||||
|
|
||||||
|
private boolean running = false;
|
||||||
|
|
||||||
|
public PluginCreatedEventReconciler(ReactiveExtensionClient client) {
|
||||||
|
this.client = client;
|
||||||
|
pluginEventQueue = new DefaultDelayQueue<>(Instant::now);
|
||||||
|
pluginEventController = this.setupWith(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Result reconcile(PluginCreatedEvent pluginCreatedEvent) {
|
||||||
|
String pluginName = pluginCreatedEvent.getPluginName();
|
||||||
|
try {
|
||||||
|
ensureConfigMapNameNotEmptyIfSettingIsNotBlank(pluginName);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
throw Exceptions.propagate(e);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Controller setupWith(ControllerBuilder builder) {
|
||||||
|
return new DefaultController<>(
|
||||||
|
this.getClass().getName(),
|
||||||
|
this,
|
||||||
|
pluginEventQueue,
|
||||||
|
null,
|
||||||
|
Duration.ofMillis(100),
|
||||||
|
Duration.ofSeconds(1000)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventListener(PluginCreatedEvent.class)
|
||||||
|
public void handlePluginCreated(PluginCreatedEvent pluginCreatedEvent) {
|
||||||
|
pluginEventQueue.addImmediately(pluginCreatedEvent);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ensureConfigMapNameNotEmptyIfSettingIsNotBlank(String pluginName)
|
||||||
|
throws InterruptedException {
|
||||||
|
client.fetch(Plugin.class, pluginName)
|
||||||
|
.switchIfEmpty(Mono.error(new IllegalStateException("Plugin not found: " + pluginName)))
|
||||||
|
.filter(plugin -> StringUtils.isNotBlank(plugin.getSpec().getSettingName()))
|
||||||
|
.filter(plugin -> StringUtils.isBlank(plugin.getSpec().getConfigMapName()))
|
||||||
|
.doOnNext(plugin -> {
|
||||||
|
// has settingName value but configMapName not configured
|
||||||
|
plugin.getSpec().setConfigMapName(UUID.randomUUID().toString());
|
||||||
|
})
|
||||||
|
.flatMap(client::update)
|
||||||
|
.block();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void start() {
|
||||||
|
pluginEventController.start();
|
||||||
|
running = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void stop() {
|
||||||
|
running = false;
|
||||||
|
pluginEventController.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isRunning() {
|
||||||
|
return running;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
package run.halo.app.plugin.event;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
import org.springframework.context.ApplicationEvent;
|
||||||
|
import run.halo.app.core.extension.Plugin;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The {@link Plugin} created event.
|
||||||
|
*
|
||||||
|
* @author guqing
|
||||||
|
* @since 2.0.0
|
||||||
|
*/
|
||||||
|
@Getter
|
||||||
|
public class PluginCreatedEvent extends ApplicationEvent {
|
||||||
|
private final String pluginName;
|
||||||
|
|
||||||
|
public PluginCreatedEvent(Object source, String pluginName) {
|
||||||
|
super(source);
|
||||||
|
this.pluginName = pluginName;
|
||||||
|
}
|
||||||
|
}
|
|
@ -28,6 +28,7 @@ import org.pf4j.PluginState;
|
||||||
import org.pf4j.PluginWrapper;
|
import org.pf4j.PluginWrapper;
|
||||||
import org.pf4j.RuntimeMode;
|
import org.pf4j.RuntimeMode;
|
||||||
import org.skyscreamer.jsonassert.JSONAssert;
|
import org.skyscreamer.jsonassert.JSONAssert;
|
||||||
|
import org.springframework.context.ApplicationEventPublisher;
|
||||||
import run.halo.app.core.extension.Plugin;
|
import run.halo.app.core.extension.Plugin;
|
||||||
import run.halo.app.core.extension.ReverseProxy;
|
import run.halo.app.core.extension.ReverseProxy;
|
||||||
import run.halo.app.extension.ExtensionClient;
|
import run.halo.app.extension.ExtensionClient;
|
||||||
|
@ -55,11 +56,14 @@ class PluginReconcilerTest {
|
||||||
@Mock
|
@Mock
|
||||||
PluginWrapper pluginWrapper;
|
PluginWrapper pluginWrapper;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
ApplicationEventPublisher eventPublisher;
|
||||||
|
|
||||||
PluginReconciler pluginReconciler;
|
PluginReconciler pluginReconciler;
|
||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
void setUp() {
|
void setUp() {
|
||||||
pluginReconciler = new PluginReconciler(extensionClient, haloPluginManager);
|
pluginReconciler = new PluginReconciler(extensionClient, haloPluginManager, eventPublisher);
|
||||||
lenient().when(haloPluginManager.validatePluginVersion(any())).thenReturn(true);
|
lenient().when(haloPluginManager.validatePluginVersion(any())).thenReturn(true);
|
||||||
lenient().when(haloPluginManager.getSystemVersion()).thenReturn("0.0.0");
|
lenient().when(haloPluginManager.getSystemVersion()).thenReturn("0.0.0");
|
||||||
lenient().when(haloPluginManager.getPlugin(any())).thenReturn(pluginWrapper);
|
lenient().when(haloPluginManager.getPlugin(any())).thenReturn(pluginWrapper);
|
||||||
|
|
Loading…
Reference in New Issue