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

Commit 0bd2065

Browse files
authored
Adding callback context delay instead of waitime (#50)
1 parent b513b18 commit 0bd2065

File tree

3 files changed

+92
-48
lines changed

3 files changed

+92
-48
lines changed

aws-codeartifact-repository/src/main/java/software/amazon/codeartifact/repository/CallbackContext.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,5 @@
77
@lombok.ToString
88
@lombok.EqualsAndHashCode(callSuper = true)
99
public class CallbackContext extends StdCallbackContext {
10+
private boolean isCreated;
1011
}

aws-codeartifact-repository/src/main/java/software/amazon/codeartifact/repository/CreateHandler.java

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,10 @@
11
package software.amazon.codeartifact.repository;
22

33
import java.util.Set;
4-
import java.util.concurrent.TimeUnit;
54
import software.amazon.awssdk.awscore.AwsResponse;
65
import software.amazon.awssdk.awscore.exception.AwsServiceException;
76
import software.amazon.awssdk.services.codeartifact.CodeartifactClient;
87
import software.amazon.awssdk.services.codeartifact.model.ResourceNotFoundException;
9-
import software.amazon.cloudformation.exceptions.CfnGeneralServiceException;
108
import software.amazon.cloudformation.exceptions.CfnInvalidRequestException;
119
import software.amazon.cloudformation.proxy.AmazonWebServicesClientProxy;
1210
import software.amazon.cloudformation.proxy.Logger;
@@ -16,8 +14,7 @@
1614

1715

1816
public class CreateHandler extends BaseHandlerStd {
19-
private static final int PROPAGATION_DELAY_S = 10;
20-
private static final long PROPAGATION_DELAY_MS = TimeUnit.SECONDS.toMillis(PROPAGATION_DELAY_S);
17+
private static final int CALLBACK_DELAY_SECONDS = 10;
2118
private Logger logger;
2219

2320
protected ProgressEvent<ResourceModel, CallbackContext> handleRequest(
@@ -65,25 +62,36 @@ private ProgressEvent<ResourceModel, CallbackContext> createRepository(
6562
ProgressEvent<ResourceModel, CallbackContext> progress,
6663
ProxyClient<CodeartifactClient> proxyClient
6764
) {
68-
return proxy.initiate("AWS-CodeArtifact-Repository::Create", proxyClient,progress.getResourceModel(), progress.getCallbackContext())
65+
CallbackContext callbackContext = progress.getCallbackContext();
66+
67+
if (callbackContext.isCreated()) {
68+
// This happens when handler gets called again during callback delay or the handler is retrying for
69+
// a Retriable exception after repository was created already. This will prevent 409s on retry.
70+
// https://code.amazon.com/packages/AWSCloudFormationRPDKJavaPlugin/blobs/mainline/--/src/main/java/software/amazon/cloudformation/proxy/HandlerErrorCode.java
71+
return ProgressEvent.progress(progress.getResourceModel(), callbackContext);
72+
}
73+
74+
return proxy.initiate("AWS-CodeArtifact-Repository::Create", proxyClient, progress.getResourceModel(), callbackContext)
6975
.translateToServiceRequest(Translator::translateToCreateRequest)
7076
.makeServiceCall((awsRequest, client) -> {
7177
AwsResponse awsResponse = null;
7278
try {
7379
awsResponse = client.injectCredentialsAndInvokeV2(awsRequest, client.client()::createRepository);
74-
logger.log(String.format("waiting %s milliseconds for repository to become available", PROPAGATION_DELAY_MS));
75-
Thread.sleep(PROPAGATION_DELAY_MS);
76-
} catch (InterruptedException e) {
77-
throw new CfnGeneralServiceException(e);
7880
} catch (final AwsServiceException e) {
7981
String repositoryName = progress.getResourceModel().getRepositoryName();
8082
Translator.throwCfnException(e, Constants.CREATE_REPOSITORY, repositoryName);
8183
}
8284
logger.log(String.format("%s successfully created.", ResourceModel.TYPE_NAME));
85+
callbackContext.setCreated(true);
8386
return awsResponse;
8487
})
8588
.stabilize((awsRequest, awsResponse, client, model, context) -> isStabilized(model, client))
86-
.progress();
89+
// This Callback delay will return IN_PROGRESS and wait a certain amount of seconds and then retry
90+
// the whole CreateHandler chain. We are doing this to wait for eventual consistencies.
91+
// Since we are setting the isCreated flag in the callback context
92+
// the handler will not try to re-create the repository but will skip createRepository and continue down
93+
// the chain.
94+
.progress(CALLBACK_DELAY_SECONDS);
8795
}
8896

8997
private boolean hasReadOnlyProperties(final ResourceModel model) {

aws-codeartifact-repository/src/test/java/software/amazon/codeartifact/repository/CreateHandlerTest.java

Lines changed: 73 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -117,11 +117,54 @@ public void handleRequest_SimpleSuccess() {
117117
.domainName(DOMAIN_NAME)
118118
.build();
119119

120-
CreateRepositoryResponse createRepositoryResponse = CreateRepositoryResponse.builder()
120+
DescribeRepositoryResponse describeRepositoryResponse = DescribeRepositoryResponse.builder()
121121
.repository(repositoryDescription)
122122
.build();
123123

124124
when(proxyClient.client().getRepositoryPermissionsPolicy(any(GetRepositoryPermissionsPolicyRequest.class))).thenThrow(ResourceNotFoundException.class);
125+
when(proxyClient.client().describeRepository(any(DescribeRepositoryRequest.class))).thenReturn(describeRepositoryResponse);
126+
127+
CallbackContext context = new CallbackContext();
128+
context.setCreated(true);
129+
final ProgressEvent<ResourceModel, CallbackContext> response = handler.handleRequest(proxy, request, context, proxyClient, logger);
130+
131+
assertSuccess(response, desiredOutputModel);
132+
133+
verify(codeartifactClient, times(1)).describeRepository(any(DescribeRepositoryRequest.class));
134+
verify(codeartifactClient, atLeastOnce()).serviceName();
135+
}
136+
137+
@Test
138+
public void handleRequest_SimpleSuccess_callBackDelayInProgress() {
139+
final CreateHandler handler = new CreateHandler();
140+
141+
final ResourceModel model = ResourceModel.builder()
142+
.domainName(DOMAIN_NAME)
143+
.domainOwner(DOMAIN_OWNER)
144+
.repositoryName(REPO_NAME)
145+
.description(DESCRIPTION)
146+
.build();
147+
148+
final ResourceHandlerRequest<ResourceModel> request = ResourceHandlerRequest.<ResourceModel>builder()
149+
.desiredResourceState(model)
150+
.region(REGION)
151+
.awsPartition(PARTITION)
152+
.awsAccountId(DOMAIN_OWNER)
153+
.build();
154+
155+
final RepositoryDescription repositoryDescription = RepositoryDescription.builder()
156+
.name(REPO_NAME)
157+
.administratorAccount(ADMIN_ACCOUNT)
158+
.arn(REPO_ARN)
159+
.description(DESCRIPTION)
160+
.domainOwner(DOMAIN_OWNER)
161+
.domainName(DOMAIN_NAME)
162+
.build();
163+
164+
CreateRepositoryResponse createRepositoryResponse = CreateRepositoryResponse.builder()
165+
.repository(repositoryDescription)
166+
.build();
167+
125168
when(proxyClient.client().createRepository(any(CreateRepositoryRequest.class))).thenReturn(createRepositoryResponse);
126169

127170
DescribeRepositoryResponse describeRepositoryResponse = DescribeRepositoryResponse.builder()
@@ -130,12 +173,18 @@ public void handleRequest_SimpleSuccess() {
130173

131174
when(proxyClient.client().describeRepository(any(DescribeRepositoryRequest.class))).thenReturn(describeRepositoryResponse);
132175

133-
final ProgressEvent<ResourceModel, CallbackContext> response = handler.handleRequest(proxy, request, new CallbackContext(), proxyClient, logger);
176+
CallbackContext context = new CallbackContext();
177+
final ProgressEvent<ResourceModel, CallbackContext> response = handler.handleRequest(proxy, request, context, proxyClient, logger);
134178

135-
assertSuccess(response, desiredOutputModel);
179+
assertThat(response).isNotNull();
180+
assertThat(response.getStatus()).isEqualTo(OperationStatus.IN_PROGRESS);
181+
assertThat(response.getCallbackDelaySeconds()).isEqualTo(10);
182+
assertThat(response.getResourceModels()).isNull();
183+
assertThat(response.getMessage()).isNull();
184+
assertThat(response.getErrorCode()).isNull();
136185

137186
verify(codeartifactClient).createRepository(any(CreateRepositoryRequest.class));
138-
verify(codeartifactClient, times(2)).describeRepository(any(DescribeRepositoryRequest.class));
187+
verify(codeartifactClient, times(1)).describeRepository(any(DescribeRepositoryRequest.class));
139188
verify(codeartifactClient, atLeastOnce()).serviceName();
140189
}
141190

@@ -179,20 +228,20 @@ public void handleRequest_SimpleSuccess_withoutDomainOwner() {
179228
.build();
180229

181230
when(proxyClient.client().getRepositoryPermissionsPolicy(any(GetRepositoryPermissionsPolicyRequest.class))).thenThrow(ResourceNotFoundException.class);
182-
when(proxyClient.client().createRepository(any(CreateRepositoryRequest.class))).thenReturn(createRepositoryResponse);
183231

184232
DescribeRepositoryResponse describeRepositoryResponse = DescribeRepositoryResponse.builder()
185233
.repository(repositoryDescription)
186234
.build();
187235

188236
when(proxyClient.client().describeRepository(any(DescribeRepositoryRequest.class))).thenReturn(describeRepositoryResponse);
189237

190-
final ProgressEvent<ResourceModel, CallbackContext> response = handler.handleRequest(proxy, request, new CallbackContext(), proxyClient, logger);
238+
CallbackContext context = new CallbackContext();
239+
context.setCreated(true);
191240

241+
final ProgressEvent<ResourceModel, CallbackContext> response = handler.handleRequest(proxy, request, context, proxyClient, logger);
192242
assertSuccess(response, desiredOutputModel);
193243

194-
verify(codeartifactClient).createRepository(any(CreateRepositoryRequest.class));
195-
verify(codeartifactClient, times(2)).describeRepository(any(DescribeRepositoryRequest.class));
244+
verify(codeartifactClient, times(1)).describeRepository(any(DescribeRepositoryRequest.class));
196245
verify(codeartifactClient, atLeastOnce()).serviceName();
197246
}
198247

@@ -239,25 +288,20 @@ public void handleRequest_withUpstreams() {
239288
.awsAccountId(DOMAIN_OWNER)
240289
.build();
241290

242-
CreateRepositoryResponse createRepositoryResponse = CreateRepositoryResponse.builder()
243-
.repository(repositoryDescription)
244-
.build();
245-
246-
when(proxyClient.client().createRepository(any(CreateRepositoryRequest.class))).thenReturn(createRepositoryResponse);
247-
248291
DescribeRepositoryResponse describeRepositoryResponse = DescribeRepositoryResponse.builder()
249292
.repository(repositoryDescription)
250293
.build();
251294

252295
when(proxyClient.client().getRepositoryPermissionsPolicy(any(GetRepositoryPermissionsPolicyRequest.class))).thenThrow(ResourceNotFoundException.class);
253296
when(proxyClient.client().describeRepository(any(DescribeRepositoryRequest.class))).thenReturn(describeRepositoryResponse);
254297

255-
final ProgressEvent<ResourceModel, CallbackContext> response = handler.handleRequest(proxy, request, new CallbackContext(), proxyClient, logger);
298+
CallbackContext context = new CallbackContext();
299+
context.setCreated(true);
300+
final ProgressEvent<ResourceModel, CallbackContext> response = handler.handleRequest(proxy, request, context, proxyClient, logger);
256301

257302
assertSuccess(response, desiredOutputModel);
258303

259-
verify(codeartifactClient).createRepository(any(CreateRepositoryRequest.class));
260-
verify(codeartifactClient, times(2)).describeRepository(any(DescribeRepositoryRequest.class));
304+
verify(codeartifactClient, times(1)).describeRepository(any(DescribeRepositoryRequest.class));
261305
verify(codeartifactClient, never()).associateExternalConnection(any(AssociateExternalConnectionRequest.class));
262306
verify(codeartifactClient, atLeastOnce()).serviceName();
263307
}
@@ -314,20 +358,19 @@ public void handleRequest_withRepoPolicy() throws JsonProcessingException {
314358

315359
when(proxyClient.client().getRepositoryPermissionsPolicy(any(GetRepositoryPermissionsPolicyRequest.class))).thenReturn(getRepositoryPermissionsPolicyResponse);
316360

317-
when(proxyClient.client().createRepository(any(CreateRepositoryRequest.class))).thenReturn(createRepositoryResponse);
318-
319361
DescribeRepositoryResponse describeRepositoryResponse = DescribeRepositoryResponse.builder()
320362
.repository(repositoryDescription)
321363
.build();
322364

323365
when(proxyClient.client().describeRepository(any(DescribeRepositoryRequest.class))).thenReturn(describeRepositoryResponse);
324366

325-
final ProgressEvent<ResourceModel, CallbackContext> response = handler.handleRequest(proxy, request, new CallbackContext(), proxyClient, logger);
367+
CallbackContext context = new CallbackContext();
368+
context.setCreated(true);
369+
final ProgressEvent<ResourceModel, CallbackContext> response = handler.handleRequest(proxy, request, context, proxyClient, logger);
326370

327371
assertSuccess(response, desiredOutputModel);
328372

329-
verify(codeartifactClient).createRepository(any(CreateRepositoryRequest.class));
330-
verify(codeartifactClient, times(2)).describeRepository(any(DescribeRepositoryRequest.class));
373+
verify(codeartifactClient, times(1)).describeRepository(any(DescribeRepositoryRequest.class));
331374

332375
ArgumentCaptor<PutRepositoryPermissionsPolicyRequest> putRepoPermissionsPolicyRequestArgumentCaptor
333376
= ArgumentCaptor.forClass(PutRepositoryPermissionsPolicyRequest.class);
@@ -388,25 +431,21 @@ public void handleRequest_withExternalConnections_happycase() {
388431
.awsAccountId(DOMAIN_OWNER)
389432
.build();
390433

391-
CreateRepositoryResponse createRepositoryResponse = CreateRepositoryResponse.builder()
392-
.repository(repositoryDescription)
393-
.build();
394-
395434
when(proxyClient.client().getRepositoryPermissionsPolicy(any(GetRepositoryPermissionsPolicyRequest.class))).thenThrow(ResourceNotFoundException.class);
396-
when(proxyClient.client().createRepository(any(CreateRepositoryRequest.class))).thenReturn(createRepositoryResponse);
397435

398436
DescribeRepositoryResponse describeRepositoryResponse = DescribeRepositoryResponse.builder()
399437
.repository(repositoryDescription)
400438
.build();
401439

402440
when(proxyClient.client().describeRepository(any(DescribeRepositoryRequest.class))).thenReturn(describeRepositoryResponse);
403441

404-
final ProgressEvent<ResourceModel, CallbackContext> response = handler.handleRequest(proxy, request, new CallbackContext(), proxyClient, logger);
442+
CallbackContext context = new CallbackContext();
443+
context.setCreated(true);
444+
final ProgressEvent<ResourceModel, CallbackContext> response = handler.handleRequest(proxy, request, context, proxyClient, logger);
405445

406446
assertSuccess(response, desiredOutputModel);
407447

408-
verify(codeartifactClient).createRepository(any(CreateRepositoryRequest.class));
409-
verify(codeartifactClient, times(2)).describeRepository(any(DescribeRepositoryRequest.class));
448+
verify(codeartifactClient, times(1)).describeRepository(any(DescribeRepositoryRequest.class));
410449
verify(codeartifactClient).associateExternalConnection(any(AssociateExternalConnectionRequest.class));
411450
verify(codeartifactClient, atLeastOnce()).serviceName();
412451
}
@@ -439,26 +478,22 @@ public void handleRequest_withExternalConnections_moreThanOneExternalConnection(
439478
.awsAccountId(DOMAIN_OWNER)
440479
.build();
441480

442-
CreateRepositoryResponse createRepositoryResponse = CreateRepositoryResponse.builder()
443-
.repository(repositoryDescription)
444-
.build();
445-
446481
when(proxyClient.client().getRepositoryPermissionsPolicy(any(GetRepositoryPermissionsPolicyRequest.class))).thenThrow(ResourceNotFoundException.class);
447-
when(proxyClient.client().createRepository(any(CreateRepositoryRequest.class))).thenReturn(createRepositoryResponse);
448482

449483
DescribeRepositoryResponse describeRepositoryResponse = DescribeRepositoryResponse.builder()
450484
.repository(repositoryDescription)
451485
.build();
452486

453487
when(proxyClient.client().describeRepository(any(DescribeRepositoryRequest.class))).thenReturn(describeRepositoryResponse);
454488

455-
final ProgressEvent<ResourceModel, CallbackContext> response = handler.handleRequest(proxy, request, new CallbackContext(), proxyClient, logger);
489+
CallbackContext context = new CallbackContext();
490+
context.setCreated(true);
491+
final ProgressEvent<ResourceModel, CallbackContext> response = handler.handleRequest(proxy, request, context, proxyClient, logger);
456492

457493
assertThat(response).isNotNull();
458494
assertThat(response.getStatus()).isEqualTo(OperationStatus.SUCCESS);
459495

460-
verify(codeartifactClient).createRepository(any(CreateRepositoryRequest.class));
461-
verify(codeartifactClient, times(2)).describeRepository(any(DescribeRepositoryRequest.class));
496+
verify(codeartifactClient, times(1)).describeRepository(any(DescribeRepositoryRequest.class));
462497
verify(codeartifactClient, times(2)).associateExternalConnection(any(AssociateExternalConnectionRequest.class));
463498
verify(codeartifactClient, atLeastOnce()).serviceName();
464499
}

0 commit comments

Comments
 (0)