io.fabric8.maven.JsonMojo.java Source code

Java tutorial

Introduction

Here is the source code for io.fabric8.maven.JsonMojo.java

Source

/**
 * Copyright 2005-2015 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 com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import io.fabric8.kubernetes.api.Annotations;
import io.fabric8.kubernetes.api.KubernetesHelper;
import io.fabric8.kubernetes.api.extensions.Templates;
import io.fabric8.kubernetes.api.model.Container;
import io.fabric8.kubernetes.api.model.ContainerPort;
import io.fabric8.kubernetes.api.model.EnvVar;
import io.fabric8.kubernetes.api.model.EnvVarBuilder;
import io.fabric8.kubernetes.api.model.ExecAction;
import io.fabric8.kubernetes.api.model.HTTPGetAction;
import io.fabric8.kubernetes.api.model.HasMetadata;
import io.fabric8.kubernetes.api.model.HostPathVolumeSource;
import io.fabric8.kubernetes.api.model.IntOrString;
import io.fabric8.kubernetes.api.model.KubernetesList;
import io.fabric8.kubernetes.api.model.KubernetesListBuilder;
import io.fabric8.kubernetes.api.model.ObjectMeta;
import io.fabric8.kubernetes.api.model.ObjectReference;
import io.fabric8.kubernetes.api.model.ObjectReferenceBuilder;
import io.fabric8.kubernetes.api.model.PersistentVolumeClaimVolumeSource;
import io.fabric8.kubernetes.api.model.PodSpec;
import io.fabric8.kubernetes.api.model.PodTemplateSpec;
import io.fabric8.kubernetes.api.model.Probe;
import io.fabric8.kubernetes.api.model.Quantity;
import io.fabric8.kubernetes.api.model.QuantityBuilder;
import io.fabric8.kubernetes.api.model.ReplicationController;
import io.fabric8.kubernetes.api.model.ReplicationControllerSpec;
import io.fabric8.kubernetes.api.model.RunAsUserStrategyOptions;
import io.fabric8.kubernetes.api.model.ServiceBuilder;
import io.fabric8.kubernetes.api.model.ServiceFluent;
import io.fabric8.kubernetes.api.model.ServicePort;
import io.fabric8.kubernetes.api.model.TCPSocketAction;
import io.fabric8.kubernetes.api.model.Volume;
import io.fabric8.kubernetes.api.model.VolumeMount;
import io.fabric8.kubernetes.api.model.VolumeMountBuilder;
import io.fabric8.maven.support.Commandline;
import io.fabric8.maven.support.JsonSchema;
import io.fabric8.maven.support.JsonSchemaProperty;
import io.fabric8.maven.support.VolumeType;
import io.fabric8.openshift.api.model.DeploymentConfig;
import io.fabric8.openshift.api.model.DeploymentConfigBuilder;
import io.fabric8.openshift.api.model.ImageStream;
import io.fabric8.openshift.api.model.ImageStreamBuilder;
import io.fabric8.openshift.api.model.ParameterBuilder;
import io.fabric8.openshift.api.model.Template;
import io.fabric8.openshift.api.model.TemplateBuilder;
import io.fabric8.utils.Base64Encoder;
import io.fabric8.utils.Files;
import io.fabric8.utils.Objects;
import io.fabric8.utils.PropertiesHelper;
import io.fabric8.utils.Strings;
import io.fabric8.utils.URLUtils;
import org.apache.maven.artifact.Artifact;
import org.apache.maven.artifact.repository.ArtifactRepository;
import org.apache.maven.artifact.resolver.ArtifactResolutionRequest;
import org.apache.maven.artifact.resolver.ArtifactResolutionResult;
import org.apache.maven.artifact.resolver.ArtifactResolver;
import org.apache.maven.model.Scm;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.plugins.annotations.Component;
import org.apache.maven.plugins.annotations.LifecyclePhase;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.project.MavenProject;
import org.apache.maven.project.MavenProjectHelper;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import static io.fabric8.kubernetes.api.KubernetesHelper.getName;
import static io.fabric8.kubernetes.api.KubernetesHelper.setName;
import static io.fabric8.utils.Files.guessMediaType;
import static io.fabric8.utils.PropertiesHelper.findPropertiesWithPrefix;
import static io.fabric8.utils.PropertiesHelper.getLong;

/**
 * Generates or copies the Kubernetes JSON file and attaches it to the build so its
 * installed and released to maven repositories like other build artifacts.
 */
@Mojo(name = "json", defaultPhase = LifecyclePhase.GENERATE_RESOURCES)
public class JsonMojo extends AbstractFabric8Mojo {

    public static final String FABRIC8_PORT_HOST_PREFIX = "docker.port.host.";
    public static final String FABRIC8_PORT_CONTAINER_PREFIX = "docker.port.container.";
    public static final String FABRIC8_PORT_SERVICE = "fabric8.service.port";
    public static final String FABRIC8_CONTAINER_PORT_SERVICE = "fabric8.service.containerPort";
    public static final String FABRIC8_NODE_PORT_SERVICE = "fabric8.service.nodePort";
    public static final String FABRIC8_PROTOCOL_SERVICE = "fabric8.service.protocol";
    public static final String FABRIC8_METRICS_PREFIX = "fabric8.metrics.";
    public static final String FABRIC8_METRICS_SCRAPE = FABRIC8_METRICS_PREFIX + "scrape";
    public static final String FABRIC8_METRICS_SCRAPE_ANNOTATION = FABRIC8_METRICS_SCRAPE + ".annotation";
    public static final String FABRIC8_METRICS_PORT = FABRIC8_METRICS_PREFIX + "port";
    public static final String FABRIC8_METRICS_PORT_ANNOTATION = FABRIC8_METRICS_PORT + ".annotation";
    public static final String FABRIC8_METRICS_SCHEME = FABRIC8_METRICS_PREFIX + "scheme";
    public static final String FABRIC8_METRICS_SCHEME_ANNOTATION = FABRIC8_METRICS_SCHEME + ".annotation";

    private static final String SERVICE_REGEX = "^fabric8\\.service\\.(?<name>[^. ]+)\\..+$";
    private static final Pattern SERVICE_PATTERN = Pattern.compile(SERVICE_REGEX);

    private static final String NAME = "name";
    private static final String ATTRIBUTE_TYPE = "attributeType";

    private static final String VOLUME_MOUNT_PATH = "mountPath";
    private static final String VOLUME_REGEX = "fabric8.volume.(?<name>[^. ]*).(?<attributeType>[^. ]*)";
    private static final Pattern VOLUME_PATTERN = Pattern.compile(VOLUME_REGEX);

    private static final String PARAM_REGEX = "fabric8.parameter.(?<name>[^. ]*)(.)?(?<attributeType>[^ ]*)";
    private static final Pattern PARAM_PATTERN = Pattern.compile(PARAM_REGEX);

    private static final String TEMPLATE_NAME = "fabric8.template";
    private static final String PARAMETER_PREFIX = "fabric8.parameter";
    private static final String PARAMETER_NAME_PREFIX = PARAMETER_PREFIX + ".%s";
    private static final String PARAMETER_PROPERTY = PARAMETER_NAME_PREFIX + ".%s";

    private static final String GENERATE = "generate";
    private static final String FROM = "from";
    private static final String VALUE = "value";
    private static final String DESCRIPTION = "description";

    @Component
    private MavenProjectHelper projectHelper;

    /**
     * The artifact type for attaching the generated kubernetes json file to the project
     */
    @Parameter(property = "fabric8.kubernetes.artifactType", defaultValue = "json")
    private String artifactType = "json";

    /**
     * The artifact classifier for attaching the generated kubernetes json file to the project
     */
    @Parameter(property = "fabric8.kubernetes.artifactClassifier", defaultValue = "kubernetes")
    private String artifactClassifier = "kubernetes";

    /**
     * Whether or not we should generate the Kubernetes JSON file using the MVEL template if there is not one specified
     * in the build (usually in src/main/resources/kubernetes.json)
     */
    @Parameter(property = "fabric8.generateJson", defaultValue = "true")
    private boolean generateJson;

    /**
     * Should we fail the build if no json files could be found
     */
    @Parameter(property = "fabric8.failOnMissingJsonFiles", defaultValue = "true")
    private boolean failOnMissingJsonFiles;

    /**
     * Should we generate any required SecurityContextConstraints DTOs in the generated json
     */
    @Parameter(property = "fabric8.generateSecurityContextConstraints", defaultValue = "false")
    private boolean generateSecurityContextConstraints;

    /**
     * Whether we should include the namespace in the containers' env vars
     */
    @Parameter(property = "fabric8.includeNamespaceEnvVar", defaultValue = "true")
    private boolean includeNamespaceEnvVar;

    /**
     * The name of the env var to add that will contain the namespace at container runtime
     */
    @Parameter(property = "fabric8.namespaceEnvVar", defaultValue = "KUBERNETES_NAMESPACE")
    private String kubernetesNamespaceEnvVar;

    /**
     * Whether we should include the namespace in the containers' env vars
     */
    @Parameter(property = "fabric8.includePodEnvVar", defaultValue = "false")
    private boolean includePodEnvVar;

    /**
     * The name of the env var to add that will contain the pod name at container runtime
     */
    @Parameter(property = "fabric8.podEnvVar", defaultValue = "KUBERNETES_POD")
    private String kubernetesPodEnvVar;

    /**
     * The provider to include as a label. Set to empty to disable.
     */
    @Parameter(property = "fabric8.provider", defaultValue = "fabric8")
    private String provider;

