mirror of https://github.com/apache/cloudstack.git
197 lines
7.7 KiB
Java
197 lines
7.7 KiB
Java
// Licensed to the Apache Software Foundation (ASF) under one
|
|
// or more contributor license agreements. See the NOTICE file
|
|
// distributed with this work for additional information
|
|
// regarding copyright ownership. The ASF licenses this file
|
|
// to you under the Apache License, Version 2.0 (the
|
|
// "License"); you may not use this file except in compliance
|
|
// with the License. You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing,
|
|
// software distributed under the License is distributed on an
|
|
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
// KIND, either express or implied. See the License for the
|
|
// specific language governing permissions and limitations
|
|
// under the License.
|
|
package com.cloud.upgrade;
|
|
|
|
import java.util.LinkedList;
|
|
import java.util.List;
|
|
import java.util.Map;
|
|
import java.util.concurrent.ConcurrentHashMap;
|
|
import java.util.function.Function;
|
|
import java.util.function.Predicate;
|
|
import java.util.stream.Collectors;
|
|
|
|
import com.google.common.collect.ImmutableList;
|
|
|
|
import org.apache.cloudstack.utils.CloudStackVersion;
|
|
|
|
import com.cloud.upgrade.dao.DbUpgrade;
|
|
|
|
/**
|
|
* @since 4.12.0.0
|
|
*/
|
|
public final class DatabaseVersionHierarchy {
|
|
private final ImmutableList<VersionNode> hierarchy;
|
|
|
|
private DatabaseVersionHierarchy(ImmutableList<VersionNode> hierarchy) {
|
|
this.hierarchy = hierarchy;
|
|
}
|
|
|
|
public static DatabaseVersionHierarchyBuilder builder() {
|
|
return new DatabaseVersionHierarchyBuilder();
|
|
}
|
|
|
|
/**
|
|
* Check if current hierarchy of Database Versions contains <code>version</code>.
|
|
*
|
|
* @param version The version to check if hierarchy contains it
|
|
*
|
|
* @return true if hierarchy contains the version, false if not
|
|
*/
|
|
public boolean contains(final CloudStackVersion version) {
|
|
return toList().contains(version);
|
|
}
|
|
|
|
/**
|
|
* Calculates an upgrade path for the passed <code>fromVersion</code>. If the <code>fromVersion</code>
|
|
* doesn't exist in list of available <code>VersionNode</code> hierarchy, then calculation assumes that
|
|
* the <code>fromVersion</code> required no schema migrations or data conversions and no upgrade path was
|
|
* defined for it. Therefore, we find the most recent version with database migrations before the
|
|
* <code>fromVersion</code> and adopt that upgrade path list.
|
|
*
|
|
* @param fromVersion The version from which the upgrade will occur
|
|
*
|
|
* @return The upgrade path from <code>fromVersion</code> to <code>LATEST</code> version.
|
|
*/
|
|
public DbUpgrade[] getPath(final CloudStackVersion fromVersion) {
|
|
return getPath(fromVersion, null);
|
|
}
|
|
|
|
/**
|
|
* Calculates an upgrade path for the passed <code>fromVersion</code> and <code>toVersion</code>. If the
|
|
* <code>fromVersion</code> doesn't exist in list of available <code>VersionNode</code> hierarchy, then
|
|
* calculation assumes that the <code>fromVersion</code> required no schema migrations or data conversions
|
|
* and no upgrade path was defined for it. Therefore, we find the most recent version with database
|
|
* migrations before the <code>fromVersion</code> and adopt that upgrade path list up to <code>toVersion</code>.
|
|
* If <code>toVersion</code> is null, we're going to find the upgrade path up to the latest available version.
|
|
*
|
|
* @param fromVersion The version from which the upgrade will occur
|
|
* @param toVersion The version up to which the upgrade will occur (can be null)
|
|
*
|
|
* @return The upgrade path from <code>fromVersion</code> to <code>toVersion</code>
|
|
*/
|
|
public DbUpgrade[] getPath(final CloudStackVersion fromVersion, final CloudStackVersion toVersion) {
|
|
if (fromVersion == null) {
|
|
return new DbUpgrade[0];
|
|
}
|
|
|
|
// The CloudStack version is latest or higher than latest
|
|
if (fromVersion.compareTo(getLatestVersion()) >= 0) {
|
|
return new DbUpgrade[0];
|
|
}
|
|
|
|
// we cannot find the version specified, so get the
|
|
// most recent one immediately before this version
|
|
if (!contains(fromVersion)) {
|
|
return getPath(getRecentVersion(fromVersion), toVersion);
|
|
}
|
|
|
|
final Predicate<? super VersionNode> predicate;
|
|
|
|
if (toVersion == null) {
|
|
// all the available versions greater than or equal to fromVersion
|
|
predicate = node -> node.version.compareTo(fromVersion) > -1;
|
|
} else {
|
|
// all the available versions greater than or equal to fromVersion AND less than toVersion
|
|
predicate = node -> node.version.compareTo(fromVersion) > -1 && node.version.compareTo(toVersion) < 0;
|
|
}
|
|
|
|
// get upgrade path from version forward (include version itself in the path)
|
|
return hierarchy
|
|
.stream()
|
|
.filter(predicate)
|
|
.filter(distinct(node -> node.upgrader.getUpgradedVersion()))
|
|
.map(node -> node.upgrader)
|
|
.toArray(DbUpgrade[]::new);
|
|
}
|
|
|
|
/**
|
|
* Find the most recent <code>CloudStackVersion</code> immediately before <code>fromVersion</code>
|
|
*
|
|
* @param fromVersion The version to look up its immediate previous available version
|
|
*
|
|
* @return The <code>CloudStackVersion</code> or null
|
|
*
|
|
* @since 4.8.2.0 (refactored in 4.11.1.0)
|
|
*/
|
|
protected CloudStackVersion getRecentVersion(final CloudStackVersion fromVersion) {
|
|
if (fromVersion == null) {
|
|
return null;
|
|
}
|
|
|
|
// find the most recent version immediately before fromVersion
|
|
return toList()
|
|
.reverse()
|
|
.stream()
|
|
.filter(version -> fromVersion.compareTo(version) > 0)
|
|
.findFirst()
|
|
.orElse(null);
|
|
}
|
|
|
|
/**
|
|
* Generate immutable list of available <code>CloudstackVersion</code> in the hierarchy
|
|
*
|
|
* @return list of available versions
|
|
*/
|
|
public ImmutableList<CloudStackVersion> toList() {
|
|
List<CloudStackVersion> versions = hierarchy
|
|
.stream()
|
|
.map(node -> node.version)
|
|
.collect(Collectors.toList());
|
|
|
|
return ImmutableList.copyOf(versions);
|
|
}
|
|
|
|
/**
|
|
* Find the distinct <code>VersionNode</code> based on the provided <code>getUpgradedVersion()</code>
|
|
*/
|
|
private Predicate<VersionNode> distinct(Function<VersionNode, String> keyExtractor) {
|
|
Map<String, Boolean> seen = new ConcurrentHashMap<>();
|
|
return t -> seen.putIfAbsent(keyExtractor.apply(t), Boolean.TRUE) == null;
|
|
}
|
|
|
|
protected static class VersionNode {
|
|
final CloudStackVersion version;
|
|
final DbUpgrade upgrader;
|
|
|
|
protected VersionNode(final CloudStackVersion version, final DbUpgrade upgrader) {
|
|
this.version = version;
|
|
this.upgrader = upgrader;
|
|
}
|
|
}
|
|
|
|
|
|
public static final class DatabaseVersionHierarchyBuilder {
|
|
private final List<VersionNode> hierarchyBuilder = new LinkedList<>();
|
|
|
|
private DatabaseVersionHierarchyBuilder() {
|
|
}
|
|
|
|
public DatabaseVersionHierarchyBuilder next(final String version, final DbUpgrade upgrader) {
|
|
hierarchyBuilder.add(new VersionNode(CloudStackVersion.parse(version), upgrader));
|
|
return this;
|
|
}
|
|
|
|
public DatabaseVersionHierarchy build() {
|
|
return new DatabaseVersionHierarchy(ImmutableList.copyOf(hierarchyBuilder));
|
|
}
|
|
}
|
|
|
|
public CloudStackVersion getLatestVersion() {
|
|
return CloudStackVersion.parse(hierarchy.get(hierarchy.size() - 1).upgrader.getUpgradedVersion());
|
|
}
|
|
}
|