Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[REFACTOR] Validator 패키지 #85

Merged
merged 7 commits into from
Dec 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.whoz_in.log_writer.common.validation;

@FunctionalInterface
public interface BiValidator<T, U>{
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);
}
}
}
Original file line number Diff line number Diff line change
@@ -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()));
}
}
Original file line number Diff line number Diff line change
@@ -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<String> errors;

public ValidationResult() {
this.errors = new ArrayList<>();
}

public ValidationResult(Collection<String> errors) {
this.errors = new ArrayList<>(errors);
}

public void addError(String error) {
errors.add(error);
}

public void addErrors(Collection<String> errors) {
this.errors.addAll(errors);
}

public boolean hasErrors() {
return !errors.isEmpty();
}

public List<String> getErrors() {
return errors;
}

@Override
public String toString() {
return String.join(", ", errors);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.whoz_in.log_writer.common.validation;


//검증 원인을 여러 개 담고 싶었는데, Spring Validator는 단일 객체를 대상으로 하는 필드 중심 검증이기 때문에 만듦
@FunctionalInterface
public interface Validator<T> {
ValidationResult getValidationResult(T t);

default void validate(T target){
ValidationResult validationResult = getValidationResult(target);
if (validationResult.hasErrors()) {
throw new ValidationException(validationResult);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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);
});
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<Integer> channels = new TransientProcess("nmcli -f SSID,CHAN dev wifi").resultList()
.stream()
Expand All @@ -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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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(){
Expand Down
Original file line number Diff line number Diff line change
@@ -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<String> {

@Override
public ValidationResult getValidationResult(String command){
ValidationResult validationResult = new ValidationResult();

List<String> results = new TransientProcess("which " + command).resultList();
if (results.isEmpty() || !results.get(0).contains("/")) {
validationResult.addError(command + "가 설치되지 않았습니다.");
}
return validationResult;
}
}
Original file line number Diff line number Diff line change
@@ -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<NetworkInterface>, List<NetworkInterface>> {

@Override
public ValidationResult getValidationResult(List<NetworkInterface> system, List<NetworkInterface> setting) {
ValidationResult validationResult = new ValidationResult();

List<NetworkInterface> 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;
}
}
Original file line number Diff line number Diff line change
@@ -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<NetworkInterface> system = getSystemNetworkInterfaces();
List<NetworkInterface> 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<String> results = new TransientProcess("which " + command).resultList();
if (results.isEmpty() || !results.get(0).contains("/")) {
throw new IllegalStateException(command + "가 설치되지 않았습니다.");
}
}

//세팅된 NetworkInterface들이 시스템에 존재하는 NetworkInterface인지 확인
private void checkNetworkInterfaces(List<NetworkInterface> system, List<NetworkInterface> setting) {
List<NetworkInterface> 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<NetworkInterface> getSystemNetworkInterfaces() {
//iwconfig의 출력을 파싱하여 네트워크 인터페이스들을 반환함
@Component
public class SystemNetworkInterfaces {
//최신 정보를 가져온다.
public List<NetworkInterface> getLatest() {
List<NetworkInterface> interfaces = new ArrayList<>();

List<String> iwconfigOutput = new TransientProcess("iwconfig").resultList();
Expand Down Expand Up @@ -112,7 +47,6 @@ private List<NetworkInterface> getSystemNetworkInterfaces() {
}
return interfaces;
}
}


/*
Expand Down Expand Up @@ -149,4 +83,5 @@ private List<NetworkInterface> getSystemNetworkInterfaces() {
" Rx invalid nwid:0 Rx invalid crypt:0 Rx invalid frag:0",
" Tx excessive retries:0 Invalid misc:0 Missed beacon:0"
);
*/
*/
}
Original file line number Diff line number Diff line change
@@ -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<String> commands = List.of("tshark", "arp-scan", "iwconfig", "nmcli");
commands.forEach(commandInstalledValidator::validate);

//네트워크 인터페이스 정보
List<NetworkInterface> system = systemNetworkInterfaces.getLatest();
List<NetworkInterface> 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("시스템 검증 완료");
}

}
Loading
Loading