refactor: compatibility issues with plugins lacking loadLocation after upgrading (#3873)

#### What type of PR is this?
/kind improvement
/area core
/milestone 2.5.x

#### What this PR does / why we need it:
修复插件缺失路径信息升级后无法使用的兼容性问题

how to test it?
1. 生产模式安装插件
2. 更新插件将插件中的 status.loadLocation 和 metadata.annotations["plugin.halo.run/plugin-path"] 删除
3. 查看插件功能是否正常
#### Does this PR introduce a user-facing change?

```release-note
修复插件缺失路径信息升级后无法使用的兼容性问题
```
pull/3874/head
guqing 2023-04-28 15:40:19 +08:00 committed by GitHub
parent 0794644dbd
commit 6789d4c90f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 37 additions and 21 deletions

View File

@ -61,6 +61,7 @@ import run.halo.app.plugin.HaloPluginManager;
import run.halo.app.plugin.PluginConst; import run.halo.app.plugin.PluginConst;
import run.halo.app.plugin.PluginExtensionLoaderUtils; import run.halo.app.plugin.PluginExtensionLoaderUtils;
import run.halo.app.plugin.PluginStartingError; import run.halo.app.plugin.PluginStartingError;
import run.halo.app.plugin.PluginUtils;
import run.halo.app.plugin.YamlPluginFinder; import run.halo.app.plugin.YamlPluginFinder;
import run.halo.app.plugin.event.PluginCreatedEvent; import run.halo.app.plugin.event.PluginCreatedEvent;
import run.halo.app.plugin.resources.BundleResourceUtils; import run.halo.app.plugin.resources.BundleResourceUtils;
@ -106,6 +107,7 @@ public class PluginReconciler implements Reconciler<Request> {
}) })
.orElse(Result.doNotRetry()); .orElse(Result.doNotRetry());
} catch (DoNotRetryException e) { } catch (DoNotRetryException e) {
log.error("Failed to reconcile plugin: [{}]", request.name(), e);
persistenceFailureStatus(request.name(), e); persistenceFailureStatus(request.name(), e);
return Result.doNotRetry(); return Result.doNotRetry();
} }
@ -117,12 +119,11 @@ public class PluginReconciler implements Reconciler<Request> {
Map<String, String> annotations = nullSafeAnnotations(plugin); Map<String, String> annotations = nullSafeAnnotations(plugin);
String oldPluginPath = annotations.get(PLUGIN_PATH); String oldPluginPath = annotations.get(PLUGIN_PATH);
String pluginPath = oldPluginPath; String pluginPath = oldPluginPath;
if (StringUtils.isBlank(oldPluginPath)) { if (StringUtils.isBlank(pluginPath)) {
URI loadLocation = plugin.statusNonNull().getLoadLocation(); URI loadLocation = plugin.statusNonNull().getLoadLocation();
if (loadLocation == null) { pluginPath = Optional.ofNullable(loadLocation)
throw new DoNotRetryException("Can not determine plugin path: " + name); .map(URI::getPath)
} .orElseGet(() -> PluginUtils.generateFileName(plugin));
pluginPath = loadLocation.getPath();
} }
annotations.put(PLUGIN_PATH, pluginPath); annotations.put(PLUGIN_PATH, pluginPath);
if (!StringUtils.equals(pluginPath, oldPluginPath)) { if (!StringUtils.equals(pluginPath, oldPluginPath)) {
@ -321,14 +322,18 @@ public class PluginReconciler implements Reconciler<Request> {
client.fetch(Plugin.class, pluginName).ifPresent(plugin -> { client.fetch(Plugin.class, pluginName).ifPresent(plugin -> {
Plugin.PluginStatus status = plugin.statusNonNull(); Plugin.PluginStatus status = plugin.statusNonNull();
PluginWrapper pluginWrapper = getPluginWrapper(pluginName); PluginWrapper pluginWrapper = haloPluginManager.getPlugin(pluginName);
status.setPhase(pluginWrapper.getPluginState()); PluginState pluginState = Optional.ofNullable(pluginWrapper)
.map(PluginWrapper::getPluginState)
.orElse(PluginState.FAILED);
status.setPhase(pluginState);
Plugin.PluginStatus oldStatus = JsonUtils.deepCopy(status); Plugin.PluginStatus oldStatus = JsonUtils.deepCopy(status);
Condition condition = Condition.builder() Condition condition = Condition.builder()
.type(PluginState.FAILED.toString()) .type(PluginState.FAILED.toString())
.reason("UnexpectedState") .reason("UnexpectedState")
.message(e.getMessage()) .message(StringUtils.defaultString(e.getMessage()))
.status(ConditionStatus.FALSE) .status(ConditionStatus.FALSE)
.lastTransitionTime(Instant.now()) .lastTransitionTime(Instant.now())
.build(); .build();

View File

@ -11,7 +11,6 @@ import java.util.Map;
import java.util.Objects; import java.util.Objects;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate; import org.apache.commons.lang3.Validate;
import org.pf4j.PluginWrapper; import org.pf4j.PluginWrapper;
import org.springframework.core.io.Resource; import org.springframework.core.io.Resource;
@ -35,6 +34,7 @@ import run.halo.app.infra.utils.VersionUtils;
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.PluginProperties; import run.halo.app.plugin.PluginProperties;
import run.halo.app.plugin.PluginUtils;
import run.halo.app.plugin.YamlPluginFinder; import run.halo.app.plugin.YamlPluginFinder;
@Slf4j @Slf4j
@ -144,7 +144,7 @@ public class PluginServiceImpl implements PluginService {
private Mono<Path> copyToPluginHome(Plugin plugin) { private Mono<Path> copyToPluginHome(Plugin plugin) {
return Mono.fromCallable( return Mono.fromCallable(
() -> { () -> {
var fileName = generateFileName(plugin); var fileName = PluginUtils.generateFileName(plugin);
var pluginRoot = Paths.get(pluginProperties.getPluginsRoot()); var pluginRoot = Paths.get(pluginProperties.getPluginsRoot());
try { try {
Files.createDirectories(pluginRoot); Files.createDirectories(pluginRoot);
@ -162,17 +162,6 @@ public class PluginServiceImpl implements PluginService {
.subscribeOn(Schedulers.boundedElastic()); .subscribeOn(Schedulers.boundedElastic());
} }
static String generateFileName(Plugin plugin) {
Assert.notNull(plugin, "The plugin must not be null.");
Assert.notNull(plugin.getMetadata(), "The plugin metadata must not be null.");
Assert.notNull(plugin.getSpec(), "The plugin spec must not be null.");
String version = plugin.getSpec().getVersion();
if (StringUtils.isBlank(version)) {
throw new ServerWebInputException("The plugin version must not be blank.");
}
return String.format("%s-%s.jar", plugin.getMetadata().getName(), version);
}
private void satisfiesRequiresVersion(Plugin newPlugin) { private void satisfiesRequiresVersion(Plugin newPlugin) {
Assert.notNull(newPlugin, "The plugin must not be null."); Assert.notNull(newPlugin, "The plugin must not be null.");
Version version = systemVersion.get(); Version version = systemVersion.get();

View File

@ -0,0 +1,22 @@
package run.halo.app.plugin;
import lombok.experimental.UtilityClass;
import org.apache.commons.lang3.StringUtils;
import org.springframework.util.Assert;
import org.springframework.web.server.ServerWebInputException;
import run.halo.app.core.extension.Plugin;
@UtilityClass
public class PluginUtils {
public static String generateFileName(Plugin plugin) {
Assert.notNull(plugin, "The plugin must not be null.");
Assert.notNull(plugin.getMetadata(), "The plugin metadata must not be null.");
Assert.notNull(plugin.getSpec(), "The plugin spec must not be null.");
String version = plugin.getSpec().getVersion();
if (StringUtils.isBlank(version)) {
throw new ServerWebInputException("The plugin version must not be blank.");
}
return String.format("%s-%s.jar", plugin.getMetadata().getName(), version);
}
}