Skip to content

Commit 7f4fc69

Browse files
committed
use latest pool config when repairing google project
1 parent 4e146d7 commit 7f4fc69

6 files changed

Lines changed: 150 additions & 6 deletions

File tree

src/main/java/bio/terra/buffer/app/controller/ResourceApiController.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ public class ResourceApiController implements ResourceApi {
3131
@Override
3232
public ResponseEntity<JobModel> repairResource(String projectId, Object requestBody) {
3333
GoogleProjectUid googleProjectUid = new GoogleProjectUid().projectId(projectId);
34-
Pool pool = poolService.getPoolForGoogleProject(googleProjectUid);
34+
Pool pool = poolService.getLatestActivePoolForProject(googleProjectUid);
3535
Optional<String> flightId = flightScheduler.submitRepairResourceFlight(pool, googleProjectUid);
3636
return jobToResponse(jobService.retrieveJob(flightId.get()));
3737
}

src/main/java/bio/terra/buffer/common/PoolId.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,10 @@ public static PoolId create(String id) {
1414
return new AutoValue_PoolId(id);
1515
}
1616

17+
public String family() {
18+
return id().replaceAll("_v\\d+$", "");
19+
}
20+
1721
@Override
1822
public String toString() {
1923
return id();

src/main/java/bio/terra/buffer/db/BufferDao.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,23 @@ public List<Pool> retrievePools() {
102102
return jdbcTemplate.query(sql, POOL_ROW_MAPPER);
103103
}
104104

105+
/** Retrieves the latest active pool whose ID matches the given family prefix. */
106+
@Transactional(propagation = Propagation.SUPPORTS)
107+
public Optional<Pool> retrieveLatestActivePoolByFamily(String family) {
108+
String sql =
109+
"select p.id, p.resource_config, p.resource_type, p.creation, p.size, p.status "
110+
+ "FROM pool p "
111+
+ "WHERE p.id LIKE :family_pattern AND p.status = :status "
112+
+ "ORDER BY p.creation DESC LIMIT 1";
113+
MapSqlParameterSource params =
114+
new MapSqlParameterSource()
115+
.addValue("family_pattern", family + "_v%")
116+
.addValue("status", PoolStatus.ACTIVE.toString());
117+
118+
return Optional.ofNullable(
119+
DataAccessUtils.singleResult(jdbcTemplate.query(sql, params, POOL_ROW_MAPPER)));
120+
}
121+
105122
/** Retrieves a pool with id. */
106123
@Transactional(propagation = Propagation.SUPPORTS)
107124
public Optional<Pool> retrievePool(PoolId poolId) {

src/main/java/bio/terra/buffer/service/pool/PoolService.java

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,45 @@ public Pool getPoolForGoogleProject(GoogleProjectUid projectId) {
112112
.orElseThrow(() -> new NotFoundException(String.format("Pool for this resource does not exist: %s.", poolId)));
113113
}
114114

115+
/**
116+
* Resolves the best pool to use when repairing a Google project. Looks up the resource's original
117+
* pool, then attempts to find the latest active pool in the same family (e.g. cwb_ws_prod_v8 ->
118+
* cwb_ws_prod_v9). Falls back to the original pool if no newer active pool exists.
119+
*/
120+
public Pool getLatestActivePoolForProject(GoogleProjectUid projectId) {
121+
CloudResourceUid cloudResourceUid = new CloudResourceUid().googleProjectUid(projectId);
122+
Optional<Resource> resource = bufferDao.retrieveResource(cloudResourceUid);
123+
if (resource.isEmpty()) {
124+
throw new NotFoundException(String.format("Resource id does not exist: %s.", projectId));
125+
}
126+
PoolId originalPoolId = resource.get().poolId();
127+
String family = originalPoolId.family();
128+
129+
Optional<Pool> latestPool = bufferDao.retrieveLatestActivePoolByFamily(family);
130+
if (latestPool.isPresent()) {
131+
if (!latestPool.get().id().equals(originalPoolId)) {
132+
logger.info(
133+
"Resolved latest active pool {} (family: {}) for resource originally in pool {}",
134+
latestPool.get().id(),
135+
family,
136+
originalPoolId);
137+
}
138+
return latestPool.get();
139+
}
140+
141+
logger.warn(
142+
"No active pool found in family '{}' for pool {}; falling back to original pool",
143+
family,
144+
originalPoolId);
145+
return bufferDao
146+
.retrievePool(originalPoolId)
147+
.orElseThrow(
148+
() ->
149+
new NotFoundException(
150+
String.format(
151+
"Pool for this resource does not exist: %s.", originalPoolId)));
152+
}
153+
115154
/** Process handout resource in on transaction (anything failure will cause database rollback). */
116155
private Resource handoutResourceTransactionally(
117156
PoolId poolId, RequestHandoutId requestHandoutId) {

src/test/java/bio/terra/buffer/app/controller/ResourceApiControllerTest.java

Lines changed: 64 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -52,17 +52,17 @@ public void repairResource_ok() throws Exception {
5252
CloudResourceUid cloudResourceUid = new CloudResourceUid().googleProjectUid(googleProjectUid);
5353

5454
Pool mockPool = Pool.builder()
55-
.id(PoolId.create("test-pool"))
55+
.id(PoolId.create("test-pool_v1"))
5656
.size(10)
57-
.resourceConfig(new ResourceConfig()) // Use appropriate resource config
57+
.resourceConfig(new ResourceConfig())
5858
.creation(BufferDao.currentInstant())
5959
.resourceType(ResourceType.GOOGLE_PROJECT)
6060
.status(PoolStatus.ACTIVE)
6161
.build();
6262

6363
Resource mockResource = Resource.builder()
6464
.id(ResourceId.create(UUID.randomUUID()))
65-
.poolId(PoolId.create("test-pool"))
65+
.poolId(PoolId.create("test-pool_v1"))
6666
.cloudResourceUid(cloudResourceUid)
6767
.creation(BufferDao.currentInstant())
6868
.state(ResourceState.HANDED_OUT)
@@ -71,7 +71,7 @@ public void repairResource_ok() throws Exception {
7171

7272
Mockito.when(bufferDao.retrieveResource(cloudResourceUid))
7373
.thenReturn(Optional.of(mockResource));
74-
Mockito.when(mockBufferDAO.retrievePool(mockResource.poolId()))
74+
Mockito.when(mockBufferDAO.retrieveLatestActivePoolByFamily("test-pool"))
7575
.thenReturn(Optional.of(mockPool));
7676
Mockito.when(mockFlightScheduler.submitRepairResourceFlight(mockPool, googleProjectUid))
7777
.thenReturn(Optional.of("flight-123"));
@@ -89,7 +89,7 @@ public void repairResource_ok() throws Exception {
8989
.perform(
9090
post("/api/resource/v1/" + projectId + "/repair")
9191
.contentType(MediaType.APPLICATION_JSON)
92-
.content("{}")) // no request body is needed for this endpoint
92+
.content("{}"))
9393
.andExpect(status().isAccepted())
9494
.andReturn()
9595
.getResponse()
@@ -101,6 +101,63 @@ public void repairResource_ok() throws Exception {
101101
assertEquals(JobModel.JobStatusEnum.RUNNING, actualJob.getJobStatus());
102102
}
103103

104+
@Test
105+
public void repairResource_usesNewerActivePool() throws Exception {
106+
String projectId = "test-project-id";
107+
GoogleProjectUid googleProjectUid = new GoogleProjectUid().projectId(projectId);
108+
CloudResourceUid cloudResourceUid = new CloudResourceUid().googleProjectUid(googleProjectUid);
109+
110+
PoolId oldPoolId = PoolId.create("cwb_ws_prod_v8");
111+
PoolId newPoolId = PoolId.create("cwb_ws_prod_v9");
112+
113+
Pool newerPool = Pool.builder()
114+
.id(newPoolId)
115+
.size(10)
116+
.resourceConfig(new ResourceConfig())
117+
.creation(BufferDao.currentInstant())
118+
.resourceType(ResourceType.GOOGLE_PROJECT)
119+
.status(PoolStatus.ACTIVE)
120+
.build();
121+
122+
Resource mockResource = Resource.builder()
123+
.id(ResourceId.create(UUID.randomUUID()))
124+
.poolId(oldPoolId)
125+
.cloudResourceUid(cloudResourceUid)
126+
.creation(BufferDao.currentInstant())
127+
.state(ResourceState.HANDED_OUT)
128+
.requestHandoutId(RequestHandoutId.create("test-handout-id"))
129+
.build();
130+
131+
Mockito.when(bufferDao.retrieveResource(cloudResourceUid))
132+
.thenReturn(Optional.of(mockResource));
133+
Mockito.when(mockBufferDAO.retrieveLatestActivePoolByFamily("cwb_ws_prod"))
134+
.thenReturn(Optional.of(newerPool));
135+
Mockito.when(mockFlightScheduler.submitRepairResourceFlight(newerPool, googleProjectUid))
136+
.thenReturn(Optional.of("flight-456"));
137+
138+
JobModel mockJobModel = new JobModel()
139+
.id("flight-456")
140+
.className("RepairResourceFlight")
141+
.description("Repair resource for project " + projectId)
142+
.jobStatus(JobModel.JobStatusEnum.RUNNING)
143+
.statusCode(202);
144+
145+
Mockito.when(mockJobService.retrieveJob("flight-456")).thenReturn(mockJobModel);
146+
147+
String response = this.mvc
148+
.perform(
149+
post("/api/resource/v1/" + projectId + "/repair")
150+
.contentType(MediaType.APPLICATION_JSON)
151+
.content("{}"))
152+
.andExpect(status().isAccepted())
153+
.andReturn()
154+
.getResponse()
155+
.getContentAsString();
156+
157+
JobModel actualJob = objectMapper.readValue(response, JobModel.class);
158+
assertEquals("flight-456", actualJob.getId());
159+
}
160+
104161
@Test
105162
public void repairResourceProjectNotFound() throws Exception {
106163
String projectId = "fake-project-id";
@@ -131,6 +188,8 @@ public void repairResourcePoolNotFound() throws Exception {
131188
.build();
132189
Mockito.when(bufferDao.retrieveResource(cloudResourceUid))
133190
.thenReturn(Optional.of(resource));
191+
Mockito.when(mockBufferDAO.retrieveLatestActivePoolByFamily("non-existent-pool"))
192+
.thenReturn(Optional.empty());
134193
Mockito.when(mockBufferDAO.retrievePool(resource.poolId()))
135194
.thenThrow(new NotFoundException(String.format("Pool for this resource does not exist: %s.", resource.poolId())));
136195
this.mvc

src/test/java/bio/terra/buffer/common/PoolIdTest.java

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,4 +14,29 @@ public void storeAndRetrieveFromFlightMap() throws Exception {
1414
id.store(flightMap);
1515
assertEquals(id, PoolId.retrieve(flightMap));
1616
}
17+
18+
@Test
19+
public void family_stripsVersionSuffix() {
20+
assertEquals("cwb_ws_prod", PoolId.create("cwb_ws_prod_v9").family());
21+
}
22+
23+
@Test
24+
public void family_stripsLargeVersion() {
25+
assertEquals("vpc_sc", PoolId.create("vpc_sc_v13").family());
26+
}
27+
28+
@Test
29+
public void family_stripsSingleDigitVersion() {
30+
assertEquals("datarepo", PoolId.create("datarepo_v1").family());
31+
}
32+
33+
@Test
34+
public void family_multipleUnderscores() {
35+
assertEquals("datarepo_fakeprod", PoolId.create("datarepo_fakeprod_v1").family());
36+
}
37+
38+
@Test
39+
public void family_noVersionSuffix() {
40+
assertEquals("no_version_suffix", PoolId.create("no_version_suffix").family());
41+
}
1742
}

0 commit comments

Comments
 (0)