From 772c7fcf97ca49e1f68200ba4dc8aa1dd556b32d Mon Sep 17 00:00:00 2001 From: guqing <38999863+guqing@users.noreply.github.com> Date: Mon, 23 Oct 2023 11:07:13 +0800 Subject: [PATCH] refactor: provide API for change plugin motion status synchronously (#4745) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #### What type of PR is this? /kind improvement /area core /area console #### What this PR does / why we need it: 提供允许同步更改插件运行状态的 API #### Which issue(s) this PR fixes: Fixes #4744 #### Does this PR introduce a user-facing change? ```release-note 提供允许同步更改插件运行状态的 API ``` --- .../extension/endpoint/PluginEndpoint.java | 73 +++++++++++++++++++ .../extensions/role-template-plugin.yaml | 2 +- 2 files changed, 74 insertions(+), 1 deletion(-) diff --git a/application/src/main/java/run/halo/app/core/extension/endpoint/PluginEndpoint.java b/application/src/main/java/run/halo/app/core/extension/endpoint/PluginEndpoint.java index 0a3ca14c33..d6d015c1ff 100644 --- a/application/src/main/java/run/halo/app/core/extension/endpoint/PluginEndpoint.java +++ b/application/src/main/java/run/halo/app/core/extension/endpoint/PluginEndpoint.java @@ -40,7 +40,9 @@ import java.util.function.Predicate; import java.util.function.Supplier; import lombok.AllArgsConstructor; +import lombok.Data; import lombok.extern.slf4j.Slf4j; +import org.pf4j.PluginState; import org.reactivestreams.Publisher; import org.springdoc.webflux.core.fn.SpringdocRouteBuilder; import org.springframework.beans.factory.DisposableBean; @@ -201,6 +203,27 @@ public RouterFunction endpoint() { .response(responseBuilder() .implementation(Plugin.class)) ) + .PUT("plugins/{name}/plugin-state", this::changePluginRunningState, + builder -> builder.operationId("ChangePluginRunningState") + .description("Change the running state of a plugin by name.") + .tag(tag) + .parameter(parameterBuilder() + .name("name") + .in(ParameterIn.PATH) + .required(true) + .implementation(String.class) + ) + .requestBody(requestBodyBuilder() + .required(true) + .content(contentBuilder() + .mediaType(MediaType.APPLICATION_JSON_VALUE) + .schema(schemaBuilder() + .implementation(RunningStateRequest.class)) + ) + ) + .response(responseBuilder() + .implementation(Plugin.class)) + ) .GET("plugins", this::list, builder -> { builder.operationId("ListPlugins") .tag(tag) @@ -255,6 +278,56 @@ public RouterFunction endpoint() { .build(); } + Mono changePluginRunningState(ServerRequest request) { + final var name = request.pathVariable("name"); + return request.bodyToMono(RunningStateRequest.class) + .flatMap(runningState -> { + final var enable = runningState.isEnable(); + return client.get(Plugin.class, name) + .flatMap(plugin -> { + plugin.getSpec().setEnabled(enable); + return client.update(plugin); + }) + .flatMap(plugin -> { + if (runningState.isAsync()) { + return Mono.just(plugin); + } + return waitForPluginToMeetExpectedState(name, p -> { + // when enabled = true,excepted phase = started || failed + // when enabled = false,excepted phase = !started + var phase = p.statusNonNull().getPhase(); + if (enable) { + return PluginState.STARTED.equals(phase) + || PluginState.FAILED.equals(phase); + } + return !PluginState.STARTED.equals(phase); + }); + }); + }) + .flatMap(plugin -> ServerResponse.ok().bodyValue(plugin)); + } + + Mono waitForPluginToMeetExpectedState(String name, Predicate predicate) { + return Mono.defer(() -> client.get(Plugin.class, name) + .map(plugin -> { + if (predicate.test(plugin)) { + return plugin; + } + throw new IllegalStateException("Plugin " + name + " is not in expected state"); + }) + ) + .retryWhen(Retry.backoff(10, Duration.ofMillis(100)) + .filter(IllegalStateException.class::isInstance) + ); + } + + @Data + @Schema(name = "PluginRunningStateRequest") + static class RunningStateRequest { + private boolean enable; + private boolean async; + } + private Mono fetchJsBundle(ServerRequest request) { Optional versionOption = request.queryParam("v"); return versionOption.map(s -> diff --git a/application/src/main/resources/extensions/role-template-plugin.yaml b/application/src/main/resources/extensions/role-template-plugin.yaml index e3e9bc1bd8..82ff040a29 100644 --- a/application/src/main/resources/extensions/role-template-plugin.yaml +++ b/application/src/main/resources/extensions/role-template-plugin.yaml @@ -17,7 +17,7 @@ rules: verbs: [ "create", "patch", "update", "delete", "deletecollection" ] - apiGroups: [ "api.console.halo.run" ] resources: [ "plugins/upgrade", "plugins/resetconfig", "plugins/config", "plugins/reload", - "plugins/install-from-uri", "plugins/upgrade-from-uri" ] + "plugins/install-from-uri", "plugins/upgrade-from-uri", "plugins/plugin-state" ] verbs: [ "*" ] - apiGroups: [ "api.console.halo.run" ] resources: [ "plugin-presets" ]