Skip to content

Commit

Permalink
Merge pull request #70 from jfdenise/main
Browse files Browse the repository at this point in the history
StatefulSet for HA application, fix deployment name
  • Loading branch information
jfdenise authored Apr 25, 2024
2 parents 69e5059 + a3e078a commit 44ca0f2
Show file tree
Hide file tree
Showing 3 changed files with 170 additions and 37 deletions.
5 changes: 5 additions & 0 deletions openshift-deployment/api/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -23,5 +23,10 @@
<groupId>io.fabric8</groupId>
<artifactId>openshift-client</artifactId>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@
import io.fabric8.kubernetes.api.model.ServicePort;
import io.fabric8.kubernetes.api.model.apps.Deployment;
import io.fabric8.kubernetes.api.model.apps.DeploymentBuilder;
import io.fabric8.kubernetes.api.model.apps.StatefulSet;
import io.fabric8.kubernetes.api.model.apps.StatefulSetBuilder;
import io.fabric8.kubernetes.client.KubernetesClientBuilder;
import io.fabric8.kubernetes.client.Watch;
import io.fabric8.kubernetes.client.Watcher;
Expand Down Expand Up @@ -88,12 +90,15 @@
public class OpenShiftSupport {

private static class BuildWatcher implements Watcher<Build>, AutoCloseable {

private final CountDownLatch latch = new CountDownLatch(1);
private final GlowMessageWriter writer;
private boolean failed;

BuildWatcher(GlowMessageWriter writer) {
this.writer = writer;
}

@Override
public void eventReceived(Action action, Build build) {
String phase = build.getStatus().getPhase();
Expand All @@ -114,9 +119,11 @@ public void eventReceived(Action action, Build build) {
@Override
public void onClose(WatcherException cause) {
}

void await() throws InterruptedException {
latch.await();
}

boolean isFailed() {
return failed;
}
Expand All @@ -125,10 +132,20 @@ boolean isFailed() {
public void close() throws Exception {
}
}

private static void createAppDeployment(GlowMessageWriter writer, Path target,
OpenShiftClient osClient, String name, Map<String, String> env, boolean ha, OpenShiftConfiguration config) throws Exception {
OpenShiftClient osClient, String name, Map<String, String> env, boolean ha, OpenShiftConfiguration config, String deploymentKind) throws Exception {
Map<String, String> matchLabels = new HashMap<>();
matchLabels.put(Deployer.LABEL, name);
IntOrString value = new IntOrString();
value.setValue(8080);
Service service = new ServiceBuilder().withNewMetadata().withLabels(createCommonLabels(config)).withName(name).endMetadata().
withNewSpec().withPorts(new ServicePort().toBuilder().withProtocol("TCP").
withPort(8080).
withTargetPort(value).build()).withType("ClusterIP").withSessionAffinity("None").withSelector(matchLabels).endSpec().build();
osClient.services().resource(service).createOr(NonDeletingOperation::update);
Utils.persistResource(target, service, name + "-service.yaml");

ContainerPort port = new ContainerPort();
port.setContainerPort(8080);
port.setName("http");
Expand All @@ -147,7 +164,7 @@ private static void createAppDeployment(GlowMessageWriter writer, Path target,
vars.add(new EnvVar().toBuilder().withName(entry.getKey()).withValue(entry.getValue()).build());
}
if (ha) {
writer.info("\n HA enabled, 2 replicas will be started.");
writer.info("\nHA enabled, 2 replicas will be started.");
IntOrString v = new IntOrString();
v.setValue(8888);
Service pingService = new ServiceBuilder().withNewMetadata().withLabels(createCommonLabels(config)).withName(name + "-ping").endMetadata().
Expand Down Expand Up @@ -198,37 +215,40 @@ private static void createAppDeployment(GlowMessageWriter writer, Path target,

Map<String, String> labels = createCommonLabels(config);
labels.putAll(matchLabels);
Deployment deployment = new DeploymentBuilder().withNewMetadata().withLabels(labels).withName(name).endMetadata().
withNewSpec().withReplicas(ha ? 2 : 1).
withNewSelector().withMatchLabels(matchLabels).endSelector().
withNewTemplate().withNewMetadata().withLabels(labels).endMetadata().withNewSpec().
withContainers(container).withRestartPolicy("Always").
endSpec().endTemplate().withNewStrategy().withType("RollingUpdate").endStrategy().endSpec().build();
osClient.resources(Deployment.class).resource(deployment).createOr(NonDeletingOperation::update);
Utils.persistResource(target, deployment, name + "-deployment.yaml");
IntOrString v = new IntOrString();
v.setValue(8080);
Service service = new ServiceBuilder().withNewMetadata().withLabels(createCommonLabels(config)).withName(name).endMetadata().
withNewSpec().withPorts(new ServicePort().toBuilder().withProtocol("TCP").
withPort(8080).
withTargetPort(v).build()).withType("ClusterIP").withSessionAffinity("None").withSelector(matchLabels).endSpec().build();
osClient.services().resource(service).createOr(NonDeletingOperation::update);
Utils.persistResource(target, service, name + "-service.yaml");

writer.info("\nWaiting until the application is ready ...");
osClient.resources(Deployment.class).resource(deployment).waitUntilReady(5, TimeUnit.MINUTES);
writer.info("\nWaiting until the application " + deploymentKind + " is ready ...");
if (ha) {
StatefulSet deployment = new StatefulSetBuilder().withNewMetadata().withLabels(labels).withName(name).endMetadata().
withNewSpec().withReplicas(ha ? 2 : 1).
withNewSelector().withMatchLabels(matchLabels).endSelector().
withNewTemplate().withNewMetadata().withLabels(labels).endMetadata().withNewSpec().
withContainers(container).withRestartPolicy("Always").
endSpec().endTemplate().withNewUpdateStrategy().withType("RollingUpdate").endUpdateStrategy().endSpec().build();
osClient.resources(StatefulSet.class).resource(deployment).createOr(NonDeletingOperation::update);
Utils.persistResource(target, deployment, name + "-statefulset.yaml");
osClient.resources(StatefulSet.class).resource(deployment).waitUntilReady(5, TimeUnit.MINUTES);
} else {
Deployment deployment = new DeploymentBuilder().withNewMetadata().withLabels(labels).withName(name).endMetadata().
withNewSpec().withReplicas(ha ? 2 : 1).
withNewSelector().withMatchLabels(matchLabels).endSelector().
withNewTemplate().withNewMetadata().withLabels(labels).endMetadata().withNewSpec().
withContainers(container).withRestartPolicy("Always").
endSpec().endTemplate().withNewStrategy().withType("RollingUpdate").endStrategy().endSpec().build();
osClient.resources(Deployment.class).resource(deployment).createOr(NonDeletingOperation::update);
Utils.persistResource(target, deployment, name + "-deployment.yaml");
osClient.resources(Deployment.class).resource(deployment).waitUntilReady(5, TimeUnit.MINUTES);
}
}

public static ConfigurationResolver.ResolvedEnvs getResolvedEnvs(Layer layer, Set<Env> input, Set<String> disabledDeployers) throws Exception {
ConfigurationResolver.ResolvedEnvs resolved = null;
List<Deployer> deployers = getEnabledDeployers(disabledDeployers);
for (Deployer d : deployers) {
if (d.getSupportedLayers().contains(layer.getName())) {
Set<Env> envs = d.getResolvedEnvs(input);
if (envs != null && !envs.isEmpty()) {
resolved = new ConfigurationResolver.ResolvedEnvs("openshift/"+d.getName(), envs);
break;
}
Set<Env> envs = d.getResolvedEnvs(input);
if (envs != null && !envs.isEmpty()) {
resolved = new ConfigurationResolver.ResolvedEnvs("openshift/" + d.getName(), envs);
break;
}
}
}
return resolved;
Expand Down Expand Up @@ -262,13 +282,56 @@ private static List<Deployer> getEnabledDeployers(Set<String> disabledDeployers)
List<Deployer> deployers = new ArrayList<>();
for (Deployer d : existingDeployers.values()) {
boolean isDisabled = isDisabled(d.getName(), disabledDeployers);
if(!isDisabled) {
if (!isDisabled) {
deployers.add(d);
}
}
return deployers;
}

static final String generateValidName(String name) {
name = name.toLowerCase();
StringBuilder validName = new StringBuilder();
char[] array = name.toCharArray();
for (int i = 0; i < array.length; i++) {
char c = array[i];
// start with an alphabetic character
if (i == 0) {
if (c <= 97 || c >= 122) {
validName.append("app-");
}
validName.append(c);
} else {
// end with an alphabetic character
if (i == array.length - 1) {
if ((c >= 48 && c <= 57) || (c >= 97 && c <= 122)) {
validName.append(c);
} else {
validName.append('0');
}
} else {
// - allowed in the middle
if (c == '-') {
validName.append(c);
} else {
// a-z or 0-9
if ((c >= 48 && c <= 57) || (c >= 97 && c <= 122)) {
validName.append(c);
} else {
// Other character are replaced by -
validName.append('-');
}
}
}
}
}
String ret = validName.toString();
if (ret.length() > 63) {
ret = ret.substring(0, 63);
}
return ret;
}

public static void deploy(List<Path> deployments,
String defaultName,
GlowMessageWriter writer,
Expand All @@ -292,11 +355,12 @@ public static void deploy(List<Path> deployments,
Files.createDirectories(deploymentsDir);
for (Path p : deployments) {
Files.copy(p, deploymentsDir.resolve(p.getFileName()));
int ext = p.getFileName().toString().indexOf(".");
int ext = p.getFileName().toString().lastIndexOf(".");
appName += p.getFileName().toString().substring(0, ext);
appName = generateValidName(appName);
}
if (appName.isEmpty()) {
appName = defaultName;
appName = defaultName;
}
Map<String, String> env = new HashMap<>();
for (Set<Env> envs : scanResults.getSuggestions().getStronglySuggestedConfigurations().values()) {
Expand Down Expand Up @@ -331,11 +395,11 @@ public static void deploy(List<Path> deployments,
} else {
writer.warn("\nThe deployer " + d.getName() + " has been disabled");
}
actualEnv.putAll(isDisabled ? Collections.emptyMap(): d.deploy(writer, target, osClient, env, host, appName, l.getName(), extraEnv));
Set<Env> buildEnv = requiredBuildTime.get(l);
actualEnv.putAll(isDisabled ? Collections.emptyMap() : d.deploy(writer, target, osClient, env, host, appName, l.getName(), extraEnv));
Set<Env> buildEnv = requiredBuildTime.get(l);
if (buildEnv != null) {
Set<String> names = new HashSet<>();
for(Env e : buildEnv) {
for (Env e : buildEnv) {
if (!buildExtraEnv.containsKey(e.getName())) {
names.add(e.getName());
}
Expand Down Expand Up @@ -365,10 +429,11 @@ public static void deploy(List<Path> deployments,
actualEnv.put("SERVER_ARGS", val);
}
}
String deploymentKind = ha ? "StatefulSet" : "Deployment";
if (!disabledDeployers.isEmpty()) {
writer.warn("The following environment variables will be set in the " + appName + " deployment. Make sure that the required env variables for the disabled deployer(s) have been set:\n");
writer.warn("The following environment variables will be set in the " + appName + " " + deploymentKind + ". Make sure that the required env variables for the disabled deployer(s) have been set:\n");
} else {
writer.warn("The following environment variables will be set in the " + appName + " deployment:\n");
writer.warn("The following environment variables will be set in the " + appName + " " + deploymentKind + ":\n");
}
if (ha) {
actualEnv.put("JGROUPS_PING_PROTOCOL", "openshift.DNS_PING");
Expand All @@ -382,7 +447,7 @@ public static void deploy(List<Path> deployments,
actualBuildEnv.putAll(buildExtraEnv);
createBuild(writer, target, osClient, appName, initScript, cliScript, actualBuildEnv, config, serverImageBuildLabels);
writer.info("Deploying application image on OpenShift");
createAppDeployment(writer, target, osClient, appName, actualEnv, ha, config);
createAppDeployment(writer, target, osClient, appName, actualEnv, ha, config, deploymentKind);
writer.info("Application route: https://" + host + ("ROOT.war".equals(appName) ? "" : "/" + appName));
}

Expand Down Expand Up @@ -529,7 +594,7 @@ private static String doServerImageBuild(GlowMessageWriter writer, Path target,
writer.warn("\nThe following environment variables have been set in the " + serverImageName + " buildConfig:\n");
for (Map.Entry<String, String> entry : buildExtraEnv.entrySet()) {
String val = buildExtraEnv.get(entry.getKey());
writer.warn(entry.getKey()+"="+entry.getValue());
writer.warn(entry.getKey() + "=" + entry.getValue());
vars.add(new EnvVar().toBuilder().withName(entry.getKey()).withValue(val == null ? entry.getValue() : val).build());
}
}
Expand All @@ -553,7 +618,7 @@ private static String doServerImageBuild(GlowMessageWriter writer, Path target,
try (Watch watcher = osClient.builds().withName(build.getMetadata().getName()).watch(buildWatcher)) {
buildWatcher.await();
}
if(buildWatcher.isFailed()) {
if (buildWatcher.isFailed()) {
osClient.imageStreams().resource(stream).delete();
throw new Exception("Server image build has failed. Check the OpenShift build log.");
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/*
* 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 org.junit.Assert;
import org.junit.Test;

/**
*
* @author jdenise
*/
public class OpenShiftSupportTestCase {

@Test
public void testAppName() {
{
String name = "foo";
Assert.assertEquals(name, OpenShiftSupport.generateValidName(name));
}
{
String name = "web-1.0";
Assert.assertEquals("web-1-0", OpenShiftSupport.generateValidName(name));
}
{
String name = "990-web-1.0";
Assert.assertEquals("app-990-web-1-0", OpenShiftSupport.generateValidName(name));
}
{
String name = "-web-1.0-foo";
Assert.assertEquals("app--web-1-0-foo", OpenShiftSupport.generateValidName(name));
}
{
String name = "web$970*";
Assert.assertEquals("web-9700", OpenShiftSupport.generateValidName(name));
}
{
String name = "web_970_456_foo";
Assert.assertEquals("web-970-456-foo", OpenShiftSupport.generateValidName(name));
}
{
String name = "ROOT";
Assert.assertEquals("root", OpenShiftSupport.generateValidName(name));
}
{
String name = "0123456789012345678901234567890123456789012345678901234567890123456789";
Assert.assertEquals("app-01234567890123456789012345678901234567890123456789012345678", OpenShiftSupport.generateValidName(name));
}
}
}

0 comments on commit 44ca0f2

Please sign in to comment.