From 1320c44a25537990e3ec0a31717970c26bb4c2e3 Mon Sep 17 00:00:00 2001 From: Brian Clozel Date: Fri, 3 Jan 2020 09:45:44 +0100 Subject: [PATCH] Use RBAC credentials for Couchbase cluster info Prior to this commit, the Couchbase auto-configuration would use the bucket credentials when RBAC is configured. This commit ensures that RBAC is used in that case. This commit also adds new configuration properties to customize the bootstrap ports for Couchbase: * `spring.couchbase.env.bootstrap.http-direct-port` * `spring.couchbase.env.bootstrap.http-ssl-port` Fixes gh-19393 --- .../spring-boot-autoconfigure/pom.xml | 5 ++ .../couchbase/CouchbaseConfiguration.java | 13 +++- .../couchbase/CouchbaseProperties.java | 39 +++++++++- ...baseAutoConfigurationIntegrationTests.java | 67 ++++++++++++----- .../couchbase/LocalCouchbaseServer.java | 72 ------------------- 5 files changed, 104 insertions(+), 92 deletions(-) delete mode 100644 spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/couchbase/LocalCouchbaseServer.java diff --git a/spring-boot-project/spring-boot-autoconfigure/pom.xml b/spring-boot-project/spring-boot-autoconfigure/pom.xml index 70f9e1349a..cd7633bb0e 100755 --- a/spring-boot-project/spring-boot-autoconfigure/pom.xml +++ b/spring-boot-project/spring-boot-autoconfigure/pom.xml @@ -957,6 +957,11 @@ cassandra test + + org.testcontainers + couchbase + test + org.testcontainers elasticsearch diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/couchbase/CouchbaseConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/couchbase/CouchbaseConfiguration.java index dca5184964..cee5ec6580 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/couchbase/CouchbaseConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/couchbase/CouchbaseConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2020 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -37,6 +37,7 @@ import org.springframework.context.annotation.Primary; * Support class to configure Couchbase based on {@link CouchbaseProperties}. * * @author Stephane Nicoll + * @author Brian Clozel * @since 2.1.0 */ @Configuration @@ -76,6 +77,9 @@ public class CouchbaseConfiguration { @Primary @DependsOn("couchbaseClient") public ClusterInfo couchbaseClusterInfo() { + if (isRoleBasedAccessControlEnabled()) { + return couchbaseCluster().clusterManager().info(); + } return couchbaseCluster() .clusterManager(this.properties.getBucket().getName(), this.properties.getBucket().getPassword()) .info(); @@ -103,7 +107,14 @@ public class CouchbaseConfiguration { protected DefaultCouchbaseEnvironment.Builder initializeEnvironmentBuilder(CouchbaseProperties properties) { CouchbaseProperties.Endpoints endpoints = properties.getEnv().getEndpoints(); CouchbaseProperties.Timeouts timeouts = properties.getEnv().getTimeouts(); + CouchbaseProperties.Bootstrap bootstrap = properties.getEnv().getBootstrap(); DefaultCouchbaseEnvironment.Builder builder = DefaultCouchbaseEnvironment.builder(); + if (bootstrap.getHttpDirectPort() != null) { + builder.bootstrapHttpDirectPort(bootstrap.getHttpDirectPort()); + } + if (bootstrap.getHttpSslPort() != null) { + builder.bootstrapHttpSslPort(bootstrap.getHttpSslPort()); + } if (timeouts.getConnect() != null) { builder = builder.connectTimeout(timeouts.getConnect().toMillis()); } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/couchbase/CouchbaseProperties.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/couchbase/CouchbaseProperties.java index e7080ec5af..21d548eea1 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/couchbase/CouchbaseProperties.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/couchbase/CouchbaseProperties.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2020 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -28,6 +28,7 @@ import org.springframework.util.StringUtils; * @author EddĂș MelĂ©ndez * @author Stephane Nicoll * @author Yulin Qin + * @author Brian Clozel * @since 1.4.0 */ @ConfigurationProperties(prefix = "spring.couchbase") @@ -116,12 +117,18 @@ public class CouchbaseProperties { public static class Env { + private final Bootstrap bootstrap = new Bootstrap(); + private final Endpoints endpoints = new Endpoints(); private final Ssl ssl = new Ssl(); private final Timeouts timeouts = new Timeouts(); + public Bootstrap getBootstrap() { + return this.bootstrap; + } + public Endpoints getEndpoints() { return this.endpoints; } @@ -314,4 +321,34 @@ public class CouchbaseProperties { } + public static class Bootstrap { + + /** + * Port for the HTTP bootstrap. + */ + private Integer httpDirectPort; + + /** + * Port for the HTTPS bootstrap. + */ + private Integer httpSslPort; + + public Integer getHttpDirectPort() { + return this.httpDirectPort; + } + + public void setHttpDirectPort(Integer httpDirectPort) { + this.httpDirectPort = httpDirectPort; + } + + public Integer getHttpSslPort() { + return this.httpSslPort; + } + + public void setHttpSslPort(Integer httpSslPort) { + this.httpSslPort = httpSslPort; + } + + } + } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/couchbase/CouchbaseAutoConfigurationIntegrationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/couchbase/CouchbaseAutoConfigurationIntegrationTests.java index d11bc5eda0..9a2549e3a0 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/couchbase/CouchbaseAutoConfigurationIntegrationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/couchbase/CouchbaseAutoConfigurationIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2020 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,17 +16,24 @@ package org.springframework.boot.autoconfigure.couchbase; +import java.time.Duration; + import com.couchbase.client.java.Bucket; import com.couchbase.client.java.Cluster; import com.couchbase.client.java.CouchbaseBucket; +import com.couchbase.client.java.bucket.BucketType; import com.couchbase.client.java.cluster.ClusterInfo; +import com.couchbase.client.java.cluster.DefaultBucketSettings; import com.couchbase.client.java.env.CouchbaseEnvironment; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; +import org.testcontainers.couchbase.CouchbaseContainer; +import org.testcontainers.junit.jupiter.Container; +import org.testcontainers.junit.jupiter.Testcontainers; -import org.springframework.boot.autoconfigure.AutoConfigurations; -import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration; -import org.springframework.boot.test.context.runner.ApplicationContextRunner; +import org.springframework.boot.test.util.TestPropertyValues; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -37,29 +44,53 @@ import static org.mockito.Mockito.mock; * Integration tests for {@link CouchbaseAutoConfiguration}. * * @author Stephane Nicoll + * @author Brian Clozel */ -@ExtendWith(LocalCouchbaseServer.class) +@Testcontainers(disabledWithoutDocker = true) class CouchbaseAutoConfigurationIntegrationTests { - private ApplicationContextRunner contextRunner = new ApplicationContextRunner().withConfiguration( - AutoConfigurations.of(PropertyPlaceholderAutoConfiguration.class, CouchbaseAutoConfiguration.class)); + @Container + static final CouchbaseContainer couchbase = new CouchbaseContainer().withClusterAdmin("spring", "password") + .withNewBucket(DefaultBucketSettings.builder().enableFlush(true).name("default").password("secret") + .quota(100).replicas(0).type(BucketType.COUCHBASE).build()) + .withStartupAttempts(5).withStartupTimeout(Duration.ofMinutes(2)); + + private AnnotationConfigApplicationContext context; + + @BeforeEach + void setUp() { + this.context = new AnnotationConfigApplicationContext(); + this.context.register(CouchbaseAutoConfiguration.class); + TestPropertyValues.of("spring.couchbase.bootstrap-hosts=localhost", + "spring.couchbase.env.bootstrap.http-direct-port:" + couchbase.getMappedPort(8091), + "spring.couchbase.username:spring", "spring.couchbase.password:password", + "spring.couchbase.bucket.name:default").applyTo(this.context.getEnvironment()); + } + + @AfterEach + void close() { + if (this.context != null) { + this.context.close(); + } + } @Test void defaultConfiguration() { - this.contextRunner.withPropertyValues("spring.couchbase.bootstrapHosts=localhost") - .run((context) -> assertThat(context).hasSingleBean(Cluster.class).hasSingleBean(ClusterInfo.class) - .hasSingleBean(CouchbaseEnvironment.class).hasSingleBean(Bucket.class)); + this.context.refresh(); + assertThat(this.context.getBeansOfType(Cluster.class)).hasSize(1); + assertThat(this.context.getBeansOfType(ClusterInfo.class)).hasSize(1); + assertThat(this.context.getBeansOfType(CouchbaseEnvironment.class)).hasSize(1); + assertThat(this.context.getBeansOfType(Bucket.class)).hasSize(1); } @Test void customConfiguration() { - this.contextRunner.withUserConfiguration(CustomConfiguration.class) - .withPropertyValues("spring.couchbase.bootstrapHosts=localhost").run((context) -> { - assertThat(context.getBeansOfType(Cluster.class)).hasSize(2); - assertThat(context.getBeansOfType(ClusterInfo.class)).hasSize(1); - assertThat(context.getBeansOfType(CouchbaseEnvironment.class)).hasSize(1); - assertThat(context.getBeansOfType(Bucket.class)).hasSize(2); - }); + this.context.register(CustomConfiguration.class); + this.context.refresh(); + assertThat(this.context.getBeansOfType(Cluster.class)).hasSize(2); + assertThat(this.context.getBeansOfType(ClusterInfo.class)).hasSize(1); + assertThat(this.context.getBeansOfType(CouchbaseEnvironment.class)).hasSize(1); + assertThat(this.context.getBeansOfType(Bucket.class)).hasSize(2); } @Configuration(proxyBeanMethods = false) diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/couchbase/LocalCouchbaseServer.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/couchbase/LocalCouchbaseServer.java deleted file mode 100644 index 80103bb32c..0000000000 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/couchbase/LocalCouchbaseServer.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright 2012-2019 the original author or authors. - * - * 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 - * - * https://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.springframework.boot.autoconfigure.couchbase; - -import java.util.concurrent.TimeUnit; - -import com.couchbase.client.java.Bucket; -import com.couchbase.client.java.Cluster; -import com.couchbase.client.java.CouchbaseCluster; -import com.couchbase.client.java.env.CouchbaseEnvironment; -import com.couchbase.client.java.env.DefaultCouchbaseEnvironment; -import org.junit.jupiter.api.extension.ConditionEvaluationResult; -import org.junit.jupiter.api.extension.ExecutionCondition; -import org.junit.jupiter.api.extension.Extension; -import org.junit.jupiter.api.extension.ExtensionContext; -import org.junit.jupiter.api.extension.TestExecutionExceptionHandler; - -import org.springframework.beans.factory.BeanCreationException; - -/** - * {@link Extension} for working with an optional Couchbase server. Expects a default - * {@link Bucket} with no password to be available on localhost. - * - * @author Stephane Nicoll - * @author Andy Wilkinson - */ -class LocalCouchbaseServer implements ExecutionCondition, TestExecutionExceptionHandler { - - @Override - public ConditionEvaluationResult evaluateExecutionCondition(ExtensionContext context) { - try { - CouchbaseEnvironment environment = DefaultCouchbaseEnvironment.create(); - Cluster cluster = CouchbaseCluster.create(environment, "localhost"); - testConnection(cluster); - cluster.disconnect(); - environment.shutdownAsync(); - return ConditionEvaluationResult.enabled("Local Couchbase server available"); - } - catch (Exception ex) { - return ConditionEvaluationResult.disabled("Local Couchbase server not available"); - } - } - - private static void testConnection(Cluster cluster) { - Bucket bucket = cluster.openBucket(2, TimeUnit.SECONDS); - bucket.close(); - } - - @Override - public void handleTestExecutionException(ExtensionContext context, Throwable ex) throws Throwable { - if ((ex instanceof BeanCreationException) - && "couchbaseClient".equals(((BeanCreationException) ex).getBeanName())) { - return; - } - throw ex; - } - -}