diff --git a/docs/src/main/asciidoc/cloud-map.adoc b/docs/src/main/asciidoc/cloud-map.adoc new file mode 100644 index 000000000..2bb8b2496 --- /dev/null +++ b/docs/src/main/asciidoc/cloud-map.adoc @@ -0,0 +1,266 @@ +[#spring-cloud-aws-cloudmap] +== Spring Cloud AWS CloudMap +This article talks about the new Spring Cloud AWS CloudMap module. + +=== What is service discovery? + +Service discovery is the mechanism through which a microservices can locate other microservices in the network. Service discovery is a critical part of microservices architecture as it enables the microservices to identify and communicate with each other. + +There are two types of service discovery: Server-side and Client-side. + + 1. Server-side service discovery allows clients applications to find other services through a router (like API gateway or a load balancer). + 2. Client-side service discovery allows clients applications to find services by looking through or querying a service registry, in which service instances and endpoints are all within the service registry. + +=== What is AWS Cloud Map? +https://aws.amazon.com/cloud-map/[AWS Cloud Map] is a client-side service registry and service discovery solution provided as a ready-to-use, highly available service. Rather than build your own client service registry, you can leverage AWS Cloud Map to register your application and its running instances, and then use either the AWS Cloud Map API or DNS lookup to resolve a service's name to a current active endpoint. + +With Cloud Map, you can define custom names for your application resources, and it maintains the updated location of these dynamically changing resources. This increases your application availability because your web service always discovers the most up-to-date locations of its resources. + +=== Spring Cloud integration with AWS Cloud Map + +Spring Cloud AWS adds support for registering and discovering service using AWS Cloud Map through Spring Boot https://docs.spring.io/spring-boot/docs/current/reference/html/spring-boot-features.html#boot-features-external-config-files-importing[config import feature]. + +Maven coordinates, using <>: + +[source,xml] +---- + + io.awspring.cloud + spring-cloud-aws-starter-aws-cloudmap + +---- + + +=== Service registration +To register a microservice to Cloud Map we need to specify the following: + + 1. Namespace - A namespace is a way to group services for an application. When you create a namespace, you specify how you want to discover service instances that you register with AWS Cloud Map + 2. Service name - A service is a template for registering service instances, which allow you to locate the resources for an application using AWS Cloud Map `DiscoverInstances` API action + 3. Service Instance - A service instance contains information about how to locate a resource, such as a web server, for an application. After you register instances, you locate them by using AWS Cloud Map `DiscoverInstances` API action. + +Spring Cloud integration with AWS Cloud Map allows you to register a microservice to Cloud Map using the following configuration: + + 1. Automatic registration - Spring Cloud AWS Cloud Map module automatically registers the microservice to Cloud Map when the application starts. In order to do this, just include `spring-cloud-aws-starter-aws-cloudmap` dependency in your application, and Cloud Map integration module will register your microservice under `default-namespace` namespace and `spring.application.name or default-service` service name. + 2. Manual registration - Cloud Map properties can be provided part of the application configuration, here is a sample: + +```properties +spring.cloud.aws.cloudmap.registry.description=Namespace for sample cloudmap registry service +spring.cloud.aws.cloudmap.registry.port=80 +spring.cloud.aws.cloudmap.registry.service=a-service +spring.cloud.aws.cloudmap.registry.nameSpace=a-namespace +``` + +=== Service discovery +To discover a microservice using Cloud Map we need to specify the following: + +Enable `DiscoveryClient` - You can use `@EnableDiscoveryClient` annotation to integrate with Spring Cloud `DiscoveryClient`, here is a sample: + +```java +@SpringBootApplication +@EnableDiscoveryClient +public class SpringCloudAwsCloudMapSample { + @Autowired + private DiscoveryClient discoveryClient; + + @Bean + ApplicationRunner applicationRunner() { + return args -> LOGGER.info("Total instances: {}", discoveryClient.getServices().size()); + } +} +``` +* Using application configuration to specify the services - You can specify the services that can be discovered using `spring.cloud.aws.cloudmap.discovery.*` property, here is a sample: + +```properties +spring.cloud.aws.cloudmap.discovery.discoveryList[0].service=a-service #array of services +spring.cloud.aws.cloudmap.discovery.discoveryList[0].nameSpace=a-namespace +``` + +=== Using ServiceDiscoveryClient + +The starter automatically configures and registers a `ServiceDiscoveryClient` bean in the Spring application context. The `ServiceDiscoveryClient` bean can be used to register or discovery service instances from AWS Cloud Map. + +[source,java] +---- +import org.springframework.stereotype.Component; +import software.amazon.awssdk.services.servicediscovery.ServiceDiscoveryClient; +... +@Autowired +private ServiceDiscoveryClient serviceDiscoveryClient; +... +Map attributes = new HashMap<>(); +attributes.put(AWS_INSTANCE_IPV_4, registrationDetails.get(IPV_4_ADDRESS)); +attributes.put(REGION, System.getenv("AWS_REGION")); +attributes.put(NAMESPACE_ID, nameSpaceId); +attributes.put(SERVICE_ID, serviceId); +attributes.put(SERVICE_INSTANCE_ID, serviceInstanceId); + +// Register instance +final String operationId = serviceDiscovery.registerInstance(RegisterInstanceRequest.builder() + .instanceId(serviceInstanceId).serviceId(serviceId).attributes(attributes).build()) + .operationId(); +---- + +=== Customizing ServiceDiscoveryClient + +To use custom `ServiceDiscoveryClient` in `spring.config.import`, provide an implementation of `BootstrapRegistryInitializer`. For example: + +[source,java] +---- +package com.app; + +import software.amazon.awssdk.auth.credentials.AwsBasicCredentials; +import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider; +import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider; +import software.amazon.awssdk.regions.Region; +import software.amazon.awssdk.services.servicediscovery.ServiceDiscoveryClient; + +import org.springframework.boot.BootstrapRegistry; +import org.springframework.boot.BootstrapRegistryInitializer; + +class AWSCloudMapBootstrapConfiguration implements BootstrapRegistryInitializer { + + @Override + public void initialize(BootstrapRegistry registry) { + registry.register(ServiceDiscoveryClient.class, context -> { + AwsCredentialsProvider awsCredentialsProvider = StaticCredentialsProvider.create(AwsBasicCredentials.create("yourAccessKey", "yourSecretKey")); + return ServiceDiscoveryClient.builder().credentialsProvider(awsCredentialsProvider).region(Region.EU_WEST_2).build(); + }); + } +} +---- + +--- + +Note that this class must be listed under `org.springframework.boot.BootstrapRegistryInitializer` key in `META-INF/spring.factories`: + +[source, properties] +---- +org.springframework.boot.BootstrapRegistryInitializer=com.app.AWSCloudMapBootstrapConfiguration +---- + +Note that this class must be listed under `org.springframework.boot.BootstrapRegistryInitializer` key in `META-INF/spring.factories`: + +[source, properties] +---- +org.springframework.boot.BootstrapRegistryInitializer=com.app.AWSCloudMapBootstrapConfiguration +---- + +If you want to use autoconfigured `ServiceDiscoveryClient` but change underlying SDKClient or ClientOverrideConfiguration you will need to register bean of type `AwsClientConfigurerCloudMap`: +Autoconfiguration will configure `ServiceDiscoveryClient` Bean with provided values after that, for example: + +If you want to use autoconfigured `ServiceDiscoveryClient` but change underlying SDKClient or ClientOverrideConfiguration you will need to register bean of type `AwsClientConfigurerCloudMap`: +Autoconfiguration will configure `ServiceDiscoveryClient` Bean with provided values after that, for example: + +[source,java] +---- +package com.app; + +import io.awspring.cloud.autoconfigure.cloudmap.AwsCloudMapStoreClientCustomizer; +import java.time.Duration; +import org.springframework.boot.BootstrapRegistry; +import org.springframework.boot.BootstrapRegistryInitializer; +import software.amazon.awssdk.core.client.config.ClientOverrideConfiguration; +import software.amazon.awssdk.http.SdkHttpClient; +import software.amazon.awssdk.http.apache.ApacheHttpClient; +import software.amazon.awssdk.services.ssm.SsmClientBuilder; + +class AWSCloudMapBootstrapConfiguration implements BootstrapRegistryInitializer { + + @Override + public void initialize(BootstrapRegistry registry) { + registry.register(AwsCloudMapStoreClientCustomizer.class, + context -> new AwsCloudMapStoreClientCustomizer() { + + @Override + public ClientOverrideConfiguration overrideConfiguration() { + return ClientOverrideConfiguration.builder().apiCallTimeout(Duration.ofMillis(500)) + .build(); + } + + @Override + public SdkHttpClient httpClient() { + return ApacheHttpClient.builder().connectionTimeout(Duration.ofMillis(1000)).build(); + } + }); + } +} +---- + +=== Configuration + +The Spring Boot Starter for Cloud Map integration provides the following configuration options for service registration: + +[cols="4,3,1,1"] +|=== +| Name | Description | Required | Default value + +| `spring.cloud.aws.cloudmap.registry.description` | Namespace for sample cloudmap registry service. | No | `default-namespace` +| `spring.cloud.aws.cloudmap.registry.port` | Port in which the microservice is exposed. | No | `null` +| `spring.cloud.aws.cloudmap.registry.service` | Service name for registering the cloudmap service. | No | `default-service or spring.application.name` +| `spring.cloud.aws.cloudmap.region` | Configures region used by `ServiceDiscoveryClient`. | No | `null` +|=== + + +The Spring Boot Starter for Cloud Map integration provides the following configuration options for discovering services: + +[cols="4,3,1,1"] +|=== +| Name | Description | Required | Default value + +| `spring.cloud.aws.cloudmap.discovery.discoveryList[*].service` | Array of Cloudmap services the module will discover part of the startup. | No | `null` +| `spring.cloud.aws.cloudmap.discovery.discoveryList[*].nameSpace` | Array of Cloudmap namespaces the module will discover part of the startup. | No | `null` +| `spring.cloud.aws.cloudmap.region` | Configures region used by `ServiceDiscoveryClient`. | No | `null` +|=== + +=== IAM Permissions +Following IAM permissions are required by Spring Cloud AWS to register and discover services in AWS Cloud Map: + +[cols="1"] +|=== +| **Policies** +| `servicediscovery:ListServices` +| `servicediscovery:GetOperation` +| `servicediscovery:DiscoverInstances` +| `servicediscovery:DeleteNamespace` +| `servicediscovery:ListNamespaces` +| `servicediscovery:RegisterInstance` +| `servicediscovery:CreateService` +| `servicediscovery:DeregisterInstance` +| `servicediscovery:DeleteService` +| `servicediscovery:GetNamespace` +| `servicediscovery:GetInstance` +| `servicediscovery:GetService` +| `servicediscovery:ListInstances` +|=== + +Sample IAM policy: + +[source,json,indent=0] +---- +{ + "Version": "2012-10-17", + "Statement": [ + { + "Sid": "Sid1", + "Effect": "Allow", + "Action": [ + "servicediscovery:ListServices", + "servicediscovery:GetOperation", + "servicediscovery:DiscoverInstances", + "servicediscovery:DeleteNamespace", + "servicediscovery:ListNamespaces", + "servicediscovery:RegisterInstance", + "servicediscovery:CreateService", + "servicediscovery:DeregisterInstance", + "servicediscovery:DeleteService", + "servicediscovery:GetNamespace", + "servicediscovery:GetInstance", + "servicediscovery:GetService", + "servicediscovery:ListInstances" + ], + "Resource": "*" + } + ] +} +---- + diff --git a/pom.xml b/pom.xml index ba1b7e7b3..4f76199eb 100644 --- a/pom.xml +++ b/pom.xml @@ -33,6 +33,7 @@ spring-cloud-aws-core + spring-cloud-aws-cloudmap spring-cloud-aws-autoconfigure spring-cloud-aws-dependencies spring-cloud-aws-parameter-store diff --git a/spring-cloud-aws-autoconfigure/pom.xml b/spring-cloud-aws-autoconfigure/pom.xml index ad70b0f8b..084c96996 100644 --- a/spring-cloud-aws-autoconfigure/pom.xml +++ b/spring-cloud-aws-autoconfigure/pom.xml @@ -65,6 +65,11 @@ spring-cloud-aws-sqs true + + io.awspring.cloud + spring-cloud-aws-cloudmap + true + io.awspring.cloud spring-cloud-aws-dynamodb diff --git a/spring-cloud-aws-autoconfigure/src/main/java/io/awspring/cloud/autoconfigure/cloudmap/AwsCloudMapStoreClientCustomizer.java b/spring-cloud-aws-autoconfigure/src/main/java/io/awspring/cloud/autoconfigure/cloudmap/AwsCloudMapStoreClientCustomizer.java new file mode 100644 index 000000000..eeffa310b --- /dev/null +++ b/spring-cloud-aws-autoconfigure/src/main/java/io/awspring/cloud/autoconfigure/cloudmap/AwsCloudMapStoreClientCustomizer.java @@ -0,0 +1,26 @@ +/* + * Copyright 2013-2022 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.awspring.cloud.autoconfigure.cloudmap; + +import io.awspring.cloud.autoconfigure.core.AwsClientCustomizer; +import software.amazon.awssdk.services.ssm.SsmClientBuilder; + +/** + * @author Hari Ohm Prasath + * @since 3.0.0 + */ +public interface AwsCloudMapStoreClientCustomizer extends AwsClientCustomizer { +} diff --git a/spring-cloud-aws-autoconfigure/src/main/java/io/awspring/cloud/autoconfigure/cloudmap/CloudMapAutoConfiguration.java b/spring-cloud-aws-autoconfigure/src/main/java/io/awspring/cloud/autoconfigure/cloudmap/CloudMapAutoConfiguration.java new file mode 100644 index 000000000..bf171749a --- /dev/null +++ b/spring-cloud-aws-autoconfigure/src/main/java/io/awspring/cloud/autoconfigure/cloudmap/CloudMapAutoConfiguration.java @@ -0,0 +1,91 @@ +/* + * Copyright 2013-2021 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.awspring.cloud.autoconfigure.cloudmap; + +import io.awspring.cloud.autoconfigure.cloudmap.discovery.CloudMapDiscoveryClient; +import io.awspring.cloud.autoconfigure.cloudmap.properties.CloudMapProperties; +import io.awspring.cloud.autoconfigure.cloudmap.properties.registration.ServiceRegistration; +import io.awspring.cloud.autoconfigure.cloudmap.registration.CloudMapAutoRegistration; +import io.awspring.cloud.autoconfigure.core.AwsClientBuilderConfigurer; +import io.awspring.cloud.autoconfigure.core.AwsClientCustomizer; +import org.springframework.beans.factory.ObjectProvider; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.ApplicationEventPublisher; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import software.amazon.awssdk.services.ec2.Ec2Client; +import software.amazon.awssdk.services.ec2.Ec2ClientBuilder; +import software.amazon.awssdk.services.servicediscovery.ServiceDiscoveryClient; +import software.amazon.awssdk.services.servicediscovery.ServiceDiscoveryClientBuilder; + +/** + * Cloudmap BootstrapConfiguration configuration class to create the required beans. + * + * @author Hari Ohm Prasath + * @since 3.0 + */ +@Configuration(proxyBeanMethods = false) +@EnableConfigurationProperties(CloudMapProperties.class) +@ConditionalOnProperty(prefix = CloudMapProperties.CONFIG_PREFIX, name = "enabled", matchIfMissing = true) +public class CloudMapAutoConfiguration { + + private final CloudMapProperties properties; + + public CloudMapAutoConfiguration(CloudMapProperties properties) { + this.properties = properties; + } + + @ConditionalOnMissingBean + @Bean + public ServiceDiscoveryClient discoveryClient(AwsClientBuilderConfigurer awsClientBuilderConfigurer, + ObjectProvider> configurer) { + return awsClientBuilderConfigurer + .configure(ServiceDiscoveryClient.builder(), this.properties, configurer.getIfAvailable()).build(); + } + + @ConditionalOnMissingBean + @Bean + public Ec2Client ec2Client(AwsClientBuilderConfigurer awsClientBuilderConfigurer, + ObjectProvider> configurer) { + return awsClientBuilderConfigurer + .configure(Ec2Client.builder(), this.properties, configurer.getIfAvailable()).build(); + } + + @Bean + @ConditionalOnMissingBean + CloudMapAutoRegistration createAutoRegistration(ApplicationEventPublisher eventPublisher, + ServiceDiscoveryClient serviceDiscovery, Ec2Client ec2Client) { + return new CloudMapAutoRegistration(eventPublisher, serviceDiscovery, ec2Client, + properties.getRegistry(), properties.getDeploymentPlatform()); + } + + @Bean + @ConditionalOnMissingBean + CloudMapDiscoveryClient createDiscoveryClient(ServiceDiscoveryClient serviceDiscovery) { + return new CloudMapDiscoveryClient(serviceDiscovery, properties); + } + + @Bean + @ConditionalOnMissingBean + ServiceRegistration serviceRegistration(Ec2Client ec2Client) { + return new ServiceRegistration(properties.getRegistry(), ec2Client, + properties.getDeploymentPlatform()); + } + +} diff --git a/spring-cloud-aws-autoconfigure/src/main/java/io/awspring/cloud/autoconfigure/cloudmap/CloudMapUtils.java b/spring-cloud-aws-autoconfigure/src/main/java/io/awspring/cloud/autoconfigure/cloudmap/CloudMapUtils.java new file mode 100644 index 000000000..664853214 --- /dev/null +++ b/spring-cloud-aws-autoconfigure/src/main/java/io/awspring/cloud/autoconfigure/cloudmap/CloudMapUtils.java @@ -0,0 +1,645 @@ +/* + * Copyright 2013-2021 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.awspring.cloud.autoconfigure.cloudmap; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.UUID; +import java.util.stream.Collectors; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import io.awspring.cloud.autoconfigure.cloudmap.discovery.CloudMapServiceInstance; +import io.awspring.cloud.autoconfigure.cloudmap.properties.discovery.CloudMapDiscoveryProperties; +import io.awspring.cloud.autoconfigure.cloudmap.properties.registration.CloudMapRegistryProperties; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import software.amazon.awssdk.services.ec2.Ec2Client; +import software.amazon.awssdk.services.ec2.model.DescribeSubnetsRequest; +import software.amazon.awssdk.services.ec2.model.Filter; +import software.amazon.awssdk.services.servicediscovery.ServiceDiscoveryClient; +import software.amazon.awssdk.services.servicediscovery.model.CreatePrivateDnsNamespaceRequest; +import software.amazon.awssdk.services.servicediscovery.model.CreateServiceRequest; +import software.amazon.awssdk.services.servicediscovery.model.DeregisterInstanceRequest; +import software.amazon.awssdk.services.servicediscovery.model.DiscoverInstancesRequest; +import software.amazon.awssdk.services.servicediscovery.model.DnsConfig; +import software.amazon.awssdk.services.servicediscovery.model.DnsRecord; +import software.amazon.awssdk.services.servicediscovery.model.DuplicateRequestException; +import software.amazon.awssdk.services.servicediscovery.model.GetOperationRequest; +import software.amazon.awssdk.services.servicediscovery.model.HttpInstanceSummary; +import software.amazon.awssdk.services.servicediscovery.model.InvalidInputException; +import software.amazon.awssdk.services.servicediscovery.model.ListNamespacesRequest; +import software.amazon.awssdk.services.servicediscovery.model.ListNamespacesResponse; +import software.amazon.awssdk.services.servicediscovery.model.ListServicesRequest; +import software.amazon.awssdk.services.servicediscovery.model.ListServicesResponse; +import software.amazon.awssdk.services.servicediscovery.model.NamespaceAlreadyExistsException; +import software.amazon.awssdk.services.servicediscovery.model.NamespaceSummary; +import software.amazon.awssdk.services.servicediscovery.model.Operation; +import software.amazon.awssdk.services.servicediscovery.model.RecordType; +import software.amazon.awssdk.services.servicediscovery.model.RegisterInstanceRequest; +import software.amazon.awssdk.services.servicediscovery.model.ResourceLimitExceededException; +import software.amazon.awssdk.services.servicediscovery.model.ServiceAlreadyExistsException; +import software.amazon.awssdk.services.servicediscovery.model.ServiceFilter; +import software.amazon.awssdk.services.servicediscovery.model.ServiceSummary; + +import org.springframework.cloud.aws.cloudmap.exceptions.CreateNameSpaceException; +import org.springframework.cloud.aws.cloudmap.exceptions.CreateServiceException; +import org.springframework.cloud.aws.cloudmap.exceptions.MaxRetryExceededException; +import org.springframework.cloud.client.ServiceInstance; +import org.springframework.core.env.Environment; +import org.springframework.util.StringUtils; +import org.springframework.web.client.RestTemplate; + +/** + * Uses Fargate Metadata URL to retrieve IPv4 address and VPC ID to register instances to cloudmap. + * + * @author Hari Ohm Prasath + * @since 3.0 + */ +public class CloudMapUtils { + + public static final String AWS_REGION = getProperty("AWS_REGION", "us-east-1"); + + /* + * Metadata URL to retrieve IPv4 address and VPC ID for EKS + */ + public static final String EC2_METADATA = "EC2_METADATA"; + /** + * Default meta data URL for EKS + */ + public static final String EC2_METADATA_URL = getProperty(EC2_METADATA, "http://169.254.169.254/latest/meta-data"); + /** + * Default namespace for cloudmap registration + */ + public static final String DEFAULT_NAMESPACE = "default.namespace"; + /** + * Default service name for cloudmap registration + */ + public static final String DEFAULT_SERVICE = "default.service"; + /* + * Metadata URL + */ + public static final String ECS_CONTAINER_METADATA_URI_V_4 = "ECS_CONTAINER_METADATA_URI_V4"; + /* + * Deployment platform type EKS + */ + public static final String EKS = "EKS"; + /* + * Deployment platform type ECS + */ + public static final String ECS = "ECS"; + /* + * AWS VPC ID + */ + private static final String VPC_ID = "VPC_ID"; + /* + * Local IP address + */ + private static final String AWS_INSTANCE_IPV_4 = "AWS_INSTANCE_IPV4"; + /* + * AWS Region + */ + private static final String REGION = "REGION"; + /* + * Logger + */ + private static final Logger LOGGER = LoggerFactory.getLogger(CloudMapUtils.class); + /* + * Namespace status - SUBMITTED + */ + private static final String SUBMITTED = "SUBMITTED"; + /* + * Namespace status - PENDING + */ + private static final String PENDING = "PENDING"; + /* + * Maximum number of polling before returning error + */ + private static final int MAX_POLL = 30; + /* + * Singleton instance + */ + private static CloudMapUtils cloudMapUtils = null; + + /* + * Request attributes - NamespaceID + */ + public final String NAMESPACE_ID = "NAMESPACE_ID"; + + /* + * Request attributes - ServiceID + */ + public final String SERVICE_ID = "SERVICE_ID"; + + /* + * Request attributes - ServiceInstanceID + */ + public final String SERVICE_INSTANCE_ID = "SERVICE_INSTANCE_ID"; + + /* + * Request attributes - IP address + */ + public final String IPV_4_ADDRESS = "IPV4_ADDRESS"; + + final ObjectMapper JSON_MAPPER = new ObjectMapper(); + + private RestTemplate restTemplate; + + public static CloudMapUtils getInstance() { + if (cloudMapUtils == null) { + cloudMapUtils = new CloudMapUtils(); + } + return cloudMapUtils; + } + + /** + * Helper method to get properties from system + * @param key name of the property + * @return value of the property + */ + private static String getProperty(String key) { + return System.getenv(key) == null ? System.getProperty(key) : System.getenv(key); + } + + /** + * Helper method to fall back to default value if property is not set + * @param key name of the property + * @param defaultValue default value + * @return value of the property + */ + private static String getProperty(String key, String defaultValue) { + final String value = getProperty(key); + if (StringUtils.hasText(value)) { + return value; + } + else { + return defaultValue; + } + } + + /** + * Uses metadata URL to fetch all the required details around IP address and VpcID to register instances to cloudmap + * service. If Deployment platform is not passed in then we consider it as classic EC2 or ECS based deployment + * platform + * @param ec2Client - AWS Ec2 client + * @param deploymentPlatform - Deployment platform + * @return map containing ip address and vpcid + */ + public Map getRegistrationAttributes(Ec2Client ec2Client, String deploymentPlatform) { + LOGGER.info("Deployment platform passed in {} ", deploymentPlatform); + if (StringUtils.hasText(deploymentPlatform) && EKS.equalsIgnoreCase(deploymentPlatform.trim())) + return getEksRegistrationAttributes(); + return getEcsRegistrationAttributes(ec2Client); + } + + /** + * Get Cloudmap namespaceID based on name + * @param serviceDiscovery AWS Service discovery + * @param nameSpace cloudmap namespace + * @return namespaceID + */ + public String getNameSpaceId(final ServiceDiscoveryClient serviceDiscovery, final String nameSpace) { + String token; + do { + ListNamespacesResponse nameSpaceResponse = serviceDiscovery + .listNamespaces(ListNamespacesRequest.builder().build()); + token = nameSpaceResponse.nextToken(); + + List namespaceSummaries = nameSpaceResponse.namespaces(); + if (namespaceSummaries != null) { + Optional namespaceId = namespaceSummaries.stream().filter(n -> n.name().equals(nameSpace)) + .map(NamespaceSummary::id).findFirst(); + if (namespaceId.isPresent()) + return namespaceId.get(); + } + else + LOGGER.warn("Namespace {} not available", nameSpace); + } + while (StringUtils.hasText(token)); + + return null; + } + + /** + * List services based on namespace and filter them based on name + * @param serviceDiscovery AWS service discovery + * @param discoveryProperties discovery properties (includes namespace and service name) + * @return list of cloudmap services + */ + public List listServices(final ServiceDiscoveryClient serviceDiscovery, + List discoveryProperties) { + final List serviceList = new ArrayList<>(); + + if (discoveryProperties != null && !discoveryProperties.isEmpty()) { + for (CloudMapDiscoveryProperties d : discoveryProperties) { + final String serviceName = d.getService(); + final String nameSpace = d.getNameSpace(); + String token = null; + + do { + // Get namespaceID + final String nameSpaceId = getNameSpaceId(serviceDiscovery, nameSpace); + if (StringUtils.hasText(nameSpaceId)) { + // Filter cloudmap services + final ServiceFilter serviceFilter = ServiceFilter.builder().name(NAMESPACE_ID).condition("EQ") + .values(nameSpaceId).build(); + final ListServicesRequest servicesRequest = ListServicesRequest.builder().filters(serviceFilter) + .build(); + final ListServicesResponse response = serviceDiscovery.listServices(servicesRequest); + if (StringUtils.hasText(response.nextToken())) + token = response.nextToken(); + + if (StringUtils.hasText(serviceName)) { + serviceList.addAll(response.services().stream().filter(r -> r.name().equals(d.getService())) + .map(r -> generateServiceId(nameSpace, r.name())).collect(Collectors.toList())); + if (serviceList.size() == discoveryProperties.size()) + return serviceList; + } + else + serviceList.addAll(response.services().stream() + .map(r -> generateServiceId(nameSpace, r.name())).collect(Collectors.toList())); + } + else + LOGGER.warn("Namespace is empty"); + } + while (StringUtils.hasText(token)); + } + } + + return serviceList; + } + + /** + * List cloudmap instances based on service name and namespace + * @param serviceDiscovery AWS Service discovery + * @param namespace cloudmap namespace + * @param serviceName cloudmap service name + * @return list of http instances + */ + public List listInstances(final ServiceDiscoveryClient serviceDiscovery, + final String namespace, String serviceName) { + final DiscoverInstancesRequest dRequest = DiscoverInstancesRequest.builder().namespaceName(namespace) + .serviceName(serviceName).build(); + + return serviceDiscovery.discoverInstances(dRequest).instances(); + } + + /** + * Get service instance from http instance summary + * @param instanceSummary HTTP instance summary - Cloudmap object + * @return Service instance - Spring object + */ + public ServiceInstance getServiceInstance(HttpInstanceSummary instanceSummary) { + return new CloudMapServiceInstance(instanceSummary); + } + + /** + * Register with cloudmap, the method takes care of the following: 1. Create namespace, if not exists 2. Create + * service, if not exists 3. Register the instance with the created namespace and service + * @param serviceDiscovery AWS Service discovery service + * @param ec2Client - AWS Ec2 client + * @param properties Cloud map registry properties + * @param environment Spring environment + * @param deploymentPlatform Deployment platform + * @return map of registration properties + */ + public Map registerInstance(final ServiceDiscoveryClient serviceDiscovery, + final Ec2Client ec2Client, final CloudMapRegistryProperties properties, + final Environment environment, final String deploymentPlatform) { + String nameSpace = properties != null ? properties.getNameSpace() : null; + if (!StringUtils.hasText(nameSpace)) + nameSpace = DEFAULT_NAMESPACE; + + String service = properties != null ? properties.getService() : null; + if (!StringUtils.hasText(service)) + service = environment.getProperty("spring.application.name"); + + if (!StringUtils.hasText(service)) + service = DEFAULT_SERVICE; + + final String serviceInstanceId = UUID.randomUUID().toString(); + + LOGGER.info("Registration details namespace {} - service {} - serviceInstance {}", nameSpace, service, + serviceInstanceId); + Map registrationDetails = getRegistrationAttributes(ec2Client, deploymentPlatform); + String nameSpaceId = getNameSpaceId(serviceDiscovery, nameSpace); + try { + // Create namespace if not exists + if (!StringUtils.hasText(nameSpaceId)) { + LOGGER.debug("Namespace " + nameSpace + "not available so creating"); + nameSpaceId = createNameSpace(serviceDiscovery, nameSpace, + properties != null ? properties.getDescription() : null, + registrationDetails.get(VPC_ID)); + } + + // Create service if not exists + String serviceId = getServiceId(serviceDiscovery, nameSpaceId, service); + if (!StringUtils.hasText(serviceId)) { + LOGGER.debug("Service " + service + " doesnt exist so creating new one"); + serviceId = createService(serviceDiscovery, nameSpaceId, service); + } + + Map attributes = new HashMap<>(); + attributes.put(AWS_INSTANCE_IPV_4, registrationDetails.get(IPV_4_ADDRESS)); + attributes.put(REGION, AWS_REGION); + attributes.put(NAMESPACE_ID, nameSpaceId); + attributes.put(SERVICE_ID, serviceId); + attributes.put(SERVICE_INSTANCE_ID, serviceInstanceId); + + // Register instance + final String operationId = serviceDiscovery.registerInstance(RegisterInstanceRequest.builder() + .instanceId(serviceInstanceId).serviceId(serviceId).attributes(attributes).build()) + .operationId(); + LOGGER.debug("Register instance initiated, polling for completion {}", operationId); + + // Poll for completion + pollForCompletion(serviceDiscovery, operationId); + + return attributes; + } + catch (InvalidInputException e) { + LOGGER.error("Invalid input passed into the service {} - {}", nameSpaceId, e.getMessage(), e); + } + catch (CreateNameSpaceException e) { + LOGGER.error("Error while creating namespace {} - {}", nameSpace, e.getMessage()); + } + catch (InterruptedException e) { + LOGGER.error("Error while polling for status update {} with error {}", nameSpace, e.getMessage()); + } + catch (CreateServiceException e) { + LOGGER.error("Error while creating service {} with {} - {}", service, nameSpace, e.getMessage()); + } + catch (MaxRetryExceededException e) { + LOGGER.error("Maximum number of retry exceeded for registering instance with {} for {}", nameSpace, + service, e); + } + + return null; + } + + /** + * Create Cloudmap namespace. + * @param serviceDiscovery AWS Service discovery + * @param nameSpace Cloudmap namespace + * @param description Cloudmap namespace description + * @param vpcId VPC ID + * @return NamespaceID + * @throws CreateNameSpaceException thrown in case of runtime exception + */ + private String createNameSpace(ServiceDiscoveryClient serviceDiscovery, String nameSpace, String description, + String vpcId) throws CreateNameSpaceException { + try { + // Create namespace + final String operationId = serviceDiscovery.createPrivateDnsNamespace(CreatePrivateDnsNamespaceRequest + .builder().name(nameSpace).vpc(vpcId).description(description).build()) + .operationId(); + LOGGER.info("Creating namespace {} with operationId {}", nameSpace, operationId); + + // Wait till completion + pollForCompletion(serviceDiscovery, operationId); + + return getNameSpaceId(serviceDiscovery, nameSpace); + } + catch (NamespaceAlreadyExistsException e) { + LOGGER.warn("Namespace {} already exists", nameSpace); + return getNameSpaceId(serviceDiscovery, nameSpace); + } + catch (InvalidInputException | ResourceLimitExceededException | DuplicateRequestException e) { + LOGGER.error("Error while registering with cloudmap {} with error {}", nameSpace, e.getMessage(), e); + throw new CreateNameSpaceException(e); + } + catch (InterruptedException e) { + LOGGER.error("Error while polling for status update {} with error {}", nameSpace, e.getMessage(), e); + throw new CreateNameSpaceException(e); + } + catch (MaxRetryExceededException e) { + LOGGER.error("Maximum number of retry exceeded for namespace {}", nameSpace, e); + throw new CreateNameSpaceException(e); + } + } + + /** + * Create service. + * @param serviceDiscovery AWS Service Discovery + * @param nameSpaceId CloudMap Namespace ID + * @param service Service name + * @return Service ID + * @throws CreateServiceException thrown in case of runtime exception + */ + private String createService(ServiceDiscoveryClient serviceDiscovery, String nameSpaceId, String service) + throws CreateServiceException { + try { + CreateServiceRequest serviceRequest = CreateServiceRequest.builder().name(service).namespaceId(nameSpaceId) + .dnsConfig(DnsConfig.builder().dnsRecords(DnsRecord.builder().type(RecordType.A).ttl(300L).build()) + .build()) + .build(); + + final String serviceId = serviceDiscovery.createService(serviceRequest).service().id(); + LOGGER.info("Service ID create {} for {} with namespace {}", serviceId, service, nameSpaceId); + return serviceId; + } + catch (ServiceAlreadyExistsException e) { + LOGGER.warn("Service {} already exists", service); + return getServiceId(serviceDiscovery, service, nameSpaceId); + } + catch (InvalidInputException | ResourceLimitExceededException e) { + LOGGER.error("Error while creating service {} with namespace {}", service, nameSpaceId); + throw new CreateServiceException(e); + } + } + + public String generateServiceId(final String namespace, final String serviceName) { + return String.format("%s@%s", namespace, serviceName); + } + + /** + * Automatically deregister the instance when the container is stopped. + * @param serviceDiscovery AWS Service Discovery Service + * @param attributeMap Service discovery attributes + */ + public void deregisterInstance(final ServiceDiscoveryClient serviceDiscovery, + final Map attributeMap) { + try { + final String serviceInstanceId = attributeMap.get(SERVICE_INSTANCE_ID); + final String serviceId = attributeMap.get(SERVICE_ID); + LOGGER.info("Initiating de-registration process {} - {}", serviceInstanceId, serviceId); + + // Deregister instance + String operationId = serviceDiscovery.deregisterInstance( + DeregisterInstanceRequest.builder().instanceId(serviceInstanceId).serviceId(serviceId).build()) + .operationId(); + + // Wait till completion + pollForCompletion(serviceDiscovery, operationId); + } + catch (InterruptedException e) { + LOGGER.error("Error while polling for status while de-registering instance {}", e.getMessage(), e); + } + catch (MaxRetryExceededException e) { + LOGGER.error("Maximum number of retry exceeded {}", e.getMessage(), e); + } + } + + /** + * Get service ID based on service name and namespace ID. + * @param serviceDiscovery AWS Service discovery + * @param nameSpaceId Namespace ID + * @param serviceName name of the cloudmap service + * @return Cloudmap service ID + */ + public String getServiceId(ServiceDiscoveryClient serviceDiscovery, String nameSpaceId, String serviceName) { + ServiceFilter filter = ServiceFilter.builder().name(NAMESPACE_ID).values(Collections.singletonList(nameSpaceId)) + .build(); + Optional serviceSummary = serviceDiscovery + .listServices(ListServicesRequest.builder().filters(filter).build()).services().stream() + .filter(s -> serviceName.equals(s.name())).findFirst(); + return serviceSummary.map(ServiceSummary::id).orElse(null); + } + + /** + * Poll for completion. + * @param serviceDiscovery AWS Service discovery + * @param operationId cloudmap operationID + * @throws InterruptedException thrown in case of thread.sleep() exception + * @throws MaxRetryExceededException thrown if maximum polling duration has exceeded + */ + private void pollForCompletion(ServiceDiscoveryClient serviceDiscovery, String operationId) + throws InterruptedException, MaxRetryExceededException { + Operation operation = serviceDiscovery + .getOperation(GetOperationRequest.builder().operationId(operationId).build()).operation(); + int counter = 0; + LOGGER.info("Operation ID {} will be polled", operationId); + while ((SUBMITTED.equalsIgnoreCase(operation.statusAsString()) + || PENDING.equalsIgnoreCase(operation.statusAsString())) && counter < MAX_POLL) { + operation = serviceDiscovery.getOperation(GetOperationRequest.builder().operationId(operationId).build()) + .operation(); + Thread.sleep(2000); + counter++; + } + + if (counter > MAX_POLL) { + throw new MaxRetryExceededException("Maximum of retry exceeded for " + operationId); + } + } + + /** + * Get CloudMap attributes for EKS platform + * @return map of cloud map attributes with Ipaddress and vpcid + */ + private Map getEksRegistrationAttributes() { + try { + String ipAddress = getUrlResponse(String.format("%s/local-ipv4", EC2_METADATA_URL)); + final String macId = getUrlResponse(String.format("%s/network/interfaces/macs", EC2_METADATA_URL)); + if (StringUtils.hasText(macId) && macId.contains("/")) { + final String macAddress = macId.split("/")[0]; + final String vpcUrl = String.format("%s/network/interfaces/macs/%s/vpc-id", EC2_METADATA_URL, + macAddress); + final String vpcId = getUrlResponse(vpcUrl); + LOGGER.info("Metadata details IP Address {}, macAddress {} - VPCId {}", ipAddress, macAddress, vpcId); + return getCloudMapAttributes(ipAddress, vpcId); + } + } + catch (Exception e) { + LOGGER.error("Error while getting registration details {}", e.getMessage(), e); + } + return new HashMap<>(); + } + + /** + * Get CloudMap attributes for ECS platform + * @param ec2Client - AWS Ec2 client + * @return map of cloud map attributes with Ipaddress and vpcid + */ + private Map getEcsRegistrationAttributes(Ec2Client ec2Client) { + try { + String metaDataUrl = getProperty(ECS_CONTAINER_METADATA_URI_V_4); + if (!StringUtils.hasText(metaDataUrl)) + metaDataUrl = EC2_METADATA_URL; + final String responseBody = getUrlResponse(metaDataUrl + "/task"); + JsonNode root = JSON_MAPPER.readTree(responseBody); + JsonNode jsonNode = root.get("Containers").get(0).get("Networks").get(0); + final String ipv4Address = getData(jsonNode.get("IPv4Addresses")); + final String cidrBlock = getData(jsonNode.get("IPv4SubnetCIDRBlock")); + final String vpcId = ec2Client.describeSubnets(DescribeSubnetsRequest.builder() + .filters(Filter.builder().name("cidr-block").values(cidrBlock).build()).build()) + .subnets().get(0).vpcId(); + LOGGER.info("IPv4Address {} - VPC ID {}", ipv4Address, vpcId); + return getCloudMapAttributes(ipv4Address, vpcId); + } + catch (Exception e) { + LOGGER.error("Error while fetching network details - {}", e.getMessage(), e); + } + return new HashMap<>(); + } + + /** + * Helper method to fetch contents of URL as string + * @param url URL to fetch from + * @return response as string + */ + private String getUrlResponse(String url) { + return getRestTemplate().getForEntity(url, String.class).getBody(); + } + + /** + * Helper method to get data from JsonNode + * @param object JsonNode object + * @return data as string + */ + private String getData(JsonNode object) { + if (object != null) { + if (object.isArray()) { + return object.get(0).asText(); + } + else { + return object.asText(); + } + } + return null; + } + + /** + * Returns hash map of cloudmap attributes + * @param ipv4Address IP Address of the instance + * @param vpcId VPC ID in which the instance is hosted + * @return hash map of cloudmap attributes + */ + private Map getCloudMapAttributes(String ipv4Address, String vpcId) { + Map attributes = new HashMap<>(); + attributes.put(IPV_4_ADDRESS, ipv4Address); + attributes.put(VPC_ID, vpcId); + return attributes; + } + + /** + * Get Rest Template + * @return restTemplate + */ + RestTemplate getRestTemplate() { + if (restTemplate == null) { + restTemplate = new RestTemplate(); + } + return restTemplate; + } + + void setRestTemplate(RestTemplate restTemplate) { + this.restTemplate = restTemplate; + } +} diff --git a/spring-cloud-aws-autoconfigure/src/main/java/io/awspring/cloud/autoconfigure/cloudmap/discovery/CloudMapDiscoveryClient.java b/spring-cloud-aws-autoconfigure/src/main/java/io/awspring/cloud/autoconfigure/cloudmap/discovery/CloudMapDiscoveryClient.java new file mode 100644 index 000000000..9021a7d51 --- /dev/null +++ b/spring-cloud-aws-autoconfigure/src/main/java/io/awspring/cloud/autoconfigure/cloudmap/discovery/CloudMapDiscoveryClient.java @@ -0,0 +1,77 @@ +/* + * Copyright 2013-2022 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.awspring.cloud.autoconfigure.cloudmap.discovery; + +import io.awspring.cloud.autoconfigure.cloudmap.CloudMapUtils; +import io.awspring.cloud.autoconfigure.cloudmap.properties.CloudMapProperties; +import io.awspring.cloud.autoconfigure.cloudmap.properties.discovery.CloudMapDiscoveryProperties; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; +import org.springframework.cloud.client.ServiceInstance; +import org.springframework.cloud.client.discovery.DiscoveryClient; +import software.amazon.awssdk.services.servicediscovery.ServiceDiscoveryClient; + +// @checkstyle: off +public class CloudMapDiscoveryClient implements DiscoveryClient { + + /** + * Description of the service. + */ + public static final String DESCRIPTION = "AWS CloudMap Discovery Client"; + + private static final CloudMapUtils UTILS = CloudMapUtils.getInstance(); + private final ServiceDiscoveryClient serviceDiscovery; + private final CloudMapProperties properties; + + public CloudMapDiscoveryClient(ServiceDiscoveryClient serviceDiscovery, CloudMapProperties properties) { + this.serviceDiscovery = serviceDiscovery; + this.properties = properties; + } + + @Override + public int getOrder() { + return DiscoveryClient.super.getOrder(); + } + + @Override + public String description() { + return DESCRIPTION; + } + + @Override + public List getServices() { + final List discoveryProperties = properties.getDiscovery().getDiscoveryList(); + if (!discoveryProperties.isEmpty()) { + return UTILS.listServices(serviceDiscovery, discoveryProperties); + } + + return Collections.emptyList(); + } + + @Override + public List getInstances(String serviceId) { + // Service ID maintained as _ + String[] split = serviceId.split("@"); + if (split.length == 2) { + return UTILS.listInstances(serviceDiscovery, split[0], split[1]).stream().map(UTILS::getServiceInstance) + .collect(Collectors.toList()); + } + + return Collections.emptyList(); + } + +} diff --git a/spring-cloud-aws-autoconfigure/src/main/java/io/awspring/cloud/autoconfigure/cloudmap/discovery/CloudMapServiceInstance.java b/spring-cloud-aws-autoconfigure/src/main/java/io/awspring/cloud/autoconfigure/cloudmap/discovery/CloudMapServiceInstance.java new file mode 100644 index 000000000..0b1705382 --- /dev/null +++ b/spring-cloud-aws-autoconfigure/src/main/java/io/awspring/cloud/autoconfigure/cloudmap/discovery/CloudMapServiceInstance.java @@ -0,0 +1,76 @@ +/* + * Copyright 2013-2022 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.awspring.cloud.autoconfigure.cloudmap.discovery; + +import io.awspring.cloud.autoconfigure.cloudmap.CloudMapUtils; +import java.net.URI; +import java.util.Map; +import org.springframework.cloud.client.ServiceInstance; +import org.springframework.util.StringUtils; +import software.amazon.awssdk.services.servicediscovery.model.HttpInstanceSummary; + +public class CloudMapServiceInstance implements ServiceInstance { + + private final CloudMapUtils UTILS = CloudMapUtils.getInstance(); + + HttpInstanceSummary instanceSummary; + + public CloudMapServiceInstance(HttpInstanceSummary httpInstanceSummary) { + this.instanceSummary = httpInstanceSummary; + } + + @Override + public String getInstanceId() { + return instanceSummary.instanceId(); + } + + @Override + public String getScheme() { + return getUri().getScheme(); + } + + @Override + public String getServiceId() { + return UTILS.generateServiceId(instanceSummary.namespaceName(), instanceSummary.serviceName()); + } + + @Override + public String getHost() { + return instanceSummary.attributes().get("AWS_INSTANCE_IPV4"); + } + + @Override + public int getPort() { + String port = instanceSummary.attributes().get("AWS_INSTANCE_PORT"); + return StringUtils.hasText(port) ? Integer.parseInt(port) : 0; + } + + @Override + public boolean isSecure() { + return false; + } + + @Override + public URI getUri() { + return URI.create(String.format("http://%s:%s", this.getHost(), this.getPort())); + } + + @Override + public Map getMetadata() { + return instanceSummary.attributes(); + } + +} diff --git a/spring-cloud-aws-autoconfigure/src/main/java/io/awspring/cloud/autoconfigure/cloudmap/discovery/package-info.java b/spring-cloud-aws-autoconfigure/src/main/java/io/awspring/cloud/autoconfigure/cloudmap/discovery/package-info.java new file mode 100644 index 000000000..ee1ba4995 --- /dev/null +++ b/spring-cloud-aws-autoconfigure/src/main/java/io/awspring/cloud/autoconfigure/cloudmap/discovery/package-info.java @@ -0,0 +1,22 @@ +/* + * Copyright 2013-2022 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Discovery client implementation for cloud map + */ +@org.springframework.lang.NonNullApi +@org.springframework.lang.NonNullFields +package io.awspring.cloud.autoconfigure.cloudmap.discovery; diff --git a/spring-cloud-aws-autoconfigure/src/main/java/io/awspring/cloud/autoconfigure/cloudmap/package-info.java b/spring-cloud-aws-autoconfigure/src/main/java/io/awspring/cloud/autoconfigure/cloudmap/package-info.java new file mode 100644 index 000000000..75e1b6e37 --- /dev/null +++ b/spring-cloud-aws-autoconfigure/src/main/java/io/awspring/cloud/autoconfigure/cloudmap/package-info.java @@ -0,0 +1,20 @@ +/* + * Copyright 2013-2022 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Spring cloud Cloud map auto configuration and implementation classes + */ +package io.awspring.cloud.autoconfigure.cloudmap; diff --git a/spring-cloud-aws-autoconfigure/src/main/java/io/awspring/cloud/autoconfigure/cloudmap/properties/CloudMapProperties.java b/spring-cloud-aws-autoconfigure/src/main/java/io/awspring/cloud/autoconfigure/cloudmap/properties/CloudMapProperties.java new file mode 100644 index 000000000..221f75e35 --- /dev/null +++ b/spring-cloud-aws-autoconfigure/src/main/java/io/awspring/cloud/autoconfigure/cloudmap/properties/CloudMapProperties.java @@ -0,0 +1,80 @@ +/* + * Copyright 2013-2022 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.awspring.cloud.autoconfigure.cloudmap.properties; + +import io.awspring.cloud.autoconfigure.AwsClientProperties; +import io.awspring.cloud.autoconfigure.cloudmap.properties.discovery.CloudMapDiscovery; +import io.awspring.cloud.autoconfigure.cloudmap.properties.registration.CloudMapRegistryProperties; + +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.boot.context.properties.NestedConfigurationProperty; + +/** + * POJO to capture all cloudmap integration parameters (both registry and discovery). + * + * @author Hari Ohm Prasath Rajagopal + * @since 3.0 + */ +@ConfigurationProperties(CloudMapProperties.CONFIG_PREFIX) +public class CloudMapProperties extends AwsClientProperties { + + /** + * Default cloudmap prefix. + */ + public static final String CONFIG_PREFIX = "spring.cloud.aws.cloudmap"; + + /** + * Compute platform `ECS` or `EKS`. + */ + private String deploymentPlatform; + + @NestedConfigurationProperty + private CloudMapRegistryProperties registry; + + @NestedConfigurationProperty + private CloudMapDiscovery discovery; + + public CloudMapRegistryProperties getRegistry() { + return this.registry; + } + + public void setRegistry(CloudMapRegistryProperties registry) { + this.registry = registry; + } + + public CloudMapDiscovery getDiscovery() { + return this.discovery; + } + + public void setDiscovery(CloudMapDiscovery discovery) { + this.discovery = discovery; + } + + public String getDeploymentPlatform() { + return deploymentPlatform; + } + + public void setDeploymentPlatform(String deploymentPlatform) { + this.deploymentPlatform = deploymentPlatform; + } + + @Override + public String toString() { + return "AwsCloudMapProperties{" + "registry=" + registry + ", discovery=" + discovery + ", region='" + + getRegion() + "', platform='" + getDeploymentPlatform() + '\'' + "}"; + } + +} diff --git a/spring-cloud-aws-autoconfigure/src/main/java/io/awspring/cloud/autoconfigure/cloudmap/properties/discovery/CloudMapDiscovery.java b/spring-cloud-aws-autoconfigure/src/main/java/io/awspring/cloud/autoconfigure/cloudmap/properties/discovery/CloudMapDiscovery.java new file mode 100644 index 000000000..5e91ad17d --- /dev/null +++ b/spring-cloud-aws-autoconfigure/src/main/java/io/awspring/cloud/autoconfigure/cloudmap/properties/discovery/CloudMapDiscovery.java @@ -0,0 +1,43 @@ +/* + * Copyright 2013-2022 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.awspring.cloud.autoconfigure.cloudmap.properties.discovery; + +import java.util.List; + +/** + * Pojo class to capture all the discovery parameters. + * + * @author Hari Ohm Prasath + * @since 3.0 + */ +public class CloudMapDiscovery { + + private List discoveryList; + + public List getDiscoveryList() { + return this.discoveryList; + } + + public void setDiscoveryList(List discoveryList) { + this.discoveryList = discoveryList; + } + + @Override + public String toString() { + return "AwsCloudMapDiscovery{" + "discoveryList=" + discoveryList.toString() + '}'; + } + +} diff --git a/spring-cloud-aws-autoconfigure/src/main/java/io/awspring/cloud/autoconfigure/cloudmap/properties/discovery/CloudMapDiscoveryProperties.java b/spring-cloud-aws-autoconfigure/src/main/java/io/awspring/cloud/autoconfigure/cloudmap/properties/discovery/CloudMapDiscoveryProperties.java new file mode 100644 index 000000000..00121ea37 --- /dev/null +++ b/spring-cloud-aws-autoconfigure/src/main/java/io/awspring/cloud/autoconfigure/cloudmap/properties/discovery/CloudMapDiscoveryProperties.java @@ -0,0 +1,69 @@ +/* + * Copyright 2013-2022 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.awspring.cloud.autoconfigure.cloudmap.properties.discovery; + +import java.util.Map; + +/** + * POJO class to capture cloudmap discovery attributes. + * + * @author Hari Ohm Prasath + * @since 3.0 + */ +public class CloudMapDiscoveryProperties { + + private String nameSpace; + + private String service; + + private Map filterAttributes; + + public String getNameSpace() { + return this.nameSpace; + } + + public void setNameSpace(String nameSpace) { + this.nameSpace = nameSpace; + } + + public String getService() { + return this.service; + } + + public void setService(String service) { + this.service = service; + } + + public Map getFilterAttributes() { + return this.filterAttributes; + } + + public void setFilterAttributes(Map filterAttributes) { + this.filterAttributes = filterAttributes; + } + + @Override + public String toString() { + String data = "AwsCloudMapDiscoveryProperties{" + "serviceNameSpace=" + nameSpace + ", service=" + service; + if (filterAttributes != null) { + data += filterAttributes.keySet().stream().map(f -> "key = " + f + ":" + filterAttributes.get(f)) + .reduce((a, b) -> a + "," + b).get(); + } + data += "}"; + return data; + } + +} diff --git a/spring-cloud-aws-autoconfigure/src/main/java/io/awspring/cloud/autoconfigure/cloudmap/properties/discovery/package-info.java b/spring-cloud-aws-autoconfigure/src/main/java/io/awspring/cloud/autoconfigure/cloudmap/properties/discovery/package-info.java new file mode 100644 index 000000000..1f9c8369c --- /dev/null +++ b/spring-cloud-aws-autoconfigure/src/main/java/io/awspring/cloud/autoconfigure/cloudmap/properties/discovery/package-info.java @@ -0,0 +1,20 @@ +/* + * Copyright 2013-2022 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Properties to capture all the discovery parameters + */ +package io.awspring.cloud.autoconfigure.cloudmap.properties.discovery; diff --git a/spring-cloud-aws-autoconfigure/src/main/java/io/awspring/cloud/autoconfigure/cloudmap/properties/package-info.java b/spring-cloud-aws-autoconfigure/src/main/java/io/awspring/cloud/autoconfigure/cloudmap/properties/package-info.java new file mode 100644 index 000000000..286e0aaea --- /dev/null +++ b/spring-cloud-aws-autoconfigure/src/main/java/io/awspring/cloud/autoconfigure/cloudmap/properties/package-info.java @@ -0,0 +1,20 @@ +/* + * Copyright 2013-2022 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Properties to capture cloud map both discovery and registration properties + */ +package io.awspring.cloud.autoconfigure.cloudmap.properties; diff --git a/spring-cloud-aws-autoconfigure/src/main/java/io/awspring/cloud/autoconfigure/cloudmap/properties/registration/CloudMapRegistryProperties.java b/spring-cloud-aws-autoconfigure/src/main/java/io/awspring/cloud/autoconfigure/cloudmap/properties/registration/CloudMapRegistryProperties.java new file mode 100644 index 000000000..c0ac97fa3 --- /dev/null +++ b/spring-cloud-aws-autoconfigure/src/main/java/io/awspring/cloud/autoconfigure/cloudmap/properties/registration/CloudMapRegistryProperties.java @@ -0,0 +1,56 @@ +/* + * Copyright 2013-2022 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.awspring.cloud.autoconfigure.cloudmap.properties.registration; + +/** + * POJO class to capture cloudmap registration parameters. + * + * @author Hari Ohm Prasath + * @since 3.0 + */ +public class CloudMapRegistryProperties { + + private String nameSpace; + + private String service; + + private String description; + + public String getNameSpace() { + return nameSpace; + } + + public void setNameSpace(String nameSpace) { + this.nameSpace = nameSpace; + } + + public String getService() { + return service; + } + + public void setService(String service) { + this.service = service; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + +} diff --git a/spring-cloud-aws-autoconfigure/src/main/java/io/awspring/cloud/autoconfigure/cloudmap/properties/registration/ServiceRegistration.java b/spring-cloud-aws-autoconfigure/src/main/java/io/awspring/cloud/autoconfigure/cloudmap/properties/registration/ServiceRegistration.java new file mode 100644 index 000000000..864e508d2 --- /dev/null +++ b/spring-cloud-aws-autoconfigure/src/main/java/io/awspring/cloud/autoconfigure/cloudmap/properties/registration/ServiceRegistration.java @@ -0,0 +1,81 @@ +/* + * Copyright 2013-2022 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.awspring.cloud.autoconfigure.cloudmap.properties.registration; + +import java.net.URI; +import java.util.Map; +import java.util.UUID; + +import io.awspring.cloud.autoconfigure.cloudmap.CloudMapUtils; +import software.amazon.awssdk.services.ec2.Ec2Client; + +import org.springframework.cloud.client.serviceregistry.Registration; + +public class ServiceRegistration implements Registration { + + private final CloudMapRegistryProperties properties; + + private final Map registrationDetails; + + private final CloudMapUtils UTILS = CloudMapUtils.getInstance(); + + public ServiceRegistration(CloudMapRegistryProperties properties, Ec2Client ec2Client, + String deploymentPlatform) { + registrationDetails = UTILS.getRegistrationAttributes(ec2Client, deploymentPlatform); + this.properties = properties; + } + + @Override + public String getInstanceId() { + return UUID.randomUUID().toString(); + } + + @Override + public String getScheme() { + return Registration.super.getScheme(); + } + + @Override + public String getServiceId() { + return UTILS.generateServiceId(properties.getNameSpace(), properties.getService()); + } + + @Override + public String getHost() { + return registrationDetails.get(UTILS.IPV_4_ADDRESS); + } + + @Override + public int getPort() { + return 0; + } + + @Override + public boolean isSecure() { + return false; + } + + @Override + public URI getUri() { + return null; + } + + @Override + public Map getMetadata() { + return registrationDetails; + } + +} diff --git a/spring-cloud-aws-autoconfigure/src/main/java/io/awspring/cloud/autoconfigure/cloudmap/properties/registration/package-info.java b/spring-cloud-aws-autoconfigure/src/main/java/io/awspring/cloud/autoconfigure/cloudmap/properties/registration/package-info.java new file mode 100644 index 000000000..83ec71459 --- /dev/null +++ b/spring-cloud-aws-autoconfigure/src/main/java/io/awspring/cloud/autoconfigure/cloudmap/properties/registration/package-info.java @@ -0,0 +1,20 @@ +/* + * Copyright 2013-2022 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Registration client implementation for cloud map + */ +package io.awspring.cloud.autoconfigure.cloudmap.properties.registration; diff --git a/spring-cloud-aws-autoconfigure/src/main/java/io/awspring/cloud/autoconfigure/cloudmap/registration/CloudMapAutoRegistration.java b/spring-cloud-aws-autoconfigure/src/main/java/io/awspring/cloud/autoconfigure/cloudmap/registration/CloudMapAutoRegistration.java new file mode 100644 index 000000000..8f2e07866 --- /dev/null +++ b/spring-cloud-aws-autoconfigure/src/main/java/io/awspring/cloud/autoconfigure/cloudmap/registration/CloudMapAutoRegistration.java @@ -0,0 +1,138 @@ +/* + * Copyright 2013-2022 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.awspring.cloud.autoconfigure.cloudmap.registration; + +import io.awspring.cloud.autoconfigure.cloudmap.CloudMapUtils; +import io.awspring.cloud.autoconfigure.cloudmap.properties.registration.CloudMapRegistryProperties; + +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.atomic.AtomicBoolean; + +import org.springframework.cloud.client.discovery.event.InstanceRegisteredEvent; +import org.springframework.cloud.client.serviceregistry.AutoServiceRegistration; +import org.springframework.context.ApplicationEvent; +import org.springframework.context.ApplicationEventPublisher; +import org.springframework.context.EnvironmentAware; +import org.springframework.context.SmartLifecycle; +import org.springframework.context.event.ContextClosedEvent; +import org.springframework.context.event.SmartApplicationListener; +import org.springframework.core.Ordered; +import org.springframework.core.env.Environment; +import org.springframework.lang.Nullable; + +import software.amazon.awssdk.services.ec2.Ec2Client; +import software.amazon.awssdk.services.servicediscovery.ServiceDiscoveryClient; + +public class CloudMapAutoRegistration + implements AutoServiceRegistration, SmartLifecycle, Ordered, SmartApplicationListener, EnvironmentAware { + + private final ServiceDiscoveryClient serviceDiscovery; + + private final CloudMapRegistryProperties properties; + private final Ec2Client ec2Client; + + private final ApplicationEventPublisher eventPublisher; + + private final AtomicBoolean running = new AtomicBoolean(false); + + private final CloudMapUtils UTILS = CloudMapUtils.getInstance(); + + @Nullable + private Environment environment; + + private String deploymentPlatform; + + private Map attributesMap = new HashMap<>(); + + public CloudMapAutoRegistration(ApplicationEventPublisher eventPublisher, ServiceDiscoveryClient serviceDiscovery, + Ec2Client ec2Client, CloudMapRegistryProperties properties, String deploymentPlatform) { + this.eventPublisher = eventPublisher; + this.serviceDiscovery = serviceDiscovery; + this.properties = properties; + this.ec2Client = ec2Client; + this.deploymentPlatform = deploymentPlatform; + } + + @Override + public boolean isAutoStartup() { + return true; + } + + @Override + public void stop(Runnable callback) { + stop(); + callback.run(); + } + + @Override + public int getPhase() { + return 0; + } + + @Override + public int getOrder() { + return 0; + } + + @Override + public void onApplicationEvent(ApplicationEvent event) { + if (event instanceof ContextClosedEvent) { + onApplicationEvent((ContextClosedEvent) event); + } + } + + @Override + public void start() { + if (!this.running.get()) { + final Map attributesMap = UTILS.registerInstance(serviceDiscovery, ec2Client, properties, + environment, deploymentPlatform); + if (attributesMap != null && attributesMap.containsKey(UTILS.SERVICE_INSTANCE_ID)) { + this.attributesMap = attributesMap; + this.eventPublisher.publishEvent(new InstanceRegisteredEvent<>(this, attributesMap)); + this.running.set(true); + } + } + } + + @Override + public void stop() { + if (this.running.get() && !attributesMap.isEmpty() && attributesMap.containsKey(UTILS.SERVICE_INSTANCE_ID)) { + UTILS.deregisterInstance(serviceDiscovery, attributesMap); + this.running.set(false); + } + } + + @Override + public boolean isRunning() { + return this.running.get(); + } + + @Override + public boolean supportsEventType(Class eventType) { + return true; + } + + @Override + public void setEnvironment(Environment environment) { + this.environment = environment; + } + + public void onApplicationEvent(ContextClosedEvent event) { + stop(); + } + +} diff --git a/spring-cloud-aws-autoconfigure/src/main/java/io/awspring/cloud/autoconfigure/cloudmap/registration/package-info.java b/spring-cloud-aws-autoconfigure/src/main/java/io/awspring/cloud/autoconfigure/cloudmap/registration/package-info.java new file mode 100644 index 000000000..81c5e3ad3 --- /dev/null +++ b/spring-cloud-aws-autoconfigure/src/main/java/io/awspring/cloud/autoconfigure/cloudmap/registration/package-info.java @@ -0,0 +1,22 @@ +/* + * Copyright 2013-2022 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Auto register implementation for cloud map + */ +@org.springframework.lang.NonNullApi +@org.springframework.lang.NonNullFields +package io.awspring.cloud.autoconfigure.cloudmap.registration; diff --git a/spring-cloud-aws-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json b/spring-cloud-aws-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json index 72dd78c62..b525e0afa 100644 --- a/spring-cloud-aws-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json +++ b/spring-cloud-aws-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json @@ -23,6 +23,12 @@ "name": "spring.cloud.aws.dynamodb.enabled", "description": "Enables DynamoDb integration.", "type": "java.lang.Boolean" + }, + { + "defaultValue": true, + "name": "spring.cloud.aws.cloudmap.enabled", + "description": "Enables CloudMap integration.", + "type": "java.lang.Boolean" } ] } diff --git a/spring-cloud-aws-autoconfigure/src/main/resources/META-INF/spring.factories b/spring-cloud-aws-autoconfigure/src/main/resources/META-INF/spring.factories index 8cc2c8970..152a9da19 100644 --- a/spring-cloud-aws-autoconfigure/src/main/resources/META-INF/spring.factories +++ b/spring-cloud-aws-autoconfigure/src/main/resources/META-INF/spring.factories @@ -8,7 +8,8 @@ io.awspring.cloud.autoconfigure.s3.S3TransferManagerAutoConfiguration,\ io.awspring.cloud.autoconfigure.s3.S3AutoConfiguration,\ io.awspring.cloud.autoconfigure.sns.SnsAutoConfiguration,\ io.awspring.cloud.autoconfigure.sqs.SqsAutoConfiguration,\ -io.awspring.cloud.autoconfigure.dynamodb.DynamoDbAutoConfiguration +io.awspring.cloud.autoconfigure.dynamodb.DynamoDbAutoConfiguration,\ +io.awspring.cloud.autoconfigure.cloudmap.CloudMapAutoConfiguration # ConfigData Location Resolvers org.springframework.boot.context.config.ConfigDataLocationResolver=\ diff --git a/spring-cloud-aws-autoconfigure/src/test/java/io/awspring/cloud/autoconfigure/cloudmap/CloudMapRegisterServiceTest.java b/spring-cloud-aws-autoconfigure/src/test/java/io/awspring/cloud/autoconfigure/cloudmap/CloudMapRegisterServiceTest.java new file mode 100644 index 000000000..c426b4140 --- /dev/null +++ b/spring-cloud-aws-autoconfigure/src/test/java/io/awspring/cloud/autoconfigure/cloudmap/CloudMapRegisterServiceTest.java @@ -0,0 +1,269 @@ +/* + * Copyright 2013-2021 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.awspring.cloud.autoconfigure.cloudmap; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +import io.awspring.cloud.autoconfigure.cloudmap.properties.discovery.CloudMapDiscoveryProperties; +import io.awspring.cloud.autoconfigure.cloudmap.properties.registration.CloudMapRegistryProperties; +import io.awspring.cloud.autoconfigure.cloudmap.properties.registration.ServiceRegistration; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import software.amazon.awssdk.services.ec2.Ec2Client; +import software.amazon.awssdk.services.ec2.model.DescribeSubnetsRequest; +import software.amazon.awssdk.services.ec2.model.DescribeSubnetsResponse; +import software.amazon.awssdk.services.ec2.model.Subnet; +import software.amazon.awssdk.services.servicediscovery.ServiceDiscoveryClient; +import software.amazon.awssdk.services.servicediscovery.model.CreatePrivateDnsNamespaceRequest; +import software.amazon.awssdk.services.servicediscovery.model.CreatePrivateDnsNamespaceResponse; +import software.amazon.awssdk.services.servicediscovery.model.CreateServiceRequest; +import software.amazon.awssdk.services.servicediscovery.model.CreateServiceResponse; +import software.amazon.awssdk.services.servicediscovery.model.DeregisterInstanceRequest; +import software.amazon.awssdk.services.servicediscovery.model.DeregisterInstanceResponse; +import software.amazon.awssdk.services.servicediscovery.model.GetOperationRequest; +import software.amazon.awssdk.services.servicediscovery.model.GetOperationResponse; +import software.amazon.awssdk.services.servicediscovery.model.ListNamespacesRequest; +import software.amazon.awssdk.services.servicediscovery.model.ListNamespacesResponse; +import software.amazon.awssdk.services.servicediscovery.model.ListServicesRequest; +import software.amazon.awssdk.services.servicediscovery.model.ListServicesResponse; +import software.amazon.awssdk.services.servicediscovery.model.NamespaceSummary; +import software.amazon.awssdk.services.servicediscovery.model.Operation; +import software.amazon.awssdk.services.servicediscovery.model.OperationStatus; +import software.amazon.awssdk.services.servicediscovery.model.RegisterInstanceRequest; +import software.amazon.awssdk.services.servicediscovery.model.RegisterInstanceResponse; +import software.amazon.awssdk.services.servicediscovery.model.Service; +import software.amazon.awssdk.services.servicediscovery.model.ServiceSummary; + +import org.springframework.core.env.Environment; +import org.springframework.http.ResponseEntity; +import org.springframework.web.client.RestTemplate; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +/** + * Unit testcase for {@link ServiceRegistration} + * + * @author Hari Ohm Prasath + * @since 3.0 + */ +public class CloudMapRegisterServiceTest { + + private static final String ECS = "ECS"; + private static final String EKS = "EKS"; + private final ServiceDiscoveryClient serviceDiscovery = mock(ServiceDiscoveryClient.class); + private final Ec2Client ec2Client = mock(Ec2Client.class); + + private final CloudMapUtils cloudMapUtils = CloudMapUtils.getInstance(); + + private final RestTemplate restTemplate = mock(RestTemplate.class); + + private final Environment environment = mock(Environment.class); + + public CloudMapRegisterServiceTest() { + cloudMapUtils.setRestTemplate(restTemplate); + } + + @Test + public void cloudMapRegisterInstancesNameSpaceAndServiceExists() { + final ListNamespacesResponse response = getListNamespacesResponse(); + final ListServicesResponse listServicesResponse = getListServicesResponse(); + final RegisterInstanceResponse registerInstanceRequest = getRegisterInstanceResponse(); + final GetOperationResponse operationResponse = getOperationResponse(); + + when(serviceDiscovery.listNamespaces(any(ListNamespacesRequest.class))).thenReturn(response); + when(serviceDiscovery.listServices(any(ListServicesRequest.class))).thenReturn(listServicesResponse); + when(serviceDiscovery.registerInstance((any(RegisterInstanceRequest.class)))) + .thenReturn(registerInstanceRequest); + when(serviceDiscovery.getOperation((any(GetOperationRequest.class)))).thenReturn(operationResponse); + + when(serviceDiscovery.listServices(any(ListServicesRequest.class))).thenReturn(listServicesResponse); + + when(restTemplate.getForEntity(CloudMapUtils.EC2_METADATA_URL + "/task", String.class)) + .thenReturn(ResponseEntity.ok(CloudMapTestConstants.ECS_REPONSE_JSON)); + when(ec2Client.describeSubnets(any(DescribeSubnetsRequest.class))).thenReturn( + DescribeSubnetsResponse.builder().subnets(Subnet.builder().vpcId("vpc-id").build()).build()); + + assertThat(cloudMapUtils.registerInstance(serviceDiscovery, ec2Client, getProperties(), environment, ECS)).isNotEmpty(); + } + + @Test + public void cloudMapRegisterInstanceEcsWithNoNameSpace() { + final ListNamespacesResponse listNamespacesResponse = getListNamespacesResponse(); + final ListServicesResponse listServicesResponse = getListServicesResponse(); + final RegisterInstanceResponse registerInstanceRequest = getRegisterInstanceResponse(); + final GetOperationResponse operationResponse = getOperationResponse(); + final CreatePrivateDnsNamespaceResponse createPrivateDnsNamespaceResponse = getCreatePrivateDnsNamespaceResponse(); + cloudMapUtils.setRestTemplate(restTemplate); + + when(serviceDiscovery.listNamespaces(any(ListNamespacesRequest.class))).thenReturn( + ListNamespacesResponse.builder().namespaces(Collections.emptyList()).build(), listNamespacesResponse); + when(serviceDiscovery.createPrivateDnsNamespace(any(CreatePrivateDnsNamespaceRequest.class))) + .thenReturn(createPrivateDnsNamespaceResponse); + when(serviceDiscovery.getOperation((any(GetOperationRequest.class)))).thenReturn(operationResponse); + + when(serviceDiscovery.listServices(any(ListServicesRequest.class))).thenReturn(listServicesResponse); + when(serviceDiscovery.registerInstance((any(RegisterInstanceRequest.class)))) + .thenReturn(registerInstanceRequest); + when(serviceDiscovery.getOperation((any(GetOperationRequest.class)))).thenReturn(operationResponse); + + when(serviceDiscovery.listServices(any(ListServicesRequest.class))).thenReturn(listServicesResponse); + + when(restTemplate.getForEntity(String.format("%s/local-ipv4", CloudMapUtils.EC2_METADATA_URL), String.class)) + .thenReturn(ResponseEntity.ok("10.1.1.1")); + when(restTemplate.getForEntity(String.format("%s/network/interfaces/macs", CloudMapUtils.EC2_METADATA_URL), + String.class)).thenReturn(ResponseEntity.ok("MacId/Id")); + when(restTemplate.getForEntity( + String.format("%s/network/interfaces/macs/%s/vpc-id", CloudMapUtils.EC2_METADATA_URL, "MacId"), + String.class)).thenReturn(ResponseEntity.ok("vpcId")); + when(ec2Client.describeSubnets(any(DescribeSubnetsRequest.class))).thenReturn( + DescribeSubnetsResponse.builder().subnets(Subnet.builder().vpcId("vpc-id").build()).build()); + + assertThat(cloudMapUtils.registerInstance(serviceDiscovery, ec2Client, getProperties(), environment, ECS)).isNotEmpty(); + } + + @Test + public void cloudMapRegisterInstanceEksWithNoNameSpace() { + final ListNamespacesResponse listNamespacesResponse = getListNamespacesResponse(); + final ListServicesResponse listServicesResponse = getListServicesResponse(); + final RegisterInstanceResponse registerInstanceRequest = getRegisterInstanceResponse(); + final GetOperationResponse operationResponse = getOperationResponse(); + final CreatePrivateDnsNamespaceResponse createPrivateDnsNamespaceResponse = getCreatePrivateDnsNamespaceResponse(); + cloudMapUtils.setRestTemplate(restTemplate); + + when(serviceDiscovery.listNamespaces(any(ListNamespacesRequest.class))).thenReturn( + ListNamespacesResponse.builder().namespaces(Collections.emptyList()).build(), listNamespacesResponse); + when(serviceDiscovery.createPrivateDnsNamespace(any(CreatePrivateDnsNamespaceRequest.class))) + .thenReturn(createPrivateDnsNamespaceResponse); + when(serviceDiscovery.getOperation((any(GetOperationRequest.class)))).thenReturn(operationResponse); + + when(serviceDiscovery.listServices(any(ListServicesRequest.class))).thenReturn(listServicesResponse); + when(serviceDiscovery.registerInstance((any(RegisterInstanceRequest.class)))) + .thenReturn(registerInstanceRequest); + when(serviceDiscovery.getOperation((any(GetOperationRequest.class)))).thenReturn(operationResponse); + + when(serviceDiscovery.listServices(any(ListServicesRequest.class))).thenReturn(listServicesResponse); + + when(restTemplate.getForEntity(CloudMapUtils.EC2_METADATA_URL + "/task", String.class)) + .thenReturn(ResponseEntity.ok(CloudMapTestConstants.ECS_REPONSE_JSON)); + when(ec2Client.describeSubnets(any(DescribeSubnetsRequest.class))).thenReturn( + DescribeSubnetsResponse.builder().subnets(Subnet.builder().vpcId("vpc-id").build()).build()); + + assertThat(cloudMapUtils.registerInstance(serviceDiscovery, ec2Client, getProperties(), environment, EKS)).isNotEmpty(); + } + + @Test + public void cloudMapRegisterInstancesWithNoService() { + final ListNamespacesResponse listNamespacesResponse = getListNamespacesResponse(); + final ListServicesResponse listServicesResponse = getListServicesResponse(); + final RegisterInstanceResponse registerInstanceResponse = getRegisterInstanceResponse(); + final GetOperationResponse operationResponse = getOperationResponse(); + final CreateServiceResponse createServiceResponse = CreateServiceResponse.builder() + .service(Service.builder().name(CloudMapTestUtils.SERVICE).id(CloudMapTestUtils.SERVICE).build()) + .build(); + + when(serviceDiscovery.listNamespaces(any(ListNamespacesRequest.class))).thenReturn(listNamespacesResponse); + + when(serviceDiscovery.listServices(any(ListServicesRequest.class))).thenReturn( + ListServicesResponse.builder().services(Collections.emptyList()).build(), listServicesResponse); + when(serviceDiscovery.createService(any(CreateServiceRequest.class))).thenReturn(createServiceResponse); + + when(serviceDiscovery.registerInstance((any(RegisterInstanceRequest.class)))) + .thenReturn(registerInstanceResponse); + when(serviceDiscovery.getOperation((any(GetOperationRequest.class)))).thenReturn(operationResponse); + + when(restTemplate.getForEntity(CloudMapUtils.EC2_METADATA_URL + "/task", String.class)) + .thenReturn(ResponseEntity.ok(CloudMapTestConstants.ECS_REPONSE_JSON)); + when(ec2Client.describeSubnets(any(DescribeSubnetsRequest.class))).thenReturn( + DescribeSubnetsResponse.builder().subnets(Subnet.builder().vpcId("vpc-id").build()).build()); + + assertThat(cloudMapUtils.registerInstance(serviceDiscovery, ec2Client, getProperties(), environment, ECS)).isNotEmpty(); + } + + @Test + public void listService() { + CloudMapDiscoveryProperties cloudMapDiscoveryProperties = new CloudMapDiscoveryProperties(); + cloudMapDiscoveryProperties.setService(CloudMapTestUtils.SERVICE); + cloudMapDiscoveryProperties.setNameSpace(CloudMapTestUtils.NAMESPACE); + final ListNamespacesResponse listNamespacesResponse = getListNamespacesResponse(); + final ListServicesResponse listServicesResponse = getListServicesResponse(); + + when(serviceDiscovery.listNamespaces(any(ListNamespacesRequest.class))).thenReturn(listNamespacesResponse); + when(serviceDiscovery.listServices(any(ListServicesRequest.class))).thenReturn(listServicesResponse); + + cloudMapUtils.listServices(serviceDiscovery, Collections.singletonList(cloudMapDiscoveryProperties)); + } + + @Test + public void deRegisterInstances() { + try { + final Map attributeMap = new HashMap<>(); + attributeMap.put("SERVICE_INSTANCE_ID", "SERVICE_INSTANCE_ID"); + attributeMap.put("SERVICE_ID", "SERVICE_ID"); + + DeregisterInstanceResponse deregisterInstanceResponse = DeregisterInstanceResponse.builder() + .operationId(CloudMapTestUtils.OPERATION_ID).build(); + when(serviceDiscovery.deregisterInstance(any(DeregisterInstanceRequest.class))) + .thenReturn(deregisterInstanceResponse); + + final GetOperationResponse operationResponse = getOperationResponse(); + when(serviceDiscovery.getOperation((any(GetOperationRequest.class)))).thenReturn(operationResponse, + operationResponse); + cloudMapUtils.deregisterInstance(serviceDiscovery, attributeMap); + } + catch (Exception e) { + Assertions.fail(); + } + } + + private CreatePrivateDnsNamespaceResponse getCreatePrivateDnsNamespaceResponse() { + return CreatePrivateDnsNamespaceResponse.builder().operationId(CloudMapTestUtils.OPERATION_ID).build(); + } + + private GetOperationResponse getOperationResponse() { + return GetOperationResponse.builder().operation(Operation.builder().status(OperationStatus.SUCCESS).build()) + .build(); + } + + private RegisterInstanceResponse getRegisterInstanceResponse() { + return RegisterInstanceResponse.builder().operationId(CloudMapTestUtils.OPERATION_ID).build(); + } + + private ListServicesResponse getListServicesResponse() { + ServiceSummary serviceSummary = ServiceSummary.builder().id(CloudMapTestUtils.SERVICE) + .name(CloudMapTestUtils.SERVICE).build(); + return ListServicesResponse.builder().services(Collections.singletonList(serviceSummary)).build(); + } + + private ListNamespacesResponse getListNamespacesResponse() { + NamespaceSummary summary = NamespaceSummary.builder().id(CloudMapTestUtils.NAMESPACE) + .name(CloudMapTestUtils.NAMESPACE).build(); + return ListNamespacesResponse.builder().namespaces(Collections.singleton(summary)).build(); + } + + private CloudMapRegistryProperties getProperties() { + CloudMapRegistryProperties properties = new CloudMapRegistryProperties(); + properties.setService(CloudMapTestUtils.SERVICE); + properties.setNameSpace(CloudMapTestUtils.NAMESPACE); + properties.setDescription("DESCRIPTION"); + return properties; + } + +} diff --git a/spring-cloud-aws-autoconfigure/src/test/java/io/awspring/cloud/autoconfigure/cloudmap/CloudMapTestConstants.java b/spring-cloud-aws-autoconfigure/src/test/java/io/awspring/cloud/autoconfigure/cloudmap/CloudMapTestConstants.java new file mode 100644 index 000000000..f4ae4c3b7 --- /dev/null +++ b/spring-cloud-aws-autoconfigure/src/test/java/io/awspring/cloud/autoconfigure/cloudmap/CloudMapTestConstants.java @@ -0,0 +1,67 @@ +/* + * Copyright 2013-2022 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.awspring.cloud.autoconfigure.cloudmap; + +/** + * Mock Responses from Ec2 meta-data endpoint + * @author Hari Ohm Prasath + * @since 3.0 + */ +public class CloudMapTestConstants { + /* + * Mock response + */ + public static final String ECS_REPONSE_JSON = "{\n" + " \"Cluster\": \"default\",\n" + + " \"TaskARN\": \"arn:aws:ecs:us-east-2:012345678910:task/9781c248-0edd-4cdb-9a93-f63cb662a5d3\",\n" + + " \"Family\": \"nginx\",\n" + " \"Revision\": \"5\",\n" + " \"DesiredStatus\": \"RUNNING\",\n" + + " \"KnownStatus\": \"RUNNING\",\n" + " \"Containers\": [\n" + " {\n" + + " \"DockerId\": \"731a0d6a3b4210e2448339bc7015aaa79bfe4fa256384f4102db86ef94cbbc4c\",\n" + + " \"Name\": \"~internal~ecs~pause\",\n" + + " \"DockerName\": \"ecs-nginx-5-internalecspause-acc699c0cbf2d6d11700\",\n" + + " \"Image\": \"amazon/amazon-ecs-pause:0.1.0\",\n" + " \"ImageID\": \"\",\n" + + " \"Labels\": {\n" + " \"com.amazonaws.ecs.cluster\": \"default\",\n" + + " \"com.amazonaws.ecs.container-name\": \"~internal~ecs~pause\",\n" + + " \"com.amazonaws.ecs.task-arn\": \"arn:aws:ecs:us-east-2:012345678910:task/9781c248-0edd-4cdb-9a93-f63cb662a5d3\",\n" + + " \"com.amazonaws.ecs.task-definition-family\": \"nginx\",\n" + + " \"com.amazonaws.ecs.task-definition-version\": \"5\"\n" + " },\n" + + " \"DesiredStatus\": \"RESOURCES_PROVISIONED\",\n" + + " \"KnownStatus\": \"RESOURCES_PROVISIONED\",\n" + " \"Limits\": {\n" + " \"CPU\": 0,\n" + + " \"Memory\": 0\n" + " },\n" + " \"CreatedAt\": \"2018-02-01T20:55:08.366329616Z\",\n" + + " \"StartedAt\": \"2018-02-01T20:55:09.058354915Z\",\n" + " \"Type\": \"CNI_PAUSE\",\n" + + " \"Networks\": [\n" + " {\n" + " \"NetworkMode\": \"awsvpc\",\n" + + " \"IPv4Addresses\": [\n" + " \"10.0.2.106\"\n" + " ],\n" + + " \"IPv4SubnetCIDRBlock\": [\n" + " \"10.0.2.106\"\n" + " ]\n" + + " }\n" + " ]\n" + " },\n" + " {\n" + + " \"DockerId\": \"43481a6ce4842eec8fe72fc28500c6b52edcc0917f105b83379f88cac1ff3946\",\n" + + " \"Name\": \"nginx-curl\",\n" + + " \"DockerName\": \"ecs-nginx-5-nginx-curl-ccccb9f49db0dfe0d901\",\n" + + " \"Image\": \"nrdlngr/nginx-curl\",\n" + + " \"ImageID\": \"sha256:2e00ae64383cfc865ba0a2ba37f61b50a120d2d9378559dcd458dc0de47bc165\",\n" + + " \"Labels\": {\n" + " \"com.amazonaws.ecs.cluster\": \"default\",\n" + + " \"com.amazonaws.ecs.container-name\": \"nginx-curl\",\n" + + " \"com.amazonaws.ecs.task-arn\": \"arn:aws:ecs:us-east-2:012345678910:task/9781c248-0edd-4cdb-9a93-f63cb662a5d3\",\n" + + " \"com.amazonaws.ecs.task-definition-family\": \"nginx\",\n" + + " \"com.amazonaws.ecs.task-definition-version\": \"5\"\n" + " },\n" + + " \"DesiredStatus\": \"RUNNING\",\n" + " \"KnownStatus\": \"RUNNING\",\n" + + " \"Limits\": {\n" + " \"CPU\": 512,\n" + " \"Memory\": 512\n" + " },\n" + + " \"CreatedAt\": \"2018-02-01T20:55:10.554941919Z\",\n" + + " \"StartedAt\": \"2018-02-01T20:55:11.064236631Z\",\n" + " \"Type\": \"NORMAL\",\n" + + " \"Networks\": [\n" + " {\n" + " \"NetworkMode\": \"awsvpc\",\n" + + " \"IPv4Addresses\": [\n" + " \"10.0.2.106\"\n" + " ]\n" + " }\n" + + " ]\n" + " }\n" + " ],\n" + " \"PullStartedAt\": \"2018-02-01T20:55:09.372495529Z\",\n" + + " \"PullStoppedAt\": \"2018-02-01T20:55:10.552018345Z\",\n" + " \"AvailabilityZone\": \"us-east-2b\"\n" + + "}"; +} diff --git a/spring-cloud-aws-autoconfigure/src/test/java/io/awspring/cloud/autoconfigure/cloudmap/CloudMapTestUtils.java b/spring-cloud-aws-autoconfigure/src/test/java/io/awspring/cloud/autoconfigure/cloudmap/CloudMapTestUtils.java new file mode 100644 index 000000000..e12c72c22 --- /dev/null +++ b/spring-cloud-aws-autoconfigure/src/test/java/io/awspring/cloud/autoconfigure/cloudmap/CloudMapTestUtils.java @@ -0,0 +1,32 @@ +/* + * Copyright 2013-2021 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.awspring.cloud.autoconfigure.cloudmap; + +/** + * Unit testcase for {@link CloudMapUtils} + * + * @author Hari Ohm Prasath + * @since 3.0 + */ +public class CloudMapTestUtils { + + public static final String NAMESPACE = "NAMESPACE"; + + public static final String SERVICE = "SERVICE"; + + public static final String OPERATION_ID = "OPERATION_ID"; + +} diff --git a/spring-cloud-aws-autoconfigure/src/test/java/io/awspring/cloud/autoconfigure/cloudmap/integration/EcsCloudMapIntegrationTest.java b/spring-cloud-aws-autoconfigure/src/test/java/io/awspring/cloud/autoconfigure/cloudmap/integration/EcsCloudMapIntegrationTest.java new file mode 100644 index 000000000..2073ebb2f --- /dev/null +++ b/spring-cloud-aws-autoconfigure/src/test/java/io/awspring/cloud/autoconfigure/cloudmap/integration/EcsCloudMapIntegrationTest.java @@ -0,0 +1,177 @@ +/* + * Copyright 2013-2021 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.awspring.cloud.autoconfigure.cloudmap.integration; + +import java.time.Duration; +import java.util.List; + +import io.awspring.cloud.autoconfigure.cloudmap.AwsCloudMapStoreClientCustomizer; +import io.awspring.cloud.autoconfigure.cloudmap.CloudMapUtils; +import io.awspring.cloud.autoconfigure.cloudmap.discovery.CloudMapDiscoveryClient; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.testcontainers.containers.localstack.LocalStackContainer; +import org.testcontainers.junit.jupiter.Container; +import org.testcontainers.junit.jupiter.Testcontainers; +import org.testcontainers.utility.DockerImageName; +import software.amazon.awssdk.core.client.config.ClientOverrideConfiguration; +import software.amazon.awssdk.http.SdkHttpClient; +import software.amazon.awssdk.http.apache.ApacheHttpClient; +import software.amazon.awssdk.services.servicediscovery.ServiceDiscoveryClient; + +import org.springframework.boot.BootstrapRegistry; +import org.springframework.boot.BootstrapRegistryInitializer; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.WebApplicationType; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.test.system.OutputCaptureExtension; +import org.springframework.context.ConfigurableApplicationContext; + +import static io.awspring.cloud.autoconfigure.cloudmap.CloudMapUtils.ECS; +import static io.awspring.cloud.autoconfigure.cloudmap.CloudMapUtils.ECS_CONTAINER_METADATA_URI_V_4; + +/** + * Integration test for Ecs based Cloud Map registration. + * + * @author Hari Ohm Prasath + * @since 3.0 + */ +@Testcontainers +@ExtendWith(OutputCaptureExtension.class) +public class EcsCloudMapIntegrationTest { + + // Local stack container with Cloud Map and Ec2 services enabled + @Container + private static final LocalStackContainer localStackContainer = new LocalStackContainer( + DockerImageName.parse("localstack/localstack:1.1.0")) + .withServices(LocalStackContainer.EnabledService.named("servicediscovery"), LocalStackContainer.Service.EC2) + .withEnv(IntegrationTestUtil.LOCAL_STACK_API, IntegrationTestUtil.LOCAL_STACK_API_KEY) + .withReuse(true); + + /** + * Create all the pre-requisites for the test. + */ + @BeforeAll + static void beforeAll() { + final String vpcId = IntegrationTestUtil.createVpc(localStackContainer); + IntegrationTestUtil.createSubnet(localStackContainer, vpcId); + IntegrationTestUtil.createCloudMapResources(localStackContainer, IntegrationTestUtil.TEST_DEFAULT_NAMESPACE + , IntegrationTestUtil.TEST_DEFAULT_SERVICE, vpcId); + } + + /** + * Register a service with no specification provided in application.properties. + */ + @Test + void registerEcsContainerWithCloudMapWithNoSpecification() { + SpringApplication application = new SpringApplication(EcsApp.class); + application.setWebApplicationType(WebApplicationType.NONE); + application.addBootstrapRegistryInitializer(new AwsConfigurerClientConfiguration()); + + List properties = IntegrationTestUtil.getCloudMapSpringBootProperties(localStackContainer); + properties.add("--spring.cloud.aws.cloudmap.deploymentPlatform=" + ECS); + + try (ConfigurableApplicationContext context = application.run(properties.toArray(new String[0]))) { + final ServiceDiscoveryClient serviceDiscoveryClient = context.getBean(ServiceDiscoveryClient.class); + final CloudMapUtils cloudMapUtils = CloudMapUtils.getInstance(); + Assertions.assertNotNull(cloudMapUtils.getNameSpaceId(serviceDiscoveryClient, CloudMapUtils.DEFAULT_NAMESPACE)); + final String serviceId = cloudMapUtils.getServiceId(serviceDiscoveryClient, CloudMapUtils.DEFAULT_NAMESPACE, + CloudMapUtils.DEFAULT_SERVICE); + Assertions.assertNotNull(serviceId); + Assertions.assertEquals(1, IntegrationTestUtil.getCloudMapRegisteredInstances(localStackContainer, serviceId)); + } + } + + /** + * Register a service with defined specifications like namespace, service, etc + */ + @Test + void registerEcsContainerWithCloudMapWithDefinedSpecification() { + SpringApplication application = new SpringApplication(EcsApp.class); + application.setWebApplicationType(WebApplicationType.NONE); + application.addBootstrapRegistryInitializer(new AwsConfigurerClientConfiguration()); + + final String nameSpace = "a.namespace"; + final String service = "a.service"; + + List properties = IntegrationTestUtil.getCloudMapSpringBootProperties(localStackContainer); + properties.add("--spring.cloud.aws.cloudmap.registry.nameSpace=" + nameSpace); + properties.add("--spring.cloud.aws.cloudmap.registry.service=" + service); + properties.add("--spring.cloud.aws.cloudmap.registry.description=Name space description"); + properties.add("--spring.cloud.aws.cloudmap.deploymentPlatform=" + ECS); + + try (ConfigurableApplicationContext context = application.run(properties.toArray(new String[0]))) { + final ServiceDiscoveryClient serviceDiscoveryClient = context.getBean(ServiceDiscoveryClient.class); + final CloudMapUtils cloudMapUtils = CloudMapUtils.getInstance(); + Assertions.assertNotNull(cloudMapUtils.getNameSpaceId(serviceDiscoveryClient, nameSpace)); + final String serviceId = cloudMapUtils.getServiceId(serviceDiscoveryClient, nameSpace, service); + Assertions.assertNotNull(serviceId); + Assertions.assertEquals(1, IntegrationTestUtil.getCloudMapRegisteredInstances(localStackContainer, serviceId)); + } + } + + /** + * Discover the service by pre-creating the namepsace and service. + */ + @Test + void discoverEcsCloudMapInstancesWithNoSpecification() { + SpringApplication application = new SpringApplication(EcsCloudMapIntegrationTest.EcsApp.class); + application.setWebApplicationType(WebApplicationType.NONE); + application.addBootstrapRegistryInitializer(new EcsCloudMapIntegrationTest.AwsConfigurerClientConfiguration()); + + List properties = IntegrationTestUtil.getCloudMapSpringBootProperties(localStackContainer); + properties.add("--spring.cloud.aws.cloudmap.discovery.discoveryList[0].nameSpace=" + IntegrationTestUtil.TEST_DEFAULT_NAMESPACE); + properties.add("--spring.cloud.aws.cloudmap.discovery.discoveryList[0].service=" + IntegrationTestUtil.TEST_DEFAULT_SERVICE); + properties.add("--spring.cloud.aws.cloudmap.deploymentPlatform=" + ECS); + + try (ConfigurableApplicationContext context = application.run(properties.toArray(new String[0]))) { + CloudMapDiscoveryClient discoveryClient = context.getBean(CloudMapDiscoveryClient.class); + final List services = discoveryClient.getServices(); + Assertions.assertNotNull(services); + Assertions.assertEquals(1, services.size()); + Assertions.assertEquals(String.format("%s@%s", IntegrationTestUtil.TEST_DEFAULT_NAMESPACE, + IntegrationTestUtil.TEST_DEFAULT_SERVICE), services.get(0)); + } + } + + @SpringBootApplication + static class EcsApp { + static { + System.setProperty(ECS_CONTAINER_METADATA_URI_V_4, IntegrationTestUtil.META_DATA_MOCK_RESPONSE); + } + } + + static class AwsConfigurerClientConfiguration implements BootstrapRegistryInitializer { + @Override + public void initialize(BootstrapRegistry registry) { + registry.register(AwsCloudMapStoreClientCustomizer.class, + context -> new AwsCloudMapStoreClientCustomizer() { + @Override + public ClientOverrideConfiguration overrideConfiguration() { + return ClientOverrideConfiguration.builder().apiCallTimeout(Duration.ofMillis(2828)) + .build(); + } + + @Override + public SdkHttpClient httpClient() { + return ApacheHttpClient.builder().connectionTimeout(Duration.ofMillis(1542)).build(); + } + }); + } + } +} diff --git a/spring-cloud-aws-autoconfigure/src/test/java/io/awspring/cloud/autoconfigure/cloudmap/integration/EksCloudMapIntegrationTest.java b/spring-cloud-aws-autoconfigure/src/test/java/io/awspring/cloud/autoconfigure/cloudmap/integration/EksCloudMapIntegrationTest.java new file mode 100644 index 000000000..9ebd32908 --- /dev/null +++ b/spring-cloud-aws-autoconfigure/src/test/java/io/awspring/cloud/autoconfigure/cloudmap/integration/EksCloudMapIntegrationTest.java @@ -0,0 +1,177 @@ +/* + * Copyright 2013-2021 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.awspring.cloud.autoconfigure.cloudmap.integration; + +import java.time.Duration; +import java.util.List; + +import io.awspring.cloud.autoconfigure.cloudmap.AwsCloudMapStoreClientCustomizer; +import io.awspring.cloud.autoconfigure.cloudmap.CloudMapUtils; +import io.awspring.cloud.autoconfigure.cloudmap.discovery.CloudMapDiscoveryClient; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.testcontainers.containers.localstack.LocalStackContainer; +import org.testcontainers.junit.jupiter.Container; +import org.testcontainers.junit.jupiter.Testcontainers; +import org.testcontainers.utility.DockerImageName; +import software.amazon.awssdk.core.client.config.ClientOverrideConfiguration; +import software.amazon.awssdk.http.SdkHttpClient; +import software.amazon.awssdk.http.apache.ApacheHttpClient; +import software.amazon.awssdk.services.servicediscovery.ServiceDiscoveryClient; + +import org.springframework.boot.BootstrapRegistry; +import org.springframework.boot.BootstrapRegistryInitializer; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.WebApplicationType; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.test.system.OutputCaptureExtension; +import org.springframework.context.ConfigurableApplicationContext; + +import static io.awspring.cloud.autoconfigure.cloudmap.CloudMapUtils.EKS; + +/** + * Integration test for Eks based Cloud Map registration. + * + * @author Hari Ohm Prasath + * @since 3.0 + */ +@Testcontainers +@ExtendWith(OutputCaptureExtension.class) +public class EksCloudMapIntegrationTest { + + // Local stack container with Cloud Map and Ec2 services enabled + @Container + private static final LocalStackContainer localStackContainer = new LocalStackContainer( + DockerImageName.parse("localstack/localstack:1.1.0")) + .withServices(LocalStackContainer.EnabledService.named("servicediscovery"), LocalStackContainer.Service.EC2) + .withEnv(IntegrationTestUtil.LOCAL_STACK_API, IntegrationTestUtil.LOCAL_STACK_API_KEY) + .withReuse(true); + + /** + * Create all the pre-requisites for the test. + */ + @BeforeAll + static void beforeAll() { + final String vpcId = IntegrationTestUtil.createVpc(localStackContainer); + IntegrationTestUtil.createSubnet(localStackContainer, vpcId); + IntegrationTestUtil.createCloudMapResources(localStackContainer, IntegrationTestUtil.TEST_DEFAULT_NAMESPACE + , IntegrationTestUtil.TEST_DEFAULT_SERVICE, vpcId); + } + + /** + * Register a service with no specification provided in application.properties. + */ + @Test + void registerEksContainerWithCloudMapWithNoSpecification() { + SpringApplication application = new SpringApplication(EksApp.class); + application.setWebApplicationType(WebApplicationType.NONE); + application.addBootstrapRegistryInitializer(new AwsConfigurerClientConfiguration()); + + List properties = IntegrationTestUtil.getCloudMapSpringBootProperties(localStackContainer); + properties.add("--spring.cloud.aws.cloudmap.deploymentPlatform=" + EKS); + + try (ConfigurableApplicationContext context = application.run(properties.toArray(new String[0]))) { + final ServiceDiscoveryClient serviceDiscoveryClient = context.getBean(ServiceDiscoveryClient.class); + final CloudMapUtils cloudMapUtils = CloudMapUtils.getInstance(); + Assertions.assertNotNull(cloudMapUtils.getNameSpaceId(serviceDiscoveryClient, CloudMapUtils.DEFAULT_NAMESPACE)); + final String serviceId = cloudMapUtils.getServiceId(serviceDiscoveryClient, CloudMapUtils.DEFAULT_NAMESPACE, + CloudMapUtils.DEFAULT_SERVICE); + Assertions.assertNotNull(serviceId); + Assertions.assertEquals(1, IntegrationTestUtil.getCloudMapRegisteredInstances(localStackContainer, serviceId)); + } + } + + /** + * Register a service with defined specifications like namespace, service, etc + */ + @Test + void registerEksContainerWithCloudMapWithDefinedSpecification() { + SpringApplication application = new SpringApplication(EcsCloudMapIntegrationTest.EcsApp.class); + application.setWebApplicationType(WebApplicationType.NONE); + application.addBootstrapRegistryInitializer(new EcsCloudMapIntegrationTest.AwsConfigurerClientConfiguration()); + + final String nameSpace = "a.namespace"; + final String service = "a.service"; + + List properties = IntegrationTestUtil.getCloudMapSpringBootProperties(localStackContainer); + properties.add("--spring.cloud.aws.cloudmap.registry.nameSpace=" + nameSpace); + properties.add("--spring.cloud.aws.cloudmap.registry.service=" + service); + properties.add("--spring.cloud.aws.cloudmap.registry.description=Name space description"); + properties.add("--spring.cloud.aws.cloudmap.deploymentPlatform=" + EKS); + + try (ConfigurableApplicationContext context = application.run(properties.toArray(new String[0]))) { + final ServiceDiscoveryClient serviceDiscoveryClient = context.getBean(ServiceDiscoveryClient.class); + final CloudMapUtils cloudMapUtils = CloudMapUtils.getInstance(); + Assertions.assertNotNull(cloudMapUtils.getNameSpaceId(serviceDiscoveryClient, nameSpace)); + final String serviceId = cloudMapUtils.getServiceId(serviceDiscoveryClient, nameSpace, service); + Assertions.assertNotNull(serviceId); + Assertions.assertEquals(1, IntegrationTestUtil.getCloudMapRegisteredInstances(localStackContainer, serviceId)); + } + } + + /** + * Discover the service by pre-creating the namepsace and service. + */ + @Test + void discoverEksCloudMapInstancesWithNoSpecification() { + SpringApplication application = new SpringApplication(EcsCloudMapIntegrationTest.EcsApp.class); + application.setWebApplicationType(WebApplicationType.NONE); + application.addBootstrapRegistryInitializer(new EcsCloudMapIntegrationTest.AwsConfigurerClientConfiguration()); + + List properties = IntegrationTestUtil.getCloudMapSpringBootProperties(localStackContainer); + properties.add("--spring.cloud.aws.cloudmap.discovery.discoveryList[0].nameSpace=" + IntegrationTestUtil.TEST_DEFAULT_NAMESPACE); + properties.add("--spring.cloud.aws.cloudmap.discovery.discoveryList[0].service=" + IntegrationTestUtil.TEST_DEFAULT_SERVICE); + properties.add("--spring.cloud.aws.cloudmap.deploymentPlatform=" + EKS); + + try (ConfigurableApplicationContext context = application.run(properties.toArray(new String[0]))) { + CloudMapDiscoveryClient discoveryClient = context.getBean(CloudMapDiscoveryClient.class); + final List services = discoveryClient.getServices(); + Assertions.assertNotNull(services); + Assertions.assertEquals(1, services.size()); + Assertions.assertEquals(String.format("%s@%s", IntegrationTestUtil.TEST_DEFAULT_NAMESPACE, + IntegrationTestUtil.TEST_DEFAULT_SERVICE), services.get(0)); + } + } + + @SpringBootApplication + static class EksApp { + static { + System.setProperty(CloudMapUtils.EC2_METADATA, IntegrationTestUtil.META_DATA_MOCK_RESPONSE); + } + } + + static class AwsConfigurerClientConfiguration implements BootstrapRegistryInitializer { + @Override + public void initialize(BootstrapRegistry registry) { + registry.register(AwsCloudMapStoreClientCustomizer.class, + context -> new AwsCloudMapStoreClientCustomizer() { + + @Override + public ClientOverrideConfiguration overrideConfiguration() { + return ClientOverrideConfiguration.builder().apiCallTimeout(Duration.ofMillis(2828)) + .build(); + } + + @Override + public SdkHttpClient httpClient() { + return ApacheHttpClient.builder().connectionTimeout(Duration.ofMillis(1542)).build(); + } + }); + } + } +} diff --git a/spring-cloud-aws-autoconfigure/src/test/java/io/awspring/cloud/autoconfigure/cloudmap/integration/IntegrationTestUtil.java b/spring-cloud-aws-autoconfigure/src/test/java/io/awspring/cloud/autoconfigure/cloudmap/integration/IntegrationTestUtil.java new file mode 100644 index 000000000..6c0813306 --- /dev/null +++ b/spring-cloud-aws-autoconfigure/src/test/java/io/awspring/cloud/autoconfigure/cloudmap/integration/IntegrationTestUtil.java @@ -0,0 +1,121 @@ +/* + * Copyright 2013-2021 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.awspring.cloud.autoconfigure.cloudmap.integration; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import org.testcontainers.containers.Container; +import org.testcontainers.containers.localstack.LocalStackContainer; + +import static org.testcontainers.containers.localstack.LocalStackContainer.Service.SSM; + +/** + * Utility class for integration tests. + * @author Hari Ohm Prasath + * @since 3.0 + */ +public class IntegrationTestUtil { + public static final String REGION = "us-east-1"; + public static final String CIDR_BLOCK = "10.0.0.0/16"; + public static final String META_DATA_MOCK_RESPONSE = "https://5qjz8e33hj.api.quickmocker.com"; + public static final String LOCAL_STACK_API = "LOCALSTACK_API_KEY"; + public static final String LOCAL_STACK_API_KEY = ""; // TODO: Add your localstack api key here + + public static final String TEST_DEFAULT_NAMESPACE = "test.namespace"; + public static final String TEST_DEFAULT_SERVICE = "test.service"; + + public static String createVpc(LocalStackContainer localStackContainer) { + try { + final Container.ExecResult execResult = localStackContainer.execInContainer("awslocal", "ec2", "create-vpc", + "--cidr-block", CIDR_BLOCK, "--region", REGION); + if (execResult.getExitCode() != 0) { + throw new RuntimeException("Failed to create VPC: " + execResult.getStderr()); + } + else { + return execResult.getStdout().split("VpcId")[1].split("\"")[2]; + } + } + catch (IOException | InterruptedException e) { + throw new RuntimeException(e); + } + } + + public static void createSubnet(LocalStackContainer localStackContainer, String vpcId) { + try { + localStackContainer.execInContainer("awslocal", "ec2", "create-subnet", "--vpc-id", vpcId, + "--cidr-block", CIDR_BLOCK, "--region", REGION); + } + catch (IOException | InterruptedException e) { + throw new RuntimeException(e); + } + } + + public static void createCloudMapResources(LocalStackContainer localStackContainer, String namespace, + String service, String vpcId) { + try { + // Create namespace + localStackContainer.execInContainer("awslocal", "servicediscovery", + "create-private-dns-namespace", "--name", namespace, "--region", REGION, "--vpc", vpcId); + + // List namespaces and get the id + final Container.ExecResult listNameSpaceOutput = localStackContainer.execInContainer("awslocal", "servicediscovery", + "list-namespaces", "--region", REGION); + final String namespaceId = listNameSpaceOutput.getStdout().split("Id")[1].split("\"")[2]; + + // Create service + final String serviceId = localStackContainer.execInContainer("awslocal", "servicediscovery", + "create-service", "--name", service, "--namespace-id", namespaceId, "--region", REGION) + .getStdout().split("Id")[1].split("\"")[2]; + localStackContainer.execInContainer("awslocal", "servicediscovery", "register-instance", + "--service-id", serviceId, "--attributes", "IPV4_ADDRESS=10.0.0.1,VPC_ID=" + vpcId, "--region", REGION); + } + catch (IOException | InterruptedException e) { + throw new RuntimeException(e); + } + } + + public static int getCloudMapRegisteredInstances(LocalStackContainer localStackContainer, String serviceId) { + try { + final Container.ExecResult execResult = localStackContainer.execInContainer("awslocal", "servicediscovery", + "list-instances", "--service-id", serviceId, "--region", REGION); + if (execResult.getExitCode() != 0) { + throw new RuntimeException("Failed to list instances: " + execResult.getStderr()); + } + else { + return execResult.getStdout().split("InstanceId").length; + } + } + catch (IOException | InterruptedException e) { + throw new RuntimeException(e); + } + } + + public static List getCloudMapSpringBootProperties(LocalStackContainer localStackContainer) { + List properties = new ArrayList<>(); + properties.add("--spring.cloud.aws.cloudmap.region=" + REGION); + properties.add("--spring.cloud.aws.cloudmap=http://non-existing-host/"); + properties.add("--spring.cloud.aws.cloudmap.endpoint=" + localStackContainer.getEndpointOverride(SSM) + .toString()); + properties.add("--spring.cloud.aws.credentials.access-key=noop"); + properties.add("--spring.cloud.aws.credentials.secret-key=noop"); + properties.add("--spring.cloud.aws.region.static=" + REGION); + properties.add("--spring.cloud.aws.cloudmap.enabled=true"); + + return properties; + } +} diff --git a/spring-cloud-aws-autoconfigure/src/test/java/io/awspring/cloud/autoconfigure/config/parameterstore/ParameterStoreConfigDataLoaderIntegrationTests.java b/spring-cloud-aws-autoconfigure/src/test/java/io/awspring/cloud/autoconfigure/config/parameterstore/ParameterStoreConfigDataLoaderIntegrationTests.java index 99ddee29d..47265e355 100644 --- a/spring-cloud-aws-autoconfigure/src/test/java/io/awspring/cloud/autoconfigure/config/parameterstore/ParameterStoreConfigDataLoaderIntegrationTests.java +++ b/spring-cloud-aws-autoconfigure/src/test/java/io/awspring/cloud/autoconfigure/config/parameterstore/ParameterStoreConfigDataLoaderIntegrationTests.java @@ -189,7 +189,7 @@ void serviceSpecificEndpointTakesPrecedenceOverGlobalAwsRegion() { "--spring.cloud.aws.endpoint=http://non-existing-host/", "--spring.cloud.aws.parameterstore.endpoint=" + localstack.getEndpointOverride(SSM).toString(), "--spring.cloud.aws.credentials.access-key=noop", "--spring.cloud.aws.credentials.secret-key=noop", - "--spring.cloud.aws.region.static=eu-west-1", + "--spring.cloud.aws.region.static=eu-west-1", "--spring.cloud.aws.cloudmap.enabled=false", "--logging.level.io.awspring.cloud.parameterstore=debug")) { assertThat(context.getEnvironment().getProperty("message")).isEqualTo("value from tests"); } @@ -204,7 +204,7 @@ void parameterStoreClientUsesGlobalRegion() { "--spring.config.import=aws-parameterstore:/config/spring/", "--spring.cloud.aws.endpoint=" + localstack.getEndpointOverride(SSM).toString(), "--spring.cloud.aws.credentials.access-key=noop", "--spring.cloud.aws.credentials.secret-key=noop", - "--spring.cloud.aws.region.static=" + REGION, + "--spring.cloud.aws.region.static=" + REGION, "--spring.cloud.aws.cloudmap.enabled=false", "--logging.level.io.awspring.cloud.parameterstore=debug")) { assertThat(context.getEnvironment().getProperty("message")).isEqualTo("value from tests"); } @@ -216,7 +216,8 @@ private ConfigurableApplicationContext runApplication(SpringApplication applicat "--spring.cloud.aws.parameterstore.region=" + REGION, "--" + endpointProperty + "=" + localstack.getEndpointOverride(SSM).toString(), "--spring.cloud.aws.credentials.access-key=noop", "--spring.cloud.aws.credentials.secret-key=noop", - "--spring.cloud.aws.region.static=eu-west-1", "--logging.level.io.awspring.cloud.parameterstore=debug"); + "--spring.cloud.aws.cloudmap.enabled=false", "--spring.cloud.aws.region.static=eu-west-1", + "--logging.level.io.awspring.cloud.parameterstore=debug"); } private ConfigurableApplicationContext runApplication(SpringApplication application, String springConfigImport) { diff --git a/spring-cloud-aws-autoconfigure/src/test/java/io/awspring/cloud/autoconfigure/config/secretsmanager/SecretsManagerConfigDataLoaderIntegrationTests.java b/spring-cloud-aws-autoconfigure/src/test/java/io/awspring/cloud/autoconfigure/config/secretsmanager/SecretsManagerConfigDataLoaderIntegrationTests.java index c23cc25fc..a989382e5 100644 --- a/spring-cloud-aws-autoconfigure/src/test/java/io/awspring/cloud/autoconfigure/config/secretsmanager/SecretsManagerConfigDataLoaderIntegrationTests.java +++ b/spring-cloud-aws-autoconfigure/src/test/java/io/awspring/cloud/autoconfigure/config/secretsmanager/SecretsManagerConfigDataLoaderIntegrationTests.java @@ -228,7 +228,7 @@ void serviceSpecificEndpointTakesPrecedenceOverGlobalAwsRegion() { "--spring.cloud.aws.secretsmanager.endpoint=" + localstack.getEndpointOverride(SECRETSMANAGER).toString(), "--spring.cloud.aws.credentials.access-key=noop", "--spring.cloud.aws.credentials.secret-key=noop", - "--spring.cloud.aws.region.static=eu-west-1")) { + "--spring.cloud.aws.cloudmap.enabled=false", "--spring.cloud.aws.region.static=eu-west-1")) { assertThat(context.getEnvironment().getProperty("message")).isEqualTo("value from tests"); } } @@ -242,7 +242,7 @@ void secretsManagerClientUsesGlobalRegion() { "--spring.config.import=aws-secretsmanager:/config/spring;/config/second", "--spring.cloud.aws.endpoint=" + localstack.getEndpointOverride(SECRETSMANAGER).toString(), "--spring.cloud.aws.credentials.access-key=noop", "--spring.cloud.aws.credentials.secret-key=noop", - "--spring.cloud.aws.region.static=" + REGION)) { + "--spring.cloud.aws.cloudmap.enabled=false", "--spring.cloud.aws.region.static=" + REGION)) { assertThat(context.getEnvironment().getProperty("message")).isEqualTo("value from tests"); } } @@ -257,7 +257,8 @@ private ConfigurableApplicationContext runApplication(SpringApplication applicat "--spring.cloud.aws.secretsmanager.region=" + REGION, "--" + endpointProperty + "=" + localstack.getEndpointOverride(SECRETSMANAGER).toString(), "--spring.cloud.aws.credentials.access-key=noop", "--spring.cloud.aws.credentials.secret-key=noop", - "--spring.cloud.aws.region.static=eu-west-1", "--logging.level.io.awspring.cloud.secretsmanager=debug"); + "--spring.cloud.aws.cloudmap.enabled=false", "--spring.cloud.aws.region.static=eu-west-1", + "--logging.level.io.awspring.cloud.secretsmanager=debug"); } private static void createSecret(LocalStackContainer localstack, String secretName, String parameterValue, diff --git a/spring-cloud-aws-cloudmap/pom.xml b/spring-cloud-aws-cloudmap/pom.xml new file mode 100644 index 000000000..b92741f34 --- /dev/null +++ b/spring-cloud-aws-cloudmap/pom.xml @@ -0,0 +1,62 @@ + + + 4.0.0 + + io.awspring.cloud + spring-cloud-aws + 3.0.0-SNAPSHOT + + + spring-cloud-aws-cloudmap + Spring Cloud Cloud Map + Spring Cloud AWS Cloud Map + + + + org.springframework + spring-context + + + + org.springframework.cloud + spring-cloud-commons + + + + org.springframework.boot + spring-boot-autoconfigure + + + + org.springframework.cloud + spring-cloud-context + + + + software.amazon.awssdk + servicediscovery + + + + software.amazon.awssdk + ec2 + + + + io.projectreactor.netty + reactor-netty-http + + + + + com.fasterxml.jackson.core + jackson-databind + 2.13.3 + + + + + + diff --git a/spring-cloud-aws-cloudmap/src/main/java/org/springframework/cloud/aws/cloudmap/exceptions/CreateNameSpaceException.java b/spring-cloud-aws-cloudmap/src/main/java/org/springframework/cloud/aws/cloudmap/exceptions/CreateNameSpaceException.java new file mode 100644 index 000000000..862a9f0c8 --- /dev/null +++ b/spring-cloud-aws-cloudmap/src/main/java/org/springframework/cloud/aws/cloudmap/exceptions/CreateNameSpaceException.java @@ -0,0 +1,25 @@ +/* + * Copyright 2013-2022 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.cloud.aws.cloudmap.exceptions; + +// Thrown in case of namespace exception. +public class CreateNameSpaceException extends RuntimeException { + + public CreateNameSpaceException(Throwable cause) { + super(cause); + } + +} diff --git a/spring-cloud-aws-cloudmap/src/main/java/org/springframework/cloud/aws/cloudmap/exceptions/CreateServiceException.java b/spring-cloud-aws-cloudmap/src/main/java/org/springframework/cloud/aws/cloudmap/exceptions/CreateServiceException.java new file mode 100644 index 000000000..30b03f3c4 --- /dev/null +++ b/spring-cloud-aws-cloudmap/src/main/java/org/springframework/cloud/aws/cloudmap/exceptions/CreateServiceException.java @@ -0,0 +1,25 @@ +/* + * Copyright 2013-2022 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.cloud.aws.cloudmap.exceptions; + +// Throw in case of cloudmap service exception. +public class CreateServiceException extends RuntimeException { + + public CreateServiceException(Throwable cause) { + super(cause); + } + +} diff --git a/spring-cloud-aws-cloudmap/src/main/java/org/springframework/cloud/aws/cloudmap/exceptions/MaxRetryExceededException.java b/spring-cloud-aws-cloudmap/src/main/java/org/springframework/cloud/aws/cloudmap/exceptions/MaxRetryExceededException.java new file mode 100644 index 000000000..af959958e --- /dev/null +++ b/spring-cloud-aws-cloudmap/src/main/java/org/springframework/cloud/aws/cloudmap/exceptions/MaxRetryExceededException.java @@ -0,0 +1,25 @@ +/* + * Copyright 2013-2022 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.cloud.aws.cloudmap.exceptions; + +// Thrown in case maximum retry for polling has exceeded. +public class MaxRetryExceededException extends RuntimeException { + + public MaxRetryExceededException(String message) { + super(message); + } + +} diff --git a/spring-cloud-aws-cloudmap/src/main/java/org/springframework/cloud/aws/cloudmap/exceptions/package-info.java b/spring-cloud-aws-cloudmap/src/main/java/org/springframework/cloud/aws/cloudmap/exceptions/package-info.java new file mode 100644 index 000000000..3c7422241 --- /dev/null +++ b/spring-cloud-aws-cloudmap/src/main/java/org/springframework/cloud/aws/cloudmap/exceptions/package-info.java @@ -0,0 +1,23 @@ +/* + * Copyright 2013-2022 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * {@link org.springframework.boot.context.config.ConfigDataLoader} implementation for AWS CloudMap. + */ + +@org.springframework.lang.NonNullApi +@org.springframework.lang.NonNullFields +package org.springframework.cloud.aws.cloudmap.exceptions; diff --git a/spring-cloud-aws-dependencies/pom.xml b/spring-cloud-aws-dependencies/pom.xml index e79993162..ce82fbb08 100644 --- a/spring-cloud-aws-dependencies/pom.xml +++ b/spring-cloud-aws-dependencies/pom.xml @@ -87,6 +87,12 @@ ${project.version} + + io.awspring.cloud + spring-cloud-aws-cloudmap + ${project.version} + + io.awspring.cloud spring-cloud-aws-autoconfigure @@ -147,6 +153,12 @@ ${project.version} + + io.awspring.cloud + spring-cloud-aws-starter-cloudmap + ${project.version} + + io.awspring.cloud spring-cloud-aws-starter-parameter-store diff --git a/spring-cloud-aws-s3-parent/spring-cloud-aws-s3-cross-region-client/src/main/java/io/awspring/cloud/s3/crossregion/AbstractCrossRegionS3Client.java b/spring-cloud-aws-s3-parent/spring-cloud-aws-s3-cross-region-client/src/main/java/io/awspring/cloud/s3/crossregion/AbstractCrossRegionS3Client.java index 309a0f877..0ce55c8df 100644 --- a/spring-cloud-aws-s3-parent/spring-cloud-aws-s3-cross-region-client/src/main/java/io/awspring/cloud/s3/crossregion/AbstractCrossRegionS3Client.java +++ b/spring-cloud-aws-s3-parent/spring-cloud-aws-s3-cross-region-client/src/main/java/io/awspring/cloud/s3/crossregion/AbstractCrossRegionS3Client.java @@ -16,753 +16,544 @@ package io.awspring.cloud.s3.crossregion; import java.util.function.Function; +import software.amazon.awssdk.services.s3.S3Client; import software.amazon.awssdk.awscore.exception.AwsServiceException; import software.amazon.awssdk.core.exception.SdkClientException; -import software.amazon.awssdk.services.s3.S3Client; abstract class AbstractCrossRegionS3Client implements S3Client { - abstract R executeInBucketRegion(String bucket, Function fn); - - abstract R executeInDefaultRegion(Function fn); - - @Override - public String serviceName() { - return S3Client.SERVICE_NAME; - } - - @Override - public software.amazon.awssdk.services.s3.model.AbortMultipartUploadResponse abortMultipartUpload( - software.amazon.awssdk.services.s3.model.AbortMultipartUploadRequest p0) - throws AwsServiceException, SdkClientException { - return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.abortMultipartUpload(p0)); - } - - @Override - public software.amazon.awssdk.services.s3.model.CompleteMultipartUploadResponse completeMultipartUpload( - software.amazon.awssdk.services.s3.model.CompleteMultipartUploadRequest p0) - throws AwsServiceException, SdkClientException { - return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.completeMultipartUpload(p0)); - } - - @Override - public software.amazon.awssdk.services.s3.model.CopyObjectResponse copyObject( - software.amazon.awssdk.services.s3.model.CopyObjectRequest p0) - throws AwsServiceException, SdkClientException { - return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.copyObject(p0)); - } - - @Override - public software.amazon.awssdk.services.s3.model.CreateBucketResponse createBucket( - software.amazon.awssdk.services.s3.model.CreateBucketRequest p0) - throws AwsServiceException, SdkClientException { - return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.createBucket(p0)); - } - - @Override - public software.amazon.awssdk.services.s3.model.CreateMultipartUploadResponse createMultipartUpload( - software.amazon.awssdk.services.s3.model.CreateMultipartUploadRequest p0) - throws AwsServiceException, SdkClientException { - return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.createMultipartUpload(p0)); - } - - @Override - public software.amazon.awssdk.services.s3.model.DeleteBucketResponse deleteBucket( - software.amazon.awssdk.services.s3.model.DeleteBucketRequest p0) - throws AwsServiceException, SdkClientException { - return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.deleteBucket(p0)); - } - - @Override - public software.amazon.awssdk.services.s3.model.DeleteBucketAnalyticsConfigurationResponse deleteBucketAnalyticsConfiguration( - software.amazon.awssdk.services.s3.model.DeleteBucketAnalyticsConfigurationRequest p0) - throws AwsServiceException, SdkClientException { - return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.deleteBucketAnalyticsConfiguration(p0)); - } - - @Override - public software.amazon.awssdk.services.s3.model.DeleteBucketCorsResponse deleteBucketCors( - software.amazon.awssdk.services.s3.model.DeleteBucketCorsRequest p0) - throws AwsServiceException, SdkClientException { - return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.deleteBucketCors(p0)); - } - - @Override - public software.amazon.awssdk.services.s3.model.DeleteBucketEncryptionResponse deleteBucketEncryption( - software.amazon.awssdk.services.s3.model.DeleteBucketEncryptionRequest p0) - throws AwsServiceException, SdkClientException { - return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.deleteBucketEncryption(p0)); - } - - @Override - public software.amazon.awssdk.services.s3.model.DeleteBucketIntelligentTieringConfigurationResponse deleteBucketIntelligentTieringConfiguration( - software.amazon.awssdk.services.s3.model.DeleteBucketIntelligentTieringConfigurationRequest p0) - throws AwsServiceException, SdkClientException { - return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.deleteBucketIntelligentTieringConfiguration(p0)); - } - - @Override - public software.amazon.awssdk.services.s3.model.DeleteBucketInventoryConfigurationResponse deleteBucketInventoryConfiguration( - software.amazon.awssdk.services.s3.model.DeleteBucketInventoryConfigurationRequest p0) - throws AwsServiceException, SdkClientException { - return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.deleteBucketInventoryConfiguration(p0)); - } - - @Override - public software.amazon.awssdk.services.s3.model.DeleteBucketLifecycleResponse deleteBucketLifecycle( - software.amazon.awssdk.services.s3.model.DeleteBucketLifecycleRequest p0) - throws AwsServiceException, SdkClientException { - return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.deleteBucketLifecycle(p0)); - } - - @Override - public software.amazon.awssdk.services.s3.model.DeleteBucketMetricsConfigurationResponse deleteBucketMetricsConfiguration( - software.amazon.awssdk.services.s3.model.DeleteBucketMetricsConfigurationRequest p0) - throws AwsServiceException, SdkClientException { - return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.deleteBucketMetricsConfiguration(p0)); - } - - @Override - public software.amazon.awssdk.services.s3.model.DeleteBucketOwnershipControlsResponse deleteBucketOwnershipControls( - software.amazon.awssdk.services.s3.model.DeleteBucketOwnershipControlsRequest p0) - throws AwsServiceException, SdkClientException { - return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.deleteBucketOwnershipControls(p0)); - } - - @Override - public software.amazon.awssdk.services.s3.model.DeleteBucketPolicyResponse deleteBucketPolicy( - software.amazon.awssdk.services.s3.model.DeleteBucketPolicyRequest p0) - throws AwsServiceException, SdkClientException { - return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.deleteBucketPolicy(p0)); - } - - @Override - public software.amazon.awssdk.services.s3.model.DeleteBucketReplicationResponse deleteBucketReplication( - software.amazon.awssdk.services.s3.model.DeleteBucketReplicationRequest p0) - throws AwsServiceException, SdkClientException { - return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.deleteBucketReplication(p0)); - } - - @Override - public software.amazon.awssdk.services.s3.model.DeleteBucketTaggingResponse deleteBucketTagging( - software.amazon.awssdk.services.s3.model.DeleteBucketTaggingRequest p0) - throws AwsServiceException, SdkClientException { - return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.deleteBucketTagging(p0)); - } - - @Override - public software.amazon.awssdk.services.s3.model.DeleteBucketWebsiteResponse deleteBucketWebsite( - software.amazon.awssdk.services.s3.model.DeleteBucketWebsiteRequest p0) - throws AwsServiceException, SdkClientException { - return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.deleteBucketWebsite(p0)); - } - - @Override - public software.amazon.awssdk.services.s3.model.DeleteObjectResponse deleteObject( - software.amazon.awssdk.services.s3.model.DeleteObjectRequest p0) - throws AwsServiceException, SdkClientException { - return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.deleteObject(p0)); - } - - @Override - public software.amazon.awssdk.services.s3.model.DeleteObjectTaggingResponse deleteObjectTagging( - software.amazon.awssdk.services.s3.model.DeleteObjectTaggingRequest p0) - throws AwsServiceException, SdkClientException { - return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.deleteObjectTagging(p0)); - } - - @Override - public software.amazon.awssdk.services.s3.model.DeleteObjectsResponse deleteObjects( - software.amazon.awssdk.services.s3.model.DeleteObjectsRequest p0) - throws AwsServiceException, SdkClientException { - return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.deleteObjects(p0)); - } - - @Override - public software.amazon.awssdk.services.s3.model.DeletePublicAccessBlockResponse deletePublicAccessBlock( - software.amazon.awssdk.services.s3.model.DeletePublicAccessBlockRequest p0) - throws AwsServiceException, SdkClientException { - return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.deletePublicAccessBlock(p0)); - } - - @Override - public software.amazon.awssdk.services.s3.model.GetBucketAccelerateConfigurationResponse getBucketAccelerateConfiguration( - software.amazon.awssdk.services.s3.model.GetBucketAccelerateConfigurationRequest p0) - throws AwsServiceException, SdkClientException { - return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.getBucketAccelerateConfiguration(p0)); - } - - @Override - public software.amazon.awssdk.services.s3.model.GetBucketAclResponse getBucketAcl( - software.amazon.awssdk.services.s3.model.GetBucketAclRequest p0) - throws AwsServiceException, SdkClientException { - return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.getBucketAcl(p0)); - } - - @Override - public software.amazon.awssdk.services.s3.model.GetBucketAnalyticsConfigurationResponse getBucketAnalyticsConfiguration( - software.amazon.awssdk.services.s3.model.GetBucketAnalyticsConfigurationRequest p0) - throws AwsServiceException, SdkClientException { - return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.getBucketAnalyticsConfiguration(p0)); - } - - @Override - public software.amazon.awssdk.services.s3.model.GetBucketCorsResponse getBucketCors( - software.amazon.awssdk.services.s3.model.GetBucketCorsRequest p0) - throws AwsServiceException, SdkClientException { - return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.getBucketCors(p0)); - } - - @Override - public software.amazon.awssdk.services.s3.model.GetBucketEncryptionResponse getBucketEncryption( - software.amazon.awssdk.services.s3.model.GetBucketEncryptionRequest p0) - throws AwsServiceException, SdkClientException { - return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.getBucketEncryption(p0)); - } - - @Override - public software.amazon.awssdk.services.s3.model.GetBucketIntelligentTieringConfigurationResponse getBucketIntelligentTieringConfiguration( - software.amazon.awssdk.services.s3.model.GetBucketIntelligentTieringConfigurationRequest p0) - throws AwsServiceException, SdkClientException { - return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.getBucketIntelligentTieringConfiguration(p0)); - } - - @Override - public software.amazon.awssdk.services.s3.model.GetBucketInventoryConfigurationResponse getBucketInventoryConfiguration( - software.amazon.awssdk.services.s3.model.GetBucketInventoryConfigurationRequest p0) - throws AwsServiceException, SdkClientException { - return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.getBucketInventoryConfiguration(p0)); - } - - @Override - public software.amazon.awssdk.services.s3.model.GetBucketLifecycleConfigurationResponse getBucketLifecycleConfiguration( - software.amazon.awssdk.services.s3.model.GetBucketLifecycleConfigurationRequest p0) - throws AwsServiceException, SdkClientException { - return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.getBucketLifecycleConfiguration(p0)); - } - - @Override - public software.amazon.awssdk.services.s3.model.GetBucketLocationResponse getBucketLocation( - software.amazon.awssdk.services.s3.model.GetBucketLocationRequest p0) - throws AwsServiceException, SdkClientException { - return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.getBucketLocation(p0)); - } - - @Override - public software.amazon.awssdk.services.s3.model.GetBucketLoggingResponse getBucketLogging( - software.amazon.awssdk.services.s3.model.GetBucketLoggingRequest p0) - throws AwsServiceException, SdkClientException { - return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.getBucketLogging(p0)); - } - - @Override - public software.amazon.awssdk.services.s3.model.GetBucketMetricsConfigurationResponse getBucketMetricsConfiguration( - software.amazon.awssdk.services.s3.model.GetBucketMetricsConfigurationRequest p0) - throws AwsServiceException, SdkClientException { - return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.getBucketMetricsConfiguration(p0)); - } - - @Override - public software.amazon.awssdk.services.s3.model.GetBucketNotificationConfigurationResponse getBucketNotificationConfiguration( - software.amazon.awssdk.services.s3.model.GetBucketNotificationConfigurationRequest p0) - throws AwsServiceException, SdkClientException { - return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.getBucketNotificationConfiguration(p0)); - } - - @Override - public software.amazon.awssdk.services.s3.model.GetBucketOwnershipControlsResponse getBucketOwnershipControls( - software.amazon.awssdk.services.s3.model.GetBucketOwnershipControlsRequest p0) - throws AwsServiceException, SdkClientException { - return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.getBucketOwnershipControls(p0)); - } - - @Override - public software.amazon.awssdk.services.s3.model.GetBucketPolicyResponse getBucketPolicy( - software.amazon.awssdk.services.s3.model.GetBucketPolicyRequest p0) - throws AwsServiceException, SdkClientException { - return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.getBucketPolicy(p0)); - } - - @Override - public software.amazon.awssdk.services.s3.model.GetBucketPolicyStatusResponse getBucketPolicyStatus( - software.amazon.awssdk.services.s3.model.GetBucketPolicyStatusRequest p0) - throws AwsServiceException, SdkClientException { - return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.getBucketPolicyStatus(p0)); - } - - @Override - public software.amazon.awssdk.services.s3.model.GetBucketReplicationResponse getBucketReplication( - software.amazon.awssdk.services.s3.model.GetBucketReplicationRequest p0) - throws AwsServiceException, SdkClientException { - return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.getBucketReplication(p0)); - } - - @Override - public software.amazon.awssdk.services.s3.model.GetBucketRequestPaymentResponse getBucketRequestPayment( - software.amazon.awssdk.services.s3.model.GetBucketRequestPaymentRequest p0) - throws AwsServiceException, SdkClientException { - return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.getBucketRequestPayment(p0)); - } - - @Override - public software.amazon.awssdk.services.s3.model.GetBucketTaggingResponse getBucketTagging( - software.amazon.awssdk.services.s3.model.GetBucketTaggingRequest p0) - throws AwsServiceException, SdkClientException { - return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.getBucketTagging(p0)); - } - - @Override - public software.amazon.awssdk.services.s3.model.GetBucketVersioningResponse getBucketVersioning( - software.amazon.awssdk.services.s3.model.GetBucketVersioningRequest p0) - throws AwsServiceException, SdkClientException { - return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.getBucketVersioning(p0)); - } - - @Override - public software.amazon.awssdk.services.s3.model.GetBucketWebsiteResponse getBucketWebsite( - software.amazon.awssdk.services.s3.model.GetBucketWebsiteRequest p0) - throws AwsServiceException, SdkClientException { - return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.getBucketWebsite(p0)); - } - - @Override - public software.amazon.awssdk.core.ResponseInputStream getObject( - software.amazon.awssdk.services.s3.model.GetObjectRequest p0) - throws AwsServiceException, SdkClientException { - return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.getObject(p0)); - } - - @Override - public software.amazon.awssdk.services.s3.model.GetObjectResponse getObject( - software.amazon.awssdk.services.s3.model.GetObjectRequest p0, java.nio.file.Path p1) - throws AwsServiceException, SdkClientException { - return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.getObject(p0, p1)); - } - - @Override - public ReturnT getObject(software.amazon.awssdk.services.s3.model.GetObjectRequest p0, - software.amazon.awssdk.core.sync.ResponseTransformer p1) - throws AwsServiceException, SdkClientException { - return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.getObject(p0, p1)); - } - - @Override - public software.amazon.awssdk.services.s3.model.GetObjectAclResponse getObjectAcl( - software.amazon.awssdk.services.s3.model.GetObjectAclRequest p0) - throws AwsServiceException, SdkClientException { - return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.getObjectAcl(p0)); - } - - @Override - public software.amazon.awssdk.core.ResponseBytes getObjectAsBytes( - software.amazon.awssdk.services.s3.model.GetObjectRequest p0) - throws AwsServiceException, SdkClientException { - return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.getObjectAsBytes(p0)); - } - - @Override - public software.amazon.awssdk.services.s3.model.GetObjectAttributesResponse getObjectAttributes( - software.amazon.awssdk.services.s3.model.GetObjectAttributesRequest p0) - throws AwsServiceException, SdkClientException { - return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.getObjectAttributes(p0)); - } - - @Override - public software.amazon.awssdk.services.s3.model.GetObjectLegalHoldResponse getObjectLegalHold( - software.amazon.awssdk.services.s3.model.GetObjectLegalHoldRequest p0) - throws AwsServiceException, SdkClientException { - return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.getObjectLegalHold(p0)); - } - - @Override - public software.amazon.awssdk.services.s3.model.GetObjectLockConfigurationResponse getObjectLockConfiguration( - software.amazon.awssdk.services.s3.model.GetObjectLockConfigurationRequest p0) - throws AwsServiceException, SdkClientException { - return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.getObjectLockConfiguration(p0)); - } - - @Override - public software.amazon.awssdk.services.s3.model.GetObjectRetentionResponse getObjectRetention( - software.amazon.awssdk.services.s3.model.GetObjectRetentionRequest p0) - throws AwsServiceException, SdkClientException { - return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.getObjectRetention(p0)); - } - - @Override - public software.amazon.awssdk.services.s3.model.GetObjectTaggingResponse getObjectTagging( - software.amazon.awssdk.services.s3.model.GetObjectTaggingRequest p0) - throws AwsServiceException, SdkClientException { - return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.getObjectTagging(p0)); - } - - @Override - public software.amazon.awssdk.core.ResponseInputStream getObjectTorrent( - software.amazon.awssdk.services.s3.model.GetObjectTorrentRequest p0) - throws AwsServiceException, SdkClientException { - return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.getObjectTorrent(p0)); - } - - @Override - public software.amazon.awssdk.services.s3.model.GetObjectTorrentResponse getObjectTorrent( - software.amazon.awssdk.services.s3.model.GetObjectTorrentRequest p0, java.nio.file.Path p1) - throws AwsServiceException, SdkClientException { - return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.getObjectTorrent(p0, p1)); - } - - @Override - public ReturnT getObjectTorrent(software.amazon.awssdk.services.s3.model.GetObjectTorrentRequest p0, - software.amazon.awssdk.core.sync.ResponseTransformer p1) - throws AwsServiceException, SdkClientException { - return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.getObjectTorrent(p0, p1)); - } - - @Override - public software.amazon.awssdk.core.ResponseBytes getObjectTorrentAsBytes( - software.amazon.awssdk.services.s3.model.GetObjectTorrentRequest p0) - throws AwsServiceException, SdkClientException { - return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.getObjectTorrentAsBytes(p0)); - } - - @Override - public software.amazon.awssdk.services.s3.model.GetPublicAccessBlockResponse getPublicAccessBlock( - software.amazon.awssdk.services.s3.model.GetPublicAccessBlockRequest p0) - throws AwsServiceException, SdkClientException { - return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.getPublicAccessBlock(p0)); - } - - @Override - public software.amazon.awssdk.services.s3.model.HeadBucketResponse headBucket( - software.amazon.awssdk.services.s3.model.HeadBucketRequest p0) - throws AwsServiceException, SdkClientException { - return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.headBucket(p0)); - } - - @Override - public software.amazon.awssdk.services.s3.model.HeadObjectResponse headObject( - software.amazon.awssdk.services.s3.model.HeadObjectRequest p0) - throws AwsServiceException, SdkClientException { - return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.headObject(p0)); - } - - @Override - public software.amazon.awssdk.services.s3.model.ListBucketAnalyticsConfigurationsResponse listBucketAnalyticsConfigurations( - software.amazon.awssdk.services.s3.model.ListBucketAnalyticsConfigurationsRequest p0) - throws AwsServiceException, SdkClientException { - return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.listBucketAnalyticsConfigurations(p0)); - } - - @Override - public software.amazon.awssdk.services.s3.model.ListBucketIntelligentTieringConfigurationsResponse listBucketIntelligentTieringConfigurations( - software.amazon.awssdk.services.s3.model.ListBucketIntelligentTieringConfigurationsRequest p0) - throws AwsServiceException, SdkClientException { - return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.listBucketIntelligentTieringConfigurations(p0)); - } - - @Override - public software.amazon.awssdk.services.s3.model.ListBucketInventoryConfigurationsResponse listBucketInventoryConfigurations( - software.amazon.awssdk.services.s3.model.ListBucketInventoryConfigurationsRequest p0) - throws AwsServiceException, SdkClientException { - return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.listBucketInventoryConfigurations(p0)); - } - - @Override - public software.amazon.awssdk.services.s3.model.ListBucketMetricsConfigurationsResponse listBucketMetricsConfigurations( - software.amazon.awssdk.services.s3.model.ListBucketMetricsConfigurationsRequest p0) - throws AwsServiceException, SdkClientException { - return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.listBucketMetricsConfigurations(p0)); - } - - @Override - public software.amazon.awssdk.services.s3.model.ListBucketsResponse listBuckets( - software.amazon.awssdk.services.s3.model.ListBucketsRequest p0) - throws AwsServiceException, SdkClientException { - return executeInDefaultRegion(s3Client -> s3Client.listBuckets(p0)); - } - - @Override - public software.amazon.awssdk.services.s3.model.ListMultipartUploadsResponse listMultipartUploads( - software.amazon.awssdk.services.s3.model.ListMultipartUploadsRequest p0) - throws AwsServiceException, SdkClientException { - return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.listMultipartUploads(p0)); - } - - @Override - public software.amazon.awssdk.services.s3.paginators.ListMultipartUploadsIterable listMultipartUploadsPaginator( - software.amazon.awssdk.services.s3.model.ListMultipartUploadsRequest p0) - throws AwsServiceException, SdkClientException { - return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.listMultipartUploadsPaginator(p0)); - } - - @Override - public software.amazon.awssdk.services.s3.model.ListObjectVersionsResponse listObjectVersions( - software.amazon.awssdk.services.s3.model.ListObjectVersionsRequest p0) - throws AwsServiceException, SdkClientException { - return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.listObjectVersions(p0)); - } - - @Override - public software.amazon.awssdk.services.s3.paginators.ListObjectVersionsIterable listObjectVersionsPaginator( - software.amazon.awssdk.services.s3.model.ListObjectVersionsRequest p0) - throws AwsServiceException, SdkClientException { - return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.listObjectVersionsPaginator(p0)); - } - - @Override - public software.amazon.awssdk.services.s3.model.ListObjectsResponse listObjects( - software.amazon.awssdk.services.s3.model.ListObjectsRequest p0) - throws AwsServiceException, SdkClientException { - return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.listObjects(p0)); - } - - @Override - public software.amazon.awssdk.services.s3.model.ListObjectsV2Response listObjectsV2( - software.amazon.awssdk.services.s3.model.ListObjectsV2Request p0) - throws AwsServiceException, SdkClientException { - return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.listObjectsV2(p0)); - } - - @Override - public software.amazon.awssdk.services.s3.paginators.ListObjectsV2Iterable listObjectsV2Paginator( - software.amazon.awssdk.services.s3.model.ListObjectsV2Request p0) - throws AwsServiceException, SdkClientException { - return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.listObjectsV2Paginator(p0)); - } - - @Override - public software.amazon.awssdk.services.s3.model.ListPartsResponse listParts( - software.amazon.awssdk.services.s3.model.ListPartsRequest p0) - throws AwsServiceException, SdkClientException { - return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.listParts(p0)); - } - - @Override - public software.amazon.awssdk.services.s3.paginators.ListPartsIterable listPartsPaginator( - software.amazon.awssdk.services.s3.model.ListPartsRequest p0) - throws AwsServiceException, SdkClientException { - return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.listPartsPaginator(p0)); - } - - @Override - public software.amazon.awssdk.services.s3.model.PutBucketAccelerateConfigurationResponse putBucketAccelerateConfiguration( - software.amazon.awssdk.services.s3.model.PutBucketAccelerateConfigurationRequest p0) - throws AwsServiceException, SdkClientException { - return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.putBucketAccelerateConfiguration(p0)); - } - - @Override - public software.amazon.awssdk.services.s3.model.PutBucketAclResponse putBucketAcl( - software.amazon.awssdk.services.s3.model.PutBucketAclRequest p0) - throws AwsServiceException, SdkClientException { - return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.putBucketAcl(p0)); - } - - @Override - public software.amazon.awssdk.services.s3.model.PutBucketAnalyticsConfigurationResponse putBucketAnalyticsConfiguration( - software.amazon.awssdk.services.s3.model.PutBucketAnalyticsConfigurationRequest p0) - throws AwsServiceException, SdkClientException { - return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.putBucketAnalyticsConfiguration(p0)); - } - - @Override - public software.amazon.awssdk.services.s3.model.PutBucketCorsResponse putBucketCors( - software.amazon.awssdk.services.s3.model.PutBucketCorsRequest p0) - throws AwsServiceException, SdkClientException { - return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.putBucketCors(p0)); - } - - @Override - public software.amazon.awssdk.services.s3.model.PutBucketEncryptionResponse putBucketEncryption( - software.amazon.awssdk.services.s3.model.PutBucketEncryptionRequest p0) - throws AwsServiceException, SdkClientException { - return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.putBucketEncryption(p0)); - } - - @Override - public software.amazon.awssdk.services.s3.model.PutBucketIntelligentTieringConfigurationResponse putBucketIntelligentTieringConfiguration( - software.amazon.awssdk.services.s3.model.PutBucketIntelligentTieringConfigurationRequest p0) - throws AwsServiceException, SdkClientException { - return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.putBucketIntelligentTieringConfiguration(p0)); - } - - @Override - public software.amazon.awssdk.services.s3.model.PutBucketInventoryConfigurationResponse putBucketInventoryConfiguration( - software.amazon.awssdk.services.s3.model.PutBucketInventoryConfigurationRequest p0) - throws AwsServiceException, SdkClientException { - return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.putBucketInventoryConfiguration(p0)); - } - - @Override - public software.amazon.awssdk.services.s3.model.PutBucketLifecycleConfigurationResponse putBucketLifecycleConfiguration( - software.amazon.awssdk.services.s3.model.PutBucketLifecycleConfigurationRequest p0) - throws AwsServiceException, SdkClientException { - return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.putBucketLifecycleConfiguration(p0)); - } - - @Override - public software.amazon.awssdk.services.s3.model.PutBucketLoggingResponse putBucketLogging( - software.amazon.awssdk.services.s3.model.PutBucketLoggingRequest p0) - throws AwsServiceException, SdkClientException { - return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.putBucketLogging(p0)); - } - - @Override - public software.amazon.awssdk.services.s3.model.PutBucketMetricsConfigurationResponse putBucketMetricsConfiguration( - software.amazon.awssdk.services.s3.model.PutBucketMetricsConfigurationRequest p0) - throws AwsServiceException, SdkClientException { - return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.putBucketMetricsConfiguration(p0)); - } - - @Override - public software.amazon.awssdk.services.s3.model.PutBucketNotificationConfigurationResponse putBucketNotificationConfiguration( - software.amazon.awssdk.services.s3.model.PutBucketNotificationConfigurationRequest p0) - throws AwsServiceException, SdkClientException { - return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.putBucketNotificationConfiguration(p0)); - } - - @Override - public software.amazon.awssdk.services.s3.model.PutBucketOwnershipControlsResponse putBucketOwnershipControls( - software.amazon.awssdk.services.s3.model.PutBucketOwnershipControlsRequest p0) - throws AwsServiceException, SdkClientException { - return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.putBucketOwnershipControls(p0)); - } - - @Override - public software.amazon.awssdk.services.s3.model.PutBucketPolicyResponse putBucketPolicy( - software.amazon.awssdk.services.s3.model.PutBucketPolicyRequest p0) - throws AwsServiceException, SdkClientException { - return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.putBucketPolicy(p0)); - } - - @Override - public software.amazon.awssdk.services.s3.model.PutBucketReplicationResponse putBucketReplication( - software.amazon.awssdk.services.s3.model.PutBucketReplicationRequest p0) - throws AwsServiceException, SdkClientException { - return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.putBucketReplication(p0)); - } - - @Override - public software.amazon.awssdk.services.s3.model.PutBucketRequestPaymentResponse putBucketRequestPayment( - software.amazon.awssdk.services.s3.model.PutBucketRequestPaymentRequest p0) - throws AwsServiceException, SdkClientException { - return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.putBucketRequestPayment(p0)); - } - - @Override - public software.amazon.awssdk.services.s3.model.PutBucketTaggingResponse putBucketTagging( - software.amazon.awssdk.services.s3.model.PutBucketTaggingRequest p0) - throws AwsServiceException, SdkClientException { - return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.putBucketTagging(p0)); - } - - @Override - public software.amazon.awssdk.services.s3.model.PutBucketVersioningResponse putBucketVersioning( - software.amazon.awssdk.services.s3.model.PutBucketVersioningRequest p0) - throws AwsServiceException, SdkClientException { - return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.putBucketVersioning(p0)); - } - - @Override - public software.amazon.awssdk.services.s3.model.PutBucketWebsiteResponse putBucketWebsite( - software.amazon.awssdk.services.s3.model.PutBucketWebsiteRequest p0) - throws AwsServiceException, SdkClientException { - return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.putBucketWebsite(p0)); - } - - @Override - public software.amazon.awssdk.services.s3.model.PutObjectResponse putObject( - software.amazon.awssdk.services.s3.model.PutObjectRequest p0, java.nio.file.Path p1) - throws AwsServiceException, SdkClientException { - return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.putObject(p0, p1)); - } - - @Override - public software.amazon.awssdk.services.s3.model.PutObjectResponse putObject( - software.amazon.awssdk.services.s3.model.PutObjectRequest p0, - software.amazon.awssdk.core.sync.RequestBody p1) throws AwsServiceException, SdkClientException { - return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.putObject(p0, p1)); - } - - @Override - public software.amazon.awssdk.services.s3.model.PutObjectAclResponse putObjectAcl( - software.amazon.awssdk.services.s3.model.PutObjectAclRequest p0) - throws AwsServiceException, SdkClientException { - return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.putObjectAcl(p0)); - } - - @Override - public software.amazon.awssdk.services.s3.model.PutObjectLegalHoldResponse putObjectLegalHold( - software.amazon.awssdk.services.s3.model.PutObjectLegalHoldRequest p0) - throws AwsServiceException, SdkClientException { - return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.putObjectLegalHold(p0)); - } - - @Override - public software.amazon.awssdk.services.s3.model.PutObjectLockConfigurationResponse putObjectLockConfiguration( - software.amazon.awssdk.services.s3.model.PutObjectLockConfigurationRequest p0) - throws AwsServiceException, SdkClientException { - return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.putObjectLockConfiguration(p0)); - } - - @Override - public software.amazon.awssdk.services.s3.model.PutObjectRetentionResponse putObjectRetention( - software.amazon.awssdk.services.s3.model.PutObjectRetentionRequest p0) - throws AwsServiceException, SdkClientException { - return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.putObjectRetention(p0)); - } - - @Override - public software.amazon.awssdk.services.s3.model.PutObjectTaggingResponse putObjectTagging( - software.amazon.awssdk.services.s3.model.PutObjectTaggingRequest p0) - throws AwsServiceException, SdkClientException { - return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.putObjectTagging(p0)); - } - - @Override - public software.amazon.awssdk.services.s3.model.PutPublicAccessBlockResponse putPublicAccessBlock( - software.amazon.awssdk.services.s3.model.PutPublicAccessBlockRequest p0) - throws AwsServiceException, SdkClientException { - return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.putPublicAccessBlock(p0)); - } - - @Override - public software.amazon.awssdk.services.s3.model.RestoreObjectResponse restoreObject( - software.amazon.awssdk.services.s3.model.RestoreObjectRequest p0) - throws AwsServiceException, SdkClientException { - return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.restoreObject(p0)); - } - - @Override - public software.amazon.awssdk.services.s3.model.UploadPartResponse uploadPart( - software.amazon.awssdk.services.s3.model.UploadPartRequest p0, java.nio.file.Path p1) - throws AwsServiceException, SdkClientException { - return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.uploadPart(p0, p1)); - } - - @Override - public software.amazon.awssdk.services.s3.model.UploadPartResponse uploadPart( - software.amazon.awssdk.services.s3.model.UploadPartRequest p0, - software.amazon.awssdk.core.sync.RequestBody p1) throws AwsServiceException, SdkClientException { - return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.uploadPart(p0, p1)); - } - - @Override - public software.amazon.awssdk.services.s3.model.UploadPartCopyResponse uploadPartCopy( - software.amazon.awssdk.services.s3.model.UploadPartCopyRequest p0) - throws AwsServiceException, SdkClientException { - return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.uploadPartCopy(p0)); - } - - @Override - public software.amazon.awssdk.services.s3.model.WriteGetObjectResponseResponse writeGetObjectResponse( - software.amazon.awssdk.services.s3.model.WriteGetObjectResponseRequest p0, java.nio.file.Path p1) - throws AwsServiceException, SdkClientException { - return executeInDefaultRegion(s3Client -> s3Client.writeGetObjectResponse(p0, p1)); - } - - @Override - public software.amazon.awssdk.services.s3.model.WriteGetObjectResponseResponse writeGetObjectResponse( - software.amazon.awssdk.services.s3.model.WriteGetObjectResponseRequest p0, - software.amazon.awssdk.core.sync.RequestBody p1) throws AwsServiceException, SdkClientException { - return executeInDefaultRegion(s3Client -> s3Client.writeGetObjectResponse(p0, p1)); - } + abstract R executeInBucketRegion(String bucket, Function fn); + + abstract R executeInDefaultRegion(Function fn); + + @Override + public String serviceName() { + return S3Client.SERVICE_NAME; + } + + @Override + public software.amazon.awssdk.services.s3.model.AbortMultipartUploadResponse abortMultipartUpload(software.amazon.awssdk.services.s3.model.AbortMultipartUploadRequest p0) throws AwsServiceException, SdkClientException { + return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.abortMultipartUpload(p0)); + } + + @Override + public software.amazon.awssdk.services.s3.model.CompleteMultipartUploadResponse completeMultipartUpload(software.amazon.awssdk.services.s3.model.CompleteMultipartUploadRequest p0) throws AwsServiceException, SdkClientException { + return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.completeMultipartUpload(p0)); + } + + @Override + public software.amazon.awssdk.services.s3.model.CopyObjectResponse copyObject(software.amazon.awssdk.services.s3.model.CopyObjectRequest p0) throws AwsServiceException, SdkClientException { + return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.copyObject(p0)); + } + + @Override + public software.amazon.awssdk.services.s3.model.CreateBucketResponse createBucket(software.amazon.awssdk.services.s3.model.CreateBucketRequest p0) throws AwsServiceException, SdkClientException { + return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.createBucket(p0)); + } + + @Override + public software.amazon.awssdk.services.s3.model.CreateMultipartUploadResponse createMultipartUpload(software.amazon.awssdk.services.s3.model.CreateMultipartUploadRequest p0) throws AwsServiceException, SdkClientException { + return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.createMultipartUpload(p0)); + } + + @Override + public software.amazon.awssdk.services.s3.model.DeleteBucketResponse deleteBucket(software.amazon.awssdk.services.s3.model.DeleteBucketRequest p0) throws AwsServiceException, SdkClientException { + return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.deleteBucket(p0)); + } + + @Override + public software.amazon.awssdk.services.s3.model.DeleteBucketAnalyticsConfigurationResponse deleteBucketAnalyticsConfiguration(software.amazon.awssdk.services.s3.model.DeleteBucketAnalyticsConfigurationRequest p0) throws AwsServiceException, SdkClientException { + return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.deleteBucketAnalyticsConfiguration(p0)); + } + + @Override + public software.amazon.awssdk.services.s3.model.DeleteBucketCorsResponse deleteBucketCors(software.amazon.awssdk.services.s3.model.DeleteBucketCorsRequest p0) throws AwsServiceException, SdkClientException { + return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.deleteBucketCors(p0)); + } + + @Override + public software.amazon.awssdk.services.s3.model.DeleteBucketEncryptionResponse deleteBucketEncryption(software.amazon.awssdk.services.s3.model.DeleteBucketEncryptionRequest p0) throws AwsServiceException, SdkClientException { + return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.deleteBucketEncryption(p0)); + } + + @Override + public software.amazon.awssdk.services.s3.model.DeleteBucketIntelligentTieringConfigurationResponse deleteBucketIntelligentTieringConfiguration(software.amazon.awssdk.services.s3.model.DeleteBucketIntelligentTieringConfigurationRequest p0) throws AwsServiceException, SdkClientException { + return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.deleteBucketIntelligentTieringConfiguration(p0)); + } + + @Override + public software.amazon.awssdk.services.s3.model.DeleteBucketInventoryConfigurationResponse deleteBucketInventoryConfiguration(software.amazon.awssdk.services.s3.model.DeleteBucketInventoryConfigurationRequest p0) throws AwsServiceException, SdkClientException { + return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.deleteBucketInventoryConfiguration(p0)); + } + + @Override + public software.amazon.awssdk.services.s3.model.DeleteBucketLifecycleResponse deleteBucketLifecycle(software.amazon.awssdk.services.s3.model.DeleteBucketLifecycleRequest p0) throws AwsServiceException, SdkClientException { + return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.deleteBucketLifecycle(p0)); + } + + @Override + public software.amazon.awssdk.services.s3.model.DeleteBucketMetricsConfigurationResponse deleteBucketMetricsConfiguration(software.amazon.awssdk.services.s3.model.DeleteBucketMetricsConfigurationRequest p0) throws AwsServiceException, SdkClientException { + return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.deleteBucketMetricsConfiguration(p0)); + } + + @Override + public software.amazon.awssdk.services.s3.model.DeleteBucketOwnershipControlsResponse deleteBucketOwnershipControls(software.amazon.awssdk.services.s3.model.DeleteBucketOwnershipControlsRequest p0) throws AwsServiceException, SdkClientException { + return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.deleteBucketOwnershipControls(p0)); + } + + @Override + public software.amazon.awssdk.services.s3.model.DeleteBucketPolicyResponse deleteBucketPolicy(software.amazon.awssdk.services.s3.model.DeleteBucketPolicyRequest p0) throws AwsServiceException, SdkClientException { + return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.deleteBucketPolicy(p0)); + } + + @Override + public software.amazon.awssdk.services.s3.model.DeleteBucketReplicationResponse deleteBucketReplication(software.amazon.awssdk.services.s3.model.DeleteBucketReplicationRequest p0) throws AwsServiceException, SdkClientException { + return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.deleteBucketReplication(p0)); + } + + @Override + public software.amazon.awssdk.services.s3.model.DeleteBucketTaggingResponse deleteBucketTagging(software.amazon.awssdk.services.s3.model.DeleteBucketTaggingRequest p0) throws AwsServiceException, SdkClientException { + return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.deleteBucketTagging(p0)); + } + + @Override + public software.amazon.awssdk.services.s3.model.DeleteBucketWebsiteResponse deleteBucketWebsite(software.amazon.awssdk.services.s3.model.DeleteBucketWebsiteRequest p0) throws AwsServiceException, SdkClientException { + return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.deleteBucketWebsite(p0)); + } + + @Override + public software.amazon.awssdk.services.s3.model.DeleteObjectResponse deleteObject(software.amazon.awssdk.services.s3.model.DeleteObjectRequest p0) throws AwsServiceException, SdkClientException { + return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.deleteObject(p0)); + } + + @Override + public software.amazon.awssdk.services.s3.model.DeleteObjectTaggingResponse deleteObjectTagging(software.amazon.awssdk.services.s3.model.DeleteObjectTaggingRequest p0) throws AwsServiceException, SdkClientException { + return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.deleteObjectTagging(p0)); + } + + @Override + public software.amazon.awssdk.services.s3.model.DeleteObjectsResponse deleteObjects(software.amazon.awssdk.services.s3.model.DeleteObjectsRequest p0) throws AwsServiceException, SdkClientException { + return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.deleteObjects(p0)); + } + + @Override + public software.amazon.awssdk.services.s3.model.DeletePublicAccessBlockResponse deletePublicAccessBlock(software.amazon.awssdk.services.s3.model.DeletePublicAccessBlockRequest p0) throws AwsServiceException, SdkClientException { + return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.deletePublicAccessBlock(p0)); + } + + @Override + public software.amazon.awssdk.services.s3.model.GetBucketAccelerateConfigurationResponse getBucketAccelerateConfiguration(software.amazon.awssdk.services.s3.model.GetBucketAccelerateConfigurationRequest p0) throws AwsServiceException, SdkClientException { + return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.getBucketAccelerateConfiguration(p0)); + } + + @Override + public software.amazon.awssdk.services.s3.model.GetBucketAclResponse getBucketAcl(software.amazon.awssdk.services.s3.model.GetBucketAclRequest p0) throws AwsServiceException, SdkClientException { + return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.getBucketAcl(p0)); + } + + @Override + public software.amazon.awssdk.services.s3.model.GetBucketAnalyticsConfigurationResponse getBucketAnalyticsConfiguration(software.amazon.awssdk.services.s3.model.GetBucketAnalyticsConfigurationRequest p0) throws AwsServiceException, SdkClientException { + return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.getBucketAnalyticsConfiguration(p0)); + } + + @Override + public software.amazon.awssdk.services.s3.model.GetBucketCorsResponse getBucketCors(software.amazon.awssdk.services.s3.model.GetBucketCorsRequest p0) throws AwsServiceException, SdkClientException { + return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.getBucketCors(p0)); + } + + @Override + public software.amazon.awssdk.services.s3.model.GetBucketEncryptionResponse getBucketEncryption(software.amazon.awssdk.services.s3.model.GetBucketEncryptionRequest p0) throws AwsServiceException, SdkClientException { + return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.getBucketEncryption(p0)); + } + + @Override + public software.amazon.awssdk.services.s3.model.GetBucketIntelligentTieringConfigurationResponse getBucketIntelligentTieringConfiguration(software.amazon.awssdk.services.s3.model.GetBucketIntelligentTieringConfigurationRequest p0) throws AwsServiceException, SdkClientException { + return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.getBucketIntelligentTieringConfiguration(p0)); + } + + @Override + public software.amazon.awssdk.services.s3.model.GetBucketInventoryConfigurationResponse getBucketInventoryConfiguration(software.amazon.awssdk.services.s3.model.GetBucketInventoryConfigurationRequest p0) throws AwsServiceException, SdkClientException { + return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.getBucketInventoryConfiguration(p0)); + } + + @Override + public software.amazon.awssdk.services.s3.model.GetBucketLifecycleConfigurationResponse getBucketLifecycleConfiguration(software.amazon.awssdk.services.s3.model.GetBucketLifecycleConfigurationRequest p0) throws AwsServiceException, SdkClientException { + return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.getBucketLifecycleConfiguration(p0)); + } + + @Override + public software.amazon.awssdk.services.s3.model.GetBucketLocationResponse getBucketLocation(software.amazon.awssdk.services.s3.model.GetBucketLocationRequest p0) throws AwsServiceException, SdkClientException { + return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.getBucketLocation(p0)); + } + + @Override + public software.amazon.awssdk.services.s3.model.GetBucketLoggingResponse getBucketLogging(software.amazon.awssdk.services.s3.model.GetBucketLoggingRequest p0) throws AwsServiceException, SdkClientException { + return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.getBucketLogging(p0)); + } + + @Override + public software.amazon.awssdk.services.s3.model.GetBucketMetricsConfigurationResponse getBucketMetricsConfiguration(software.amazon.awssdk.services.s3.model.GetBucketMetricsConfigurationRequest p0) throws AwsServiceException, SdkClientException { + return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.getBucketMetricsConfiguration(p0)); + } + + @Override + public software.amazon.awssdk.services.s3.model.GetBucketNotificationConfigurationResponse getBucketNotificationConfiguration(software.amazon.awssdk.services.s3.model.GetBucketNotificationConfigurationRequest p0) throws AwsServiceException, SdkClientException { + return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.getBucketNotificationConfiguration(p0)); + } + + @Override + public software.amazon.awssdk.services.s3.model.GetBucketOwnershipControlsResponse getBucketOwnershipControls(software.amazon.awssdk.services.s3.model.GetBucketOwnershipControlsRequest p0) throws AwsServiceException, SdkClientException { + return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.getBucketOwnershipControls(p0)); + } + + @Override + public software.amazon.awssdk.services.s3.model.GetBucketPolicyResponse getBucketPolicy(software.amazon.awssdk.services.s3.model.GetBucketPolicyRequest p0) throws AwsServiceException, SdkClientException { + return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.getBucketPolicy(p0)); + } + + @Override + public software.amazon.awssdk.services.s3.model.GetBucketPolicyStatusResponse getBucketPolicyStatus(software.amazon.awssdk.services.s3.model.GetBucketPolicyStatusRequest p0) throws AwsServiceException, SdkClientException { + return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.getBucketPolicyStatus(p0)); + } + + @Override + public software.amazon.awssdk.services.s3.model.GetBucketReplicationResponse getBucketReplication(software.amazon.awssdk.services.s3.model.GetBucketReplicationRequest p0) throws AwsServiceException, SdkClientException { + return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.getBucketReplication(p0)); + } + + @Override + public software.amazon.awssdk.services.s3.model.GetBucketRequestPaymentResponse getBucketRequestPayment(software.amazon.awssdk.services.s3.model.GetBucketRequestPaymentRequest p0) throws AwsServiceException, SdkClientException { + return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.getBucketRequestPayment(p0)); + } + + @Override + public software.amazon.awssdk.services.s3.model.GetBucketTaggingResponse getBucketTagging(software.amazon.awssdk.services.s3.model.GetBucketTaggingRequest p0) throws AwsServiceException, SdkClientException { + return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.getBucketTagging(p0)); + } + + @Override + public software.amazon.awssdk.services.s3.model.GetBucketVersioningResponse getBucketVersioning(software.amazon.awssdk.services.s3.model.GetBucketVersioningRequest p0) throws AwsServiceException, SdkClientException { + return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.getBucketVersioning(p0)); + } + + @Override + public software.amazon.awssdk.services.s3.model.GetBucketWebsiteResponse getBucketWebsite(software.amazon.awssdk.services.s3.model.GetBucketWebsiteRequest p0) throws AwsServiceException, SdkClientException { + return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.getBucketWebsite(p0)); + } + + @Override + public software.amazon.awssdk.core.ResponseInputStream getObject(software.amazon.awssdk.services.s3.model.GetObjectRequest p0) throws AwsServiceException, SdkClientException { + return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.getObject(p0)); + } + + @Override + public software.amazon.awssdk.services.s3.model.GetObjectResponse getObject(software.amazon.awssdk.services.s3.model.GetObjectRequest p0, java.nio.file.Path p1) throws AwsServiceException, SdkClientException { + return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.getObject(p0, p1)); + } + + @Override + public ReturnT getObject(software.amazon.awssdk.services.s3.model.GetObjectRequest p0, software.amazon.awssdk.core.sync.ResponseTransformer p1) throws AwsServiceException, SdkClientException { + return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.getObject(p0, p1)); + } + + @Override + public software.amazon.awssdk.services.s3.model.GetObjectAclResponse getObjectAcl(software.amazon.awssdk.services.s3.model.GetObjectAclRequest p0) throws AwsServiceException, SdkClientException { + return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.getObjectAcl(p0)); + } + + @Override + public software.amazon.awssdk.core.ResponseBytes getObjectAsBytes(software.amazon.awssdk.services.s3.model.GetObjectRequest p0) throws AwsServiceException, SdkClientException { + return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.getObjectAsBytes(p0)); + } + + @Override + public software.amazon.awssdk.services.s3.model.GetObjectAttributesResponse getObjectAttributes(software.amazon.awssdk.services.s3.model.GetObjectAttributesRequest p0) throws AwsServiceException, SdkClientException { + return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.getObjectAttributes(p0)); + } + + @Override + public software.amazon.awssdk.services.s3.model.GetObjectLegalHoldResponse getObjectLegalHold(software.amazon.awssdk.services.s3.model.GetObjectLegalHoldRequest p0) throws AwsServiceException, SdkClientException { + return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.getObjectLegalHold(p0)); + } + + @Override + public software.amazon.awssdk.services.s3.model.GetObjectLockConfigurationResponse getObjectLockConfiguration(software.amazon.awssdk.services.s3.model.GetObjectLockConfigurationRequest p0) throws AwsServiceException, SdkClientException { + return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.getObjectLockConfiguration(p0)); + } + + @Override + public software.amazon.awssdk.services.s3.model.GetObjectRetentionResponse getObjectRetention(software.amazon.awssdk.services.s3.model.GetObjectRetentionRequest p0) throws AwsServiceException, SdkClientException { + return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.getObjectRetention(p0)); + } + + @Override + public software.amazon.awssdk.services.s3.model.GetObjectTaggingResponse getObjectTagging(software.amazon.awssdk.services.s3.model.GetObjectTaggingRequest p0) throws AwsServiceException, SdkClientException { + return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.getObjectTagging(p0)); + } + + @Override + public software.amazon.awssdk.core.ResponseInputStream getObjectTorrent(software.amazon.awssdk.services.s3.model.GetObjectTorrentRequest p0) throws AwsServiceException, SdkClientException { + return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.getObjectTorrent(p0)); + } + + @Override + public software.amazon.awssdk.services.s3.model.GetObjectTorrentResponse getObjectTorrent(software.amazon.awssdk.services.s3.model.GetObjectTorrentRequest p0, java.nio.file.Path p1) throws AwsServiceException, SdkClientException { + return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.getObjectTorrent(p0, p1)); + } + + @Override + public ReturnT getObjectTorrent(software.amazon.awssdk.services.s3.model.GetObjectTorrentRequest p0, software.amazon.awssdk.core.sync.ResponseTransformer p1) throws AwsServiceException, SdkClientException { + return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.getObjectTorrent(p0, p1)); + } + + @Override + public software.amazon.awssdk.core.ResponseBytes getObjectTorrentAsBytes(software.amazon.awssdk.services.s3.model.GetObjectTorrentRequest p0) throws AwsServiceException, SdkClientException { + return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.getObjectTorrentAsBytes(p0)); + } + + @Override + public software.amazon.awssdk.services.s3.model.GetPublicAccessBlockResponse getPublicAccessBlock(software.amazon.awssdk.services.s3.model.GetPublicAccessBlockRequest p0) throws AwsServiceException, SdkClientException { + return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.getPublicAccessBlock(p0)); + } + + @Override + public software.amazon.awssdk.services.s3.model.HeadBucketResponse headBucket(software.amazon.awssdk.services.s3.model.HeadBucketRequest p0) throws AwsServiceException, SdkClientException { + return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.headBucket(p0)); + } + + @Override + public software.amazon.awssdk.services.s3.model.HeadObjectResponse headObject(software.amazon.awssdk.services.s3.model.HeadObjectRequest p0) throws AwsServiceException, SdkClientException { + return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.headObject(p0)); + } + + @Override + public software.amazon.awssdk.services.s3.model.ListBucketAnalyticsConfigurationsResponse listBucketAnalyticsConfigurations(software.amazon.awssdk.services.s3.model.ListBucketAnalyticsConfigurationsRequest p0) throws AwsServiceException, SdkClientException { + return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.listBucketAnalyticsConfigurations(p0)); + } + + @Override + public software.amazon.awssdk.services.s3.model.ListBucketIntelligentTieringConfigurationsResponse listBucketIntelligentTieringConfigurations(software.amazon.awssdk.services.s3.model.ListBucketIntelligentTieringConfigurationsRequest p0) throws AwsServiceException, SdkClientException { + return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.listBucketIntelligentTieringConfigurations(p0)); + } + + @Override + public software.amazon.awssdk.services.s3.model.ListBucketInventoryConfigurationsResponse listBucketInventoryConfigurations(software.amazon.awssdk.services.s3.model.ListBucketInventoryConfigurationsRequest p0) throws AwsServiceException, SdkClientException { + return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.listBucketInventoryConfigurations(p0)); + } + + @Override + public software.amazon.awssdk.services.s3.model.ListBucketMetricsConfigurationsResponse listBucketMetricsConfigurations(software.amazon.awssdk.services.s3.model.ListBucketMetricsConfigurationsRequest p0) throws AwsServiceException, SdkClientException { + return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.listBucketMetricsConfigurations(p0)); + } + + @Override + public software.amazon.awssdk.services.s3.model.ListBucketsResponse listBuckets(software.amazon.awssdk.services.s3.model.ListBucketsRequest p0) throws AwsServiceException, SdkClientException { + return executeInDefaultRegion(s3Client -> s3Client.listBuckets(p0)); + } + + @Override + public software.amazon.awssdk.services.s3.model.ListMultipartUploadsResponse listMultipartUploads(software.amazon.awssdk.services.s3.model.ListMultipartUploadsRequest p0) throws AwsServiceException, SdkClientException { + return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.listMultipartUploads(p0)); + } + + @Override + public software.amazon.awssdk.services.s3.paginators.ListMultipartUploadsIterable listMultipartUploadsPaginator(software.amazon.awssdk.services.s3.model.ListMultipartUploadsRequest p0) throws AwsServiceException, SdkClientException { + return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.listMultipartUploadsPaginator(p0)); + } + + @Override + public software.amazon.awssdk.services.s3.model.ListObjectVersionsResponse listObjectVersions(software.amazon.awssdk.services.s3.model.ListObjectVersionsRequest p0) throws AwsServiceException, SdkClientException { + return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.listObjectVersions(p0)); + } + + @Override + public software.amazon.awssdk.services.s3.paginators.ListObjectVersionsIterable listObjectVersionsPaginator(software.amazon.awssdk.services.s3.model.ListObjectVersionsRequest p0) throws AwsServiceException, SdkClientException { + return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.listObjectVersionsPaginator(p0)); + } + + @Override + public software.amazon.awssdk.services.s3.model.ListObjectsResponse listObjects(software.amazon.awssdk.services.s3.model.ListObjectsRequest p0) throws AwsServiceException, SdkClientException { + return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.listObjects(p0)); + } + + @Override + public software.amazon.awssdk.services.s3.model.ListObjectsV2Response listObjectsV2(software.amazon.awssdk.services.s3.model.ListObjectsV2Request p0) throws AwsServiceException, SdkClientException { + return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.listObjectsV2(p0)); + } + + @Override + public software.amazon.awssdk.services.s3.paginators.ListObjectsV2Iterable listObjectsV2Paginator(software.amazon.awssdk.services.s3.model.ListObjectsV2Request p0) throws AwsServiceException, SdkClientException { + return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.listObjectsV2Paginator(p0)); + } + + @Override + public software.amazon.awssdk.services.s3.model.ListPartsResponse listParts(software.amazon.awssdk.services.s3.model.ListPartsRequest p0) throws AwsServiceException, SdkClientException { + return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.listParts(p0)); + } + + @Override + public software.amazon.awssdk.services.s3.paginators.ListPartsIterable listPartsPaginator(software.amazon.awssdk.services.s3.model.ListPartsRequest p0) throws AwsServiceException, SdkClientException { + return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.listPartsPaginator(p0)); + } + + @Override + public software.amazon.awssdk.services.s3.model.PutBucketAccelerateConfigurationResponse putBucketAccelerateConfiguration(software.amazon.awssdk.services.s3.model.PutBucketAccelerateConfigurationRequest p0) throws AwsServiceException, SdkClientException { + return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.putBucketAccelerateConfiguration(p0)); + } + + @Override + public software.amazon.awssdk.services.s3.model.PutBucketAclResponse putBucketAcl(software.amazon.awssdk.services.s3.model.PutBucketAclRequest p0) throws AwsServiceException, SdkClientException { + return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.putBucketAcl(p0)); + } + + @Override + public software.amazon.awssdk.services.s3.model.PutBucketAnalyticsConfigurationResponse putBucketAnalyticsConfiguration(software.amazon.awssdk.services.s3.model.PutBucketAnalyticsConfigurationRequest p0) throws AwsServiceException, SdkClientException { + return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.putBucketAnalyticsConfiguration(p0)); + } + + @Override + public software.amazon.awssdk.services.s3.model.PutBucketCorsResponse putBucketCors(software.amazon.awssdk.services.s3.model.PutBucketCorsRequest p0) throws AwsServiceException, SdkClientException { + return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.putBucketCors(p0)); + } + + @Override + public software.amazon.awssdk.services.s3.model.PutBucketEncryptionResponse putBucketEncryption(software.amazon.awssdk.services.s3.model.PutBucketEncryptionRequest p0) throws AwsServiceException, SdkClientException { + return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.putBucketEncryption(p0)); + } + + @Override + public software.amazon.awssdk.services.s3.model.PutBucketIntelligentTieringConfigurationResponse putBucketIntelligentTieringConfiguration(software.amazon.awssdk.services.s3.model.PutBucketIntelligentTieringConfigurationRequest p0) throws AwsServiceException, SdkClientException { + return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.putBucketIntelligentTieringConfiguration(p0)); + } + + @Override + public software.amazon.awssdk.services.s3.model.PutBucketInventoryConfigurationResponse putBucketInventoryConfiguration(software.amazon.awssdk.services.s3.model.PutBucketInventoryConfigurationRequest p0) throws AwsServiceException, SdkClientException { + return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.putBucketInventoryConfiguration(p0)); + } + + @Override + public software.amazon.awssdk.services.s3.model.PutBucketLifecycleConfigurationResponse putBucketLifecycleConfiguration(software.amazon.awssdk.services.s3.model.PutBucketLifecycleConfigurationRequest p0) throws AwsServiceException, SdkClientException { + return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.putBucketLifecycleConfiguration(p0)); + } + + @Override + public software.amazon.awssdk.services.s3.model.PutBucketLoggingResponse putBucketLogging(software.amazon.awssdk.services.s3.model.PutBucketLoggingRequest p0) throws AwsServiceException, SdkClientException { + return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.putBucketLogging(p0)); + } + + @Override + public software.amazon.awssdk.services.s3.model.PutBucketMetricsConfigurationResponse putBucketMetricsConfiguration(software.amazon.awssdk.services.s3.model.PutBucketMetricsConfigurationRequest p0) throws AwsServiceException, SdkClientException { + return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.putBucketMetricsConfiguration(p0)); + } + + @Override + public software.amazon.awssdk.services.s3.model.PutBucketNotificationConfigurationResponse putBucketNotificationConfiguration(software.amazon.awssdk.services.s3.model.PutBucketNotificationConfigurationRequest p0) throws AwsServiceException, SdkClientException { + return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.putBucketNotificationConfiguration(p0)); + } + + @Override + public software.amazon.awssdk.services.s3.model.PutBucketOwnershipControlsResponse putBucketOwnershipControls(software.amazon.awssdk.services.s3.model.PutBucketOwnershipControlsRequest p0) throws AwsServiceException, SdkClientException { + return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.putBucketOwnershipControls(p0)); + } + + @Override + public software.amazon.awssdk.services.s3.model.PutBucketPolicyResponse putBucketPolicy(software.amazon.awssdk.services.s3.model.PutBucketPolicyRequest p0) throws AwsServiceException, SdkClientException { + return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.putBucketPolicy(p0)); + } + + @Override + public software.amazon.awssdk.services.s3.model.PutBucketReplicationResponse putBucketReplication(software.amazon.awssdk.services.s3.model.PutBucketReplicationRequest p0) throws AwsServiceException, SdkClientException { + return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.putBucketReplication(p0)); + } + + @Override + public software.amazon.awssdk.services.s3.model.PutBucketRequestPaymentResponse putBucketRequestPayment(software.amazon.awssdk.services.s3.model.PutBucketRequestPaymentRequest p0) throws AwsServiceException, SdkClientException { + return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.putBucketRequestPayment(p0)); + } + + @Override + public software.amazon.awssdk.services.s3.model.PutBucketTaggingResponse putBucketTagging(software.amazon.awssdk.services.s3.model.PutBucketTaggingRequest p0) throws AwsServiceException, SdkClientException { + return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.putBucketTagging(p0)); + } + + @Override + public software.amazon.awssdk.services.s3.model.PutBucketVersioningResponse putBucketVersioning(software.amazon.awssdk.services.s3.model.PutBucketVersioningRequest p0) throws AwsServiceException, SdkClientException { + return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.putBucketVersioning(p0)); + } + + @Override + public software.amazon.awssdk.services.s3.model.PutBucketWebsiteResponse putBucketWebsite(software.amazon.awssdk.services.s3.model.PutBucketWebsiteRequest p0) throws AwsServiceException, SdkClientException { + return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.putBucketWebsite(p0)); + } + + @Override + public software.amazon.awssdk.services.s3.model.PutObjectResponse putObject(software.amazon.awssdk.services.s3.model.PutObjectRequest p0, java.nio.file.Path p1) throws AwsServiceException, SdkClientException { + return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.putObject(p0, p1)); + } + + @Override + public software.amazon.awssdk.services.s3.model.PutObjectResponse putObject(software.amazon.awssdk.services.s3.model.PutObjectRequest p0, software.amazon.awssdk.core.sync.RequestBody p1) throws AwsServiceException, SdkClientException { + return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.putObject(p0, p1)); + } + + @Override + public software.amazon.awssdk.services.s3.model.PutObjectAclResponse putObjectAcl(software.amazon.awssdk.services.s3.model.PutObjectAclRequest p0) throws AwsServiceException, SdkClientException { + return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.putObjectAcl(p0)); + } + + @Override + public software.amazon.awssdk.services.s3.model.PutObjectLegalHoldResponse putObjectLegalHold(software.amazon.awssdk.services.s3.model.PutObjectLegalHoldRequest p0) throws AwsServiceException, SdkClientException { + return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.putObjectLegalHold(p0)); + } + + @Override + public software.amazon.awssdk.services.s3.model.PutObjectLockConfigurationResponse putObjectLockConfiguration(software.amazon.awssdk.services.s3.model.PutObjectLockConfigurationRequest p0) throws AwsServiceException, SdkClientException { + return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.putObjectLockConfiguration(p0)); + } + + @Override + public software.amazon.awssdk.services.s3.model.PutObjectRetentionResponse putObjectRetention(software.amazon.awssdk.services.s3.model.PutObjectRetentionRequest p0) throws AwsServiceException, SdkClientException { + return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.putObjectRetention(p0)); + } + + @Override + public software.amazon.awssdk.services.s3.model.PutObjectTaggingResponse putObjectTagging(software.amazon.awssdk.services.s3.model.PutObjectTaggingRequest p0) throws AwsServiceException, SdkClientException { + return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.putObjectTagging(p0)); + } + + @Override + public software.amazon.awssdk.services.s3.model.PutPublicAccessBlockResponse putPublicAccessBlock(software.amazon.awssdk.services.s3.model.PutPublicAccessBlockRequest p0) throws AwsServiceException, SdkClientException { + return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.putPublicAccessBlock(p0)); + } + + @Override + public software.amazon.awssdk.services.s3.model.RestoreObjectResponse restoreObject(software.amazon.awssdk.services.s3.model.RestoreObjectRequest p0) throws AwsServiceException, SdkClientException { + return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.restoreObject(p0)); + } + + @Override + public software.amazon.awssdk.services.s3.model.UploadPartResponse uploadPart(software.amazon.awssdk.services.s3.model.UploadPartRequest p0, java.nio.file.Path p1) throws AwsServiceException, SdkClientException { + return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.uploadPart(p0, p1)); + } + + @Override + public software.amazon.awssdk.services.s3.model.UploadPartResponse uploadPart(software.amazon.awssdk.services.s3.model.UploadPartRequest p0, software.amazon.awssdk.core.sync.RequestBody p1) throws AwsServiceException, SdkClientException { + return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.uploadPart(p0, p1)); + } + + @Override + public software.amazon.awssdk.services.s3.model.UploadPartCopyResponse uploadPartCopy(software.amazon.awssdk.services.s3.model.UploadPartCopyRequest p0) throws AwsServiceException, SdkClientException { + return executeInBucketRegion(p0.bucket(), s3Client -> s3Client.uploadPartCopy(p0)); + } + + @Override + public software.amazon.awssdk.services.s3.model.WriteGetObjectResponseResponse writeGetObjectResponse(software.amazon.awssdk.services.s3.model.WriteGetObjectResponseRequest p0, java.nio.file.Path p1) throws AwsServiceException, SdkClientException { + return executeInDefaultRegion(s3Client -> s3Client.writeGetObjectResponse(p0, p1)); + } + + @Override + public software.amazon.awssdk.services.s3.model.WriteGetObjectResponseResponse writeGetObjectResponse(software.amazon.awssdk.services.s3.model.WriteGetObjectResponseRequest p0, software.amazon.awssdk.core.sync.RequestBody p1) throws AwsServiceException, SdkClientException { + return executeInDefaultRegion(s3Client -> s3Client.writeGetObjectResponse(p0, p1)); + } } + diff --git a/spring-cloud-aws-samples/pom.xml b/spring-cloud-aws-samples/pom.xml index 08dc7366a..95579cb1d 100644 --- a/spring-cloud-aws-samples/pom.xml +++ b/spring-cloud-aws-samples/pom.xml @@ -21,6 +21,7 @@ spring-cloud-aws-secrets-manager-sample spring-cloud-aws-ses-sample spring-cloud-aws-sns-sample + spring-cloud-aws-cloud-map-sample diff --git a/spring-cloud-aws-samples/spring-cloud-aws-cloud-map-sample/Dockerfile b/spring-cloud-aws-samples/spring-cloud-aws-cloud-map-sample/Dockerfile new file mode 100644 index 000000000..ef95ad6e1 --- /dev/null +++ b/spring-cloud-aws-samples/spring-cloud-aws-cloud-map-sample/Dockerfile @@ -0,0 +1,5 @@ +FROM amazoncorretto:11 +ARG JAR_FILE=target/spring-cloud-aws-cloud-map-sample-3.0.0-SNAPSHOT.jar +COPY ${JAR_FILE} app.jar +ENTRYPOINT ["java","-jar","/app.jar"] +EXPOSE 8080 diff --git a/spring-cloud-aws-samples/spring-cloud-aws-cloud-map-sample/README.md b/spring-cloud-aws-samples/spring-cloud-aws-cloud-map-sample/README.md new file mode 100644 index 000000000..0881097ea --- /dev/null +++ b/spring-cloud-aws-samples/spring-cloud-aws-cloud-map-sample/README.md @@ -0,0 +1,249 @@ +# Spring Cloud AWS Cloud Map Sample + +Here is a step by step guide to run the sample application. + +---- + +## Prerequisites + +* Active internet connection +* Java 8 or above +* AWS credentials configured in `~/.aws/credentials` or as environment variables (see [AWS SDK documentation](https://docs.aws.amazon.com/sdk-for-java/v1/developer-guide/credentials.html)) +* Docker installed + +## Running the sample + +* Build the sample application + +```bash +mvn clean install +``` + +* Run a docker build to create a docker image using the `Dockerfile` defined in the root directory of this sample project: + +```bash +docker build -t aws-samples-cloudmap . +``` + +* Create a ECS Cluster using AWS CLI: + +```bash +aws ecs create-cluster --cluster-name cloudmap-sample +``` + +* Create a ECR repository using AWS CLI: + +```bash +aws ecr create-repository --repository-name cloudmap-sample +``` + +* Login to ECR, tag the docker image and push it to ECR: + +```bash +$(aws ecr get-login --no-include-email --region us-east-1) +docker tag aws-samples-cloudmap:latest .dkr.ecr.us-east-1.amazonaws.com/cloudmap-sample:latest +docker push .dkr.ecr.us-east-1.amazonaws.com/cloudmap-sample:latest +``` + +* Create a IAM role for ECS task execution: + +```bash +aws iam create-role --role-name ecsTaskExecutionRole --assume-role-policy-document file://task-execution-assume-role.json +``` + +**task-execution-assume-role.json** + +```json +{ + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Principal": { + "Service": "ecs-tasks.amazonaws.com" + }, + "Action": "sts:AssumeRole" + } + ] +} +``` + +* Attach the following IAM policy to the role created in the previous step: + +```bash +aws iam put-role-policy --role-name ecsTaskExecutionRole --policy-name ecsTaskExecutionRole --policy-document file://task-execution-role-policy.json +``` + +**task-execution-role-policy.json** + +```json +{ + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Action": [ + "logs:CreateLogStream", + "logs:PutLogEvents" + ], + "Resource": "arn:aws:logs:*:*:*" + }, + { + "Effect": "Allow", + "Action": [ + "ecr:GetAuthorizationToken", + "ecr:BatchCheckLayerAvailability", + "ecr:GetDownloadUrlForLayer", + "ecr:BatchGetImage", + "ecr:DescribeRepositories" + ], + "Resource": "*" + } + ] +} +``` + +* Create a ECS Task role: + +```bash +aws iam create-role --role-name ecsTaskRole --assume-role-policy-document file://task-assume-role.json +``` + +**task-assume-role.json** + +```json +{ + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Principal": { + "Service": "ecs-tasks.amazonaws.com" + }, + "Action": "sts:AssumeRole" + } + ] +} +``` + +* Attach the following IAM policy to the role created in the previous step: + +```bash +aws iam put-role-policy --role-name ecsTaskRole --policy-name ecsTaskRole --policy-document file://task-role-policy.json +``` + +**task-role-policy.json** + +```json +{ + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Action": [ + "servicediscovery:CreateService", + "servicediscovery:DeleteService", + "servicediscovery:GetService", + "servicediscovery:ListInstances", + "servicediscovery:ListNamespaces", + "servicediscovery:ListServices", + "servicediscovery:RegisterInstance", + "servicediscovery:DeregisterInstance", + "servicediscovery:ListOperations", + "servicediscovery:GetOperation" + ], + "Resource": "*" + } + ] +} +``` + +* Create a Task Definition using AWS CLI: + +```bash +aws ecs register-task-definition --name cloudmap-sample-definition --cli-input-json file://task-definition.json +``` + +**Task definition file** + +```json +{ + "executionRoleArn": "arn:aws:iam:::role/ecsTaskExecutionRole", + "containerDefinitions": [ + { + "logConfiguration": { + "logDriver": "awslogs", + "options": { + "awslogs-group": "/ecs/CloudMap", + "awslogs-region": "us-east-1", + "awslogs-stream-prefix": "ecs" + } + }, + "cpu": 0, + "image": ".dkr.ecr.us-east-1.amazonaws.com/cloudmap-sample:latest", + "name": "mainContainer" + } + ], + "memory": "7168", + "taskRoleArn": "arn:aws:iam:::role/ecsTaskRole", + "family": "CloudMap", + "requiresCompatibilities": [ + "FARGATE" + ], + "networkMode": "awsvpc", + "runtimePlatform": { + "operatingSystemFamily": "LINUX" + }, + "cpu": "2048" +} +``` + +* Run the task using AWS CLI: + +```bash +aws ecs run-task --cluster cloudmap-sample --task-definition cloudmap-sample-definition --launch-type FARGATE --network-configuration "awsvpcConfiguration={subnets=[],securityGroups=[],assignPublicIp=ENABLED}" +``` + +## Verify Cloud Map service registration in AWS console + +* Login to AWS console and navigate to Cloud Map service. You should see a namespace (`a-namespace`) and a service (`a-service`) created. Under the service, you should see a instance registered with the IP address of the container running in ECS. + +## Clean up + +* Stop the task using AWS CLI: + +```bash +aws ecs stop-task --cluster cloudmap-sample --task +``` + +* Delete the task definition using AWS CLI: + +```bash +aws ecs deregister-task-definition --task-definition cloudmap-sample-definition +``` + +* Delete the ECS cluster using AWS CLI: + +```bash +aws ecs delete-cluster --cluster cloudmap-sample +``` + +* Detach the associated policies + +```bash +aws iam detach-role-policy --role-name ecsTaskExecutionRole --policy-arn arn:aws:iam:::policy/ecsTaskExecutionRole +aws iam detach-role-policy --role-name ecsTaskRole --policy-arn arn:aws:iam:::policy/service-role/AmazonECSTaskExecutionRolePolicy +``` + +* Delete the IAM role for ECS task execution using AWS CLI: + +```bash +aws iam delete-role --role-name ecsTaskExecutionRole +aws iam delete-role --role-name ecsTaskRole +``` + +* Delete ECR repository using AWS CLI: + +```bash +aws ecr delete-repository --repository-name cloudmap-sample --force +``` diff --git a/spring-cloud-aws-samples/spring-cloud-aws-cloud-map-sample/pom.xml b/spring-cloud-aws-samples/spring-cloud-aws-cloud-map-sample/pom.xml new file mode 100644 index 000000000..31f82b3dd --- /dev/null +++ b/spring-cloud-aws-samples/spring-cloud-aws-cloud-map-sample/pom.xml @@ -0,0 +1,36 @@ + + + + io.awspring.cloud + spring-cloud-aws-samples + 3.0.0-SNAPSHOT + + 4.0.0 + spring-cloud-aws-cloud-map-sample + + + + org.springframework.boot + spring-boot-starter + + + org.springframework.boot + spring-boot-starter-web + + + io.awspring.cloud + spring-cloud-aws-starter-cloudmap + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + diff --git a/spring-cloud-aws-samples/spring-cloud-aws-cloud-map-sample/src/main/java/io/awspring/cloud/cloudmap/sample/SpringCloudAwsCloudMapSample.java b/spring-cloud-aws-samples/spring-cloud-aws-cloud-map-sample/src/main/java/io/awspring/cloud/cloudmap/sample/SpringCloudAwsCloudMapSample.java new file mode 100644 index 000000000..45f2a5450 --- /dev/null +++ b/spring-cloud-aws-samples/spring-cloud-aws-cloud-map-sample/src/main/java/io/awspring/cloud/cloudmap/sample/SpringCloudAwsCloudMapSample.java @@ -0,0 +1,44 @@ +/* + * Copyright 2013-2021 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.awspring.cloud.cloudmap.sample; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.ApplicationRunner; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cloud.client.discovery.DiscoveryClient; +import org.springframework.cloud.client.discovery.EnableDiscoveryClient; +import org.springframework.context.annotation.Bean; + +@SpringBootApplication +@EnableDiscoveryClient +public class SpringCloudAwsCloudMapSample { + + private static final Logger LOGGER = LoggerFactory.getLogger(SpringCloudAwsCloudMapSample.class); + @Autowired + private DiscoveryClient discoveryClient; + + public static void main(String[] args) { + SpringApplication.run(SpringCloudAwsCloudMapSample.class, args); + } + + @Bean + ApplicationRunner applicationRunner() { + return args -> LOGGER.info("Total instances: {}", discoveryClient.getServices().size()); + } +} diff --git a/spring-cloud-aws-samples/spring-cloud-aws-cloud-map-sample/src/main/resources/application.properties b/spring-cloud-aws-samples/spring-cloud-aws-cloud-map-sample/src/main/resources/application.properties new file mode 100644 index 000000000..d10a15ca3 --- /dev/null +++ b/spring-cloud-aws-samples/spring-cloud-aws-cloud-map-sample/src/main/resources/application.properties @@ -0,0 +1,15 @@ +spring.cloud.aws.cloudmap.enabled=true +spring.application.name=cloudmap-namespace-here + +spring.cloud.aws.cloudmap.deploymentPlatform=ECS + +# Discover existing cloudmap instances +spring.cloud.aws.cloudmap.discovery.discoveryList[0].service=a-service +spring.cloud.aws.cloudmap.discovery.discoveryList[0].nameSpace=a-namespace + +# Register new instance + +spring.cloud.aws.cloudmap.registry.description=Namespace for sample cloudmap registry service +spring.cloud.aws.cloudmap.registry.port=80 +spring.cloud.aws.cloudmap.registry.service=a-service +spring.cloud.aws.cloudmap.registry.nameSpace=a-namespace diff --git a/spring-cloud-aws-samples/spring-cloud-aws-cloud-map-sample/src/main/resources/bootstrap.properties b/spring-cloud-aws-samples/spring-cloud-aws-cloud-map-sample/src/main/resources/bootstrap.properties new file mode 100644 index 000000000..88533696b --- /dev/null +++ b/spring-cloud-aws-samples/spring-cloud-aws-cloud-map-sample/src/main/resources/bootstrap.properties @@ -0,0 +1,14 @@ +spring.cloud.aws.cloudmap.region=us-east-1 +spring.cloud.aws.cloudmap.enabled=true +spring.application.name=cloudmap-namespace-here + +# Discover existing cloudmap instances +spring.cloud.aws.cloudmap.discovery.discoveryList[0].service=a-service +spring.cloud.aws.cloudmap.discovery.discoveryList[0].nameSpace=a-namespace + +# Register new instance + +spring.cloud.aws.cloudmap.registry.description=Namespace for sample cloudmap registry service +spring.cloud.aws.cloudmap.registry.port=80 +spring.cloud.aws.cloudmap.registry.service=a-service +spring.cloud.aws.cloudmap.registry.nameSpace=a-namespace diff --git a/spring-cloud-aws-starters/pom.xml b/spring-cloud-aws-starters/pom.xml index 15193d28d..84586adb7 100644 --- a/spring-cloud-aws-starters/pom.xml +++ b/spring-cloud-aws-starters/pom.xml @@ -14,6 +14,7 @@ spring-cloud-aws-starter + spring-cloud-aws-starter-cloudmap spring-cloud-aws-starter-metrics spring-cloud-aws-starter-parameter-store spring-cloud-aws-starter-secrets-manager diff --git a/spring-cloud-aws-starters/spring-cloud-aws-starter-cloudmap/pom.xml b/spring-cloud-aws-starters/spring-cloud-aws-starter-cloudmap/pom.xml new file mode 100644 index 000000000..c3c94bc96 --- /dev/null +++ b/spring-cloud-aws-starters/spring-cloud-aws-starter-cloudmap/pom.xml @@ -0,0 +1,27 @@ + + + 4.0.0 + + spring-cloud-aws-starters + io.awspring.cloud + 3.0.0-SNAPSHOT + + + spring-cloud-aws-starter-cloudmap + Spring Cloud AWS Cloud Map Starter + Spring Cloud AWS Cloud Map Starter + + + + io.awspring.cloud + spring-cloud-aws-cloudmap + + + io.awspring.cloud + spring-cloud-aws-starter + + + + diff --git a/spring-cloud-aws-starters/spring-cloud-aws-starter/pom.xml b/spring-cloud-aws-starters/spring-cloud-aws-starter/pom.xml index 6434dd6b1..52e5d982b 100644 --- a/spring-cloud-aws-starters/spring-cloud-aws-starter/pom.xml +++ b/spring-cloud-aws-starters/spring-cloud-aws-starter/pom.xml @@ -16,6 +16,7 @@ io.awspring.cloud spring-cloud-aws-autoconfigure + 3.0.0-SNAPSHOT io.awspring.cloud