From 9a4bf6c1b66aef932941d140c736950f78f13697 Mon Sep 17 00:00:00 2001 From: gyeome06 Date: Sat, 2 May 2026 11:10:34 +0900 Subject: [PATCH 1/3] =?UTF-8?q?[refactor]=20#213=20UID/GID=20=EA=B4=80?= =?UTF-8?q?=EB=A6=AC=20=EA=B6=8C=ED=95=9C=EC=9D=84=20config-server?= =?UTF-8?q?=EB=A1=9C=20=EC=9D=B4=EC=A0=84=20-=20WAS=EC=97=90=EC=84=9C=20ui?= =?UTF-8?q?d=20=EC=99=84=EC=A0=84=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit WAS가 uid를 직접 결정하던 구조에서 config-server가 단독 관리하는 구조로 변경. UID 충돌(동일 uid에 복수 계정 존재) 문제를 구조적으로 해결. 삭제: - UsedId entity / UsedIdRepository - CounterKey.UID - IdAllocationService: allocateFor(), allocateNewUid(), findReusableUidByUbuntuUsername(), releaseId(), AllocationResult - Request.ubuntuUid 필드 및 assignUbuntuUid() 메서드 - RequestRepository.findTopByUbuntuUsernameAndUbuntuUidIsNotNullOrderByApprovedAtDesc 수정: - UserCreationRequestDTO: uid/gid 필드 제거, passwd_sha512 → passwd_base64 - AdminRequestCommandService: idAllocationService 의존성 및 uid/primary group 로직 제거 - RequestExpiryService: releaseId() 호출 제거 - Group: usedId FK 필드 제거 - AcceptInfoResponseDTO: uid 필드 제거 - SaveRequestResponseDTO: ubuntuUid 필드 제거 - IdAllocationService: allocateNewGid()만 유지, usedIdRepository 의존성 제거 DB 마이그레이션 필요: - requests.ubuntuUid 컬럼 삭제 - groups.ubuntu_gid → used_ids.id_value FK 제약 해제 - used_ids 테이블 DROP - id_counter 테이블 UID row 삭제 --- .../admin_be/domain/groups/entity/Group.java | 7 +- .../dto/request/UserCreationRequestDTO.java | 6 +- .../dto/response/AcceptInfoResponseDTO.java | 3 - .../dto/response/SaveRequestResponseDTO.java | 8 - .../domain/requests/entity/Request.java | 9 - .../repository/RequestRepository.java | 1 - .../service/AdminRequestCommandService.java | 18 +- .../service/RequestExpiryService.java | 8 - .../domain/usedIds/entity/CounterKey.java | 1 - .../domain/usedIds/entity/UsedId.java | 25 --- .../usedIds/repository/UsedIdRepository.java | 15 -- .../usedIds/service/IdAllocationService.java | 88 +-------- .../service/IdAllocationServiceTest.java | 170 +----------------- 13 files changed, 9 insertions(+), 350 deletions(-) delete mode 100644 src/main/java/DGU_AI_LAB/admin_be/domain/usedIds/entity/UsedId.java delete mode 100644 src/main/java/DGU_AI_LAB/admin_be/domain/usedIds/repository/UsedIdRepository.java diff --git a/src/main/java/DGU_AI_LAB/admin_be/domain/groups/entity/Group.java b/src/main/java/DGU_AI_LAB/admin_be/domain/groups/entity/Group.java index f528f170..1e8823db 100644 --- a/src/main/java/DGU_AI_LAB/admin_be/domain/groups/entity/Group.java +++ b/src/main/java/DGU_AI_LAB/admin_be/domain/groups/entity/Group.java @@ -1,7 +1,6 @@ package DGU_AI_LAB.admin_be.domain.groups.entity; -import DGU_AI_LAB.admin_be.domain.requests.entity.RequestGroup; // 임포트 추가 -import DGU_AI_LAB.admin_be.domain.usedIds.entity.UsedId; +import DGU_AI_LAB.admin_be.domain.requests.entity.RequestGroup; import jakarta.persistence.*; import lombok.*; @@ -26,10 +25,6 @@ public class Group { @Column(name = "ubuntu_gid", unique = true, nullable = false) private Long ubuntuGid; - @OneToOne(fetch = FetchType.LAZY) - @JoinColumn(name = "ubuntu_gid", referencedColumnName = "id_value", insertable = false, updatable = false) - private UsedId usedId; - @OneToMany(mappedBy = "group", cascade = CascadeType.ALL, orphanRemoval = true) private Set requestGroups = new HashSet<>(); diff --git a/src/main/java/DGU_AI_LAB/admin_be/domain/requests/dto/request/UserCreationRequestDTO.java b/src/main/java/DGU_AI_LAB/admin_be/domain/requests/dto/request/UserCreationRequestDTO.java index 64510e16..f8ee4f34 100644 --- a/src/main/java/DGU_AI_LAB/admin_be/domain/requests/dto/request/UserCreationRequestDTO.java +++ b/src/main/java/DGU_AI_LAB/admin_be/domain/requests/dto/request/UserCreationRequestDTO.java @@ -5,10 +5,8 @@ public record UserCreationRequestDTO( @JsonProperty("name") String username, - int uid, - int gid, - @JsonProperty("passwd_sha512") - String passwordSha512, + @JsonProperty("passwd_base64") + String passwordBase64, String gecos, @JsonProperty("primary_group_name") String primaryGroupName, diff --git a/src/main/java/DGU_AI_LAB/admin_be/domain/requests/dto/response/AcceptInfoResponseDTO.java b/src/main/java/DGU_AI_LAB/admin_be/domain/requests/dto/response/AcceptInfoResponseDTO.java index 892051ce..e33a5d33 100644 --- a/src/main/java/DGU_AI_LAB/admin_be/domain/requests/dto/response/AcceptInfoResponseDTO.java +++ b/src/main/java/DGU_AI_LAB/admin_be/domain/requests/dto/response/AcceptInfoResponseDTO.java @@ -16,8 +16,6 @@ public record AcceptInfoResponseDTO( String username, @Schema(description = "컨테이너 이미지 (이름:버전)", example = "cuda:11.8") String image, - @Schema(description = "Ubuntu UID", example = "10001") - Long uid, @Schema(description = "Ubuntu GID 목록", example = "[1005, 1006]") List gid, @Schema(description = "볼륨 크기 (GiB)", example = "20") @@ -78,7 +76,6 @@ public static AcceptInfoResponseDTO fromEntity(Request request, List nodes return AcceptInfoResponseDTO.builder() .username(request.getUbuntuUsername()) .image(image.getImageName() + ":" + image.getImageVersion()) - .uid(request.getUbuntuUid().getIdValue()) .gid( request.getRequestGroups().stream() .map(rg -> rg.getGroup().getUbuntuGid()) diff --git a/src/main/java/DGU_AI_LAB/admin_be/domain/requests/dto/response/SaveRequestResponseDTO.java b/src/main/java/DGU_AI_LAB/admin_be/domain/requests/dto/response/SaveRequestResponseDTO.java index 56d861e7..8e46905c 100644 --- a/src/main/java/DGU_AI_LAB/admin_be/domain/requests/dto/response/SaveRequestResponseDTO.java +++ b/src/main/java/DGU_AI_LAB/admin_be/domain/requests/dto/response/SaveRequestResponseDTO.java @@ -32,8 +32,6 @@ public record SaveRequestResponseDTO( String imageVersion, @Schema(description = "Ubuntu 사용자명", example = "test2014") String ubuntuUsername, - @Schema(description = "Ubuntu UID", example = "10001", nullable = true) - Long ubuntuUid, @Schema(description = "Ubuntu GID 목록", example = "[1005, 1006]") List ubuntuGids, @Schema(description = "볼륨 크기 (GiB)", example = "20") @@ -131,9 +129,6 @@ public static SaveRequestResponseDTO fromEntity(Request request) { .imageName(request.getContainerImage().getImageName()) .imageVersion(request.getContainerImage().getImageVersion()) .ubuntuUsername(request.getUbuntuUsername()) - .ubuntuUid(request.getUbuntuUid() != null - ? request.getUbuntuUid().getIdValue() - : null) .ubuntuGids( request.getRequestGroups().stream() .map(rg -> rg.getGroup().getUbuntuGid()) @@ -172,9 +167,6 @@ public static SaveRequestResponseDTO fromEntityWithPortMappings(Request request, .imageName(request.getContainerImage().getImageName()) .imageVersion(request.getContainerImage().getImageVersion()) .ubuntuUsername(request.getUbuntuUsername()) - .ubuntuUid(request.getUbuntuUid() != null - ? request.getUbuntuUid().getIdValue() - : null) .ubuntuGids( request.getRequestGroups().stream() .map(rg -> rg.getGroup().getUbuntuGid()) diff --git a/src/main/java/DGU_AI_LAB/admin_be/domain/requests/entity/Request.java b/src/main/java/DGU_AI_LAB/admin_be/domain/requests/entity/Request.java index 5e25a716..f252311a 100644 --- a/src/main/java/DGU_AI_LAB/admin_be/domain/requests/entity/Request.java +++ b/src/main/java/DGU_AI_LAB/admin_be/domain/requests/entity/Request.java @@ -3,7 +3,6 @@ import DGU_AI_LAB.admin_be.domain.containerImage.entity.ContainerImage; import DGU_AI_LAB.admin_be.domain.groups.entity.Group; import DGU_AI_LAB.admin_be.domain.resourceGroups.entity.ResourceGroup; -import DGU_AI_LAB.admin_be.domain.usedIds.entity.UsedId; import DGU_AI_LAB.admin_be.domain.users.entity.User; import DGU_AI_LAB.admin_be.error.ErrorCode; import DGU_AI_LAB.admin_be.error.exception.BusinessException; @@ -65,10 +64,6 @@ public class Request extends BaseTimeEntity { @JoinColumn(name = "user_id", nullable = false) private User user; - @ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.REMOVE) - @JoinColumn(name = "ubuntuUid", referencedColumnName = "id_value", nullable = true) - private UsedId ubuntuUid; - @Column(name = "pod_name", length = 255) private String podName; @@ -166,10 +161,6 @@ public void update(Long newVolumeSizeGiB, LocalDateTime newExpiresAt, String rea } - public void assignUbuntuUid(UsedId uid) { - this.ubuntuUid = uid; - } - public void assignPodInfo(String podName, String nodeName) { this.podName = podName; this.nodeName = nodeName; diff --git a/src/main/java/DGU_AI_LAB/admin_be/domain/requests/repository/RequestRepository.java b/src/main/java/DGU_AI_LAB/admin_be/domain/requests/repository/RequestRepository.java index 948b012a..f51cdabb 100644 --- a/src/main/java/DGU_AI_LAB/admin_be/domain/requests/repository/RequestRepository.java +++ b/src/main/java/DGU_AI_LAB/admin_be/domain/requests/repository/RequestRepository.java @@ -21,7 +21,6 @@ public interface RequestRepository extends JpaRepository { List findAllByStatus(Status status); Optional findByUbuntuUsernameAndUbuntuPassword(String username, String passwordBase64); List findByUserUserIdAndStatus(Long userId, Status status); - Optional findTopByUbuntuUsernameAndUbuntuUidIsNotNullOrderByApprovedAtDesc(String ubuntuUsername); boolean existsByUbuntuUsername(String ubuntuUsername); List findAllByUser_UserIdAndStatus(Long userId, Status status); boolean existsByUbuntuUsernameAndUser_UserId(String ubuntuUsername, Long userId); diff --git a/src/main/java/DGU_AI_LAB/admin_be/domain/requests/service/AdminRequestCommandService.java b/src/main/java/DGU_AI_LAB/admin_be/domain/requests/service/AdminRequestCommandService.java index 29d02aff..90c87243 100644 --- a/src/main/java/DGU_AI_LAB/admin_be/domain/requests/service/AdminRequestCommandService.java +++ b/src/main/java/DGU_AI_LAB/admin_be/domain/requests/service/AdminRequestCommandService.java @@ -3,8 +3,6 @@ import DGU_AI_LAB.admin_be.domain.alarm.service.AlarmService; import DGU_AI_LAB.admin_be.domain.containerImage.entity.ContainerImage; import DGU_AI_LAB.admin_be.domain.containerImage.repository.ContainerImageRepository; -import DGU_AI_LAB.admin_be.domain.groups.entity.Group; -import DGU_AI_LAB.admin_be.domain.groups.repository.GroupRepository; import DGU_AI_LAB.admin_be.domain.pod.entity.PodExternalPort; import DGU_AI_LAB.admin_be.domain.pod.repository.PodExternalPortRepository; import DGU_AI_LAB.admin_be.domain.requests.dto.request.*; @@ -17,7 +15,6 @@ import DGU_AI_LAB.admin_be.domain.requests.repository.RequestRepository; import DGU_AI_LAB.admin_be.domain.resourceGroups.entity.ResourceGroup; import DGU_AI_LAB.admin_be.domain.resourceGroups.repository.ResourceGroupRepository; -import DGU_AI_LAB.admin_be.domain.usedIds.service.IdAllocationService; import DGU_AI_LAB.admin_be.domain.users.entity.User; import DGU_AI_LAB.admin_be.domain.users.repository.UserRepository; import DGU_AI_LAB.admin_be.error.ErrorCode; @@ -52,7 +49,6 @@ public class AdminRequestCommandService { private final UserRepository userRepository; private final ContainerImageRepository containerImageRepository; private final ResourceGroupRepository resourceGroupRepository; - private final IdAllocationService idAllocationService; private final ChangeRequestRepository changeRequestRepository; private final GroupRepository groupRepository; private final PodExternalPortRepository podExternalPortRepository; @@ -100,17 +96,13 @@ public SaveRequestResponseDTO approveRequest(ApproveRequestDTO dto) { if (request.getStatus() != Status.PENDING) { throw new BusinessException(ErrorCode.INVALID_REQUEST_STATUS); } - var allocation = idAllocationService.allocateFor(request); - // 1. 사용자 생성 API 호출 UserCreationRequestDTO userCreationDto = new UserCreationRequestDTO( request.getUbuntuUsername(), - allocation.getUid().getIdValue().intValue(), - allocation.getPrimaryGroup().getUbuntuGid().intValue(), request.getUbuntuPassword(), request.getUser().getName(), - allocation.getPrimaryGroup().getGroupName(), - false // sudo 권한은 기본값으로 false를 설정 + request.getUbuntuUsername(), // primary_group_name = username (Linux 관례) + false ); try { @@ -152,12 +144,6 @@ public SaveRequestResponseDTO approveRequest(ApproveRequestDTO dto) { CreatePodResponseDTO podResponse = podService.createPod(request.getUbuntuUsername()); // 4. API 호출이 모두 성공한 후에 DB 업데이트 - request.assignUbuntuUid(allocation.getUid()); - boolean alreadyLinked = request.getRequestGroups().stream() - .anyMatch(rg -> rg.getGroup().getUbuntuGid().equals(allocation.getPrimaryGroup().getUbuntuGid())); - if (!alreadyLinked) { - request.addGroup(allocation.getPrimaryGroup()); - } ContainerImage image = containerImageRepository.findById(dto.imageId()) .orElseThrow(() -> new BusinessException(ErrorCode.RESOURCE_NOT_FOUND)); ResourceGroup rg = resourceGroupRepository.findById(dto.resourceGroupId()) diff --git a/src/main/java/DGU_AI_LAB/admin_be/domain/requests/service/RequestExpiryService.java b/src/main/java/DGU_AI_LAB/admin_be/domain/requests/service/RequestExpiryService.java index 68cc0309..ef278a29 100644 --- a/src/main/java/DGU_AI_LAB/admin_be/domain/requests/service/RequestExpiryService.java +++ b/src/main/java/DGU_AI_LAB/admin_be/domain/requests/service/RequestExpiryService.java @@ -3,8 +3,6 @@ import DGU_AI_LAB.admin_be.domain.requests.entity.Request; import DGU_AI_LAB.admin_be.domain.requests.entity.Status; import DGU_AI_LAB.admin_be.domain.requests.repository.RequestRepository; -import DGU_AI_LAB.admin_be.domain.usedIds.entity.UsedId; -import DGU_AI_LAB.admin_be.domain.usedIds.service.IdAllocationService; import DGU_AI_LAB.admin_be.domain.users.entity.User; import DGU_AI_LAB.admin_be.error.ErrorCode; import DGU_AI_LAB.admin_be.error.exception.EntityNotFoundException; @@ -22,7 +20,6 @@ public class RequestExpiryService { private final RequestRepository requestRepository; private final UbuntuAccountService ubuntuAccountService; - private final IdAllocationService idAllocationService; private final ApplicationEventPublisher eventPublisher; @Transactional @@ -36,11 +33,6 @@ public void deleteExpiredRequest(Long requestId) { String ubuntuUsername = request.getUbuntuUsername(); User user = request.getUser(); - UsedId usedId = request.getUbuntuUid(); - if (usedId != null) { - request.assignUbuntuUid(null); - idAllocationService.releaseId(usedId); - } ubuntuAccountService.deleteUbuntuAccount(ubuntuUsername); request.delete(); diff --git a/src/main/java/DGU_AI_LAB/admin_be/domain/usedIds/entity/CounterKey.java b/src/main/java/DGU_AI_LAB/admin_be/domain/usedIds/entity/CounterKey.java index e7c27bde..b1c632a0 100644 --- a/src/main/java/DGU_AI_LAB/admin_be/domain/usedIds/entity/CounterKey.java +++ b/src/main/java/DGU_AI_LAB/admin_be/domain/usedIds/entity/CounterKey.java @@ -1,6 +1,5 @@ package DGU_AI_LAB.admin_be.domain.usedIds.entity; public enum CounterKey { - UID, SHARED_GID; } diff --git a/src/main/java/DGU_AI_LAB/admin_be/domain/usedIds/entity/UsedId.java b/src/main/java/DGU_AI_LAB/admin_be/domain/usedIds/entity/UsedId.java deleted file mode 100644 index 854da6cb..00000000 --- a/src/main/java/DGU_AI_LAB/admin_be/domain/usedIds/entity/UsedId.java +++ /dev/null @@ -1,25 +0,0 @@ -package DGU_AI_LAB.admin_be.domain.usedIds.entity; - -import DGU_AI_LAB.admin_be.domain.groups.entity.Group; // Group 임포트 필수 -import jakarta.persistence.*; -import lombok.*; - -@Entity -@Table(name = "used_ids") -@Getter -@NoArgsConstructor(access = AccessLevel.PROTECTED) -@EqualsAndHashCode(of = "idValue") -public class UsedId { - - @Id - @Column(name = "id_value", nullable = false) - private Long idValue; - - @OneToOne(mappedBy = "usedId", cascade = CascadeType.ALL, orphanRemoval = true) - private Group group; - - @Builder - public UsedId(Long idValue) { - this.idValue = idValue; - } -} \ No newline at end of file diff --git a/src/main/java/DGU_AI_LAB/admin_be/domain/usedIds/repository/UsedIdRepository.java b/src/main/java/DGU_AI_LAB/admin_be/domain/usedIds/repository/UsedIdRepository.java deleted file mode 100644 index 1d5241be..00000000 --- a/src/main/java/DGU_AI_LAB/admin_be/domain/usedIds/repository/UsedIdRepository.java +++ /dev/null @@ -1,15 +0,0 @@ -package DGU_AI_LAB.admin_be.domain.usedIds.repository; - -import DGU_AI_LAB.admin_be.domain.usedIds.entity.UsedId; -import org.springframework.data.jpa.repository.JpaRepository; -import org.springframework.data.jpa.repository.Query; - -import java.util.Optional; - -public interface UsedIdRepository extends JpaRepository { - @Query("SELECT MAX(u.idValue) FROM UsedId u") - Optional findMaxIdValue(); - - @Query("SELECT MAX(u.idValue) FROM UsedId u WHERE u.idValue >= :startRange AND u.idValue <= :endRange") - Optional findMaxIdValueInRange(Long startRange, Long endRange); -} \ No newline at end of file diff --git a/src/main/java/DGU_AI_LAB/admin_be/domain/usedIds/service/IdAllocationService.java b/src/main/java/DGU_AI_LAB/admin_be/domain/usedIds/service/IdAllocationService.java index 362129d9..d0bd9d4c 100644 --- a/src/main/java/DGU_AI_LAB/admin_be/domain/usedIds/service/IdAllocationService.java +++ b/src/main/java/DGU_AI_LAB/admin_be/domain/usedIds/service/IdAllocationService.java @@ -1,74 +1,25 @@ package DGU_AI_LAB.admin_be.domain.usedIds.service; -import DGU_AI_LAB.admin_be.domain.groups.entity.Group; -import DGU_AI_LAB.admin_be.domain.groups.repository.GroupRepository; -import DGU_AI_LAB.admin_be.domain.requests.entity.Request; -import DGU_AI_LAB.admin_be.domain.requests.repository.RequestRepository; import DGU_AI_LAB.admin_be.domain.usedIds.entity.CounterKey; import DGU_AI_LAB.admin_be.domain.usedIds.entity.IdCounter; -import DGU_AI_LAB.admin_be.domain.usedIds.entity.UsedId; import DGU_AI_LAB.admin_be.domain.usedIds.repository.IdCounterRepository; -import DGU_AI_LAB.admin_be.domain.usedIds.repository.UsedIdRepository; import DGU_AI_LAB.admin_be.error.ErrorCode; import DGU_AI_LAB.admin_be.error.exception.BusinessException; -import lombok.AllArgsConstructor; -import lombok.Getter; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.springframework.dao.DataIntegrityViolationException; import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; -import java.util.Optional; - @Slf4j @Service @RequiredArgsConstructor @Transactional public class IdAllocationService { - private final UsedIdRepository usedIdRepository; private final IdCounterRepository idCounterRepository; - private final GroupRepository groupRepository; - private final RequestRepository requestRepository; - - @Getter - @AllArgsConstructor - public static class AllocationResult { - private final UsedId uid; - private final Group primaryGroup; - } - - @Transactional - public AllocationResult allocateFor(Request request) { - final String username = request.getUbuntuUsername(); - - UsedId uid = findReusableUidByUbuntuUsername(username) - .orElseGet(this::allocateNewUid); - - Group primaryGroup = groupRepository.findById(uid.getIdValue()) - .orElseGet(() -> createPrimaryGroup(username, uid.getIdValue())); - - return new AllocationResult(uid, primaryGroup); - } - - private Optional findReusableUidByUbuntuUsername(String ubuntuUsername) { - return requestRepository - .findTopByUbuntuUsernameAndUbuntuUidIsNotNullOrderByApprovedAtDesc(ubuntuUsername) - .map(Request::getUbuntuUid); - } - - private UsedId allocateNewUid() { - IdCounter counter = idCounterRepository.findByKey(CounterKey.UID) - .orElseThrow(() -> new BusinessException(ErrorCode.UID_ALLOCATION_FAILED)); - long uid = counter.allocateOne(); - idCounterRepository.saveAndFlush(counter); - return usedIdRepository.saveAndFlush(UsedId.builder().idValue(uid).build()); - } /** - * 새로운 GID를 할당하고 UsedId 테이블에 저장합니다. + * 새로운 GID를 할당합니다. * IdCounter에 비관적 락을 적용하여 동시성을 보장합니다. */ @Transactional @@ -76,44 +27,9 @@ public Long allocateNewGid() { IdCounter counter = idCounterRepository.findByKey(CounterKey.SHARED_GID) .orElseThrow(() -> new BusinessException(ErrorCode.GID_ALLOCATION_FAILED)); - long gid = counter.allocateOne(); // 범위 초과 시 NO_AVAILABLE_RESOURCES 예외 발생 + long gid = counter.allocateOne(); idCounterRepository.saveAndFlush(counter); - try { - usedIdRepository.saveAndFlush(UsedId.builder().idValue(gid).build()); - } catch (DataIntegrityViolationException e) { - throw new BusinessException(ErrorCode.GID_ALLOCATION_FAILED); - } - return gid; } - - private Group createPrimaryGroup(String username, long uidValue) { - return groupRepository.saveAndFlush( - Group.builder() - .groupName(username) - .ubuntuGid(uidValue) - .build() - ); - } - - /** - * 사용 완료된 UsedId를 DB에서 삭제하여 반환합니다. - * 스케줄러의 트랜잭션에 참여합니다. - */ - @Transactional(propagation = Propagation.MANDATORY) - public void releaseId(UsedId usedId) { - if (usedId == null) { - log.warn("반환할 UsedId가 null입니다. 작업을 건너뜁니다."); - return; - } - - try { - usedIdRepository.delete(usedId); - log.info("UsedId 반환 (삭제) 성공: {}", usedId.getIdValue()); - } catch (Exception e) { - log.error("UsedId 반환 (삭제) 실패: {}", usedId.getIdValue(), e); - throw new BusinessException("UsedId 반환 실패", ErrorCode.USED_ID_RELEASE_FAILED); - } - } } diff --git a/src/test/java/DGU_AI_LAB/admin_be/domain/usedIds/service/IdAllocationServiceTest.java b/src/test/java/DGU_AI_LAB/admin_be/domain/usedIds/service/IdAllocationServiceTest.java index 95f7614c..8c1d5c49 100644 --- a/src/test/java/DGU_AI_LAB/admin_be/domain/usedIds/service/IdAllocationServiceTest.java +++ b/src/test/java/DGU_AI_LAB/admin_be/domain/usedIds/service/IdAllocationServiceTest.java @@ -1,14 +1,8 @@ package DGU_AI_LAB.admin_be.domain.usedIds.service; -import DGU_AI_LAB.admin_be.domain.groups.entity.Group; -import DGU_AI_LAB.admin_be.domain.groups.repository.GroupRepository; -import DGU_AI_LAB.admin_be.domain.requests.entity.Request; -import DGU_AI_LAB.admin_be.domain.requests.repository.RequestRepository; import DGU_AI_LAB.admin_be.domain.usedIds.entity.CounterKey; import DGU_AI_LAB.admin_be.domain.usedIds.entity.IdCounter; -import DGU_AI_LAB.admin_be.domain.usedIds.entity.UsedId; import DGU_AI_LAB.admin_be.domain.usedIds.repository.IdCounterRepository; -import DGU_AI_LAB.admin_be.domain.usedIds.repository.UsedIdRepository; import DGU_AI_LAB.admin_be.error.ErrorCode; import DGU_AI_LAB.admin_be.error.exception.BusinessException; import org.junit.jupiter.api.DisplayName; @@ -18,41 +12,29 @@ import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; -import org.springframework.dao.DataIntegrityViolationException; import java.util.Optional; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.*; +import static org.mockito.Mockito.when; @ExtendWith(MockitoExtension.class) class IdAllocationServiceTest { - @Mock - private UsedIdRepository usedIdRepository; - - @Mock - private GroupRepository groupRepository; - - @Mock - private RequestRepository requestRepository; - @Mock private IdCounterRepository idCounterRepository; @InjectMocks private IdAllocationService idAllocationService; - // ===== allocateNewGid ===== - @Nested @DisplayName("allocateNewGid") class AllocateNewGidTest { @Test - @DisplayName("카운터가 존재하면 GID를 할당하고 UsedId를 저장한다") + @DisplayName("카운터가 존재하면 GID를 할당하고 카운터를 증가시킨다") void success() { IdCounter counter = IdCounter.builder() .key(CounterKey.SHARED_GID) @@ -65,15 +47,11 @@ void success() { .thenReturn(Optional.of(counter)); when(idCounterRepository.saveAndFlush(any(IdCounter.class))) .thenReturn(counter); - when(usedIdRepository.saveAndFlush(any(UsedId.class))) - .thenAnswer(inv -> inv.getArgument(0)); Long gid = idAllocationService.allocateNewGid(); assertThat(gid).isEqualTo(2000L); assertThat(counter.getNextValue()).isEqualTo(2001L); - verify(idCounterRepository).saveAndFlush(counter); - verify(usedIdRepository).saveAndFlush(any(UsedId.class)); } @Test @@ -107,29 +85,6 @@ void maxExceeded_throwsException() { .isEqualTo(ErrorCode.NO_AVAILABLE_RESOURCES); } - @Test - @DisplayName("UsedId 저장 시 중복 키 충돌이면 GID_ALLOCATION_FAILED 예외를 던진다") - void duplicateKey_throwsException() { - IdCounter counter = IdCounter.builder() - .key(CounterKey.SHARED_GID) - .nextValue(2000L) - .minValue(2000L) - .maxValue(65535L) - .build(); - - when(idCounterRepository.findByKey(CounterKey.SHARED_GID)) - .thenReturn(Optional.of(counter)); - when(idCounterRepository.saveAndFlush(any(IdCounter.class))) - .thenReturn(counter); - when(usedIdRepository.saveAndFlush(any(UsedId.class))) - .thenThrow(new DataIntegrityViolationException("duplicate")); - - assertThatThrownBy(() -> idAllocationService.allocateNewGid()) - .isInstanceOf(BusinessException.class) - .extracting(e -> ((BusinessException) e).getErrorCode()) - .isEqualTo(ErrorCode.GID_ALLOCATION_FAILED); - } - @Test @DisplayName("연속 할당 시 GID가 순차적으로 증가한다") void sequential_incrementsCorrectly() { @@ -144,8 +99,6 @@ void sequential_incrementsCorrectly() { .thenReturn(Optional.of(counter)); when(idCounterRepository.saveAndFlush(any(IdCounter.class))) .thenReturn(counter); - when(usedIdRepository.saveAndFlush(any(UsedId.class))) - .thenAnswer(inv -> inv.getArgument(0)); Long first = idAllocationService.allocateNewGid(); Long second = idAllocationService.allocateNewGid(); @@ -156,123 +109,4 @@ void sequential_incrementsCorrectly() { assertThat(third).isEqualTo(2002L); } } - - // ===== allocateFor ===== - - @Nested - @DisplayName("allocateFor") - class AllocateForTest { - - @Test - @DisplayName("기존 UID가 있으면 재사용하고, 기존 그룹이 있으면 재사용한다") - void reusesExistingUidAndGroup() { - UsedId existingUid = UsedId.builder().idValue(10001L).build(); - Group existingGroup = Group.builder().groupName("testuser").ubuntuGid(10001L).build(); - Request prevRequest = mock(Request.class); - Request request = mock(Request.class); - - when(request.getUbuntuUsername()).thenReturn("testuser"); - when(requestRepository.findTopByUbuntuUsernameAndUbuntuUidIsNotNullOrderByApprovedAtDesc("testuser")) - .thenReturn(Optional.of(prevRequest)); - when(prevRequest.getUbuntuUid()).thenReturn(existingUid); - when(groupRepository.findById(10001L)).thenReturn(Optional.of(existingGroup)); - - IdAllocationService.AllocationResult result = idAllocationService.allocateFor(request); - - assertThat(result.getUid()).isEqualTo(existingUid); - assertThat(result.getPrimaryGroup()).isEqualTo(existingGroup); - verify(idCounterRepository, never()).findByKey(any()); - } - - @Test - @DisplayName("기존 UID가 없으면 새 UID를 할당한다") - void allocatesNewUidWhenNoneExists() { - Request request = mock(Request.class); - when(request.getUbuntuUsername()).thenReturn("newuser"); - when(requestRepository.findTopByUbuntuUsernameAndUbuntuUidIsNotNullOrderByApprovedAtDesc("newuser")) - .thenReturn(Optional.empty()); - - IdCounter uidCounter = IdCounter.builder() - .key(CounterKey.UID) - .nextValue(10000L) - .minValue(10000L) - .maxValue(2147483647L) - .build(); - - UsedId newUid = UsedId.builder().idValue(10000L).build(); - Group newGroup = Group.builder().groupName("newuser").ubuntuGid(10000L).build(); - - when(idCounterRepository.findByKey(CounterKey.UID)) - .thenReturn(Optional.of(uidCounter)); - when(idCounterRepository.saveAndFlush(any(IdCounter.class))) - .thenReturn(uidCounter); - when(usedIdRepository.saveAndFlush(any(UsedId.class))) - .thenReturn(newUid); - when(groupRepository.findById(10000L)) - .thenReturn(Optional.empty()); - when(groupRepository.saveAndFlush(any(Group.class))) - .thenReturn(newGroup); - - IdAllocationService.AllocationResult result = idAllocationService.allocateFor(request); - - assertThat(result.getUid().getIdValue()).isEqualTo(10000L); - assertThat(result.getPrimaryGroup().getGroupName()).isEqualTo("newuser"); - verify(idCounterRepository).findByKey(CounterKey.UID); - verify(idCounterRepository).saveAndFlush(uidCounter); - } - - @Test - @DisplayName("UID 카운터가 없으면 UID_ALLOCATION_FAILED 예외를 던진다") - void noUidCounter_throwsException() { - Request request = mock(Request.class); - when(request.getUbuntuUsername()).thenReturn("newuser"); - when(requestRepository.findTopByUbuntuUsernameAndUbuntuUidIsNotNullOrderByApprovedAtDesc("newuser")) - .thenReturn(Optional.empty()); - when(idCounterRepository.findByKey(CounterKey.UID)) - .thenReturn(Optional.empty()); - - assertThatThrownBy(() -> idAllocationService.allocateFor(request)) - .isInstanceOf(BusinessException.class) - .extracting(e -> ((BusinessException) e).getErrorCode()) - .isEqualTo(ErrorCode.UID_ALLOCATION_FAILED); - } - } - - // ===== releaseId ===== - - @Nested - @DisplayName("releaseId") - class ReleaseIdTest { - - @Test - @DisplayName("UsedId를 정상적으로 삭제한다") - void success() { - UsedId usedId = UsedId.builder().idValue(10001L).build(); - - idAllocationService.releaseId(usedId); - - verify(usedIdRepository).delete(usedId); - } - - @Test - @DisplayName("null이 전달되면 삭제를 건너뛴다") - void nullInput_skips() { - idAllocationService.releaseId(null); - - verify(usedIdRepository, never()).delete(any()); - } - - @Test - @DisplayName("삭제 실패 시 USED_ID_RELEASE_FAILED 예외를 던진다") - void deleteFails_throwsException() { - UsedId usedId = UsedId.builder().idValue(10001L).build(); - doThrow(new RuntimeException("DB error")) - .when(usedIdRepository).delete(usedId); - - assertThatThrownBy(() -> idAllocationService.releaseId(usedId)) - .isInstanceOf(BusinessException.class) - .extracting(e -> ((BusinessException) e).getErrorCode()) - .isEqualTo(ErrorCode.USED_ID_RELEASE_FAILED); - } - } } From 9ff5d91ee39b8e18a196cb2a959ce43d07d86fc2 Mon Sep 17 00:00:00 2001 From: gyeome06 Date: Sat, 2 May 2026 11:30:07 +0900 Subject: [PATCH 2/3] =?UTF-8?q?[fix]=20#213=20=EC=BB=B4=ED=8C=8C=EC=9D=BC?= =?UTF-8?q?=20=EC=98=A4=EB=A5=98=20=EC=88=98=EC=A0=95=20=EB=B0=8F=20?= =?UTF-8?q?=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=BD=94=EB=93=9C=20=EC=A0=95?= =?UTF-8?q?=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - GroupService: UsedId/UsedIdRepository 의존성 제거 (used_ids 테이블 삭제에 따른 대응) - AdminRequestCommandService: Group/GroupRepository import 누락 복구 (approveModification GROUP 케이스에서 계속 사용) - 테스트 코드: UsedId/IdAllocationService 참조 제거, CounterKey.UID → SHARED_GID 교체 Co-Authored-By: Claude Sonnet 4.6 --- .../domain/groups/service/GroupService.java | 6 ----- .../service/AdminRequestCommandService.java | 2 ++ .../groups/service/GroupServiceTest.java | 9 ------- .../AdminRequestCommandServiceTest.java | 19 +------------ .../service/ConfigRequestServiceTest.java | 17 ------------ .../RequestSchedulerServiceTest.java | 27 +++++-------------- .../domain/usedIds/entity/IdCounterTest.java | 4 +-- 7 files changed, 12 insertions(+), 72 deletions(-) diff --git a/src/main/java/DGU_AI_LAB/admin_be/domain/groups/service/GroupService.java b/src/main/java/DGU_AI_LAB/admin_be/domain/groups/service/GroupService.java index 0e491725..9c5c4459 100644 --- a/src/main/java/DGU_AI_LAB/admin_be/domain/groups/service/GroupService.java +++ b/src/main/java/DGU_AI_LAB/admin_be/domain/groups/service/GroupService.java @@ -5,8 +5,6 @@ import DGU_AI_LAB.admin_be.domain.groups.entity.Group; import DGU_AI_LAB.admin_be.domain.groups.repository.GroupRepository; import DGU_AI_LAB.admin_be.domain.requests.repository.RequestRepository; -import DGU_AI_LAB.admin_be.domain.usedIds.entity.UsedId; -import DGU_AI_LAB.admin_be.domain.usedIds.repository.UsedIdRepository; import DGU_AI_LAB.admin_be.domain.usedIds.service.IdAllocationService; import DGU_AI_LAB.admin_be.error.ErrorCode; import DGU_AI_LAB.admin_be.error.exception.BusinessException; @@ -32,7 +30,6 @@ public class GroupService { private final GroupRepository groupRepository; - private final UsedIdRepository usedIdRepository; private final RequestRepository requestRepository; private final IdAllocationService idAllocationService; private final @Qualifier("configWebClient") WebClient groupCreationWebClient; @@ -141,9 +138,6 @@ public GroupResponseDTO createGroup(CreateGroupRequestDTO dto, Long userId) { } // 5. API 호출이 성공한 후에만 로컬 DB에 그룹을 저장합니다. - UsedId usedId = usedIdRepository.findById(assignedGid) - .orElseGet(() -> usedIdRepository.saveAndFlush(UsedId.builder().idValue(assignedGid).build())); - Group group = Group.builder() .groupName(dto.groupName()) .ubuntuGid(assignedGid) diff --git a/src/main/java/DGU_AI_LAB/admin_be/domain/requests/service/AdminRequestCommandService.java b/src/main/java/DGU_AI_LAB/admin_be/domain/requests/service/AdminRequestCommandService.java index 90c87243..ee56123d 100644 --- a/src/main/java/DGU_AI_LAB/admin_be/domain/requests/service/AdminRequestCommandService.java +++ b/src/main/java/DGU_AI_LAB/admin_be/domain/requests/service/AdminRequestCommandService.java @@ -3,6 +3,8 @@ import DGU_AI_LAB.admin_be.domain.alarm.service.AlarmService; import DGU_AI_LAB.admin_be.domain.containerImage.entity.ContainerImage; import DGU_AI_LAB.admin_be.domain.containerImage.repository.ContainerImageRepository; +import DGU_AI_LAB.admin_be.domain.groups.entity.Group; +import DGU_AI_LAB.admin_be.domain.groups.repository.GroupRepository; import DGU_AI_LAB.admin_be.domain.pod.entity.PodExternalPort; import DGU_AI_LAB.admin_be.domain.pod.repository.PodExternalPortRepository; import DGU_AI_LAB.admin_be.domain.requests.dto.request.*; diff --git a/src/test/java/DGU_AI_LAB/admin_be/domain/groups/service/GroupServiceTest.java b/src/test/java/DGU_AI_LAB/admin_be/domain/groups/service/GroupServiceTest.java index 788887f4..b7374abb 100644 --- a/src/test/java/DGU_AI_LAB/admin_be/domain/groups/service/GroupServiceTest.java +++ b/src/test/java/DGU_AI_LAB/admin_be/domain/groups/service/GroupServiceTest.java @@ -5,8 +5,6 @@ import DGU_AI_LAB.admin_be.domain.groups.entity.Group; import DGU_AI_LAB.admin_be.domain.groups.repository.GroupRepository; import DGU_AI_LAB.admin_be.domain.requests.repository.RequestRepository; -import DGU_AI_LAB.admin_be.domain.usedIds.entity.UsedId; -import DGU_AI_LAB.admin_be.domain.usedIds.repository.UsedIdRepository; import DGU_AI_LAB.admin_be.domain.usedIds.service.IdAllocationService; import DGU_AI_LAB.admin_be.error.exception.BusinessException; import org.junit.jupiter.api.DisplayName; @@ -21,7 +19,6 @@ import java.util.List; import java.util.Map; -import java.util.Optional; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; @@ -38,9 +35,6 @@ class GroupServiceTest { @Mock private GroupRepository groupRepository; - @Mock - private UsedIdRepository usedIdRepository; - @Mock private RequestRepository requestRepository; @@ -99,9 +93,6 @@ void createGroup_success() { doReturn(responseSpec).when(bodySpec).retrieve(); doReturn(Mono.just(Map.of("result", "ok"))).when(responseSpec).bodyToMono(Map.class); - UsedId usedId = UsedId.builder().idValue(2000L).build(); - when(usedIdRepository.findById(2000L)).thenReturn(Optional.of(usedId)); - Group savedGroup = Group.builder().groupName("developers").ubuntuGid(2000L).build(); when(groupRepository.save(any(Group.class))).thenReturn(savedGroup); diff --git a/src/test/java/DGU_AI_LAB/admin_be/domain/requests/service/AdminRequestCommandServiceTest.java b/src/test/java/DGU_AI_LAB/admin_be/domain/requests/service/AdminRequestCommandServiceTest.java index 452566e0..726f0278 100644 --- a/src/test/java/DGU_AI_LAB/admin_be/domain/requests/service/AdminRequestCommandServiceTest.java +++ b/src/test/java/DGU_AI_LAB/admin_be/domain/requests/service/AdminRequestCommandServiceTest.java @@ -15,8 +15,6 @@ import DGU_AI_LAB.admin_be.domain.requests.repository.RequestRepository; import DGU_AI_LAB.admin_be.domain.resourceGroups.entity.ResourceGroup; import DGU_AI_LAB.admin_be.domain.resourceGroups.repository.ResourceGroupRepository; -import DGU_AI_LAB.admin_be.domain.usedIds.entity.UsedId; -import DGU_AI_LAB.admin_be.domain.usedIds.service.IdAllocationService; import DGU_AI_LAB.admin_be.domain.users.entity.User; import DGU_AI_LAB.admin_be.domain.users.repository.UserRepository; import com.fasterxml.jackson.databind.ObjectMapper; @@ -51,7 +49,6 @@ class AdminRequestCommandServiceTest { @Mock private UserRepository userRepository; @Mock private ContainerImageRepository containerImageRepository; @Mock private ResourceGroupRepository resourceGroupRepository; - @Mock private IdAllocationService idAllocationService; @Mock private ChangeRequestRepository changeRequestRepository; @Mock private GroupRepository groupRepository; @Mock private PodExternalPortRepository podExternalPortRepository; @@ -81,7 +78,7 @@ void setUp() { // @RequiredArgsConstructor 생성자 필드 선언 순서대로 주입 service = new AdminRequestCommandService( alarmService, requestRepository, userRepository, containerImageRepository, - resourceGroupRepository, idAllocationService, changeRequestRepository, + resourceGroupRepository, changeRequestRepository, groupRepository, podExternalPortRepository, podService, new ObjectMapper(), mockWebClient, mockWebClient ); @@ -128,24 +125,12 @@ private Request buildMockedRequest(Long requestId) { return request; } - /** 공통 IdAllocation mock 설정 */ - private void stubIdAllocation(Request request, long uidValue) { - UsedId uid = mock(UsedId.class); - when(uid.getIdValue()).thenReturn(uidValue); - Group primaryGroup = mock(Group.class); - when(primaryGroup.getUbuntuGid()).thenReturn(uidValue); - when(primaryGroup.getGroupName()).thenReturn("testuser"); - when(idAllocationService.allocateFor(request)) - .thenReturn(new IdAllocationService.AllocationResult(uid, primaryGroup)); - } - @Test @DisplayName("승인 시 Pod 응답의 external ports가 PodExternalPortRepository에 올바르게 저장된다") void approveRequest_savesPodExternalPortsToNewTable() { // Given Long requestId = 1L; Request request = buildMockedRequest(requestId); - stubIdAllocation(request, 10000L); stubWebClientPut(); stubWebClientPost(); @@ -191,7 +176,6 @@ void approveRequest_noPodPorts_savesNoPodExternalPorts() { // Given Long requestId = 2L; Request request = buildMockedRequest(requestId); - stubIdAllocation(request, 10001L); stubWebClientPut(); stubWebClientPost(); @@ -218,7 +202,6 @@ void approveRequest_callsPodServiceCreatePodOnce() { // Given Long requestId = 3L; Request request = buildMockedRequest(requestId); - stubIdAllocation(request, 10002L); stubWebClientPut(); stubWebClientPost(); diff --git a/src/test/java/DGU_AI_LAB/admin_be/domain/requests/service/ConfigRequestServiceTest.java b/src/test/java/DGU_AI_LAB/admin_be/domain/requests/service/ConfigRequestServiceTest.java index c74981a5..67dcbff7 100644 --- a/src/test/java/DGU_AI_LAB/admin_be/domain/requests/service/ConfigRequestServiceTest.java +++ b/src/test/java/DGU_AI_LAB/admin_be/domain/requests/service/ConfigRequestServiceTest.java @@ -9,7 +9,6 @@ import DGU_AI_LAB.admin_be.domain.requests.entity.Request; import DGU_AI_LAB.admin_be.domain.requests.repository.RequestRepository; import DGU_AI_LAB.admin_be.domain.resourceGroups.entity.ResourceGroup; -import DGU_AI_LAB.admin_be.domain.usedIds.entity.UsedId; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -45,9 +44,6 @@ void getAcceptInfo_returnsAdditionalPortsFromPortRequests() { // Given String username = "testuser"; - UsedId uid = mock(UsedId.class); - when(uid.getIdValue()).thenReturn(10000L); - ContainerImage image = mock(ContainerImage.class); when(image.getImageName()).thenReturn("containerssh-guest"); when(image.getImageVersion()).thenReturn("ubuntu22.04"); @@ -59,7 +55,6 @@ void getAcceptInfo_returnsAdditionalPortsFromPortRequests() { Request request = mock(Request.class); when(request.getRequestId()).thenReturn(1L); when(request.getUbuntuUsername()).thenReturn(username); - when(request.getUbuntuUid()).thenReturn(uid); when(request.getContainerImage()).thenReturn(image); when(request.getResourceGroup()).thenReturn(rg); when(request.getRequestGroups()).thenReturn(new LinkedHashSet<>()); @@ -102,9 +97,6 @@ void getAcceptInfo_additionalPortsDoNotContainExternalPort() { // Given String username = "testuser2"; - UsedId uid = mock(UsedId.class); - when(uid.getIdValue()).thenReturn(10001L); - ContainerImage image = mock(ContainerImage.class); when(image.getImageName()).thenReturn("cuda"); when(image.getImageVersion()).thenReturn("11.8"); @@ -116,7 +108,6 @@ void getAcceptInfo_additionalPortsDoNotContainExternalPort() { Request request = mock(Request.class); when(request.getRequestId()).thenReturn(2L); when(request.getUbuntuUsername()).thenReturn(username); - when(request.getUbuntuUid()).thenReturn(uid); when(request.getContainerImage()).thenReturn(image); when(request.getResourceGroup()).thenReturn(rg); when(request.getRequestGroups()).thenReturn(new LinkedHashSet<>()); @@ -150,9 +141,6 @@ void getAcceptInfo_noAdditionalPorts_returnsEmptyList() { // Given String username = "testuser3"; - UsedId uid = mock(UsedId.class); - when(uid.getIdValue()).thenReturn(10002L); - ContainerImage image = mock(ContainerImage.class); when(image.getImageName()).thenReturn("cuda"); when(image.getImageVersion()).thenReturn("11.8"); @@ -164,7 +152,6 @@ void getAcceptInfo_noAdditionalPorts_returnsEmptyList() { Request request = mock(Request.class); when(request.getRequestId()).thenReturn(3L); when(request.getUbuntuUsername()).thenReturn(username); - when(request.getUbuntuUid()).thenReturn(uid); when(request.getContainerImage()).thenReturn(image); when(request.getResourceGroup()).thenReturn(rg); when(request.getRequestGroups()).thenReturn(new LinkedHashSet<>()); @@ -187,9 +174,6 @@ void getAcceptInfo_convertsNodeInfoToGpuNodes() { // Given String username = "testuser4"; - UsedId uid = mock(UsedId.class); - when(uid.getIdValue()).thenReturn(10003L); - ContainerImage image = mock(ContainerImage.class); when(image.getImageName()).thenReturn("cuda"); when(image.getImageVersion()).thenReturn("11.8"); @@ -201,7 +185,6 @@ void getAcceptInfo_convertsNodeInfoToGpuNodes() { Request request = mock(Request.class); when(request.getRequestId()).thenReturn(4L); when(request.getUbuntuUsername()).thenReturn(username); - when(request.getUbuntuUid()).thenReturn(uid); when(request.getContainerImage()).thenReturn(image); when(request.getResourceGroup()).thenReturn(rg); when(request.getRequestGroups()).thenReturn(new LinkedHashSet<>()); diff --git a/src/test/java/DGU_AI_LAB/admin_be/domain/scheduler/RequestSchedulerServiceTest.java b/src/test/java/DGU_AI_LAB/admin_be/domain/scheduler/RequestSchedulerServiceTest.java index 8245438e..24eeda66 100644 --- a/src/test/java/DGU_AI_LAB/admin_be/domain/scheduler/RequestSchedulerServiceTest.java +++ b/src/test/java/DGU_AI_LAB/admin_be/domain/scheduler/RequestSchedulerServiceTest.java @@ -10,8 +10,6 @@ import DGU_AI_LAB.admin_be.domain.requests.service.UbuntuAccountService; import DGU_AI_LAB.admin_be.domain.resourceGroups.entity.ResourceGroup; import DGU_AI_LAB.admin_be.domain.resourceGroups.repository.ResourceGroupRepository; -import DGU_AI_LAB.admin_be.domain.usedIds.entity.UsedId; -import DGU_AI_LAB.admin_be.domain.usedIds.repository.UsedIdRepository; import DGU_AI_LAB.admin_be.domain.users.entity.User; import DGU_AI_LAB.admin_be.domain.users.repository.UserRepository; import DGU_AI_LAB.admin_be.global.util.MessageUtils; @@ -53,7 +51,6 @@ public class RequestSchedulerServiceTest { @Autowired private RequestRepository requestRepository; @Autowired private UserRepository userRepository; - @Autowired private UsedIdRepository usedIdRepository; @Autowired private ResourceGroupRepository resourceGroupRepository; @Autowired private ContainerImageRepository containerImageRepository; @@ -72,8 +69,6 @@ void tearDown() { private void deleteTestUser() { userRepository.findByEmail("test@dgu.ac.kr").ifPresent(user -> { requestRepository.deleteAllInBatch(requestRepository.findAllByUser(user)); - List testUsedIds = usedIdRepository.findAllById(List.of(1000L, 1001L, 1002L, 1003L, 1004L)); - usedIdRepository.deleteAll(testUsedIds); userRepository.deleteById(user.getUserId()); }); } @@ -104,23 +99,17 @@ void runScheduler_IntegrationTest() { .description("Test Image") .build()); - // 2. UsedId 및 Request 생성 - UsedId uidExpired = usedIdRepository.save(UsedId.builder().idValue(1000L).build()); - UsedId uid1Day = usedIdRepository.save(UsedId.builder().idValue(1001L).build()); - UsedId uid3Day = usedIdRepository.save(UsedId.builder().idValue(1002L).build()); - UsedId uid7Day = usedIdRepository.save(UsedId.builder().idValue(1003L).build()); - UsedId uidOk = usedIdRepository.save(UsedId.builder().idValue(1004L).build()); - + // 2. Request 생성 // (1) 만료 (어제) - Request reqExpired = createTestRequest(MOCK_NOW.minusDays(1), Status.FULFILLED, uidExpired, "user-expired", testUser, testRg, testImage); + Request reqExpired = createTestRequest(MOCK_NOW.minusDays(1), Status.FULFILLED, "user-expired", testUser, testRg, testImage); // (2) 1일 전 (내일) - Request req1Day = createTestRequest(MOCK_NOW.plusDays(1).withHour(12), Status.FULFILLED, uid1Day, "user-1day", testUser, testRg, testImage); + Request req1Day = createTestRequest(MOCK_NOW.plusDays(1).withHour(12), Status.FULFILLED, "user-1day", testUser, testRg, testImage); // (3) 3일 전 - Request req3Day = createTestRequest(MOCK_NOW.plusDays(3).withHour(14), Status.FULFILLED, uid3Day, "user-3day", testUser, testRg, testImage); + Request req3Day = createTestRequest(MOCK_NOW.plusDays(3).withHour(14), Status.FULFILLED, "user-3day", testUser, testRg, testImage); // (4) 7일 전 - Request req7Day = createTestRequest(MOCK_NOW.plusDays(7).withHour(15), Status.FULFILLED, uid7Day, "user-7day", testUser, testRg, testImage); + Request req7Day = createTestRequest(MOCK_NOW.plusDays(7).withHour(15), Status.FULFILLED, "user-7day", testUser, testRg, testImage); // (5) 넉넉함 - createTestRequest(MOCK_NOW.plusDays(30), Status.FULFILLED, uidOk, "user-ok", testUser, testRg, testImage); + createTestRequest(MOCK_NOW.plusDays(30), Status.FULFILLED, "user-ok", testUser, testRg, testImage); // --- Given: 시간 고정 & 스케줄러 실행 --- @@ -201,7 +190,7 @@ private void verifyPreExpiryAlert(Request request, String dayLabel, User user) { ); } - private Request createTestRequest(LocalDateTime expiresAt, Status status, UsedId usedId, String ubuntuUsername, + private Request createTestRequest(LocalDateTime expiresAt, Status status, String ubuntuUsername, User testUser, ResourceGroup testRg, ContainerImage testImage) { Request req = Request.builder() .ubuntuUsername(ubuntuUsername) @@ -217,12 +206,10 @@ private Request createTestRequest(LocalDateTime expiresAt, Status status, UsedId if (status == Status.FULFILLED || status == Status.DELETED) { req.approve(testImage, testRg, 10L, "approved"); - req.assignUbuntuUid(usedId); } if (status == Status.DELETED) { req.delete(); - req.assignUbuntuUid(null); } return requestRepository.saveAndFlush(req); diff --git a/src/test/java/DGU_AI_LAB/admin_be/domain/usedIds/entity/IdCounterTest.java b/src/test/java/DGU_AI_LAB/admin_be/domain/usedIds/entity/IdCounterTest.java index d9aa6073..a9a88e92 100644 --- a/src/test/java/DGU_AI_LAB/admin_be/domain/usedIds/entity/IdCounterTest.java +++ b/src/test/java/DGU_AI_LAB/admin_be/domain/usedIds/entity/IdCounterTest.java @@ -13,7 +13,7 @@ class IdCounterTest { @DisplayName("allocateOne: nextValue를 반환하고 1 증가시킨다") void allocateOne_returnsCurrentAndIncrements() { IdCounter counter = IdCounter.builder() - .key(CounterKey.UID) + .key(CounterKey.SHARED_GID) .nextValue(10000L) .minValue(10000L) .maxValue(99999L) @@ -78,7 +78,7 @@ void allocateOne_atMaxBoundary_allocatesThenThrows() { @DisplayName("allocateOne: 연속 호출 시 순차적으로 증가한다") void allocateOne_sequential_incrementsCorrectly() { IdCounter counter = IdCounter.builder() - .key(CounterKey.UID) + .key(CounterKey.SHARED_GID) .nextValue(10000L) .minValue(10000L) .maxValue(99999L) From 32dd626504bea1260a98dfeadb43dc9b7eef17f1 Mon Sep 17 00:00:00 2001 From: gyeome06 Date: Sat, 2 May 2026 11:33:34 +0900 Subject: [PATCH 3/3] =?UTF-8?q?[fix]=20#213=20ContainerInfoDTO=20ubuntuUid?= =?UTF-8?q?=20=ED=95=84=EB=93=9C=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit getUbuntuUid() 메서드 삭제로 항상 null을 반환하게 된 필드 정리 Co-Authored-By: Claude Sonnet 4.6 --- .../domain/requests/dto/response/ContainerInfoDTO.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/main/java/DGU_AI_LAB/admin_be/domain/requests/dto/response/ContainerInfoDTO.java b/src/main/java/DGU_AI_LAB/admin_be/domain/requests/dto/response/ContainerInfoDTO.java index 91f2339c..a042c778 100644 --- a/src/main/java/DGU_AI_LAB/admin_be/domain/requests/dto/response/ContainerInfoDTO.java +++ b/src/main/java/DGU_AI_LAB/admin_be/domain/requests/dto/response/ContainerInfoDTO.java @@ -16,8 +16,6 @@ public record ContainerInfoDTO( String userName, @Schema(description = "Ubuntu 사용자명", example = "test2014") String ubuntuUsername, - @Schema(description = "Ubuntu UID", example = "10001") - Long ubuntuUid, @Schema(description = "Ubuntu GID 목록", example = "[1005, 1006]") List ubuntuGids, @Schema(description = "리소스 그룹 ID", example = "1") @@ -34,7 +32,6 @@ public static ContainerInfoDTO fromEntity(Request request) { .userId(request.getUser().getUserId()) .userName(request.getUser().getName()) .ubuntuUsername(request.getUbuntuUsername()) - //.ubuntuUid(request.getUbuntuUid()) .ubuntuGids( request.getRequestGroups().stream() .map(rg -> rg.getGroup().getUbuntuGid())