Skip to content
This repository was archived by the owner on Aug 29, 2025. It is now read-only.

Commit b0019f0

Browse files
committed
Make State a read-only property for Server
Cloud Control needs this to be visible as a read-only property. Also, fixed contract tests issues with tagging. The tagging test is failing in the pipeline without the changes added here.
1 parent c86314c commit b0019f0

File tree

8 files changed

+107
-10
lines changed

8 files changed

+107
-10
lines changed

aws-transfer-server/aws-transfer-server.json

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,17 @@
171171
"PUBLIC_KEY_AND_PASSWORD"
172172
]
173173
},
174+
"State": {
175+
"type": "string",
176+
"enum": [
177+
"OFFLINE",
178+
"ONLINE",
179+
"STARTING",
180+
"STOPPING",
181+
"START_FAILED",
182+
"STOP_FAILED"
183+
]
184+
},
174185
"Tag": {
175186
"type": "object",
176187
"properties": {
@@ -327,6 +338,9 @@
327338
"minLength": 19,
328339
"pattern": "^s-([0-9a-f]{17})$"
329340
},
341+
"State": {
342+
"$ref": "#/definitions/State"
343+
},
330344
"StructuredLogDestinations": {
331345
"type": "array",
332346
"insertionOrder": false,
@@ -355,7 +369,8 @@
355369
"readOnlyProperties": [
356370
"/properties/Arn",
357371
"/properties/As2ServiceManagedEgressIpAddresses",
358-
"/properties/ServerId"
372+
"/properties/ServerId",
373+
"/properties/State"
359374
],
360375
"writeOnlyProperties": [
361376
"/properties/IdentityProviderType"

aws-transfer-server/docs/README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -236,3 +236,7 @@ The list of egress IP addresses of this server. These IP addresses are only rele
236236

237237
Returns the <code>ServerId</code> value.
238238

239+
#### State
240+
241+
Returns the <code>State</code> value.
242+

aws-transfer-server/src/main/java/software/amazon/transfer/server/BaseHandlerStd.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -232,7 +232,7 @@ private static int getDelaySeconds(Exception e) {
232232
return THROTTLE_CALLBACK_DELAY_SECONDS;
233233
}
234234

235-
private String getErrorCode(Exception e) {
235+
protected String getErrorCode(Exception e) {
236236
if (e instanceof AwsServiceException && ((AwsServiceException) e).awsErrorDetails() != null) {
237237
return ((AwsServiceException) e).awsErrorDetails().errorCode();
238238
}

aws-transfer-server/src/main/java/software/amazon/transfer/server/ReadHandler.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ private ResourceModel translateFromReadResponse(final DescribeServerResponse res
7070
.protocols(server.protocolsAsStrings())
7171
.protocolDetails(ProtocolDetailsTranslator.fromSdk(server.protocolDetails()))
7272
.securityPolicyName(server.securityPolicyName())
73+
.state(server.stateAsString())
7374
.tags(translateFromSdkTags(server.tags()))
7475
.workflowDetails(WorkflowDetailsTranslator.fromSdk(server.workflowDetails()))
7576
.structuredLogDestinations(server.structuredLogDestinations())

aws-transfer-server/src/main/java/software/amazon/transfer/server/UpdateHandler.java

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
import software.amazon.awssdk.services.transfer.model.UpdateServerResponse;
3030
import software.amazon.awssdk.utils.CollectionUtils;
3131
import software.amazon.cloudformation.proxy.AmazonWebServicesClientProxy;
32+
import software.amazon.cloudformation.proxy.HandlerErrorCode;
3233
import software.amazon.cloudformation.proxy.Logger;
3334
import software.amazon.cloudformation.proxy.ProgressEvent;
3435
import software.amazon.cloudformation.proxy.ProxyClient;
@@ -43,6 +44,8 @@
4344

4445
public class UpdateHandler extends BaseHandlerStd {
4546

47+
private static final String ACCESS_DENIED_ERROR_CODE = "AccessDenied";
48+
4649
protected ProgressEvent<ResourceModel, CallbackContext> handleRequest(
4750
final AmazonWebServicesClientProxy proxy,
4851
final ResourceHandlerRequest<ResourceModel> request,
@@ -395,8 +398,13 @@ private ProgressEvent<ResourceModel, CallbackContext> tagResource(
395398
return client.injectCredentialsAndInvokeV2(tagRequest, transferClient::tagResource);
396399
}
397400
})
398-
.handleError((ignored, exception, proxyClient, model, context) ->
399-
handleError(UPDATE, exception, model, context, clientRequestToken))
401+
.handleError((ignored, exception, proxyClient, model, context) -> {
402+
if (isEnvironmentTaggingException(exception)) {
403+
return ProgressEvent.failed(
404+
model, context, HandlerErrorCode.UnauthorizedTaggingOperation, exception.getMessage());
405+
}
406+
return handleError(UPDATE, exception, model, context, clientRequestToken);
407+
})
400408
.progress();
401409
}
402410

@@ -439,8 +447,17 @@ private ProgressEvent<ResourceModel, CallbackContext> untagResource(
439447
return client.injectCredentialsAndInvokeV2(untagRequest, transferClient::untagResource);
440448
}
441449
})
442-
.handleError((ignored, exception, proxyClient, model, context) ->
443-
handleError(UPDATE, exception, model, context, clientRequestToken))
450+
.handleError((ignored, exception, proxyClient, model, context) -> {
451+
if (isEnvironmentTaggingException(exception)) {
452+
return ProgressEvent.failed(
453+
model, context, HandlerErrorCode.UnauthorizedTaggingOperation, exception.getMessage());
454+
}
455+
return handleError(UPDATE, exception, model, context, clientRequestToken);
456+
})
444457
.progress();
445458
}
459+
460+
private boolean isEnvironmentTaggingException(Exception e) {
461+
return StringUtils.equals(ACCESS_DENIED_ERROR_CODE, getErrorCode(e));
462+
}
446463
}

aws-transfer-server/src/test/java/software/amazon/transfer/server/AbstractTestBase.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,7 @@ public T client() {
151151

152152
protected static DescribeServerResponse describeServerFromModel(
153153
String serverId, String state, ResourceModel model) {
154+
model.setState(state);
154155
return DescribeServerResponse.builder()
155156
.server(DescribedServer.builder()
156157
.arn(getTestServerArn(serverId))
@@ -193,6 +194,7 @@ protected static ResourceModel setupSimpleServerModel(String endpointType) {
193194
.identityProviderType(DEFAULT_IDENTITY_PROVIDER_TYPE)
194195
.securityPolicyName(DEFAULT_SECURITY_POLICY)
195196
.protocols(DEFAULT_PROTOCOLS)
197+
.state(software.amazon.awssdk.services.transfer.model.State.ONLINE.name())
196198
.structuredLogDestinations(Collections.emptyList())
197199
.tags(Collections.emptyList())
198200
.build();
@@ -240,6 +242,7 @@ protected static ResourceModel fullyLoadedServerModel() {
240242
.securityPolicyName(DEFAULT_SECURITY_POLICY)
241243
.protocols(DEFAULT_PROTOCOLS)
242244
.tags(MODEL_TAGS)
245+
.state(software.amazon.awssdk.services.transfer.model.State.ONLINE.name())
243246
.structuredLogDestinations(Collections.singletonList("FooLog"))
244247
.loggingRole("loggingRole")
245248
.preAuthenticationLoginBanner("pre")

aws-transfer-server/src/test/java/software/amazon/transfer/server/UpdateHandlerTest.java

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@
2727
import org.mockito.junit.jupiter.MockitoExtension;
2828
import org.mockito.stubbing.Stubber;
2929

30+
import software.amazon.awssdk.awscore.exception.AwsErrorDetails;
31+
import software.amazon.awssdk.awscore.exception.AwsServiceException;
3032
import software.amazon.awssdk.services.transfer.model.DescribeServerRequest;
3133
import software.amazon.awssdk.services.transfer.model.DescribeServerResponse;
3234
import software.amazon.awssdk.services.transfer.model.EndpointType;
@@ -73,6 +75,45 @@ public void handleRequest_SimpleUpdate() {
7375
verify(sdkClient, atLeastOnce()).updateServer(any(UpdateServerRequest.class));
7476
}
7577

78+
@Test
79+
public void handleRequest_SimpleUpdate_Tagging_Failed() {
80+
ResourceModel model = setupSimpleServerModel(DEFAULT_ENDPOINT_TYPE);
81+
setServerId(model, "testServer");
82+
ResourceModel newModel = setupSimpleServerModel(DEFAULT_ENDPOINT_TYPE);
83+
setServerId(newModel, "testServer");
84+
newModel.setTags(Translator.translateTagMapToTagList(EXTRA_MODEL_TAGS));
85+
86+
ResourceHandlerRequest<ResourceModel> request = getResourceHandlerRequestBuilder()
87+
.previousResourceState(model)
88+
.desiredResourceState(newModel)
89+
.desiredResourceTags(EXTRA_MODEL_TAGS)
90+
.build();
91+
92+
AwsServiceException ex = AwsServiceException.builder()
93+
.awsErrorDetails(
94+
AwsErrorDetails.builder().errorCode("AccessDenied").build())
95+
.build();
96+
doThrow(ex).when(sdkClient).tagResource(any(TagResourceRequest.class));
97+
98+
updateServerAndAssertStatus(request, "ONLINE", OperationStatus.FAILED);
99+
100+
verify(sdkClient, atLeastOnce()).updateServer(any(UpdateServerRequest.class));
101+
verify(sdkClient, atLeastOnce()).tagResource(any(TagResourceRequest.class));
102+
103+
// Do untag now
104+
request = getResourceHandlerRequestBuilder()
105+
.previousResourceState(newModel)
106+
.desiredResourceState(model)
107+
.previousResourceTags(EXTRA_MODEL_TAGS)
108+
.build();
109+
doThrow(ex).when(sdkClient).untagResource(any(UntagResourceRequest.class));
110+
111+
updateServerAndAssertStatus(request, "ONLINE", OperationStatus.FAILED);
112+
113+
verify(sdkClient, atLeastOnce()).updateServer(any(UpdateServerRequest.class));
114+
verify(sdkClient, atLeastOnce()).untagResource(any(UntagResourceRequest.class));
115+
}
116+
76117
private static void setServerId(ResourceModel model, String serverId) {
77118
Region region = Region.getRegion(Regions.US_EAST_1);
78119
ServerArn serverArn = new ServerArn(region, "123456789012", serverId);

aws-transfer-user/src/main/java/software/amazon/transfer/user/BaseHandlerStd.java

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,8 @@ public abstract class BaseHandlerStd extends BaseHandler<CallbackContext> {
5151
private static final String FAILURE_LOG_MESSAGE =
5252
"[ClientRequestToken: %s] Resource %s failed in %s operation, Error: %s%n";
5353
private static final String THROTTLING_EXCEPTION_ERR_CODE = "ThrottlingException";
54+
private static final String ACCESS_DENIED_ERROR_CODE = "AccessDenied";
55+
5456
protected Logger logger;
5557

5658
@Override
@@ -334,8 +336,13 @@ private ProgressEvent<ResourceModel, CallbackContext> tagResource(
334336
return client.injectCredentialsAndInvokeV2(tagRequest, transferClient::tagResource);
335337
}
336338
})
337-
.handleError((ignored, exception, proxyClient, model, context) ->
338-
handleError(UPDATE, exception, model, context, clientRequestToken))
339+
.handleError((ignored, exception, proxyClient, model, context) -> {
340+
if (isEnvironmentTaggingException(exception)) {
341+
return ProgressEvent.failed(
342+
model, context, HandlerErrorCode.UnauthorizedTaggingOperation, exception.getMessage());
343+
}
344+
return handleError(UPDATE, exception, model, context, clientRequestToken);
345+
})
339346
.progress();
340347
}
341348

@@ -378,11 +385,20 @@ private ProgressEvent<ResourceModel, CallbackContext> untagResource(
378385
return client.injectCredentialsAndInvokeV2(untagRequest, transferClient::untagResource);
379386
}
380387
})
381-
.handleError((ignored, exception, proxyClient, model, context) ->
382-
handleError(UPDATE, exception, model, context, clientRequestToken))
388+
.handleError((ignored, exception, proxyClient, model, context) -> {
389+
if (isEnvironmentTaggingException(exception)) {
390+
return ProgressEvent.failed(
391+
model, context, HandlerErrorCode.UnauthorizedTaggingOperation, exception.getMessage());
392+
}
393+
return handleError(UPDATE, exception, model, context, clientRequestToken);
394+
})
383395
.progress();
384396
}
385397

398+
private boolean isEnvironmentTaggingException(Exception e) {
399+
return StringUtils.equals(ACCESS_DENIED_ERROR_CODE, getErrorCode(e));
400+
}
401+
386402
protected DescribeUserRequest translateToReadRequest(final ResourceModel model) {
387403
return DescribeUserRequest.builder()
388404
.serverId(model.getServerId())

0 commit comments

Comments
 (0)