    /**
     * The labels passed into the generated Kubernetes JSON template.
     * <p/>
     * If no value is explicitly configured in the maven plugin then we use all maven properties starting with "fabric8.label."
     */
    @Parameter()
    private Map<String, String> labels;

    /**
     * The annotations for the PodSpec
     */
    @Parameter()
    private Map<String, String> podSpecAnnotations;

    /**
     * The annotations for the ReplicationController
     */
    @Parameter()
    private Map<String, String> rcAnnotations;

    /**
     * The annotations for the Template
     */
    @Parameter()
    private Map<String, String> templateAnnotations;

    /**
     * The annotations for the Service
     */
    @Parameter()
    private Map<String, String> serviceAnnotations;
    /**
     * The environment variables passed into the generated Kubernetes JSON template.
     * <p/>
     * If no value is explicitly configured in the maven plugin then we use all maven properties starting with "fabric8.env."
     */
    @Parameter()
    private List<EnvVar> environmentVariables;

    /**
     * The container ports passed into the generated Kubernetes JSON template.
     */
    @Parameter()
    private List<ContainerPort> containerPorts;

    /**
     * Maps the port names to the default container port numbers
     */
    @Parameter()
    private Map<String, Integer> defaultContainerPortMap;

    /**
     * The service ports passed into the generated Kubernetes JSON template.
     */
    @Parameter()
    private List<ServicePort> servicePorts;

    /**
     * The ID prefix used in the generated Kubernetes JSON template
     */
    @Parameter(property = "fabric8.replicas", defaultValue = "1")
    private Integer replicaCount;

    /**
     * Should we wrap the generated ReplicationController objects in a DeploymentConfig
     */
    // TODO lets disable by default until its working :)
    @Parameter(property = "fabric8.useDeploymentConfig", defaultValue = "false")
    private boolean useDeploymentConfig;

    /**
     * The last triggered image tag if generating a DeploymentConfig
     */
    @Parameter(property = "fabric8.lastTriggeredImageTag", defaultValue = "latest")
    private String lastTriggeredImageTag;

    /**
     * The strategy name for the DeploymentConfig
     */
    @Parameter(property = "fabric8.deploymentStrategy", defaultValue = "Recreate")
    private String deploymentStrategy;

    /**
     * The extra additional kubernetes JSON file for things like services
     */
    @Parameter(property = "fabric8.extra.json", defaultValue = "${basedir}/target/classes/kubernetes-extra.json")
    private File kubernetesExtraJson;

    /**
     * Temporary directory used for creating the template annotations
     */
    @Parameter(property = "fabric8.templateTempDir", defaultValue = "${basedir}/target/fabric8/template-workdir")
    private File templateTempDir;

    /**
     * The URL to use to link to the icon in the generated Template.
     * <p/>
     * For using a common set of icons, see the {@link #iconRef} option.
     */
    @Parameter(property = "fabric8.iconUrl")
    private String iconUrl;

    /**
     * The URL prefix added to the relative path of the icon file
     */
    @Parameter(property = "fabric8.iconUrlPrefix")
    private String iconUrlPrefix;

    /**
     * The SCM branch used when creating a URL to the icon file
     */
    @Parameter(property = "fabric8.iconBranch", defaultValue = "master")
    private String iconBranch;

    /**
     * The replication controller name used in the generated Kubernetes JSON template
     */
    @Parameter(property = "fabric8.replicationController.name", defaultValue = "${project.artifactId}")
    private String replicationControllerName;

    /**
     * The project label used in the generated Kubernetes JSON template
     */
    @Parameter(property = "fabric8.label.project", defaultValue = "${project.artifactId}")
    private String projectName;

    /**
     * The group label used in the generated Kubernetes JSON template
     */
    @Parameter(property = "fabric8.label.group", defaultValue = "${project.groupId}")
    private String groupName;

    /**
     * The name label used in the generated Kubernetes JSON template
     */
    @Parameter(property = "fabric8.container.name", defaultValue = "${project.artifactId}")
    private String kubernetesContainerName;

    /**
     * The service name
     */
    @Parameter(property = "fabric8.service.name", defaultValue = "${project.artifactId}")
    private String serviceName;

    /**
     * Should we generate headless services (services with no ports)
     */
    // TODO for now lets default to not creating headless services as it barfs when used with kubernetes...
    //@Parameter(property = "fabric8.service.headless", defaultValue = "true")
    @Parameter(property = "fabric8.service.headless", defaultValue = "false")
    private boolean headlessService;

    /**
     * The <a href="http://releases.k8s.io/HEAD/docs/user-guide/services.md#external-services">Type of the service</a>. Set to
     * <code>"LoadBalancer"</code>  if you wish an
     * <a href="https://github.com/GoogleCloudPlatform/kubernetes/blob/master/docs/user-guide/services.md#type-loadbalancer"></a>external load balancer</a> to be created
     */
    @Parameter(property = "fabric8.service.type")
    private String serviceType;

    /**
     * Annotation value to add for metrics scraping.
     */
    @Parameter(property = FABRIC8_METRICS_SCRAPE, defaultValue = "false")
    private boolean metricsScrape;

    /**
     * Annotation to add for metrics scraping.
     */
    @Parameter(property = FABRIC8_METRICS_SCRAPE_ANNOTATION, defaultValue = "prometheus.io/scrape")
    private String metricsScrapeAnnotation;

    /**
     * Annotation value to add for metrics port.
     */
    @Parameter(property = FABRIC8_METRICS_PORT)
    private Integer metricsPort;

    /**
     * Annotation value to add for metrics port.
     */
    @Parameter(property = FABRIC8_METRICS_PORT_ANNOTATION, defaultValue = "prometheus.io/port")
    private String metricsPortAnnotation;

    /**
     * Annotation value to add for metrics scheme.
     */
    @Parameter(property = FABRIC8_METRICS_SCHEME)
    private String metricsScheme;

    /**
     * Annotation to add for metrics scheme.
     */
    @Parameter(property = FABRIC8_METRICS_SCHEME_ANNOTATION, defaultValue = "prometheus.io/scheme")
    private String metricsSchemeAnnotation;

    /**
     * The service port
     */
    @Parameter(property = FABRIC8_PORT_SERVICE)
    private Integer servicePort;

    /**
     * The service container port
     */
    @Parameter(property = FABRIC8_CONTAINER_PORT_SERVICE)
    private String serviceContainerPort;

    /**
     * The service node port
     */
    @Parameter(property = FABRIC8_NODE_PORT_SERVICE)
    private Integer serviceNodePort;

    /**
     * The service protocol
     */
    @Parameter(property = FABRIC8_PROTOCOL_SERVICE, defaultValue = "TCP")
    private String serviceProtocol;

    /**
     * The docker image pull policy for non-snapshots
     */
    @Parameter(property = "fabric8.imagePullPolicy")
    private String imagePullPolicy;

    /**
     * The docker image pull policy for snapshot releases (which should pull always)
     */
    @Parameter(property = "fabric8.imagePullPolicySnapshot")
    private String imagePullPolicySnapshot;

    /**
     * Whether the plugin should discover all the environment variable json schema files in the classpath and export those into the generated kubernetes JSON
     */
    @Parameter(property = "fabric8.includeAllEnvironmentVariables", defaultValue = "true")
    private boolean includeAllEnvironmentVariables;

    @Parameter(property = "fabric8.containerPrivileged")
    protected Boolean containerPrivileged;

    @Parameter(property = "fabric8.serviceAccount")
    protected String serviceAccount;

    /**
     * The properties file used to specify the OpenShift Template parameter values and descriptions. The properties file should be of the form
     * <code>
     *     <pre>
     *         FOO.value = ABC
     *         FOO.description = this is the description of FOO
     *     </pre>
     * </code>
     */
    @Parameter(property = "fabric8.templateParametersFile", defaultValue = "${basedir}/src/main/fabric8/templateParameters.properties")
    protected File templateParametersPropertiesFile;

    /**
     * The properties file used to specify the annotations to be added to the generated PodSpec
     * <code>
     *     <pre>
     *         acme.com/cheese = SOMETHING
     *     </pre>
     * </code>
     */
    @Parameter(property = "fabric8.podSpecAnnotationsFile", defaultValue = "${basedir}/src/main/fabric8/podSpecAnnotations.properties")
    protected File podSpecAnnotationsFile;

    /**
     * The properties file used to specify the annotations to be added to the generated ReplicationController
     * <code>
     *     <pre>
     *         acme.com/cheese = SOMETHING
     *     </pre>
     * </code>
     */
    @Parameter(property = "fabric8.rcAnnotationsFile", defaultValue = "${basedir}/src/main/fabric8/rcAnnotations.properties")
    protected File rcAnnotationsFile;

    /**
     * The properties file used to specify the annotations to be added to the generated Template
     * <code>
     *     <pre>
     *         acme.com/cheese = SOMETHING
     *     </pre>
     * </code>
     */
    @Parameter(property = "fabric8.templateAnnotationsFile", defaultValue = "${basedir}/src/main/fabric8/templateAnnotations.properties")
    protected File templateAnnotationsFile;

    /**
     * The properties file used to specify the annotations to be added to the generated Service
     * <code>
     *     <pre>
     *         acme.com/cheese = SOMETHING
     *     </pre>
     * </code>
     */
    @Parameter(property = "fabric8.serviceAnnotationsFile", defaultValue = "${basedir}/src/main/fabric8/serviceAnnotations.properties")
    protected File serviceAnnotationsFile;

