diff --git a/core/src/main/java/io/fabric8/maven/core/config/ResourceConfig.java b/core/src/main/java/io/fabric8/maven/core/config/ResourceConfig.java index de676d0b9a..073a4b74db 100644 --- a/core/src/main/java/io/fabric8/maven/core/config/ResourceConfig.java +++ b/core/src/main/java/io/fabric8/maven/core/config/ResourceConfig.java @@ -92,8 +92,14 @@ public class ResourceConfig { @Parameter private List serviceAccounts; + @Parameter private List ingressRules; + /** + * Host/domain for Route/Ingress. + */ + private String routeDomain; + public Optional> getEnv() { return Optional.ofNullable(env); } @@ -170,6 +176,9 @@ public List getRemotes() { public List getCrdContexts() { return customResourceDefinitions; } public List getIngressRules() { return ingressRules; } + + public String getRouteDomain() { return routeDomain; } + // ============================================================================================= public static class Builder { @@ -198,6 +207,7 @@ public Builder(ResourceConfig config) { this.config.namespace = config.getNamespace(); this.config.remotes = config.getRemotes(); this.config.ingressRules = config.getIngressRules(); + this.config.routeDomain= config.getRouteDomain(); } } @@ -271,6 +281,11 @@ public Builder withCustomResourceDefinitions(List customResourceDefiniti return this; } + public Builder withRouteDomain(String routeDomain) { + config.routeDomain = routeDomain; + return this; + } + public ResourceConfig build() { return config; } @@ -302,4 +317,4 @@ public ResourceConfig build() { // fabric8.namespaceEnvVar // fabric8.provider -} +} \ No newline at end of file diff --git a/enricher/standard/src/main/java/io/fabric8/maven/enricher/standard/DefaultNamespaceEnricher.java b/enricher/standard/src/main/java/io/fabric8/maven/enricher/standard/DefaultNamespaceEnricher.java index 09570e27be..60f80bd532 100644 --- a/enricher/standard/src/main/java/io/fabric8/maven/enricher/standard/DefaultNamespaceEnricher.java +++ b/enricher/standard/src/main/java/io/fabric8/maven/enricher/standard/DefaultNamespaceEnricher.java @@ -29,6 +29,7 @@ import io.fabric8.maven.enricher.api.MavenEnricherContext; import io.fabric8.openshift.api.model.Project; import io.fabric8.openshift.api.model.ProjectBuilder; +import io.fabric8.openshift.api.model.ProjectStatus; import java.util.Arrays; @@ -132,14 +133,18 @@ public void visit(ObjectMetaBuilder metaBuilder) { builder.accept(new TypedVisitor() { @Override public void visit(NamespaceBuilder builder) { - builder.withNewStatus("active").editMetadata().withNamespace(null).endMetadata().build(); + if (builder.getStatus().equals("active")) { + builder.editOrNewStatus().endStatus().build(); + } } }); builder.accept(new TypedVisitor() { @Override public void visit(ProjectBuilder builder) { - builder.withNewStatus("active").editMetadata().withNamespace(null).endMetadata().build(); + if (builder.getStatus().equals(new ProjectStatus("active"))) { + builder.editOrNewStatus().endStatus().build(); + } } }); } diff --git a/enricher/standard/src/main/java/io/fabric8/maven/enricher/standard/IngressEnricher.java b/enricher/standard/src/main/java/io/fabric8/maven/enricher/standard/IngressEnricher.java index bcf8ec18ab..508daa66ec 100644 --- a/enricher/standard/src/main/java/io/fabric8/maven/enricher/standard/IngressEnricher.java +++ b/enricher/standard/src/main/java/io/fabric8/maven/enricher/standard/IngressEnricher.java @@ -17,25 +17,41 @@ package io.fabric8.maven.enricher.standard; import io.fabric8.kubernetes.api.builder.TypedVisitor; +import io.fabric8.kubernetes.api.model.HasMetadata; import io.fabric8.kubernetes.api.model.KubernetesListBuilder; import io.fabric8.kubernetes.api.model.ObjectMeta; import io.fabric8.kubernetes.api.model.Service; import io.fabric8.kubernetes.api.model.ServiceBuilder; import io.fabric8.kubernetes.api.model.ServicePort; import io.fabric8.kubernetes.api.model.ServiceSpec; +import io.fabric8.kubernetes.api.model.extensions.HTTPIngressPath; +import io.fabric8.kubernetes.api.model.extensions.HTTPIngressPathBuilder; +import io.fabric8.kubernetes.api.model.extensions.HTTPIngressRuleValue; import io.fabric8.kubernetes.api.model.extensions.Ingress; +import io.fabric8.kubernetes.api.model.extensions.IngressBackend; import io.fabric8.kubernetes.api.model.extensions.IngressBackendBuilder; import io.fabric8.kubernetes.api.model.extensions.IngressBuilder; +import io.fabric8.kubernetes.api.model.extensions.IngressList; +import io.fabric8.kubernetes.api.model.extensions.IngressRule; +import io.fabric8.kubernetes.api.model.extensions.IngressSpec; import io.fabric8.kubernetes.api.model.extensions.IngressSpecBuilder; +import io.fabric8.kubernetes.client.KubernetesClient; import io.fabric8.maven.core.config.PlatformMode; import io.fabric8.maven.core.config.ResourceConfig; +import io.fabric8.maven.core.util.Configs; +import io.fabric8.maven.core.util.FileUtil; import io.fabric8.maven.core.util.kubernetes.Fabric8Annotations; +import io.fabric8.maven.core.util.kubernetes.KubernetesHelper; import io.fabric8.maven.enricher.api.BaseEnricher; import io.fabric8.maven.enricher.api.MavenEnricherContext; +import org.apache.commons.lang3.StringUtils; import java.util.ArrayList; +import java.util.Collection; import java.util.List; import java.util.Map; +import java.util.Objects; +import java.util.Set; import java.util.concurrent.atomic.AtomicBoolean; /** @@ -48,41 +64,80 @@ public IngressEnricher(MavenEnricherContext buildContext) { super(buildContext, "fmp-ingress"); } + private String routeDomainPostfix; + @Override public void create(PlatformMode platformMode, final KubernetesListBuilder listBuilder) { + ResourceConfig resourceConfig = getConfiguration().getResource().orElse(null); + routeDomainPostfix = resourceConfig.getRouteDomain(); + if (platformMode == PlatformMode.kubernetes) { - final List ingresses = new ArrayList<>(); listBuilder.accept(new TypedVisitor() { @Override public void visit(ServiceBuilder serviceBuilder) { addIngress(listBuilder, serviceBuilder); } }); - } } private void addIngress(KubernetesListBuilder listBuilder, ServiceBuilder serviceBuilder) { ObjectMeta metadata = serviceBuilder.getMetadata(); - if (metadata != null && isExposedService(serviceBuilder)) { - String name = metadata.getName(); - if (!hasIngress(listBuilder, name)) { + if (metadata != null && isExposedService(serviceBuilder) && shouldCreateExternalURLForService(serviceBuilder)) { + String serviceName = metadata.getName(); + if (!hasIngress(listBuilder, serviceName)) { Integer servicePort = getServicePort(serviceBuilder); if (servicePort != null) { ResourceConfig resourceConfig = getConfiguration().getResource().orElse(null); + if (StringUtils.isNotBlank(routeDomainPostfix)) { + routeDomainPostfix = serviceName + "." + FileUtil.stripPrefix(routeDomainPostfix, "."); + } + IngressBuilder ingressBuilder = new IngressBuilder(). withMetadata(serviceBuilder.getMetadata()). withNewSpec(). - endSpec(); IngressSpecBuilder specBuilder = new IngressSpecBuilder().withBackend(new IngressBackendBuilder(). - withNewServiceName(name). + withNewServiceName(serviceName). withNewServicePort(getServicePort(serviceBuilder)). build()); if (resourceConfig != null) { - specBuilder.addAllToRules(resourceConfig.getIngressRules()); + if (resourceConfig.getIngressRules() != null) { + specBuilder.addAllToRules(resourceConfig.getIngressRules()); + } else { + List paths = new ArrayList<>(); + List ports = serviceBuilder.getSpec().getPorts(); + if (ports != null) { + for (ServicePort port : ports) { + Integer portNumber = port.getPort(); + if (portNumber != null) { + HTTPIngressPath path = + new HTTPIngressPathBuilder() + .withNewBackend() + .withServiceName(serviceName) + .withServicePort(KubernetesHelper.createIntOrString(portNumber)) + .endBackend() + .build(); + paths.add(path); + } + } + } + if (paths.isEmpty()) { + ingressBuilder = ingressBuilder.withSpec(specBuilder.build()); + listBuilder.addToIngressItems(ingressBuilder.build()); + return; + } + + ingressBuilder = ingressBuilder.withSpec(specBuilder.addNewRule(). + withHost(routeDomainPostfix). + withNewHttp(). + withPaths(paths). + endHttp(). + endRule().build()); + listBuilder.addToIngressItems(ingressBuilder.build()); + } } } } @@ -145,4 +200,35 @@ private boolean isExposedService(Service service) { } return false; } + + /** + * Should we try to create an external URL for the given service? + *

+ * By default lets ignore the kubernetes services and any service which does not expose ports 80 and 443 + * + * @return true if we should create an Ingress for this service. + */ + private boolean shouldCreateExternalURLForService(ServiceBuilder service) { + String serviceName = service.getMetadata().getName(); + if ("kubernetes".equals(serviceName) || "kubernetes-ro".equals(serviceName)) { + return false; + } + ServiceSpec spec = service.getSpec(); + List ports = spec.getPorts(); + log.debug("Service " + serviceName + " has ports: " + ports); + if (ports.size() == 1) { + String type = null; + if (spec != null) { + type = spec.getType(); + if (Objects.equals(type, "LoadBalancer")) { + return true; + } + } + log.info("Not generating Ingress for service " + serviceName + " type is not LoadBalancer: " + type); + return false; + } else { + log.info("Not generating Ingress for service " + serviceName + " as only single port services are supported. Has ports: " + ports); + return false; + } + } } \ No newline at end of file diff --git a/enricher/standard/src/main/java/io/fabric8/maven/enricher/standard/openshift/RouteEnricher.java b/enricher/standard/src/main/java/io/fabric8/maven/enricher/standard/openshift/RouteEnricher.java index 4f9bd0e82c..26050bd893 100644 --- a/enricher/standard/src/main/java/io/fabric8/maven/enricher/standard/openshift/RouteEnricher.java +++ b/enricher/standard/src/main/java/io/fabric8/maven/enricher/standard/openshift/RouteEnricher.java @@ -17,8 +17,10 @@ package io.fabric8.maven.enricher.standard.openshift; import java.util.ArrayList; +import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.concurrent.atomic.AtomicBoolean; import io.fabric8.kubernetes.api.builder.TypedVisitor; @@ -30,12 +32,15 @@ import io.fabric8.kubernetes.api.model.ServicePort; import io.fabric8.kubernetes.api.model.ServiceSpec; import io.fabric8.maven.core.config.PlatformMode; +import io.fabric8.maven.core.config.ResourceConfig; +import io.fabric8.maven.core.util.FileUtil; import io.fabric8.maven.core.util.kubernetes.Fabric8Annotations; import io.fabric8.maven.enricher.api.BaseEnricher; import io.fabric8.maven.enricher.api.MavenEnricherContext; import io.fabric8.openshift.api.model.Route; import io.fabric8.openshift.api.model.RouteBuilder; import io.fabric8.openshift.api.model.RoutePort; +import org.apache.commons.lang3.StringUtils; /** * Enricher which generates a Route for each exposed Service @@ -48,8 +53,16 @@ public RouteEnricher(MavenEnricherContext buildContext) { this.generateRoute = getValueFromConfig(GENERATE_ROUTE, true); } + private String routeDomainPostfix; + @Override public void create(PlatformMode platformMode, final KubernetesListBuilder listBuilder) { + ResourceConfig resourceConfig = getConfiguration().getResource().orElse(null); + + if (resourceConfig != null && resourceConfig.getRouteDomain() != null) { + routeDomainPostfix = resourceConfig.getRouteDomain(); + } + if(platformMode == PlatformMode.openshift && generateRoute.equals(Boolean.TRUE)) { final List routes = new ArrayList<>(); listBuilder.accept(new TypedVisitor() { @@ -68,25 +81,6 @@ public void visit(ServiceBuilder serviceBuilder) { } } - private void addRoute(KubernetesListBuilder listBuilder, ServiceBuilder serviceBuilder, List routes) { - ObjectMeta metadata = serviceBuilder.getMetadata(); - if (metadata != null && isExposedService(serviceBuilder)) { - String name = metadata.getName(); - if (!hasRoute(listBuilder, name)) { - RoutePort routePort = createRoutePort(serviceBuilder); - if (routePort != null) { - // TODO one day lets support multiple ports on a Route when the model supports it - routes.add(new RouteBuilder(). - withMetadata(serviceBuilder.getMetadata()). - withNewSpec(). - withPort(routePort). - withNewTo().withKind("Service").withName(name).endTo(). - endSpec(). - build()); - } - } - } - } private RoutePort createRoutePort(ServiceBuilder serviceBuilder) { RoutePort routePort = null; @@ -107,6 +101,74 @@ private RoutePort createRoutePort(ServiceBuilder serviceBuilder) { return routePort; } + private String prepareHostForRoute(String routeDomainPostfix, String name) { + String ret = FileUtil.stripPostfix(name,"-service"); + ret = FileUtil.stripPostfix(ret,"."); + ret += "."; + ret += FileUtil.stripPrefix(routeDomainPostfix, "."); + return ret; + } + + private Set getPorts(ServiceBuilder service) { + Set answer = new HashSet<>(); + if (service != null) { + ServiceSpec spec = getOrCreateSpec(service); + for (ServicePort port : spec.getPorts()) { + answer.add(port.getPort()); + } + } + return answer; + } + + public static ServiceSpec getOrCreateSpec(ServiceBuilder entity) { + ServiceSpec spec = entity.getSpec(); + if (spec == null) { + spec = new ServiceSpec(); + entity.editOrNewSpec().endSpec(); + } + return spec; + } + + private boolean hasExactlyOneServicePort(ServiceBuilder service, String id) { + Set ports = getPorts(service); + if (ports.size() != 1) { + log.info("Not generating route for service " + id + " as only single port services are supported. Has ports: " + + ports); + return false; + } else { + return true; + } + } + + private void addRoute(KubernetesListBuilder listBuilder, ServiceBuilder serviceBuilder, List routes) { + ObjectMeta metadata = serviceBuilder.getMetadata(); + + if (metadata != null && StringUtils.isNotBlank(metadata.getName()) + && hasExactlyOneServicePort(serviceBuilder, metadata.getName()) && isExposedService(serviceBuilder)) { + String name = metadata.getName(); + if (!hasRoute(listBuilder, name)) { + if (StringUtils.isNotBlank(routeDomainPostfix)) { + routeDomainPostfix = prepareHostForRoute(routeDomainPostfix, name); + } else { + routeDomainPostfix = ""; + } + + RoutePort routePort = createRoutePort(serviceBuilder); + if (routePort != null) { + // TODO one day lets support multiple ports on a Route when the model supports it + routes.add(new RouteBuilder(). + withMetadata(serviceBuilder.getMetadata()). + withNewSpec(). + withPort(routePort). + withNewTo().withKind("Service").withName(name).endTo(). + withHost(routeDomainPostfix). + endSpec(). + build()); + } + } + } + } + /** * Returns true if we already have a route created for the given name */ diff --git a/plugin/src/main/java/io/fabric8/maven/plugin/enricher/EnricherManager.java b/plugin/src/main/java/io/fabric8/maven/plugin/enricher/EnricherManager.java index eb82a9bec3..f9550e5d4c 100644 --- a/plugin/src/main/java/io/fabric8/maven/plugin/enricher/EnricherManager.java +++ b/plugin/src/main/java/io/fabric8/maven/plugin/enricher/EnricherManager.java @@ -60,7 +60,6 @@ public EnricherManager(ResourceConfig resourceConfig, EnricherContext enricherCo "META-INF/fabric8/enricher"); logEnrichers(filterEnrichers(defaultEnricherConfig, enrichers)); - } public void createDefaultResources(PlatformMode platformMode, final KubernetesListBuilder builder) { @@ -79,17 +78,13 @@ public void enrich(PlatformMode platformMode, KubernetesListBuilder builder) { enrich(platformMode, defaultEnricherConfig, builder); } - public void enrich(PlatformMode platformMode, ProcessorConfig config, KubernetesListBuilder builder) { - enrich(platformMode, config, builder, enrichers); - } - /** * Allow enricher to add Metadata to the resources. - * + * @param platformMode + * @param enricherConfig * @param builder builder to customize - * @param enricherList list of enrichers */ - private void enrich(PlatformMode platformMode, final ProcessorConfig enricherConfig, final KubernetesListBuilder builder, final List enricherList) { + public void enrich(PlatformMode platformMode, final ProcessorConfig enricherConfig, final KubernetesListBuilder builder) { loop(enricherConfig, enricher -> { enricher.enrich(platformMode, builder); return null; diff --git a/plugin/src/main/java/io/fabric8/maven/plugin/mojo/build/ApplyMojo.java b/plugin/src/main/java/io/fabric8/maven/plugin/mojo/build/ApplyMojo.java index 5d40825eaa..9a694841ff 100644 --- a/plugin/src/main/java/io/fabric8/maven/plugin/mojo/build/ApplyMojo.java +++ b/plugin/src/main/java/io/fabric8/maven/plugin/mojo/build/ApplyMojo.java @@ -400,7 +400,7 @@ private Ingress createIngressForService(String routeDomainPostfix, String namesp return ingress; } ingress = new IngressBuilder(). - withNewMetadata().withName(ingressId).withNamespace(namespace).endMetadata(). + withNewMetadata().withName(serviceName).withNamespace(namespace).endMetadata(). withNewSpec(). addNewRule(). withHost(host). diff --git a/plugin/src/main/java/io/fabric8/maven/plugin/mojo/build/ResourceMojo.java b/plugin/src/main/java/io/fabric8/maven/plugin/mojo/build/ResourceMojo.java index d08d6cba95..5e5a877e18 100644 --- a/plugin/src/main/java/io/fabric8/maven/plugin/mojo/build/ResourceMojo.java +++ b/plugin/src/main/java/io/fabric8/maven/plugin/mojo/build/ResourceMojo.java @@ -195,6 +195,13 @@ public class ResourceMojo extends AbstractFabric8Mojo { @Parameter(property = "fabric8.gitRemote") private String gitRemote; + + /** + * The domain added to the service ID when creating OpenShift routes + */ + @Parameter(property = "fabric8.domain") + private String routeDomain; + @Parameter private ProcessorConfig enricher; @@ -446,6 +453,10 @@ private KubernetesList generateResources(PlatformMode platformMode) if (namespace != null && !namespace.isEmpty()) { resources = new ResourceConfig.Builder(resources).withNamespace(namespace).build(); } + + if (routeDomain != null && !routeDomain.isEmpty() ) { + resources = new ResourceConfig.Builder(resources).withRouteDomain(routeDomain).build(); + } // Manager for calling enrichers. MavenEnricherContext.Builder ctxBuilder = new MavenEnricherContext.Builder() .project(project)