From 79f1393395c43773f4c5b1c4d14c84ff82640847 Mon Sep 17 00:00:00 2001 From: John Niang Date: Wed, 29 Mar 2023 13:46:14 +0800 Subject: [PATCH] Fix the problem not allowing logging in after upgrading Halo (#3603) #### What type of PR is this? /kind bug /area core #### What this PR does / why we need it: This PR makes extension initialization before starting reconcilers to prevent modification conflicts. #### Which issue(s) this PR fixes: Fixes https://github.com/halo-dev/halo/issues/3601 #### Special notes for your reviewer: 1. Try to run Halo 2.3.2 with command `docker run --rm -it -v ~/halo2-dev:/root/.halo2 -p 8090:8090 halohub/halo:2.3.2` 2. Then run Halo 2.4.0-SNAPSHOT with dev profile. ```bash ./gradlew bootRun --args="--spring.profiles.active=dev" ``` 4. Check logs and logging functionality 5. Repeat steps above #### Does this PR introduce a user-facing change? ```release-note None ``` --- .../controller/DefaultControllerManager.java | 6 ++-- .../extension/gc/GcControllerInitializer.java | 6 ++-- .../app/infra/ExtensionInitializedEvent.java | 16 +++++++++++ .../infra/ExtensionResourceInitializer.java | 28 ++++++++----------- .../ExtensionResourceInitializerTest.java | 18 ++++++------ 5 files changed, 44 insertions(+), 30 deletions(-) create mode 100644 application/src/main/java/run/halo/app/infra/ExtensionInitializedEvent.java diff --git a/application/src/main/java/run/halo/app/extension/controller/DefaultControllerManager.java b/application/src/main/java/run/halo/app/extension/controller/DefaultControllerManager.java index a6d68526c..f6313d983 100644 --- a/application/src/main/java/run/halo/app/extension/controller/DefaultControllerManager.java +++ b/application/src/main/java/run/halo/app/extension/controller/DefaultControllerManager.java @@ -11,11 +11,11 @@ import org.springframework.context.ApplicationContextAware; import org.springframework.context.ApplicationListener; import run.halo.app.extension.ExtensionClient; import run.halo.app.extension.controller.Reconciler.Request; -import run.halo.app.infra.SchemeInitializedEvent; +import run.halo.app.infra.ExtensionInitializedEvent; @Slf4j public class DefaultControllerManager - implements ApplicationListener, + implements ApplicationListener, ApplicationContextAware, DisposableBean, ControllerManager { private final ExtensionClient client; @@ -68,7 +68,7 @@ public class DefaultControllerManager } @Override - public void onApplicationEvent(SchemeInitializedEvent event) { + public void onApplicationEvent(ExtensionInitializedEvent event) { // register reconcilers in system after scheme initialized applicationContext.>getBeanProvider( forClassWithGenerics(Reconciler.class, Request.class)) diff --git a/application/src/main/java/run/halo/app/extension/gc/GcControllerInitializer.java b/application/src/main/java/run/halo/app/extension/gc/GcControllerInitializer.java index a20d82b93..6b32f5add 100644 --- a/application/src/main/java/run/halo/app/extension/gc/GcControllerInitializer.java +++ b/application/src/main/java/run/halo/app/extension/gc/GcControllerInitializer.java @@ -4,11 +4,11 @@ import org.springframework.beans.factory.DisposableBean; import org.springframework.context.ApplicationListener; import org.springframework.stereotype.Component; import run.halo.app.extension.controller.Controller; -import run.halo.app.infra.SchemeInitializedEvent; +import run.halo.app.infra.ExtensionInitializedEvent; @Component public class GcControllerInitializer - implements ApplicationListener, DisposableBean { + implements ApplicationListener, DisposableBean { private final Controller gcController; @@ -17,7 +17,7 @@ public class GcControllerInitializer } @Override - public void onApplicationEvent(SchemeInitializedEvent event) { + public void onApplicationEvent(ExtensionInitializedEvent event) { gcController.start(); } diff --git a/application/src/main/java/run/halo/app/infra/ExtensionInitializedEvent.java b/application/src/main/java/run/halo/app/infra/ExtensionInitializedEvent.java new file mode 100644 index 000000000..428b1a86f --- /dev/null +++ b/application/src/main/java/run/halo/app/infra/ExtensionInitializedEvent.java @@ -0,0 +1,16 @@ +package run.halo.app.infra; + +import org.springframework.context.ApplicationEvent; + +/** + * ExtensionInitializedEvent is fired after extensions have been initialized completely. + * + * @author johnniang + */ +public class ExtensionInitializedEvent extends ApplicationEvent { + + public ExtensionInitializedEvent(Object source) { + super(source); + } + +} diff --git a/application/src/main/java/run/halo/app/infra/ExtensionResourceInitializer.java b/application/src/main/java/run/halo/app/infra/ExtensionResourceInitializer.java index 03b5bfcbd..d26120d1d 100644 --- a/application/src/main/java/run/halo/app/infra/ExtensionResourceInitializer.java +++ b/application/src/main/java/run/halo/app/infra/ExtensionResourceInitializer.java @@ -1,21 +1,18 @@ package run.halo.app.infra; import java.io.IOException; -import java.time.Duration; import java.util.HashSet; import java.util.List; import java.util.Set; import lombok.extern.slf4j.Slf4j; -import org.springframework.boot.context.event.ApplicationReadyEvent; +import org.springframework.context.ApplicationEventPublisher; import org.springframework.context.event.EventListener; import org.springframework.core.io.Resource; import org.springframework.core.io.support.PathMatchingResourcePatternResolver; -import org.springframework.dao.OptimisticLockingFailureException; import org.springframework.stereotype.Component; import org.springframework.util.CollectionUtils; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; -import reactor.util.retry.Retry; import run.halo.app.extension.ReactiveExtensionClient; import run.halo.app.extension.Unstructured; import run.halo.app.infra.properties.HaloProperties; @@ -38,14 +35,18 @@ public class ExtensionResourceInitializer { private final HaloProperties haloProperties; private final ReactiveExtensionClient extensionClient; + private final ApplicationEventPublisher eventPublisher; + public ExtensionResourceInitializer(HaloProperties haloProperties, - ReactiveExtensionClient extensionClient) { + ReactiveExtensionClient extensionClient, + ApplicationEventPublisher eventPublisher) { this.haloProperties = haloProperties; this.extensionClient = extensionClient; + this.eventPublisher = eventPublisher; } - @EventListener(ApplicationReadyEvent.class) - public Mono initialize(ApplicationReadyEvent readyEvent) { + @EventListener(SchemeInitializedEvent.class) + public Mono initialize(SchemeInitializedEvent initializedEvent) { var locations = new HashSet(); if (!haloProperties.isRequiredExtensionDisabled()) { locations.addAll(REQUIRED_EXTENSION_LOCATIONS); @@ -80,7 +81,8 @@ public class ExtensionResourceInitializer { extension.getMetadata().getName()); } }) - .then(); + .then(Mono.fromRunnable( + () -> eventPublisher.publishEvent(new ExtensionInitializedEvent(this)))); } private Mono createOrUpdate(Unstructured extension) { @@ -88,17 +90,11 @@ public class ExtensionResourceInitializer { .flatMap(ext -> extensionClient.fetch(extension.groupVersionKind(), extension.getMetadata().getName())) .flatMap(existingExt -> { + // force update extension.getMetadata().setVersion(existingExt.getMetadata().getVersion()); return extensionClient.update(extension); }) - .switchIfEmpty(Mono.defer(() -> extensionClient.create(extension))) - .retryWhen(Retry.fixedDelay(3, Duration.ofMillis(100)) - .filter(t -> t instanceof OptimisticLockingFailureException)) - .onErrorContinue(OptimisticLockingFailureException.class, (throwable, o) -> { - log.warn("Failed to create or update extension resource: {}/{} due to modification " - + "conflict", - extension.groupVersionKind(), extension.getMetadata().getName()); - }); + .switchIfEmpty(Mono.defer(() -> extensionClient.create(extension))); } private List listResources(String location) { diff --git a/application/src/test/java/run/halo/app/infra/ExtensionResourceInitializerTest.java b/application/src/test/java/run/halo/app/infra/ExtensionResourceInitializerTest.java index ec95ac004..4d80e8813 100644 --- a/application/src/test/java/run/halo/app/infra/ExtensionResourceInitializerTest.java +++ b/application/src/test/java/run/halo/app/infra/ExtensionResourceInitializerTest.java @@ -19,10 +19,11 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.ArgumentCaptor; +import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; import org.skyscreamer.jsonassert.JSONAssert; -import org.springframework.boot.context.event.ApplicationReadyEvent; +import org.springframework.context.ApplicationEventPublisher; import org.springframework.util.FileSystemUtils; import reactor.core.publisher.Mono; import reactor.test.StepVerifier; @@ -42,21 +43,22 @@ import run.halo.app.infra.utils.JsonUtils; class ExtensionResourceInitializerTest { @Mock - private ReactiveExtensionClient extensionClient; + ReactiveExtensionClient extensionClient; @Mock - private HaloProperties haloProperties; + HaloProperties haloProperties; @Mock - private ApplicationReadyEvent applicationReadyEvent; + SchemeInitializedEvent applicationReadyEvent; - private ExtensionResourceInitializer extensionResourceInitializer; + @Mock + ApplicationEventPublisher eventPublisher; + + @InjectMocks + ExtensionResourceInitializer extensionResourceInitializer; List dirsToClean; @BeforeEach void setUp() throws IOException { - extensionResourceInitializer = - new ExtensionResourceInitializer(haloProperties, extensionClient); - dirsToClean = new ArrayList<>(2); Path tempDirectory = Files.createTempDirectory("extension-resource-initializer-test");