
io.fabric8.maven.AbstractFabric8Mojo Maven / Gradle / Ivy
/**
* Copyright 2005-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;
import io.fabric8.devops.ProjectConfig;
import io.fabric8.devops.ProjectConfigs;
import io.fabric8.devops.ProjectRepositories;
import io.fabric8.kubernetes.api.Annotations;
import io.fabric8.kubernetes.api.KubernetesHelper;
import io.fabric8.kubernetes.api.ServiceNames;
import io.fabric8.kubernetes.api.model.HasMetadata;
import io.fabric8.kubernetes.api.model.KubernetesList;
import io.fabric8.kubernetes.api.model.ReplicationController;
import io.fabric8.kubernetes.api.model.Service;
import io.fabric8.kubernetes.client.KubernetesClient;
import io.fabric8.maven.support.JsonSchema;
import io.fabric8.maven.support.JsonSchemas;
import io.fabric8.openshift.api.model.DeploymentConfig;
import io.fabric8.openshift.api.model.Template;
import io.fabric8.utils.Files;
import io.fabric8.utils.GitHelpers;
import io.fabric8.utils.Objects;
import io.fabric8.utils.Strings;
import io.fabric8.utils.Systems;
import io.fabric8.utils.URLUtils;
import org.apache.maven.artifact.Artifact;
import org.apache.maven.model.DistributionManagement;
import org.apache.maven.model.Site;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.project.MavenProject;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.storage.file.FileRepositoryBuilder;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.InputStream;
import java.net.ConnectException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.TreeSet;
import java.util.jar.JarEntry;
import java.util.jar.JarInputStream;
import static io.fabric8.utils.PropertiesHelper.findPropertiesWithPrefix;
import static io.fabric8.utils.PropertiesHelper.toMap;
/**
* Abstract base class for Fabric8 based Mojos
*/
public abstract class AbstractFabric8Mojo extends AbstractNamespacedMojo {
private static final String DEFAULT_CONFIG_FILE_NAME = "kubernetes.json";
public static String[] ICON_EXTENSIONS = new String[]{".svg", ".png", ".gif", ".jpg", ".jpeg"};
/**
* Name of the created app zip file
*/
@Parameter(property = "fabric8.zip.file", defaultValue = "${project.build.directory}/${project.artifactId}-${project.version}-app.zip")
protected File zipFile;
/**
* The folder used for defining project specific files
*/
@Parameter(property = "fabric8.source.dir", defaultValue = "${basedir}/src/main/fabric8")
protected File appConfigDir;
/**
* Provides the resource name of the icon to use; found using the current classpath (including the ones shipped inside the maven plugin).
*
* You can refer to a common set of icons by setting this option to a value of:
*
* - activemq
* - camel
* - java
* - jetty
* - karaf
* - mule
* - spring-boot
* - tomcat
* - tomee
* - weld
* - wildfly
*
*/
@Parameter(property = "fabric8.iconRef")
protected String iconRef;
/**
* The generated kubernetes JSON file
*/
@Parameter(property = "fabric8.json.target", defaultValue = "${basedir}/target/classes/kubernetes.json")
private File kubernetesJson;
/**
* The source kubernetes JSON file
*/
@Parameter(property = "fabric8.json.source", defaultValue = "${basedir}/src/main/fabric8/kubernetes.json")
protected File kubernetesSourceJson;
/**
* Whether we should combine kubernetes JSON dependencies on the classpath into the generated JSON
*/
@Parameter(property = "fabric8.combineDependencies", defaultValue = "false")
protected boolean combineDependencies;
/**
* The generated kubernetes JSON file dependencies on the classpath
*/
@Parameter(property = "fabric8.combineJson.target")
private File kubernetesCombineJson;
/**
* Should we exclude OpenShift templates and any extensions like OAuthConfigs in the generated or combined JSON?
*/
@Parameter(property = "fabric8.pureKubernetes", defaultValue = "false")
protected boolean pureKubernetes;
/**
* The number of replicas of this container if we are auto generating the kubernetes JSON file (creating
* a Replication Controller if this value
* is greater than 0 or a pod if not).
*/
@Parameter(property = "fabric8.replicas", defaultValue = "1")
private Integer replicas;
/**
* Whether or not we should ignoreProject this maven project from goals like fabric8:deploy
*/
@Parameter(property = "fabric8.ignoreProject", defaultValue = "false")
private boolean ignoreProject;
/**
* The properties file used to specify environment variables which allows ${FOO_BAR} expressions to be used
* without any Maven property expansion
*/
@Parameter(property = "fabric8.envProperties", defaultValue = "${basedir}/src/main/fabric8/env.properties")
protected File envPropertiesFile;
/**
* Specifies a file which maps environment variables or system properties to annotations which are then recorded on the
* ReplicationController of the generated or applied JSON
*/
@Parameter(property = "fabric8.environmentVariableToAnnotationsFile", defaultValue = "${basedir}/src/main/fabric8/environemntToAnnotations.properties")
protected File environmentVariableToAnnotationsFile;
@Parameter(defaultValue = "${project}", readonly = true)
private MavenProject project;
/**
* Files to be excluded
*/
@Parameter(property = "fabric8.excludedFiles", defaultValue = "io.fabric8.agent.properties")
private String[] filesToBeExcluded;
/**
* Is this build a CD Pipeline build (and so raise warning levels if we cannot detect CD related
* metadata for the build,
* such as the git commit id, git URL, Jenkins job URL etc
*/
@Parameter(property = "fabric8.cd.build", defaultValue = "false")
private boolean cdBuild;
/**
* The environment variable used to detect if the current build is inside a CD Pipeline build
* to enable verbose logging if we cannot auto default the CD related metadata for the build,
* such as the git commit id, git URL, Jenkins job URL etc
*/
@Parameter(property = "fabric8.cd.envVar", defaultValue = "JENKINS_HOME")
private String cdEnvVarName;
/**
* The docker image to use.
*/
@Parameter(property = "docker.image")
private String dockerImage;
/**
* Whether to try to fetch extended environment metadata during the json, or apply goals.
*
* The following ENV variables is supported: BUILD_URI, GIT_URL, GIT_COMMIT, GIT_BRANCH
* If any of these ENV variable is empty then if this option is enabled, then the value is attempted to
* be fetched from an online connection to the Kubernetes master. If the connection fails then the
* goal will report this as a failure gently and continue.
*
* This option can be turned off, to avoid any live connection to the Kubernetes master.
*/
@Parameter(property = "fabric8.extended.environment.metadata", defaultValue = "true")
private Boolean extendedMetadata;
protected static File copyReadMe(File src, File appBuildDir) throws IOException {
return copyReadMe(src, appBuildDir, null);
}
protected static File copyReadMe(File src, File appBuildDir, String outputFileName) throws IOException {
File[] files = src.listFiles(new FilenameFilter() {
@Override
public boolean accept(File dir, String name) {
return name.toLowerCase(Locale.ENGLISH).startsWith("readme.");
}
});
if (files != null && files.length == 1) {
File readme = files[0];
if (Strings.isNullOrBlank(outputFileName)) {
outputFileName = readme.getName();
}
File outFile = new File(appBuildDir, outputFileName);
Files.copy(readme, outFile);
return outFile;
}
return null;
}
protected static Object loadJsonFile(File file) throws MojoExecutionException {
try {
return KubernetesHelper.loadJson(file);
} catch (IOException e) {
throw new MojoExecutionException("Failed to parse JSON " + file + ". " + e, e);
}
}
@Override
public MavenProject getProject() {
return project;
}
protected static URLClassLoader createURLClassLoader(Collection jars) {
return new URLClassLoader(jars.toArray(new URL[jars.size()]));
}
public File getKubernetesJson() {
return kubernetesJson;
}
public File getKubernetesCombineJson() {
return kubernetesCombineJson;
}
public Integer getReplicas() {
return replicas;
}
public boolean isIgnoreProject() {
return ignoreProject;
}
public File getZipFile() {
return zipFile;
}
/**
* Returns true if this project is a pom packaging project
*/
protected boolean isPom(MavenProject reactorProject) {
return "pom".equals(reactorProject.getPackaging());
}
protected InputStream loadPluginResource(String iconRef) throws MojoExecutionException {
InputStream answer = Thread.currentThread().getContextClassLoader().getResourceAsStream(iconRef);
if (answer == null) {
answer = getTestClassLoader().getResourceAsStream(iconRef);
}
if (answer == null) {
answer = this.getClass().getResourceAsStream(iconRef);
}
return answer;
}
protected URLClassLoader getCompileClassLoader() throws MojoExecutionException {
try {
List classpathElements = getProject().getCompileClasspathElements();
return createClassLoader(classpathElements, getProject().getBuild().getOutputDirectory());
} catch (Exception e) {
throw new MojoExecutionException("Failed to resolve classpath: " + e, e);
}
}
protected URLClassLoader getTestClassLoader() throws MojoExecutionException {
try {
List classpathElements = getProject().getTestClasspathElements();
return createClassLoader(classpathElements, getProject().getBuild().getTestOutputDirectory());
} catch (Exception e) {
throw new MojoExecutionException("Failed to resolve classpath: " + e, e);
}
}
protected URLClassLoader createClassLoader(List classpathElements, String... paths) throws MalformedURLException {
List urls = new ArrayList<>();
for (String path : paths) {
URL url = pathToUrl(path);
urls.add(url);
}
for (Object object : classpathElements) {
if (object != null) {
String path = object.toString();
URL url = pathToUrl(path);
urls.add(url);
}
}
getLog().debug("Creating class loader from: " + urls);
return createURLClassLoader(urls);
}
private URL pathToUrl(String path) throws MalformedURLException {
File file = new File(path);
return file.toURI().toURL();
}
protected boolean hasConfigDir() {
return appConfigDir.isDirectory();
}
protected boolean isPomProject() {
return isPom(getProject());
}
protected void addEnvironmentAnnotations(File json) throws MojoExecutionException {
try {
Object dto = loadJsonFile(json);
if (dto instanceof KubernetesList) {
KubernetesList container = (KubernetesList) dto;
List items = container.getItems();
addEnvironmentAnnotations(items);
getLog().info("Added environment annotations:");
printSummary(items);
container.setItems(items);
KubernetesHelper.saveJson(json, container);
} else if (dto instanceof Template) {
Template container = (Template) dto;
List items = container.getObjects();
addEnvironmentAnnotations(items);
getLog().info("Added environment annotations:");
printSummary(items);
container.setObjects(items);
getLog().info("Template is now:");
printSummary(container.getObjects());
KubernetesHelper.saveJson(json, container);
}
} catch (IOException e) {
throw new MojoExecutionException("Failed to updated JSON file " + json + ". " + e, e);
}
}
protected void addEnvironmentAnnotations(Iterable items) throws MojoExecutionException {
if (items != null) {
for (HasMetadata item : items) {
if (item instanceof KubernetesList) {
KubernetesList list = (KubernetesList) item;
addEnvironmentAnnotations(list.getItems());
} else if (item instanceof Template) {
Template template = (Template) item;
addEnvironmentAnnotations(template.getObjects());
} else if (item instanceof ReplicationController) {
addEnvironmentAnnotations(item);
} else if (item instanceof DeploymentConfig) {
addEnvironmentAnnotations(item);
}
}
}
}
protected void addEnvironmentAnnotations(HasMetadata resource) throws MojoExecutionException {
Map mapEnvVarToAnnotation = new HashMap<>();
String resourceName = "environmentAnnotations.properties";
URL url = getClass().getResource(resourceName);
if (url == null) {
throw new MojoExecutionException("Cannot find resource `" + resourceName + "` on the classpath!");
}
addPropertiesFileToMap(url, mapEnvVarToAnnotation);
addPropertiesFileToMap(this.environmentVariableToAnnotationsFile, mapEnvVarToAnnotation);
Map annotations = KubernetesHelper.getOrCreateAnnotations(resource);
Set> entries = mapEnvVarToAnnotation.entrySet();
for (Map.Entry entry : entries) {
String envVar = entry.getKey();
String annotation = entry.getValue();
if (Strings.isNotBlank(envVar) && Strings.isNotBlank(annotation)) {
String value = Systems.getEnvVarOrSystemProperty(envVar);
if (Strings.isNullOrBlank(value)) {
value = tryDefaultAnnotationEnvVar(envVar);
}
if (Strings.isNotBlank(value)) {
String oldValue = annotations.get(annotation);
if (Strings.isNotBlank(oldValue)) {
getLog().debug("Not adding annotation `" + annotation + "` to " + KubernetesHelper.getKind(resource) + " " + KubernetesHelper.getName(resource) + " with value `" + value + "` as there is already an annotation value of `" + oldValue + "`");
} else {
annotations.put(annotation, value);
}
}
}
}
// lets try and figure out the documentation URL
String docUrl = findDocumentationUrl();
if (Strings.isNotBlank(docUrl)) {
annotations.put(Annotations.Builds.DOCS_URL, docUrl);
getLog().info("Found documentation URL: " + docUrl);
}
}
protected String findDocumentationUrl() {
DistributionManagement distributionManagement = findProjectDistributionManagement();
if (distributionManagement != null) {
Site site = distributionManagement.getSite();
if (site != null) {
String url = site.getUrl();
if (Strings.isNotBlank(url)) {
// lets replace any properties...
MavenProject project = getProject();
if (project != null) {
url = replaceProperties(url, project.getProperties());
}
// lets convert the internal dns name to a public name
try {
String urlToParse = url;
int idx = url.indexOf("://");
if (idx > 0) {
// lets strip any previous schemes such as "dav:"
int idx2 = url.substring(0, idx).lastIndexOf(':');
if (idx2 >= 0 && idx2 < idx) {
urlToParse = url.substring(idx2 + 1);
}
}
URL u = new URL(urlToParse);
String host = u.getHost();
// lets see if the host name is a service name in which case we'll resolve to the public URL
KubernetesClient kubernetes = getKubernetes();
String ns = kubernetes.getNamespace();
Service service = kubernetes.services().inNamespace(ns).withName(host).get();
if (service != null) {
String publicUrl = KubernetesHelper.getServiceURL(kubernetes, host, ns, u.getProtocol(), true);
return URLUtils.pathJoin(publicUrl, u.getPath());
}
} catch (MalformedURLException e) {
getLog().error("Failed to parse URL: " + url, e);
}
return url;
}
}
}
return null;
}
/**
* Replaces all text of the form {foo}
$ with the value in the properties object
*/
protected static String replaceProperties(String text, Properties properties) {
Set> entries = properties.entrySet();
for (Map.Entry
© 2015 - 2025 Weber Informatics LLC | Privacy Policy