    /**
     * Defines the maximum size in kilobytes that the data encoded URL of the icon should be before we defer
     * and try to use an external URL
     */
    @Parameter(property = "fabric8.maximumDataUrlSizeK", defaultValue = "2")
    private int maximumDataUrlSizeK;

    @Component
    protected ArtifactResolver resolver;

    @Parameter(property = "localRepository", readonly = true, required = true)
    protected ArtifactRepository localRepository;

    @Parameter(property = "project.remoteArtifactRepositories")
    protected List remoteRepositories;

    /**
     * The default requests storage size for a PersistenceVolumeClaim if its created for a persistent volume via a claim
     */
    @Parameter(property = "fabric8.defaultPersistentVolumeClaimRequestsStorage", defaultValue = "20")
    private String defaultPersistentVolumeClaimRequestsStorage;

    /**
     * Should we remove the version label from the service selector?
     */
    @Parameter(property = "fabric8.removeVersionLabelFromServiceSelector", defaultValue = "true")
    private boolean removeVersionLabelFromServiceSelector;

    @Override
    public void execute() throws MojoExecutionException, MojoFailureException {
        File json = getKubernetesJson();
        getLog().info("Configured with file: " + json);
        if (json == null) {
            throw new MojoExecutionException("No kubernetes json file is specified!");
        }
        if (shouldGenerateForThisProject()) {
            if (!isIgnoreProject() || combineDependencies) {
                if (generateJson) {
                    generateKubernetesJson(json);
                    if (combineDependencies) {
                        combineDependentJsonFiles(json);
                    }
                    if (kubernetesExtraJson != null && kubernetesExtraJson.exists()) {
                        combineJsonFiles(json, kubernetesExtraJson);
                    }
                }
                if (json.exists() && json.isFile()) {
                    if (useDeploymentConfig) {
                        wrapInDeploymentConfigs(json);
                    }
                    addEnvironmentAnnotations(json);
                }
            }
        }
    }

    @Override
    protected boolean shouldGenerateForThisProject() {
        return super.shouldGenerateForThisProject() || combineDependencies;
    }

    protected void combineDependentJsonFiles(File json) throws MojoExecutionException {
        try {
            MavenProject project = getProject();
            Set<File> jsonFiles = new LinkedHashSet<>();
            Set<Artifact> dependencyArtifacts = project.getDependencyArtifacts();
            for (Artifact artifact : dependencyArtifacts) {
                String classifier = artifact.getClassifier();
                String type = artifact.getType();
                File file = artifact.getFile();

                if (isKubernetesJsonArtifact(classifier, type)) {
                    if (file != null) {
                        System.out.println("Found kubernetes JSON dependency: " + artifact);
                        jsonFiles.add(file);
                    } else {
                        Set<Artifact> artifacts = resolveArtifacts(artifact);
                        for (Artifact resolvedArtifact : artifacts) {
                            classifier = resolvedArtifact.getClassifier();
                            type = resolvedArtifact.getType();
                            file = resolvedArtifact.getFile();
                            if (isKubernetesJsonArtifact(classifier, type) && file != null) {
                                System.out.println("Resolved kubernetes JSON dependency: " + artifact);
                                jsonFiles.add(file);
                            }
                        }
                    }
                }
            }
            List<Object> jsonObjectList = new ArrayList<>();
            for (File file : jsonFiles) {
                addKubernetesJsonFileToList(jsonObjectList, file);
            }
            if (jsonObjectList.isEmpty()) {
                if (failOnMissingJsonFiles) {
                    throw new MojoExecutionException("Could not find any dependent kubernetes JSON files!");
                } else {
                    getLog().warn("Could not find any dependent kubernetes JSON files");
                    return;
                }
            }
            Object combinedJson;
            if (jsonObjectList.size() == 1) {
                combinedJson = jsonObjectList.get(0);
            } else {
                combinedJson = KubernetesHelper.combineJson(jsonObjectList.toArray());
            }
            if (combinedJson instanceof Template) {
                Template template = (Template) combinedJson;
                String templateName = getProjectName();
                setName(template, templateName);
                configureTemplateDescriptionAndIcon(template, getIconUrl());

                addLabelIntoObjects(template.getObjects(), "package", templateName);

                if (pureKubernetes) {
                    combinedJson = applyTemplates(template);
                }
            }
            if (pureKubernetes) {
                combinedJson = filterPureKubernetes(combinedJson);
            }
            json.getParentFile().mkdirs();
            KubernetesHelper.saveJson(json, combinedJson);
            getLog().info("Saved as :" + json.getAbsolutePath());
        } catch (Exception e) {
            throw new MojoExecutionException("Failed to save combined JSON files " + json + " and "
                    + kubernetesExtraJson + " as " + json + ". " + e, e);
        }
    }

    protected void addLabelIntoObjects(List<HasMetadata> objects, String label, String value) {
        for (HasMetadata object : objects) {
            addLabelIfNotExist(object, label, value);
            if (object instanceof ReplicationController) {
                ReplicationController entity = (ReplicationController) object;
                ReplicationControllerSpec spec = entity.getSpec();
                if (spec != null) {
                    final PodTemplateSpec template = spec.getTemplate();
                    if (template != null) {
                        // TODO hack until this is fixed https://github.com/fabric8io/kubernetes-model/issues/112
                        HasMetadata hasMetadata = new HasMetadata() {
                            @Override
                            public ObjectMeta getMetadata() {
                                return template.getMetadata();
                            }

                            @Override
                            public void setMetadata(ObjectMeta objectMeta) {
                                template.setMetadata(objectMeta);
                            }

                            @Override
                            public String getKind() {
                                return "PodTemplateSpec";
                            }
                        };
                        addLabelIfNotExist(hasMetadata, label, value);
                    }
                }
            }
        }
    }

    protected boolean addLabelIfNotExist(HasMetadata object, String label, String value) {
        if (object != null) {
            Map<String, String> labels = KubernetesHelper.getOrCreateLabels(object);
            if (labels.get(label) == null) {
                labels.put(label, value);
                return true;
            }
        }
        return false;
    }

    protected Object applyTemplates(Template template) throws IOException {
        overrideTemplateParameters(template);
        return Templates.processTemplatesLocally(template, false);
    }

    protected Object filterPureKubernetes(Object dto) throws IOException {
        List<HasMetadata> items = KubernetesHelper.toItemList(dto);
        List<HasMetadata> filtered = new ArrayList<>();
        for (HasMetadata item : items) {
            if (KubernetesHelper.isPureKubernetes(item)) {
                filtered.add(item);
            }
        }
        KubernetesList answer = new KubernetesList();
        answer.setItems(filtered);
        return answer;
    }

    private void addKubernetesJsonFileToList(List<Object> list, File file) {
        if (file.exists() && file.isFile()) {
            try {
                Object jsonObject = loadJsonFile(file);
                if (jsonObject != null) {
                    list.add(jsonObject);
                } else {
                    getLog().warn("No object found for file: " + file);
                }
            } catch (MojoExecutionException e) {
                getLog().warn("Failed to parse file " + file + ". " + e, e);
            }

        } else {
            getLog().warn("Ignoring missing file " + file);
        }
    }

    protected Set<Artifact> resolveArtifacts(Artifact artifact) {
        ArtifactResolutionRequest request = new ArtifactResolutionRequest();
        request.setArtifact(artifact);
        request.setRemoteRepositories(remoteRepositories);
        request.setLocalRepository(localRepository);

        ArtifactResolutionResult resolve = resolver.resolve(request);
        return resolve.getArtifacts();
    }

    protected void combineJsonFiles(File json, File kubernetesExtraJson) throws MojoExecutionException {
        // lets combine json files together
        getLog().info("Combining generated json " + json + " with extra json " + kubernetesExtraJson);
        Object extra = loadJsonFile(kubernetesExtraJson);
        Object generated = loadJsonFile(json);
        try {
            Object combinedJson = KubernetesHelper.combineJson(generated, extra);
            KubernetesHelper.saveJson(json, combinedJson);
            getLog().info("Saved as :" + json.getAbsolutePath());
        } catch (IOException e) {
            throw new MojoExecutionException("Failed to save combined JSON files " + json + " and "
                    + kubernetesExtraJson + " as " + json + ". " + e, e);
        }
    }

    protected void wrapInDeploymentConfigs(File json) throws MojoExecutionException {
        try {
            Object dto = loadJsonFile(json);
            if (dto instanceof KubernetesList) {
                KubernetesList container = (KubernetesList) dto;
                List<HasMetadata> items = container.getItems();
                items = wrapInDeploymentConfigs(items);
                getLog().info("Wrapped in DeploymentConfigs:");
                printSummary(items);
                container.setItems(items);
                KubernetesHelper.saveJson(json, container);
            } else if (dto instanceof Template) {
                Template container = (Template) dto;
                List<HasMetadata> items = container.getObjects();
                items = wrapInDeploymentConfigs(items);
                getLog().info("Wrapped in DeploymentConfigs:");
                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 save combined JSON files " + json + " and "
                    + kubernetesExtraJson + " as " + json + ". " + e, e);
        }
    }

    protected List<HasMetadata> wrapInDeploymentConfigs(List<HasMetadata> items) {
        List<HasMetadata> answer = new ArrayList<>();
        for (HasMetadata item : items) {
            if (item instanceof ReplicationController) {
                ReplicationController replicationController = (ReplicationController) item;
                wrapInDeploymentConfigs(answer, replicationController);
            } else {
                answer.add(item);
            }
        }
        return answer;
    }

