Skip to content

Commit

Permalink
fix(fabric8io#1372): Set Kind/Filename mapping as AsciiDoc file (fabr…
Browse files Browse the repository at this point in the history
…ic8io#1381)

The mapping between Kind and Filename is moved to an AsciiDoc file, so it can be reused in documentation as well.

By default, it loads this mapping from `/fabric8/default-kind-filename-mapping.adoc` classpath location, but using env variable or system property named `fabric8.mapping` you can specify your own file.

Initially will inspect classpath and if it returns null, then it will treat `fabric8.mapping` value as a disk location.
  • Loading branch information
lordofthejars authored and rhuss committed Sep 16, 2018
1 parent 321b4b3 commit 71fca8b
Show file tree
Hide file tree
Showing 16 changed files with 591 additions and 67 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ We use semantic versioning in some slight variation until our feature set has st

After this we will switch probably to real [Semantic Versioning 2.0.0](http://semver.org/)

###4.0.0

* Fix 1372: Filename to type mappings should be more flexible than a string array

###3.5.42
* Fix 1021: Avoids empty deployment selector value in generated yaml resource

Expand Down
6 changes: 6 additions & 0 deletions core/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,12 @@
<scope>test</scope>
</dependency>

<dependency>
<groupId>com.github.stefanbirkner</groupId>
<artifactId>system-rules</artifactId>
<scope>test</scope>
</dependency>

<dependency>
<groupId>io.fabric8</groupId>
<artifactId>mockwebserver</artifactId>
Expand Down
157 changes: 157 additions & 0 deletions core/src/main/java/io/fabric8/maven/core/util/AsciiDocParser.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
/*
* Copyright 2016 Red Hat, Inc.
*
* Red Hat 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 io.fabric8.maven.core.util;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
* AsciiDoc utils for parsing specific elements coded in AsciiDoc format.
*/
public class AsciiDocParser {

private static final String END_TABLE = "|===";

/**
*
* This method serializes the content of an AsciiDoc table used to set the mapping between kinds and filenames.
* These tables are of form:
*
* <pre>
* [cols=2*,options="header"]
* |===
* |Filename
* |Kind
*
* a|ConfigMap
* a|`cm`
*
* |CronJob
* |cronjob
* |===
* </pre>
*
* Basically the table contains an optional attributes section (@code{[]}), then the headers of the columns.
* Between line breaks, both columns representing the kind (first column) and filename (second column).
* Notice that one-line columns definition @code{|Cell in column 1, row 1|Cell in column 1, row 1} is not supported.
*
* This method returns an @code{IllegalArgumentException} if does not contain two columns.
*
* @param table definition in AsciiDoc format.
* @return A serialization of all columns, being pair elements the first column and the odd elements the second column. In previous example @code{"cm","ConfigMap","cronjob","CronJob"}
*/
public Map<String, List<String>> serializeKindFilenameTable(final InputStream table) {

final Map<String, List<String>> serializedContent = new HashMap<>();

try (final BufferedReader tableContent = new BufferedReader(new InputStreamReader(table))) {

skipUntilColumns(tableContent);

boolean endTable = false;

while (!endTable) {
final List<String> readRow = readRow(tableContent);
final String separator = readEmptyLineOrEndTable(tableContent);

String kind = readRow.get(0);
serializedContent.put(kind, readRow.subList(1, readRow.size()));

if (END_TABLE.equals(separator)) {
endTable = true;
}

}

} catch (IOException e) {
throw new IllegalStateException(e);
}

return serializedContent;

}

private List<String> readRow(final BufferedReader tableContent) throws IOException {
final List<String> content = new ArrayList<>();

final String firstColumn = readColumn(tableContent);
final String secondColumn = readColumn(tableContent);

content.add(firstColumn);
final String[] filenameTypes = secondColumn.split(",");

for (String filenameType : filenameTypes) {
content.add(filenameType.trim());
}

return content;
}

private String readColumn(final BufferedReader tableContent) throws IOException {
final String column = tableContent.readLine();

if(column == null || column.isEmpty()) {
throw new IllegalArgumentException("Trying to read a column but white line or EOF was found.");
}

int separator;
if ((separator = column.indexOf("|")) < 0) {
throw new IllegalArgumentException(String.format("Expected the initial of a column with (|) but %s found.", column));
}

return column.trim().substring(separator + 1)
.replaceAll("[`_*]", "")
.trim();

}

/**
* Reads empty line or throw an exception if a none empty line was found.
*/
private String readEmptyLineOrEndTable(final BufferedReader tableContent) throws IOException {
final String column = tableContent.readLine();

if (column != null && column.startsWith(END_TABLE)) {
return END_TABLE;
}

if(column == null || !column.isEmpty()) {
throw new IllegalArgumentException(String.format("Trying to read an empty line for end of row, but content %s was found or EOF", column));
}

return "";
}

/**
* Moves buffer until it finds the first content column (skipping headers).
* @param tableContent
*/
private void skipUntilColumns(final BufferedReader tableContent) throws IOException {
String line;
while ((line = tableContent.readLine()) != null) {
if(line.trim().isEmpty()){
break;
}
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/*
* Copyright 2016 Red Hat, Inc.
*
* Red Hat 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 io.fabric8.maven.core.util.kubernetes;

import io.fabric8.maven.core.util.AsciiDocParser;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import java.util.Map;

public class KindFilenameMapperUtil {

public static Map<String, List<String>> loadMappings() {

final String location = "/META-INF/fabric8/kind-fileindicator-mapping-default.adoc";

try (final InputStream mappingFile = loadContent(location)) {
final AsciiDocParser asciiDocParser = new AsciiDocParser();
return asciiDocParser.serializeKindFilenameTable(mappingFile);
} catch (IOException e) {
throw new IllegalStateException(e);
}
}

private static InputStream loadContent(String location) {
InputStream resourceAsStream = KindFilenameMapperUtil.class.getResourceAsStream(location);

if (resourceAsStream == null) {
throw new IllegalArgumentException(String.format("%s cannot be found in classpath", location));
}

return resourceAsStream;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -182,64 +182,29 @@ public boolean accept(File dir, String name) {

// ========================================================================================================

private final static Map<String,String> FILENAME_TO_KIND_MAPPER = new HashMap<>();
private final static Map<String,String> KIND_TO_FILENAME_MAPPER = new HashMap<>();
private static String mappings[] =
{
// lets put the abbreviation we want to use first
"cm", "ConfigMap",
"configmap", "ConfigMap",
"cronjob", "CronJob",
"cr", "ClusterRole",
"crole", "ClusterRole",
"clusterrole", "ClusterRole",
"crd", "CustomResourceDefinition",
"crb", "ClusterRoleBinding",
"clusterrb", "ClusterRoleBinding",
"cj", "CronJob",
"deployment", "Deployment",
"is", "ImageStream",
"istag", "ImageStreamTag",
"job", "Job",
"lr", "LimitRange",
"limitrange", "LimitRange",
"ns", "Namespace",
"namespace", "Namespace",
"oauthclient", "OAuthClient",
"pb", "PolicyBinding",
"pv", "PersistentVolume",
"pvc", "PersistentVolumeClaim",
"project", "Project",
"pr", "ProjectRequest",
"rq", "ResourceQuota",
"resourcequota", "ResourceQuota",
"role", "Role",
"rb", "RoleBinding",
"rolebinding", "RoleBinding",
"rbr", "RoleBindingRestriction",
"rolebindingrestriction", "RoleBindingRestriction",
"secret", "Secret",
"service", "Service",
"svc", "Service",
"sa", "ServiceAccount",
"rc", "ReplicationController",
"rs", "ReplicaSet",
"daemonset", "DaemonSet",
"ds", "DaemonSet",
"statefulset", "StatefulSet",

// OpenShift Resources:
"bc", "BuildConfig",
"dc", "DeploymentConfig",
"deploymentconfig", "DeploymentConfig",
"route", "Route",
"template", "Template",
};
protected final static Map<String,String> FILENAME_TO_KIND_MAPPER = new HashMap<>();
protected final static Map<String,String> KIND_TO_FILENAME_MAPPER = new HashMap<>();

static {
for (int i = 0; i < mappings.length; i+=2) {
FILENAME_TO_KIND_MAPPER.put(mappings[i], mappings[i+1]);
KIND_TO_FILENAME_MAPPER.put(mappings[i+1], mappings[i]);
initializeKindFilenameMapper();
}

protected final static void initializeKindFilenameMapper() {
final Map<String, List<String>> mappings = KindFilenameMapperUtil.loadMappings();

final Set<Map.Entry<String, List<String>>> entries = mappings.entrySet();

for (Map.Entry<String, List<String>> entry : entries) {

final List<String> filenameTypes = entry.getValue();
final String kind = entry.getKey();
for (String filenameType : filenameTypes) {
FILENAME_TO_KIND_MAPPER.put(filenameType, kind);
}

// In previous version, last one wins, so we do the same.
KIND_TO_FILENAME_MAPPER.put(kind, filenameTypes.get(filenameTypes.size() - 1));

}
}

Expand Down
Loading

0 comments on commit 71fca8b

Please sign in to comment.