Skip to content

Commit 0f79342

Browse files
tiandin0Tian Ding
andauthored
Add LocationFSxOpenZFS, LocationFSxLustre and other resource change (#29)
* Add LocationFSxOpenZFS, LocationFSxLustre and other resource change Co-authored-by: Tian Ding <[email protected]>
1 parent 91db27b commit 0f79342

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

53 files changed

+4110
-0
lines changed
Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
package software.amazon.datasync.agent;
2+
3+
import com.google.common.collect.MapDifference;
4+
import com.google.common.collect.Maps;
5+
import com.google.common.collect.Sets;
6+
import software.amazon.awssdk.services.datasync.DataSyncClient;
7+
import software.amazon.awssdk.services.datasync.model.DataSyncException;
8+
import software.amazon.awssdk.services.datasync.model.InternalException;
9+
import software.amazon.awssdk.services.datasync.model.InvalidRequestException;
10+
import software.amazon.awssdk.services.datasync.model.ListTagsForResourceRequest;
11+
import software.amazon.awssdk.services.datasync.model.ListTagsForResourceResponse;
12+
import software.amazon.awssdk.services.datasync.model.TagResourceRequest;
13+
import software.amazon.awssdk.services.datasync.model.UntagResourceRequest;
14+
import software.amazon.cloudformation.exceptions.CfnInvalidRequestException;
15+
import software.amazon.cloudformation.exceptions.CfnNotFoundException;
16+
import software.amazon.cloudformation.exceptions.CfnServiceInternalErrorException;
17+
import software.amazon.cloudformation.proxy.AmazonWebServicesClientProxy;
18+
import software.amazon.cloudformation.proxy.Logger;
19+
import software.amazon.cloudformation.proxy.ResourceHandlerRequest;
20+
21+
import java.util.HashMap;
22+
import java.util.HashSet;
23+
import java.util.Map;
24+
import java.util.Set;
25+
import java.util.stream.Collectors;
26+
27+
/**
28+
* Since tags cannot be retrieved or updated through the DataSync Describe and Update
29+
* API calls, these methods make the appropriate tag-specific API requests.
30+
*/
31+
public class TagRequestMaker {
32+
private static final String AWS_TAG_PREFIX = "aws:";
33+
34+
/**
35+
* Retrieve the tags associated with the given resource.
36+
*
37+
* @param proxy
38+
* @param client
39+
* @param resourceArn
40+
* @return the set of tags currently attached to the resource
41+
*/
42+
public static Set<Tag> listTagsForResource(
43+
final AmazonWebServicesClientProxy proxy,
44+
final DataSyncClient client,
45+
final String resourceArn) {
46+
final ListTagsForResourceRequest listTagsForResourceRequest = TagTranslator.translateToListTagsRequest(resourceArn);
47+
48+
ListTagsForResourceResponse tagsResponse;
49+
try {
50+
tagsResponse = proxy.injectCredentialsAndInvokeV2(listTagsForResourceRequest, client::listTagsForResource);
51+
} catch (InvalidRequestException e) {
52+
throw new CfnNotFoundException(ResourceModel.TYPE_NAME, resourceArn);
53+
} catch (InternalException e) {
54+
throw new CfnServiceInternalErrorException(e.getMessage(), e.getCause());
55+
} catch (DataSyncException e) {
56+
throw Translator.translateDataSyncExceptionToCfnException(e);
57+
}
58+
59+
if (tagsResponse.tags() != null) {
60+
return TagTranslator.translateTagListEntries(tagsResponse.tags());
61+
}
62+
return new HashSet<Tag>();
63+
}
64+
65+
/**
66+
* Calculate and perform a delta update (additions and removals as needed) to
67+
* resource tags based on the current and previous tags supplied by the CloudFormation request.
68+
*
69+
* @param proxy
70+
* @param client
71+
* @param resourceArn
72+
* @param request
73+
* @param logger
74+
*/
75+
public static void updateTagsForResource(
76+
final AmazonWebServicesClientProxy proxy,
77+
final DataSyncClient client,
78+
final String resourceArn,
79+
final ResourceHandlerRequest<ResourceModel> request,
80+
final Logger logger) {
81+
82+
Map<String, String> tagList = request.getDesiredResourceTags();
83+
if (tagList == null) {
84+
tagList = new HashMap<String, String>();
85+
}
86+
87+
Map<String, String> prevTagList = new HashMap<String, String>();
88+
if (request.getPreviousResourceTags() != null) {
89+
prevTagList = request.getPreviousResourceTags();
90+
}
91+
92+
final Set<String> keysToRemove = Sets.difference(
93+
prevTagList.keySet(),
94+
tagList.keySet()
95+
);
96+
97+
if (!keysToRemove.isEmpty()) {
98+
UntagResourceRequest untagResourceRequest = TagTranslator.translateToUntagResourceRequest(
99+
keysToRemove, resourceArn);
100+
try {
101+
proxy.injectCredentialsAndInvokeV2(untagResourceRequest, client::untagResource);
102+
logger.log(String.format("%s %s old tags removed successfully", ResourceModel.TYPE_NAME,
103+
resourceArn));
104+
} catch (InvalidRequestException e) {
105+
throw new CfnNotFoundException(ResourceModel.TYPE_NAME, resourceArn);
106+
} catch (InternalException e) {
107+
throw new CfnServiceInternalErrorException(e.getMessage(), e.getCause());
108+
}
109+
}
110+
111+
MapDifference<String, String> mapDifference = Maps.difference(tagList, prevTagList);
112+
final Set<Tag> tagsToAdd = mapDifference.entriesDiffering().entrySet().stream().map(entry -> {
113+
return Tag.builder().key(entry.getKey()).value(entry.getValue().leftValue()).build();
114+
}).collect(Collectors.toSet());
115+
tagsToAdd.addAll(TagTranslator.translateMapToTags(mapDifference.entriesOnlyOnLeft()));
116+
117+
for (Tag tag : tagsToAdd) {
118+
if (tag.getKey().trim().toLowerCase().startsWith(AWS_TAG_PREFIX)) {
119+
throw new CfnInvalidRequestException(tag.getKey() + " is an invalid key. aws: prefixed tag key names cannot be requested.");
120+
}
121+
}
122+
123+
if (request.getPreviousSystemTags() == null && request.getSystemTags() != null) {
124+
tagsToAdd.addAll(TagTranslator.translateMapToTags(request.getSystemTags()));
125+
}
126+
127+
if (!tagsToAdd.isEmpty()) {
128+
TagResourceRequest tagResourceRequest = TagTranslator.translateToTagResourceRequest(
129+
tagsToAdd, resourceArn);
130+
try {
131+
proxy.injectCredentialsAndInvokeV2(tagResourceRequest, client::tagResource);
132+
logger.log(String.format("%s %s tags updated successfully", ResourceModel.TYPE_NAME,
133+
resourceArn));
134+
} catch (InvalidRequestException e) {
135+
throw new CfnNotFoundException(ResourceModel.TYPE_NAME, resourceArn);
136+
} catch (InternalException e) {
137+
throw new CfnServiceInternalErrorException(e.getMessage(), e.getCause());
138+
}
139+
}
140+
}
141+
}
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
package software.amazon.datasync.agent;
2+
3+
import software.amazon.awssdk.services.datasync.model.ListTagsForResourceRequest;
4+
import software.amazon.awssdk.services.datasync.model.TagListEntry;
5+
import software.amazon.awssdk.services.datasync.model.TagResourceRequest;
6+
import software.amazon.awssdk.services.datasync.model.UntagResourceRequest;
7+
8+
import java.util.Collections;
9+
import java.util.List;
10+
import java.util.Map;
11+
import java.util.Set;
12+
import java.util.stream.Collectors;
13+
14+
public class TagTranslator {
15+
public static ListTagsForResourceRequest translateToListTagsRequest(final String resourceArn) {
16+
return ListTagsForResourceRequest.builder()
17+
.resourceArn(resourceArn)
18+
.build();
19+
}
20+
21+
public static TagResourceRequest translateToTagResourceRequest(Set<Tag> tagsToAdd, String agentArn) {
22+
return TagResourceRequest.builder()
23+
.resourceArn(agentArn)
24+
.tags(translateTags(tagsToAdd))
25+
.build();
26+
}
27+
28+
public static UntagResourceRequest translateToUntagResourceRequest(Set<String> tagsToRemove, String agentArn) {
29+
return UntagResourceRequest.builder()
30+
.resourceArn(agentArn)
31+
.keys(tagsToRemove)
32+
.build();
33+
}
34+
35+
// Convert Tag to TagListEntry
36+
static Set<TagListEntry> translateTags(final Set<Tag> tags) {
37+
if (tags == null)
38+
return Collections.emptySet();
39+
return tags.stream()
40+
.map(tag -> TagListEntry.builder().key(tag.getKey()).value(tag.getValue()).build())
41+
.collect(Collectors.toSet());
42+
}
43+
44+
// Convert TagListEntry to Tag
45+
static Set<Tag> translateTagListEntries(final List<TagListEntry> tags) {
46+
if (tags == null)
47+
return Collections.emptySet();
48+
return tags.stream()
49+
.map(tag -> Tag.builder().key(tag.key()).value(tag.value()).build())
50+
.collect(Collectors.toSet());
51+
}
52+
53+
static Map<String, String> translateTagsToMap(final Set<Tag> tags) {
54+
if (tags == null)
55+
return Collections.emptyMap();
56+
return tags.stream().collect(Collectors.toMap(Tag::getKey, Tag::getValue));
57+
}
58+
59+
static Set<TagListEntry> translateMapToTagListEntries(final Map<String, String> tags) {
60+
if (tags == null)
61+
return Collections.emptySet();
62+
return tags.entrySet().stream().map(entry -> {
63+
return TagListEntry.builder().key(entry.getKey()).value(entry.getValue()).build();
64+
}).collect(Collectors.toSet());
65+
}
66+
67+
static Set<Tag> translateMapToTags(final Map<String, String> tags) {
68+
if (tags == null)
69+
return Collections.emptySet();
70+
return tags.entrySet().stream().map(entry -> {
71+
return Tag.builder().key(entry.getKey()).value(entry.getValue()).build();
72+
}).collect(Collectors.toSet());
73+
}
74+
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
package software.amazon.datasync.agent;
2+
3+
import software.amazon.awssdk.services.datasync.model.ListTagsForResourceResponse;
4+
5+
import java.util.Arrays;
6+
import java.util.HashSet;
7+
import java.util.Set;
8+
9+
public class TagTestResources {
10+
final static Set<Tag> defaultTags = new HashSet<Tag>(Arrays.asList(
11+
Tag.builder().key("Constant").value("Should remain").build(),
12+
Tag.builder().key("Update").value("Should be updated").build(),
13+
Tag.builder().key("Delete").value("Should be deleted").build()
14+
));
15+
16+
final static Set<Tag> updatedTags = new HashSet<Tag>(Arrays.asList(
17+
Tag.builder().key("Constant").value("Should remain").build(),
18+
Tag.builder().key("Update").value("Has been updated").build(),
19+
Tag.builder().key("Add").value("Should be added").build()
20+
));
21+
22+
final static Set<Tag> TagsWithSystemTag = new HashSet<Tag>(Arrays.asList(
23+
Tag.builder().key("Constant").value("Should remain").build(),
24+
Tag.builder().key("Update").value("Should be updated").build(),
25+
Tag.builder().key("Delete").value("Should be deleted").build(),
26+
Tag.builder().key("aws:cloudformation:stackid").value("123").build()
27+
));
28+
29+
static ListTagsForResourceResponse buildDefaultTagsResponse() {
30+
return ListTagsForResourceResponse.builder()
31+
.tags(TagTranslator.translateTags(defaultTags))
32+
.build();
33+
}
34+
35+
static ListTagsForResourceResponse buildUpdatedTagsResponse() {
36+
return ListTagsForResourceResponse.builder()
37+
.tags(TagTranslator.translateTags(updatedTags))
38+
.build();
39+
}
40+
41+
static ListTagsForResourceResponse buildTagsWithSystemTagResponse() {
42+
return ListTagsForResourceResponse.builder()
43+
.tags(TagTranslator.translateTags(TagsWithSystemTag))
44+
.build();
45+
}
46+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# macOS
2+
.DS_Store
3+
._*
4+
5+
# Maven outputs
6+
.classpath
7+
8+
# IntelliJ
9+
*.iml
10+
.idea
11+
out.java
12+
out/
13+
.settings
14+
.project
15+
16+
# auto-generated files
17+
target/
18+
19+
# our logs
20+
rpdk.log*
21+
22+
# contains credentials
23+
sam-tests/
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
{
2+
"artifact_type": "RESOURCE",
3+
"typeName": "AWS::DataSync::LocationFSxLustre",
4+
"language": "java",
5+
"runtime": "java8",
6+
"entrypoint": "software.amazon.datasync.locationfsxlustre.HandlerWrapper::handleRequest",
7+
"testEntrypoint": "software.amazon.datasync.locationfsxlustre.HandlerWrapper::testEntrypoint",
8+
"settings": {
9+
"version": false,
10+
"subparser_name": null,
11+
"verbose": 0,
12+
"force": false,
13+
"type_name": null,
14+
"artifact_type": null,
15+
"namespace": [
16+
"software",
17+
"amazon",
18+
"datasync",
19+
"locationfsxlustre"
20+
],
21+
"codegen_template_path": "default",
22+
"protocolVersion": "2.0.0"
23+
},
24+
"executableEntrypoint": "software.amazon.datasync.locationfsxlustre.HandlerWrapperExecutable"
25+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# AWS::DataSync::LocationFSxLustre
2+
3+
Congratulations on starting development! Next steps:
4+
5+
1. Write the JSON schema describing your resource, `aws-datasync-locationfsxlustre.json`
6+
1. Implement your resource handlers.
7+
8+
The RPDK will automatically generate the correct resource model from the schema whenever the project is built via Maven. You can also do this manually with the following command: `cfn generate`.
9+
10+
> Please don't modify files under `target/generated-sources/rpdk`, as they will be automatically overwritten.
11+
12+
The code uses [Lombok](https://projectlombok.org/), and [you may have to install IDE integrations](https://projectlombok.org/setup/overview) to enable auto-complete for Lombok-annotated classes.

0 commit comments

Comments
 (0)