    /**
     * Wraps the given {@link ReplicationController} in a {@link DeploymentConfig} and adds it to the given list
     * along with any other required entities
     */
    protected void wrapInDeploymentConfigs(List<HasMetadata> list, ReplicationController replicationController) {
        DeploymentConfigBuilder builder = new DeploymentConfigBuilder();

        String name = getName(replicationController);
        if (Strings.isNotBlank(name)) {
            name = Strings.stripSuffix(name, "-controller");
        }
        if (Strings.isNullOrBlank(name)) {
            name = getProject().getArtifactId();
        }
        String deploymentName = name;
        String imageStream = name;

        Map<String, String> labels = KubernetesHelper.getLabels(replicationController);
        builder = builder.withNewMetadata().withName(deploymentName).withLabels(labels).endMetadata();

        ReplicationControllerSpec spec = replicationController.getSpec();
        if (spec != null) {
            List<String> containerNames = new ArrayList<>();
            PodTemplateSpec podTemplateSpec = spec.getTemplate();
            if (podTemplateSpec != null) {
                PodSpec podSpec = podTemplateSpec.getSpec();
                if (podSpec != null) {
                    List<Container> containers = podSpec.getContainers();
                    if (containers != null) {
                        for (Container container : containers) {
                            String containerName = container.getName();
                            if (Strings.isNotBlank(containerName)) {
                                containerNames.add(containerName);
                            }
                        }
                    }
                }
            }
            getOrAddImageStream(list, imageStream, labels);
            builder = builder.withNewSpec().withTemplate(podTemplateSpec).withReplicas(spec.getReplicas())
                    .withSelector(spec.getSelector()).withNewStrategy().withType(deploymentStrategy).endStrategy()
                    .addNewTrigger().withType("ImageChange").withNewImageChangeParams().withAutomatic(true)
                    .withContainerNames(containerNames).withNewFrom()
                    .withName(imageStream + ":" + lastTriggeredImageTag).endFrom()
                    .withLastTriggeredImage(lastTriggeredImageTag).endImageChangeParams().endTrigger().endSpec();
        }
        DeploymentConfig config = builder.build();
        list.add(config);
    }

    protected ImageStream getOrAddImageStream(List<HasMetadata> list, String imageStreamName,
            Map<String, String> labels) {
        for (HasMetadata item : list) {
            if (item instanceof ImageStream) {
                ImageStream stream = (ImageStream) item;
                if (Objects.equal(imageStreamName, getName(stream))) {
                    return stream;
                }
            }
        }
        ImageStream imageStream = new ImageStreamBuilder().withNewMetadata().withName(imageStreamName)
                .withLabels(labels).endMetadata().build();
        list.add(imageStream);
        return imageStream;
    }

    protected void generateKubernetesJson(File kubernetesJson) throws MojoExecutionException {
        // TODO populate properties, project etc.
        MavenProject project = getProject();
        Map<String, String> labelMap = getLabels();
        String name = getProjectName();
        String group = getGroupName();
        if (!labelMap.containsKey("version")) {
            labelMap.put("version", project.getVersion());
        }
        if (!labelMap.containsKey("project") && Strings.isNotBlank(name)) {
            labelMap.put("project", name);
        }
        if (!labelMap.containsKey("group") && Strings.isNotBlank(group)) {
            labelMap.put("group", group);
        }
        if (!labelMap.containsKey("provider") && Strings.isNotBlank(provider)) {
            labelMap.put("provider", provider);
        }

        Map<String, String> podSpecAnnotations = getPodSpecAnnotations();
        Map<String, String> rcAnnotations = getRCAnnotations();
        KubernetesListBuilder builder = new KubernetesListBuilder();

        // lets add a ServiceAccount object if we add any new secret annotations
        boolean addedServiceAcount = addServiceAccountIfIUsingSecretAnnotations(builder, podSpecAnnotations);

        List<Volume> volumes = getVolumes();
        List<VolumeMount> volumeMounts = getVolumeMounts();
        Boolean containerPrivileged = getContainerPrivileged();

        if (addedServiceAcount) {
            addServiceConstraints(builder, volumes,
                    containerPrivileged != null && containerPrivileged.booleanValue());
        }
        builder.addNewReplicationControllerItem().withNewMetadata()
                .withName(KubernetesHelper.validateKubernetesId(replicationControllerName,
                        "fabric8.replicationController.name"))
                .withLabels(labelMap).withAnnotations(rcAnnotations).endMetadata().withNewSpec()
                .withReplicas(replicaCount).withSelector(labelMap).withNewTemplate().withNewMetadata()
                .withLabels(labelMap).withAnnotations(podSpecAnnotations).endMetadata().withNewSpec()
                .withServiceAccountName(serviceAccount).addNewContainer().withName(getKubernetesContainerName())
                .withImage(getDockerImage()).withImagePullPolicy(getImagePullPolicy())
                .withEnv(getEnvironmentVariables()).withNewSecurityContext().withPrivileged(containerPrivileged)
                .endSecurityContext().withPorts(getContainerPorts()).withVolumeMounts(volumeMounts)
                .withLivenessProbe(getLivenessProbe()).withReadinessProbe(getReadinessProbe()).endContainer()
                .withVolumes(volumes).endSpec().endTemplate().endSpec().endReplicationControllerItem();

        addPersistentVolumeClaims(builder, volumes);

        addServices(builder, labelMap);

        Template template = getTemplate();
        String iconUrl = getIconUrl();
        if (!template.getParameters().isEmpty() || Strings.isNotBlank(iconUrl)) {
            configureTemplateDescriptionAndIcon(template, iconUrl);
            builder = builder.addToTemplateItems(template);
        }

        KubernetesList kubernetesList = builder.build();

        Object result = Templates.combineTemplates(kubernetesList);
        if (result instanceof Template) {
            Template resultTemplate = (Template) result;
            configureTemplateDescriptionAndIcon(resultTemplate, iconUrl);

            if (pureKubernetes) {
                try {
                    result = applyTemplates(resultTemplate);
                } catch (IOException e) {
                    throw new MojoExecutionException("Failed to process template locally " + e, e);
                }
            }
        }
        try {
            if (pureKubernetes) {
                result = filterPureKubernetes(result);
            }

            ObjectMapper mapper = new ObjectMapper().enable(SerializationFeature.INDENT_OUTPUT);
            String generated = mapper.writeValueAsString(result);
            Files.writeToFile(kubernetesJson, generated, Charset.defaultCharset());
        } catch (Exception e) {
            throw new IllegalArgumentException("Failed to generate Kubernetes JSON.", e);
        }
    }

    private void addServices(KubernetesListBuilder builder, Map<String, String> labelMap)
            throws MojoExecutionException {
        MavenProject project = getProject();
        Properties properties = getProjectAndFabric8Properties(project);

        Set<String> serviceNames = new HashSet<>(Arrays.asList(""));
        for (Map.Entry<Object, Object> entry : properties.entrySet()) {
            Object key = entry.getKey();
            if (key instanceof String) {
                String s = (String) key;
                Matcher m = SERVICE_PATTERN.matcher(s);
                if (m.matches()) {
                    String name = m.group(NAME);
                    serviceNames.add(name);
                }
            }
        }

        for (String serviceName : serviceNames) {
            Map<String, String> serviceAnnotations = getServiceAnnotations();
            serviceAnnotations.putAll(getMetricsAnnotations(serviceName));

            Map<String, String> selector = new HashMap<>(labelMap);
            if (removeVersionLabelFromServiceSelector) {
                if (selector.remove("version") != null) {
                    getLog().info(
                            "Removed 'version' label from service selector for service `" + serviceName + "`");
                }
            }

            String tempServiceName = serviceName;
            if (Strings.isNullOrBlank(tempServiceName)) {
                tempServiceName = this.serviceName;
            }

            ServiceBuilder serviceBuilder = new ServiceBuilder().withNewMetadata().withName(tempServiceName)
                    .withLabels(labelMap).withAnnotations(serviceAnnotations).endMetadata();

            ServiceFluent.SpecNested<ServiceBuilder> serviceSpecBuilder = serviceBuilder.withNewSpec()
                    .withSelector(selector);

            List<ServicePort> servicePorts = getServicePorts(serviceName);
            getLog().info("Generated ports: " + servicePorts);
            boolean hasPorts = servicePorts != null && !servicePorts.isEmpty();
            if (hasPorts) {
                serviceSpecBuilder.withPorts(servicePorts);
            }

            String headlessPrefix = buildServicePrefix(serviceName, "fabric8.service", "headless");
            Boolean tempHeadlessService = Boolean.valueOf(properties.getProperty(headlessPrefix));
            if (tempHeadlessService) {
                serviceSpecBuilder.withClusterIP("None");

                // If this is a headless service with no ports then see if metrics is enabled & add that as a port - hacky!
                if (!hasPorts && Boolean.parseBoolean(serviceAnnotations.get(metricsScrapeAnnotation))) {
                    try {
                        String port = serviceAnnotations.get(metricsPortAnnotation);
                        Integer metricsPort = Integer.parseInt(port);
                        if (metricsPort != null) {
                            ServicePort servicePort = new ServicePort();
                            servicePort.setPort(metricsPort);
                            servicePort.setTargetPort(new IntOrString(metricsPort));
                            serviceSpecBuilder.withPorts(Arrays.asList(servicePort));
                        }
                    } catch (NumberFormatException e) {
                        // Ignore this.
                    }
                }
            }

            if (Strings.isNotBlank(serviceType)) {
                serviceSpecBuilder.withType(serviceType);
            }
            serviceSpecBuilder.endSpec();

            if (tempHeadlessService || hasPorts) {
                builder = builder.addToServiceItems(serviceBuilder.build());
            }
        }
    }

