fix: theme status phase is always FAILED even if version of Halo is satisfied (#3390)

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

#### What this PR does / why we need it:
修复主题 requires 版本匹配后依然显示 FAILED 状态的问题

how to test it?
1. 安装一个主题后更新主题的 requires 为不匹配系统版本号,此时主题 status 会提示版本号不匹配的错误
2. 再更新 requires 为正确的,phase 会从 FAILED 变为 READY

#### Which issue(s) this PR fixes:
Fixes #3326
#### Does this PR introduce a user-facing change?
```release-note
修复主题状态显示不正确的问题
```
pull/3425/head
guqing 2023-02-28 22:10:18 +08:00 committed by GitHub
parent 166a440df1
commit 3146589d25
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 52 additions and 16 deletions

View File

@ -6,8 +6,10 @@ import java.time.Instant;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Objects;
import java.util.Set; import java.util.Set;
import java.util.UUID; import java.util.UUID;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.springframework.retry.support.RetryTemplate; import org.springframework.retry.support.RetryTemplate;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
@ -82,38 +84,40 @@ public class ThemeReconciler implements Reconciler<Request> {
.build(); .build();
} }
private void reconcileStatus(String name) { void reconcileStatus(String name) {
client.fetch(Theme.class, name).ifPresent(theme -> { client.fetch(Theme.class, name).ifPresent(theme -> {
final Theme oldTheme = JsonUtils.deepCopy(theme); final Theme.ThemeStatus status =
if (theme.getStatus() == null) { ObjectUtils.defaultIfNull(theme.getStatus(), new Theme.ThemeStatus());
theme.setStatus(new Theme.ThemeStatus()); final Theme.ThemeStatus oldStatus = JsonUtils.deepCopy(status);
} theme.setStatus(status);
Theme.ThemeStatus status = theme.getStatus();
Path themePath = themePathPolicy.generate(theme); Path themePath = themePathPolicy.generate(theme);
status.setLocation(themePath.toAbsolutePath().toString()); status.setLocation(themePath.toAbsolutePath().toString());
if (status.getPhase() == null) {
status.setPhase(Theme.ThemePhase.READY); status.setPhase(Theme.ThemePhase.READY);
} Condition.ConditionBuilder conditionBuilder = Condition.builder()
.type(Theme.ThemePhase.READY.name())
.status(ConditionStatus.TRUE)
.reason(Theme.ThemePhase.READY.name())
.message(StringUtils.EMPTY)
.lastTransitionTime(Instant.now());
// Check if this theme version is match requires param. // Check if this theme version is match requires param.
String normalVersion = systemVersionSupplier.get().getNormalVersion(); String normalVersion = systemVersionSupplier.get().getNormalVersion();
String requires = theme.getSpec().getRequires(); String requires = theme.getSpec().getRequires();
if (!VersionUtils.satisfiesRequires(normalVersion, requires)) { if (!VersionUtils.satisfiesRequires(normalVersion, requires)) {
status.setPhase(Theme.ThemePhase.FAILED); status.setPhase(Theme.ThemePhase.FAILED);
Condition condition = Condition.builder() conditionBuilder
.type(Theme.ThemePhase.FAILED.name()) .type(Theme.ThemePhase.FAILED.name())
.status(ConditionStatus.FALSE) .status(ConditionStatus.FALSE)
.reason("UnsatisfiedRequiresVersion") .reason("UnsatisfiedRequiresVersion")
.message(String.format( .message(String.format(
"Theme requires a minimum system version of [%s], and you have [%s].", "Theme requires a minimum system version of [%s], and you have [%s].",
requires, normalVersion)) requires, normalVersion));
.lastTransitionTime(Instant.now())
.build();
Theme.nullSafeConditionList(theme).add(condition);
} }
Theme.nullSafeConditionList(theme).addAndEvictFIFO(conditionBuilder.build());
if (!oldTheme.equals(theme)) { if (!Objects.equals(oldStatus, status)) {
client.update(theme); client.update(theme);
} }
}); });

View File

@ -195,6 +195,38 @@ class ThemeReconcilerTest {
verify(extensionClient, times(2)).fetch(eq(Setting.class), eq(settingName)); verify(extensionClient, times(2)).fetch(eq(Setting.class), eq(settingName));
} }
@Test
void reconcileStatus() {
when(systemVersionSupplier.get()).thenReturn(Version.valueOf("2.3.0"));
Path testWorkDir = tempDirectory.resolve("reconcile-delete");
when(haloProperties.getWorkDir()).thenReturn(testWorkDir);
final ThemeReconciler themeReconciler =
new ThemeReconciler(extensionClient, haloProperties, systemVersionSupplier);
Theme theme = fakeTheme();
theme.setStatus(null);
theme.getSpec().setRequires(">2.3.0");
when(extensionClient.fetch(eq(Theme.class), eq("fake-theme")))
.thenReturn(Optional.of(theme));
themeReconciler.reconcileStatus("fake-theme");
ArgumentCaptor<Theme> themeUpdateCaptor = ArgumentCaptor.forClass(Theme.class);
verify(extensionClient).update(themeUpdateCaptor.capture());
Theme value = themeUpdateCaptor.getValue();
assertThat(value.getStatus()).isNotNull();
assertThat(value.getStatus().getConditions().peekFirst().getType())
.isEqualTo(Theme.ThemePhase.FAILED.name());
assertThat(value.getStatus().getPhase())
.isEqualTo(Theme.ThemePhase.FAILED);
theme.getSpec().setRequires(">=2.3.0");
when(extensionClient.fetch(eq(Theme.class), eq("fake-theme")))
.thenReturn(Optional.of(theme));
themeReconciler.reconcileStatus("fake-theme");
verify(extensionClient, times(2)).update(themeUpdateCaptor.capture());
assertThat(themeUpdateCaptor.getValue().getStatus().getPhase())
.isEqualTo(Theme.ThemePhase.READY);
}
private Theme fakeTheme() { private Theme fakeTheme() {
Theme theme = new Theme(); Theme theme = new Theme();
Metadata metadata = new Metadata(); Metadata metadata = new Metadata();
@ -206,7 +238,7 @@ class ThemeReconcilerTest {
Theme.ThemeSpec themeSpec = new Theme.ThemeSpec(); Theme.ThemeSpec themeSpec = new Theme.ThemeSpec();
themeSpec.setSettingName("theme-test-setting"); themeSpec.setSettingName("theme-test-setting");
theme.setSpec(themeSpec); theme.setSpec(themeSpec);
when(extensionClient.fetch(eq(Theme.class), eq(metadata.getName()))) lenient().when(extensionClient.fetch(eq(Theme.class), eq(metadata.getName())))
.thenReturn(Optional.of(theme)); .thenReturn(Optional.of(theme));
return theme; return theme;
} }