Skip to content

Commit

Permalink
feat: prevent conflicts when switching deployment kinds
Browse files Browse the repository at this point in the history
(cherry picked from commit 2d04805)
  • Loading branch information
iocanel authored and gsmet committed Mar 6, 2024
1 parent 0191094 commit 6eb2419
Show file tree
Hide file tree
Showing 7 changed files with 125 additions and 38 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
import io.quarkus.deployment.annotations.BuildProducer;
import io.quarkus.deployment.annotations.BuildStep;
import io.quarkus.deployment.builditem.ApplicationInfoBuildItem;
import io.quarkus.kubernetes.deployment.DeploymentResourceKind;
import io.quarkus.kubernetes.deployment.OpenshiftConfig;
import io.quarkus.kubernetes.deployment.OpenshiftConfig.DeploymentResourceKind;
import io.quarkus.kubernetes.deployment.ResourceNameUtil;
import io.quarkus.kubernetes.spi.KubernetesDeploymentTargetBuildItem;
import io.quarkus.kubernetes.spi.KubernetesResourceMetadataBuildItem;
Expand All @@ -22,13 +22,13 @@ public void checkOpenshift(ApplicationInfoBuildItem applicationInfo, Capabilitie
DeploymentResourceKind deploymentResourceKind = config.getDeploymentResourceKind(capabilities);
deploymentTargets
.produce(
new KubernetesDeploymentTargetBuildItem(OPENSHIFT, deploymentResourceKind.kind,
deploymentResourceKind.apiGroup,
deploymentResourceKind.apiVersion, true,
new KubernetesDeploymentTargetBuildItem(OPENSHIFT, deploymentResourceKind.getKind(),
deploymentResourceKind.getGroup(),
deploymentResourceKind.getVersion(), true,
config.getDeployStrategy()));

String name = ResourceNameUtil.getResourceName(config, applicationInfo);
resourceMeta.produce(new KubernetesResourceMetadataBuildItem(OPENSHIFT, deploymentResourceKind.apiGroup,
deploymentResourceKind.apiVersion, deploymentResourceKind.kind, name));
resourceMeta.produce(new KubernetesResourceMetadataBuildItem(OPENSHIFT, deploymentResourceKind.getGroup(),
deploymentResourceKind.getVersion(), deploymentResourceKind.getKind(), name));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@

import java.util.Set;

import io.dekorate.utils.Strings;
import io.fabric8.kubernetes.api.model.HasMetadata;

public enum DeploymentResourceKind {

Deployment(DEPLOYMENT, DEPLOYMENT_GROUP, DEPLOYMENT_VERSION),
Expand All @@ -29,23 +32,60 @@ public enum DeploymentResourceKind {
CronJob(CRONJOB, BATCH_GROUP, BATCH_VERSION),
KnativeService(KNATIVE_SERVICE, KNATIVE_SERVICE_GROUP, KNATIVE_SERVICE_VERSION, KNATIVE);

public final String kind;
public final String apiGroup;
public final String apiVersion;
public final Set<String> requiredTargets;
private final String kind;
private final String group;
private final String version;
private final Set<String> requiredTargets;

DeploymentResourceKind(String kind, String apiGroup, String apiVersion, String... requiredTargets) {
this(kind, apiGroup, apiVersion, Set.of(requiredTargets));
DeploymentResourceKind(String kind, String group, String version, String... requiredTargets) {
this(kind, group, version, Set.of(requiredTargets));
}

DeploymentResourceKind(String kind, String apiGroup, String apiVersion, Set<String> requiredTargets) {
DeploymentResourceKind(String kind, String group, String version, Set<String> requiredTargets) {
this.kind = kind;
this.apiGroup = apiGroup;
this.apiVersion = apiVersion;
this.group = group;
this.version = version;
this.requiredTargets = requiredTargets;
}

public static final DeploymentResourceKind find(String apiGroup, String apiVersion, String kind) {
for (DeploymentResourceKind deploymentResourceKind : DeploymentResourceKind.values()) {
if (deploymentResourceKind.kind.equals(kind) && deploymentResourceKind.group.equals(apiGroup)
&& deploymentResourceKind.version.equals(apiVersion)) {
return deploymentResourceKind;
}
}
String apiGroupVersion = Strings.isNullOrEmpty(apiGroup) ? apiVersion : apiGroup + "/" + apiVersion;
throw new IllegalArgumentException("Could not find DeploymentResourceKind for " + apiGroupVersion + " " + kind);
}

public boolean isAvailalbleOn(String target) {
return requiredTargets.isEmpty() || requiredTargets.contains(target);
}

public boolean matches(HasMetadata resource) {
String resourceKind = HasMetadata.getKind(resource.getClass());
String resourceVersion = HasMetadata.getApiVersion(resource.getClass());
return resourceKind.equals(getKind()) && resourceVersion.equals(getApiVersion());
}

public String getKind() {
return kind;
}

public String getGroup() {
return group;
}

public String getVersion() {
return version;
}

public Set<String> getRequiredTargets() {
return requiredTargets;
}

public String getApiVersion() {
return group + "/" + version;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@

public class DeploymentTargetEntry {
private final String name;
private final String kind;
private final DeploymentResourceKind deploymentResourceKind;
private final int priority;
private final DeployStrategy deployStrategy;

public DeploymentTargetEntry(String name, String kind, int priority, DeployStrategy deployStrategy) {
public DeploymentTargetEntry(String name, DeploymentResourceKind kind, int priority, DeployStrategy deployStrategy) {
this.name = name;
this.kind = kind;
this.deploymentResourceKind = kind;
this.priority = priority;
this.deployStrategy = deployStrategy;
}
Expand All @@ -19,8 +19,8 @@ public String getName() {
return name;
}

public String getKind() {
return kind;
public DeploymentResourceKind getDeploymentResourceKind() {
return deploymentResourceKind;
}

public int getPriority() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@
import io.quarkus.kubernetes.spi.KubernetesDeploymentClusterBuildItem;
import io.quarkus.kubernetes.spi.KubernetesOptionalResourceDefinitionBuildItem;
import io.quarkus.kubernetes.spi.KubernetesOutputDirectoryBuildItem;
import io.quarkus.logging.Log;

public class KubernetesDeployer {

Expand Down Expand Up @@ -198,16 +199,31 @@ private DeploymentResultBuildItem deploy(DeploymentTargetEntry deploymentTarget,

try (FileInputStream fis = new FileInputStream(manifest)) {
KubernetesList list = Serialization.unmarshalAsList(fis);

Optional<GenericKubernetesResource> conflictingResource = findConflictingResource(client, deploymentTarget,
list.getItems());
if (conflictingResource.isPresent()) {
String messsage = "Skipping deployment of " + deploymentTarget.getDeploymentResourceKind() + " "
+ conflictingResource.get().getMetadata().getName() + " because a "
+ conflictingResource.get().getKind() + " with the same name exists.";
log.warn(messsage);
Log.warn("This may occur when switching deployment targets, or when the default deployment target is changed.");
Log.warn("Please remove conflicting resource and try again.");
throw new IllegalStateException(messsage);
}

list.getItems().stream().filter(distinctByResourceKey()).forEach(i -> {
deployResource(deploymentTarget, client, i, optionalResourceDefinitions);
log.info("Applied: " + i.getKind() + " " + i.getMetadata().getName() + ".");
});

printExposeInformation(client, list, openshiftConfig, applicationInfo);

HasMetadata m = list.getItems().stream().filter(r -> r.getKind().equals(deploymentTarget.getKind()))
HasMetadata m = list.getItems().stream()
.filter(r -> deploymentTarget.getDeploymentResourceKind().matches(r))
.findFirst().orElseThrow(() -> new IllegalStateException(
"No " + deploymentTarget.getKind() + " found under: " + manifest.getAbsolutePath()));
"No " + deploymentTarget.getDeploymentResourceKind() + " found under: "
+ manifest.getAbsolutePath()));
return new DeploymentResultBuildItem(m.getMetadata().getName(), m.getMetadata().getLabels());
} catch (FileNotFoundException e) {
throw new IllegalStateException("Can't find generated kubernetes manifest: " + manifest.getAbsolutePath());
Expand Down Expand Up @@ -255,6 +271,35 @@ private void deployResource(DeploymentTargetEntry deploymentTarget, KubernetesCl
}
}

private Optional<GenericKubernetesResource> findConflictingResource(KubernetesClient clinet,
DeploymentTargetEntry deploymentTarget, List<HasMetadata> generated) {
HasMetadata deploymentResource = generated.stream()
.filter(r -> deploymentTarget.getDeploymentResourceKind().matches(r))
.findFirst()
.orElseThrow(() -> new IllegalStateException(
"No " + deploymentTarget.getDeploymentResourceKind() + " found under: " + deploymentTarget.getName()));
String name = deploymentResource.getMetadata().getName();

for (DeploymentResourceKind deploymentKind : DeploymentResourceKind.values()) {
if (deploymentKind.matches(deploymentResource)) {
continue;
}
try {
GenericKubernetesResource resource = clinet
.genericKubernetesResources(deploymentKind.getApiVersion(), deploymentKind.getKind()).withName(name)
.get();
if (resource != null) {
Log.warn("Found conflicting resource:" + resource.getApiVersion() + "/" + resource.getKind() + ":"
+ resource.getMetadata().getName());
return Optional.of(resource);
}
} catch (KubernetesClientException e) {
// ignore
}
}
return Optional.empty();
}

private void deleteResource(HasMetadata metadata, Resource<?> r) {
r.delete();
try {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,9 +79,10 @@ public EnabledKubernetesDeploymentTargetsBuildItem enabledKubernetesDeploymentTa
List<DeploymentTargetEntry> entries = new ArrayList<>(mergedDeploymentTargets.size());
for (KubernetesDeploymentTargetBuildItem deploymentTarget : mergedDeploymentTargets) {
if (deploymentTarget.isEnabled()) {
entries.add(new DeploymentTargetEntry(deploymentTarget.getName(),
deploymentTarget.getKind(), deploymentTarget.getPriority(),
deploymentTarget.getDeployStrategy()));
DeploymentResourceKind deploymentResourceKind = DeploymentResourceKind.find(deploymentTarget.getGroup(),
deploymentTarget.getVersion(), deploymentTarget.getKind());
entries.add(new DeploymentTargetEntry(deploymentTarget.getName(), deploymentResourceKind,
deploymentTarget.getPriority(), deploymentTarget.getDeployStrategy()));
}
}
return new EnabledKubernetesDeploymentTargetsBuildItem(entries);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,12 +93,13 @@ public void checkOpenshift(ApplicationInfoBuildItem applicationInfo, Capabilitie

DeploymentResourceKind deploymentResourceKind = config.getDeploymentResourceKind(capabilities);
deploymentTargets.produce(
new KubernetesDeploymentTargetBuildItem(OPENSHIFT, deploymentResourceKind.kind, deploymentResourceKind.apiGroup,
deploymentResourceKind.apiVersion, OPENSHIFT_PRIORITY, openshiftEnabled, config.deployStrategy));
new KubernetesDeploymentTargetBuildItem(OPENSHIFT, deploymentResourceKind.getKind(),
deploymentResourceKind.getGroup(),
deploymentResourceKind.getVersion(), OPENSHIFT_PRIORITY, openshiftEnabled, config.deployStrategy));
if (openshiftEnabled) {
String name = ResourceNameUtil.getResourceName(config, applicationInfo);
resourceMeta.produce(new KubernetesResourceMetadataBuildItem(OPENSHIFT, deploymentResourceKind.apiGroup,
deploymentResourceKind.apiVersion, deploymentResourceKind.kind, name));
resourceMeta.produce(new KubernetesResourceMetadataBuildItem(OPENSHIFT, deploymentResourceKind.getGroup(),
deploymentResourceKind.getVersion(), deploymentResourceKind.getKind(), name));
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@

package io.quarkus.kubernetes.deployment;

import static io.quarkus.kubernetes.deployment.Constants.DEPLOYMENT_GROUP;
import static io.quarkus.kubernetes.deployment.Constants.DEPLOYMENT_VERSION;
import static io.quarkus.kubernetes.deployment.Constants.INGRESS;
import static io.quarkus.kubernetes.deployment.Constants.KUBERNETES;
import static io.quarkus.kubernetes.deployment.Constants.LIVENESS_PROBE;
Expand Down Expand Up @@ -76,22 +74,24 @@ public void checkVanillaKubernetes(ApplicationInfoBuildItem applicationInfo, Cap
KubernetesConfig config,
BuildProducer<KubernetesDeploymentTargetBuildItem> deploymentTargets,
BuildProducer<KubernetesResourceMetadataBuildItem> resourceMeta) {
String kind = config.getDeploymentResourceKind(capabilities).kind;
DeploymentResourceKind deploymentResourceKind = config.getDeploymentResourceKind(capabilities);

List<String> userSpecifiedDeploymentTargets = KubernetesConfigUtil.getConfiguredDeploymentTargets();
if (userSpecifiedDeploymentTargets.isEmpty() || userSpecifiedDeploymentTargets.contains(KUBERNETES)) {
// when nothing was selected by the user, we enable vanilla Kubernetes by default
deploymentTargets.produce(new KubernetesDeploymentTargetBuildItem(KUBERNETES, kind, DEPLOYMENT_GROUP,
DEPLOYMENT_VERSION, VANILLA_KUBERNETES_PRIORITY, true, config.deployStrategy));
deploymentTargets.produce(new KubernetesDeploymentTargetBuildItem(KUBERNETES,
deploymentResourceKind.getKind(), deploymentResourceKind.getGroup(), deploymentResourceKind.getVersion(),
VANILLA_KUBERNETES_PRIORITY, true, config.deployStrategy));

String name = ResourceNameUtil.getResourceName(config, applicationInfo);
resourceMeta.produce(new KubernetesResourceMetadataBuildItem(KUBERNETES, DEPLOYMENT_GROUP, DEPLOYMENT_VERSION,
kind, name));
resourceMeta.produce(new KubernetesResourceMetadataBuildItem(KUBERNETES, deploymentResourceKind.getGroup(),
deploymentResourceKind.getVersion(), deploymentResourceKind.getKind(), name));

} else {
deploymentTargets
.produce(new KubernetesDeploymentTargetBuildItem(KUBERNETES, kind, DEPLOYMENT_GROUP,
DEPLOYMENT_VERSION, VANILLA_KUBERNETES_PRIORITY, false, config.deployStrategy));
.produce(new KubernetesDeploymentTargetBuildItem(KUBERNETES, deploymentResourceKind.getKind(),
deploymentResourceKind.getGroup(),
deploymentResourceKind.getVersion(), VANILLA_KUBERNETES_PRIORITY, false, config.deployStrategy));
}
}

Expand Down

0 comments on commit 6eb2419

Please sign in to comment.