diff --git a/modules/infrastructure/log-writer/src/main/java/com/whoz_in/log_writer/common/process/TransientProcess.java b/modules/infrastructure/log-writer/src/main/java/com/whoz_in/log_writer/common/process/TransientProcess.java index 391c622b..eb6ff281 100644 --- a/modules/infrastructure/log-writer/src/main/java/com/whoz_in/log_writer/common/process/TransientProcess.java +++ b/modules/infrastructure/log-writer/src/main/java/com/whoz_in/log_writer/common/process/TransientProcess.java @@ -35,7 +35,7 @@ public TransientProcess(String command, @Nullable String sudoPassword) { writer.write(sudoPassword + System.lineSeparator()); writer.flush(); } catch (IOException e) { - throw new RuntimeException(e); + throw new RuntimeException("TransientProcess 실행 실패 -", e); } } diff --git a/modules/infrastructure/log-writer/src/main/java/com/whoz_in/log_writer/common/validation/BiValidator.java b/modules/infrastructure/log-writer/src/main/java/com/whoz_in/log_writer/common/validation/BiValidator.java new file mode 100644 index 00000000..a6e859d0 --- /dev/null +++ b/modules/infrastructure/log-writer/src/main/java/com/whoz_in/log_writer/common/validation/BiValidator.java @@ -0,0 +1,13 @@ +package com.whoz_in.log_writer.common.validation; + +@FunctionalInterface +public interface BiValidator{ + ValidationResult getValidationResult(T t, U u); + + default void validate(T target1, U target2){ + ValidationResult validationResult = getValidationResult(target1, target2); + if (validationResult.hasErrors()) { + throw new ValidationException(validationResult); + } + } +} diff --git a/modules/infrastructure/log-writer/src/main/java/com/whoz_in/log_writer/common/validation/ValidationException.java b/modules/infrastructure/log-writer/src/main/java/com/whoz_in/log_writer/common/validation/ValidationException.java new file mode 100644 index 00000000..79ce5c7b --- /dev/null +++ b/modules/infrastructure/log-writer/src/main/java/com/whoz_in/log_writer/common/validation/ValidationException.java @@ -0,0 +1,14 @@ +package com.whoz_in.log_writer.common.validation; + +import java.util.stream.Collectors; + +public class ValidationException extends RuntimeException{ + + public ValidationException(String message) { + super(message); + } + + public ValidationException(ValidationResult result) { + super(String.join(", ", result.getErrors())); + } +} diff --git a/modules/infrastructure/log-writer/src/main/java/com/whoz_in/log_writer/common/validation/ValidationResult.java b/modules/infrastructure/log-writer/src/main/java/com/whoz_in/log_writer/common/validation/ValidationResult.java new file mode 100644 index 00000000..32519a62 --- /dev/null +++ b/modules/infrastructure/log-writer/src/main/java/com/whoz_in/log_writer/common/validation/ValidationResult.java @@ -0,0 +1,38 @@ +package com.whoz_in.log_writer.common.validation; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +public class ValidationResult { + private final List errors; + + public ValidationResult() { + this.errors = new ArrayList<>(); + } + + public ValidationResult(Collection errors) { + this.errors = new ArrayList<>(errors); + } + + public void addError(String error) { + errors.add(error); + } + + public void addErrors(Collection errors) { + this.errors.addAll(errors); + } + + public boolean hasErrors() { + return !errors.isEmpty(); + } + + public List getErrors() { + return errors; + } + + @Override + public String toString() { + return String.join(", ", errors); + } +} \ No newline at end of file diff --git a/modules/infrastructure/log-writer/src/main/java/com/whoz_in/log_writer/common/validation/Validator.java b/modules/infrastructure/log-writer/src/main/java/com/whoz_in/log_writer/common/validation/Validator.java new file mode 100644 index 00000000..5c8f143a --- /dev/null +++ b/modules/infrastructure/log-writer/src/main/java/com/whoz_in/log_writer/common/validation/Validator.java @@ -0,0 +1,15 @@ +package com.whoz_in.log_writer.common.validation; + + +//검증 원인을 여러 개 담고 싶었는데, Spring Validator는 단일 객체를 대상으로 하는 필드 중심 검증이기 때문에 만듦 +@FunctionalInterface +public interface Validator { + ValidationResult getValidationResult(T t); + + default void validate(T target){ + ValidationResult validationResult = getValidationResult(target); + if (validationResult.hasErrors()) { + throw new ValidationException(validationResult); + } + } +} diff --git a/modules/infrastructure/log-writer/src/main/java/com/whoz_in/log_writer/managed/mdns/MdnsLogWriter.java b/modules/infrastructure/log-writer/src/main/java/com/whoz_in/log_writer/managed/mdns/MdnsLogWriter.java index d7d4296f..75d033fd 100644 --- a/modules/infrastructure/log-writer/src/main/java/com/whoz_in/log_writer/managed/mdns/MdnsLogWriter.java +++ b/modules/infrastructure/log-writer/src/main/java/com/whoz_in/log_writer/managed/mdns/MdnsLogWriter.java @@ -31,6 +31,7 @@ public MdnsLogWriter(ManagedLogDAO dao, MdnsLogParser parser, NetworkConfig conf config.getMdnsList().parallelStream() .forEach(managedInfo -> { this.processes.put(managedInfo, new MdnsLogProcess(managedInfo, sudoPassword)); + log.info("[managed - mdns({})] started", managedInfo.ssid()); this.wasDead.put(managedInfo, false); }); } diff --git a/modules/infrastructure/log-writer/src/main/java/com/whoz_in/log_writer/monitor/ChannelHopper.java b/modules/infrastructure/log-writer/src/main/java/com/whoz_in/log_writer/monitor/ChannelHopper.java index cf11c0ed..cd05dec0 100644 --- a/modules/infrastructure/log-writer/src/main/java/com/whoz_in/log_writer/monitor/ChannelHopper.java +++ b/modules/infrastructure/log-writer/src/main/java/com/whoz_in/log_writer/monitor/ChannelHopper.java @@ -24,6 +24,7 @@ public ChannelHopper(NetworkInterface monitor, @Value("${sudo_password}") String @Scheduled(initialDelay = 5000, fixedDelay = 1000) public void hop(){ + //hopping할 채널이 없을경우 채널 불러오기 if (!channelsToHop.iterator().hasNext()) { Set channels = new TransientProcess("nmcli -f SSID,CHAN dev wifi").resultList() .stream() @@ -34,9 +35,11 @@ public void hop(){ log.info("channels to hop : "+channels); channelsToHop.addAll(channels); } + //hop channel Integer channel = channelsToHop.iterator().next(); - channelsToHop.remove(channel); String hopCommand = "sudo -S iwconfig %s channel %d".formatted(monitor.getName(), channel); new TransientProcess(hopCommand, sudoPassword); + //hopping된 채널 삭제 + channelsToHop.remove(channel); } } diff --git a/modules/infrastructure/log-writer/src/main/java/com/whoz_in/log_writer/monitor/MonitorLogWriter.java b/modules/infrastructure/log-writer/src/main/java/com/whoz_in/log_writer/monitor/MonitorLogWriter.java index 365c77c3..1b6c1f2d 100644 --- a/modules/infrastructure/log-writer/src/main/java/com/whoz_in/log_writer/monitor/MonitorLogWriter.java +++ b/modules/infrastructure/log-writer/src/main/java/com/whoz_in/log_writer/monitor/MonitorLogWriter.java @@ -24,6 +24,7 @@ public MonitorLogWriter(MonitorLogParser parser, MonitorLogDAO repo, NetworkConf this.monitorInfo = config.getMonitorInfo(); this.sudoPassword = sudoPassword; this.process = new MonitorLogProcess(monitorInfo, sudoPassword); + log.info("[monitor] started"); } @Scheduled(initialDelay = 10000, fixedDelay = 10000) private void saveLogs(){ diff --git a/modules/infrastructure/log-writer/src/main/java/com/whoz_in/log_writer/system_validator/CommandInstalledValidator.java b/modules/infrastructure/log-writer/src/main/java/com/whoz_in/log_writer/system_validator/CommandInstalledValidator.java new file mode 100644 index 00000000..d7ab5004 --- /dev/null +++ b/modules/infrastructure/log-writer/src/main/java/com/whoz_in/log_writer/system_validator/CommandInstalledValidator.java @@ -0,0 +1,24 @@ +package com.whoz_in.log_writer.system_validator; + +import com.whoz_in.log_writer.common.validation.ValidationResult; +import com.whoz_in.log_writer.common.validation.Validator; +import com.whoz_in.log_writer.common.process.TransientProcess; +import java.util.List; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; + +@Component +@RequiredArgsConstructor +public class CommandInstalledValidator implements Validator { + + @Override + public ValidationResult getValidationResult(String command){ + ValidationResult validationResult = new ValidationResult(); + + List results = new TransientProcess("which " + command).resultList(); + if (results.isEmpty() || !results.get(0).contains("/")) { + validationResult.addError(command + "가 설치되지 않았습니다."); + } + return validationResult; + } +} diff --git a/modules/infrastructure/log-writer/src/main/java/com/whoz_in/log_writer/system_validator/NetworkInterfaceValidator.java b/modules/infrastructure/log-writer/src/main/java/com/whoz_in/log_writer/system_validator/NetworkInterfaceValidator.java new file mode 100644 index 00000000..8471efc8 --- /dev/null +++ b/modules/infrastructure/log-writer/src/main/java/com/whoz_in/log_writer/system_validator/NetworkInterfaceValidator.java @@ -0,0 +1,35 @@ +package com.whoz_in.log_writer.system_validator; + +import com.whoz_in.log_writer.common.NetworkInterface; +import com.whoz_in.log_writer.common.validation.BiValidator; +import com.whoz_in.log_writer.common.validation.ValidationResult; +import java.util.List; +import java.util.stream.Collectors; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; + +//세팅된 NetworkInterface들이 시스템에 존재하는 NetworkInterface인지 확인 +@Component +@RequiredArgsConstructor +public class NetworkInterfaceValidator implements BiValidator, List> { + + @Override + public ValidationResult getValidationResult(List system, List setting) { + ValidationResult validationResult = new ValidationResult(); + + List unmatchedNIs = setting.stream() + .filter(ni -> !system.contains(ni)) + .toList(); + + if (!unmatchedNIs.isEmpty()) { + validationResult.addError( + "설정된 네트워크 인터페이스가 시스템에 존재하지 않거나 상태가 올바르지 않습니다: \n" + + unmatchedNIs.stream() + .map(Object::toString) + .collect(Collectors.joining("\n")) + ); + } + + return validationResult; + } +} diff --git a/modules/infrastructure/log-writer/src/main/java/com/whoz_in/log_writer/SystemValidator.java b/modules/infrastructure/log-writer/src/main/java/com/whoz_in/log_writer/system_validator/SystemNetworkInterfaces.java similarity index 55% rename from modules/infrastructure/log-writer/src/main/java/com/whoz_in/log_writer/SystemValidator.java rename to modules/infrastructure/log-writer/src/main/java/com/whoz_in/log_writer/system_validator/SystemNetworkInterfaces.java index 6107f130..f9ec0de5 100644 --- a/modules/infrastructure/log-writer/src/main/java/com/whoz_in/log_writer/SystemValidator.java +++ b/modules/infrastructure/log-writer/src/main/java/com/whoz_in/log_writer/system_validator/SystemNetworkInterfaces.java @@ -1,81 +1,16 @@ -package com.whoz_in.log_writer; - +package com.whoz_in.log_writer.system_validator; import com.whoz_in.log_writer.common.NetworkInterface; import com.whoz_in.log_writer.common.process.TransientProcess; -import com.whoz_in.log_writer.config.NetworkConfig; import java.util.ArrayList; import java.util.List; -import java.util.stream.Collectors; -import lombok.extern.slf4j.Slf4j; -import org.springframework.scheduling.annotation.Scheduled; - -//서버 시작 시 시스템을 검증함 -//또한 주기적으로 시스템을 검증함 -@Slf4j -public final class SystemValidator { - private final NetworkConfig config; - public SystemValidator( - NetworkConfig config - ) { - this.config = config; - log.info("시스템 검증을 수행합니다"); - - //명령어 설치 확인 - checkCommandInstalled("tshark"); - checkCommandInstalled("arp-scan"); - checkCommandInstalled("iwconfig"); - checkCommandInstalled("nmcli"); - - //네트워크 인터페이스 확인 - List system = getSystemNetworkInterfaces(); - List setting = config.getNetworkInterfaces(); - log.info("\n시스템 네트워크 인터페이스 - \n{}", - system.stream() - .map(Object::toString) - .collect(Collectors.joining("\n"))); - log.info("\n설정된 네트워크 인터페이스 - \n{}", - setting.stream() - .map(Object::toString) - .collect(Collectors.joining("\n"))); - checkNetworkInterfaces(system, setting); - log.info("시스템 검증 완료"); - } +import org.springframework.stereotype.Component; - //정기적으로 시스템 상태를 검사합니다. - @Scheduled(fixedDelay = 30000) - private void checkRegularly(){ - try { - checkNetworkInterfaces(getSystemNetworkInterfaces(), this.config.getNetworkInterfaces()); - }catch (Exception e){ - log.error(e.getMessage()); - } - } - - private void checkCommandInstalled(String command) { - List results = new TransientProcess("which " + command).resultList(); - if (results.isEmpty() || !results.get(0).contains("/")) { - throw new IllegalStateException(command + "가 설치되지 않았습니다."); - } - } - - //세팅된 NetworkInterface들이 시스템에 존재하는 NetworkInterface인지 확인 - private void checkNetworkInterfaces(List system, List setting) { - List unmatchedNIs = setting.stream() - .filter(ni -> !system.contains(ni)) - .toList(); - - if (!unmatchedNIs.isEmpty()) { - throw new IllegalStateException( - "시스템에 존재하지 않거나 상태가 올바르지 않습니다: \n" + - unmatchedNIs.stream() - .map(Object::toString) - .collect(Collectors.joining("\n")) - ); - } - } - - private List getSystemNetworkInterfaces() { +//iwconfig의 출력을 파싱하여 네트워크 인터페이스들을 반환함 +@Component +public class SystemNetworkInterfaces { + //최신 정보를 가져온다. + public List getLatest() { List interfaces = new ArrayList<>(); List iwconfigOutput = new TransientProcess("iwconfig").resultList(); @@ -112,7 +47,6 @@ private List getSystemNetworkInterfaces() { } return interfaces; } -} /* @@ -149,4 +83,5 @@ private List getSystemNetworkInterfaces() { " Rx invalid nwid:0 Rx invalid crypt:0 Rx invalid frag:0", " Tx excessive retries:0 Invalid misc:0 Missed beacon:0" ); - */ \ No newline at end of file +*/ +} diff --git a/modules/infrastructure/log-writer/src/main/java/com/whoz_in/log_writer/system_validator/SystemValidator.java b/modules/infrastructure/log-writer/src/main/java/com/whoz_in/log_writer/system_validator/SystemValidator.java new file mode 100644 index 00000000..5bfe2364 --- /dev/null +++ b/modules/infrastructure/log-writer/src/main/java/com/whoz_in/log_writer/system_validator/SystemValidator.java @@ -0,0 +1,75 @@ +package com.whoz_in.log_writer.system_validator; + + +import com.whoz_in.log_writer.common.NetworkInterface; +import com.whoz_in.log_writer.common.validation.ValidationException; +import com.whoz_in.log_writer.common.validation.ValidationResult; +import com.whoz_in.log_writer.common.validation.Validator; +import com.whoz_in.log_writer.config.NetworkConfig; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; +import lombok.extern.slf4j.Slf4j; +import org.springframework.scheduling.annotation.Scheduled; + +//시스템의 전체적인 검증을 진행하는 클래스 +//SystemValidatorConfig에 의해 빈으로 등록됩니다 +@Slf4j +public final class SystemValidator { + private final NetworkConfig config; + private final SystemNetworkInterfaces systemNetworkInterfaces; + private final NetworkInterfaceValidator networkInterfaceValidator; + + //서버 시작 시 검증 (실패 시 예외가 발생하여 서버 시작이 실패하게 됨) + public SystemValidator( + NetworkConfig config, + SystemNetworkInterfaces systemNetworkInterfaces, + CommandInstalledValidator commandInstalledValidator, + NetworkInterfaceValidator networkInterfaceValidator) { + this.config = config; + this.systemNetworkInterfaces = systemNetworkInterfaces; + this.networkInterfaceValidator = networkInterfaceValidator; + + log.info("시스템 검증을 수행합니다"); + + //커맨드 설치 여부 검증 + List commands = List.of("tshark", "arp-scan", "iwconfig", "nmcli"); + commands.forEach(commandInstalledValidator::validate); + + //네트워크 인터페이스 정보 + List system = systemNetworkInterfaces.getLatest(); + List setting = config.getNetworkInterfaces(); + + //네트워크 인터페이스 출력 + log.info("\n시스템 네트워크 인터페이스 - \n{}\n설정된 네트워크 인터페이스 - \n{}", + system.stream() + .map(Object::toString) + .collect(Collectors.joining("\n")), + setting.stream() + .map(Object::toString) + .collect(Collectors.joining("\n"))); + + //네트워크 인터페이스 상태 검증 + networkInterfaceValidator.validate(system, setting); + + log.info("시스템 검증 완료"); + } + + //정기적으로 시스템 상태를 검사함 + //예외를 띄우지 않고 로깅만 하기 + @Scheduled(fixedDelay = 30000) + private void checkRegularly() { + log.info("시스템 검증 시작.."); + //네트워크 인터페이스 상태 검증 + ValidationResult result = networkInterfaceValidator.getValidationResult( + systemNetworkInterfaces.getLatest(), config.getNetworkInterfaces()); + //더 많은 검증하면 result에 추가하기.. + + if (result.hasErrors()) { + log.error("시스템 검증 실패 : [{}]", result); + return; + } + log.info("시스템 검증 완료"); + } + +} \ No newline at end of file diff --git a/modules/infrastructure/log-writer/src/main/java/com/whoz_in/log_writer/SystemValidatorConfig.java b/modules/infrastructure/log-writer/src/main/java/com/whoz_in/log_writer/system_validator/SystemValidatorConfig.java similarity index 52% rename from modules/infrastructure/log-writer/src/main/java/com/whoz_in/log_writer/SystemValidatorConfig.java rename to modules/infrastructure/log-writer/src/main/java/com/whoz_in/log_writer/system_validator/SystemValidatorConfig.java index 67693ec1..ea514962 100644 --- a/modules/infrastructure/log-writer/src/main/java/com/whoz_in/log_writer/SystemValidatorConfig.java +++ b/modules/infrastructure/log-writer/src/main/java/com/whoz_in/log_writer/system_validator/SystemValidatorConfig.java @@ -1,5 +1,7 @@ -package com.whoz_in.log_writer; +package com.whoz_in.log_writer.system_validator; +import com.whoz_in.log_writer.common.validation.BiValidator; +import com.whoz_in.log_writer.common.validation.ValidationResult; import com.whoz_in.log_writer.config.NetworkConfig; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; @@ -11,10 +13,20 @@ public class SystemValidatorConfig { private final String profile; private final NetworkConfig config; + private final SystemNetworkInterfaces systemNetworkInterfaces; + private final CommandInstalledValidator commandInstalledValidator; + private final NetworkInterfaceValidator networkInterfaceValidator; + public SystemValidatorConfig(@Value("${spring.profiles.active}") String profile, - NetworkConfig config) { + NetworkConfig config, + SystemNetworkInterfaces systemNetworkInterfaces, + CommandInstalledValidator commandInstalledValidator, + NetworkInterfaceValidator networkInterfaceValidator) { this.profile = profile; this.config = config; + this.systemNetworkInterfaces = systemNetworkInterfaces; + this.commandInstalledValidator = commandInstalledValidator; + this.networkInterfaceValidator = networkInterfaceValidator; } @Bean @@ -27,6 +39,6 @@ public SystemValidator systemValidator(){ log.info("리눅스가 아니거나 스프링 프로필이 prod가 아니므로 시스템 검증을 수행하지 않습니다."); return null; } - return new SystemValidator(config); + return new SystemValidator(config, systemNetworkInterfaces, commandInstalledValidator, networkInterfaceValidator); } }