Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
public enum ExecutionMode {

/**
* Static initializiation.
* Static initialization.
*/
STATIC_INIT,

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@

import java.io.IOException;
import java.lang.reflect.Method;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Properties;
import java.util.function.Consumer;
Expand All @@ -21,6 +24,7 @@
import io.quarkus.bootstrap.model.ApplicationModel;
import io.quarkus.bootstrap.util.BootstrapUtils;
import io.quarkus.maven.dependency.ArtifactCoords;
import io.quarkus.maven.dependency.ResolvedDependency;
import io.quarkus.paths.PathCollection;
import io.quarkus.paths.PathList;
import io.quarkus.runtime.LaunchMode;
Expand Down Expand Up @@ -108,6 +112,9 @@ void generateCode(PathCollection sourceParents, Consumer<Path> sourceRegistrar,
// the resolver and re-resolving it as part of the test bootstrap
if (test && curatedApplication != null) {
var appModel = curatedApplication.getApplicationModel();

injectTestJvmArgsFromDependencies(appModel.getRuntimeDependencies());

closeApplication(LaunchMode.TEST);
if (isSerializeTestModel()) {
final int workspaceId = getWorkspaceId();
Expand Down Expand Up @@ -154,4 +161,79 @@ protected PathCollection getParentDirs(List<String> sourceDirs) {
private Path generatedSourcesDir(boolean test) {
return test ? buildDir().toPath().resolve("generated-test-sources") : buildDir().toPath().resolve("generated-sources");
}

private static final String QUARKUS_TEST_JVM_CONFIG = "META-INF/quarkus-test-jvm-config.properties";
private static final String XX_PREFIX = "xx.";
private static final String STD_PREFIX = "std.";

private void injectTestJvmArgsFromDependencies(Collection<ResolvedDependency> dependencies) {
List<String> args = collectTestJvmArgs(dependencies);
if (args.isEmpty()) {
return;
}
Properties properties = mavenProject().getProperties();
String argLine = properties.getProperty("argLine", "");
StringBuilder sb = new StringBuilder(argLine);
for (String arg : args) {
if (!argLine.contains(arg)) {
sb.append(" ").append(arg);
}
}
String newArgLine = sb.toString().trim();
if (!newArgLine.equals(argLine)) {
properties.setProperty("argLine", newArgLine);
if (getLog().isDebugEnabled()) {
getLog().debug("Injected test JVM args from dependencies: " + args);
}
}
}

private List<String> collectTestJvmArgs(Collection<ResolvedDependency> dependencies) {
List<String> args = new ArrayList<>();
for (ResolvedDependency dep : dependencies) {
dep.getContentTree().accept(QUARKUS_TEST_JVM_CONFIG, visit -> {
if (visit == null) {
return;
}
try (var is = Files.newInputStream(visit.getPath())) {
Properties jvmConfig = new Properties();
jvmConfig.load(is);
if (getLog().isDebugEnabled()) {
getLog().debug("Found " + QUARKUS_TEST_JVM_CONFIG + " in "
+ dep.getGroupId() + ":" + dep.getArtifactId());
}
args.addAll(parseJvmConfigProperties(jvmConfig));
} catch (IOException e) {
getLog().debug("Failed to read " + QUARKUS_TEST_JVM_CONFIG + " from "
+ dep.getGroupId() + ":" + dep.getArtifactId() + ": " + e.getMessage());
}
});
}
return args;
}

private static List<String> parseJvmConfigProperties(Properties jvmConfig) {
List<String> args = new ArrayList<>();
for (String name : jvmConfig.stringPropertyNames()) {
String value = jvmConfig.getProperty(name).trim();
if (name.startsWith(XX_PREFIX)) {
String optionName = name.substring(XX_PREFIX.length());
if ("true".equalsIgnoreCase(value)) {
args.add("-XX:+" + optionName);
} else if ("false".equalsIgnoreCase(value)) {
args.add("-XX:-" + optionName);
} else if (!value.isEmpty()) {
args.add("-XX:" + optionName + "=" + value);
}
} else if (name.startsWith(STD_PREFIX)) {
String optionName = name.substring(STD_PREFIX.length());
if (value.isEmpty()) {
args.add("--" + optionName);
} else {
args.add("--" + optionName + "=" + value);
}
}
}
return args;
}
}
32 changes: 16 additions & 16 deletions docs/src/main/asciidoc/deploying-to-openshift-s2i-howto.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ You can deploy {project-name} applications to {openshift} with Java {jdk-version
. Create a directory called `.s2i` at the same level as the `pom.xml` file.
. Create a file called `environment` in the `.s2i` directory and add the following content:
+
[source]
[source,text]
----
MAVEN_S2I_ARTIFACT_DIRS=target/quarkus-app
S2I_SOURCE_DEPLOYMENTS_FILTER=app lib quarkus quarkus-run.jar
Expand All @@ -66,20 +66,20 @@ JAVA_APP_JAR=/deployments/quarkus-run.jar
+
* Java {jdk-version-earliest}:
+
[source,terminal,subs="attributes+",options="nowrap"]
[source,shell,subs="attributes+",options="nowrap"]
----
oc import-image {name-image-ubi9-open-jdk-17-short} --from={name-image-ubi9-open-jdk-17} --confirm
----
* Java {jdk-version-other}:
+
[source,terminal,subs="attributes+",options="nowrap"]
[source,shell,subs="attributes+",options="nowrap"]
----
oc import-image {name-image-ubi9-open-jdk-21-short} --from={name-image-ubi9-open-jdk-21} --confirm
----
+
* Java {jdk-version-latest}:
+
[source,terminal,subs="attributes+",options="nowrap"]
[source,shell,subs="attributes+",options="nowrap"]
----
oc import-image {name-image-ubi9-open-jdk-25-short} --from={name-image-ubi9-open-jdk-25} --confirm
----
Expand All @@ -100,31 +100,31 @@ For information about this image, see link:https://catalog.redhat.com/en/softwar
+
* Java {jdk-version-earliest}:
+
[source,terminal,subs="attributes+",options="nowrap"]
[source,shell,subs="attributes+",options="nowrap"]
----
oc new-app registry.access.redhat.com/ubi9/openjdk-17~<git_path> --name=<project_name>
oc new-app {name-image-ubi9-open-jdk-17}~<git_path> --name=<project_name>
----
* Java {jdk-version-other}:
+
[source,terminal,subs="attributes+",options="nowrap"]
[source,shell,subs="attributes+",options="nowrap"]
----
oc new-app registry.access.redhat.com/ubi9/openjdk-21~<git_path> --name=<project_name>
oc new-app {name-image-ubi9-open-jdk-21}~<git_path> --name=<project_name>
----
+
* Java {jdk-version-latest}:
+
[source,terminal,subs="attributes+",options="nowrap"]
[source,shell,subs="attributes+",options="nowrap"]
----
oc new-app registry.access.redhat.com/ubi9/openjdk-25~<git_path> --name=<project_name>
oc new-app {name-image-ubi9-open-jdk-25}~<git_path> --name=<project_name>
----
+
.. Replace `<git_path>` with the path of the Git repository that hosts your Quarkus project.
+
For example, for Java {jdk-version-other}:
+
[source,terminal,subs="attributes+",options="nowrap"]
[source,shell,subs="attributes+",options="nowrap"]
----
oc new-app registry.access.redhat.com/ubi9/openjdk-21~https://github.com/johndoe/code-with-quarkus.git --name=code-with-quarkus
oc new-app {name-image-ubi9-open-jdk-21}~https://github.com/johndoe/code-with-quarkus.git --name=code-with-quarkus
----
{nbsp}
+
Expand All @@ -139,14 +139,14 @@ If you are deploying on IBM Z infrastructure, enter `oc new-app ubi9/openjdk-21~

. To deploy an updated version of the project, push changes to the Git repository, and then run:
+
[source,terminal,subs="attributes+",options="nowrap"]
[source,shell,options="nowrap"]
----
oc start-build <project_name>
----
+
. To expose a route to the application, run the following command:
+
[source,terminal,subs="attributes+"]
[source,shell]
----
oc expose svc <project_name>
----
Expand All @@ -155,13 +155,13 @@ oc expose svc <project_name>

. List the pods associated with your current {openshift} project:
+
[source,terminal,subs="attributes+"]
[source,shell]
----
oc get pods
----
. To get the log output for your application's pod, run the following command, replacing `<pod_name>` with the name of the latest pod prefixed by your application name:
+
[source,terminal,subs="attributes+"]
[source,shell]
----
oc logs -f <pod_name>
----
Expand Down
Original file line number Diff line number Diff line change
@@ -1,78 +1,47 @@
package io.quarkus.kubernetes.deployment;

import java.util.Optional;
import java.util.function.Predicate;

import io.dekorate.kubernetes.decorator.Decorator;
import io.dekorate.kubernetes.decorator.NamedResourceDecorator;
import io.dekorate.kubernetes.decorator.ResourceProvidingDecorator;
import io.fabric8.kubernetes.api.model.ContainerBuilder;
import io.fabric8.kubernetes.api.model.ObjectMeta;
import io.fabric8.kubernetes.api.model.PodSecurityContextBuilder;
import io.fabric8.kubernetes.api.model.PodSpecFluent;
import io.fabric8.kubernetes.api.model.SELinuxOptions;
import io.fabric8.kubernetes.api.model.SELinuxOptionsBuilder;
import io.fabric8.kubernetes.api.model.SysctlBuilder;
import io.fabric8.kubernetes.api.model.WindowsSecurityContextOptions;
import io.fabric8.kubernetes.api.model.WindowsSecurityContextOptionsBuilder;

public class ApplySecuritySettingsDecorator extends NamedResourceDecorator<PodSpecFluent> {
public class ApplySecuritySettingsDecorator extends NamedResourceDecorator<PodSpecFluent<?>> {

private final SecurityContextConfig securityContext;
private final Predicate<ContainerBuilder> hasNamedContainer = cb -> cb.getName().equals(name);

public ApplySecuritySettingsDecorator(String resourceName, SecurityContextConfig securityContext) {
super(resourceName);
this.securityContext = securityContext;
}

@Override
public void andThenVisit(PodSpecFluent podSpec, ObjectMeta resourceMeta) {
PodSecurityContextBuilder securityContextBuilder = new PodSecurityContextBuilder();

securityContext.runAsUser().ifPresent(securityContextBuilder::withRunAsUser);
securityContext.runAsGroup().ifPresent(securityContextBuilder::withRunAsGroup);
securityContext.runAsNonRoot().ifPresent(securityContextBuilder::withRunAsNonRoot);
securityContext.supplementalGroups().ifPresent(securityContextBuilder::addAllToSupplementalGroups);
securityContext.fsGroup().ifPresent(securityContextBuilder::withFsGroup);
securityContext.sysctls().entrySet().stream()
.map(entry -> new SysctlBuilder().withName(entry.getKey()).withValue(entry.getValue()).build())
.forEach(securityContextBuilder::addToSysctls);
securityContext.fsGroupChangePolicy().map(e -> e.name()).ifPresent(securityContextBuilder::withFsGroupChangePolicy);
buildSeLinuxOptions().ifPresent(securityContextBuilder::withSeLinuxOptions);
buildWindowsOptions().ifPresent(securityContextBuilder::withWindowsOptions);

podSpec.withSecurityContext(securityContextBuilder.build());
public void andThenVisit(PodSpecFluent<?> podSpec, ObjectMeta resourceMeta) {
podSpec.withSecurityContext(securityContext.buildSecurityContext());

// configure application container with security options if present
final var maybeReadOnly = securityContext.readOnlyRootFilesystem();
final var maybeEscalation = securityContext.allowPrivilegeEscalation();
if (maybeReadOnly.isPresent() || maybeEscalation.isPresent()) {
// create container if absent
if (!podSpec.hasMatchingContainer(hasNamedContainer)) {
podSpec.addNewContainer().withName(name).endContainer();
}

final var containerSecContext = podSpec.editMatchingContainer(hasNamedContainer)
.editOrNewSecurityContext();
maybeReadOnly.ifPresent(containerSecContext::withReadOnlyRootFilesystem);
maybeEscalation.ifPresent(containerSecContext::withAllowPrivilegeEscalation);
containerSecContext.endSecurityContext().endContainer();
}
}

@Override
public Class<? extends Decorator>[] after() {
return new Class[] { ResourceProvidingDecorator.class };
}

private Optional<WindowsSecurityContextOptions> buildWindowsOptions() {
WindowsSecurityContextOptions item = null;
if (securityContext.windowsOptions().isAnyPropertySet()) {
WindowsSecurityContextOptionsBuilder builder = new WindowsSecurityContextOptionsBuilder();
securityContext.windowsOptions().gmsaCredentialSpec().ifPresent(builder::withGmsaCredentialSpec);
securityContext.windowsOptions().gmsaCredentialSpecName().ifPresent(builder::withGmsaCredentialSpecName);
securityContext.windowsOptions().hostProcess().ifPresent(builder::withHostProcess);
securityContext.windowsOptions().runAsUserName().ifPresent(builder::withRunAsUserName);
item = builder.build();
}

return Optional.ofNullable(item);
}

private Optional<SELinuxOptions> buildSeLinuxOptions() {
SELinuxOptions item = null;
if (securityContext.seLinuxOptions().isAnyPropertySet()) {
SELinuxOptionsBuilder builder = new SELinuxOptionsBuilder();
securityContext.seLinuxOptions().user().ifPresent(builder::withUser);
securityContext.seLinuxOptions().role().ifPresent(builder::withRole);
securityContext.seLinuxOptions().level().ifPresent(builder::withLevel);
securityContext.seLinuxOptions().type().ifPresent(builder::withType);
item = builder.build();
}

return Optional.ofNullable(item);
}

}
Loading
Loading