Skip to content

Commit

Permalink
Merge pull request fabric8io#2601 from adietish/issue-2599
Browse files Browse the repository at this point in the history
  • Loading branch information
fusesource-ci authored Nov 27, 2020
2 parents b93d612 + 8955e15 commit 28e2052
Show file tree
Hide file tree
Showing 5 changed files with 819 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,15 @@
import io.fabric8.kubernetes.api.model.HasMetadata;
import io.fabric8.kubernetes.api.model.Namespaced;
import io.fabric8.kubernetes.api.model.apiextensions.v1beta1.CustomResourceDefinition;
import io.fabric8.kubernetes.api.model.apiextensions.v1beta1.CustomResourceDefinitionVersion;
import io.fabric8.kubernetes.client.KubernetesClientException;
import io.fabric8.kubernetes.client.utils.ApiVersionUtil;
import io.fabric8.kubernetes.client.utils.Pluralize;
import io.fabric8.kubernetes.client.utils.Utils;
import io.fabric8.kubernetes.api.model.apiextensions.v1beta1.CustomResourceDefinitionSpec;
import io.fabric8.kubernetes.client.utils.KubernetesVersionPriority;

import java.util.stream.Collectors;

public class CustomResourceDefinitionContext {
private String name;
Expand Down Expand Up @@ -81,14 +86,25 @@ public static CustomResourceDefinitionContext fromCustomResourceType(Class<? ext
public static CustomResourceDefinitionContext fromCrd(CustomResourceDefinition crd) {
return new CustomResourceDefinitionContext.Builder()
.withGroup(crd.getSpec().getGroup())
.withVersion(crd.getSpec().getVersion())
.withVersion(getVersion(crd.getSpec()))
.withScope(crd.getSpec().getScope())
.withName(crd.getMetadata().getName())
.withPlural(crd.getSpec().getNames().getPlural())
.withKind(crd.getSpec().getNames().getKind())
.build();
}

private static String getVersion(CustomResourceDefinitionSpec spec) {
String version = KubernetesVersionPriority.highestPriority(
spec.getVersions().stream()
.map(CustomResourceDefinitionVersion::getName)
.collect(Collectors.toList()));
if (version == null) {
version = spec.getVersion();
}
return version;
}

public static class Builder {
private final CustomResourceDefinitionContext customResourceDefinitionContext;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,248 @@
/**
* Copyright (C) 2015 Red Hat, Inc.
*
* Licensed 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 io.fabric8.kubernetes.client.utils;

import java.util.Optional;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
* A factory that is creating a version instance for a given version string.
* Returns {@link KubernetesVersion} instances for kubernetes versions (starts with 'v')
* and {@link NonKubernetesVersion} otherwise.
*/
public class KubernetesVersionFactory {

private KubernetesVersionFactory() {}

public static Version create(String versionValue) {
Version version = KubernetesVersion.FACTORY.create(versionValue);
if (version == null) {
version = NonKubernetesVersion.FACTORY.create(versionValue);
}
return version;
}

public interface VersionFactory<T extends Version> {
T create(String version);
}

/**
* A kubernetes version. This represents a version that starts with a 'v' followed by a numerical major version.
* Optionally this may be followed by an 'alpha' or 'beta' qualifier and an also optional numeric minor version.
*/
public static class KubernetesVersion extends Version {

public static final VersionFactory<KubernetesVersion> FACTORY = new VersionFactory<KubernetesVersion>() {

private final Pattern versionPattern = Pattern.compile("v([0-9]+)((alpha|beta)([0-9]+)?)*");

@Override
public KubernetesVersion create(String version) {
if (version == null) {
return null;
}
Matcher matcher = versionPattern.matcher(version);
if (!matcher.matches()) {
return null;
}
Integer majorValue = getInt(matcher.group(1));
String qualifierValue = matcher.group(3);
Integer minorValue = getInt(matcher.group(4));
return new KubernetesVersion(majorValue, qualifierValue, minorValue, version);
}

private Integer getInt(String value) {
if (value == null) {
return null;
}
try {
return Integer.parseInt(value);
} catch(NumberFormatException e) {
return null;
}
}
};

private final Integer major;
private final Optional<String> qualifier;
private final Optional<Integer> minor;

private KubernetesVersion(Integer major, String qualifier, Integer minor, String version) {
super(version);
this.major = major;
this.qualifier = Optional.ofNullable(qualifier);
this.minor = Optional.ofNullable(minor);
}

public Integer getMajor() {
return major;
}

public Optional<String> getQualifier() {
return qualifier;
}

public Optional<Integer> getMinor() {
return minor;
}

public boolean isStable() {
return qualifier.orElse(null) == null;
}

@Override
public boolean isKubernetes() {
return true;
}

/**
* Compares this version to another version and returns whether this version has a
* higher, equal or lower priority than the version that it is being compared to.
*
* The kubernetes specs v1.17 at <a href="https://v1-17.docs.kubernetes.io/docs/tasks/access-kubernetes-api/custom-resources/custom-resource-definition-versioning/#version-priority">Version Priority</a>
* state the following:
*
* <ul>
* <li>Entries that follow Kubernetes version patterns are sorted before those that do not.</li>
* <li>For entries that follow Kubernetes version patterns, the numeric portions of the version string is sorted largest to smallest.</li>
* <li>If the strings beta or alpha follow the first numeric portion, they sorted in that order, after the equivalent string without the beta
* or alpha suffix (which is presumed to be the GA version).</li>
* <li>If another number follows the beta, or alpha, those numbers are also sorted from largest to smallest.</li>
* <li>Strings that don’t fit the above format are sorted alphabetically and the numeric portions are not treated specially. Notice that in the example below, foo1 is sorted above foo10.
* This is different from the sorting of the numeric portion of entries that do follow the Kubernetes version patterns.</li>
* </ul>
*
* @param other the version to compare this version to
* @return -1 if this version has a lower, 1 if it has a higher or 0 if the priorities are equal
*
*/
@Override
public int compareTo(Version other) {
if (other == this) {
return 0;
}
if (other instanceof NonKubernetesVersion) {
return 1;
}
if (!(other instanceof KubernetesVersion)) {
return 1;
}
KubernetesVersion otherKube = (KubernetesVersion) other;
if (qualifier.isPresent()) {
if (!otherKube.qualifier.isPresent()) {
return -1;
}
int qualifierComparison = qualifier.get().compareTo(otherKube.qualifier.orElse(null));
if (qualifierComparison != 0) {
return qualifierComparison;
}
int majorComparison = compareMajor(otherKube);
if (majorComparison != 0) {
return majorComparison;
}
return compareMinor(otherKube);
} else {
if (!otherKube.qualifier.isPresent()) {
return compareMajor(otherKube);
} else {
return 1;
}
}
}

private int compareMajor(KubernetesVersion other) {
return major.compareTo(other.major);
}

private int compareMinor(KubernetesVersion other) {
if (minor.isPresent()) {
if (!other.minor.isPresent()) {
return 1;
}
return minor.get().compareTo(other.minor.orElse(null));
} else {
if (!other.minor.isPresent()) {
return 0;
}
return -1;
}
}
}

/**
* A non kubernetes version which is any version string that does not start with a 'v'.
*/
public static class NonKubernetesVersion extends Version {

public static final VersionFactory<NonKubernetesVersion> FACTORY = version -> {
if (version == null) {
return null;
}
return new NonKubernetesVersion(version);
};

private NonKubernetesVersion(String version) {
super(version);
}

@Override
public boolean isKubernetes() {
return false;
}

/**
* Non-Kubernetes versions have lower priority than kubernetes versions. Among them they're inverse-lexicographically sorted.
*
* @param other the other version to compare to
* @return -1 if this has a lower, 0 if equal or 1 if higher priority
*/
@Override
public int compareTo(Version other) {
if (other == this) {
return 0;
}
if (other instanceof KubernetesVersion) {
return -1;
} else if (other instanceof NonKubernetesVersion) {
return full.compareTo(other.full) * -1; // inverse lexicographical order
} else {
return -1;
}
}
}

protected abstract static class Version implements Comparable<Version> {

protected final String full;

protected Version(String full) {
this.full = full;
}

public abstract boolean isKubernetes();

public String getFull() {
return full;
}

@Override
public String toString() {
return full;
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/**
* Copyright (C) 2015 Red Hat, Inc.
*
* Licensed 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 io.fabric8.kubernetes.client.utils;

import io.fabric8.kubernetes.api.model.apiextensions.v1.CustomResourceDefinitionSpec;
import io.fabric8.kubernetes.api.model.apiextensions.v1.CustomResourceDefinitionVersion;

import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;

import static io.fabric8.kubernetes.client.utils.KubernetesVersionFactory.Version;

/**
* A util class that allows to deal with kubernetes versions.
*/
public class KubernetesVersionPriority {

private KubernetesVersionPriority() {}

/**
* Returns the version with the highest priority for the given list of versions.
*
* @param versions the versions to pick the version with the highest priority from
* @return the version with the highest priority
*
* @see CustomResourceDefinitionVersion#getName()
* @see CustomResourceDefinitionSpec#getVersions()
*/
public static String highestPriority(List<String> versions) {
List<Version> byPriority = getByPriority(versions);
if (byPriority.isEmpty()) {
return null;
}
return byPriority.get(0).getFull();
}

private static List<Version> getByPriority(List<String> versions) {
if (versions == null
|| versions.isEmpty()) {
return Collections.emptyList();
}
return versions.stream()
.map(KubernetesVersionFactory::create)
.sorted(Collections.reverseOrder())
.collect(Collectors.toList());
}
}
Loading

0 comments on commit 28e2052

Please sign in to comment.