    private Map<? extends String, ? extends String> getMetricsAnnotations(String serviceName) {
        Map<String, String> metricsAnnotations = new HashMap<>();

        boolean tempMetricsScrape;
        Integer tempMetricsPort = null;
        String tempMetricsScheme;

        if (Strings.isNotBlank(serviceName)) {
            Properties properties = getProjectAndFabric8Properties(getProject());
            tempMetricsScrape = Boolean.parseBoolean(
                    properties.getProperty(buildServicePrefix(serviceName, "fabric8.service", "metrics.scrape")));
            tempMetricsScheme = properties
                    .getProperty(buildServicePrefix(serviceName, "fabric8.service", "metrics.scheme"));
            String port = properties
                    .getProperty(buildServicePrefix(serviceName, "fabric8.service", "metrics.port"));
            if (port != null) {
                tempMetricsPort = Integer.parseInt(port);
            }
        } else {
            tempMetricsScrape = metricsScrape;
            tempMetricsScheme = metricsScheme;
            tempMetricsPort = metricsPort;
        }

        if (tempMetricsScrape) {
            metricsAnnotations.put(metricsScrapeAnnotation, Boolean.toString(tempMetricsScrape));
            if (tempMetricsPort != null) {
                metricsAnnotations.put(metricsPortAnnotation, tempMetricsPort.toString());
            }
            if (tempMetricsScheme != null) {
                metricsAnnotations.put(metricsSchemeAnnotation, tempMetricsScheme);
            }
        }
        return metricsAnnotations;
    }

    protected void addPersistentVolumeClaims(KubernetesListBuilder builder, List<Volume> volumes) {
        for (Volume volume : volumes) {
            PersistentVolumeClaimVolumeSource persistentVolumeClaim = volume.getPersistentVolumeClaim();
            if (persistentVolumeClaim != null) {
                String name = volume.getName();
                String claimName = persistentVolumeClaim.getClaimName();
                Boolean readOnly = persistentVolumeClaim.getReadOnly();

                if (Strings.isNotBlank(claimName)) {
                    String accessModes;
                    if (readOnly != null && readOnly.booleanValue()) {
                        accessModes = "ReadOnly";
                    } else {
                        accessModes = "ReadWriteMany";
                    }
                    Properties properties = getProjectAndFabric8Properties(getProject());
                    String requestStorageProperty = String.format(VolumeType.VOLUME_PROPERTY, name,
                            VolumeType.VOLUME_PVC_REQUEST_STORAGE);
                    String amount = properties.getProperty(requestStorageProperty);
                    if (Strings.isNullOrBlank(amount)) {
                        amount = defaultPersistentVolumeClaimRequestsStorage;
                        getLog().info("No maven property defined for `" + requestStorageProperty
                                + "` so defaulting the requestStorage to " + amount);
                    } else {
                        getLog().debug("Maven property `" + requestStorageProperty + "` = " + amount);
                    }

                    Map<String, Quantity> requests = new HashMap<>();
                    Quantity requestLimit = new QuantityBuilder().withAmount(amount).build();
                    requests.put("storage", requestLimit);

                    builder.addNewPersistentVolumeClaimItem().withNewMetadata().withName(claimName).endMetadata()
                            .withNewSpec().withAccessModes(accessModes).withVolumeName(claimName).withNewResources()
                            .withRequests(requests).endResources().endSpec().endPersistentVolumeClaimItem();
                } else {
                    getLog().warn("No claimName for persistent volume " + volume);
                }
            }
        }
    }

    protected boolean addServiceAccountIfIUsingSecretAnnotations(KubernetesListBuilder builder,
            Map<String, String> annotations) {
        Set<String> secretAnnotations = new HashSet<>(Arrays.asList(Annotations.Secrets.SSH_KEY,
                Annotations.Secrets.SSH_PUBLIC_KEY, Annotations.Secrets.GPG_KEY));
        Set<Map.Entry<String, String>> entries = annotations.entrySet();
        Set<String> secretNameSet = new TreeSet<>();
        for (Map.Entry<String, String> entry : entries) {
            String key = entry.getKey();
            String value = entry.getValue();
            if (secretAnnotations.contains(key)) {
                List<String> secretNames = parseSecretNames(value);
                secretNameSet.addAll(secretNames);
            }
        }

        List<ObjectReference> secrets = new ArrayList<>();
        for (String secretName : secretNameSet) {
            ObjectReference secretRef = new ObjectReferenceBuilder().withName(secretName).build();
            secrets.add(secretRef);
        }

        if (!secrets.isEmpty()) {
            if (Strings.isNullOrBlank(serviceAccount)) {
                serviceAccount = getProject().getArtifactId();
            }

            builder.addNewServiceAccountItem().withNewMetadata().withName(serviceAccount).endMetadata()
                    .withSecrets(secrets).endServiceAccountItem();
            return true;

        }
        return false;
    }

    protected void addServiceConstraints(KubernetesListBuilder builder, List<Volume> volumes,
            boolean containerPrivileged) {
        if (generateSecurityContextConstraints) {
            boolean hostVolume = hasHostVolume(volumes);
            if (hostVolume || containerPrivileged) {
                RunAsUserStrategyOptions runAsUser;
                builder.addNewSecurityContextConstraintsItem().withNewMetadata().withName(serviceAccount)
                        .endMetadata().withAllowHostDirVolumePlugin(hostVolume)
                        .withAllowPrivilegedContainer(containerPrivileged).withNewRunAsUser().withType("RunAsAny")
                        .endRunAsUser().withNewSeLinuxContext().withType("RunAsAny").endSeLinuxContext()
                        .withUsers("system:serviceaccount:" + getNamespace() + ":" + serviceAccount)
                        .endSecurityContextConstraintsItem();
            }
        }
    }

    protected boolean hasHostVolume(List<Volume> volumes) {
        if (volumes != null) {
            for (Volume volume : volumes) {
                HostPathVolumeSource hostPath = volume.getHostPath();
                if (hostPath != null && Strings.isNotBlank(hostPath.getPath())) {
                    return true;
                }
            }
        }
        return false;
    }

    public static List<String> parseSecretNames(String value) {
        // lets split by [...] first removing those out
        List<String> answer = new ArrayList<>();
        String[] split = value.split("\\[|\\]");
        if (split != null && split.length > 0) {
            int i = 0;
            while (i < split.length) {
                String name = split[i];
                if (name.startsWith(",")) {
                    name = name.substring(1);
                }
                splitCommas(name, answer);
                // ignore next which is the conetnts of the array
                i += 2;
            }
        } else {
            splitCommas(value, answer);
        }
        return answer;
    }

    private static void splitCommas(String value, List<String> answer) {
        String[] split = value.split(",");
        if (split != null && split.length > 0) {
            answer.addAll(Arrays.asList(split));
        } else {
            answer.add(value);
        }
    }

    protected void configureTemplateDescriptionAndIcon(Template template, String iconUrl) {
        Map<String, String> annotations = KubernetesHelper.getOrCreateAnnotations(template);
        addDocumentationAnnotations(template, annotations);
        if (Strings.isNotBlank(iconUrl)) {
            annotations.put(getTemplateKey(template, AnnotationKeys.ICON_URL), iconUrl);
        }
    }

    protected String getTemplateKey(Template template, String key) {
        String name = getName(template);
        if (Strings.isNullOrBlank(name)) {
            name = getProject().getArtifactId();
        }
        return AnnotationKeys.PREFIX + name + "/" + key;
    }

    protected void addDocumentationAnnotations(Template template, Map<String, String> annotations) {
        // we want summary before description
        try {
            copySummaryText(templateTempDir);
            copyReadMe(templateTempDir);
        } catch (IOException e) {
            getLog().warn("Failed to copy documentation: " + e, e);
        }

        File summary = new File(templateTempDir, "Summary.md");
        if (summary.exists() && summary.isFile()) {
            try {
                String text = Files.toString(summary);
                annotations.put(getTemplateKey(template, AnnotationKeys.SUMMARY), text);
            } catch (IOException e) {
                getLog().warn("Failed to load " + summary + ". " + e, e);
            }
        }

        String description = null;
        File readme = new File(templateTempDir, "ReadMe.md");
        if (readme.exists() && readme.isFile()) {
            try {
                description = Files.toString(readme);
            } catch (IOException e) {
                getLog().warn("Failed to load " + readme + ". " + e, e);
            }
        }
        if (description == null) {
            description = getProject().getDescription();
        }
        if (Strings.isNotBlank(description)) {
            annotations.put(AnnotationKeys.DESCRIPTION, description);
        }
    }

