Skip to content

Commit

Permalink
Do not use CDI beans to read the SM
Browse files Browse the repository at this point in the history
  • Loading branch information
radcortez committed Sep 6, 2023
1 parent 1f6829d commit 9e1a12d
Show file tree
Hide file tree
Showing 9 changed files with 129 additions and 55 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@
import io.quarkiverse.googlecloudservices.common.GcpConfigHolder;
import io.quarkiverse.googlecloudservices.common.GcpCredentialProducer;
import io.quarkiverse.googlecloudservices.common.GcpCredentialProviderProducer;
import io.quarkiverse.googlecloudservices.common.GcpDefaultsConfigBuilder;
import io.quarkus.arc.deployment.AdditionalBeanBuildItem;
import io.quarkus.deployment.annotations.BuildStep;
import io.quarkus.deployment.builditem.ExtensionSslNativeSupportBuildItem;
import io.quarkus.deployment.builditem.RunTimeConfigBuilderBuildItem;
import io.quarkus.deployment.builditem.nativeimage.NativeImageConfigBuildItem;

public class CommonBuildSteps {
Expand All @@ -22,6 +24,11 @@ public ExtensionSslNativeSupportBuildItem ssl() {
return new ExtensionSslNativeSupportBuildItem("google-cloud-common");
}

@BuildStep
public RunTimeConfigBuilderBuildItem defaultsConfig() {
return new RunTimeConfigBuilderBuildItem(GcpDefaultsConfigBuilder.class);
}

/**
* Work around for https://github.com/quarkusio/quarkus/issues/25501 until
* https://github.com/oracle/graal/issues/4543 gets resolved
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package io.quarkiverse.googlecloudservices.common;

import io.quarkus.runtime.configuration.ConfigBuilder;
import io.smallrye.config.SmallRyeConfigBuilder;

public class GcpDefaultsConfigBuilder implements ConfigBuilder {
@Override
public SmallRyeConfigBuilder configBuilder(final SmallRyeConfigBuilder builder) {
return builder.withSources(new GcpDefaultsConfigSourceFactory());
}
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
package io.quarkiverse.googlecloudservices.secretmanager.deployment;

import io.quarkiverse.googlecloudservices.secretmanager.runtime.SecretManagerProducer;
import io.quarkiverse.googlecloudservices.secretmanager.runtime.config.SecretManagerConfigBuilder;
import io.quarkus.arc.deployment.AdditionalBeanBuildItem;
import io.quarkus.deployment.annotations.BuildStep;
import io.quarkus.deployment.builditem.FeatureBuildItem;
import io.quarkus.deployment.builditem.RunTimeConfigBuilderBuildItem;

public class SecretManagerBuildSteps {
private static final String FEATURE = "google-cloud-secret-manager";
Expand All @@ -17,4 +19,9 @@ public FeatureBuildItem feature() {
public AdditionalBeanBuildItem producer() {
return new AdditionalBeanBuildItem(SecretManagerProducer.class);
}

@BuildStep
public RunTimeConfigBuilderBuildItem secretManagerConfig() {
return new RunTimeConfigBuilderBuildItem(SecretManagerConfigBuilder.class);
}
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package io.quarkiverse.googlecloudservices.secretmanager.runtime.config;

import io.quarkus.runtime.configuration.ConfigBuilder;
import io.smallrye.config.SmallRyeConfigBuilder;

public class SecretManagerConfigBuilder implements ConfigBuilder {
@Override
public SmallRyeConfigBuilder configBuilder(final SmallRyeConfigBuilder builder) {
return builder.withSources(new SecretManagerConfigSourceFactory());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,30 +4,33 @@
import java.util.Map;
import java.util.Set;

import com.google.cloud.secretmanager.v1.AccessSecretVersionResponse;
import com.google.cloud.secretmanager.v1.SecretVersionName;

import io.smallrye.config.common.AbstractConfigSource;
import io.smallrye.config.common.MapBackedConfigSource;

public class SecretManagerConfigSource extends AbstractConfigSource {
public class SecretManagerConfigSource extends MapBackedConfigSource {

/** The ordinal is set to < 100 (which is the default) so that this config source is retrieved from last. */
private static final int SECRET_MANAGER_ORDINAL = 50;

private static final String CONFIG_SOURCE_NAME = "io.quarkiverse.googlecloudservices.secretmanager.runtime.config";

private final String defaultProject;
private final String projectId;

private final SecretManagerClientProvider clientProvider = new SecretManagerClientProvider();

public SecretManagerConfigSource(String defaultProject) {
super(CONFIG_SOURCE_NAME, SECRET_MANAGER_ORDINAL);
this.defaultProject = defaultProject;
public SecretManagerConfigSource(final Map<String, String> properties, final String projectId) {
super(CONFIG_SOURCE_NAME, properties, SECRET_MANAGER_ORDINAL);
this.projectId = projectId;
}

@Override
public Map<String, String> getProperties() {
return Collections.emptyMap();
public String getValue(String propertyName) {
SecretVersionName secretVersionName = SecretManagerConfigUtils.getSecretVersionName(propertyName, projectId);
if (secretVersionName == null) {
// The propertyName is not in the form "${sm//...}" so return null.
return null;
}

return super.getProperties().get(secretVersionName.toString());
}

@Override
Expand All @@ -36,14 +39,7 @@ public Set<String> getPropertyNames() {
}

@Override
public String getValue(String propertyName) {
SecretVersionName secretVersionName = SecretManagerConfigUtils.getSecretVersionName(propertyName, defaultProject);
if (secretVersionName == null) {
// The propertyName is not in the form "${sm//...}" so return null.
return null;
}

AccessSecretVersionResponse response = clientProvider.get().accessSecretVersion(secretVersionName);
return response.getPayload().getData().toStringUtf8();
public Map<String, String> getProperties() {
return Collections.emptyMap();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,30 +3,96 @@
import static java.util.Collections.emptyList;
import static java.util.Collections.singletonList;

import java.util.Optional;
import java.io.ByteArrayInputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;

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

import com.google.auth.oauth2.GoogleCredentials;
import com.google.cloud.ServiceOptions;
import com.google.cloud.secretmanager.v1.AccessSecretVersionResponse;
import com.google.cloud.secretmanager.v1.ProjectName;
import com.google.cloud.secretmanager.v1.Secret;
import com.google.cloud.secretmanager.v1.SecretManagerServiceClient;
import com.google.cloud.secretmanager.v1.SecretManagerServiceSettings;
import com.google.cloud.secretmanager.v1.SecretVersion;
import com.google.cloud.secretmanager.v1.SecretVersionName;

import io.quarkiverse.googlecloudservices.common.GcpBootstrapConfiguration;
import io.smallrye.config.ConfigSourceContext;
import io.smallrye.config.ConfigSourceFactory;
import io.smallrye.config.ConfigValue;
import io.smallrye.config.Converters;
import io.smallrye.config.SmallRyeConfig;
import io.smallrye.config.SmallRyeConfigBuilder;

public class SecretManagerConfigSourceFactory implements ConfigSourceFactory {

@Override
public Iterable<ConfigSource> getConfigSources(final ConfigSourceContext context) {
ConfigValue enableMetadataServer = context.getValue("quarkus.google.cloud.enable-metadata-server");
if (enableMetadataServer.getValue() != null) {
if (Converters.getImplicitConverter(Boolean.class).convert(enableMetadataServer.getValue())) {
String projectId = Optional.ofNullable(context.getValue("quarkus.google.cloud.project-id").getValue())
.orElse(ServiceOptions.getDefaultProjectId());
if (projectId != null) {
return singletonList(new SecretManagerConfigSource(projectId));
}
SmallRyeConfig config = new SmallRyeConfigBuilder()
.withSources(new ConfigSourceContext.ConfigSourceContextConfigSource(context))
.withMapping(GcpBootstrapConfiguration.class)
.withMappingIgnore("quarkus.**")
.build();

GcpBootstrapConfiguration gcpConfig = config.getConfigMapping(GcpBootstrapConfiguration.class);

if (gcpConfig.enableMetadataServer()) {
String projectId = gcpConfig.projectId().orElse(ServiceOptions.getDefaultProjectId());
if (projectId != null) {
return singletonList(new SecretManagerConfigSource(getProperties(gcpConfig, projectId), projectId));
}
}
return emptyList();
}

private Map<String, String> getProperties(final GcpBootstrapConfiguration config, final String projectId) {
try {
SecretManagerServiceClient client = SecretManagerServiceClient.create(
SecretManagerServiceSettings.newBuilder()
.setQuotaProjectId(projectId)
.setCredentialsProvider(() -> credentials(config))
.build());

Map<String, String> properties = new HashMap<>();
for (Secret secret : client.listSecrets(ProjectName.of(projectId)).iterateAll()) {
boolean latest = true;
for (SecretVersion secretVersion : client.listSecretVersions(secret.getName()).iterateAll()) {
AccessSecretVersionResponse accessSecretVersion = client.accessSecretVersion(secretVersion.getName());
properties.put(secretVersion.getName(), accessSecretVersion.getPayload().getData().toStringUtf8());
if (latest) {
SecretVersionName secretVersionName = SecretVersionName.parse(secretVersion.getName());
properties.put(SecretVersionName
.of(secretVersionName.getProject(), secretVersionName.getSecret(), "latest").toString(),
accessSecretVersion.getPayload().getData().toStringUtf8());
latest = false;
}
}
}

client.close();
return properties;
} catch (IOException e) {
throw new RuntimeException(e);
}
}

private static final String CLOUD_OAUTH_SCOPE = "https://www.googleapis.com/auth/cloud-platform";

private static GoogleCredentials credentials(final GcpBootstrapConfiguration config) throws IOException {
if (config.serviceAccountLocation().isPresent()) {
try (FileInputStream is = new FileInputStream(config.serviceAccountLocation().get())) {
return GoogleCredentials.fromStream(is).createScoped(CLOUD_OAUTH_SCOPE);
}
} else if (config.serviceAccountEncodedKey().isPresent()) {
byte[] decode = Base64.getDecoder().decode(config.serviceAccountEncodedKey().get());
try (ByteArrayInputStream is = new ByteArrayInputStream(decode)) {
return GoogleCredentials.fromStream(is).createScoped(CLOUD_OAUTH_SCOPE);
}
}
return GoogleCredentials.getApplicationDefault().createScoped(CLOUD_OAUTH_SCOPE);
}
}

This file was deleted.

0 comments on commit 9e1a12d

Please sign in to comment.