Skip to content

Commit

Permalink
fix: Multiple Custom Resources with same (different apiGroup) name ca…
Browse files Browse the repository at this point in the history
…n be added

Signed-off-by: Marc Nuri <[email protected]>
  • Loading branch information
manusa committed Apr 30, 2021
1 parent 9317f5e commit 8063d3d
Show file tree
Hide file tree
Showing 21 changed files with 255 additions and 101 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ Usage:
* Fix #622: Corrected documentation for `jkube-healthcheck-karaf`
* Fix #630: DeploymentConfigEnricher and DefaultControllerEnricher refactored and aligned
* Fix #639: Quotas for OpenShift BuildConfig not working
* Fix #688: Multiple Custom Resources with same (different apiGroup) name can be added

### 1.2.0 (2021-03-31)
* Fix #529: `.maven-dockerignore`, `.maven-dockerexclude`, `.maven-dockerinclude` are no longer supported
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@

import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

// Required if using any of the ObjectMappers provided by KubernetesDeserializer (yamlMapper, jsonMapper)
@JsonDeserialize(
Expand Down Expand Up @@ -82,4 +83,16 @@ public void setAdditionalProperty(String name, Object value) {
this.additionalProperties.put(name, value);
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
GenericCustomResource that = (GenericCustomResource) o;
return Objects.equals(apiVersion, that.apiVersion) && Objects.equals(kind, that.kind) && Objects.equals(metadata, that.metadata) && Objects.equals(additionalProperties, that.additionalProperties);
}

@Override
public int hashCode() {
return Objects.hash(apiVersion, kind, metadata, additionalProperties);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,14 @@
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.TreeSet;
import java.util.concurrent.CountDownLatch;
import java.util.function.Predicate;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

import org.eclipse.jkube.kit.common.GenericCustomResource;
import org.eclipse.jkube.kit.common.KitLogger;
import org.eclipse.jkube.kit.common.ResourceFileType;

import io.fabric8.kubernetes.api.model.Config;
import io.fabric8.kubernetes.api.model.Container;
Expand All @@ -49,6 +52,7 @@
import io.fabric8.kubernetes.api.model.EnvVar;
import io.fabric8.kubernetes.api.model.EnvVarBuilder;
import io.fabric8.kubernetes.api.model.HasMetadata;
import io.fabric8.kubernetes.api.model.HasMetadataComparator;
import io.fabric8.kubernetes.api.model.IntOrString;
import io.fabric8.kubernetes.api.model.KubernetesList;
import io.fabric8.kubernetes.api.model.LabelSelector;
Expand Down Expand Up @@ -85,18 +89,14 @@
import io.fabric8.kubernetes.client.dsl.LogWatch;
import io.fabric8.kubernetes.client.dsl.NonNamespaceOperation;
import io.fabric8.kubernetes.client.dsl.PodResource;
import io.fabric8.kubernetes.api.model.HasMetadataComparator;
import io.fabric8.kubernetes.client.dsl.base.CustomResourceDefinitionContext;
import io.fabric8.kubernetes.client.dsl.Resource;
import io.fabric8.kubernetes.client.dsl.base.CustomResourceDefinitionContext;
import io.fabric8.openshift.api.model.Build;
import io.fabric8.openshift.api.model.DeploymentConfig;
import io.fabric8.openshift.api.model.DeploymentConfigSpec;
import io.fabric8.openshift.api.model.Template;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.eclipse.jkube.kit.common.GenericCustomResource;
import org.eclipse.jkube.kit.common.KitLogger;
import org.eclipse.jkube.kit.common.ResourceFileType;

import static io.fabric8.kubernetes.client.utils.ApiVersionUtil.trimGroup;
import static io.fabric8.kubernetes.client.utils.ApiVersionUtil.trimVersion;
Expand Down Expand Up @@ -510,10 +510,11 @@ public static void handleKubernetesClientException(KubernetesClientException e,
}
}

public static Set<HasMetadata> loadResources(File manifest) throws IOException {
final Set<HasMetadata> entities = new TreeSet<>(new HasMetadataComparator());
entities.addAll(ResourceUtil.deserializeKubernetesListOrTemplate(manifest));
return entities;
public static List<HasMetadata> loadResources(File manifest) throws IOException {
return ResourceUtil.deserializeKubernetesListOrTemplate(manifest).stream()
.distinct()
.sorted(new HasMetadataComparator())
.collect(Collectors.toList());
}

public static String getBuildStatusPhase(Build build) {
Expand Down Expand Up @@ -606,7 +607,7 @@ public static FilterWatchListDeletable<Pod, PodList> withSelector(NonNamespaceOp
return answer;
}

public static LabelSelector extractPodLabelSelector(Set<HasMetadata> entities) {
public static LabelSelector extractPodLabelSelector(Collection<HasMetadata> entities) {
LabelSelector chosenSelector = null;
for (HasMetadata entity : entities) {
LabelSelector selector = extractPodLabelSelector(entity);
Expand Down Expand Up @@ -866,7 +867,7 @@ public static String getFullyQualifiedApiGroupWithKind(CustomResourceDefinitionC
return crdContext.getGroup() + "/" + crdContext.getVersion() + "#" + crdContext.getKind();
}

public static String getNewestApplicationPodName(KubernetesClient client, String namespace, Set<HasMetadata> resources) {
public static String getNewestApplicationPodName(KubernetesClient client, String namespace, Collection<HasMetadata> resources) {
LabelSelector selector = extractPodLabelSelector(resources);
PodList pods = client.pods().inNamespace(namespace).withLabelSelector(selector).list();
Pod newestPod = KubernetesHelper.getNewestPod(pods.getItems());
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
/**
* Copyright (c) 2019 Red Hat, Inc.
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at:
*
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Red Hat, Inc. - initial API and implementation
*/
package org.eclipse.jkube.kit.common;

import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Set;

import io.fabric8.kubernetes.api.model.ObjectMetaBuilder;
import org.junit.Test;

import static org.assertj.core.api.Assertions.assertThat;

public class GenericCustomResourceEqualsHashCodeTest {

@Test
public void equals_withNull_shouldBeFalse() {
// Given
final GenericCustomResource gcr = new GenericCustomResource();
// When - Then
final boolean result = gcr.equals(null);
// Then
assertThat(result).isFalse();
}

@Test
public void equals_withSameFields_shouldBeTrue() {
// Given
final GenericCustomResource one = initGenericCustomResource();
final GenericCustomResource other = initGenericCustomResource();
// When - Then
assertThat(one).isEqualTo(other).isNotSameAs(other);
}

@Test
public void equals_withDifferentMapFields_shouldBeFalse() {
// Given
final GenericCustomResource one = initGenericCustomResource();
final GenericCustomResource other = initGenericCustomResource();
other.setAdditionalProperty("extra", "other");
// When - Then
assertThat(one).isNotEqualTo(other).isNotSameAs(other);
}

@Test
public void hashSet_withSomeDuplicates_duplicatesAreRemoved() {
// Given
final Set<GenericCustomResource> uniqueValues = new HashSet<>();
uniqueValues.add(initGenericCustomResource());
uniqueValues.add(initGenericCustomResource());
final GenericCustomResource different = initGenericCustomResource();
different.setKind("Other");
uniqueValues.add(different);
// When - Then
assertThat(uniqueValues).hasSize(2).extracting("kind").containsExactlyInAnyOrder("Kind", "Other");
}

private GenericCustomResource initGenericCustomResource() {
final GenericCustomResource gcr = new GenericCustomResource();
gcr.setApiVersion("v1");
gcr.setKind("Kind");
gcr.setMetadata(new ObjectMetaBuilder().withName("name").build());
gcr.setAdditionalProperties(new HashMap<>(Collections.singletonMap("key", "value")));
return gcr;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,32 @@
*/
package org.eclipse.jkube.kit.common.util;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.eclipse.jkube.kit.common.GenericCustomResource;
import org.eclipse.jkube.kit.common.KitLogger;

import io.fabric8.kubernetes.api.model.ConfigMapBuilder;
import io.fabric8.kubernetes.api.model.ContainerPort;
import io.fabric8.kubernetes.api.model.ContainerPortBuilder;
import io.fabric8.kubernetes.api.model.EnvVar;
import io.fabric8.kubernetes.api.model.EnvVarBuilder;
import io.fabric8.kubernetes.api.model.HasMetadata;
import io.fabric8.kubernetes.api.model.Namespace;
import io.fabric8.kubernetes.api.model.NamespaceBuilder;
import io.fabric8.kubernetes.api.model.ObjectMetaBuilder;
import io.fabric8.kubernetes.api.model.Quantity;
import io.fabric8.kubernetes.api.model.ReplicationControllerBuilder;
import io.fabric8.kubernetes.api.model.Service;
import io.fabric8.kubernetes.api.model.ServiceBuilder;
import io.fabric8.kubernetes.api.model.apiextensions.v1beta1.CustomResourceDefinition;
import io.fabric8.kubernetes.api.model.apiextensions.v1beta1.CustomResourceDefinitionBuilder;
import io.fabric8.kubernetes.api.model.apiextensions.v1beta1.CustomResourceDefinitionList;
Expand All @@ -35,32 +50,16 @@
import io.fabric8.kubernetes.api.model.apps.DeploymentBuilder;
import io.fabric8.kubernetes.api.model.apps.ReplicaSetBuilder;
import io.fabric8.kubernetes.api.model.apps.StatefulSetBuilder;
import io.fabric8.kubernetes.client.dsl.base.CustomResourceDefinitionContext;
import io.fabric8.openshift.api.model.DeploymentConfigBuilder;
import io.fabric8.kubernetes.api.model.Service;
import io.fabric8.kubernetes.api.model.ServiceBuilder;
import io.fabric8.kubernetes.client.KubernetesClient;
import io.fabric8.kubernetes.client.dsl.Resource;
import io.fabric8.kubernetes.client.dsl.base.CustomResourceDefinitionContext;
import io.fabric8.openshift.api.model.DeploymentConfigBuilder;
import io.fabric8.openshift.api.model.Template;
import mockit.Expectations;
import mockit.Mocked;
import mockit.Verifications;
import org.eclipse.jkube.kit.common.GenericCustomResource;
import org.eclipse.jkube.kit.common.KitLogger;
import org.junit.Test;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;

import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
Expand Down Expand Up @@ -420,7 +419,7 @@ public void loadResourcesWithNestedTemplateAndDuplicateResources() throws IOExce
final File manifest = new File(KubernetesHelperTest.class.getResource(
"/util/kubernetes-helper/list-with-duplicates-and-template.yml").getFile());
// When
final Set<HasMetadata> result = KubernetesHelper.loadResources(manifest);
final List<HasMetadata> result = KubernetesHelper.loadResources(manifest);
// Then
assertThat(result)
.hasSize(3)
Expand All @@ -429,6 +428,21 @@ public void loadResourcesWithNestedTemplateAndDuplicateResources() throws IOExce
.containsExactly("should-be-first", "custom-resource", "template-example");
}

@Test
public void loadResourcesWithDuplicateAndSameNameCustomResources() throws IOException {
// Given
final File manifest = new File(KubernetesHelperTest.class.getResource(
"/util/kubernetes-helper/list-with-duplicates-and-same-name-custom-resource.yml").getFile());
// When
final List<HasMetadata> result = KubernetesHelper.loadResources(manifest);
// Then
assertThat(result)
.hasSize(3)
.hasOnlyElementsOfTypes(Namespace.class, GenericCustomResource.class)
.extracting("metadata.name")
.containsExactly("should-be-first", "custom-resource", "custom-resource");
}

@Test
public void testGetCrdContextReturnsValidCrdContext() {
// Given
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
---
apiVersion: v1
kind: List
items:
- apiVersion: custom.resource.example.com/v1
kind: Example
metadata:
labels:
app: custom-resources
name: custom-resource
namespace: code
spec:
field: i-have-a-duplicate
- apiVersion: custom.resource.example.com/v1
kind: Example
metadata:
labels:
app: custom-resources
name: custom-resource
namespace: code
spec:
field: i-have-a-duplicate
- apiVersion: custom-other.resource.example.com/v1
kind: Example
metadata:
labels:
app: custom-resources
name: custom-resource
namespace: code
spec:
field: not-the-same
- apiVersion: v1
kind: Namespace
metadata:
labels:
provider: jkube
name: should-be-first
- apiVersion: v1
kind: Namespace
metadata:
labels:
provider: jkube
name: should-be-first
Original file line number Diff line number Diff line change
Expand Up @@ -1407,14 +1407,14 @@ public void setRollingUpgradePreserveScale(boolean rollingUpgradePreserveScale)
this.rollingUpgradePreserveScale = rollingUpgradePreserveScale;
}

public void applyEntities(String fileName, Set<HasMetadata> entities, KitLogger serviceLogger,
public void applyEntities(String fileName, Collection<HasMetadata> entities, KitLogger serviceLogger,
long serviceUrlWaitTimeSeconds) throws InterruptedException {

applyStandardEntities(fileName, getK8sListWithNamespaceFirst(entities));
logExposeServiceUrl(entities, serviceLogger, serviceUrlWaitTimeSeconds);
}

private void logExposeServiceUrl(Set<HasMetadata> entities, KitLogger serviceLogger, long serviceUrlWaitTimeSeconds) throws InterruptedException {
private void logExposeServiceUrl(Collection<HasMetadata> entities, KitLogger serviceLogger, long serviceUrlWaitTimeSeconds) throws InterruptedException {
String url = KubernetesHelper.getServiceExposeUrl(kubernetesClient, entities, serviceUrlWaitTimeSeconds, JKubeAnnotations.SERVICE_EXPOSE_URL.value());
if (url != null) {
serviceLogger.info("ExposeController Service URL: %s", url);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@

import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.TreeMap;
import java.util.function.Consumer;
import java.util.function.Predicate;
Expand Down Expand Up @@ -70,7 +70,7 @@ public DebugService(KitLogger log, KubernetesClient kubernetesClient, PortForwar
}

public void debug(
String namespace, String fileName, Set<HasMetadata> entities, String localDebugPort, boolean debugSuspend, KitLogger podWaitLog
String namespace, String fileName, Collection<HasMetadata> entities, String localDebugPort, boolean debugSuspend, KitLogger podWaitLog
) {
if (!isDebugApplicable(entities)) {
log.error("Unable to proceed with Debug. No application resource found running in the cluster");
Expand All @@ -96,7 +96,7 @@ public void debug(
* @param entities list of Kubernetes resources generated by plugin
* @return boolean value indicating whether debug should be done or not
*/
private boolean isDebugApplicable(Set<HasMetadata> entities) {
private boolean isDebugApplicable(Collection<HasMetadata> entities) {
boolean controllersApplied = !entities.isEmpty();
for (HasMetadata h : entities) {
if (KubernetesHelper.isControllerResource(h)) {
Expand Down
Loading

0 comments on commit 8063d3d

Please sign in to comment.