    /**
     * Generate a URL for the icon.
     *
     * Lets use a data URL if possible if the icon is relatively small; otherwise lets try convert the icon name
     * to an external link (e.g. using github).
     */
    protected String getIconUrl() {
        String answer = iconUrl;
        if (Strings.isNullOrBlank(answer)) {
            try {
                if (templateTempDir != null) {
                    templateTempDir.mkdirs();
                    File iconFile = copyIconToFolder(templateTempDir);
                    if (iconFile == null) {
                        copyAppConfigFiles(templateTempDir, appConfigDir);

                        // lets find the icon file...
                        for (String ext : ICON_EXTENSIONS) {
                            File file = new File(templateTempDir, "icon" + ext);
                            if (file.exists() && file.isFile()) {
                                iconFile = file;
                                break;
                            }
                        }
                    }
                    if (iconFile != null) {
                        answer = convertIconFileToURL(iconFile);
                    }
                }
            } catch (Exception e) {
                getLog().warn("Failed to load icon file: " + e, e);
            }
        }

        if (Strings.isNullOrBlank(answer)) {
            // maybe its a common icon
            String commonRef = asCommonIconRef(iconRef);
            if (commonRef != null) {
                answer = URLUtils.pathJoin("https://cdn.rawgit.com/fabric8io/fabric8", iconBranch,
                        "/fabric8-maven-plugin/src/main/resources/", commonRef);
            }
        }

        if (Strings.isNullOrBlank(answer)) {
            getLog().debug("No icon file found for this project");
        } else {
            getLog().info("Icon URL: " + answer);
        }

        return answer;
    }

    protected String convertIconFileToURL(File iconFile) throws IOException {
        long length = iconFile.length();

        int sizeK = Math.round(length / 1024);

        byte[] bytes = Files.readBytes(iconFile);
        byte[] encoded = Base64Encoder.encode(bytes);

        int base64SizeK = Math.round(encoded.length / 1024);

        if (base64SizeK < maximumDataUrlSizeK) {
            String mimeType = guessMediaType(iconFile);
            return "data:" + mimeType + ";charset=UTF-8;base64," + new String(encoded);
        } else {
            File iconSourceFile = new File(appConfigDir, iconFile.getName());
            if (iconSourceFile.exists()) {
                File rootProjectFolder = getRootProjectFolder();
                if (rootProjectFolder != null) {
                    String relativePath = Files.getRelativePath(rootProjectFolder, iconSourceFile);
                    String relativeParentPath = Files.getRelativePath(rootProjectFolder, getProject().getBasedir());
                    String urlPrefix = iconUrlPrefix;
                    if (Strings.isNullOrBlank(urlPrefix)) {
                        Scm scm = getProject().getScm();
                        if (scm != null) {
                            String url = scm.getUrl();
                            if (url != null) {
                                String[] prefixes = { "http://github.com/", "https://github.com/" };
                                for (String prefix : prefixes) {
                                    if (url.startsWith(prefix)) {
                                        url = URLUtils.pathJoin("https://cdn.rawgit.com/",
                                                url.substring(prefix.length()));
                                        break;
                                    }
                                }
                                if (url.endsWith(relativeParentPath)) {
                                    url = url.substring(0, url.length() - relativeParentPath.length());
                                }
                                urlPrefix = url;
                            }
                        }
                    }
                    if (Strings.isNullOrBlank(urlPrefix)) {
                        getLog().warn(
                                "No iconUrlPrefix defined or could be found via SCM in the pom.xml so cannot add an icon URL!");
                    } else {
                        String answer = URLUtils.pathJoin(urlPrefix, iconBranch, relativePath);
                        return answer;
                    }
                }
            } else {
                String commonRef = asCommonIconRef(iconRef);
                if (commonRef != null) {
                    String answer = URLUtils.pathJoin("https://cdn.rawgit.com/fabric8io/fabric8", iconBranch,
                            "/fabric8-maven-plugin/src/main/resources/", commonRef);
                    return answer;
                } else {
                    getLog().warn("Cannot find url for icon to use " + iconUrl);
                }
            }
        }
        return null;
    }

    protected String asCommonIconRef(String iconRef) {
        if (iconRef == null) {
            return null;
        }

        if (iconRef.startsWith("icons/")) {
            iconRef = iconRef.substring(6);
        }

        if (iconRef.contains("activemq")) {
            return "icons/activemq.svg";
        } else if (iconRef.contains("camel")) {
            return "icons/camel.svg";
        } else if (iconRef.contains("java")) {
            return "icons/java.svg";
        } else if (iconRef.contains("jetty")) {
            return "icons/jetty.svg";
        } else if (iconRef.contains("karaf")) {
            return "icons/karaf.svg";
        } else if (iconRef.contains("mule")) {
            return "icons/mule.svg";
        } else if (iconRef.contains("spring-boot")) {
            return "icons/spring-boot.svg";
        } else if (iconRef.contains("tomcat")) {
            return "icons/tomcat.svg";
        } else if (iconRef.contains("tomee")) {
            return "icons/tomee.svg";
        } else if (iconRef.contains("weld")) {
            return "icons/weld.svg";
        } else if (iconRef.contains("wildfly")) {
            return "icons/wildfly.svg";
        }

        return null;
    }

    protected Probe getLivenessProbe() {
        return getProbe("fabric8.livenessProbe");
    }

    protected Probe getReadinessProbe() {
        return getProbe("fabric8.readinessProbe");
    }

    protected Probe getProbe(String prefix) {
        Probe probe = new Probe();
        Properties properties = getProjectAndFabric8Properties(getProject());
        Long initialDelaySeconds = getLong(properties, prefix + ".initialDelaySeconds");
        if (initialDelaySeconds != null) {
            probe.setInitialDelaySeconds(initialDelaySeconds);
        }
        Long timeoutSeconds = getLong(properties, prefix + ".timeoutSeconds");
        if (timeoutSeconds != null) {
            probe.setTimeoutSeconds(timeoutSeconds);
        }
        HTTPGetAction httpGetAction = getHTTPGetAction(prefix, properties);
        if (httpGetAction != null) {
            probe.setHttpGet(httpGetAction);
            return probe;
        }
        ExecAction execAction = getExecAction(prefix, properties);
        if (execAction != null) {
            probe.setExec(execAction);
            return probe;
        }
        TCPSocketAction tcpSocketAction = getTCPSocketAction(prefix, properties);
        if (tcpSocketAction != null) {
            probe.setTcpSocket(tcpSocketAction);
            return probe;
        }

        return null;
    }

    private HTTPGetAction getHTTPGetAction(String prefix, Properties properties) {
        HTTPGetAction action = null;
        String httpGetPath = properties.getProperty(prefix + ".httpGet.path");
        String httpGetPort = properties.getProperty(prefix + ".httpGet.port");
        String httpGetHost = properties.getProperty(prefix + ".httpGet.host");
        if (Strings.isNotBlank(httpGetPath)) {
            action = new HTTPGetAction();
            action.setPath(httpGetPath);
            action.setHost(httpGetHost);
            if (Strings.isNotBlank(httpGetPort)) {
                IntOrString httpGetPortIntOrString = KubernetesHelper.createIntOrString(httpGetPort);
                action.setPort(httpGetPortIntOrString);
            }
        }
        return action;
    }

    private TCPSocketAction getTCPSocketAction(String prefix, Properties properties) {
        TCPSocketAction action = null;
        String port = properties.getProperty(prefix + ".port");
        if (Strings.isNotBlank(port)) {
            IntOrString portObj = new IntOrString();
            try {
                Integer portInt = Integer.parseInt(port);
                portObj.setIntVal(portInt);
            } catch (NumberFormatException e) {
                portObj.setStrVal(port);
            }
            action = new TCPSocketAction(portObj);
        }
        return action;
    }

    private ExecAction getExecAction(String prefix, Properties properties) {
        ExecAction action = null;
        String execCmd = properties.getProperty(prefix + ".exec");
        if (Strings.isNotBlank(execCmd)) {
            List<String> splitCommandLine = Commandline.translateCommandline(execCmd);
            if (!splitCommandLine.isEmpty()) {
                action = new ExecAction(splitCommandLine);
            }
        }
        return action;
    }

    public Boolean getContainerPrivileged() {
        return containerPrivileged;
    }

    public String getImagePullPolicy() {
        MavenProject project = getProject();
        String pullPolicy = imagePullPolicy;
        if (project != null) {
            String version = project.getVersion();
            if (Strings.isNullOrBlank(pullPolicy)) {
                if (version != null && version.endsWith("SNAPSHOT")) {
                    // TODO pullPolicy = "PullAlways";
                    pullPolicy = imagePullPolicySnapshot;
                }
            }
        }
        return pullPolicy;
    }

    public String getKubernetesContainerName() {
        if (Strings.isNullOrBlank(kubernetesContainerName)) {
            // lets generate it from the docker user and the camelCase artifactId
            String groupPrefix = null;
            MavenProject project = getProject();
            String imageName = getDockerImage();
            if (Strings.isNotBlank(imageName)) {
                String[] paths = imageName.split("/");
                if (paths != null) {
                    if (paths.length == 2) {
                        groupPrefix = paths[0];
                    } else if (paths.length == 3) {
                        groupPrefix = paths[1];
                    }
                }
            }
            if (Strings.isNullOrBlank(groupPrefix)) {
                groupPrefix = project.getGroupId();
            }
            kubernetesContainerName = groupPrefix + "-" + project.getArtifactId();
        }
        return kubernetesContainerName;
    }

    public void setKubernetesContainerName(String kubernetesContainerName) {
        this.kubernetesContainerName = kubernetesContainerName;
    }

    public String getProjectName() {
        return projectName;
    }

    public void setProjectName(String projectName) {
        this.projectName = projectName;
    }

    public String getGroupName() {
        return groupName;
    }

    public void setGroupName(String groupName) {
        this.groupName = groupName;
    }

