Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@
<module>spring-cloud-aws-starters/spring-cloud-aws-starter-integration-sns</module>
<module>spring-cloud-aws-starters/spring-cloud-aws-starter-sqs</module>
<module>spring-cloud-aws-starters/spring-cloud-aws-starter-integration-sqs</module>
<module>spring-cloud-aws-starters/spring-cloud-aws-starter-ecs</module>
<module>spring-cloud-aws-samples</module>
<module>spring-cloud-aws-test</module>
<module>spring-cloud-aws-modulith</module>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/*
* 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.ecs;

import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.web.client.RestClient;

/**
* Configuration for exposing ECS task metadata as properties in the Spring application context.
*
* @author Jukka Palomäki
* @since 3.5.0
*/
@AutoConfiguration
@ConditionalOnProperty(name = "spring.cloud.aws.ecs-task-metadata.enabled", havingValue = "true", matchIfMissing = false)
@ConditionalOnExpression("environment['ECS_CONTAINER_METADATA_URI_V4'] != null and environment['ECS_CONTAINER_METADATA_URI_V4'].trim() != ''")
public class EcsTaskMetadataAutoConfiguration {

@Bean
@ConditionalOnMissingBean
public EcsTaskMetadataPropertySource ecsTaskMetadataPropertySource(ConfigurableEnvironment env,
EcsTaskMetadataResolver ecsTaskMetadataResolver) {
EcsTaskMetadataPropertySource propertySource = new EcsTaskMetadataPropertySource("EcsTaskMetadata",
ecsTaskMetadataResolver);
propertySource.init();
env.getPropertySources().addFirst(propertySource);
return propertySource;
}

@Bean
@ConditionalOnMissingBean
public EcsTaskMetadataResolver ecsTaskMetadataResolver(RestClient.Builder restClientBuilder) {
return new EcsTaskMetadataResolver(restClientBuilder);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/*
* 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.ecs;

import io.awspring.cloud.core.config.AwsPropertySource;
import java.util.LinkedHashMap;
import java.util.Map;
import org.springframework.lang.Nullable;

/**
* Exposes ECS task metadata as Spring properties.
*
* @author Jukka Palomäki
* @since 3.5.0
*/
public class EcsTaskMetadataPropertySource
extends AwsPropertySource<EcsTaskMetadataPropertySource, EcsTaskMetadataResolver> {

private final String context;

private final EcsTaskMetadataResolver ecsTaskMetadataResolver;

private final Map<String, String> properties = new LinkedHashMap<>();

public EcsTaskMetadataPropertySource(String context, EcsTaskMetadataResolver ecsTaskMetadataResolver) {
super(context, ecsTaskMetadataResolver);
this.context = context;
this.ecsTaskMetadataResolver = ecsTaskMetadataResolver;
}

@Override
public EcsTaskMetadataPropertySource copy() {
return new EcsTaskMetadataPropertySource(context, source);
}

@Override
public void init() {
properties.putAll(ecsTaskMetadataResolver.getEcsTaskMetadata());
}

@Override
public String[] getPropertyNames() {
return properties.keySet().toArray(String[]::new);
}

@Override
@Nullable
public Object getProperty(String name) {
return properties.get(name);
}
}
Original file line number Diff line number Diff line change
@@ -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.ecs;

import java.util.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.MediaType;
import org.springframework.web.client.RestClient;
import org.springframework.web.client.RestClient.Builder;
import org.springframework.web.client.RestClientException;

/**
* Resolves ECS task metadata. Supports ECS task metadata endpoint version 4.
* <p>
* See <a href="https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task-metadata-endpoint-v4.html">AWS
* documentation</a> for more details.
*
* @author Jukka Palomäki
* @since 3.5.0
*/
public class EcsTaskMetadataResolver {

private static final Logger LOGGER = LoggerFactory.getLogger(EcsTaskMetadataResolver.class);
private static final String ECS_CONTAINER_METADATA_URI_V4 = System.getenv("ECS_CONTAINER_METADATA_URI_V4");

/** We expose a subset of available task metadata */
private static final Set<String> EXPOSED_PROPERTIES = Set.of("Cluster", "ServiceName", "TaskARN", "Family",
"Revision", "AvailabilityZone", "LaunchType");

private final RestClient restClient;

public EcsTaskMetadataResolver(Builder restClientBuilder) {
this.restClient = restClientBuilder.baseUrl(ECS_CONTAINER_METADATA_URI_V4).build();
}

/**
* Returns ECS task metadata. If unable to fetch the metadata, an empty Map is returned.
*/
public Map<String, String> getEcsTaskMetadata() {
Map<String, String> properties = new LinkedHashMap<>();
tryPopulateTaskMetadata(properties);
return properties;
}

@SuppressWarnings("unchecked")
private void tryPopulateTaskMetadata(Map<String, String> properties) {
try {
LOGGER.debug("Getting ECS task metadata from {}/task", ECS_CONTAINER_METADATA_URI_V4);
Map<String, Object> taskMetadata = restClient.get().uri("/task").accept(MediaType.APPLICATION_JSON)
.retrieve().body(Map.class);
if (taskMetadata != null) {
for (String key : EXPOSED_PROPERTIES) {
if (taskMetadata.containsKey(key)) {
properties.put("ecs-task-metadata." + key, taskMetadata.get(key).toString());
}
}
}
} catch (IllegalArgumentException | RestClientException e) {
LOGGER.error("Error getting ECS task metadata", e);
}
}
}
Original file line number Diff line number Diff line change
@@ -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-configuration for ECS (Elastic Container Service) integration.
*/
@org.springframework.lang.NonNullApi
@org.springframework.lang.NonNullFields
package io.awspring.cloud.autoconfigure.ecs;
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,4 @@ io.awspring.cloud.autoconfigure.config.secretsmanager.SecretsManagerAutoConfigur
io.awspring.cloud.autoconfigure.config.parameterstore.ParameterStoreReloadAutoConfiguration
io.awspring.cloud.autoconfigure.config.parameterstore.ParameterStoreAutoConfiguration
io.awspring.cloud.autoconfigure.config.s3.S3ReloadAutoConfiguration
io.awspring.cloud.autoconfigure.ecs.EcsTaskMetadataAutoConfiguration
23 changes: 23 additions & 0 deletions spring-cloud-aws-starters/spring-cloud-aws-starter-ecs/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>spring-cloud-aws</artifactId>
<groupId>io.awspring.cloud</groupId>
<version>4.0.0-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>

<name>Spring Cloud ECS Starter</name>
<artifactId>spring-cloud-aws-starter-ecs</artifactId>

<dependencies>
<dependency>
<groupId>io.awspring.cloud</groupId>
<artifactId>spring-cloud-aws-starter</artifactId>
</dependency>
</dependencies>

</project>