Fix an optimistic lock error always appearing when restarting Halo (#2795)

#### What type of PR is this?

/kind improvement
/area core
/milestone 2.0.x

#### What this PR does / why we need it:

This PR only do one improvement for reconciling Role. I always fetch the latest data and compare the difference between the latest and current, and then decide whether to update based on the result.

#### Which issue(s) this PR fixes:

Fixes https://github.com/halo-dev/halo/issues/2784

#### Special notes for your reviewer:

Steps to test:

1. Restart Halo multiple times and see the log

#### Does this PR introduce a user-facing change?

```release-note
None
```
pull/2804/head^2
John Niang 2022-11-30 11:51:47 +08:00 committed by GitHub
parent 9d60b8ae06
commit 5aff60d5b4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 38 additions and 27 deletions

View File

@ -1,11 +1,12 @@
package run.halo.app.core.extension.reconciler; package run.halo.app.core.extension.reconciler;
import static java.util.Objects.deepEquals;
import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.core.type.TypeReference;
import java.util.HashMap; import java.util.LinkedHashMap;
import java.util.LinkedHashSet; import java.util.LinkedHashSet;
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 lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
@ -40,32 +41,29 @@ public class RoleReconciler implements Reconciler<Request> {
@Override @Override
public Result reconcile(Request request) { public Result reconcile(Request request) {
client.fetch(Role.class, request.name()).ifPresent(role -> { client.fetch(Role.class, request.name())
final Role oldRole = JsonUtils.deepCopy(role); .ifPresent(role -> {
Map<String, String> annotations = role.getMetadata().getAnnotations(); var annotations = role.getMetadata().getAnnotations();
if (annotations == null) { if (annotations == null) {
annotations = new HashMap<>(); annotations = new LinkedHashMap<>();
role.getMetadata().setAnnotations(annotations); role.getMetadata().setAnnotations(annotations);
} }
var roleDependencies = readValue(annotations.get(Role.ROLE_DEPENDENCIES_ANNO));
var dependenciesRole = roleService.listDependencies(roleDependencies);
var dependencyRules = dependenciesRole.stream()
.map(Role::getRules)
.flatMap(List::stream)
.sorted()
.toList();
var uiPermissions = aggregateUiPermissions(dependenciesRole);
// override dependency rules to annotations
annotations.put(Role.ROLE_DEPENDENCY_RULES,
JsonUtils.objectToJson(dependencyRules));
annotations.put(Role.UI_PERMISSIONS_AGGREGATED_ANNO,
JsonUtils.objectToJson(uiPermissions));
Set<String> roleDependencies = readValue(annotations.get(Role.ROLE_DEPENDENCIES_ANNO)); updateLabelsAndAnnotations(role);
});
List<Role> dependenciesRole = roleService.listDependencies(roleDependencies);
List<Role.PolicyRule> dependencyRules = dependenciesRole.stream()
.map(Role::getRules)
.flatMap(List::stream)
.sorted()
.toList();
List<String> uiPermissions = aggregateUiPermissions(dependenciesRole);
// override dependency rules to annotations
annotations.put(Role.ROLE_DEPENDENCY_RULES, JsonUtils.objectToJson(dependencyRules));
annotations.put(Role.UI_PERMISSIONS_AGGREGATED_ANNO,
JsonUtils.objectToJson(uiPermissions));
if (!Objects.equals(oldRole, role)) {
client.update(role);
}
});
return new Result(false, null); return new Result(false, null);
} }
@ -76,6 +74,19 @@ public class RoleReconciler implements Reconciler<Request> {
.build(); .build();
} }
private void updateLabelsAndAnnotations(Role role) {
var annotations = role.getMetadata().getAnnotations();
var labels = role.getMetadata().getLabels();
client.fetch(Role.class, role.getMetadata().getName())
.filter(freshRole -> !deepEquals(annotations, freshRole.getMetadata().getAnnotations())
|| deepEquals(labels, freshRole.getMetadata().getLabels()))
.ifPresent(freshRole -> {
freshRole.getMetadata().setAnnotations(annotations);
freshRole.getMetadata().setLabels(labels);
client.update(freshRole);
});
}
private List<String> aggregateUiPermissions(List<Role> dependencyRoles) { private List<String> aggregateUiPermissions(List<Role> dependencyRoles) {
return dependencyRoles.stream() return dependencyRoles.stream()
.filter(role -> role.getMetadata().getAnnotations() != null) .filter(role -> role.getMetadata().getAnnotations() != null)