    public Map<String, Integer> getDefaultContainerPortMap() {
        if (defaultContainerPortMap == null) {
            defaultContainerPortMap = new HashMap<>();
        }
        if (defaultContainerPortMap.isEmpty()) {
            // lets populate default values
            defaultContainerPortMap.put("jolokia", 8778);
            defaultContainerPortMap.put("web", 8080);
        }
        return defaultContainerPortMap;
    }

    public void setDefaultContainerPortMap(Map<String, Integer> defaultContainerPortMap) {
        this.defaultContainerPortMap = defaultContainerPortMap;
    }

    public List<ContainerPort> getContainerPorts() {
        if (containerPorts == null) {
            containerPorts = new ArrayList<>();
        }
        if (containerPorts.isEmpty()) {
            Map<String, ContainerPort> portMap = new HashMap<>();
            Properties properties1 = getProjectAndFabric8Properties(getProject());
            Map<String, String> hostPorts = findPropertiesWithPrefix(properties1, FABRIC8_PORT_HOST_PREFIX);
            Properties properties = getProjectAndFabric8Properties(getProject());
            Map<String, String> containerPortsMap = findPropertiesWithPrefix(properties,
                    FABRIC8_PORT_CONTAINER_PREFIX);

            for (Map.Entry<String, String> entry : containerPortsMap.entrySet()) {
                String name = entry.getKey();
                String portText = entry.getValue();
                Integer portNumber = parsePort(portText, FABRIC8_PORT_CONTAINER_PREFIX + name);
                if (portNumber != null) {
                    ContainerPort port = getOrCreatePort(portMap, name);
                    port.setContainerPort(portNumber);
                    port.setName(name);
                }
            }
            for (Map.Entry<String, String> entry : hostPorts.entrySet()) {
                String name = entry.getKey();
                String portText = entry.getValue();
                Integer portNumber = parsePort(portText, FABRIC8_PORT_HOST_PREFIX + name);
                if (portNumber != null) {
                    ContainerPort port = getOrCreatePort(portMap, name);
                    port.setHostPort(portNumber);

                    // if the container port isn't set, lets try default that using defaults
                    if (port.getContainerPort() == null) {
                        port.setContainerPort(getDefaultContainerPortMap().get(name));
                    }
                }
            }
            getLog().info("Generated port mappings: " + portMap);
            getLog().debug("from host ports: " + hostPorts);
            getLog().debug("from containerPorts ports: " + containerPorts);
            containerPorts.addAll(portMap.values());
        }
        return containerPorts;
    }

    protected static ContainerPort getOrCreatePort(Map<String, ContainerPort> portMap, String name) {
        ContainerPort answer = portMap.get(name);
        if (answer == null) {
            answer = new ContainerPort();
            portMap.put(name, answer);

            answer.setName(name);
        }
        return answer;
    }

    private String buildServicePrefix(String name, String prefix, String suffix) {
        String servicePrefix = prefix;
        if (Strings.isNotBlank(name)) {
            servicePrefix += "." + name;
        }
        return servicePrefix + "." + suffix;
    }

    private List<ServicePort> getServicePorts(String serviceName) throws MojoExecutionException {
        String servicePortPrefix = buildServicePrefix(serviceName, "fabric8.service", "port");
        String serviceContainerPortPrefix = buildServicePrefix(serviceName, "fabric8.service", "containerPort");
        String serviceNodePortPrefix = buildServicePrefix(serviceName, "fabric8.service", "nodePort");
        String serviceProtocolPrefix = buildServicePrefix(serviceName, "fabric8.service", "protocol");

        Properties properties1 = getProjectAndFabric8Properties(getProject());

        List<ServicePort> servicePorts = new ArrayList<>();
        Map<String, String> servicePortProperties = findPropertiesWithPrefix(properties1, servicePortPrefix + ".");
        Map<String, String> serviceContainerPortProperties = findPropertiesWithPrefix(properties1,
                serviceContainerPortPrefix + ".");
        Map<String, String> serviceNodePortProperties = findPropertiesWithPrefix(properties1,
                serviceNodePortPrefix + ".");
        Map<String, String> serviceProtocolProperties = findPropertiesWithPrefix(properties1,
                serviceProtocolPrefix + ".");

        for (Map.Entry<String, String> entry : servicePortProperties.entrySet()) {
            String name = entry.getKey();
            String servicePortText = entry.getValue();
            Integer servicePortNumber = parsePort(servicePortText, servicePortPrefix + name);
            if (servicePortNumber != null) {
                String containerPort = serviceContainerPortProperties.get(name);
                if (Strings.isNullOrBlank(containerPort)) {
                    getLog().warn("Missing container port for service - need to specify "
                            + serviceContainerPortPrefix + name + " property");
                } else {
                    ServicePort servicePort = new ServicePort();
                    servicePort.setName(name);
                    servicePort.setPort(servicePortNumber);

                    IntOrString containerPortSpec = getPortSpec(containerPort, serviceContainerPortPrefix, name);
                    servicePort.setTargetPort(containerPortSpec);

                    String nodePort = serviceNodePortProperties.get(name);
                    if (nodePort != null) {
                        IntOrString nodePortSpec = getPortSpec(nodePort, serviceNodePortPrefix, name);
                        Integer nodePortInt = nodePortSpec.getIntVal();
                        if (nodePortInt != null) {
                            servicePort.setNodePort(nodePortInt);
                        }
                    }

                    String portProtocol = serviceProtocolProperties.get(name);
                    if (portProtocol != null) {
                        servicePort.setProtocol(portProtocol);
                    }

                    servicePorts.add(servicePort);
                }
            }
        }

        Integer tempPort;
        String tempContainerPort;
        Integer tempNodePort;
        String tempServiceProtocol;

        if (Strings.isNotBlank(serviceName)) {
            tempPort = parsePort(properties1.getProperty(servicePortPrefix), servicePortPrefix);
            tempContainerPort = properties1.getProperty(serviceContainerPortPrefix);
            tempNodePort = parsePort(properties1.getProperty(serviceNodePortPrefix), serviceNodePortPrefix);
            tempServiceProtocol = properties1.getProperty(serviceProtocolPrefix, "TCP");
        } else {
            tempPort = servicePort;
            tempContainerPort = serviceContainerPort;
            tempNodePort = serviceNodePort;
            tempServiceProtocol = serviceProtocol;
        }

        if (tempContainerPort != null || tempPort != null) {

            if (servicePorts.size() > 0) {
                throw new MojoExecutionException(
                        "Multi-port services must use the " + servicePortPrefix + "<name> format");
            }

            ServicePort actualServicePort = new ServicePort();

            IntOrString containerPort = getPortSpec(tempContainerPort, serviceContainerPortPrefix, null);

            actualServicePort.setTargetPort(containerPort);
            actualServicePort.setPort(tempPort);
            if (tempNodePort != null) {
                actualServicePort.setNodePort(tempNodePort);
            }
            if (tempServiceProtocol != null) {
                actualServicePort.setProtocol(tempServiceProtocol);
                servicePorts.add(actualServicePort);
            }
        }

        return servicePorts;
    }

    private IntOrString getPortSpec(String portText, String portServicePrefix, String name) {
        IntOrString portSpec = new IntOrString();
        String portServiceName = portServicePrefix;
        if (name != null) {
            portServiceName = portServicePrefix + name;
        }
        Integer portNumber = parsePort(portText, portServiceName);
        if (portNumber != null) {
            portSpec.setIntVal(portNumber);
        } else {
            portSpec.setStrVal(portText);
        }

        return portSpec;
    }

    protected static EnvVar getOrCreateEnv(Map<String, EnvVar> envMap, String name) {
        EnvVar answer = envMap.get(name);
        if (answer == null) {
            answer = new EnvVar();
            envMap.put(name, answer);
        }
        return answer;
    }

    protected Integer parsePort(String portText, String propertyName) {
        if (Strings.isNotBlank(portText)) {
            try {
                return Integer.parseInt(portText);
            } catch (NumberFormatException e) {
                getLog().debug("Failed to parse port text: " + portText + " from maven property " + propertyName
                        + ". " + e, e);
            }
        }
        return null;
    }

    public void setContainerPorts(List<ContainerPort> ports) {
        this.containerPorts = ports;
    }

    public void setServicePorts(List<ServicePort> ports) {
        this.servicePorts = ports;
    }

    public Map<String, String> getLabels() {
        if (labels == null) {
            labels = new HashMap<>();
        }
        if (labels.isEmpty()) {
            labels = findPropertiesWithPrefix(getProjectAndFabric8Properties(getProject()), "fabric8.label.",
                    Strings.toLowerCaseFunction());
        }
        return labels;
    }

    public Map<String, String> getPodSpecAnnotations() throws MojoExecutionException {
        if (podSpecAnnotations == null) {
            podSpecAnnotations = loadAnnotations(podSpecAnnotationsFile, "fabric8.annotations.podSpec.", "PodSpec");
        }
        return podSpecAnnotations;
    }

    public Map<String, String> getRCAnnotations() throws MojoExecutionException {
        if (rcAnnotations == null) {
            rcAnnotations = loadAnnotations(rcAnnotationsFile, "fabric8.annotations.rc.", "RC");
        }
        return rcAnnotations;
    }

    public Map<String, String> getTemplateAnnotations() throws MojoExecutionException {
        if (templateAnnotations == null) {
            templateAnnotations = loadAnnotations(templateAnnotationsFile, "fabric8.annotations.template.",
                    "Template");
        }
        return templateAnnotations;
    }

