From 3c122dab3a33a76ef6c4e334d62911081bbeffee Mon Sep 17 00:00:00 2001 From: Fabio Burzigotti Date: Thu, 9 Nov 2023 11:13:24 +0100 Subject: [PATCH 1/2] [issue 105] - Adding the RHSSO Template provisioner --- .ci/openshift-ci/build-root/e2e-test-prod.sh | 1 + .../OpenShiftProvisionerTestBase.java | 55 ++++++ .../openshift/ProvisionerCleanupTestCase.java | 4 +- .../openshift/RhSsoTemplateTestCase.java | 49 +++++ .../intersmash/tools/IntersmashConfig.java | 7 +- tools/intersmash-tools-provisioners/README.md | 1 + .../RhSsoTemplateOpenShiftApplication.java | 63 +++++++ .../openshift/template/RhSsoTemplate.java | 46 +++++ .../RhSsoTemplateOpenShiftProvisioner.java | 174 ++++++++++++++++++ ...soTemplateOpenShiftProvisionerFactory.java | 18 ++ .../template/RhSsoTemplateProvisioner.java | 78 ++++++++ .../util/keycloak/KeycloakAdminClient.java | 1 + ...ersmash.tools.provision.ProvisionerFactory | 1 + .../provision/ProvisionerManagerTestCase.java | 18 ++ 14 files changed, 514 insertions(+), 2 deletions(-) create mode 100644 testsuite/src/test/java/org/jboss/intersmash/testsuite/provision/openshift/RhSsoTemplateTestCase.java create mode 100644 tools/intersmash-tools-provisioners/src/main/java/org/jboss/intersmash/tools/application/openshift/RhSsoTemplateOpenShiftApplication.java create mode 100644 tools/intersmash-tools-provisioners/src/main/java/org/jboss/intersmash/tools/application/openshift/template/RhSsoTemplate.java create mode 100644 tools/intersmash-tools-provisioners/src/main/java/org/jboss/intersmash/tools/provision/openshift/RhSsoTemplateOpenShiftProvisioner.java create mode 100644 tools/intersmash-tools-provisioners/src/main/java/org/jboss/intersmash/tools/provision/openshift/RhSsoTemplateOpenShiftProvisionerFactory.java create mode 100644 tools/intersmash-tools-provisioners/src/main/java/org/jboss/intersmash/tools/provision/openshift/template/RhSsoTemplateProvisioner.java diff --git a/.ci/openshift-ci/build-root/e2e-test-prod.sh b/.ci/openshift-ci/build-root/e2e-test-prod.sh index 4241c50fb..6b29b3f65 100644 --- a/.ci/openshift-ci/build-root/e2e-test-prod.sh +++ b/.ci/openshift-ci/build-root/e2e-test-prod.sh @@ -118,6 +118,7 @@ mvn test -Dmaven.repo.local=./local-repo-prod -pl testsuite/ -Pts.prod \ -Dintersmash.rhsso.image=registry.redhat.io/rh-sso-7/sso76-openshift-rhel8:latest \ -Dintersmash.rhsso.operators.catalog_source=redhat-operators \ -Dintersmash.rhsso.operators.package_manifest=rhsso-operator \ + -Dintersmash.rhsso.templates=https://raw.githubusercontent.com/jboss-container-images/redhat-sso-7-openshift-image/sso76-dev/templates/ \ -Dintersmash.infinispan.image=registry.redhat.io/jboss-datagrid-7/datagrid73-openshift:latest \ -Dintersmash.infinispan.operators.catalog_source=redhat-operators \ -Dintersmash.infinispan.operators.package_manifest=datagrid \ diff --git a/testsuite/src/test/java/org/jboss/intersmash/testsuite/provision/openshift/OpenShiftProvisionerTestBase.java b/testsuite/src/test/java/org/jboss/intersmash/testsuite/provision/openshift/OpenShiftProvisionerTestBase.java index f4d1e26c1..0a6f513a6 100644 --- a/testsuite/src/test/java/org/jboss/intersmash/testsuite/provision/openshift/OpenShiftProvisionerTestBase.java +++ b/testsuite/src/test/java/org/jboss/intersmash/testsuite/provision/openshift/OpenShiftProvisionerTestBase.java @@ -15,6 +15,8 @@ */ package org.jboss.intersmash.testsuite.provision.openshift; +import java.io.IOException; +import java.io.InputStream; import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; @@ -38,12 +40,15 @@ import org.jboss.intersmash.tools.application.openshift.KafkaOperatorApplication; import org.jboss.intersmash.tools.application.openshift.MysqlImageOpenShiftApplication; import org.jboss.intersmash.tools.application.openshift.PostgreSQLImageOpenShiftApplication; +import org.jboss.intersmash.tools.application.openshift.RhSsoTemplateOpenShiftApplication; import org.jboss.intersmash.tools.application.openshift.WildflyImageOpenShiftApplication; import org.jboss.intersmash.tools.application.openshift.input.BinarySource; import org.jboss.intersmash.tools.application.openshift.input.BuildInput; import org.jboss.intersmash.tools.application.openshift.input.BuildInputBuilder; import org.jboss.intersmash.tools.application.openshift.template.Eap7Template; import org.jboss.intersmash.tools.util.openshift.WildflyOpenShiftUtils; +import org.jboss.intersmash.tools.application.openshift.template.RhSsoTemplate; +import org.jboss.intersmash.tools.util.ProcessKeystoreGenerator; import org.jboss.intersmash.tools.util.wildfly.Eap7CliScriptBuilder; import cz.xtf.builder.builders.SecretBuilder; @@ -83,6 +88,56 @@ public class OpenShiftProvisionerTestBase { static final String EAP7_TEST_APP_REPO = "https://github.com/openshift/openshift-jee-sample.git"; static final String EAP7_TEST_APP_REF = "master"; + static RhSsoTemplateOpenShiftApplication getHttpsRhSso() { + return new RhSsoTemplateOpenShiftApplication() { + private final String secureAppHostname = "secure-" + getOpenShiftHostName(); + private final Path keystore = ProcessKeystoreGenerator.generateKeystore(secureAppHostname); + private final String jceksFileName = "jgroups.jceks"; + private final Path truststore = ProcessKeystoreGenerator.getTruststore(); + + @Override + public String getName() { + return "sso-app"; + } + + @Override + public Map getParameters() { + Map parameters = new HashMap<>(); + + parameters.put("APPLICATION_NAME", getName()); + parameters.put("SSO_REALM", "eap-realm"); + parameters.put("SSO_SERVICE_USERNAME", "client"); + parameters.put("SSO_SERVICE_PASSWORD", "creator"); + parameters.put("SSO_ADMIN_USERNAME", "admin"); + parameters.put("SSO_ADMIN_PASSWORD", "admin"); + parameters.put("JGROUPS_CLUSTER_PASSWORD", "xpaasQEpassword"); + parameters.put("IMAGE_STREAM_NAMESPACE", OpenShiftConfig.namespace()); + + return Collections.unmodifiableMap(parameters); + } + + @Override + public List getSecrets() { + SecretBuilder sb; + try (InputStream is = getClass().getClassLoader().getResourceAsStream("certs/jgroups.jceks")) { + sb = new SecretBuilder(getName() + "-secret") + .addData(keystore.getFileName().toString(), keystore) + .addData(jceksFileName, is) + .addData(truststore.getFileName().toString(), truststore); + } catch (IOException e) { + throw new RuntimeException(e); + } + + return Collections.singletonList(sb.build()); + } + + @Override + public RhSsoTemplate getTemplate() { + return RhSsoTemplate.X509_HTTPS; + } + }; + } + static Eap7TemplateOpenShiftApplication getEap7OpenShiftTemplateApplication() { return new Eap7TemplateOpenShiftApplication() { diff --git a/testsuite/src/test/java/org/jboss/intersmash/testsuite/provision/openshift/ProvisionerCleanupTestCase.java b/testsuite/src/test/java/org/jboss/intersmash/testsuite/provision/openshift/ProvisionerCleanupTestCase.java index dc9911bb4..dd13ebd09 100644 --- a/testsuite/src/test/java/org/jboss/intersmash/testsuite/provision/openshift/ProvisionerCleanupTestCase.java +++ b/testsuite/src/test/java/org/jboss/intersmash/testsuite/provision/openshift/ProvisionerCleanupTestCase.java @@ -23,6 +23,7 @@ import org.jboss.intersmash.tools.provision.openshift.MysqlImageOpenShiftProvisioner; import org.jboss.intersmash.tools.provision.openshift.OpenShiftProvisioner; import org.jboss.intersmash.tools.provision.openshift.PostgreSQLImageOpenShiftProvisioner; +import org.jboss.intersmash.tools.provision.openshift.RhSsoTemplateOpenShiftProvisioner; import org.jboss.intersmash.tools.provision.openshift.WildflyBootableJarImageOpenShiftProvisioner; import org.jboss.intersmash.tools.provision.openshift.WildflyImageOpenShiftProvisioner; import org.junit.jupiter.api.Assertions; @@ -51,7 +52,8 @@ private static Stream provisionerProvider() { } else if (IntersmashTestsuiteProperties.isProductizedTestExecutionProfileEnabled()) { return Stream.of( new WildflyImageOpenShiftProvisioner( - OpenShiftProvisionerTestBase.getWildflyOpenShiftLocalBinaryTargetServerApplication())); + OpenShiftProvisionerTestBase.getWildflyOpenShiftLocalBinaryTargetServerApplication()), + new RhSsoTemplateOpenShiftProvisioner(OpenShiftProvisionerTestBase.getHttpsRhSso())); } else { throw new IllegalStateException( String.format("Unknown Intersmash test suite execution profile: %s", diff --git a/testsuite/src/test/java/org/jboss/intersmash/testsuite/provision/openshift/RhSsoTemplateTestCase.java b/testsuite/src/test/java/org/jboss/intersmash/testsuite/provision/openshift/RhSsoTemplateTestCase.java new file mode 100644 index 000000000..2fc4ab60a --- /dev/null +++ b/testsuite/src/test/java/org/jboss/intersmash/testsuite/provision/openshift/RhSsoTemplateTestCase.java @@ -0,0 +1,49 @@ +package org.jboss.intersmash.testsuite.provision.openshift; + +import org.assertj.core.api.Assertions; +import org.jboss.intersmash.testsuite.junit5.categories.NotForCommunityExecutionProfile; +import org.jboss.intersmash.tools.application.openshift.RhSsoTemplateOpenShiftApplication; +import org.jboss.intersmash.tools.provision.openshift.RhSsoTemplateOpenShiftProvisioner; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import cz.xtf.core.openshift.OpenShift; +import cz.xtf.core.openshift.OpenShifts; +import cz.xtf.junit5.annotations.CleanBeforeAll; + +@CleanBeforeAll +@NotForCommunityExecutionProfile +public class RhSsoTemplateTestCase { + private static final OpenShift openShift = OpenShifts.master(); + private static final RhSsoTemplateOpenShiftApplication application = OpenShiftProvisionerTestBase.getHttpsRhSso(); + private static final RhSsoTemplateOpenShiftProvisioner provisioner = new RhSsoTemplateOpenShiftProvisioner(application); + + @BeforeAll + public static void deploy() { + provisioner.preDeploy(); + provisioner.deploy(); + } + + @AfterAll + public static void undeploy() { + provisioner.undeploy(); + provisioner.postUndeploy(); + } + + @Test + public void scale() { + provisioner.scale(1, true); + openShift.waiters().areExactlyNPodsReady(1, application.getName()).waitFor(); + provisioner.scale(2, true); + openShift.waiters().areExactlyNPodsReady(2, application.getName()).waitFor(); + } + + @Test + public void pods() { + provisioner.scale(2, true); + Assertions.assertThat(provisioner.getPods().size()).isEqualTo(2); + provisioner.scale(3, true); + Assertions.assertThat(provisioner.getPods().size()).isEqualTo(3); + } +} diff --git a/tools/intersmash-tools-core/src/main/java/org/jboss/intersmash/tools/IntersmashConfig.java b/tools/intersmash-tools-core/src/main/java/org/jboss/intersmash/tools/IntersmashConfig.java index f5a1717b0..3f9a0030f 100644 --- a/tools/intersmash-tools-core/src/main/java/org/jboss/intersmash/tools/IntersmashConfig.java +++ b/tools/intersmash-tools-core/src/main/java/org/jboss/intersmash/tools/IntersmashConfig.java @@ -111,8 +111,9 @@ public class IntersmashConfig { private static final String INFINISPAN_IMAGE_URL = "intersmash.infinispan.image"; // KEYCLOAK/RHSSO - private static final String RHSSO_IMAGE_URL = "intersmash.rhsso.image"; private static final String KEYCLOAK_IMAGE_URL = "intersmash.keycloak.image"; + private static final String RHSSO_IMAGE_URL = "intersmash.rhsso.image"; + private static final String RHSSO_TEMPLATES = "intersmash.rhsso.templates"; // ACTIVEMQ private static final String ACTIVEMQ_IMAGE_URL = "intersmash.activemq.image"; @@ -319,6 +320,10 @@ public static String rhSsoProductCode() { return getProductCode(rhSsoImageURL()); } + public static String rhSsoTemplates() { + return XTFConfig.get(RHSSO_TEMPLATES); + } + public static String activeMQImageUrl() { return XTFConfig.get(ACTIVEMQ_IMAGE_URL); } diff --git a/tools/intersmash-tools-provisioners/README.md b/tools/intersmash-tools-provisioners/README.md index 58bb2e1a2..ddbd9c195 100644 --- a/tools/intersmash-tools-provisioners/README.md +++ b/tools/intersmash-tools-provisioners/README.md @@ -63,6 +63,7 @@ public class PostgresqlProvisionTest { | Red Hat JBoss EAP 7 | :x: | :heavy_check_mark: | Eap7TemplateOpenShiftApplication | Eap7TemplateOpenShiftProvisioner | Available Git sources and template based EAP 7 s2i (legacy) build | | Red Hat JBoss EAP 7 | :x: | :heavy_check_mark: | Eap7LegacyS2iBuildTemplateApplication | Eap7LegacyS2iBuildTemplateProvisioner | Git based EAP 7 s2i (legacy) build, used to generate image streams that can be deployed by WildflyOperatorProvisioner | | Red Hat SSO 7 | :x: | :heavy_check_mark: | RhSsoOperatorApplication | RhSsoOperatorProvisioner | Based on the archived Keycloak operator project, which contains the latest Red Hat SSO 7.z CRDs definitions, see details [below](#operator-based-provisioning) | +| Red Hat SSO 7 | :x: | :heavy_check_mark: | RhSsoTemplateOpenShiftApplication | RhSsoTemplateOpenShiftProvisioner | Allows provisioning Red Hat SSO 7.6.z based on latest templates, see https://raw.githubusercontent.com/jboss-container-images/redhat-sso-7-openshift-image/sso76-dev/templates/ | The only thing users have to take care of is to implement the correct `Application` (see the table above) interface and diff --git a/tools/intersmash-tools-provisioners/src/main/java/org/jboss/intersmash/tools/application/openshift/RhSsoTemplateOpenShiftApplication.java b/tools/intersmash-tools-provisioners/src/main/java/org/jboss/intersmash/tools/application/openshift/RhSsoTemplateOpenShiftApplication.java new file mode 100644 index 000000000..76ab0dabd --- /dev/null +++ b/tools/intersmash-tools-provisioners/src/main/java/org/jboss/intersmash/tools/application/openshift/RhSsoTemplateOpenShiftApplication.java @@ -0,0 +1,63 @@ +package org.jboss.intersmash.tools.application.openshift; + +import java.nio.file.Path; + +import org.jboss.intersmash.tools.application.openshift.template.RhSsoTemplate; + +/** + * End user Application interface which presents RH-SSO template application on OpenShift Container Platform. + *

