Skip to content

Commit

Permalink
Fix eclipse-jkube#515: Properties now get resolved in CustomResource…
Browse files Browse the repository at this point in the history
… fragments

CustomResource fragments are now deserialized/serialized into GenericCustomResource so
that they are processed with standard kubernetes types
  • Loading branch information
rohanKanojia committed Jan 27, 2021
1 parent ff0bbc8 commit 3d20220
Show file tree
Hide file tree
Showing 16 changed files with 910 additions and 190 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 #535: Bump JKube base images to 0.0.9
* Fix #509: Port of ServiceDiscoveryEnricher from FMP
* Fix #511: Namespace as resource fragment results in NullPointerException
* Fix #515: Properties now get resolved in CustomResource fragments
* Fix #521: NPE on Buildconfig#getContextDir if `<dockerFile>` references a file with no directory

### 1.0.2 (2020-10-30)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
/**
* 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 com.fasterxml.jackson.annotation.JsonAnyGetter;
import com.fasterxml.jackson.annotation.JsonAnySetter;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import io.fabric8.kubernetes.api.model.HasMetadata;
import io.fabric8.kubernetes.api.model.ObjectMeta;

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

// Required if using any of the ObjectMappers provided by KubernetesDeserializer (yamlMapper, jsonMapper)
@JsonDeserialize(
using = JsonDeserializer.None.class
)
@JsonInclude(JsonInclude.Include.NON_NULL)
public class GenericCustomResource implements HasMetadata {

@JsonProperty("apiVersion")
private String apiVersion;
@JsonProperty("kind")
private String kind;
@JsonProperty("metadata")
private ObjectMeta metadata;
@JsonIgnore
private Map<String, Object> additionalProperties = new HashMap<>();

public String getApiVersion() {
return apiVersion;
}

public void setApiVersion(String apiVersion) {
this.apiVersion = apiVersion;
}

@Override
public String getKind() {
return kind;
}

public void setKind(String kind) {
this.kind = kind;
}

public ObjectMeta getMetadata() {
return metadata;
}

public void setMetadata(ObjectMeta metadata) {
this.metadata = metadata;
}

@JsonAnyGetter
public Map<String, Object> getAdditionalProperties() {
return this.additionalProperties;
}

public void setAdditionalProperties(Map<String, Object> additionalProperties) {
this.additionalProperties = additionalProperties;
}

@JsonAnySetter
public void setAdditionalProperty(String name, Object value) {
this.additionalProperties.put(name, value);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/**
* 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 io.fabric8.kubernetes.api.builder.BaseFluent;
import io.fabric8.kubernetes.api.builder.VisitableBuilder;
import io.fabric8.kubernetes.api.model.ObjectMetaBuilder;

import java.util.HashMap;

public class GenericCustomResourceBuilder extends BaseFluent<GenericCustomResourceBuilder>
implements VisitableBuilder<GenericCustomResource, GenericCustomResourceBuilder> {

private final GenericCustomResource genericCustomResource;
private final ObjectMetaBuilder metadata;

public GenericCustomResourceBuilder(GenericCustomResource item) {
this.genericCustomResource = new GenericCustomResource();
this.genericCustomResource.setApiVersion(item.getApiVersion());
this.genericCustomResource.setKind(item.getKind());
this.genericCustomResource.setAdditionalProperties(new HashMap<>(item.getAdditionalProperties()));
metadata = item.getMetadata() == null ? new ObjectMetaBuilder() : new ObjectMetaBuilder(item.getMetadata());
_visitables.get("metadata").add(this.metadata);
}

@Override
public GenericCustomResource build() {
genericCustomResource.setMetadata(metadata.build());
return genericCustomResource;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
Expand Down Expand Up @@ -67,6 +66,9 @@
import io.fabric8.kubernetes.api.model.ReplicationController;
import io.fabric8.kubernetes.api.model.ReplicationControllerSpec;
import io.fabric8.kubernetes.api.model.Service;
import io.fabric8.kubernetes.api.model.apiextensions.v1beta1.CustomResourceDefinition;
import io.fabric8.kubernetes.api.model.apiextensions.v1beta1.CustomResourceDefinitionList;
import io.fabric8.kubernetes.api.model.apiextensions.v1beta1.CustomResourceDefinitionVersion;
import io.fabric8.kubernetes.api.model.apps.DaemonSet;
import io.fabric8.kubernetes.api.model.apps.DaemonSetSpec;
import io.fabric8.kubernetes.api.model.apps.Deployment;
Expand All @@ -87,14 +89,15 @@
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.utils.Serialization;
import io.fabric8.kubernetes.client.utils.ApiVersionUtil;
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.Project;
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;

Expand Down Expand Up @@ -509,7 +512,7 @@ public static void handleKubernetesClientException(KubernetesClientException e,
}

public static Set<HasMetadata> loadResources(File manifest) throws IOException {
Object dto = ResourceUtil.load(manifest, KubernetesResource.class);
Object dto = ResourceUtil.loadKubernetesResourceList(manifest);
if (dto == null) {
throw new IllegalStateException("Cannot load kubernetes manifest " + manifest);
}
Expand Down Expand Up @@ -870,28 +873,6 @@ private static File[] listRemoteResourceFragments(List<String> remotes, KitLogge
return new File[0];
}

@SuppressWarnings("unchecked")
public static Map<String, Object> unmarshalCustomResourceFile(File customResourceFile) throws IOException {
return Serialization.unmarshal(new FileInputStream(customResourceFile), Map.class);
}

public static Map<File, String> getCustomResourcesFileToNameMap(
File resourceDir, List<String> remotes, KitLogger log) throws IOException {

Map<File, String> fileToCrdGroupMap = new HashMap<>();
File[] resourceFiles = listResourceFragments(resourceDir, remotes, log);

for (File file : resourceFiles) {
if (file.getName().endsWith("cr.yml") || file.getName().endsWith("cr.yaml")) {
Map<String, Object> customResource = unmarshalCustomResourceFile(file);
String apiVersion = customResource.get("apiVersion").toString();
String kind = customResource.get("kind").toString();
fileToCrdGroupMap.put(file, apiVersion + "#" + kind);
}
}
return fileToCrdGroupMap;
}

public static String getFullyQualifiedApiGroupWithKind(CustomResourceDefinitionContext crdContext) {
return crdContext.getGroup() + "/" + crdContext.getVersion() + "#" + crdContext.getKind();
}
Expand Down Expand Up @@ -988,5 +969,41 @@ public static ContainerPort addPort(String portNumberText, String portName, KitL
public static boolean isControllerResource(HasMetadata h) {
return Arrays.stream(POD_CONTROLLER_KINDS).anyMatch(c -> c.equals(h.getKind()));
}

public static CustomResourceDefinitionContext getCrdContext(CustomResourceDefinitionList customResourceDefinitionList, GenericCustomResource customResource) {
for (CustomResourceDefinition crd: customResourceDefinitionList.getItems()) {
if (isCrdForCustomResource(customResource, crd)) {
return CustomResourceDefinitionContext.fromCrd(crd);
}
}
return null;
}

private static boolean isCrdForCustomResource(GenericCustomResource customResource, CustomResourceDefinition crd) {
String group = ApiVersionUtil.trimGroup(customResource.getApiVersion());
String version = ApiVersionUtil.trimVersion(customResource.getApiVersion());
String kind = customResource.getKind();

return crdHasGroup(crd, group) && crdHasVersion(crd, version) && crdHasKind(crd, kind);
}

private static boolean crdHasKind(CustomResourceDefinition crd, String kind) {
return crd.getSpec().getNames().getKind().equals(kind);
}

private static boolean crdHasGroup(CustomResourceDefinition crd, String group) {
return crd.getSpec().getGroup().equals(group);
}

private static boolean crdHasVersion(CustomResourceDefinition crd, String version) {
if (crd.getSpec().getVersion() != null && crd.getSpec().getVersion().equals(version)) {
return true;
}

return crd.getSpec().getVersions()
.stream()
.map(CustomResourceDefinitionVersion::getName)
.anyMatch(n -> n.equals(version));
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,19 @@
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.google.gson.JsonObject;
import io.fabric8.kubernetes.api.model.KubernetesResource;
import org.apache.commons.io.FilenameUtils;
import org.eclipse.jkube.kit.common.GenericCustomResource;
import org.eclipse.jkube.kit.common.ResourceFileType;
import org.apache.commons.lang3.StringUtils;

Expand All @@ -50,6 +55,42 @@ public static <T> List<T> loadList(File file, Class<T> clazz) throws IOException
return getObjectMapper(ResourceFileType.fromFile(file)).readerFor(clazz).<T>readValues(file).readAll();
}

public static List<KubernetesResource> loadKubernetesResourceList(File file) throws IOException {
ResourceFileType resourceFileType = ResourceFileType.fromFile(file);
List<KubernetesResource> kubernetesResources = new ArrayList<>();
if (file.isFile()) {
String fileContentAsStr = new String(Files.readAllBytes(file.toPath()));
if (StringUtils.isNotBlank(fileContentAsStr)) {
kubernetesResources.addAll(loadKubernetesResourceListFromString(resourceFileType, fileContentAsStr));

}
}
return kubernetesResources;
}

private static List<KubernetesResource> loadKubernetesResourceListFromString(ResourceFileType resourceFileType, String fileContentAsStr) throws JsonProcessingException {
Map<String, Object> listAsMap = getObjectMapper(resourceFileType).readValue(fileContentAsStr, Map.class);
List<KubernetesResource> kubernetesResources = new ArrayList<>();
List<Map<String, Object>> items = (List<Map<String, Object>>)listAsMap.get("items");
for (Map<String, Object> item : items) {
KubernetesResource resource = getHasMetadataOrGenericResource(resourceFileType, item);
kubernetesResources.add(resource);
}
return kubernetesResources;
}

private static KubernetesResource getHasMetadataOrGenericResource(ResourceFileType resourceFileType, Map<String, Object> item) {
try {
return convertValue(resourceFileType, item, KubernetesResource.class);
} catch (IllegalArgumentException illegalArgumentException) {
return convertValue(resourceFileType, item, GenericCustomResource.class);
}
}

private static <T> T convertValue(ResourceFileType resourceFileType, Map<String, Object> item, Class<T> clazz) {
return getObjectMapper(resourceFileType).convertValue(item, clazz);
}

public static <T> T load(File file, Class<T> clazz) throws IOException {
ResourceFileType type = ResourceFileType.fromFile(file);
return load(file, clazz, type);
Expand Down
Loading

0 comments on commit 3d20220

Please sign in to comment.