diff --git a/cli/assembly.xml b/cli/assembly.xml index 9df68c65..8a8ca605 100644 --- a/cli/assembly.xml +++ b/cli/assembly.xml @@ -31,6 +31,7 @@ examples kitchensink.war + todo-backend.war diff --git a/cli/src/main/java/org/wildfly/glow/cli/commands/OpenShiftSupport.java b/cli/src/main/java/org/wildfly/glow/cli/commands/OpenShiftSupport.java index af692ee1..c575eddf 100644 --- a/cli/src/main/java/org/wildfly/glow/cli/commands/OpenShiftSupport.java +++ b/cli/src/main/java/org/wildfly/glow/cli/commands/OpenShiftSupport.java @@ -35,7 +35,6 @@ import io.fabric8.kubernetes.client.Watcher; import io.fabric8.kubernetes.client.WatcherException; import io.fabric8.kubernetes.client.dsl.NonDeletingOperation; -import io.fabric8.kubernetes.client.utils.Serialization; import io.fabric8.openshift.api.model.Build; import io.fabric8.openshift.api.model.BuildConfig; import io.fabric8.openshift.api.model.BuildConfigBuilder; @@ -54,7 +53,6 @@ import io.fabric8.openshift.client.OpenShiftClient; import java.nio.file.Files; import java.nio.file.Path; -import java.nio.file.Paths; import java.security.MessageDigest; import java.util.ArrayList; import java.util.HashMap; @@ -78,7 +76,7 @@ import org.wildfly.glow.GlowMessageWriter; import org.wildfly.glow.Layer; import org.wildfly.glow.deployment.openshift.api.Deployer; - +import org.wildfly.glow.deployment.openshift.api.Utils; /** * * @author jdenise @@ -123,7 +121,7 @@ private static void createAppDeployment(GlowMessageWriter writer, Path target, O withType("ClusterIP").withIpFamilyPolicy("SingleStack"). withSessionAffinity("None").withSelector(labels).endSpec().build(); osClient.services().resource(pingService).createOr(NonDeletingOperation::update); - Files.write(target.resolve(name + "-ping-service.yaml"), Serialization.asYaml(pingService).getBytes()); + Utils.persistResource(target, pingService, name + "-ping-service.yaml"); } Container container = new Container(); container.setName(name); @@ -166,7 +164,7 @@ private static void createAppDeployment(GlowMessageWriter writer, Path target, O withContainers(container).withRestartPolicy("Always"). endSpec().endTemplate().withNewStrategy().withType("RollingUpdate").endStrategy().endSpec().build(); osClient.resources(Deployment.class).resource(deployment).createOr(NonDeletingOperation::update); - Files.write(target.resolve(name + "-deployment.yaml"), Serialization.asYaml(deployment).getBytes()); + Utils.persistResource(target, deployment, name + "-deployment.yaml"); IntOrString v = new IntOrString(); v.setValue(8080); Service service = new ServiceBuilder().withNewMetadata().withName(name).endMetadata(). @@ -174,7 +172,7 @@ private static void createAppDeployment(GlowMessageWriter writer, Path target, O withPort(8080). withTargetPort(v).build()).withType("ClusterIP").withSessionAffinity("None").withSelector(labels).endSpec().build(); osClient.services().resource(service).createOr(NonDeletingOperation::update); - Files.write(target.resolve(name + "-service.yaml"), Serialization.asYaml(service).getBytes()); + Utils.persistResource(target, service, name + "-service.yaml"); writer.info("Waiting until the application is ready ..."); osClient.resources(Deployment.class).resource(deployment).waitUntilReady(5, TimeUnit.MINUTES); @@ -192,7 +190,7 @@ static void deploy(GlowMessageWriter writer, Path target, String appName, Map existingDeployers = new HashMap<>(); @@ -243,13 +241,14 @@ static void deploy(GlowMessageWriter writer, Path target, String appName, Map entry : actualEnv.entrySet()) { writer.warn(entry.getKey() + "=" + entry.getValue()); } + writer.warn("\n"); } createAppDeployment(writer, target, osClient, appName, actualEnv, ha); writer.info("\nApplication route: https://" + host + ("ROOT.war".equals(appName) ? "" : "/" + appName)); @@ -283,12 +282,10 @@ private static String bytesToHex(byte[] hash) { return hexString.toString(); } - static Map createLabels(Path provisioning) throws Exception { + static Map createLabels(Path target, Path provisioning) throws Exception { GalleonBuilder provider = new GalleonBuilder(); - Path dir = provisioning.getParent().resolve("tmpHome"); + Path dir = target.resolve("tmp").resolve("tmpHome"); Files.createDirectory(dir); - StringBuilder layers = new StringBuilder(); - StringBuilder excludedLayers = new StringBuilder(); StringBuilder fps = new StringBuilder(); Map labels = new HashMap<>(); try (Provisioning p = provider.newProvisioningBuilder(provisioning).setInstallationHome(dir).build()) { @@ -317,13 +314,6 @@ static Map createLabels(Path provisioning) throws Exception { return labels; } - private static String format(String label) { - if (label.length() > 63) { - label = label.substring(0, 56); - label += ".trunc"; - } - return label; - } static String doServerImageBuild(GlowMessageWriter writer, Path target, OpenShiftClient osClient) throws Exception { Path provisioning = target.resolve("galleon").resolve("provisioning.xml"); byte[] content = Files.readAllBytes(provisioning); @@ -339,19 +329,18 @@ static String doServerImageBuild(GlowMessageWriter writer, Path target, OpenShif if (existingStream == null) { writer.info("\nBuilding server image (this can take up to few minutes the first time)..."); // zip deployment and provisioning.xml to be pushed to OpenShift - Path file = Paths.get("openshiftServer.zip"); + Path file = target.resolve("tmp").resolve("openshiftServer.zip"); if (Files.exists(file)) { Files.delete(file); } - file.toFile().deleteOnExit(); // First do a build of the naked server - Path stepOne = target.resolve("step-one"); + Path stepOne = target.resolve("tmp").resolve("step-one"); Files.createDirectories(stepOne); IoUtils.copy(target.resolve("galleon"), stepOne.resolve("galleon")); ZipUtils.zip(stepOne, file); - stream = stream.toBuilder().editOrNewMetadata().withLabels(createLabels(provisioning)).endMetadata().build(); + stream = stream.toBuilder().editOrNewMetadata().withLabels(createLabels(target, provisioning)).endMetadata().build(); osClient.imageStreams().resource(stream).createOr(NonDeletingOperation::update); - Files.write(target.resolve(serverImageName + "-image-stream.yaml"), Serialization.asYaml(stream).getBytes()); + Utils.persistResource(target, stream, serverImageName + "-image-stream.yaml"); BuildConfigBuilder builder = new BuildConfigBuilder(); ObjectReference ref = new ObjectReference(); ref.setKind("ImageStreamTag"); @@ -369,7 +358,7 @@ static String doServerImageBuild(GlowMessageWriter writer, Path target, OpenShif endSourceStrategy().endStrategy().withNewSource(). withType("Binary").endSource().endSpec().build(); osClient.buildConfigs().resource(buildConfig).createOr(NonDeletingOperation::update); - Files.write(target.resolve(serverImageName + "-build-config.yaml"), Serialization.asYaml(buildConfig).getBytes()); + Utils.persistResource(target, buildConfig, serverImageName + "-build-config.yaml"); Build build = osClient.buildConfigs().withName(serverImageName + "-build").instantiateBinary().fromFile(file.toFile()); CountDownLatch latch = new CountDownLatch(1); @@ -383,7 +372,7 @@ static String doServerImageBuild(GlowMessageWriter writer, Path target, OpenShif static void doAppImageBuild(String serverImageName, GlowMessageWriter writer, Path target, OpenShiftClient osClient, String name, Path initScript) throws Exception { // Now step 2 // From the server image, do a docker build, copy the server and copy in it the deployments and init file. - Path stepTwo = target.resolve("step-two"); + Path stepTwo = target.resolve("tmp").resolve("step-two"); IoUtils.copy(target.resolve("deployments"), stepTwo.resolve("deployments")); StringBuilder dockerFileBuilder = new StringBuilder(); dockerFileBuilder.append("FROM wildfly-runtime:latest\n"); @@ -400,12 +389,12 @@ static void doAppImageBuild(String serverImageName, GlowMessageWriter writer, Pa Path dockerFile = stepTwo.resolve("Dockerfile"); Files.write(dockerFile, dockerFileBuilder.toString().getBytes()); - Path file2 = Paths.get("openshiftApp.zip"); + Path file2 = target.resolve("tmp").resolve("openshiftApp.zip"); if (Files.exists(file2)) { Files.delete(file2); } ZipUtils.zip(stepTwo, file2); - writer.info("\nCreating and starting application image build on OpenShift..."); + writer.info("\nBuilding application image..."); ImageStream runtimeStream = new ImageStreamBuilder().withNewMetadata().withName("wildfly-runtime"). endMetadata().withNewSpec(). addToTags(0, new TagReferenceBuilder() @@ -439,7 +428,7 @@ static void doAppImageBuild(String serverImageName, GlowMessageWriter writer, Pa withDockerfilePath("./Dockerfile"). endDockerStrategy().endStrategy().endSpec().build(); osClient.buildConfigs().resource(buildConfig2).createOr(NonDeletingOperation::update); - Files.write(target.resolve(name + "-build-config.yaml"), Serialization.asYaml(buildConfig2).getBytes()); + Utils.persistResource(target, buildConfig2, name + "-build-config.yaml"); Build build = osClient.buildConfigs().withName(name + "-build").instantiateBinary().fromFile(file2.toFile()); CountDownLatch latch = new CountDownLatch(1); diff --git a/cli/src/main/java/org/wildfly/glow/cli/commands/ScanCommand.java b/cli/src/main/java/org/wildfly/glow/cli/commands/ScanCommand.java index 56d614b4..49d10ffe 100644 --- a/cli/src/main/java/org/wildfly/glow/cli/commands/ScanCommand.java +++ b/cli/src/main/java/org/wildfly/glow/cli/commands/ScanCommand.java @@ -368,7 +368,7 @@ public Integer call() throws Exception { } OpenShiftSupport.deploy(GlowMessageWriter.DEFAULT, target, name == null ? "app-from-wildfly-glow" : name.toLowerCase(), envMap, scanResults.getDiscoveredLayers(), scanResults.getEnabledAddOns(), haProfile.orElse(false), extraEnv, disableDeployers, initScriptFile.orElse(null)); - print("@|bold Openshift build and deploy DONE.|@"); + print("@|bold \nOpenshift build and deploy DONE.|@"); } if (content.getDockerImageName() != null) { print("@|bold To run the image call: 'docker run " + content.getDockerImageName() + "'|@"); diff --git a/dist/README.adoc b/dist/README.adoc index 4467de72..b05b6e2a 100644 --- a/dist/README.adoc +++ b/dist/README.adoc @@ -27,4 +27,21 @@ This war comes from the WildFly https://github.com/wildfly/quickstart/tree/30.0. === To provision a WildFly server for the cloud and produce a Docker image: -`./wildfly-glow scan ./examples/kitchensink.war --provision=DOCKER_IMAGE --cloud` \ No newline at end of file +`./wildfly-glow scan ./examples/kitchensink.war --provision=DOCKER_IMAGE --cloud` + +=== To provision a WildFly server for the cloud and directly deploy to OpenShift cluster: + +You must first log into an OpenShift cluster. + +`./wildfly-glow scan ./examples/kitchensink.war --provision=OPENSHIFT --cloud` + +== ToDo backend example + +This war comes from the WildFly https://github.com/wildfly/quickstart/tree/30.0.0.Final/todo-backend[todo-backend] quickstart. + +=== To provision a WildFly server for the cloud and directly deploy to OpenShift cluster: + +You must first log into an OpenShift cluster. +In this case, a `postgresql` database is started. The WildFly server automatically connects to it. + +`./wildfly-glow scan ./examples/todo-backend.war --provision=OPENSHIFT --cloud --add-ons postgresql` diff --git a/docs/guide/intro/index.adoc b/docs/guide/intro/index.adoc index 2e867774..041e46a2 100644 --- a/docs/guide/intro/index.adoc +++ b/docs/guide/intro/index.adoc @@ -67,6 +67,8 @@ provisioning and create your application deployment. At the end of the build, the application is deployed and the route to your application inside the cluster is printed. Use it to interact with your application. +Note: The OpenShift resources yaml files are generated in the directory `server-/resources` + Note: the support for OpenShift is currently specified by this WildFly Glow project link:https://github.com/wildfly/wildfly-glow/issues/49[GitHub Issue]. ###### Automatic deployment of PostGreSQL, MySQL, MariaDB, Artemis JMS Broker and Keycloak diff --git a/examples/war/todo-backend.war b/examples/war/todo-backend.war new file mode 100644 index 00000000..b5df5fde Binary files /dev/null and b/examples/war/todo-backend.war differ diff --git a/openshift-deployment/api/src/main/java/org/wildfly/glow/deployment/openshift/api/AbstractDatabaseDeployer.java b/openshift-deployment/api/src/main/java/org/wildfly/glow/deployment/openshift/api/AbstractDatabaseDeployer.java index ad3b82ef..9335cf87 100644 --- a/openshift-deployment/api/src/main/java/org/wildfly/glow/deployment/openshift/api/AbstractDatabaseDeployer.java +++ b/openshift-deployment/api/src/main/java/org/wildfly/glow/deployment/openshift/api/AbstractDatabaseDeployer.java @@ -27,9 +27,7 @@ import io.fabric8.kubernetes.api.model.apps.Deployment; import io.fabric8.kubernetes.api.model.apps.DeploymentBuilder; import io.fabric8.kubernetes.client.dsl.NonDeletingOperation; -import io.fabric8.kubernetes.client.utils.Serialization; import io.fabric8.openshift.client.OpenShiftClient; -import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; import java.util.HashMap; @@ -103,7 +101,6 @@ private Map getExistingEnv(Map env) { @Override public Map deploy(GlowMessageWriter writer, Path target, OpenShiftClient osClient, Map env, String appHost, String appName, String matching) throws Exception { - writer.info("\nDeploying " + dbName + " server"); Map labels = new HashMap<>(); labels.put(LABEL, dbName); ContainerPort port = new ContainerPort(); @@ -129,7 +126,7 @@ public Map deploy(GlowMessageWriter writer, Path target, OpenShi withContainers(container).withRestartPolicy("Always"). endSpec().endTemplate().withNewStrategy().withType("RollingUpdate").endStrategy().endSpec().build(); osClient.resources(Deployment.class).resource(deployment).createOr(NonDeletingOperation::update); - Files.write(target.resolve(dbName + "-deployment.yaml"), Serialization.asYaml(deployment).getBytes()); + Utils.persistResource(target, deployment, dbName + "-deployment.yaml"); IntOrString v = new IntOrString(); v.setValue(this.port); Service service = new ServiceBuilder().withNewMetadata().withName(dbName).endMetadata(). @@ -137,10 +134,11 @@ public Map deploy(GlowMessageWriter writer, Path target, OpenShi withPort(this.port). withTargetPort(v).build()).withType("ClusterIP").withSessionAffinity("None").withSelector(labels).endSpec().build(); osClient.services().resource(service).createOr(NonDeletingOperation::update); - Files.write(target.resolve(dbName + "-service.yaml"), Serialization.asYaml(service).getBytes()); + Utils.persistResource(target, service, dbName + "-service.yaml"); Map ret = new HashMap<>(); ret.putAll(getExistingEnv(env)); ret.putAll(APP_MAP); + writer.info("\n" + dbName + " server has been deployed"); return ret; } diff --git a/openshift-deployment/api/src/main/java/org/wildfly/glow/deployment/openshift/api/Utils.java b/openshift-deployment/api/src/main/java/org/wildfly/glow/deployment/openshift/api/Utils.java new file mode 100644 index 00000000..8baace36 --- /dev/null +++ b/openshift-deployment/api/src/main/java/org/wildfly/glow/deployment/openshift/api/Utils.java @@ -0,0 +1,36 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2024 Red Hat, Inc., and individual contributors + * as indicated by the @author tags. + * + * Licensed 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 org.wildfly.glow.deployment.openshift.api; + +import io.fabric8.kubernetes.client.utils.Serialization; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; + +/** + * + * @author jdenise + */ +public class Utils { + + public static void persistResource(Path target, Object resource, String name) throws IOException { + Path dir = target.resolve("resources"); + Files.createDirectories(dir); + Path resourceFile = dir.resolve(name); + Files.write(resourceFile, Serialization.asYaml(resource).getBytes()); + } +} diff --git a/openshift-deployment/artemis-broker/src/main/java/org/wildfly/glow/deployment/openshift/artemis/ArtemisDeployer.java b/openshift-deployment/artemis-broker/src/main/java/org/wildfly/glow/deployment/openshift/artemis/ArtemisDeployer.java index 312dff5c..09deefa8 100644 --- a/openshift-deployment/artemis-broker/src/main/java/org/wildfly/glow/deployment/openshift/artemis/ArtemisDeployer.java +++ b/openshift-deployment/artemis-broker/src/main/java/org/wildfly/glow/deployment/openshift/artemis/ArtemisDeployer.java @@ -27,9 +27,7 @@ import io.fabric8.kubernetes.api.model.apps.Deployment; import io.fabric8.kubernetes.api.model.apps.DeploymentBuilder; import io.fabric8.kubernetes.client.dsl.NonDeletingOperation; -import io.fabric8.kubernetes.client.utils.Serialization; import io.fabric8.openshift.client.OpenShiftClient; -import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; import java.util.HashMap; @@ -39,6 +37,7 @@ import java.util.Set; import org.wildfly.glow.GlowMessageWriter; import org.wildfly.glow.deployment.openshift.api.Deployer; +import org.wildfly.glow.deployment.openshift.api.Utils; /** * @@ -96,7 +95,7 @@ public Map deploy(GlowMessageWriter writer, Path target, OpenShi withContainers(container).withRestartPolicy("Always"). endSpec().endTemplate().withNewStrategy().withType("RollingUpdate").endStrategy().endSpec().build(); osClient.resources(Deployment.class).resource(deployment).createOr(NonDeletingOperation::update); - Files.write(target.resolve(REMOTE_BROKER_NAME + "-deployment.yaml"), Serialization.asYaml(deployment).getBytes()); + Utils.persistResource(target, deployment, REMOTE_BROKER_NAME + "-deployment.yaml"); IntOrString v = new IntOrString(); v.setValue(61616); Service service = new ServiceBuilder().withNewMetadata().withName(REMOTE_BROKER_NAME).endMetadata(). @@ -104,7 +103,7 @@ public Map deploy(GlowMessageWriter writer, Path target, OpenShi withPort(61616). withTargetPort(v).build()).withType("ClusterIP").withSessionAffinity("None").withSelector(labels).endSpec().build(); osClient.services().resource(service).createOr(NonDeletingOperation::update); - Files.write(target.resolve(REMOTE_BROKER_NAME + "-service.yaml"), Serialization.asYaml(service).getBytes()); + Utils.persistResource(target, service, REMOTE_BROKER_NAME + "-service.yaml"); Map ret = new HashMap<>(); ret.putAll(REMOTE_BROKER_APP_MAP); return REMOTE_BROKER_APP_MAP; diff --git a/openshift-deployment/keycloak/src/main/java/org/wildfly/glow/deployment/openshift/keycloak/KeycloakDeployer.java b/openshift-deployment/keycloak/src/main/java/org/wildfly/glow/deployment/openshift/keycloak/KeycloakDeployer.java index c894e69a..e0fc3d5d 100644 --- a/openshift-deployment/keycloak/src/main/java/org/wildfly/glow/deployment/openshift/keycloak/KeycloakDeployer.java +++ b/openshift-deployment/keycloak/src/main/java/org/wildfly/glow/deployment/openshift/keycloak/KeycloakDeployer.java @@ -19,7 +19,6 @@ import io.fabric8.kubernetes.api.model.KubernetesList; import io.fabric8.kubernetes.client.dsl.NonDeletingOperation; -import io.fabric8.kubernetes.client.utils.Serialization; import io.fabric8.openshift.api.model.DeploymentConfig; import io.fabric8.openshift.api.model.DeploymentConfigBuilder; import io.fabric8.openshift.api.model.Route; @@ -27,7 +26,6 @@ import io.fabric8.openshift.api.model.Template; import io.fabric8.openshift.client.OpenShiftClient; import java.net.URL; -import java.nio.file.Files; import java.nio.file.Path; import java.util.HashMap; import java.util.HashSet; @@ -36,6 +34,7 @@ import java.util.concurrent.TimeUnit; import org.wildfly.glow.GlowMessageWriter; import org.wildfly.glow.deployment.openshift.api.Deployer; +import org.wildfly.glow.deployment.openshift.api.Utils; /** * @@ -93,7 +92,7 @@ public Map deploy(GlowMessageWriter writer, Path target, OpenShi withName(KEYCLOAK_NAME) .process(parameters); osClient.resourceList(processedTemplateWithCustomParameters).createOrReplace(); - Files.write(target.resolve(KEYCLOAK_NAME + "-resources.yaml"), Serialization.asYaml(processedTemplateWithCustomParameters).getBytes()); + Utils.persistResource(target, processedTemplateWithCustomParameters, KEYCLOAK_NAME + "-resources.yaml"); writer.info("Waiting until keycloak is ready ..."); DeploymentConfig dc = new DeploymentConfigBuilder().withNewMetadata().withName(KEYCLOAK_NAME).endMetadata().build(); osClient.resources(DeploymentConfig.class).resource(dc).waitUntilReady(5, TimeUnit.MINUTES);