Skip to content

Commit

Permalink
Support Multiple Profiles.
Browse files Browse the repository at this point in the history
  • Loading branch information
radcortez committed Oct 21, 2020
1 parent 26bf3d7 commit 39e8da9
Show file tree
Hide file tree
Showing 16 changed files with 317 additions and 17 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -325,7 +325,7 @@ static final class GenerateOperation implements AutoCloseable {
clinit = cc.getMethodCreator(MethodDescriptor.ofMethod(CONFIG_CLASS_NAME, "<clinit>", void.class));
clinit.setModifiers(Opcodes.ACC_STATIC);

clinit.invokeStaticMethod(PM_SET_RUNTIME_DEFAULT_PROFILE, clinit.load(ProfileManager.getActiveProfile()));
clinit.invokeStaticMethod(PM_SET_RUNTIME_DEFAULT_PROFILE, clinit.load(ProfileManager.getProfileConfiguration()));
clinitNameBuilder = clinit.newInstance(SB_NEW);
clinit.invokeVirtualMethod(SB_APPEND_STRING, clinitNameBuilder, clinit.load("quarkus"));

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ public static void run(Application application, Class<? extends QuarkusApplicati
}
} else {
applicationLogger.errorv(rootCause, "Failed to start application (with profile {0})",
ProfileManager.getActiveProfile());
ProfileManager.getActiveProfiles());
}
}
stateLock.lock();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ public static SmallRyeConfigBuilder configBuilder(final boolean runTime, final b
final ApplicationPropertiesConfigSource.InJar inJar = new ApplicationPropertiesConfigSource.InJar();
final ApplicationPropertiesConfigSource.MpConfigInJar mpConfig = new ApplicationPropertiesConfigSource.MpConfigInJar();
builder.withSources(inFileSystem, inJar, mpConfig, new DotEnvConfigSource());
builder.withProfile(ProfileManager.getActiveProfile());
builder.withProfile(ProfileManager.getProfileConfiguration());
builder.addDefaultInterceptors();
final ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
if (runTime) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
package io.quarkus.runtime.configuration;

import static io.smallrye.config.Converters.newCollectionConverter;
import static io.smallrye.config.Converters.newEmptyValueConverter;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import org.eclipse.microprofile.config.spi.Converter;

import io.quarkus.runtime.LaunchMode;

/**
Expand All @@ -13,7 +22,6 @@
* - The QUARKUS_PROFILE environment entry
* - The default runtime profile provided during build
* - The default property for the launch mode
*
*/
public class ProfileManager {

Expand All @@ -23,6 +31,9 @@ public class ProfileManager {
private static final String BACKWARD_COMPATIBLE_QUARKUS_PROFILE_PROP = "quarkus-profile";

private static volatile LaunchMode launchMode = LaunchMode.NORMAL;

// This one is used to record the build profile, so when running native, the runtime uses the same profile and
// not default to prod. See https://github.com/quarkusio/quarkus/issues/3147
private static String runtimeDefaultProfile = null;

public static void setLaunchMode(LaunchMode mode) {
Expand All @@ -37,7 +48,33 @@ public static void setRuntimeDefaultProfile(final String profile) {
runtimeDefaultProfile = profile;
}

/**
* Return the active profile. If multiple profiles are active, the returned profile is a single {@code String} with
* each profile name separated with a comma @{code ","}.
*
* @deprecated this method does not properly return the active profile, since multiple profile may be active.
* Prefer to use {@link #getActiveProfiles()}.
*
* @see #getActiveProfiles()
* @return the name of the active profile.
*/
@Deprecated
public static String getActiveProfile() {
return String.join(",", getActiveProfiles());
}

/**
* Lookups the profile or profiles to use from an accepted list of environment variables or system properties.
* <p>
* Multiple profiles may be set in any of these sources, by separating each profile with a comma @{code ","}.
*
* @see #QUARKUS_PROFILE_ENV
* @see #QUARKUS_PROFILE_PROP
* @see #QUARKUS_TEST_PROFILE_PROP
* @see #BACKWARD_COMPATIBLE_QUARKUS_PROFILE_PROP
* @return the profile configuration.
*/
public static String getProfileConfiguration() {
if (launchMode == LaunchMode.TEST) {
String profile = System.getProperty(QUARKUS_TEST_PROFILE_PROP);
if (profile != null) {
Expand Down Expand Up @@ -69,4 +106,21 @@ public static String getActiveProfile() {
return launchMode.getDefaultProfile();
}

/**
* Returns the active list of profiles.
*
* @return a list of names with all the active profiles.
*/
public static List<String> getActiveProfiles() {
List<String> profiles = PROFILE_CONVERTER.convert(getProfileConfiguration());
Collections.reverse(profiles);
return profiles;
}

public static boolean isProfileActive(final String profile) {
return getActiveProfiles().contains(profile);
}

private static final Converter<List<String>> PROFILE_CONVERTER = newCollectionConverter(
newEmptyValueConverter(value -> value), ArrayList::new);
}
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ private SmallRyeConfigBuilder configBuilder(String... keyValues) {
return new SmallRyeConfigBuilder()
.addDefaultInterceptors()
.withSources(new PropertiesConfigSource(maps(keyValues), "test input", 500))
.withProfile(ProfileManager.getActiveProfile());
.withProfile(ProfileManager.getProfileConfiguration());
}

private SmallRyeConfig buildConfig(String... keyValues) {
Expand Down
14 changes: 12 additions & 2 deletions docs/src/main/asciidoc/config.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -483,9 +483,17 @@ quarkus.http.port=9090

And then set the `QUARKUS_PROFILE` environment variable to `staging` to activate my profile.

==== Multiple Profiles

Multiple Profiles may be active at the same time. The configuration `quarkus.profile` or `QUARKUS_PROFILE` accepts a
comma-separated list of profile names.

When multiple profiles are active, the rules for profile configuration are exactly the same. If two profiles define the
same configuration, then the last listed profile has priority.

[NOTE]
====
The proper way to check the active profile programmatically is to use the `getActiveProfile` method of `io.quarkus.runtime.configuration.ProfileManager`.
The proper way to check the active profile programmatically is to use the `isProfileActive` method of `io.quarkus.runtime.configuration.ProfileManager`.
Using `@ConfigProperty("quarkus.profile")` will *not* work properly.
====
Expand Down Expand Up @@ -816,7 +824,7 @@ quarkus:
In general, `null` YAML keys are not included in assembly of the configuration property name, allowing them to be used to
any level for disambiguating configuration keys.

== More info on how to configure
== Additional Configuration Features

Quarkus relies on SmallRye Config and inherits its features.

Expand All @@ -829,6 +837,8 @@ SmallRye Config provides:
* Fallback Configuration Properties
* Logging
* Hide Secrets
* Config Mappings
* Multiple Profiles

For more information, please check the
link:https://smallrye.io/docs/smallrye-config/index.html[SmallRye Config documentation].
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ void ifBuildProfile(CombinedIndexBuildItem index, BuildProducer<BuildTimeConditi
Collection<AnnotationInstance> annotationInstances = index.getIndex().getAnnotations(IF_BUILD_PROFILE);
for (AnnotationInstance instance : annotationInstances) {
String profileOnInstance = instance.value().asString();
boolean enabled = profileOnInstance.equals(ProfileManager.getActiveProfile());
boolean enabled = ProfileManager.isProfileActive(profileOnInstance);
if (enabled) {
LOGGER.debug("Enabling " + instance + " since the profile value matches the active profile.");
} else {
Expand All @@ -58,7 +58,7 @@ void unlessBuildProfile(CombinedIndexBuildItem index, BuildProducer<BuildTimeCon
Collection<AnnotationInstance> annotationInstances = index.getIndex().getAnnotations(UNLESS_BUILD_PROFILE);
for (AnnotationInstance instance : annotationInstances) {
String profileOnInstance = instance.value().asString();
boolean enabled = !profileOnInstance.equals(ProfileManager.getActiveProfile());
boolean enabled = !ProfileManager.isProfileActive(profileOnInstance);
if (enabled) {
LOGGER.debug("Enabling " + instance + " since the profile value does not match the active profile.");
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ public Map<String, String> createUserTransaction() {
@Transactional
@Path("/import")
public Map<String, String> get() {
boolean isProdMode = ProfileManager.getActiveProfile().equals(LaunchMode.NORMAL.getDefaultProfile());
boolean isProdMode = ProfileManager.isProfileActive(LaunchMode.NORMAL.getDefaultProfile());
Gift gift = em.find(Gift.class, 100000L);
Map<String, String> map = new HashMap<>();
// Native tests are run under the 'prod' profile for now. In NORMAL mode, Quarkus doesn't execute the SQL import file
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,14 +35,14 @@ private void resetProfileManagerState() {

@Test
public void testDefaultTestProfile() {
Assertions.assertEquals(LaunchMode.TEST.getDefaultProfile(), ProfileManager.getActiveProfile());
Assertions.assertTrue(ProfileManager.isProfileActive(LaunchMode.TEST.getDefaultProfile()));
}

@Test
public void testCustomTestProfile() {
String customProfile = "foo";
System.setProperty(ProfileManager.QUARKUS_TEST_PROFILE_PROP, customProfile);
Assertions.assertEquals(customProfile, ProfileManager.getActiveProfile());
Assertions.assertTrue(ProfileManager.isProfileActive(customProfile));
}

@Test
Expand All @@ -60,7 +60,7 @@ private void testCustomProfile(LaunchMode launchMode) {
ProfileManager.setRuntimeDefaultProfile("foo");
String customProfile = "bar";
System.setProperty(ProfileManager.QUARKUS_PROFILE_PROP, customProfile);
Assertions.assertEquals(customProfile, ProfileManager.getActiveProfile());
Assertions.assertTrue(ProfileManager.isProfileActive(customProfile));
}

@Test
Expand All @@ -78,7 +78,7 @@ private void testBackwardCompatibleCustomProfile(LaunchMode launchMode) {
ProfileManager.setRuntimeDefaultProfile("foo");
String customProfile = "bar";
System.setProperty(BACKWARD_COMPATIBLE_QUARKUS_PROFILE_PROP, customProfile);
Assertions.assertEquals(customProfile, ProfileManager.getActiveProfile());
Assertions.assertTrue(ProfileManager.isProfileActive(customProfile));
}

@Test
Expand All @@ -95,7 +95,7 @@ private void testCustomRuntimeProfile(LaunchMode launchMode) {
ProfileManager.setLaunchMode(launchMode);
String customProfile = "foo";
ProfileManager.setRuntimeDefaultProfile(customProfile);
Assertions.assertEquals(customProfile, ProfileManager.getActiveProfile());
Assertions.assertTrue(ProfileManager.isProfileActive(customProfile));
}

@Test
Expand All @@ -110,6 +110,6 @@ public void testDefaultDevProfile() {

private void testDefaultProfile(LaunchMode launchMode) {
ProfileManager.setLaunchMode(launchMode);
Assertions.assertEquals(launchMode.getDefaultProfile(), ProfileManager.getActiveProfile());
Assertions.assertTrue(ProfileManager.isProfileActive(launchMode.getDefaultProfile()));
}
}
31 changes: 30 additions & 1 deletion integration-tests/smallrye-config/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@
<scope>test</scope>
</dependency>


<!-- Minimal test dependencies to *-deployment artifacts for consistent build order -->
<dependency>
<groupId>io.quarkus</groupId>
Expand Down Expand Up @@ -83,6 +82,15 @@
<artifactId>maven-compiler-plugin</artifactId>
<version>${compiler-plugin.version}</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<systemPropertyVariables>
<quarkus.test.profile>common,test</quarkus.test.profile>
</systemPropertyVariables>
</configuration>
</plugin>
</plugins>
</build>

Expand All @@ -96,6 +104,24 @@
</activation>
<build>
<plugins>
<!-- This makes sure that the native binary builds with the common and prod profiles -->
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>properties-maven-plugin</artifactId>
<version>1.0.0</version>
<executions>
<execution>
<goals>
<goal>set-system-properties</goal>
</goals>
<configuration>
<properties>
<quarkus.profile>common,prod</quarkus.profile>
</properties>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
Expand All @@ -112,6 +138,9 @@
<goal>verify</goal>
</goals>
<configuration>
<environmentVariables>
<QUARKUS_PROFILE>common,prod</QUARKUS_PROFILE>
</environmentVariables>
<systemPropertyVariables>
<native.image.path>${project.build.directory}/${project.build.finalName}-runner</native.image.path>
</systemPropertyVariables>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package io.quarkus.it.smallrye.config;

import javax.inject.Inject;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;

import org.eclipse.microprofile.config.Config;

import io.quarkus.runtime.annotations.RegisterForReflection;
import io.smallrye.config.ConfigValue;
import io.smallrye.config.SmallRyeConfig;

@Path("/config")
@Produces(MediaType.APPLICATION_JSON)
public class ConfigResource {
@Inject
Config config;

@GET
@Path("/{name}")
public Response configValue(@PathParam("name") final String name) {
final io.smallrye.config.ConfigValue configValue = ((SmallRyeConfig) config).getConfigValue(name);
return Response.ok(new ConfigValue(configValue.getName(), configValue.getValue())).build();
}

@RegisterForReflection
public static class ConfigValue {
final String name;
final String value;

public ConfigValue(final String name, final String value) {
this.name = name;
this.value = value;
}

public String getName() {
return name;
}

public String getValue() {
return value;
}
}
}
Loading

0 comments on commit 39e8da9

Please sign in to comment.