+ * RH-SSO application that is supposed to run on OpenShift needs to implement this interface. + * Usage: + *

+ *     @Appsint(
+ *     		@Service(RhssoApp.class)
+ *     })
+ * 
+ * The application will be deployed by: + *
    + *
  • {@link RhSsoTemplateOpenShiftProvisioner}
  • + *
+ *

+ * See {@link RhSsoTemplate} for available templates the + * application can represent. + */ +public interface RhSsoTemplateOpenShiftApplication extends TemplateApplication, HasSecrets { + + default String getName() { + return "rh-sso"; + } + + /** + * Realm configuration in json format for Keycloak partial import + *

+ * https://www.keycloak.org/docs/9.0/server_admin/index.html#importing-a-realm-from-exported-json-file + *

+ * Requires template parameters SSO_REALM, SSO_SERVICE_USERNAME, SSO_SERVICE_PASSWORD to be set + * + * @return Instance of {@link Path} representing a YAML definition for the desired realm configuration + */ + default Path getRealmConfigurationFilePath() { + return null; + } + + /** + * Non x509 templates expose an HTTP route named after the application name; + * x509 templates don't expose an HTTP route; + * + * @return The service HTTP route + */ + default String getHttpRouteName() { + return getTemplate().isX509() ? null : getName(); + } + + /** + * Non x509 templates expose an HTTPS route named after the application name with the "secure-" prefix; + * x509 templates expose an HTTPS route named after the application name; + * + * @return The service HTTPS route + */ + default String getHttpsRouteName() { + return getTemplate().isX509() ? getName() : String.format("secure-%s", getName()); + } +} diff --git a/tools/intersmash-tools-provisioners/src/main/java/org/jboss/intersmash/tools/application/openshift/template/RhSsoTemplate.java b/tools/intersmash-tools-provisioners/src/main/java/org/jboss/intersmash/tools/application/openshift/template/RhSsoTemplate.java new file mode 100644 index 000000000..b61f6a73a --- /dev/null +++ b/tools/intersmash-tools-provisioners/src/main/java/org/jboss/intersmash/tools/application/openshift/template/RhSsoTemplate.java @@ -0,0 +1,46 @@ +package org.jboss.intersmash.tools.application.openshift.template; + +import java.util.HashMap; +import java.util.Map; + +import org.jboss.intersmash.tools.provision.openshift.template.OpenShiftTemplate; + +/** + * OpenShift template for RH-SSO. + *

+ * See https://github.com/jboss-container-images/redhat-sso-7-openshift-image + */ +public enum RhSsoTemplate implements OpenShiftTemplate { + HTTPS("https"), + POSTGRESQL("postgresql"), + POSTGRESQL_PERSISTENT("postgresql-persistent"), + X509_HTTPS("x509-https"), + X509_POSTGRESQL_PERSISTENT("x509-postgresql-persistent"); + + private static final Map BY_LABEL = new HashMap<>(); + + static { + for (RhSsoTemplate e : values()) { + BY_LABEL.put(e.label, e); + } + } + + private String label; + + RhSsoTemplate(String label) { + this.label = label; + } + + @Override + public String getLabel() { + return label; + } + + public boolean isX509() { + return label.contains("x509"); + } + + public static RhSsoTemplate valueOfLabel(String label) { + return BY_LABEL.get(label); + } +} diff --git a/tools/intersmash-tools-provisioners/src/main/java/org/jboss/intersmash/tools/provision/openshift/RhSsoTemplateOpenShiftProvisioner.java b/tools/intersmash-tools-provisioners/src/main/java/org/jboss/intersmash/tools/provision/openshift/RhSsoTemplateOpenShiftProvisioner.java new file mode 100644 index 000000000..eebae2530 --- /dev/null +++ b/tools/intersmash-tools-provisioners/src/main/java/org/jboss/intersmash/tools/provision/openshift/RhSsoTemplateOpenShiftProvisioner.java @@ -0,0 +1,174 @@ +package org.jboss.intersmash.tools.provision.openshift; + +import java.io.IOException; +import java.io.InputStream; +import java.net.MalformedURLException; +import java.nio.file.Files; +import java.security.KeyManagementException; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.jboss.intersmash.tools.IntersmashConfig; +import org.jboss.intersmash.tools.application.openshift.RhSsoTemplateOpenShiftApplication; +import org.jboss.intersmash.tools.provision.openshift.template.OpenShiftTemplate; +import org.jboss.intersmash.tools.provision.openshift.template.OpenShiftTemplateProvisioner; +import org.jboss.intersmash.tools.provision.openshift.template.RhSsoTemplateProvisioner; +import org.jboss.intersmash.tools.util.keycloak.KeycloakAdminClient; +import org.slf4j.event.Level; + +import cz.xtf.client.Http; +import cz.xtf.core.event.helpers.EventHelper; +import cz.xtf.core.openshift.OpenShiftWaiters; +import cz.xtf.core.waiting.failfast.FailFastCheck; +import io.fabric8.kubernetes.api.model.EnvVarBuilder; +import io.fabric8.kubernetes.api.model.Pod; +import io.fabric8.openshift.api.model.DeploymentConfig; +import io.fabric8.openshift.api.model.ImageStream; +import io.fabric8.openshift.api.model.Route; +import io.fabric8.openshift.api.model.Template; +import lombok.NonNull; +import lombok.extern.slf4j.Slf4j; + +@Slf4j +public class RhSsoTemplateOpenShiftProvisioner implements OpenShiftProvisioner { + + // TODO: what if only OpenShiftApplication is implemented + private final RhSsoTemplateOpenShiftApplication rhSsoApplication; + private final OpenShiftTemplate rhSsoTemplate; + private List deployedImageStreams; + private Template deployedTemplate; + + public RhSsoTemplateOpenShiftProvisioner(@NonNull RhSsoTemplateOpenShiftApplication rhSsoApplication) { + this.rhSsoApplication = rhSsoApplication; + this.rhSsoTemplate = rhSsoApplication.getTemplate(); + } + + @Override + public RhSsoTemplateOpenShiftApplication getApplication() { + return rhSsoApplication; + } + + @Override + public void deploy() { + deployTemplate(); + if (rhSsoApplication.getRealmConfigurationFilePath() != null) { + importRealmConfigurationFromFile(); + } + } + + @Override + public void undeploy() { + Map labels = new HashMap<>(2); + labels.put("application", rhSsoApplication.getName()); + labels.put(APP_LABEL_KEY, rhSsoApplication.getName()); + OpenShiftUtils.deleteResourcesWithLabels(openShift, labels); + // when using geit repo S2I create soe custom maps and buil pods + openShift.getConfigMaps() + .stream() + .filter(cfMap -> cfMap.getMetadata().getName().startsWith(rhSsoApplication.getName())) + .forEach(openShift::deleteConfigMap); + openShift.getPods() + .stream() + .filter(pod -> pod.getMetadata().getName().startsWith(rhSsoApplication.getName())) + .forEach(openShift::deletePod); + deployedImageStreams.forEach(openShift::deleteImageStream); + openShift.deleteTemplate(deployedTemplate); + } + + private void deployTemplate() { + FailFastCheck failFastCheck = FailFastUtils.getFailFastCheck(EventHelper.timeOfLastEventBMOrTestNamespaceOrEpoch(), + rhSsoApplication.getName()); + OpenShiftTemplateProvisioner templateProvisioner = new RhSsoTemplateProvisioner(); + deployedImageStreams = templateProvisioner.deployImageStreams(); + deployedTemplate = templateProvisioner.deployTemplate(rhSsoTemplate); + + openShift.processAndDeployTemplate(deployedTemplate.getMetadata().getName(), + rhSsoApplication.getParameters()); + // run post deploy scripts before waiting, there is a plenty of time (app building) for openshift to deal with it + postDeploy(rhSsoApplication); + + // Equivalent of oc get route sso-app -o template --template "{{.spec.host}}" + Route route = openShift.getRoute(rhSsoApplication.getHttpsRouteName()); + if (route == null) { + throw new RuntimeException(String.format("RH-SSO Template \"%s\" doesn't provide an HTTPS Route!", + rhSsoApplication.getTemplate().getLabel())); + } + String url = "https://" + route.getSpec().getHost() + "/auth/"; + try { + Http.get(url) + .trustAll() + .waiters() + .ok() + .failFast(failFastCheck) + .level(Level.DEBUG) + .waitFor(); + } catch (MalformedURLException e) { + throw new RuntimeException(String.format("RH-SSO secure host name url \"%s\" is malformed", url), e); + } + } + + private void postDeploy(RhSsoTemplateOpenShiftApplication rhSsoApplication) { + if (IntersmashConfig.scriptDebug() != null) { + DeploymentConfig dc = openShift.getDeploymentConfig(rhSsoApplication.getName()); + dc.getSpec().getTemplate().getSpec().getContainers().get(0).getEnv() + .add(new EnvVarBuilder().withName(SCRIPT_DEBUG).withValue(IntersmashConfig.scriptDebug()).build()); + openShift.deploymentConfigs().createOrReplace(dc); + } + } + + private void importRealmConfigurationFromFile() { + log.debug("Realm configuration is specified, importing it."); + final String ssoRealmParameter = "SSO_REALM"; + final String ssoServiceUsernameParameter = "SSO_SERVICE_USERNAME"; + final String ssoServicePasswordParameter = "SSO_SERVICE_PASSWORD"; + + // validate all required parameters are set + Map parameters = rhSsoApplication.getParameters(); + List.of(ssoRealmParameter, ssoServiceUsernameParameter, ssoServicePasswordParameter) + .forEach(expected -> { + if (parameters.get(expected) == null || parameters.get(expected).isBlank()) { + throw new IllegalStateException( + "Realm configuration is specified but required template parameter" + expected + + " isn't."); + } + }); + + final String ssoRealm = parameters.get(ssoRealmParameter); + final String ssoUser = parameters.get(ssoServiceUsernameParameter); + final String ssoPassword = parameters.get(ssoServicePasswordParameter); + final String url = getUrl("secure-" + rhSsoApplication.getName(), true) + "/auth/"; + + KeycloakAdminClient rhSsoAdminClient = null; + try { + rhSsoAdminClient = new KeycloakAdminClient(url, ssoRealm, ssoUser, ssoPassword); + try (InputStream is = Files.newInputStream(rhSsoApplication.getRealmConfigurationFilePath())) { + rhSsoAdminClient.importRealmConfiguration(is); + } + } catch (KeyStoreException | NoSuchAlgorithmException | KeyManagementException | IOException e) { + throw new RuntimeException("Error while importing realm configuration.", e); + } + } + + @Override + public void scale(int replicas, boolean wait) { + openShift.scale(rhSsoApplication.getName(), replicas); + if (wait) { + OpenShiftWaiters.get(openShift, () -> false).areExactlyNPodsReady(replicas, rhSsoApplication.getName()) + .level(Level.DEBUG).waitFor(); + } + } + + @Override + public List getPods() { + return openShift.getPods(getApplication().getName()); + } + + @Override + public String getUrl(String routeName, boolean secure) { + String protocol = secure ? "https" : "http"; + return protocol + "://" + openShift.generateHostname(routeName); + } +} diff --git a/tools/intersmash-tools-provisioners/src/main/java/org/jboss/intersmash/tools/provision/openshift/RhSsoTemplateOpenShiftProvisionerFactory.java b/tools/intersmash-tools-provisioners/src/main/java/org/jboss/intersmash/tools/provision/openshift/RhSsoTemplateOpenShiftProvisionerFactory.java new file mode 100644 index 000000000..16645744a --- /dev/null +++ b/tools/intersmash-tools-provisioners/src/main/java/org/jboss/intersmash/tools/provision/openshift/RhSsoTemplateOpenShiftProvisionerFactory.java @@ -0,0 +1,18 @@ +package org.jboss.intersmash.tools.provision.openshift; + +import org.jboss.intersmash.tools.application.Application; +import org.jboss.intersmash.tools.application.openshift.RhSsoTemplateOpenShiftApplication; +import org.jboss.intersmash.tools.provision.ProvisionerFactory; + +import lombok.extern.slf4j.Slf4j; + +@Slf4j +public class RhSsoTemplateOpenShiftProvisionerFactory implements ProvisionerFactory { + + @Override + public RhSsoTemplateOpenShiftProvisioner getProvisioner(Application application) { + if (RhSsoTemplateOpenShiftApplication.class.isAssignableFrom(application.getClass())) + return new RhSsoTemplateOpenShiftProvisioner((RhSsoTemplateOpenShiftApplication) application); + return null; + } +} diff --git a/tools/intersmash-tools-provisioners/src/main/java/org/jboss/intersmash/tools/provision/openshift/template/RhSsoTemplateProvisioner.java b/tools/intersmash-tools-provisioners/src/main/java/org/jboss/intersmash/tools/provision/openshift/template/RhSsoTemplateProvisioner.java new file mode 100644 index 000000000..ed49cae57 --- /dev/null +++ b/tools/intersmash-tools-provisioners/src/main/java/org/jboss/intersmash/tools/provision/openshift/template/RhSsoTemplateProvisioner.java @@ -0,0 +1,78 @@ +package org.jboss.intersmash.tools.provision.openshift.template; + +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.util.List; +import java.util.stream.Collectors; + +import org.jboss.intersmash.tools.IntersmashConfig; +import org.jboss.intersmash.tools.application.openshift.template.RhSsoTemplate; + +import io.fabric8.kubernetes.api.model.HasMetadata; +import io.fabric8.openshift.api.model.ImageStream; +import io.fabric8.openshift.api.model.TagImportPolicyBuilder; + +public class RhSsoTemplateProvisioner implements OpenShiftTemplateProvisioner { + @Override + public String getTemplatesUrl() { + return IntersmashConfig.rhSsoTemplates(); + } + + @Override + public String getProductCode() { + return IntersmashConfig.rhSsoProductCode(); + } + + @Override + public List deployImageStreams() { + String url = getTemplatesUrl() + getProductCode() + "-image-stream.json"; + try (InputStream is = new URL(url).openStream()) { + // since RH-SSO 76, an additional PostgreSQL image has been added to the template + List imageStreams = openShift.load(is).items(); + // get a reference to the actual RH-SSO ImageStream definition (by skipping the PostgreSQL one) + ImageStream ssoImageStream = imageStreams.stream() + .filter(item -> !"PostgreSQL".equals(item.getMetadata().getAnnotations().get("openshift.io/display-name"))) + .map(item -> (ImageStream) item) + .findFirst().orElseThrow( + () -> new IllegalStateException( + String.format( + "The RH-SSO template provisioner didn't find a suitable RH-SSO image stream in the provided image streams definition: %s", + url))); + // update the tags with the RH-SSO image set via configuration mechanism + ssoImageStream.getSpec().getTags().stream() + .filter(tagReference -> tagReference.getFrom().getKind().equals("DockerImage")) + .forEach(tagReference -> { + tagReference.getFrom().setName(IntersmashConfig.rhSsoImageURL()); + tagReference.setImportPolicy(new TagImportPolicyBuilder().withInsecure(true).build()); + }); + return imageStreams.stream() + .map(imageStream -> openShift.imageStreams().createOrReplace((ImageStream) imageStream)) + .collect(Collectors.toList()); + } catch (IOException e) { + throw new RuntimeException("Failed to deploy RH-SSO image streams from " + url, e); + } + } + + @Override + public String getTemplateFileName(OpenShiftTemplate openShiftTemplate) { + return String.format("%s-ocp4-%s", getProductCode(), openShiftTemplate.getLabel()); + } + + @Override + public String getTemplateFileUrl(OpenShiftTemplate openShiftTemplate) { + switch (RhSsoTemplate.valueOfLabel(openShiftTemplate.getLabel())) { + case HTTPS: + case POSTGRESQL: + case POSTGRESQL_PERSISTENT: + return String.format("%s/%s/%s.json", getTemplatesUrl(), "passthrough/ocp-4.x", + getTemplateFileName(openShiftTemplate)); + case X509_HTTPS: + case X509_POSTGRESQL_PERSISTENT: + return String.format("%s/%s/%s.json", getTemplatesUrl(), "reencrypt/ocp-4.x", + getTemplateFileName(openShiftTemplate)); + default: + throw new IllegalArgumentException(String.format("Unknown template name: %s", openShiftTemplate.getLabel())); + } + } +} diff --git a/tools/intersmash-tools-provisioners/src/main/java/org/jboss/intersmash/tools/util/keycloak/KeycloakAdminClient.java b/tools/intersmash-tools-provisioners/src/main/java/org/jboss/intersmash/tools/util/keycloak/KeycloakAdminClient.java index 816f335a7..6e1a31244 100644 --- a/tools/intersmash-tools-provisioners/src/main/java/org/jboss/intersmash/tools/util/keycloak/KeycloakAdminClient.java +++ b/tools/intersmash-tools-provisioners/src/main/java/org/jboss/intersmash/tools/util/keycloak/KeycloakAdminClient.java @@ -62,6 +62,7 @@ public KeycloakAdminClient(final String url, final String realm, final String us */ public void importRealmConfiguration(InputStream is) throws IOException { PartialImportRepresentation piRep = JsonSerialization.readValue(is, PartialImportRepresentation.class); + // TODO - fix, the realm name should be passed in as a parameter keycloak.realm("wildfly-realm").partialImport(piRep); } } diff --git a/tools/intersmash-tools-provisioners/src/main/resources/META-INF/services/org.jboss.intersmash.tools.provision.ProvisionerFactory b/tools/intersmash-tools-provisioners/src/main/resources/META-INF/services/org.jboss.intersmash.tools.provision.ProvisionerFactory index 3ad401748..4363dfd75 100644 --- a/tools/intersmash-tools-provisioners/src/main/resources/META-INF/services/org.jboss.intersmash.tools.provision.ProvisionerFactory +++ b/tools/intersmash-tools-provisioners/src/main/resources/META-INF/services/org.jboss.intersmash.tools.provision.ProvisionerFactory @@ -9,3 +9,4 @@ org.jboss.intersmash.tools.provision.openshift.InfinispanOperatorProvisionerFact org.jboss.intersmash.tools.provision.openshift.RhSsoOperatorProvisionerFactory org.jboss.intersmash.tools.provision.helm.WildflyHelmChartOpenShiftProvisionerFactory org.jboss.intersmash.tools.provision.openshift.Eap7ImageOpenShiftProvisionerFactory +org.jboss.intersmash.tools.provision.openshift.RhSsoTemplateOpenShiftProvisionerFactory diff --git a/tools/intersmash-tools-provisioners/src/test/java/org/jboss/intersmash/tools/provision/ProvisionerManagerTestCase.java b/tools/intersmash-tools-provisioners/src/test/java/org/jboss/intersmash/tools/provision/ProvisionerManagerTestCase.java index 9836f2c4e..20bd4bdf7 100644 --- a/tools/intersmash-tools-provisioners/src/test/java/org/jboss/intersmash/tools/provision/ProvisionerManagerTestCase.java +++ b/tools/intersmash-tools-provisioners/src/test/java/org/jboss/intersmash/tools/provision/ProvisionerManagerTestCase.java @@ -16,6 +16,7 @@ package org.jboss.intersmash.tools.provision; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; import org.jboss.intersmash.tools.application.Application; import org.jboss.intersmash.tools.application.openshift.ActiveMQOperatorApplication; @@ -24,13 +25,16 @@ import org.jboss.intersmash.tools.application.openshift.KafkaOperatorApplication; import org.jboss.intersmash.tools.application.openshift.MysqlImageOpenShiftApplication; import org.jboss.intersmash.tools.application.openshift.PostgreSQLImageOpenShiftApplication; +import org.jboss.intersmash.tools.application.openshift.RhSsoTemplateOpenShiftApplication; import org.jboss.intersmash.tools.application.openshift.WildflyImageOpenShiftApplication; import org.jboss.intersmash.tools.application.openshift.WildflyOperatorApplication; +import org.jboss.intersmash.tools.application.openshift.template.RhSsoTemplate; import org.jboss.intersmash.tools.provision.openshift.ActiveMQOperatorProvisioner; import org.jboss.intersmash.tools.provision.openshift.Eap7LegacyS2iBuildTemplateProvisioner; import org.jboss.intersmash.tools.provision.openshift.KafkaOperatorProvisioner; import org.jboss.intersmash.tools.provision.openshift.MysqlImageOpenShiftProvisioner; import org.jboss.intersmash.tools.provision.openshift.PostgreSQLImageOpenShiftProvisioner; +import org.jboss.intersmash.tools.provision.openshift.RhSsoTemplateOpenShiftProvisioner; import org.jboss.intersmash.tools.provision.openshift.WildflyBootableJarImageOpenShiftProvisioner; import org.jboss.intersmash.tools.provision.openshift.WildflyImageOpenShiftProvisioner; import org.jboss.intersmash.tools.provision.openshift.WildflyOperatorProvisioner; @@ -49,6 +53,7 @@ * | ActiveMQOperatorApplication | OPERATOR | ActiveMQOperatorProvisioner | * | KafkaOperatorApplication | OPERATOR | KafkaOperatorProvisioner | * | WildflyBootableJarOpenShiftApplication | IMAGE | WildflyBootableJarImageOpenShiftProvisioner | + * | RhSsoTemplateOpenShiftApplication | TEMPLATE | RhSsoTemplateOpenShiftProvisioner | */ public class ProvisionerManagerTestCase { private Application application; @@ -141,6 +146,19 @@ public void eapS2iBuildTemplateProvisioner() { Assertions.assertEquals(Eap7LegacyS2iBuildTemplateProvisioner.class, actual.getClass()); } + + /** + * | RhSsoTemplateOpenShiftApplication | TEMPLATE | RhSsoTemplateOpenShiftProvisioner | + */ + @Test + public void openShiftRhSsoTemplateProvisioner() { + application = mock(RhSsoTemplateOpenShiftApplication.class); + when(((RhSsoTemplateOpenShiftApplication) application).getTemplate()).thenReturn(RhSsoTemplate.X509_HTTPS); + + Provisioner actual = ProvisionerManager.getProvisioner(application); + Assertions.assertEquals(RhSsoTemplateOpenShiftProvisioner.class, actual.getClass()); + } + @Test public void unsupportedProvisioner() { Assertions.assertThrows(UnsupportedOperationException.class, From e88d246901d8957532d6aa172c0a660611858472 Mon Sep 17 00:00:00 2001 From: Fabio Burzigotti Date: Thu, 16 Nov 2023 15:30:15 +0100 Subject: [PATCH 2/2] [issue 105] - Fixing the KeycloakAdminClient so that the realm name is stored and used later --- .../tools/util/keycloak/KeycloakAdminClient.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tools/intersmash-tools-provisioners/src/main/java/org/jboss/intersmash/tools/util/keycloak/KeycloakAdminClient.java b/tools/intersmash-tools-provisioners/src/main/java/org/jboss/intersmash/tools/util/keycloak/KeycloakAdminClient.java index 6e1a31244..e748cf4d9 100644 --- a/tools/intersmash-tools-provisioners/src/main/java/org/jboss/intersmash/tools/util/keycloak/KeycloakAdminClient.java +++ b/tools/intersmash-tools-provisioners/src/main/java/org/jboss/intersmash/tools/util/keycloak/KeycloakAdminClient.java @@ -38,7 +38,8 @@ */ public class KeycloakAdminClient { - private Keycloak keycloak; + private final String realmName; + private final Keycloak keycloak; public KeycloakAdminClient(final String url, final String realm, final String username, final String password) throws KeyStoreException, NoSuchAlgorithmException, KeyManagementException { @@ -46,7 +47,7 @@ public KeycloakAdminClient(final String url, final String realm, final String us .create() .loadTrustMaterial(TrustAllStrategy.INSTANCE) .build(); - + realmName = realm; keycloak = Keycloak.getInstance( url, realm, @@ -62,7 +63,6 @@ public KeycloakAdminClient(final String url, final String realm, final String us */ public void importRealmConfiguration(InputStream is) throws IOException { PartialImportRepresentation piRep = JsonSerialization.readValue(is, PartialImportRepresentation.class); - // TODO - fix, the realm name should be passed in as a parameter - keycloak.realm("wildfly-realm").partialImport(piRep); + keycloak.realm(this.realmName).partialImport(piRep); } }