diff --git a/core-microservice-framework/microservice-framework-resttemplate/src/test/java/com/netcracker/cloud/microserviceframework/application/TestApplicationOnRestTemplate.java b/core-microservice-framework/microservice-framework-resttemplate/src/test/java/com/netcracker/cloud/microserviceframework/application/TestApplicationOnRestTemplate.java index 501c933e9..8fc33588f 100644 --- a/core-microservice-framework/microservice-framework-resttemplate/src/test/java/com/netcracker/cloud/microserviceframework/application/TestApplicationOnRestTemplate.java +++ b/core-microservice-framework/microservice-framework-resttemplate/src/test/java/com/netcracker/cloud/microserviceframework/application/TestApplicationOnRestTemplate.java @@ -7,8 +7,12 @@ import org.springframework.cloud.config.client.ConfigClientAutoConfiguration; import org.springframework.context.annotation.Configuration; +import com.netcracker.cloud.security.common.DummyM2MManagerConfiguration; +import org.springframework.context.annotation.Import; + @Configuration @EnableServiceDbaasPostgresql @EnableAutoConfiguration(exclude = {DataElasticsearchAutoConfiguration.class, ConfigClientAutoConfiguration.class}) +@Import(DummyM2MManagerConfiguration.class) public class TestApplicationOnRestTemplate extends BaseApplicationOnRestTemplate { } diff --git a/core-mongo-evolution/mongo-evolution-java/src/main/java/com/netcracker/cloud/mongoevolution/java/AbstractMongoEvolution.java b/core-mongo-evolution/mongo-evolution-java/src/main/java/com/netcracker/cloud/mongoevolution/java/AbstractMongoEvolution.java index 0525dc407..f1984b15e 100644 --- a/core-mongo-evolution/mongo-evolution-java/src/main/java/com/netcracker/cloud/mongoevolution/java/AbstractMongoEvolution.java +++ b/core-mongo-evolution/mongo-evolution-java/src/main/java/com/netcracker/cloud/mongoevolution/java/AbstractMongoEvolution.java @@ -27,6 +27,7 @@ public class AbstractMongoEvolution { private static final Logger LOGGER = LoggerFactory.getLogger(AbstractMongoEvolution.class); private static final long INITIAL_VERSION = 0; + public static final String TRACKER_ID = "singleton_evolution_tracker"; public static final String TRACKER_COLLECTION = "_schema_evolution"; public static final String TRACKER_KEY_UPDATE_START = "startTime"; public static final String TRACKER_KEY_UPDATE_END = "endTime"; @@ -112,14 +113,14 @@ protected void doEvolve(AnnotationProcessor processor) throws Exception { /* insert current DB version at start */ boolean startUpdate = insertUpdateFlag(updatesTracker, currentVersion, true); if (startUpdate) { - updateFieldWithMongoCurrentDate(updatesTracker, TRACKER_KEY_UPDATE_START, null); - updateFieldWithMongoCurrentDate(updatesTracker, TRACKER_KEY_UPDATE_LAST, null); + updateFieldWithMongoCurrentDate(updatesTracker, TRACKER_KEY_UPDATE_START, new BasicDBObject("_id", TRACKER_ID)); + updateFieldWithMongoCurrentDate(updatesTracker, TRACKER_KEY_UPDATE_LAST, new BasicDBObject("_id", TRACKER_ID)); processor.applyChanges(currentVersion); /* insert expected version at finish */ finishUpdate = insertUpdateFlag(updatesTracker, expectedVersion, false); if (finishUpdate) { - updateFieldWithMongoCurrentDate(updatesTracker, TRACKER_KEY_UPDATE_END, null); + updateFieldWithMongoCurrentDate(updatesTracker, TRACKER_KEY_UPDATE_END, new BasicDBObject("_id", TRACKER_ID)); } } else { try { @@ -177,6 +178,7 @@ public static void updateFieldWithMongoCurrentDate(MongoCollection col public Document createTrackerCollectionRecord(long dateStart, long dateEnd, boolean in_progress, long version) { return new Document() + .append("_id", TRACKER_ID) .append(TRACKER_KEY_UPDATE_START, dateStart) .append(TRACKER_IN_PROGRESS, in_progress) .append(TRACKER_KEY_UPDATE_END, dateEnd) @@ -186,8 +188,11 @@ public Document createTrackerCollectionRecord(long dateStart, long dateEnd, bool public boolean isDatabaseUpdateLockAlive() { MongoCollection updatesTracker = database.getCollection(TRACKER_COLLECTION); - long lastUpdateStatusTimeMillis = 1000L * (((BsonTimestamp) updatesTracker.find(). - first().get(TRACKER_KEY_UPDATE_LAST)) + Document doc = updatesTracker.find(Filters.eq("_id", TRACKER_ID)).first(); + if (doc == null) { + return false; + } + long lastUpdateStatusTimeMillis = 1000L * (((BsonTimestamp) doc.get(TRACKER_KEY_UPDATE_LAST)) .getTime()); long currentTimeMillis = currentTimeMillis(); long millisecDiff = currentTimeMillis - lastUpdateStatusTimeMillis; @@ -197,19 +202,16 @@ public boolean isDatabaseUpdateLockAlive() { public boolean isUpdateInProgress() throws Exception { MongoCollection updatesTracker = database.getCollection(TRACKER_COLLECTION); try { - FindIterable docs = updatesTracker.find(); - Document doc; - if (!docs.iterator().hasNext()) { + Document doc = updatesTracker.find(Filters.eq("_id", TRACKER_ID)).first(); + if (doc == null) { long currentTime = currentTimeMillis(); doc = createTrackerCollectionRecord(currentTime, currentTime, false, INITIAL_VERSION); updatesTracker.createIndex(new Document(TRACKER_IN_PROGRESS, 1), new IndexOptions().unique(true)); updatesTracker.insertOne(doc); - updateFieldWithMongoCurrentDate(updatesTracker, TRACKER_KEY_UPDATE_START, null); - updateFieldWithMongoCurrentDate(updatesTracker, TRACKER_KEY_UPDATE_END, null); - } else { - doc = docs.first(); + updateFieldWithMongoCurrentDate(updatesTracker, TRACKER_KEY_UPDATE_START, new BasicDBObject("_id", TRACKER_ID)); + updateFieldWithMongoCurrentDate(updatesTracker, TRACKER_KEY_UPDATE_END, new BasicDBObject("_id", TRACKER_ID)); } return (boolean) doc.get(TRACKER_IN_PROGRESS); @@ -248,7 +250,11 @@ boolean insertUpdateFlag(MongoCollection collection, Long expectedVers newDoc.append(TRACKER_KEY_UPDATE_END, currentTime); } - Document previousDoc = collection.findOneAndUpdate(Filters.eq(TRACKER_IN_PROGRESS, !updateInProgress), + Document previousDoc = collection.findOneAndUpdate( + Filters.and( + Filters.eq("_id", TRACKER_ID), + Filters.eq(TRACKER_IN_PROGRESS, !updateInProgress) + ), new Document("$set", newDoc)); return (null == previousDoc) ? false : true; @@ -268,7 +274,7 @@ boolean insertUpdateFlag(MongoCollection collection, Long expectedVers public Long getDbCurrentVersion() { MongoCollection updatesTracker = database.getCollection(TRACKER_COLLECTION); - Document doc = updatesTracker.find().first(); + Document doc = updatesTracker.find(Filters.eq("_id", TRACKER_ID)).first(); return (Long) doc.get(TRACKER_CURRENT_VERSION); } } diff --git a/core-mongo-evolution/mongo-evolution-java/src/test/java/com/netcracker/cloud/mongoevolution/java/MongoEvolutionTest.java b/core-mongo-evolution/mongo-evolution-java/src/test/java/com/netcracker/cloud/mongoevolution/java/MongoEvolutionTest.java index 454a75cd1..6450b3bd5 100644 --- a/core-mongo-evolution/mongo-evolution-java/src/test/java/com/netcracker/cloud/mongoevolution/java/MongoEvolutionTest.java +++ b/core-mongo-evolution/mongo-evolution-java/src/test/java/com/netcracker/cloud/mongoevolution/java/MongoEvolutionTest.java @@ -1,5 +1,6 @@ package com.netcracker.cloud.mongoevolution.java; +import com.mongodb.BasicDBObject; import com.mongodb.client.MongoCollection; import com.mongodb.client.model.Filters; import org.bson.BsonTimestamp; @@ -158,15 +159,28 @@ void evolveErrorDataBase() throws Exception { @Test void isDatabaseUpdateLockAliveTest() { + // Use a generous threshold for time-based tests to avoid flakiness in CI + long gracePeriodSeconds = 5; + Document doc1 = new Document() + .append("_id", AbstractMongoEvolution.TRACKER_ID) .append(MongoEvolution.TRACKER_KEY_UPDATE_LAST, new BsonTimestamp((int) getCurrentTimeInSeconds(), 0)); mongoCollection.insertOne(doc1); - assertTrue(mongoEvolution.isDatabaseUpdateLockAlive()); + + assertTrue(mongoEvolution.isDatabaseUpdateLockAlive(), "Lock should be alive when updated just now"); + mongoCollection.deleteOne(Filters.eq("_id", doc1.get("_id"))); + + // Ensure the timestamp is definitely in the past, beyond the threshold + long pastTime = getCurrentTimeInSeconds() - (mongoEvolution.getWaitTimeMillisecForUpdateStatusTask() / 1000 + gracePeriodSeconds); Document doc2 = new Document() - .append(MongoEvolution.TRACKER_KEY_UPDATE_LAST, new BsonTimestamp((int) (getCurrentTimeInSeconds() - mongoEvolution.getWaitTimeMillisecForUpdateStatusTask() / 1000), 0)); + .append("_id", AbstractMongoEvolution.TRACKER_ID) + .append(MongoEvolution.TRACKER_KEY_UPDATE_LAST, new BsonTimestamp((int) pastTime, 0)); + mongoCollection.insertOne(doc2); - assertFalse(mongoEvolution.isDatabaseUpdateLockAlive()); + + assertFalse(mongoEvolution.isDatabaseUpdateLockAlive(), "Lock should be considered dead when timestamp is significantly in the past"); + mongoCollection.deleteOne(doc2); } @@ -204,12 +218,14 @@ void saveEntryInChangeLogTest() { @Test void updateFieldWithMongoCurrentDateTest() { - Document dtest = new Document().append("TestField1", "Test Field 1").append("TestimeField", getCurrentTimeInSeconds() - 8); + Document dtest = new Document() + .append("_id", AbstractMongoEvolution.TRACKER_ID) + .append("TestField1", "Test Field 1").append("TestimeField", getCurrentTimeInSeconds() - 8); mongoCollection.insertOne(dtest); long before = getCurrentTimeInSeconds(); - MongoEvolution.updateFieldWithMongoCurrentDate(mongoCollection, "TestimeField", null); + MongoEvolution.updateFieldWithMongoCurrentDate(mongoCollection, "TestimeField", new BasicDBObject("_id", AbstractMongoEvolution.TRACKER_ID)); long after = getCurrentTimeInSeconds(); - long updtime = ((BsonTimestamp) mongoCollection.find().first().get("TestimeField")).getTime(); + long updtime = ((BsonTimestamp) mongoCollection.find(Filters.eq("_id", AbstractMongoEvolution.TRACKER_ID)).first().get("TestimeField")).getTime(); assertTrue((updtime >= before && updtime <= after)); } diff --git a/core-quarkus-extensions/config-sources/springcloud-config-source/runtime/pom.xml b/core-quarkus-extensions/config-sources/springcloud-config-source/runtime/pom.xml index db01951f3..9c8dabcea 100644 --- a/core-quarkus-extensions/config-sources/springcloud-config-source/runtime/pom.xml +++ b/core-quarkus-extensions/config-sources/springcloud-config-source/runtime/pom.xml @@ -58,7 +58,13 @@ com.squareup.okhttp3 okhttp - + + com.netcracker.cloud.security.core.utils + k8s-utils + 3.1.0-SNAPSHOT + + + org.mockito mockito-core diff --git a/core-quarkus-extensions/config-sources/springcloud-config-source/runtime/src/main/java/com/netcracker/cloud/springcloud/config/source/ConfigServerClientImpl.java b/core-quarkus-extensions/config-sources/springcloud-config-source/runtime/src/main/java/com/netcracker/cloud/springcloud/config/source/ConfigServerClientImpl.java index 55802062b..e3c260562 100644 --- a/core-quarkus-extensions/config-sources/springcloud-config-source/runtime/src/main/java/com/netcracker/cloud/springcloud/config/source/ConfigServerClientImpl.java +++ b/core-quarkus-extensions/config-sources/springcloud-config-source/runtime/src/main/java/com/netcracker/cloud/springcloud/config/source/ConfigServerClientImpl.java @@ -6,8 +6,8 @@ import com.fasterxml.jackson.datatype.jdk8.Jdk8Module; import com.fasterxml.jackson.module.paramnames.ParameterNamesModule; import com.netcracker.cloud.quarkus.security.auth.M2MManager; -import com.netcracker.cloud.security.core.auth.Token; import com.netcracker.cloud.security.core.utils.tls.TlsUtils; +import com.netcracker.cloud.security.core.utils.k8s.M2MClientFactory; import okhttp3.*; import org.apache.commons.lang3.exception.ExceptionUtils; import org.eclipse.microprofile.config.Config; @@ -36,7 +36,8 @@ public class ConfigServerClientImpl implements ConfigServerClient { private URL url; public ConfigServerClientImpl(String csUrl) throws MalformedURLException { - client = new OkHttpClient.Builder() + client = M2MClientFactory.getM2mOkHttpClient(() -> M2MManager.getInstance().getToken().getTokenValue()) + .newBuilder() .connectionSpecs(Collections.singletonList( csUrl.startsWith("https") ? ConnectionSpec.COMPATIBLE_TLS : ConnectionSpec.CLEARTEXT) ) @@ -87,9 +88,7 @@ private String processRequest(Request request) throws IOException { int count = 1; while (true) { try { - Token token = M2MManager.getInstance().getToken(); request = request.newBuilder() - .addHeader("Authorization", token.getTokenType() + " " + token.getTokenValue()) .build(); Response response = client.newCall(request).execute(); return response.body().string(); diff --git a/core-quarkus-extensions/dbaas-client/dbaas-common/runtime/pom.xml b/core-quarkus-extensions/dbaas-client/dbaas-common/runtime/pom.xml index 7fd6ee6d2..b512de634 100644 --- a/core-quarkus-extensions/dbaas-client/dbaas-common/runtime/pom.xml +++ b/core-quarkus-extensions/dbaas-client/dbaas-common/runtime/pom.xml @@ -63,6 +63,11 @@ com.netcracker.cloud.security.core.utils tls-utils + + com.netcracker.cloud.security.core.utils + k8s-utils + 3.1.0-SNAPSHOT + io.quarkus diff --git a/core-quarkus-extensions/dbaas-client/dbaas-common/runtime/src/main/java/com/netcracker/cloud/dbaas/common/config/DbaasClientProducer.java b/core-quarkus-extensions/dbaas-client/dbaas-common/runtime/src/main/java/com/netcracker/cloud/dbaas/common/config/DbaasClientProducer.java index efc805b3c..1d5da1be2 100644 --- a/core-quarkus-extensions/dbaas-client/dbaas-common/runtime/src/main/java/com/netcracker/cloud/dbaas/common/config/DbaasClientProducer.java +++ b/core-quarkus-extensions/dbaas-client/dbaas-common/runtime/src/main/java/com/netcracker/cloud/dbaas/common/config/DbaasClientProducer.java @@ -12,14 +12,14 @@ public class DbaasClientProducer { @Produces @DefaultBean - public DbaasClient dbaaSClient(DbaasClientConfig dbaasClientConfig) { + public DbaasClient dbaaSClient(SecurityConfig securityConfig, DbaasClientConfig dbaasClientConfig) { if (dbaasClientConfig.dbaasUrl().isPresent() && dbaasClientConfig.dbaasUsername().isPresent() && dbaasClientConfig.dbaasPassword().isPresent()) { log.debug("Create dbaas client with basic auth"); return new BasicDbaaSClient(dbaasClientConfig).build(); } log.debug("Create dbaas client with m2m auth"); - return new M2MDbaaSClient(dbaasClientConfig).build(); + return new M2MDbaaSClient(securityConfig, dbaasClientConfig).build(); } } diff --git a/core-quarkus-extensions/dbaas-client/dbaas-common/runtime/src/main/java/com/netcracker/cloud/dbaas/common/config/M2MDbaaSClient.java b/core-quarkus-extensions/dbaas-client/dbaas-common/runtime/src/main/java/com/netcracker/cloud/dbaas/common/config/M2MDbaaSClient.java index 559aa3ddc..8681e0213 100644 --- a/core-quarkus-extensions/dbaas-client/dbaas-common/runtime/src/main/java/com/netcracker/cloud/dbaas/common/config/M2MDbaaSClient.java +++ b/core-quarkus-extensions/dbaas-client/dbaas-common/runtime/src/main/java/com/netcracker/cloud/dbaas/common/config/M2MDbaaSClient.java @@ -1,40 +1,53 @@ package com.netcracker.cloud.dbaas.common.config; -import jakarta.enterprise.context.ApplicationScoped; -import okhttp3.OkHttpClient; -import okhttp3.Request; import com.netcracker.cloud.context.propagation.core.ContextManager; import com.netcracker.cloud.dbaas.client.DbaaSClientOkHttpImpl; import com.netcracker.cloud.dbaas.client.DbaasClient; import com.netcracker.cloud.framework.contexts.tenant.TenantContextObject; import com.netcracker.cloud.quarkus.security.auth.M2MManager; -import com.netcracker.cloud.security.core.auth.Token; +import com.netcracker.cloud.security.core.utils.k8s.M2MClientFactory; import com.netcracker.cloud.security.core.utils.tls.TlsUtils; +import jakarta.enterprise.context.ApplicationScoped; +import lombok.extern.slf4j.Slf4j; +import okhttp3.OkHttpClient; +import okhttp3.Request; import java.util.Optional; import static com.netcracker.cloud.dbaas.common.config.DbaasClientConfig.DEFAULT_DBAAS_AGENT_ADDRESS; import static com.netcracker.cloud.framework.contexts.tenant.BaseTenantProvider.TENANT_CONTEXT_NAME; +@Slf4j @ApplicationScoped public class M2MDbaaSClient { - private DbaasClientConfig config; private static final int MAX_RETRIES = 3; private static final long INITIAL_RETRY_DELAY = 500; - public M2MDbaaSClient(DbaasClientConfig config) { - this.config = config; + private final SecurityConfig securityConfig; + private final DbaasClientConfig dbaasConfig; + + public M2MDbaaSClient(SecurityConfig securityConfig, DbaasClientConfig dbaasConfig) { + this.securityConfig = securityConfig; + this.dbaasConfig = dbaasConfig; } public DbaasClient build() { - String url = config.dbaasAgentUrl().orElse(DEFAULT_DBAAS_AGENT_ADDRESS); - OkHttpClient httpClient = new OkHttpClient.Builder() + String dbaasAgentUrl = dbaasConfig.dbaasAgentUrl().orElse(DEFAULT_DBAAS_AGENT_ADDRESS); + + String dbaasUrl = dbaasAgentUrl; + if(securityConfig.k8sEnabled()) { + if(dbaasConfig.dbaasUrl().isEmpty()) { + log.warn("DBaaS address is not available, falling back to dbaas-agent. Specify 'api.dbaas.address' property to DBaaS url"); + } + dbaasUrl = dbaasConfig.dbaasUrl().orElse(dbaasAgentUrl); + } + + OkHttpClient httpClient = M2MClientFactory.getDbaasOkHttpClient(() -> M2MManager.getInstance().getToken().getTokenValue()); + + httpClient = httpClient.newBuilder() .addInterceptor(chain -> { Request original = chain.request(); - Token token = M2MManager.getInstance().getToken(); - String credentials = token.getTokenType() + " " + token.getTokenValue(); - Request.Builder requestBuilder = original.newBuilder() - .addHeader("Authorization", credentials); + Request.Builder requestBuilder = original.newBuilder(); Optional tenantContextData = ContextManager.getSafe(TENANT_CONTEXT_NAME); if (tenantContextData.isPresent() && tenantContextData.get().getTenant() != null) { requestBuilder.addHeader("tenant", tenantContextData.get().getTenant()); @@ -44,6 +57,6 @@ public DbaasClient build() { .addInterceptor(new RetryInterceptor(MAX_RETRIES, INITIAL_RETRY_DELAY)) .sslSocketFactory(TlsUtils.getSslContext().getSocketFactory(), TlsUtils.getTrustManager()) .build(); - return new DbaaSClientOkHttpImpl(url, httpClient); + return new DbaaSClientOkHttpImpl(dbaasUrl, httpClient); } } diff --git a/core-quarkus-extensions/dbaas-client/dbaas-common/runtime/src/main/java/com/netcracker/cloud/dbaas/common/config/SecurityConfig.java b/core-quarkus-extensions/dbaas-client/dbaas-common/runtime/src/main/java/com/netcracker/cloud/dbaas/common/config/SecurityConfig.java new file mode 100644 index 000000000..947f0bf49 --- /dev/null +++ b/core-quarkus-extensions/dbaas-client/dbaas-common/runtime/src/main/java/com/netcracker/cloud/dbaas/common/config/SecurityConfig.java @@ -0,0 +1,15 @@ +package com.netcracker.cloud.dbaas.common.config; + +import io.smallrye.config.ConfigMapping; +import io.smallrye.config.WithDefault; +import io.smallrye.config.WithName; + +@ConfigMapping(prefix = "security.m2m") +public interface SecurityConfig { + /** + * kubernetes tokens authentication enabled + */ + @WithName("kubernetes.enabled") + @WithDefault("false") + boolean k8sEnabled(); +} diff --git a/core-quarkus-extensions/dbaas-client/dbaas-common/runtime/src/test/java/com/netcracker/cloud/dbaas/common/config/M2MDbaaSClientTest.java b/core-quarkus-extensions/dbaas-client/dbaas-common/runtime/src/test/java/com/netcracker/cloud/dbaas/common/config/M2MDbaaSClientTest.java index adea1a0b5..85c49a456 100644 --- a/core-quarkus-extensions/dbaas-client/dbaas-common/runtime/src/test/java/com/netcracker/cloud/dbaas/common/config/M2MDbaaSClientTest.java +++ b/core-quarkus-extensions/dbaas-client/dbaas-common/runtime/src/test/java/com/netcracker/cloud/dbaas/common/config/M2MDbaaSClientTest.java @@ -16,11 +16,18 @@ class M2MDbaaSClientTest { private M2MDbaaSClient m2MDbaaSClient; private static final String DB_AGENT_URL = "http://dbaas-agent:8080"; + private static final String DB_AGGREGATOR_URL = "http://dbaas-aggregator:8080"; + @BeforeEach void setUp() { - DbaasClientConfig config = mock(DbaasClientConfig.class); - when(config.dbaasAgentUrl()).thenReturn(Optional.of(DB_AGENT_URL)); - m2MDbaaSClient = new M2MDbaaSClient(config); + SecurityConfig securityConfig = mock(SecurityConfig.class); + when(securityConfig.k8sEnabled()).thenReturn(true); + + DbaasClientConfig dbaasConfig = mock(DbaasClientConfig.class); + when(dbaasConfig.dbaasAgentUrl()).thenReturn(Optional.of(DB_AGENT_URL)); + when(dbaasConfig.dbaasUrl()).thenReturn(Optional.of(DB_AGGREGATOR_URL)); + + m2MDbaaSClient = new M2MDbaaSClient(securityConfig, dbaasConfig); } @Test void testBuild() throws NoSuchFieldException, IllegalAccessException { @@ -29,8 +36,6 @@ void testBuild() throws NoSuchFieldException, IllegalAccessException { clientField.setAccessible(true); OkHttpClient clientValue = (OkHttpClient) clientField.get(client); assertNotNull(client); - assertEquals(2, clientValue.interceptors().size()); + assertEquals(3, clientValue.interceptors().size()); } } - - diff --git a/core-quarkus-extensions/routes-registrator/runtime/pom.xml b/core-quarkus-extensions/routes-registrator/runtime/pom.xml index cfc3433b1..19812eb3a 100644 --- a/core-quarkus-extensions/routes-registrator/runtime/pom.xml +++ b/core-quarkus-extensions/routes-registrator/runtime/pom.xml @@ -46,7 +46,12 @@ com.netcracker.cloud.security.core.utils tls-utils - + + com.netcracker.cloud.security.core.utils + k8s-utils + 3.1.0-SNAPSHOT + + io.quarkus quarkus-junit diff --git a/core-quarkus-extensions/routes-registrator/runtime/src/main/java/com/netcracker/cloud/quarkus/routesregistration/runtime/gateway/route/RouteRegistrationConfig.java b/core-quarkus-extensions/routes-registrator/runtime/src/main/java/com/netcracker/cloud/quarkus/routesregistration/runtime/gateway/route/RouteRegistrationConfig.java index fca416dcb..c47acadbd 100644 --- a/core-quarkus-extensions/routes-registrator/runtime/src/main/java/com/netcracker/cloud/quarkus/routesregistration/runtime/gateway/route/RouteRegistrationConfig.java +++ b/core-quarkus-extensions/routes-registrator/runtime/src/main/java/com/netcracker/cloud/quarkus/routesregistration/runtime/gateway/route/RouteRegistrationConfig.java @@ -4,7 +4,7 @@ import com.netcracker.cloud.routesregistration.common.gateway.route.*; import com.netcracker.cloud.routesregistration.common.gateway.route.rest.RegistrationRequestFactory; import com.netcracker.cloud.routesregistration.common.gateway.route.transformation.RouteTransformer; -import com.netcracker.cloud.security.core.auth.Token; +import com.netcracker.cloud.security.core.utils.tls.TlsUtils; import io.quarkus.arc.Unremovable; import io.reactivex.Scheduler; import io.reactivex.schedulers.Schedulers; @@ -12,8 +12,8 @@ import jakarta.enterprise.inject.Produces; import jakarta.inject.Named; import okhttp3.OkHttpClient; -import okhttp3.Request; import org.eclipse.microprofile.config.inject.ConfigProperty; +import com.netcracker.cloud.security.core.utils.k8s.M2MClientFactory; import java.util.Optional; @@ -82,15 +82,8 @@ ControlPlaneClient controlPlaneClient(@Named(CONTROL_PLANE_HTTP_CLIENT) OkHttpCl @Produces @Named(CONTROL_PLANE_HTTP_CLIENT) OkHttpClient controlPlaneHttpClient() { - return new OkHttpClient.Builder() - .addInterceptor(chain -> { - Token token = M2MManager.getInstance().getToken(); - Request original = chain.request(); - Request request = original.newBuilder() - .addHeader("Authorization", token.getTokenType() + " " + token.getTokenValue()) - .build(); - return chain.proceed(request); - }) + return M2MClientFactory.getM2mOkHttpClient(() -> M2MManager.getInstance().getToken().getTokenValue()) + .newBuilder() .retryOnConnectionFailure(true) .build(); } diff --git a/core-rest-libraries/config-server-loader/config-server-loader-resttemplate/pom.xml b/core-rest-libraries/config-server-loader/config-server-loader-resttemplate/pom.xml index ff4748195..f0fdf8574 100644 --- a/core-rest-libraries/config-server-loader/config-server-loader-resttemplate/pom.xml +++ b/core-rest-libraries/config-server-loader/config-server-loader-resttemplate/pom.xml @@ -25,5 +25,15 @@ rest-security-adapters ${project.version} + + com.netcracker.cloud + microservice-restclient-okhttp + 7.0.9-SNAPSHOT + + + com.netcracker.cloud.security.core.utils + k8s-utils + 3.1.0-SNAPSHOT + diff --git a/core-rest-libraries/config-server-loader/config-server-loader-resttemplate/src/main/java/com/netcracker/cloud/configserver/resttemplate/RestTemplateConfigServerConfigDataLocationResolver.java b/core-rest-libraries/config-server-loader/config-server-loader-resttemplate/src/main/java/com/netcracker/cloud/configserver/resttemplate/RestTemplateConfigServerConfigDataLocationResolver.java index 23b3d2ca9..2bdf2c7c3 100644 --- a/core-rest-libraries/config-server-loader/config-server-loader-resttemplate/src/main/java/com/netcracker/cloud/configserver/resttemplate/RestTemplateConfigServerConfigDataLocationResolver.java +++ b/core-rest-libraries/config-server-loader/config-server-loader-resttemplate/src/main/java/com/netcracker/cloud/configserver/resttemplate/RestTemplateConfigServerConfigDataLocationResolver.java @@ -1,5 +1,7 @@ package com.netcracker.cloud.configserver.resttemplate; +import com.netcracker.cloud.restclient.okhttp.MicroserviceOkHttpRestClient; +import com.netcracker.cloud.security.core.utils.k8s.M2MClientFactory; import org.apache.hc.client5.http.classic.HttpClient; import org.apache.hc.client5.http.impl.classic.HttpClients; import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManager; @@ -14,10 +16,9 @@ import org.springframework.boot.bootstrap.ConfigurableBootstrapContext; import org.springframework.boot.logging.DeferredLogFactory; import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; +import org.springframework.http.client.JdkClientHttpRequestFactory; import org.springframework.web.client.RestTemplate; -import java.util.Collections; - public class RestTemplateConfigServerConfigDataLocationResolver extends AbstractCustomConfigServerConfigDataLocationResolver { @Value("${connection.readTimeout:60000}") @@ -32,10 +33,14 @@ public RestTemplateConfigServerConfigDataLocationResolver(DeferredLogFactory log @Override public MicroserviceRestClient getMicroserviceRestClient() { - return new MicroserviceRestTemplate(createM2MRestTemplate()); + if (hasM2M(configurableBootstrapContext)) { + var client = M2MClientFactory.getM2mOkHttpClient(() -> getM2MToken(configurableBootstrapContext)); + return new MicroserviceOkHttpRestClient(client); + } + return createM2MRestTemplate(); } - private RestTemplate createM2MRestTemplate() { + private MicroserviceRestClient createM2MRestTemplate() { RestTemplate template = new RestTemplate(); SocketConfig socketConfig = SocketConfig.custom().setSoTimeout(Timeout.ofMilliseconds(readTimeout)).build(); @@ -45,14 +50,7 @@ private RestTemplate createM2MRestTemplate() { HttpClient httpClient = HttpClients.custom().setConnectionManager(poolingHttpClientConnectionManager).build(); template.setRequestFactory(new HttpComponentsClientHttpRequestFactory(httpClient)); - if (hasM2M(configurableBootstrapContext)) { - template.setInterceptors(Collections.singletonList((request, body, execution) -> { - request.getHeaders().setBearerAuth(getM2MToken(configurableBootstrapContext)); - return execution.execute(request, body); - })); - } - - return template; + return new MicroserviceRestTemplate(template); } private String getM2MToken(ConfigurableBootstrapContext configurableBootstrapContext) { diff --git a/core-rest-libraries/config-server-loader/config-server-loader-webclient/pom.xml b/core-rest-libraries/config-server-loader/config-server-loader-webclient/pom.xml index 9a3efcdf6..457be9902 100644 --- a/core-rest-libraries/config-server-loader/config-server-loader-webclient/pom.xml +++ b/core-rest-libraries/config-server-loader/config-server-loader-webclient/pom.xml @@ -25,5 +25,15 @@ rest-security-adapters ${project.version} + + com.netcracker.cloud + microservice-restclient-okhttp + 7.0.9-SNAPSHOT + + + com.netcracker.cloud.security.core.utils + k8s-utils + 3.1.0-SNAPSHOT + diff --git a/core-rest-libraries/config-server-loader/config-server-loader-webclient/src/main/java/com/netcracker/cloud/configserver/webclient/WebClientConfigServerConfigDataLocationResolver.java b/core-rest-libraries/config-server-loader/config-server-loader-webclient/src/main/java/com/netcracker/cloud/configserver/webclient/WebClientConfigServerConfigDataLocationResolver.java index 9fa2e419b..dd7be08a5 100644 --- a/core-rest-libraries/config-server-loader/config-server-loader-webclient/src/main/java/com/netcracker/cloud/configserver/webclient/WebClientConfigServerConfigDataLocationResolver.java +++ b/core-rest-libraries/config-server-loader/config-server-loader-webclient/src/main/java/com/netcracker/cloud/configserver/webclient/WebClientConfigServerConfigDataLocationResolver.java @@ -2,14 +2,15 @@ import com.netcracker.cloud.configserver.common.configuration.AbstractCustomConfigServerConfigDataLocationResolver; import com.netcracker.cloud.restclient.MicroserviceRestClient; +import com.netcracker.cloud.restclient.okhttp.MicroserviceOkHttpRestClient; import com.netcracker.cloud.restclient.webclient.MicroserviceWebClient; import com.netcracker.cloud.security.core.auth.M2MManager; +import com.netcracker.cloud.security.core.utils.k8s.M2MClientFactory; import org.springframework.boot.bootstrap.ConfigurableBootstrapContext; import org.springframework.boot.logging.DeferredLogFactory; -import org.springframework.web.reactive.function.client.ClientRequest; +import org.springframework.http.client.reactive.JdkClientHttpConnector; import org.springframework.web.reactive.function.client.WebClient; - -import static org.springframework.http.HttpHeaders.AUTHORIZATION; +import java.net.http.HttpClient; public class WebClientConfigServerConfigDataLocationResolver extends AbstractCustomConfigServerConfigDataLocationResolver { @@ -22,19 +23,16 @@ public WebClientConfigServerConfigDataLocationResolver(DeferredLogFactory log, C @Override public MicroserviceRestClient getMicroserviceRestClient() { - return new MicroserviceWebClient(createM2MWebClient()); - } - - private WebClient createM2MWebClient() { - WebClient.Builder builder = WebClient.builder(); if (hasM2M(configurableBootstrapContext)) { - builder.filter( - (request, next) -> - next.exchange(ClientRequest.from(request). - header(AUTHORIZATION, "Bearer " + getM2MToken(configurableBootstrapContext)).build()) - ); + var client = M2MClientFactory.getM2mOkHttpClient(() -> getM2MToken(configurableBootstrapContext)); + return new MicroserviceOkHttpRestClient(client); } - return builder.build(); + return createM2MWebClient(); + } + + private MicroserviceRestClient createM2MWebClient() { + var builder = WebClient.builder(); + return new MicroserviceWebClient(builder.build()); } private String getM2MToken(ConfigurableBootstrapContext configurableBootstrapContext) { diff --git a/core-restclient/microservice-restclient-okhttp/pom.xml b/core-restclient/microservice-restclient-okhttp/pom.xml new file mode 100644 index 000000000..95cd50f1d --- /dev/null +++ b/core-restclient/microservice-restclient-okhttp/pom.xml @@ -0,0 +1,79 @@ + + + 4.0.0 + + microservice-restclient-parent + com.netcracker.cloud + 7.1.0-SNAPSHOT + ../parent/pom.xml + + microservice-restclient-okhttp + + + com.netcracker.cloud + microservice-restclient-api + ${project.version} + + + com.squareup.okhttp3 + okhttp + 4.12.0 + + + com.fasterxml.jackson.core + jackson-databind + 2.18.2 + provided + + + org.projectlombok + lombok + 1.18.44 + compile + true + + + org.slf4j + slf4j-api + 2.0.17 + compile + true + + + org.slf4j + slf4j-simple + 2.0.17 + test + + + com.netcracker.cloud + microservice-restclient-test-utils + ${project.version} + test + + + com.squareup.okhttp3 + mockwebserver + + + + + com.squareup.okhttp3 + mockwebserver + 4.12.0 + test + + + org.mockito + mockito-core + ${mockito.version} + test + + + org.junit.jupiter + junit-jupiter + 6.0.3 + test + + + diff --git a/core-restclient/microservice-restclient-okhttp/src/main/java/com/netcracker/cloud/restclient/okhttp/MicroserviceOkHttpRestClient.java b/core-restclient/microservice-restclient-okhttp/src/main/java/com/netcracker/cloud/restclient/okhttp/MicroserviceOkHttpRestClient.java new file mode 100644 index 000000000..ec724d806 --- /dev/null +++ b/core-restclient/microservice-restclient-okhttp/src/main/java/com/netcracker/cloud/restclient/okhttp/MicroserviceOkHttpRestClient.java @@ -0,0 +1,150 @@ +package com.netcracker.cloud.restclient.okhttp; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.netcracker.cloud.core.error.rest.exception.RemoteCodeException; +import com.netcracker.cloud.core.error.rest.tmf.DefaultTmfErrorResponseConverter; +import com.netcracker.cloud.core.error.rest.tmf.TmfErrorResponse; +import com.netcracker.cloud.core.error.rest.tmf.TmfErrorResponseConverter; +import com.netcracker.cloud.restclient.AbstractMicroserviceRestClient; +import com.netcracker.cloud.restclient.HttpMethod; +import com.netcracker.cloud.restclient.entity.RestClientResponseEntity; +import com.netcracker.cloud.restclient.exception.MicroserviceRestClientException; +import com.netcracker.cloud.restclient.exception.MicroserviceRestClientResponseException; +import lombok.Getter; +import lombok.Setter; +import lombok.extern.slf4j.Slf4j; +import okhttp3.*; + +import java.io.IOException; +import java.net.URI; +import java.util.List; +import java.util.Map; + +@Slf4j +public class MicroserviceOkHttpRestClient extends AbstractMicroserviceRestClient { + + private final OkHttpClient client; + + @Getter + @Setter + private ObjectMapper mapper = new ObjectMapper() + .setDefaultPropertyInclusion(JsonInclude.Include.NON_NULL) + .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + + @Getter + @Setter + private TmfErrorResponseConverter converter = new DefaultTmfErrorResponseConverter(); + + public MicroserviceOkHttpRestClient(OkHttpClient client) { + this.client = client; + } + + @Override + public RestClientResponseEntity doRequest(String urlTemplate, + HttpMethod httpMethod, + Map> headers, + Object requestBody, + Class responseClass, + Map params) { + String expandedUrl = expandUrl(urlTemplate, params); + return doRequest(URI.create(expandedUrl), httpMethod, headers, requestBody, responseClass); + } + + @Override + @SuppressWarnings("unchecked") + public RestClientResponseEntity doRequest(URI uri, + HttpMethod httpMethod, + Map> headers, + Object requestBody, + Class responseClass) { + Request.Builder requestBuilder = new Request.Builder().url(uri.toString()); + + Headers.Builder okHeadersBuilder = new Headers.Builder(); + if (headers != null) { + headers.forEach((name, values) -> values.forEach(value -> okHeadersBuilder.add(name, value))); + } + + if (okHeadersBuilder.get("Content-Type") == null) { + okHeadersBuilder.set("Content-Type", "application/json"); + } + Headers okHeaders = okHeadersBuilder.build(); + requestBuilder.headers(okHeaders); + + RequestBody okBody = null; + if (requestBody != null) { + byte[] bodyBytes; + try { + if (requestBody instanceof String) { + bodyBytes = ((String) requestBody).getBytes(); + } else if (requestBody instanceof byte[]) { + bodyBytes = (byte[]) requestBody; + } else { + bodyBytes = mapper.writeValueAsBytes(requestBody); + } + } catch (IOException e) { + throw new MicroserviceRestClientException("Failed to serialize request body", e); + } + okBody = RequestBody.create(bodyBytes, MediaType.parse(okHeaders.get("Content-Type"))); + } else if (HttpMethod.POST.equals(httpMethod) || HttpMethod.PUT.equals(httpMethod) || HttpMethod.PATCH.equals(httpMethod)) { + okBody = RequestBody.create(new byte[0], MediaType.parse(okHeaders.get("Content-Type"))); + } + + requestBuilder.method(httpMethod.name(), okBody); + + try (Response response = client.newCall(requestBuilder.build()).execute()) { + int code = response.code(); + Map> responseHeaders = response.headers().toMultimap(); + byte[] responseBodyBytes = null; + if (response.body() != null) { + responseBodyBytes = response.body().bytes(); + } + + if (response.isSuccessful()) { + T mappedBody = null; + if (responseBodyBytes != null && responseBodyBytes.length > 0 && responseClass != Void.class) { + if (responseClass == String.class) { + mappedBody = (T) new String(responseBodyBytes); + } else if (responseClass == byte[].class) { + mappedBody = (T) responseBodyBytes; + } else { + mappedBody = mapper.readValue(responseBodyBytes, responseClass); + } + } + return new RestClientResponseEntity<>(mappedBody, code, responseHeaders); + } else { + MicroserviceRestClientResponseException mce; + try { + if (responseBodyBytes != null && responseBodyBytes.length > 0) { + TmfErrorResponse tmfErrorResponse = mapper.readValue(responseBodyBytes, TmfErrorResponse.class); + final RemoteCodeException remoteCodeException = converter.buildErrorCodeException(tmfErrorResponse); + mce = new MicroserviceRestClientResponseException(remoteCodeException.getMessage(), + remoteCodeException, code, responseBodyBytes, responseHeaders); + } else { + mce = new MicroserviceRestClientResponseException("Request failed with status " + code, + null, code, responseBodyBytes, responseHeaders); + } + } catch (Exception ce) { + log.warn("Failed to parse response as TMF error response, cause: {}", ce.getMessage()); + mce = new MicroserviceRestClientResponseException("Request failed with status " + code, + ce, code, responseBodyBytes, responseHeaders); + } + throw mce; + } + } catch (IOException e) { + throw new MicroserviceRestClientException(e.getMessage(), e); + } + } + + private String expandUrl(String urlTemplate, Map params) { + if (params == null || params.isEmpty()) { + return urlTemplate; + } + String expanded = urlTemplate; + for (Map.Entry entry : params.entrySet()) { + expanded = expanded.replace("{" + entry.getKey() + "}", String.valueOf(entry.getValue())); + } + return expanded; + } +} diff --git a/core-restclient/microservice-restclient-okhttp/src/test/java/com/netcracker/cloud/restclient/okhttp/MicroserviceOkHttpRestClientTest.java b/core-restclient/microservice-restclient-okhttp/src/test/java/com/netcracker/cloud/restclient/okhttp/MicroserviceOkHttpRestClientTest.java new file mode 100644 index 000000000..44898d871 --- /dev/null +++ b/core-restclient/microservice-restclient-okhttp/src/test/java/com/netcracker/cloud/restclient/okhttp/MicroserviceOkHttpRestClientTest.java @@ -0,0 +1,67 @@ +package com.netcracker.cloud.restclient.okhttp; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.netcracker.cloud.core.error.rest.exception.RemoteCodeException; +import com.netcracker.cloud.core.error.rest.tmf.TmfErrorResponse; +import com.netcracker.cloud.restclient.BaseMicroserviceRestClientTest; +import com.netcracker.cloud.restclient.HttpMethod; +import com.netcracker.cloud.restclient.exception.MicroserviceRestClientResponseException; +import okhttp3.OkHttpClient; +import okhttp3.mockwebserver.MockResponse; +import okhttp3.mockwebserver.RecordedRequest; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.util.UUID; +import java.util.concurrent.TimeUnit; + +import static org.junit.jupiter.api.Assertions.*; + +class MicroserviceOkHttpRestClientTest extends BaseMicroserviceRestClientTest { + + @BeforeEach + void setUp() { + restClient = new MicroserviceOkHttpRestClient(new OkHttpClient()); + } + + @Test + void testDefaultRequestHeaders() throws InterruptedException { + mockBackEnd.enqueue(new MockResponse().setResponseCode(200).setBody("Test response body")); + + restClient.doRequest(testUrl, HttpMethod.POST, null, null, Void.class); + RecordedRequest recordedRequest = mockBackEnd.takeRequest(60, TimeUnit.SECONDS); + + assertNotNull(recordedRequest); + assertEquals("application/json", recordedRequest.getHeader("Content-Type")); + } + + @Test + void testTMFRestClientResponseException() throws Exception { + TmfErrorResponse tmfErrorResponse = TmfErrorResponse.builder() + .id(UUID.randomUUID().toString()) + .code("TEST") + .reason("test reason") + .detail("test detail") + .status("500") + .type(TmfErrorResponse.TYPE_V1_0) + .build(); + + mockBackEnd.enqueue(new MockResponse() + .setHeader("test-header", "test-value") + .setResponseCode(500) + .setBody(new ObjectMapper().writeValueAsString(tmfErrorResponse))); + + try { + restClient.doRequest(testUrl, HttpMethod.POST, null, null, Void.class); + fail("Expected MicroserviceRestClientResponseException"); + } catch (MicroserviceRestClientResponseException e) { + assertEquals(500, e.getHttpStatus()); + assertEquals("test-value", e.getResponseHeaders().get("test-header").get(0)); + assertTrue(e.getCause() instanceof RemoteCodeException); + RemoteCodeException remoteCodeException = (RemoteCodeException) e.getCause(); + assertEquals(tmfErrorResponse.getCode(), remoteCodeException.getErrorCode().getCode()); + } finally { + mockBackEnd.takeRequest(60, TimeUnit.SECONDS); + } + } +} diff --git a/core-restclient/parent/pom.xml b/core-restclient/parent/pom.xml index 6366ef9b7..d70e42eab 100644 --- a/core-restclient/parent/pom.xml +++ b/core-restclient/parent/pom.xml @@ -27,6 +27,7 @@ ${project.basedir}/../microservice-restclient-report-aggregate/target/site/jacoco-aggregate/jacoco.xml + 5.21.0 diff --git a/core-restclient/pom.xml b/core-restclient/pom.xml index 351e12688..cb07852bc 100644 --- a/core-restclient/pom.xml +++ b/core-restclient/pom.xml @@ -22,6 +22,7 @@ microservice-restclient-test-utils microservice-restclient-webclient microservice-restclient-resttemplate + microservice-restclient-okhttp microservice-restclient-report-aggregate diff --git a/core-utils/k8s/pom.xml b/core-utils/k8s/pom.xml index 79d62a963..ac34e350e 100644 --- a/core-utils/k8s/pom.xml +++ b/core-utils/k8s/pom.xml @@ -41,6 +41,16 @@ jose4j 0.9.6 + + com.github.ben-manes.caffeine + caffeine + 3.2.3 + + + com.squareup.okhttp3 + okhttp + 4.12.0 + @@ -61,6 +71,7 @@ com.squareup.okhttp3 mockwebserver + 4.12.0 test @@ -68,6 +79,18 @@ logback-classic test + + org.wiremock + wiremock + 3.13.2 + test + + + com.github.stefanbirkner + system-lambda + 1.2.1 + test + diff --git a/core-utils/k8s/src/main/java/com/netcracker/cloud/security/core/utils/k8s/M2MClientFactory.java b/core-utils/k8s/src/main/java/com/netcracker/cloud/security/core/utils/k8s/M2MClientFactory.java new file mode 100644 index 000000000..dacb782e5 --- /dev/null +++ b/core-utils/k8s/src/main/java/com/netcracker/cloud/security/core/utils/k8s/M2MClientFactory.java @@ -0,0 +1,45 @@ +package com.netcracker.cloud.security.core.utils.k8s; + +import com.netcracker.cloud.security.core.utils.k8s.impl.M2MInterceptor; +import com.netcracker.cloud.security.core.utils.k8s.impl.UrlCache; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; +import okhttp3.OkHttpClient; + +import java.util.Optional; +import java.util.function.Supplier; + +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public final class M2MClientFactory { + public static final String DBAAS_AGENT_URL_PROP = "com.netcracker.cloud.dbaas.agent.url"; + public static final String MAAS_AGENT_URL_PROP = "com.netcracker.cloud.maas.agent.url"; + + private static final Supplier k8sAuthHeaderSupplier = + getBearerAuthHeaderSupplier(() -> KubernetesAudienceToken.getToken(AudienceName.NETCRACKER)); + + public static OkHttpClient getM2mOkHttpClient(Supplier keycloakTokenSupplier) { + return getOkHttpClient(new M2MInterceptor(new UrlCache(), getBearerAuthHeaderSupplier(keycloakTokenSupplier), k8sAuthHeaderSupplier)); + } + + public static OkHttpClient getDbaasOkHttpClient(Supplier keycloakTokenSupplier) { + return getAgentOkHttpClient(keycloakTokenSupplier, Optional.ofNullable(System.getProperty(DBAAS_AGENT_URL_PROP)).orElse("http://dbaas-agent:8080")); + } + + public static OkHttpClient getMaasOkHttpClient(Supplier keycloakTokenSupplier) { + return getAgentOkHttpClient(keycloakTokenSupplier, Optional.ofNullable(System.getProperty(MAAS_AGENT_URL_PROP)).orElse("http://maas-agent:8080")); + } + + private static OkHttpClient getAgentOkHttpClient(Supplier keycloakTokenSupplier, String agentUrl) { + return getOkHttpClient(new M2MInterceptor(new UrlCache(), getBearerAuthHeaderSupplier(keycloakTokenSupplier), k8sAuthHeaderSupplier, agentUrl)); + } + + private static OkHttpClient getOkHttpClient(M2MInterceptor interceptor) { + return new OkHttpClient.Builder() + .addInterceptor(interceptor) + .build(); + } + + private static Supplier getBearerAuthHeaderSupplier(Supplier tokenSupplier) { + return () -> "Bearer " + tokenSupplier.get(); + } +} diff --git a/core-utils/k8s/src/main/java/com/netcracker/cloud/security/core/utils/k8s/impl/M2MInterceptor.java b/core-utils/k8s/src/main/java/com/netcracker/cloud/security/core/utils/k8s/impl/M2MInterceptor.java new file mode 100644 index 000000000..38b2acf16 --- /dev/null +++ b/core-utils/k8s/src/main/java/com/netcracker/cloud/security/core/utils/k8s/impl/M2MInterceptor.java @@ -0,0 +1,120 @@ +package com.netcracker.cloud.security.core.utils.k8s.impl; + +import lombok.extern.slf4j.Slf4j; +import okhttp3.HttpUrl; +import okhttp3.Interceptor; +import okhttp3.Request; +import okhttp3.Response; +import org.apache.commons.lang3.StringUtils; +import org.jetbrains.annotations.NotNull; + +import java.io.IOException; +import java.util.Objects; +import java.util.function.Supplier; + +import static com.netcracker.cloud.security.core.utils.k8s.impl.UrlCache.calculateCacheKey; +import static java.net.HttpURLConnection.HTTP_UNAUTHORIZED; + +@Slf4j +public final class M2MInterceptor implements Interceptor { + + public static final String KUBERNETES_TOKEN_ACQUISITION_ERROR = """ + Error acquiring kubernetes token for m2m communication. + The current version of the security library expects a kubernetes token with the `netcracker` audience to be mounted in the deployment. + if you do not intend to use a kubernetes token at this time, please roll back to a previous version of the library. + otherwise, make sure that a kubernetes token with the `netcracker` audience is properly mounted. + the previous authentication method will be used as a fallback."""; + public static final String KUBERNETES_TOKEN_UNAUTHORIZED_ERROR = """ + Unauthorized access (http 401). + During an m2m interaction attempt using a kubernetes token with the `netcracker` audience, a 401 error was received. + The possible cause is an outdated version of the security library on the server side. + The previous authentication method will be used as a fallback."""; + + private final boolean k8sEnabled; + private final UrlCache urlCache; + private final Supplier fallbackAuthHeaderSupplier; + private final Supplier k8sAuthHeaderSupplier; + private final HttpUrl fallbackBaseUrl; + + public M2MInterceptor(UrlCache urlCache, Supplier fallbackAuthHeaderSupplier, Supplier k8sAuthHeaderSupplier) { + this(urlCache, fallbackAuthHeaderSupplier, k8sAuthHeaderSupplier, null); + } + + public M2MInterceptor(UrlCache urlCache, Supplier fallbackAuthHeaderSupplier, Supplier k8sAuthHeaderSupplier, String fallbackBaseUrl) { + String k8sEnabledProp = System.getProperty("security.m2m.kubernetes.enabled"); + if (k8sEnabledProp == null) { + k8sEnabledProp = System.getenv("SECURITY_M2M_KUBERNETES_ENABLED"); + } + + this.k8sEnabled = Boolean.parseBoolean(k8sEnabledProp); + this.urlCache = urlCache; + this.fallbackAuthHeaderSupplier = fallbackAuthHeaderSupplier; + this.k8sAuthHeaderSupplier = k8sAuthHeaderSupplier; + this.fallbackBaseUrl = (fallbackBaseUrl != null) ? HttpUrl.get(fallbackBaseUrl) : null; + } + + @NotNull + @Override + public Response intercept(final Interceptor.Chain chain) throws IOException { + final Request request = chain.request(); + final String cacheKey = calculateCacheKey(request.url().toString()); + if (k8sEnabled && !urlCache.containsKey(cacheKey)) { + //first call (no information) / kubernetes token is applicable + final Request altered; + try { + altered = buildRequest(request, k8sAuthHeaderSupplier.get(), false); + } catch (IllegalStateException|IllegalArgumentException ex) { + final Request fallbackRequest = buildRequest(request, fallbackAuthHeaderSupplier.get(), true); + return doRequestFallback(fallbackRequest, KUBERNETES_TOKEN_ACQUISITION_ERROR, cacheKey, chain); + } + final Response response = chain.proceed(altered); + if (response.code() == HTTP_UNAUTHORIZED) { + //authentication failed, need to use old approach + response.close(); + final Request fallbackRequest = buildRequest(request, fallbackAuthHeaderSupplier.get(), true); + return doRequestFallback(fallbackRequest, KUBERNETES_TOKEN_UNAUTHORIZED_ERROR, cacheKey, chain); + } + return response; + } + final Request fallbackRequest = buildRequest(request, fallbackAuthHeaderSupplier.get(), true); + return chain.proceed(fallbackRequest); + } + + private Response doRequestFallback(final Request fallbackRequest, + final String reason, + final String cacheKey, + final Interceptor.Chain chain) throws IOException { + final Response fallbackResponse = chain.proceed(fallbackRequest); + if (fallbackResponse.isSuccessful()) { + urlCache.store(cacheKey); + if(k8sEnabled && Objects.equals(reason, KUBERNETES_TOKEN_ACQUISITION_ERROR)) { + log.warn("Failed to establish m2m connection to {}\n{}", fallbackRequest.url(), reason); + } else { + log.debug("Failed to establish m2m connection to {}\n{}", fallbackRequest.url(), reason); + } + } + return fallbackResponse; + } + + private Request buildRequest(final Request initialRequest, final String authHeader, final boolean useFallbackUrl) { + if (StringUtils.isEmpty(authHeader)) { + throw new IllegalStateException("M2M auth header is empty."); + } + HttpUrl targetUrl = initialRequest.url(); + if(k8sEnabled && useFallbackUrl && fallbackBaseUrl != null) { + targetUrl = rebaseUrl(initialRequest.url(), fallbackBaseUrl); + } + return initialRequest.newBuilder() + .url(targetUrl) + .header("Authorization", authHeader) + .build(); + } + + private static HttpUrl rebaseUrl(final HttpUrl original, final HttpUrl base) { + return original.newBuilder() + .scheme(base.scheme()) + .host(base.host()) + .port(base.port()) + .build(); + } +} diff --git a/core-utils/k8s/src/main/java/com/netcracker/cloud/security/core/utils/k8s/impl/UrlCache.java b/core-utils/k8s/src/main/java/com/netcracker/cloud/security/core/utils/k8s/impl/UrlCache.java new file mode 100644 index 000000000..24edd19b8 --- /dev/null +++ b/core-utils/k8s/src/main/java/com/netcracker/cloud/security/core/utils/k8s/impl/UrlCache.java @@ -0,0 +1,90 @@ +package com.netcracker.cloud.security.core.utils.k8s.impl; + +import com.github.benmanes.caffeine.cache.Cache; +import com.github.benmanes.caffeine.cache.Caffeine; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.jetbrains.annotations.NotNull; + +import java.net.URI; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.TimeUnit; + +@Slf4j +public class UrlCache { + private static final String INTERNAL_GATEWAY = "internal-gateway"; + private static final int CACHE_SIZE = 400; + private static final long CACHE_DURATION_SECONDS = TimeUnit.HOURS.toSeconds(5); + private final Cache cache; + + public UrlCache() { + this(CACHE_SIZE, CACHE_DURATION_SECONDS); + } + + public UrlCache(final int cacheSize, final long ttlSeconds) { + this.cache = Caffeine.newBuilder() + .maximumSize(cacheSize) + .expireAfterAccess(ttlSeconds, TimeUnit.SECONDS) + .build(); + } + + public void store(@NotNull final String key) { + cache.put(key, Boolean.TRUE); + } + + public boolean containsKey(@NotNull final String key) { + return cache.getIfPresent(key) != null; + } + + public static String calculateCacheKey(final String rawUrl) { + URI parsedURI; + try { + parsedURI = new URI(rawUrl); + } catch (Exception ex) { + throw new RuntimeException("Failed during parsing of URL: ", ex); //NOSONAR + } + return calculateCacheKey(parsedURI); + } + + public static String calculateCacheKey(final URI parsedURI) { + return parsedURI.getHost().contains(INTERNAL_GATEWAY) + ? calculateCacheKeyForInternalGateway(parsedURI) + : parsedURI.getHost() + ":" + parsedURI.getPort(); + } + + private static String calculateCacheKeyForInternalGateway(final URI parsedUri) { + final String[] segments = StringUtils.strip(parsedUri.getPath(), "/").split("/"); + final List filteredSegments = new ArrayList<>(); + + String version = ""; + String serviceName = ""; + + for (String segment : segments) { + if (StringUtils.isNotEmpty(version)) { + serviceName = segment; + break; + } + filteredSegments.add(segment); + if (isVersion(segment)) { + version = segment; + } + } + + if (StringUtils.isEmpty(version)) { + log.debug("internal-gateway url does not contain any version; whole path will be used as a key for m2m decision cache"); + } + String key = parsedUri.getHost() + ":" + parsedUri.getPort() + "/" + StringUtils.join(filteredSegments, "/"); + if (parsedUri.getPath().startsWith("/api") && StringUtils.isNotEmpty(serviceName)) { + key = key + "/" + serviceName; + } + + return key; + } + + private static boolean isVersion(final String segment) { + if (segment.length() < 2 || segment.charAt(0) != 'v') + return false; + return segment.substring(1).matches("\\d+"); + } +} diff --git a/core-utils/k8s/src/test/java/com/netcracker/cloud/security/core/utils/k8s/impl/M2MInterceptorTest.java b/core-utils/k8s/src/test/java/com/netcracker/cloud/security/core/utils/k8s/impl/M2MInterceptorTest.java new file mode 100644 index 000000000..88098981d --- /dev/null +++ b/core-utils/k8s/src/test/java/com/netcracker/cloud/security/core/utils/k8s/impl/M2MInterceptorTest.java @@ -0,0 +1,185 @@ +package com.netcracker.cloud.security.core.utils.k8s.impl; + +import org.junit.jupiter.api.Test; +import com.github.tomakehurst.wiremock.WireMockServer; +import com.github.tomakehurst.wiremock.client.WireMock; +import lombok.SneakyThrows; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.Response; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.mockito.Mockito; + +import java.util.function.Supplier; + +import static com.github.tomakehurst.wiremock.client.WireMock.*; +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.when; + +class M2MInterceptorTest { + private static final String TEST_ENDPOINT = "/test/endpoint"; + private static final int TEST_CACHE_SIZE = 10; + private static final long TEST_CACHE_DURATION_SEC = 60; + + private WireMockServer wireMockServer; + private OkHttpClient client; + + private Supplier fallbackSupplier; + private Supplier k8sSupplier; + + private static final String K8S_TOKEN_HEADER = "Bearer k8s-test-token"; + private static final String FALLBACK_TOKEN_HEADER = "Bearer fallback-test-token"; + + @BeforeEach + @SuppressWarnings("unchecked") + void beforeEach() { + System.setProperty("security.m2m.kubernetes.enabled", "true"); + + wireMockServer = new WireMockServer(0); + wireMockServer.start(); + WireMock.configureFor("localhost", wireMockServer.port()); + + UrlCache urlCache = new UrlCache(TEST_CACHE_SIZE, TEST_CACHE_DURATION_SEC); + fallbackSupplier = Mockito.mock(Supplier.class); + k8sSupplier = Mockito.mock(Supplier.class); + + // Default behavior: return valid tokens + when(k8sSupplier.get()).thenReturn(K8S_TOKEN_HEADER); + when(fallbackSupplier.get()).thenReturn(FALLBACK_TOKEN_HEADER); + + final M2MInterceptor interceptor = new M2MInterceptor(urlCache, fallbackSupplier, k8sSupplier); + + client = new OkHttpClient.Builder() + .addInterceptor(interceptor) + .build(); + } + + @AfterEach + void afterEach() { + wireMockServer.stop(); + System.clearProperty("security.m2m.kubernetes.enabled"); + } + + @Test + @SneakyThrows + void kubernetesTokenAuth_Success() { + stubFor(get(urlEqualTo(TEST_ENDPOINT)) + .withHeader("Authorization", equalTo(K8S_TOKEN_HEADER)) + .willReturn(aResponse().withStatus(200))); + + try (Response response = client.newCall(buildRequest()).execute()) { + assertEquals(200, response.code()); + } + + verify(1, getRequestedFor(urlEqualTo(TEST_ENDPOINT))); + } + + @Test + @SneakyThrows + void keycloakTokenAuth_UnauthorizedFallback() { + // 1. First call with K8s token returns 401 + stubFor(get(urlEqualTo(TEST_ENDPOINT)) + .withHeader("Authorization", equalTo(K8S_TOKEN_HEADER)) + .willReturn(aResponse().withStatus(401))); + + // 2. Fallback call with Keycloak token returns 200 + stubFor(get(urlEqualTo(TEST_ENDPOINT)) + .withHeader("Authorization", equalTo(FALLBACK_TOKEN_HEADER)) + .willReturn(aResponse().withStatus(200))); + + try (Response response = client.newCall(buildRequest()).execute()) { + assertEquals(200, response.code()); + } + + // Verify both requests were made + verify(1, getRequestedFor(urlEqualTo(TEST_ENDPOINT)).withHeader("Authorization", equalTo(K8S_TOKEN_HEADER))); + verify(1, getRequestedFor(urlEqualTo(TEST_ENDPOINT)).withHeader("Authorization", equalTo(FALLBACK_TOKEN_HEADER))); + + // 3. Second call should go STRAIGHT to fallback because URL is now cached as "non-k8s" + try (Response response = client.newCall(buildRequest()).execute()) { + assertEquals(200, response.code()); + } + + // Total count for fallback should be 2, but K8s should still be 1 + verify(1, getRequestedFor(urlEqualTo(TEST_ENDPOINT)).withHeader("Authorization", equalTo(K8S_TOKEN_HEADER))); + verify(2, getRequestedFor(urlEqualTo(TEST_ENDPOINT)).withHeader("Authorization", equalTo(FALLBACK_TOKEN_HEADER))); + } + + @Test + @SneakyThrows + void kubernetesTokenAcquisitionError_Fallback() { + // Simulate acquisition error + when(k8sSupplier.get()).thenThrow(new IllegalStateException("K8s failed")); + + stubFor(get(urlEqualTo(TEST_ENDPOINT)) + .withHeader("Authorization", equalTo(FALLBACK_TOKEN_HEADER)) + .willReturn(aResponse().withStatus(200))); + + try (Response response = client.newCall(buildRequest()).execute()) { + assertEquals(200, response.code()); + } + + // Verify it never tried K8s at the network level and went straight to fallback + verify(0, getRequestedFor(urlEqualTo(TEST_ENDPOINT)).withHeader("Authorization", equalTo(K8S_TOKEN_HEADER))); + verify(1, getRequestedFor(urlEqualTo(TEST_ENDPOINT)).withHeader("Authorization", equalTo(FALLBACK_TOKEN_HEADER))); + } + + @Test + @SneakyThrows + void bothTokensEmpty_ThrowsException() { + when(k8sSupplier.get()).thenReturn(""); + when(fallbackSupplier.get()).thenReturn(""); + + assertThrows(IllegalStateException.class, () -> { + client.newCall(buildRequest()).execute(); + }); + } + + private Request buildRequest() { + return new Request.Builder() + .url(wireMockServer.baseUrl() + TEST_ENDPOINT) + .get() + .build(); + } + + @Test + @SneakyThrows + void fallbackUrl_RebasesHostWhenFallbackOccurs() { + WireMockServer fallbackServer = new WireMockServer(0); + fallbackServer.start(); + WireMock.configureFor("localhost", fallbackServer.port()); + + fallbackServer.stubFor(get(urlEqualTo(TEST_ENDPOINT)) + .withHeader("Authorization", equalTo(FALLBACK_TOKEN_HEADER)) + .willReturn(aResponse().withStatus(200))); + + wireMockServer.stubFor(get(urlEqualTo(TEST_ENDPOINT)) + .withHeader("Authorization", equalTo(K8S_TOKEN_HEADER)) + .willReturn(aResponse().withStatus(401))); + + UrlCache urlCache = new UrlCache(TEST_CACHE_SIZE, TEST_CACHE_DURATION_SEC); + String fallbackBaseUrl = "http://localhost:" + fallbackServer.port(); + + M2MInterceptor interceptor = new M2MInterceptor(urlCache, fallbackSupplier, k8sSupplier, fallbackBaseUrl); + OkHttpClient clientWithFallbackUrl = new OkHttpClient.Builder() + .addInterceptor(interceptor) + .build(); + + Request request = new Request.Builder() + .url(wireMockServer.baseUrl() + TEST_ENDPOINT) + .get() + .build(); + + try (Response response = clientWithFallbackUrl.newCall(request).execute()) { + assertEquals(200, response.code()); + } + + wireMockServer.verify(1, getRequestedFor(urlEqualTo(TEST_ENDPOINT)) + .withHeader("Authorization", equalTo(K8S_TOKEN_HEADER))); + fallbackServer.verify(1, getRequestedFor(urlEqualTo(TEST_ENDPOINT)) + .withHeader("Authorization", equalTo(FALLBACK_TOKEN_HEADER))); + + fallbackServer.stop(); + } +} diff --git a/core-utils/k8s/src/test/java/com/netcracker/cloud/security/core/utils/k8s/impl/UrlCacheTest.java b/core-utils/k8s/src/test/java/com/netcracker/cloud/security/core/utils/k8s/impl/UrlCacheTest.java new file mode 100644 index 000000000..a450fe4dd --- /dev/null +++ b/core-utils/k8s/src/test/java/com/netcracker/cloud/security/core/utils/k8s/impl/UrlCacheTest.java @@ -0,0 +1,38 @@ +package com.netcracker.cloud.security.core.utils.k8s.impl; + +import org.junit.jupiter.api.Test; + +import static com.netcracker.cloud.security.core.utils.k8s.impl.UrlCache.calculateCacheKey; +import static org.junit.jupiter.api.Assertions.*; + +class UrlCacheTest { + + @Test + void calculateCacheKeyTest() { + String key = calculateCacheKey("https://internal-gateway:3030/api/v1/service-a/resource/123"); + assertEquals("internal-gateway:3030/api/v1/service-a", key); + + key = calculateCacheKey("https://internal-gateway:3030/api/v1"); + assertEquals("internal-gateway:3030/api/v1", key); + + key = calculateCacheKey("https://internal-gateway:3030/custom-prefix/api/v2/module-b/action"); + assertEquals("internal-gateway:3030/custom-prefix/api/v2", key); + + key = calculateCacheKey("https://internal-gateway:3030/long/complex/path/v3/target/item"); + assertEquals("internal-gateway:3030/long/complex/path/v3", key); + + key = calculateCacheKey("https://internal-gateway:3030/api/v/resource"); + assertEquals("internal-gateway:3030/api/v/resource", key); + + key = calculateCacheKey("https://internal-gateway:3030/api/vv/resource"); + assertEquals("internal-gateway:3030/api/vv/resource", key); + + key = calculateCacheKey("https://internal-gateway:3030/api/v1/service?query=param&data=true"); + assertEquals("internal-gateway:3030/api/v1/service", key); + + key = calculateCacheKey("https://external-service:8080/api/v1/resource"); + assertEquals("external-service:8080", key); + + assertThrows(RuntimeException.class, () -> calculateCacheKey("illegal characters here")); + } +} diff --git a/core-utils/k8s/src/test/resources/logback.xml b/core-utils/k8s/src/test/resources/logback.xml index 9fa50918a..473e4e83e 100644 --- a/core-utils/k8s/src/test/resources/logback.xml +++ b/core-utils/k8s/src/test/resources/logback.xml @@ -1,17 +1,19 @@ - - %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} -%kvp- %msg%n - + + + + + diff --git a/dbaas-client/dbaas-client-java/dbaas-client-core/src/main/java/com/netcracker/cloud/dbaas/client/config/DbaasClientConfiguration.java b/dbaas-client/dbaas-client-java/dbaas-client-core/src/main/java/com/netcracker/cloud/dbaas/client/config/DbaasClientConfiguration.java index c4ffd1a9d..3781a5832 100644 --- a/dbaas-client/dbaas-client-java/dbaas-client-core/src/main/java/com/netcracker/cloud/dbaas/client/config/DbaasClientConfiguration.java +++ b/dbaas-client/dbaas-client-java/dbaas-client-core/src/main/java/com/netcracker/cloud/dbaas/client/config/DbaasClientConfiguration.java @@ -78,4 +78,3 @@ DbaasClassifierFactory dbaasClassifierFactory(MSInfoProvider msInfoProvider) { return new DbaasClassifierFactory(msInfoProvider); } } - diff --git a/dbaas-client/dbaas-client-java/dbaas-client-core/src/main/java/com/netcracker/cloud/dbaas/client/config/SpringDbaasApiProperties.java b/dbaas-client/dbaas-client-java/dbaas-client-core/src/main/java/com/netcracker/cloud/dbaas/client/config/SpringDbaasApiProperties.java index d71c3687b..eff84933d 100644 --- a/dbaas-client/dbaas-client-java/dbaas-client-core/src/main/java/com/netcracker/cloud/dbaas/client/config/SpringDbaasApiProperties.java +++ b/dbaas-client/dbaas-client-java/dbaas-client-core/src/main/java/com/netcracker/cloud/dbaas/client/config/SpringDbaasApiProperties.java @@ -2,17 +2,27 @@ import lombok.AccessLevel; import lombok.Getter; +import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; import java.util.Optional; +@Slf4j @Getter public class SpringDbaasApiProperties { private static final String DEFAULT_DBAAS_AGENT_URL = "http://dbaas-agent:8080"; @Getter(AccessLevel.NONE) @Value("${dbaas.api.address:#{null}}") - private Optional address; + private Optional dbaasAgentAddress; + + @Getter(AccessLevel.NONE) + @Value("${api.dbaas.address:#{null}}") + private Optional dbaasAddress; + + @Getter(AccessLevel.NONE) + @Value("${security.m2m.kubernetes.enabled:false}") + private boolean k8sEnabled; @Value("${dbaas.api.retry.default.template.maxAttempts:10}") private int dbaasDefaultRetryMaxAttempts; @@ -24,6 +34,13 @@ public class SpringDbaasApiProperties { private int dbaasAsyncRetryTimeoutInS; public String getAddress() { - return address.orElse(DEFAULT_DBAAS_AGENT_URL); + if(!k8sEnabled) { + return dbaasAgentAddress.orElse(DEFAULT_DBAAS_AGENT_URL); + } + if(dbaasAddress.isEmpty()) { + log.warn("DBaaS address is not available, falling back to dbaas-agent. Specify 'api.dbaas.address' property to DBaaS url"); + return dbaasAgentAddress.orElse(DEFAULT_DBAAS_AGENT_URL); + } + return dbaasAddress.get(); } } diff --git a/dbaas-client/dbaas-client-java/dbaas-client-core/src/test/java/com/netcracker/cloud/dbaas/client/DbaasClientRetryTest.java b/dbaas-client/dbaas-client-java/dbaas-client-core/src/test/java/com/netcracker/cloud/dbaas/client/DbaasClientRetryTest.java index 1dce9d926..01881957c 100644 --- a/dbaas-client/dbaas-client-java/dbaas-client-core/src/test/java/com/netcracker/cloud/dbaas/client/DbaasClientRetryTest.java +++ b/dbaas-client/dbaas-client-java/dbaas-client-core/src/test/java/com/netcracker/cloud/dbaas/client/DbaasClientRetryTest.java @@ -12,6 +12,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.TestPropertySource; import org.springframework.test.context.junit.jupiter.SpringExtension; import java.net.URI; @@ -22,6 +23,7 @@ @ExtendWith(SpringExtension.class) @ContextConfiguration(classes = {TestDbaasCoreConfiguration.class}) +@TestPropertySource(properties = "api.dbaas.address=http://ms-name.namespace:8080") public class DbaasClientRetryTest { @Autowired diff --git a/dbaas-client/dbaas-client-java/dbaas-client-core/src/test/resources/application.yml b/dbaas-client/dbaas-client-java/dbaas-client-core/src/test/resources/application.yml index 3aefce013..31acfef01 100644 --- a/dbaas-client/dbaas-client-java/dbaas-client-core/src/test/resources/application.yml +++ b/dbaas-client/dbaas-client-java/dbaas-client-core/src/test/resources/application.yml @@ -1,3 +1,7 @@ spring: main: - allow-bean-definition-overriding: true \ No newline at end of file + allow-bean-definition-overriding: true + +api: + dbaas: + address: http://ms-name.namespace:8080 diff --git a/dbaas-client/dbaas-client-java/dbaas-client-restclient/dbaas-client-resttemplate/pom.xml b/dbaas-client/dbaas-client-java/dbaas-client-restclient/dbaas-client-resttemplate/pom.xml index 3ce967a3b..05610bb9b 100644 --- a/dbaas-client/dbaas-client-java/dbaas-client-restclient/dbaas-client-resttemplate/pom.xml +++ b/dbaas-client/dbaas-client-java/dbaas-client-restclient/dbaas-client-resttemplate/pom.xml @@ -31,6 +31,16 @@ com.netcracker.cloud.security.core.utils tls-utils + + com.netcracker.cloud.security.core.utils + k8s-utils + 3.1.0-SNAPSHOT + + + com.netcracker.cloud + microservice-restclient-okhttp + 7.1.0-SNAPSHOT + diff --git a/dbaas-client/dbaas-client-java/dbaas-client-restclient/dbaas-client-resttemplate/src/main/java/com/netcracker/cloud/dbaas/client/restclient/resttemplate/DbaasRestTemplateConfiguration.java b/dbaas-client/dbaas-client-java/dbaas-client-restclient/dbaas-client-resttemplate/src/main/java/com/netcracker/cloud/dbaas/client/restclient/resttemplate/DbaasRestTemplateConfiguration.java index ad19034dd..d98ae7464 100644 --- a/dbaas-client/dbaas-client-java/dbaas-client-restclient/dbaas-client-resttemplate/src/main/java/com/netcracker/cloud/dbaas/client/restclient/resttemplate/DbaasRestTemplateConfiguration.java +++ b/dbaas-client/dbaas-client-java/dbaas-client-restclient/dbaas-client-resttemplate/src/main/java/com/netcracker/cloud/dbaas/client/restclient/resttemplate/DbaasRestTemplateConfiguration.java @@ -1,9 +1,11 @@ package com.netcracker.cloud.dbaas.client.restclient.resttemplate; import com.netcracker.cloud.restclient.MicroserviceRestClient; +import com.netcracker.cloud.restclient.okhttp.MicroserviceOkHttpRestClient; import com.netcracker.cloud.restclient.resttemplate.MicroserviceRestTemplate; import com.netcracker.cloud.restlegacy.resttemplate.configuration.annotation.EnableFrameworkRestTemplate; -import org.springframework.beans.factory.annotation.Qualifier; +import com.netcracker.cloud.security.core.auth.M2MManager; +import com.netcracker.cloud.security.core.utils.k8s.M2MClientFactory; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -14,7 +16,8 @@ @ConditionalOnProperty(value = "dbaas.restclient.resttemplate.basic-auth", havingValue = "false", matchIfMissing = true) public class DbaasRestTemplateConfiguration { @Bean("dbaasRestClient") - public MicroserviceRestClient dbaasRestClient(@Qualifier("m2mRestTemplate") RestTemplate restTemplate){ - return new MicroserviceRestTemplate(restTemplate); + public MicroserviceRestClient dbaasRestClient(M2MManager m2MManager){ + var client = M2MClientFactory.getDbaasOkHttpClient(() -> m2MManager.getToken().getTokenValue()); + return new MicroserviceOkHttpRestClient(client); } } diff --git a/dbaas-client/dbaas-client-java/dbaas-client-restclient/dbaas-client-webclient/pom.xml b/dbaas-client/dbaas-client-java/dbaas-client-restclient/dbaas-client-webclient/pom.xml index ca793b544..e1fa7af4f 100644 --- a/dbaas-client/dbaas-client-java/dbaas-client-restclient/dbaas-client-webclient/pom.xml +++ b/dbaas-client/dbaas-client-java/dbaas-client-restclient/dbaas-client-webclient/pom.xml @@ -18,6 +18,16 @@ webclient ${project.groupId} + + com.netcracker.cloud.security.core.utils + k8s-utils + 3.1.0-SNAPSHOT + + + com.netcracker.cloud + microservice-restclient-okhttp + 7.1.0-SNAPSHOT + diff --git a/dbaas-client/dbaas-client-java/dbaas-client-restclient/dbaas-client-webclient/src/main/java/com/netcracker/cloud/dbaas/client/restclient/webclient/DbaasWebClientConfiguration.java b/dbaas-client/dbaas-client-java/dbaas-client-restclient/dbaas-client-webclient/src/main/java/com/netcracker/cloud/dbaas/client/restclient/webclient/DbaasWebClientConfiguration.java index c47208395..05cb0f563 100644 --- a/dbaas-client/dbaas-client-java/dbaas-client-restclient/dbaas-client-webclient/src/main/java/com/netcracker/cloud/dbaas/client/restclient/webclient/DbaasWebClientConfiguration.java +++ b/dbaas-client/dbaas-client-java/dbaas-client-restclient/dbaas-client-webclient/src/main/java/com/netcracker/cloud/dbaas/client/restclient/webclient/DbaasWebClientConfiguration.java @@ -20,17 +20,8 @@ public class DbaasWebClientConfiguration { @Bean("dbaasRestClient") public MicroserviceRestClient dbaasRestClient(@Qualifier("m2mWebClient") WebClient webClient) { - WebClient customizedWebClient = webClient.mutate() - .filters(new DisableHttpTraceFilterConsumer()) - .codecs(clientCodecsConfigurer -> clientCodecsConfigurer.defaultCodecs() - .configureDefaultCodec(o -> { - if (o instanceof Jackson2JsonDecoder decoder) { - decoder.getObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); - } - })) - .build(); - - return new MicroserviceWebClient(customizedWebClient); + var client = M2MClientFactory.getDbaasOkHttpClient(() -> m2MManager.getToken().getTokenValue()); + return new MicroserviceOkHttpRestClient(client); } // If sleuth enabled, it tries to get db health from http filters. But dataSource can be not initialized yet. diff --git a/maas-client/client/pom.xml b/maas-client/client/pom.xml index 30fada87f..a45cafea5 100644 --- a/maas-client/client/pom.xml +++ b/maas-client/client/pom.xml @@ -53,6 +53,11 @@ org.slf4j slf4j-api + + com.netcracker.cloud.security.core.utils + k8s-utils + 3.1.0-SNAPSHOT + diff --git a/maas-client/client/src/main/java/com/netcracker/cloud/maas/client/impl/Env.java b/maas-client/client/src/main/java/com/netcracker/cloud/maas/client/impl/Env.java index c9aa5694c..3e32738e0 100644 --- a/maas-client/client/src/main/java/com/netcracker/cloud/maas/client/impl/Env.java +++ b/maas-client/client/src/main/java/com/netcracker/cloud/maas/client/impl/Env.java @@ -23,20 +23,32 @@ public class Env { static final String ENV_CLOUD_NAMESPACE = "CLOUD_NAMESPACE"; static final String ENV_ORIGIN_NAMESPACE = "ORIGIN_NAMESPACE"; static final String ENV_MICROSERVICE_NAME = "MICROSERVICE_NAME"; + static final String ENV_K8S_ENABLED = "SECURITY_M2M_KUBERNETES_ENABLED"; public static final String PROP_CLOUD_NAMESPACE = "cloud.microservice.namespace"; public static final String PROP_NAMESPACE = "maas.client.classifier.namespace"; //todo deprecated - delete in the next major release public static final String PROP_ORIGIN_NAMESPACE = "origin_namespace"; //todo change to 'origin.namespace' - public static final String PROP_API_URL = "maas.client.api.url"; + public static final String PROP_MAAS_AGENT_URL = "maas.client.api.url"; + public static final String PROP_MAAS_URL = "maas.internal.address"; public static final String PROP_API_AUTH = "maas.client.api.auth"; public static final String PROP_TENANT_MANAGER_URL = "maas.client.tenant-manager.url"; public static final String PROP_TENANT_MANAGER_RECONNECT_TIMEOUT = "maas.client.tenant-manager.reconnect-timeout"; public static final String PROP_HTTP_TIMEOUT = "maas.http.timeout"; public static String apiUrl() { - return stringProperty(PROP_API_URL) + boolean k8sEnabled = Boolean.parseBoolean(System.getenv().get(ENV_K8S_ENABLED)); + String maasAgentUrl = stringProperty(PROP_MAAS_AGENT_URL) .map(Env::normalizeUrl) .orElse(addr2http("maas-agent")); + if(!k8sEnabled) { + return maasAgentUrl; + } + return stringProperty(PROP_MAAS_URL) + .map(Env::normalizeUrl) + .orElseGet(() -> { + log.warn("MaaS address is not available, falling back to maas-agent. Specify '{}'property to MaaS url", PROP_MAAS_URL); + return maasAgentUrl; + }); } public static String apiAuth() { diff --git a/maas-client/client/src/main/java/com/netcracker/cloud/maas/client/impl/MaaSAPIClientImpl.java b/maas-client/client/src/main/java/com/netcracker/cloud/maas/client/impl/MaaSAPIClientImpl.java index 49a66ad8e..e8717678b 100644 --- a/maas-client/client/src/main/java/com/netcracker/cloud/maas/client/impl/MaaSAPIClientImpl.java +++ b/maas-client/client/src/main/java/com/netcracker/cloud/maas/client/impl/MaaSAPIClientImpl.java @@ -20,14 +20,14 @@ public class MaaSAPIClientImpl implements MaaSAPIClient { private final ApiUrlProvider apiProvider; public MaaSAPIClientImpl(Supplier tokenSupplier) { - this.restClient = new HttpClient(tokenSupplier); + this.restClient = HttpClient.getMaasClient(tokenSupplier); this.serverApiVersion = new ServerApiVersion(restClient, Env.apiUrl()); - this.tenantManagerConnector = new Lazy<>(() -> new TenantManagerConnectorImpl(restClient)); + this.tenantManagerConnector = new Lazy<>(() -> new TenantManagerConnectorImpl(HttpClient.getM2mClient(tokenSupplier))); this.apiProvider = new ApiUrlProvider(serverApiVersion, Env.apiUrl()); } public MaaSAPIClientImpl(Supplier tokenSupplier, TenantManagerConnector tenantManagerConnector, BlueGreenStatePublisher statePublisher) { - this.restClient = new HttpClient(tokenSupplier); + this.restClient = HttpClient.getMaasClient(tokenSupplier); this.serverApiVersion = new ServerApiVersion(restClient, Env.apiUrl()); this.tenantManagerConnector = new Lazy<>(() -> tenantManagerConnector); this.apiProvider = new ApiUrlProvider(serverApiVersion, Env.apiUrl()); diff --git a/maas-client/client/src/main/java/com/netcracker/cloud/maas/client/impl/http/HttpClient.java b/maas-client/client/src/main/java/com/netcracker/cloud/maas/client/impl/http/HttpClient.java index a7b2f366d..c19152069 100644 --- a/maas-client/client/src/main/java/com/netcracker/cloud/maas/client/impl/http/HttpClient.java +++ b/maas-client/client/src/main/java/com/netcracker/cloud/maas/client/impl/http/HttpClient.java @@ -2,6 +2,7 @@ import com.netcracker.cloud.context.propagation.core.RequestContextPropagation; import com.netcracker.cloud.maas.client.impl.Env; +import com.netcracker.cloud.security.core.utils.k8s.M2MClientFactory; import okhttp3.OkHttpClient; import okhttp3.Request; @@ -10,16 +11,21 @@ public class HttpClient { private final OkHttpClient httpClient; - public HttpClient(Supplier tokenSupplier) { - this.httpClient = new OkHttpClient.Builder() + public static HttpClient getM2mClient(Supplier tokenSupplier) { + return new HttpClient(M2MClientFactory.getM2mOkHttpClient(tokenSupplier)); + } + + public static HttpClient getMaasClient(Supplier tokenSupplier) { + return new HttpClient(M2MClientFactory.getMaasOkHttpClient(tokenSupplier)); + } + + private HttpClient(OkHttpClient client) { + this.httpClient = client.newBuilder() .addInterceptor(chain -> { Request.Builder reqBuilder = chain.request().newBuilder(); // dump context RequestContextPropagation.populateResponse((key, value) -> reqBuilder.header(key, String.valueOf(value))); - - // add authorization token - reqBuilder.header("Authorization", Env.apiAuth() + " " + tokenSupplier.get()); Env.namespaceOpt().ifPresent(ns -> reqBuilder.header("X-Origin-Namespace", ns)); // process request diff --git a/maas-client/client/src/test/java/com/netcracker/cloud/maas/client/impl/EnvTest.java b/maas-client/client/src/test/java/com/netcracker/cloud/maas/client/impl/EnvTest.java index 31048b5ad..205e9ed5b 100644 --- a/maas-client/client/src/test/java/com/netcracker/cloud/maas/client/impl/EnvTest.java +++ b/maas-client/client/src/test/java/com/netcracker/cloud/maas/client/impl/EnvTest.java @@ -11,25 +11,38 @@ class EnvTest { @Test void testApiUrl() { - withProp(Env.PROP_API_URL, null, () -> + withProp(Env.PROP_MAAS_AGENT_URL, null, () -> assertEquals("http://maas-agent:8080", Env.apiUrl()) ); } @Test void testApiUrlOverride() { - withProp(Env.PROP_API_URL, "http://localhost:8080/", () -> + withProp(Env.PROP_MAAS_AGENT_URL, "http://localhost:8080/", () -> assertEquals("http://localhost:8080", Env.apiUrl()) ); } @Test void testApiUrlWrongOverride() { - withProp(Env.PROP_API_URL, "localhost:8080", () -> - assertThrows(IllegalArgumentException.class, () -> Env.apiUrl()) + withProp(Env.PROP_MAAS_AGENT_URL, "localhost:8080", () -> + assertThrows(IllegalArgumentException.class, Env::apiUrl) ); } + @Test + void testApiUrlK8sEnabled() throws Exception { + withEnvironmentVariable(Env.ENV_K8S_ENABLED, "true") + .execute(() -> { + withProp(Env.PROP_MAAS_AGENT_URL, null, () -> + assertEquals("http://maas-agent:8080", Env.apiUrl()) + ); + withProp(Env.PROP_MAAS_URL, "http://localhost:8080/", () -> + assertEquals( "http://localhost:8080", Env.apiUrl()) + ); + }); + } + @Test void testTenantManagerReconnectTimeoutDefaults() { withProp(Env.PROP_TENANT_MANAGER_RECONNECT_TIMEOUT, null, () -> diff --git a/maas-client/client/src/test/java/com/netcracker/cloud/maas/client/impl/apiversion/ServerApiVersionTest.java b/maas-client/client/src/test/java/com/netcracker/cloud/maas/client/impl/apiversion/ServerApiVersionTest.java index fa0809339..f8829c92c 100644 --- a/maas-client/client/src/test/java/com/netcracker/cloud/maas/client/impl/apiversion/ServerApiVersionTest.java +++ b/maas-client/client/src/test/java/com/netcracker/cloud/maas/client/impl/apiversion/ServerApiVersionTest.java @@ -2,6 +2,7 @@ import com.netcracker.cloud.maas.client.Utils; import com.netcracker.cloud.maas.client.impl.http.HttpClient; +import com.netcracker.cloud.security.core.utils.k8s.M2MClientFactory; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockserver.integration.ClientAndServer; @@ -50,7 +51,9 @@ private ServerApiVersion setup(ClientAndServer mockServer, String version) { .withBody(Utils.readResourceAsString("api-version." + version + ".json")) ); - var httpClient = new HttpClient(() -> "faketoken"); + System.setProperty(M2MClientFactory.MAAS_AGENT_URL_PROP, "http://localhost:" + mockServer.getPort()); + var httpClient = HttpClient.getMaasClient(() -> "faketoken"); + System.clear(M2MClientFactory.MAAS_AGENT_URL_PROP, "http://localhost:" + mockServer.getPort()); return new ServerApiVersion(httpClient, "http://localhost:" + mockServer.getPort()); } } diff --git a/maas-client/client/src/test/java/com/netcracker/cloud/maas/client/impl/kafka/KafkaMaaSClientImplTest.java b/maas-client/client/src/test/java/com/netcracker/cloud/maas/client/impl/kafka/KafkaMaaSClientImplTest.java index f02a69d18..2124736c0 100644 --- a/maas-client/client/src/test/java/com/netcracker/cloud/maas/client/impl/kafka/KafkaMaaSClientImplTest.java +++ b/maas-client/client/src/test/java/com/netcracker/cloud/maas/client/impl/kafka/KafkaMaaSClientImplTest.java @@ -17,6 +17,7 @@ import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; +import com.netcracker.cloud.security.core.utils.k8s.M2MClientFactory; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -546,7 +547,7 @@ public void testGetOrCreateTopicV2(ClientAndServer mockServer) { @Test void testWatchTopicCreate(ClientAndServer mockServer) throws InterruptedException { withProp(Env.PROP_NAMESPACE, "cloud-dev", () -> { - withProp(Env.PROP_API_URL, "http://localhost:" + mockServer.getPort(), () -> { + withProp(Env.PROP_MAAS_AGENT_URL, "http://localhost:" + mockServer.getPort(), () -> { HttpRequest req = request().withMethod("POST").withPath("/api/v2/kafka/topic/watch-create"); ExpectationResponseCallback respWithError = httpRequest -> { @@ -581,7 +582,7 @@ void testWatchTopicCreate(ClientAndServer mockServer) throws InterruptedExceptio @Test void testTopicDeleteSuccess(ClientAndServer mockServer) throws Exception { withProp(Env.PROP_NAMESPACE, "cloud-dev", () -> { - withProp(Env.PROP_API_URL, "http://localhost:" + mockServer.getPort(), () -> { + withProp(Env.PROP_MAAS_AGENT_URL, "http://localhost:" + mockServer.getPort(), () -> { mockServer.when( request().withMethod("DELETE").withPath("/api/v2/kafka/topic"), Times.once() @@ -604,7 +605,7 @@ void testTopicDeleteSuccess(ClientAndServer mockServer) throws Exception { @Test void testTopicDeleteError(ClientAndServer mockServer) throws Exception { withProp(Env.PROP_NAMESPACE, "cloud-dev", () -> { - withProp(Env.PROP_API_URL, "http://localhost:" + mockServer.getPort(), () -> { + withProp(Env.PROP_MAAS_AGENT_URL, "http://localhost:" + mockServer.getPort(), () -> { mockServer.when( request().withMethod("DELETE").withPath("/api/v2/kafka/topic"), @@ -764,8 +765,10 @@ void testSearchTopic(ClientAndServer mockServer) { } private KafkaMaaSClientImpl createKafkaClient(String agentUrl) { - var httpClient = new HttpClient(() -> "faketoken"); + System.setProperty(M2MClientFactory.MAAS_AGENT_URL_PROP, agentUrl); + var httpClient = HttpClient.getMaasClient(() -> "faketoken"); var serverApiVersion = new ServerApiVersion(httpClient, agentUrl); + System.clearProperty(M2MClientFactory.MAAS_AGENT_URL_PROP, agentUrl); return new KafkaMaaSClientImpl( httpClient, diff --git a/maas-client/client/src/test/java/com/netcracker/cloud/maas/client/impl/kafka/KafkaWatchTenantTopicsTest.java b/maas-client/client/src/test/java/com/netcracker/cloud/maas/client/impl/kafka/KafkaWatchTenantTopicsTest.java index 58af89501..6920aa8c1 100644 --- a/maas-client/client/src/test/java/com/netcracker/cloud/maas/client/impl/kafka/KafkaWatchTenantTopicsTest.java +++ b/maas-client/client/src/test/java/com/netcracker/cloud/maas/client/impl/kafka/KafkaWatchTenantTopicsTest.java @@ -5,6 +5,7 @@ import com.netcracker.cloud.maas.client.impl.Env; import com.netcracker.cloud.maas.client.impl.apiversion.ServerApiVersion; import com.netcracker.cloud.maas.client.impl.http.HttpClient; +import com.netcracker.cloud.security.core.utils.k8s.M2MClientFactory; import com.netcracker.cloud.tenantmanager.client.impl.TenantManagerConnectorImpl; import com.netcracker.cloud.testharness.MaaSCocoonExtension; import com.netcracker.cloud.testharness.TenantManagerMockInject; @@ -50,13 +51,15 @@ public void resetMOckServer(ClientAndServer mockServer) { @Test public void testWatchEvents(ClientAndServer mockServer) throws Exception { withProp(Env.PROP_NAMESPACE, "core-dev", () -> { - HttpClient httpClient = new HttpClient(() -> "faketoken"); - var agentUrl = "http://localhost:" + mockServer.getPort(); + var agentUrl = "http://localhost:" + mockServer.getPort(); + System.setProperty(M2MClientFactory.MAAS_AGENT_URL_PROP, agentUrl); + + HttpClient httpClient = HttpClient.getMaasClient(() -> "faketoken"); var serverApiVersion = new ServerApiVersion(httpClient, agentUrl); KafkaMaaSClientImpl client = new KafkaMaaSClientImpl( httpClient, - () -> new TenantManagerConnectorImpl(tmMock.getUrl(), httpClient), + () -> new TenantManagerConnectorImpl(tmMock.getUrl(), HttpClient.getM2mClient(() -> "faketoken")), new ApiUrlProvider(serverApiVersion, agentUrl)); BlockingQueue> events = new LinkedBlockingDeque<>(); @@ -130,11 +133,13 @@ public void testWatchEvents(ClientAndServer mockServer) throws Exception { @Test public void testWatchEvents_ButTopicsNotFoundInMaaS(ClientAndServer mockServer) throws Exception { withProp(Env.PROP_NAMESPACE, "core-dev", () -> { - HttpClient httpClient = new HttpClient(() -> "faketoken"); - var agentUrl = "http://localhost:" + mockServer.getPort(); + var agentUrl = "http://localhost:" + mockServer.getPort(); + System.setProperty(M2MClientFactory.MAAS_AGENT_URL_PROP, agentUrl); + + HttpClient httpClient = HttpClient.getMaasClient(() -> "faketoken"); KafkaMaaSClientImpl client = new KafkaMaaSClientImpl( httpClient, - () -> new TenantManagerConnectorImpl(tmMock.getUrl(), httpClient), + () -> new TenantManagerConnectorImpl(tmMock.getUrl(), HttpClient.getM2mClient(() -> "faketoken")), new ApiUrlProvider(new ServerApiVersion(httpClient, agentUrl), agentUrl)); BlockingQueue> events = new LinkedBlockingDeque<>(); diff --git a/maas-client/client/src/test/java/com/netcracker/cloud/maas/client/impl/rabbit/RabbitMaaSClientImplTest.java b/maas-client/client/src/test/java/com/netcracker/cloud/maas/client/impl/rabbit/RabbitMaaSClientImplTest.java index 6badea5fb..a5fae2cb5 100644 --- a/maas-client/client/src/test/java/com/netcracker/cloud/maas/client/impl/rabbit/RabbitMaaSClientImplTest.java +++ b/maas-client/client/src/test/java/com/netcracker/cloud/maas/client/impl/rabbit/RabbitMaaSClientImplTest.java @@ -7,6 +7,7 @@ import com.netcracker.cloud.maas.client.impl.Env; import com.netcracker.cloud.maas.client.impl.apiversion.ServerApiVersion; import com.netcracker.cloud.maas.client.impl.http.HttpClient; +import com.netcracker.cloud.security.core.utils.k8s.M2MClientFactory; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -146,6 +147,8 @@ public void testGetVHost(ClientAndServer mockServer) { assertEquals("scott", vhost.getUsername()); assertEquals("tiger", vhost.getPassword()); assertEquals("http://rabbit-cluster:15672/api", vhost.getApiUrl()); + + System.clearProperty(Env.PROP_NAMESPACE, "core-dev"); } @Test @@ -217,7 +220,8 @@ public void testVersionedQueueName1() { } private RabbitMaaSClientImpl createRabbitClient(String agentUrl) { - var httpClient = new HttpClient(() -> "faketoken"); + System.setProperty(M2MClientFactory.MAAS_AGENT_URL_PROP, agentUrl); + var httpClient = HttpClient.getMaasClient(() -> "faketoken"); var serverApiVersion = new ServerApiVersion(httpClient, agentUrl); return new RabbitMaaSClientImpl(httpClient, new ApiUrlProvider(serverApiVersion, agentUrl)); diff --git a/maas-client/client/src/test/java/com/netcracker/cloud/tenantmanager/client/impl/TenantManagerConnectorImplTest.java b/maas-client/client/src/test/java/com/netcracker/cloud/tenantmanager/client/impl/TenantManagerConnectorImplTest.java index dac2283d9..4596bf1c2 100644 --- a/maas-client/client/src/test/java/com/netcracker/cloud/tenantmanager/client/impl/TenantManagerConnectorImplTest.java +++ b/maas-client/client/src/test/java/com/netcracker/cloud/tenantmanager/client/impl/TenantManagerConnectorImplTest.java @@ -31,7 +31,7 @@ class TenantManagerConnectorImplTest { @Test public void testApi() throws Exception { BlockingQueue> events = new LinkedBlockingDeque<>(); - try (TenantManagerConnectorImpl client = new TenantManagerConnectorImpl(tmMock.getUrl(), new HttpClient(() -> "faketoken"))) { + try (TenantManagerConnectorImpl client = new TenantManagerConnectorImpl(tmMock.getUrl(), HttpClient.getM2mClient(() -> "faketoken"))) { client.subscribe(events::add); List tenants = events.poll(1, TimeUnit.SECONDS); assertNotNull(tenants); @@ -66,7 +66,7 @@ public void testApi() throws Exception { public void testReconnect() throws Exception { withProp(Env.PROP_TENANT_MANAGER_RECONNECT_TIMEOUT, "1", () -> { BlockingQueue> events = new LinkedBlockingDeque<>(); - try (TenantManagerConnectorImpl client = new TenantManagerConnectorImpl(tmMock.getUrl(), new HttpClient(() -> "faketoken"))) { + try (TenantManagerConnectorImpl client = new TenantManagerConnectorImpl(tmMock.getUrl(), HttpClient.getM2mClient(() -> "faketoken"))) { client.subscribe(events::add); List tenants = events.poll(1, TimeUnit.SECONDS); diff --git a/maas-declarative-client-quarkus/maas-kafka-quarkus-client/runtime/src/main/java/com/netcracker/maas/declarative/kafka/quarkus/client/config/MaasKafkaProdClientConfig.java b/maas-declarative-client-quarkus/maas-kafka-quarkus-client/runtime/src/main/java/com/netcracker/maas/declarative/kafka/quarkus/client/config/MaasKafkaProdClientConfig.java index 48b2d690c..fce4f4131 100644 --- a/maas-declarative-client-quarkus/maas-kafka-quarkus-client/runtime/src/main/java/com/netcracker/maas/declarative/kafka/quarkus/client/config/MaasKafkaProdClientConfig.java +++ b/maas-declarative-client-quarkus/maas-kafka-quarkus-client/runtime/src/main/java/com/netcracker/maas/declarative/kafka/quarkus/client/config/MaasKafkaProdClientConfig.java @@ -21,7 +21,7 @@ public class MaasKafkaProdClientConfig { @Singleton @Produces KafkaMaaSClient kafkaMaaSClient(MaasKafkaProps props, M2MManager m2mManager) { - HttpClient httpClient = new HttpClient(() -> m2mManager.getToken().getTokenValue()); + HttpClient httpClient = HttpClient.getM2mClient(() -> m2mManager.getToken().getTokenValue()); return new KafkaMaaSClientImpl( httpClient, () -> new TenantManagerConnectorImpl(httpClient), @@ -41,7 +41,7 @@ MaasKafkaTopicServiceProvider maasKafkaTopicServiceProvider(KafkaMaaSClient kafk @Produces @DefaultBean InternalTenantService internalTenantService(M2MManager m2mManager) { - HttpClient httpClient = new HttpClient(() -> m2mManager.getToken().getTokenValue()); + HttpClient httpClient = HttpClient.getM2mClient(() -> m2mManager.getToken().getTokenValue()); TenantManagerConnectorImpl tenantManagerConnector = new TenantManagerConnectorImpl(httpClient); return new InternalTenantServiceImpl(tenantManagerConnector); } diff --git a/maas-declarative-client-spring/maas-kafka-spring-client/src/main/java/com/netcracker/maas/declarative/kafka/spring/client/config/MaasKafkaProdClientConfig.java b/maas-declarative-client-spring/maas-kafka-spring-client/src/main/java/com/netcracker/maas/declarative/kafka/spring/client/config/MaasKafkaProdClientConfig.java index 46d95d69c..7dae07738 100644 --- a/maas-declarative-client-spring/maas-kafka-spring-client/src/main/java/com/netcracker/maas/declarative/kafka/spring/client/config/MaasKafkaProdClientConfig.java +++ b/maas-declarative-client-spring/maas-kafka-spring-client/src/main/java/com/netcracker/maas/declarative/kafka/spring/client/config/MaasKafkaProdClientConfig.java @@ -29,12 +29,12 @@ public class MaasKafkaProdClientConfig { @Bean HttpClient maasHttpClient(@Autowired M2MManager m2MManager) { - return new HttpClient(() -> m2MManager.getToken().getTokenValue()); + return HttpClient.getMaasClient(() -> m2MManager.getToken().getTokenValue()); } @Bean - TenantManagerConnector tenantManagerConnector(HttpClient httpClient) { - return new TenantManagerConnectorImpl(httpClient); + TenantManagerConnector tenantManagerConnector(@Autowired M2MManager m2MManager) { + return new TenantManagerConnectorImpl(HttpClient.getM2mClient(() -> m2MManager.getToken().getTokenValue())); } @Bean