From b48aa4c6fa0f652c71f2447ba1606e9e0d76c791 Mon Sep 17 00:00:00 2001 From: Rohan Kumar Date: Fri, 23 Nov 2018 22:38:49 +0530 Subject: [PATCH] Fix #1019: Custom Liveness/Readiness probes are not being created Added CustomProbeEnricher for adding probes via XML config --- CHANGELOG.md | 1 + .../maven/core/config/ResourceConfig.java | 10 ++ .../maven/core/handler/ProbeHandler.java | 10 +- .../maven/core/handler/ProbeHandlerTest.java | 8 +- .../standard/CustomProbeEnricher.java | 60 ++++++++ .../META-INF/fabric8/enricher-default | 3 + .../standard/CustomProbeEnricherTest.java | 130 ++++++++++++++++++ .../META-INF/fabric8/profiles-default.yml | 1 + 8 files changed, 214 insertions(+), 9 deletions(-) create mode 100644 enricher/standard/src/main/java/io/fabric8/maven/enricher/standard/CustomProbeEnricher.java create mode 100644 enricher/standard/src/test/java/io/fabric8/maven/enricher/standard/CustomProbeEnricherTest.java diff --git a/CHANGELOG.md b/CHANGELOG.md index 9d27d91ce3..0de37923d0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ After this we will switch probably to real [Semantic Versioning 2.0.0](http://se * Fix 839: Sets Spring Boot generator color config property as String (http://docs.spring.io/spring-boot/docs/current/api/org/springframework/boot/ansi/AnsiOutput.Enabled.html) * Fix 1412: mvn deploy fails when using a Dockerfile during S2I build * Fix 796: Remove workaround to produce both .yaml and .json files +* Fix 1019: Custom liveness/readiness probes are not being created ### 3.5-SNAPSHOT * Fix 1021: Avoids empty deployment selector value in generated yaml resource 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 c88599fc51..deed3fdbcc 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 @@ -189,6 +189,16 @@ public Builder withConfigMap(ConfigMap configMap) { return this; } + public Builder withLiveness(ProbeConfig liveness) { + config.liveness = liveness; + return this; + } + + public Builder withReadiness(ProbeConfig readiness) { + config.readiness = readiness; + return this; + } + public ResourceConfig build() { return config; } diff --git a/core/src/main/java/io/fabric8/maven/core/handler/ProbeHandler.java b/core/src/main/java/io/fabric8/maven/core/handler/ProbeHandler.java index 9bfc87ecae..78597ad1cf 100644 --- a/core/src/main/java/io/fabric8/maven/core/handler/ProbeHandler.java +++ b/core/src/main/java/io/fabric8/maven/core/handler/ProbeHandler.java @@ -77,11 +77,11 @@ private HTTPGetAction getHTTPGetAction(String getUrl) { } try { URL url = new URL(getUrl); - return new HTTPGetAction(url.getHost(), - null /* headers */, - url.getPath(), - new IntOrString(url.getPort()), - url.getProtocol()); + return new HTTPGetAction(url.getHost(), + null /* headers */, + url.getPath(), + new IntOrString(url.getPort()), + url.getProtocol().toUpperCase()); } catch (MalformedURLException e) { throw new IllegalArgumentException("Invalid URL " + getUrl + " given for HTTP GET readiness check"); } diff --git a/core/src/test/java/io/fabric8/maven/core/handler/ProbeHandlerTest.java b/core/src/test/java/io/fabric8/maven/core/handler/ProbeHandlerTest.java index 9c9c0ff672..80457bfe5e 100644 --- a/core/src/test/java/io/fabric8/maven/core/handler/ProbeHandlerTest.java +++ b/core/src/test/java/io/fabric8/maven/core/handler/ProbeHandlerTest.java @@ -84,7 +84,7 @@ public void getHTTPProbeWithHTTPURLTest() { assertEquals(null,probe.getHttpGet().getHttpHeaders()); assertEquals("/healthz",probe.getHttpGet().getPath()); assertEquals(8080,probe.getHttpGet().getPort().getIntVal().intValue()); - assertEquals("http",probe.getHttpGet().getScheme()); + assertEquals("HTTP",probe.getHttpGet().getScheme()); assertNull(probe.getExec()); assertNull(probe.getTcpSocket()); } @@ -199,7 +199,7 @@ public void getTCPProbeWithHTTPURLAndPortTest() { assertEquals(null,probe.getHttpGet().getHttpHeaders()); assertEquals("/healthz",probe.getHttpGet().getPath()); assertEquals(8080,probe.getHttpGet().getPort().getIntVal().intValue()); - assertEquals("http",probe.getHttpGet().getScheme()); + assertEquals("HTTP",probe.getHttpGet().getScheme()); } @Test @@ -265,7 +265,7 @@ public void getTCPWithHTTPURLAndWithoutPort() { assertEquals(null,probe.getHttpGet().getHttpHeaders()); assertEquals("/healthz",probe.getHttpGet().getPath()); assertEquals(8080,probe.getHttpGet().getPort().getIntVal().intValue()); - assertEquals("http",probe.getHttpGet().getScheme()); + assertEquals("HTTP",probe.getHttpGet().getScheme()); } @Test @@ -293,4 +293,4 @@ public void getTCPProbeWithInvalidURLTest() { probe = probeHandler.getProbe(probeConfig); } -} \ No newline at end of file +} diff --git a/enricher/standard/src/main/java/io/fabric8/maven/enricher/standard/CustomProbeEnricher.java b/enricher/standard/src/main/java/io/fabric8/maven/enricher/standard/CustomProbeEnricher.java new file mode 100644 index 0000000000..234a975409 --- /dev/null +++ b/enricher/standard/src/main/java/io/fabric8/maven/enricher/standard/CustomProbeEnricher.java @@ -0,0 +1,60 @@ +/** + * Copyright 2016 Red Hat, Inc. + * + * Red Hat licenses this file to you under the Apache License, version + * 2.0 (the "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +package io.fabric8.maven.enricher.standard; + +import io.fabric8.kubernetes.api.builder.TypedVisitor; +import io.fabric8.kubernetes.api.model.ContainerBuilder; +import io.fabric8.kubernetes.api.model.KubernetesListBuilder; +import io.fabric8.kubernetes.api.model.Probe; +import io.fabric8.maven.core.config.ProbeConfig; +import io.fabric8.maven.core.config.ResourceConfig; +import io.fabric8.maven.core.handler.ProbeHandler; +import io.fabric8.maven.enricher.api.BaseEnricher; +import io.fabric8.maven.enricher.api.MavenEnricherContext; + +import java.util.NoSuchElementException; + +public class CustomProbeEnricher extends BaseEnricher { + public CustomProbeEnricher(MavenEnricherContext buildContext) { + super(buildContext, "fmp-customprobe"); + } + + @Override + public void addMissingResources(KubernetesListBuilder builder) { + builder.accept(new TypedVisitor() { + @Override + public void visit(ContainerBuilder containerBuilder) { + try { + ResourceConfig resourceConfig = getConfiguration().getResource().get(); + + containerBuilder.withReadinessProbe(getProbe(resourceConfig.getReadiness())); + containerBuilder.withLivenessProbe(getProbe(resourceConfig.getLiveness())); + } catch (NoSuchElementException ex) { + // In this case resource is not being configured for plugin, let's just ignore. + } + } + }); + } + + private Probe getProbe(ProbeConfig pc) { + ProbeHandler probeHandler = new ProbeHandler(); + if(pc != null) { + return probeHandler.getProbe(pc); + } + return null; + } +} diff --git a/enricher/standard/src/main/resources/META-INF/fabric8/enricher-default b/enricher/standard/src/main/resources/META-INF/fabric8/enricher-default index 15ce067323..3910acb0ac 100644 --- a/enricher/standard/src/main/resources/META-INF/fabric8/enricher-default +++ b/enricher/standard/src/main/resources/META-INF/fabric8/enricher-default @@ -73,3 +73,6 @@ io.fabric8.maven.enricher.standard.openshift.RouteEnricher # Add an "expose" label to every service (TODO: Combine this with a Route/Ingress enricher) io.fabric8.maven.enricher.standard.openshift.ExposeEnricher + +# Add custom liveliness/readiness probes to every container via XML config +io.fabric8.maven.enricher.standard.CustomProbeEnricher diff --git a/enricher/standard/src/test/java/io/fabric8/maven/enricher/standard/CustomProbeEnricherTest.java b/enricher/standard/src/test/java/io/fabric8/maven/enricher/standard/CustomProbeEnricherTest.java new file mode 100644 index 0000000000..30336ecffb --- /dev/null +++ b/enricher/standard/src/test/java/io/fabric8/maven/enricher/standard/CustomProbeEnricherTest.java @@ -0,0 +1,130 @@ +/** + * Copyright 2016 Red Hat, Inc. + * + * Red Hat licenses this file to you under the Apache License, version + * 2.0 (the "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing + * permissions and limitations under the License. + */ +package io.fabric8.maven.enricher.standard; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.jayway.jsonpath.matchers.JsonPathMatchers; +import io.fabric8.kubernetes.api.model.Container; +import io.fabric8.kubernetes.api.model.ContainerBuilder; +import io.fabric8.kubernetes.api.model.ContainerPortBuilder; +import io.fabric8.kubernetes.api.model.KubernetesList; +import io.fabric8.kubernetes.api.model.KubernetesListBuilder; +import io.fabric8.kubernetes.api.model.PodTemplateBuilder; +import io.fabric8.maven.core.config.ProbeConfig; +import io.fabric8.maven.core.config.ProcessorConfig; +import io.fabric8.maven.core.config.ResourceConfig; +import io.fabric8.maven.core.model.Configuration; +import io.fabric8.maven.core.util.ResourceUtil; +import io.fabric8.maven.docker.config.ImageConfiguration; +import io.fabric8.maven.enricher.api.MavenEnricherContext; +import mockit.Expectations; +import mockit.Mocked; +import org.hamcrest.Matchers; +import org.junit.Test; + +import java.util.Arrays; +import java.util.Collections; +import java.util.TreeMap; + +import static org.junit.Assert.assertThat; + +public class CustomProbeEnricherTest { + @Mocked + private MavenEnricherContext context; + + @Mocked + ImageConfiguration imageConfiguration; + + @Test + public void testReadinessProbeEnrichment() throws JsonProcessingException { + final TreeMap controllerConfig = new TreeMap(); + controllerConfig.put("type", "LoadBalancer"); + ResourceConfig resourceConfig = new ResourceConfig.Builder() + .withReadiness(new ProbeConfig.Builder().getUrl("http://192.168.42.20:8443/").build()) + .build(); + + setupExpectations(controllerConfig, resourceConfig); + + // Enrich + CustomProbeEnricher enricher = new CustomProbeEnricher(context); + KubernetesListBuilder builder = getPodTemplateList(); + enricher.addMissingResources(builder); + + // Validate that the generated resource contains + KubernetesList list = builder.build(); + + String json = ResourceUtil.toJson(list.getItems().get(0)); + assertThat(json, JsonPathMatchers.isJson()); + assertThat(json, JsonPathMatchers.hasJsonPath("$.template.spec.containers[0].readinessProbe.httpGet.host", Matchers.equalTo("192.168.42.20"))); + assertThat(json, JsonPathMatchers.hasJsonPath("$.template.spec.containers[0].readinessProbe.httpGet.port", Matchers.equalTo(8443))); + assertThat(json, JsonPathMatchers.hasJsonPath("$.template.spec.containers[0].readinessProbe.httpGet.path", Matchers.equalTo("/"))); + assertThat(json, JsonPathMatchers.hasJsonPath("$.template.spec.containers[0].readinessProbe.httpGet.scheme", Matchers.equalTo("HTTP"))); + } + + @Test + public void testLivenessProbeEnrichment() throws JsonProcessingException { + final TreeMap controllerConfig = new TreeMap(); + controllerConfig.put("type", "LoadBalancer"); + ResourceConfig resourceConfig = new ResourceConfig.Builder() + .withLiveness(new ProbeConfig.Builder().initialDelaySeconds(5).exec("cat /tmp/probe").build()) + .build(); + + setupExpectations(controllerConfig, resourceConfig); + + // Enrich + CustomProbeEnricher enricher = new CustomProbeEnricher(context); + KubernetesListBuilder builder = getPodTemplateList(); + enricher.addMissingResources(builder); + + // Validate that the generated resources contain desired enrichments + KubernetesList list = builder.build(); + + String json = ResourceUtil.toJson(list.getItems().get(0)); + assertThat(json, JsonPathMatchers.isJson()); + assertThat(json, JsonPathMatchers.hasJsonPath("$.template.spec.containers[0].livenessProbe.initialDelaySeconds", Matchers.equalTo(5))); + assertThat(json, JsonPathMatchers.hasJsonPath("$.template.spec.containers[0].livenessProbe.exec.command[0]", Matchers.equalTo("cat"))); + } + + private KubernetesListBuilder getPodTemplateList() { + Container container = new ContainerBuilder() + .withName("test-port-enricher") + .withImage("test-image") + .withPorts(new ContainerPortBuilder().withContainerPort(80).withProtocol("TCP").build()) + .build(); + PodTemplateBuilder ptb = new PodTemplateBuilder() + .withNewMetadata().withName("test-pod") + .endMetadata() + .withNewTemplate() + .withNewSpec() + .withContainers(container) + .endSpec() + .endTemplate(); + return new KubernetesListBuilder().addToPodTemplateItems(ptb.build()); + } + + private void setupExpectations(final TreeMap controllerConfig, ResourceConfig resourceConfig) { + new Expectations() {{ + + Configuration config = new Configuration.Builder() + .images(Arrays.asList(imageConfiguration)) + .processorConfig(new ProcessorConfig(null, null, Collections.singletonMap("fmp-customprobe", controllerConfig))) + .resource(resourceConfig).build(); + + context.getConfiguration(); + result = config; + }}; + } +} diff --git a/plugin/src/main/resources/META-INF/fabric8/profiles-default.yml b/plugin/src/main/resources/META-INF/fabric8/profiles-default.yml index 808f146c6b..aa2c9f8086 100644 --- a/plugin/src/main/resources/META-INF/fabric8/profiles-default.yml +++ b/plugin/src/main/resources/META-INF/fabric8/profiles-default.yml @@ -38,6 +38,7 @@ - fmp-remove-build-annotations - fmp-volume-permission - fmp-configmap-file + - fmp-customprobe # Route exposure - fmp-openshift-service-expose