diff --git a/pom.xml b/pom.xml index b99fb7d..1602ec3 100644 --- a/pom.xml +++ b/pom.xml @@ -64,7 +64,7 @@ entando-k8s ${github.organization}_${project.artifactId} - 7.3.1-ENG-5347-PR-152 + 7.3.1-ENG-5487-PR-158 true in-process smoke diff --git a/src/main/java/org/entando/kubernetes/controller/app/AppBuilderDeployableContainer.java b/src/main/java/org/entando/kubernetes/controller/app/AppBuilderDeployableContainer.java index a1153c2..71b0136 100644 --- a/src/main/java/org/entando/kubernetes/controller/app/AppBuilderDeployableContainer.java +++ b/src/main/java/org/entando/kubernetes/controller/app/AppBuilderDeployableContainer.java @@ -74,16 +74,32 @@ public Optional getHealthCheckPath() { return Optional.of("/app-builder/favicon-entando.png"); } + /** + * Provides environment variables generated from specific CR specs properties and/or contextual information. + */ @Override public List getEnvironmentVariables() { - List vars = new ArrayList<>(); - vars.add(new EnvVar("DOMAIN", EntandoAppHelper.getNormalizedDeAppWebContextPath(entandoApp), - null)); - return vars; + return EntandoAppHelper.subtractEnvironmentVariables( + getBaseEnvironmentVariables(), + entandoApp.getSpec().getEnvironmentVariablesAppBuilder() + ); } + /** + * Provides environment variables from the common and module-specific "environmentVariables" properties of the CR. + */ @Override public List getEnvironmentVariableOverrides() { - return entandoApp.getSpec().getEnvironmentVariables(); + return EntandoAppHelper.combineEnvironmentVariables( + entandoApp.getSpec().getEnvironmentVariables(), + entandoApp.getSpec().getEnvironmentVariablesAppBuilder() + ); + } + + private List getBaseEnvironmentVariables() { + List vars = new ArrayList<>(); + vars.add(new EnvVar("DOMAIN", EntandoAppHelper.getNormalizedDeAppWebContextPath(entandoApp), + null)); + return vars; } } diff --git a/src/main/java/org/entando/kubernetes/controller/app/ComponentManagerDeployableContainer.java b/src/main/java/org/entando/kubernetes/controller/app/ComponentManagerDeployableContainer.java index 3f5396d..56d7b59 100644 --- a/src/main/java/org/entando/kubernetes/controller/app/ComponentManagerDeployableContainer.java +++ b/src/main/java/org/entando/kubernetes/controller/app/ComponentManagerDeployableContainer.java @@ -103,8 +103,7 @@ public int getPrimaryPort() { return 8083; } - @Override - public List getEnvironmentVariables() { + private List getBaseEnvironmentVariables() { List vars = new ArrayList<>(); String entandoUrl = EntandoAppDeployableContainer.determineEntandoServiceBaseUrl(this.entandoApp); vars.add(new EnvVar("ENTANDO_APP_NAME", entandoApp.getMetadata().getName(), null)); @@ -197,9 +196,26 @@ public String getVolumeMountPath() { return "/entando-data"; } + /** + * Provides environment variables generated from specific CR specs properties and/or contextual information. + */ + @Override + public List getEnvironmentVariables() { + return EntandoAppHelper.subtractEnvironmentVariables( + getBaseEnvironmentVariables(), + entandoApp.getSpec().getEnvironmentVariablesComponentManager() + ); + } + + /** + * Provides environment variables from the common and module-specific "environmentVariables" properties of the CR. + */ @Override public List getEnvironmentVariableOverrides() { - return entandoApp.getSpec().getEnvironmentVariables(); + return EntandoAppHelper.combineEnvironmentVariables( + entandoApp.getSpec().getEnvironmentVariables(), + entandoApp.getSpec().getEnvironmentVariablesComponentManager() + ); } } diff --git a/src/main/java/org/entando/kubernetes/controller/app/CustomConfigFromOperator.java b/src/main/java/org/entando/kubernetes/controller/app/CustomConfigFromOperator.java index e68215a..21e6317 100644 --- a/src/main/java/org/entando/kubernetes/controller/app/CustomConfigFromOperator.java +++ b/src/main/java/org/entando/kubernetes/controller/app/CustomConfigFromOperator.java @@ -1,17 +1,10 @@ package org.entando.kubernetes.controller.app; -import io.fabric8.kubernetes.api.model.EnvVar; -import java.util.List; - public class CustomConfigFromOperator { private String ecrPostInitConfiguration; private boolean tlsEnabled; - private List environmentVariablesAppEngine; - private List environmentVariablesComponentManager; - private List environmentVariablesSso; - public boolean isTlsEnabled() { return tlsEnabled; } @@ -28,27 +21,4 @@ public void setEcrPostInitConfiguration(String ecrPostInitConfiguration) { this.ecrPostInitConfiguration = ecrPostInitConfiguration; } - public List getEnvironmentVariablesAppEngine() { - return environmentVariablesAppEngine; - } - - public void setEnvironmentVariablesAppEngine(List environmentVariablesAppEngine) { - this.environmentVariablesAppEngine = environmentVariablesAppEngine; - } - - public List getEnvironmentVariablesComponentManager() { - return environmentVariablesComponentManager; - } - - public void setEnvironmentVariablesComponentManager(List environmentVariablesComponentManager) { - this.environmentVariablesComponentManager = environmentVariablesComponentManager; - } - - public List getEnvironmentVariablesSso() { - return environmentVariablesSso; - } - - public void setEnvironmentVariablesSso(List environmentVariablesSso) { - this.environmentVariablesSso = environmentVariablesSso; - } } diff --git a/src/main/java/org/entando/kubernetes/controller/app/EntandoAppController.java b/src/main/java/org/entando/kubernetes/controller/app/EntandoAppController.java index 79abaf5..f147591 100644 --- a/src/main/java/org/entando/kubernetes/controller/app/EntandoAppController.java +++ b/src/main/java/org/entando/kubernetes/controller/app/EntandoAppController.java @@ -45,7 +45,6 @@ import org.entando.kubernetes.controller.spi.common.EntandoOperatorSpiConfig; import org.entando.kubernetes.controller.spi.common.NameUtils; import org.entando.kubernetes.controller.spi.common.ResourceUtils; -import org.entando.kubernetes.controller.spi.container.DeployableContainer; import org.entando.kubernetes.controller.spi.container.ProvidedDatabaseCapability; import org.entando.kubernetes.controller.spi.container.ProvidedSsoCapability; import org.entando.kubernetes.controller.spi.deployable.IngressingDeployable; @@ -116,7 +115,7 @@ public void run() { final SsoConnectionInfo ssoConnectionInfo = provideSso(); // SETTINGS - final CustomConfigFromOperator customConfig = readEntandoAppCustomConfig(); + final CustomConfigFromOperator customConfig = readOperatorCustomConfig(); final int timeoutForDbAware = calculateDbAwareTimeout(); final int timeoutForNonDbAware = EntandoOperatorSpiConfig.getPodReadinessTimeoutSeconds(); @@ -124,7 +123,9 @@ public void run() { deployAppEngine(ssoConnectionInfo, dbConnectionInfo, customConfig, timeoutForDbAware); deployAppBuilder(timeoutForNonDbAware); deployComponentManager(ssoConnectionInfo, dbConnectionInfo, customConfig, timeoutForDbAware); - waitCompletionAndFinalized(timeoutForDbAware, timeoutForNonDbAware); + + // FINALIZE + waitCompletionAndFinalize(timeoutForDbAware, timeoutForNonDbAware); } catch (Exception e) { attachControllerFailure(e, EntandoAppController.class, NameUtils.MAIN_QUALIFIER); } @@ -134,7 +135,7 @@ public void run() { }); } - private void waitCompletionAndFinalized(int timeoutForDbAware, int timeoutForNonDbAware) throws InterruptedException, TimeoutException { + private void waitCompletionAndFinalize(int timeoutForDbAware, int timeoutForNonDbAware) throws InterruptedException, TimeoutException { executor.shutdown(); final int totalTimeout = timeoutForDbAware * 2 + timeoutForNonDbAware; if (!executor.awaitTermination(totalTimeout, TimeUnit.SECONDS)) { @@ -148,14 +149,19 @@ private void deployAppBuilder(int timeoutForNonDbAware) { queueDeployable(new AppBuilderDeployable(entandoApp.get()), timeoutForNonDbAware); } - private void deployComponentManager(SsoConnectionInfo ssoConnectionInfo, DatabaseConnectionInfo dbConnectionInfo, - CustomConfigFromOperator customConfig, int timeoutForDbAware) { - EntandoK8SService k8sService = new EntandoK8SService( - k8sClientForControllers.loadControllerService(EntandoAppController.ENTANDO_K8S_SERVICE)); - queueDeployable( - new ComponentManagerDeployable(entandoApp.get(), ssoConnectionInfo, k8sService, dbConnectionInfo, - simpleK8SClient.secrets(), customConfig), - timeoutForDbAware); + private void deployComponentManager( + SsoConnectionInfo ssoConnectionInfo, DatabaseConnectionInfo dbConnectionInfo, + CustomConfigFromOperator customConfig, int timeoutForDbAware + ) { + var k8sService = new EntandoK8SService( + k8sClientForControllers.loadControllerService(EntandoAppController.ENTANDO_K8S_SERVICE) + ); + var deployable = new ComponentManagerDeployable( + entandoApp.get(), ssoConnectionInfo, k8sService, dbConnectionInfo, + simpleK8SClient.secrets(), customConfig + ); + + queueDeployable(deployable, timeoutForDbAware); } private void deployAppEngine( @@ -170,7 +176,7 @@ private void deployAppEngine( queueDeployable(deployable, timeoutForDbAware); } - private CustomConfigFromOperator readEntandoAppCustomConfig() { + private CustomConfigFromOperator readOperatorCustomConfig() { CustomConfigFromOperator customConfig = new CustomConfigFromOperator(); customConfig.setEcrPostInitConfiguration(lookupProperty(KEY_ENTANDO_ECR_POSTINIT_CONFIGURATION).orElse("")); customConfig.setTlsEnabled(StringUtils.isNotBlank( diff --git a/src/main/java/org/entando/kubernetes/controller/app/EntandoAppDeployableContainer.java b/src/main/java/org/entando/kubernetes/controller/app/EntandoAppDeployableContainer.java index 5d30c6d..2d280e5 100644 --- a/src/main/java/org/entando/kubernetes/controller/app/EntandoAppDeployableContainer.java +++ b/src/main/java/org/entando/kubernetes/controller/app/EntandoAppDeployableContainer.java @@ -132,13 +132,6 @@ public String getNameQualifier() { return NameUtils.DEFAULT_SERVER_QUALIFIER; } - @Override - public List getEnvironmentVariables() { - var vars = getBaseEnvironmentVariables(); - vars.addAll(customConfig.getEnvironmentVariablesAppEngine()); - return vars; - } - @Override public int getPrimaryPort() { return PORT; @@ -229,9 +222,26 @@ public Optional getResourceRequirementsOverride() { return entandoApp.getSpec().getResourceRequirements(); } + /** + * Provides environment variables generated from specific CR specs properties and/or contextual information. + */ + @Override + public List getEnvironmentVariables() { + return EntandoAppHelper.subtractEnvironmentVariables( + getBaseEnvironmentVariables(), + entandoApp.getSpec().getEnvironmentVariablesAppEngine() + ); + } + + /** + * Provides environment variables from the common and module-specific "environmentVariables" properties of the CR. + */ @Override public List getEnvironmentVariableOverrides() { - return entandoApp.getSpec().getEnvironmentVariables(); + return EntandoAppHelper.combineEnvironmentVariables( + entandoApp.getSpec().getEnvironmentVariables(), + entandoApp.getSpec().getEnvironmentVariablesAppEngine() + ); } @Override @@ -273,7 +283,7 @@ public List getCommand() { @Override public List getEnvironmentVariables() { - return entandoAppDeployableContainer.getBaseEnvironmentVariables(); + return entandoAppDeployableContainer.getDatabaseConnectionVariables(); } } diff --git a/src/main/java/org/entando/kubernetes/controller/app/EntandoAppHelper.java b/src/main/java/org/entando/kubernetes/controller/app/EntandoAppHelper.java index 06f4d2d..68f813a 100644 --- a/src/main/java/org/entando/kubernetes/controller/app/EntandoAppHelper.java +++ b/src/main/java/org/entando/kubernetes/controller/app/EntandoAppHelper.java @@ -16,6 +16,11 @@ package org.entando.kubernetes.controller.app; +import io.fabric8.kubernetes.api.model.EnvVar; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; import org.apache.commons.lang3.StringUtils; import org.entando.kubernetes.controller.spi.common.EntandoOperatorComplianceMode; import org.entando.kubernetes.controller.spi.common.EntandoOperatorSpiConfig; @@ -66,4 +71,28 @@ private static String getRootIfBlankOrValue(String path) { return StringUtils.isBlank(path) ? "/" : path; } + public static List combineEnvironmentVariables(List a, List b) { + if (a == null) { + a = new ArrayList<>(); + } + if (b == null) { + b = new ArrayList<>(); + } + + return Stream.concat(a.stream(), b.stream()).distinct().collect(Collectors.toList()); + } + + public static List subtractEnvironmentVariables(List a, List b) { + if (a == null) { + return new ArrayList<>(); + } + + if (b == null || b.isEmpty()) { + return new ArrayList<>(a); + } + + return a.stream() + .filter(ea -> b.stream().noneMatch(eb -> eb.getName().equals(ea.getName()))) + .collect(Collectors.toList()); + } } diff --git a/src/test/java/org/entando/kubernetes/controller/app/DeployedEntandoAppServerTest.java b/src/test/java/org/entando/kubernetes/controller/app/DeployedEntandoAppServerTest.java index 8aeb837..cad2d70 100644 --- a/src/test/java/org/entando/kubernetes/controller/app/DeployedEntandoAppServerTest.java +++ b/src/test/java/org/entando/kubernetes/controller/app/DeployedEntandoAppServerTest.java @@ -26,6 +26,7 @@ import static org.mockito.Mockito.verify; import io.fabric8.kubernetes.api.model.Container; +import io.fabric8.kubernetes.api.model.EnvVar; import io.fabric8.kubernetes.api.model.LimitRange; import io.fabric8.kubernetes.api.model.LimitRangeBuilder; import io.fabric8.kubernetes.api.model.OwnerReference; @@ -42,6 +43,7 @@ import io.qameta.allure.Description; import io.qameta.allure.Feature; import io.qameta.allure.Issue; +import java.util.ArrayList; import java.util.HashMap; import java.util.Map; import org.entando.kubernetes.controller.spi.common.DbmsVendorConfig; @@ -93,8 +95,13 @@ class DeployedEntandoAppServerTest extends EntandoAppTestBase implements Variabl void shouldDeployEntandoEapImageWithDefaultValues() { initSecretsMock(); - - EntandoAppSpec spec = new EntandoAppSpecBuilder().withStandardServerImage(JeeServer.WILDFLY).build(); + + EntandoAppSpec spec = new EntandoAppSpecBuilder() + .withStandardServerImage(JeeServer.WILDFLY) + .withEnvironmentVariables(mkTestEnvVars(false)) + .withEnvironmentVariablesAppEngine(mkTestEnvVars(true)) + .withEnvironmentVariablesComponentManager(mkTestEnvVars(true)) + .build(); this.app = new EntandoAppBuilder() .withNewMetadata() .withName(MY_APP) @@ -161,6 +168,8 @@ void shouldDeployEntandoEapImageWithDefaultValues() { mainDbPreprationJob); verifyEntandoDbVariables(entandoApp, portDbSecret, "PORTDB", populator); verifyEntandoDbVariables(entandoApp, servDbSecret, "SERVDB", populator); + + verifyEntandoModulesVariables(entandoApp); }); step("And a Kubernetes Deployment was created reflecting the requirements of the Entando Eap container:", @@ -391,6 +400,17 @@ void shouldDeployEntandoEapImageWithDefaultValues() { }); } + private static ArrayList mkTestEnvVars(boolean overwrite) { + var res = new ArrayList(); + if (!overwrite) { + res.add(new EnvVar("TEST_VAR", "TEST_VALUE", null)); + res.add(new EnvVar("TEST_VAR2", "TEST_VALUE2", null)); + } else { + res.add(new EnvVar("TEST_VAR2", "TEST_VALUE2_OV", null)); + } + return res; + } + private LimitRange getLimitRange(EntandoApp entandoApp) { final OwnerReference ownerRef = new OwnerReferenceBuilder() .withApiVersion("entando.org/v1") @@ -657,4 +677,27 @@ private void verifyDbJobAdminVariables(EntandoApp entandoApp, Container initCont verifyDbJobAdminCredentials("default-postgresql-dbms-in-namespace-admin-secret", initContainer); } + private void verifyEntandoModulesVariables(EntandoApp entandoApp) { + String baseName = entandoApp.getMetadata().getName(); + var ae = client.deployments().loadDeployment(entandoApp, baseName + "-" + NameUtils.DEFAULT_DEPLOYMENT_SUFFIX); + var ab = client.deployments().loadDeployment(entandoApp, baseName + "-ab-" + NameUtils.DEFAULT_DEPLOYMENT_SUFFIX); + var cm = client.deployments().loadDeployment(entandoApp, baseName + "-cm-" + NameUtils.DEFAULT_DEPLOYMENT_SUFFIX); + + step("and module specific variables were applied to AppEngine", () -> { + var c = ae.getSpec().getTemplate().getSpec().getContainers().get(0); + assertThat(theVariableNamed("TEST_VAR").on(c)).isEqualTo("TEST_VALUE"); + assertThat(theVariableNamed("TEST_VAR2").on(c)).isEqualTo("TEST_VALUE2_OV"); + }); + step("and module specific variables were applied to ComponentManager", () -> { + var c = cm.getSpec().getTemplate().getSpec().getContainers().get(0); + assertThat(theVariableNamed("TEST_VAR").on(c)).isEqualTo("TEST_VALUE"); + assertThat(theVariableNamed("TEST_VAR2").on(c)).isEqualTo("TEST_VALUE2_OV"); + }); + step("and module specific variables were applied to AppBuilder", () -> { + var c = ab.getSpec().getTemplate().getSpec().getContainers().get(0); + assertThat(theVariableNamed("TEST_VAR").on(c)).isEqualTo("TEST_VALUE"); + assertThat(theVariableNamed("TEST_VAR2").on(c)).isEqualTo("TEST_VALUE2"); + }); + } + } diff --git a/src/test/java/org/entando/kubernetes/controller/app/EntandoAppHelperTest.java b/src/test/java/org/entando/kubernetes/controller/app/EntandoAppHelperTest.java new file mode 100644 index 0000000..1ec723e --- /dev/null +++ b/src/test/java/org/entando/kubernetes/controller/app/EntandoAppHelperTest.java @@ -0,0 +1,138 @@ +/* + * + * Copyright 2015-Present Entando Inc. (http://www.entando.com) All rights reserved. + * + * This library is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 2.1 of the License, or (at your option) + * any later version. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more + * details. + * + */ + +package org.entando.kubernetes.controller.app; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import io.fabric8.kubernetes.api.model.EnvVar; +import java.util.ArrayList; +import java.util.List; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +class EntandoAppHelperTest { + + @Test + void testCombineEnvironmentVariables() { + // Both empty + List a = new ArrayList<>(); + List b = new ArrayList<>(); + List result = EntandoAppHelper.combineEnvironmentVariables(a, b); + assertTrue(result.isEmpty()); + + // "a" is empty, "b" has one element + a = new ArrayList<>(); + b = new ArrayList<>(); + b.add(new EnvVar("VAR1", "value1", null)); + result = EntandoAppHelper.combineEnvironmentVariables(a, b); + assertEquals(1, result.size()); + assertEquals("VAR1", result.get(0).getName()); + assertEquals("value1", result.get(0).getValue()); + + // "a" is null, "b" has one element + a = null; + b = new ArrayList<>(); + b.add(new EnvVar("VAR1", "value1", null)); + result = EntandoAppHelper.combineEnvironmentVariables(a, b); + assertEquals(1, result.size()); + assertEquals("VAR1", result.get(0).getName()); + assertEquals("value1", result.get(0).getValue()); + + // "a" has one element, "b" is null + a = new ArrayList<>(); + a.add(new EnvVar("VAR2", "value2", null)); + b = null; + result = EntandoAppHelper.combineEnvironmentVariables(a, b); + assertEquals(1, result.size()); + assertEquals("VAR2", result.get(0).getName()); + assertEquals("value2", result.get(0).getValue()); + + // Both lists have different elements + a = new ArrayList<>(); + a.add(new EnvVar("VAR3", "value3", null)); + b = new ArrayList<>(); + b.add(new EnvVar("VAR4", "value4", null)); + result = EntandoAppHelper.combineEnvironmentVariables(a, b); + assertEquals(2, result.size()); + assertTrue(result.contains(new EnvVar("VAR3", "value3", null))); + assertTrue(result.contains(new EnvVar("VAR4", "value4", null))); + + // Both lists have the same elements + a = new ArrayList<>(); + a.add(new EnvVar("VAR5", "value5", null)); + b = new ArrayList<>(); + b.add(new EnvVar("VAR5", "value5", null)); + result = EntandoAppHelper.combineEnvironmentVariables(a, b); + assertEquals(1, result.size()); + assertEquals("VAR5", result.get(0).getName()); + assertEquals("value5", result.get(0).getValue()); + } + + @Test + void testSubtractEnvironmentVariables() { + // With no variable in common + List a = new ArrayList<>(); + a.add(new EnvVar("A", "ValueA", null)); + a.add(new EnvVar("B", "ValueB", null)); + + List b = new ArrayList<>(); + b.add(new EnvVar("C", "ValueC", null)); + b.add(new EnvVar("D", "ValueD", null)); + + List result = EntandoAppHelper.subtractEnvironmentVariables(a, b); + + assertEquals(a.size(), result.size()); + assertEquals(a, result); + + // with variables in common + a = new ArrayList<>(); + a.add(new EnvVar("A", "ValueA", null)); + a.add(new EnvVar("B", "ValueB", null)); + + b = new ArrayList<>(); + b.add(new EnvVar("A", "ValueA", null)); + b.add(new EnvVar("C", "ValueC", null)); + + List expected = new ArrayList<>(); + expected.add(new EnvVar("B", "ValueB", null)); + + result = EntandoAppHelper.subtractEnvironmentVariables(a, b); + + assertEquals(expected.size(), result.size()); + assertEquals(expected, result); + + // "b" is empty + a = new ArrayList<>(); + a.add(new EnvVar("A", "ValueA", null)); + a.add(new EnvVar("B", "ValueB", null)); + + result = EntandoAppHelper.subtractEnvironmentVariables(a, new ArrayList<>()); + + assertEquals(a.size(), result.size()); + assertEquals(a, result); + + // "a" is null and "b" is not empty + a = null; + b = new ArrayList<>(); + b.add(new EnvVar("B", "ValueB", null)); + + result = EntandoAppHelper.subtractEnvironmentVariables(a, b); + + assertEquals(0, result.size()); + } +} \ No newline at end of file