    public Map<String, String> getServiceAnnotations() throws MojoExecutionException {
        Map<String, String> serviceAnnotations = loadAnnotations(serviceAnnotationsFile,
                "fabric8.annotations.service.", "Service");
        return serviceAnnotations;
    }

    protected Map<String, String> loadAnnotations(File annotationsFile, String propertiesPrefix,
            String annotationsName) throws MojoExecutionException {
        Map<String, String> answer = findPropertiesWithPrefix(getProjectAndFabric8Properties(getProject()),
                propertiesPrefix, Strings.toLowerCaseFunction());
        if (annotationsFile != null && annotationsFile.exists() && annotationsFile.isFile()) {
            try {
                Properties properties = new Properties();
                properties.load(new FileInputStream(annotationsFile));
                Map<String, String> fileAnnotations = PropertiesHelper.toMap(properties);
                answer.putAll(fileAnnotations);
            } catch (IOException e) {
                throw new MojoExecutionException("Failed to load podSpecAnnotationsFile properties file "
                        + podSpecAnnotationsFile + ". " + e, e);
            }
        }
        //kubernetes annotation keys can be prefixed by namespace like namespace/name, but
        //xml tags can't contain slashes. So by convention we will change the last "." into a "/".
        //for example 'apiman.io.servicepath' will be turned into 'apiman.io/servicepath'
        Map<String, String> newAnswer = new HashMap<String, String>();
        for (String key : answer.keySet()) {
            int lastDot = key.lastIndexOf(".");
            if (!key.contains("/") && lastDot > 0) {
                String namespace = key.substring(0, lastDot);
                String name = key.substring(lastDot + 1);
                newAnswer.put(namespace + "/" + name, answer.get(key));
            } else {
                newAnswer.put(key, answer.get(key));
            }
        }
        return newAnswer;
    }

    public List<EnvVar> getEnvironmentVariables() throws MojoExecutionException {
        if (environmentVariables == null) {
            environmentVariables = new ArrayList<EnvVar>();
        }
        if (environmentVariables.isEmpty()) {
            Map<String, EnvVar> envMap = new HashMap<>();
            Map<String, String> envs = getExportedEnvironmentVariables();

            for (Map.Entry<String, String> entry : envs.entrySet()) {
                String name = entry.getKey();
                String value = entry.getValue();

                if (name != null) {
                    EnvVar env = getOrCreateEnv(envMap, name);
                    env.setName(name);

                    if (env.getValue() == null) {
                        env.setValue(value);
                    }
                }
            }
            getLog().info("Generated env mappings: " + envMap);
            getLog().debug("from envs: " + envs);
            environmentVariables.addAll(envMap.values());
        }

        if (includeNamespaceEnvVar) {
            environmentVariables.add(new EnvVarBuilder().withName(kubernetesNamespaceEnvVar).withNewValueFrom()
                    .withNewFieldRef().withFieldPath("metadata.namespace").endFieldRef().endValueFrom().build());
        }
        if (includePodEnvVar) {
            environmentVariables.add(new EnvVarBuilder().withName(kubernetesPodEnvVar).withNewValueFrom()
                    .withNewFieldRef().withFieldPath("metadata.name").endFieldRef().endValueFrom().build());
        }

        return environmentVariables;
    }

    public Map<String, String> getExportedEnvironmentVariables() throws MojoExecutionException {
        if (includeAllEnvironmentVariables) {
            try {
                JsonSchema schema = getEnvironmentVariableJsonSchema();
                Map<String, String> answer = new TreeMap<>();
                Map<String, JsonSchemaProperty> properties = schema.getProperties();
                Set<Map.Entry<String, JsonSchemaProperty>> entries = properties.entrySet();
                for (Map.Entry<String, JsonSchemaProperty> entry : entries) {
                    String name = entry.getKey();
                    String value = entry.getValue().getDefaultValue();
                    if (value == null) {
                        value = "";
                    }
                    answer.put(name, value);
                }
                Map<String, String> mavenEnvVars = getEnvironmentVariableProperties();
                answer.putAll(mavenEnvVars);
                return answer;
            } catch (IOException e) {
                throw new MojoExecutionException("Failed to load environment variable json schema files: " + e, e);
            }
        } else {
            return getEnvironmentVariableProperties();
        }
    }

    public List<VolumeMount> getVolumeMounts() {
        List<VolumeMount> volumeMount = new ArrayList<>();
        MavenProject project = getProject();
        for (Map.Entry<Object, Object> entry : getProjectAndFabric8Properties(project).entrySet()) {
            Object key = entry.getKey();
            if (key instanceof String) {
                String s = (String) key;
                Matcher m = VOLUME_PATTERN.matcher(s);
                if (m.matches()) {
                    String name = m.group(NAME);
                    String type = m.group(ATTRIBUTE_TYPE);
                    if (type.equals(VOLUME_MOUNT_PATH)) {
                        String path = String.valueOf(entry.getValue());
                        volumeMount.add(new VolumeMountBuilder().withName(name).withMountPath(path)
                                .withReadOnly(false).build());
                    }
                }
            }
        }
        return volumeMount;
    }

    public List<Volume> getVolumes() {
        List<Volume> volumes = new ArrayList<>();
        MavenProject project = getProject();
        Properties properties = getProjectAndFabric8Properties(project);

        for (Map.Entry<Object, Object> entry : properties.entrySet()) {
            Object key = entry.getKey();
            if (key instanceof String) {
                String s = (String) key;
                Matcher m = VOLUME_PATTERN.matcher(s);
                if (m.matches()) {
                    String name = m.group(NAME);
                    String type = m.group(ATTRIBUTE_TYPE);
                    VolumeType volumeType = VolumeType.typeFor(type);
                    if (volumeType != null) {
                        volumes.add(volumeType.fromProperties(name, properties));
                    }
                }
            }
        }
        return volumes;
    }

    public Template getTemplate() throws MojoExecutionException {
        List<io.fabric8.openshift.api.model.Parameter> parameters = new ArrayList<>();
        MavenProject project = getProject();
        Properties projectProperties = getProjectAndFabric8Properties(getProject());
        Set<String> paramNames = new HashSet<>();
        if (templateParametersPropertiesFile != null && templateParametersPropertiesFile.isFile()
                && templateParametersPropertiesFile.exists()) {
            final String valuePostfix = ".value";
            final String descriptionPostfix = ".description";
            try {
                Properties properties = new Properties();
                properties.load(new FileInputStream(templateParametersPropertiesFile));
                // lets append the prefix
                Set<Object> keys = properties.keySet();
                Properties prefixedProperties = new Properties();
                for (Object key : keys) {
                    if (key != null) {
                        String name = key.toString();
                        String value = properties.getProperty(name);
                        prefixedProperties.put(PARAMETER_PREFIX + "." + name, value);
                    }
                }
                loadParametersFromProperties(prefixedProperties, parameters, paramNames);
            } catch (IOException e) {
                throw new MojoExecutionException("Failed to load templateParameters properties file "
                        + templateParametersPropertiesFile + ". " + e, e);
            }
        }
        loadParametersFromProperties(projectProperties, parameters, paramNames);
        String templateName = projectProperties.containsKey(TEMPLATE_NAME)
                ? String.valueOf(projectProperties.getProperty(TEMPLATE_NAME))
                : project.getArtifactId();
        return new TemplateBuilder().withNewMetadata().withName(templateName)
                .withAnnotations(getTemplateAnnotations()).endMetadata().withParameters(parameters).build();
    }

    protected void loadParametersFromProperties(Properties properties,
            List<io.fabric8.openshift.api.model.Parameter> parameters, Set<String> paramNames) {
        for (Map.Entry<Object, Object> entry : properties.entrySet()) {
            Object key = entry.getKey();
            if (key instanceof String) {
                String s = (String) key;
                Matcher m = PARAM_PATTERN.matcher(s);
                if (m.matches()) {
                    String name = m.group(NAME);
                    if (paramNames.add(name)) {
                        String value = properties.getProperty(String.format(PARAMETER_PROPERTY, name, VALUE));
                        String from = properties.getProperty(String.format(PARAMETER_PROPERTY, name, FROM));
                        String description = properties
                                .getProperty(String.format(PARAMETER_PROPERTY, name, DESCRIPTION));
                        String generate = properties.getProperty(String.format(PARAMETER_PROPERTY, name, GENERATE));
                        //If neither value nor from has been specified read the value inline.
                        if (Strings.isNullOrBlank(value) && Strings.isNullOrBlank(from)) {
                            value = properties.getProperty(String.format(PARAMETER_NAME_PREFIX, name));
                        }
                        getLog().info("Found Template parameter: " + name + labelValueOrBlank("value", value)
                                + labelValueOrBlank("from", from) + labelValueOrBlank("generate", generate)
                                + labelValueOrBlank("description", description));

                        parameters.add(new ParameterBuilder().withName(name).withFrom(from).withValue(value)
                                .withGenerate(generate).withDescription(description).build());
                    }
                }
            }
        }
    }

    private String labelValueOrBlank(String label, String value) {
        if (Strings.isNotBlank(value)) {
            return " " + label + ": " + value;
        } else {
            return "";
        }
    }

    public void setLabels(Map<String, String> labels) {
        this.labels = labels;
    }

    protected static void addIfNotDefined(Map<String, Object> variables, String key, String value) {
        if (!variables.containsKey(key)) {
            variables.put(key, value);
        }
    }

}