diff --git a/CHANGELOG.md b/CHANGELOG.md index 9bb86c1d681522176631e96aa83c1bfdd370f090..e4d81aedbff0bfe574964db1a86e6bd0ca6ed290 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,6 @@ # 2.0.14 * [LGR-73](https://jira.software.geant.org/browse/LGR-73) - Remove router endpoint +* [LGR-72](https://jira.software.geant.org/browse/LGR-72) - Check user is authenticated prior to executing commands on internal routers # 2.0.13 * [LGR-70](https://jira.software.geant.org/browse/LGR-70) - Pull UI out of jar diff --git a/pom.xml b/pom.xml index 5652f0fb061e960d85468c929295018d46978d7f..cc4f764a2caf4d03b3d50d4f56dbdea038d657d4 100644 --- a/pom.xml +++ b/pom.xml @@ -145,8 +145,7 @@ <line>After=syslog.target</line> <line>[Service]</line> <line>User=${artifactId}</line> - <line>ExecStart=/opt/${artifactId}/latest/bin/${artifactId}.${project.packaging} - --spring.config.location=file:/opt/${artifactId}/${version}/config/application.yml + <line>ExecStart=/opt/${artifactId}/latest/bin/${artifactId}.${project.packaging} --spring.config.location=file:/opt/${artifactId}/${version}/config/application.yml </line> <line>StandardOutput=syslog</line> <line>StandardError=syslog</line> diff --git a/src/main/java/org/geant/lgservice/LookingGlassServiceApplication.java b/src/main/java/org/geant/lgservice/LookingGlassServiceApplication.java index 0e3e273e48f9f2339af39553b651762cd9cebcf5..8ed4c25017169ceaa4cb6fbac1c77f6d41c56f67 100644 --- a/src/main/java/org/geant/lgservice/LookingGlassServiceApplication.java +++ b/src/main/java/org/geant/lgservice/LookingGlassServiceApplication.java @@ -1,14 +1,15 @@ package org.geant.lgservice; -import org.geant.lgservice.infrastructure.rest.inventoryprovider.Config; +import org.geant.lgservice.infrastructure.rest.inventoryprovider.InventoryProviderConfig; +import org.geant.lgservice.infrastructure.ssh.SSHConfig; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.Import; @SpringBootApplication @Import({ - org.geant.lgservice.infrastructure.rest.inventoryprovider.Config.class, - org.geant.lgservice.infrastructure.ssh.Config.class + InventoryProviderConfig.class, + SSHConfig.class }) public class LookingGlassServiceApplication { diff --git a/src/main/java/org/geant/lgservice/infrastructure/rest/inventoryprovider/Config.java b/src/main/java/org/geant/lgservice/infrastructure/rest/inventoryprovider/Config.java deleted file mode 100644 index 46b88e30f924457baece8e105b0b605135286c7f..0000000000000000000000000000000000000000 --- a/src/main/java/org/geant/lgservice/infrastructure/rest/inventoryprovider/Config.java +++ /dev/null @@ -1,16 +0,0 @@ -package org.geant.lgservice.infrastructure.rest.inventoryprovider; - -import org.geant.lgservice.domain.RouterRepository; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; - -@Configuration -public class Config { - - @Bean - public RouterRepository routerRepository(@Value("${inventoryprovider.baseUrl}")String baseUrl) { - return InventoryProviderRouterRepository.builder().baseUrl(baseUrl).build(); - } - -} diff --git a/src/main/java/org/geant/lgservice/infrastructure/rest/inventoryprovider/InventoryProviderConfig.java b/src/main/java/org/geant/lgservice/infrastructure/rest/inventoryprovider/InventoryProviderConfig.java new file mode 100644 index 0000000000000000000000000000000000000000..81601df36f310ae51232bf8e3f27aee2c1bf3d59 --- /dev/null +++ b/src/main/java/org/geant/lgservice/infrastructure/rest/inventoryprovider/InventoryProviderConfig.java @@ -0,0 +1,39 @@ +package org.geant.lgservice.infrastructure.rest.inventoryprovider; + +import lombok.extern.slf4j.Slf4j; +import okhttp3.Interceptor; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.Response; +import org.geant.lgservice.domain.RouterRepository; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import java.io.IOException; + +@Configuration +public class InventoryProviderConfig { + + @Bean + public RouterRepository routerRepository(@Value("${inventoryprovider.baseUrl}")String baseUrl) { + return InventoryProviderRouterRepository.builder().baseUrl(baseUrl) + .client(new OkHttpClient.Builder().addInterceptor(new OkHttpLogger()).build()).build(); + } + + @Slf4j + private static class OkHttpLogger implements Interceptor { + + @Override + public Response intercept(Chain chain) throws IOException { + + Request request = chain.request(); + log.info("Making request to {} with headers {}", request.url().toString(), request.headers()); + Response response = chain.proceed(request); + long contentLength = response.body().contentLength(); + log.info("Response recieved with status {} and body {}", response.code(), response.peekBody(contentLength >= 0 ? contentLength : Long.MAX_VALUE).string()); + return response; + } + } + +} diff --git a/src/main/java/org/geant/lgservice/infrastructure/rest/inventoryprovider/InventoryProviderRouterRepository.java b/src/main/java/org/geant/lgservice/infrastructure/rest/inventoryprovider/InventoryProviderRouterRepository.java index 7eabb49d91d965defbdb4268224a70fa74e37ba0..02003fac615c7070ee22e8bb99cf67f1915a5623 100644 --- a/src/main/java/org/geant/lgservice/infrastructure/rest/inventoryprovider/InventoryProviderRouterRepository.java +++ b/src/main/java/org/geant/lgservice/infrastructure/rest/inventoryprovider/InventoryProviderRouterRepository.java @@ -7,7 +7,9 @@ import com.google.common.collect.ImmutableMap; import lombok.Builder; import lombok.Getter; import lombok.extern.slf4j.Slf4j; +import okhttp3.Interceptor; import okhttp3.OkHttpClient; +import okhttp3.Request; import org.geant.lgservice.domain.Router; import org.geant.lgservice.domain.RouterRepository; import org.geant.lgservice.exceptions.TechnicalException; @@ -23,8 +25,11 @@ import retrofit2.http.GET; import java.io.IOException; import java.util.Collection; +import java.util.List; import java.util.Map; import java.util.Optional; +import java.util.stream.Collectors; +import java.util.stream.Stream; import static java.nio.charset.Charset.defaultCharset; import static org.geant.lgservice.domain.Router.Access.INTERNAL; @@ -63,9 +68,22 @@ public class InventoryProviderRouterRepository implements RouterRepository { @Builder public InventoryProviderRouterRepository(String baseUrl, OkHttpClient client) { + OkHttpClient.Builder builder = + (client != null ? client.newBuilder() : new OkHttpClient.Builder()); + + List<Interceptor> existingInterceptors = builder.interceptors(); + List<Interceptor> interceptors = Stream.concat( + Stream.of((Interceptor.Chain chain) -> chain.proceed(chain.request().newBuilder() + .addHeader("Accept", "application/json") + .build())), + existingInterceptors.stream()).collect(Collectors.toList()); + + existingInterceptors.removeIf(interceptors::contains); + existingInterceptors.addAll(interceptors); + this.inventoryProvider = new Retrofit.Builder() .baseUrl(baseUrl) - .client(client != null ? client : new OkHttpClient()) + .client(builder.build()) .addConverterFactory(JacksonConverterFactory.create()) .build() .create(InventoryProvider.class); diff --git a/src/main/java/org/geant/lgservice/infrastructure/ssh/ConnectionFactory.java b/src/main/java/org/geant/lgservice/infrastructure/ssh/ConnectionFactory.java index 46fa347a6f95c2d371f47333c158cf0b38a3e1ac..838cadc104ae218bcc523e36640f832a49e4b35a 100644 --- a/src/main/java/org/geant/lgservice/infrastructure/ssh/ConnectionFactory.java +++ b/src/main/java/org/geant/lgservice/infrastructure/ssh/ConnectionFactory.java @@ -30,7 +30,7 @@ public class ConnectionFactory { } catch (IOException e) { throw new TechnicalException(e); } - log.info("Connection for {} created"); + log.info("Connection for {} created", hostname); return connection; } } diff --git a/src/main/java/org/geant/lgservice/infrastructure/ssh/ConnectionManager.java b/src/main/java/org/geant/lgservice/infrastructure/ssh/ConnectionManager.java index 0f77845bb5a6a7ef01b37b5e2f26adb3f53d54f7..17f3249b21630c9213320fd606a08bf5db365fe7 100644 --- a/src/main/java/org/geant/lgservice/infrastructure/ssh/ConnectionManager.java +++ b/src/main/java/org/geant/lgservice/infrastructure/ssh/ConnectionManager.java @@ -8,6 +8,7 @@ import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import static org.geant.lgservice.infrastructure.ssh.SSHHost.ConnectionStateListener.State.DISCONNECTED; +import static org.geant.lgservice.infrastructure.ssh.SSHHost.ConnectionStateListener.State.ERROR; @Slf4j @Component @@ -35,9 +36,11 @@ public class ConnectionManager implements SSHHost.ConnectionStateListener { @Override public void stateChanged(SSHHost host, State state) { synchronized (this) { - if (state.equals(DISCONNECTED)) { + log.info("host [{}] state changed to [{}]", host, state); + if (state.equals(DISCONNECTED) || state.equals(ERROR)) { hosts.remove(host); } + log.info("currently active connections: [{}]", hosts.keySet()); } } } diff --git a/src/main/java/org/geant/lgservice/infrastructure/ssh/Config.java b/src/main/java/org/geant/lgservice/infrastructure/ssh/SSHConfig.java similarity index 96% rename from src/main/java/org/geant/lgservice/infrastructure/ssh/Config.java rename to src/main/java/org/geant/lgservice/infrastructure/ssh/SSHConfig.java index de7917939699e080eb5804697bf7296ce4318545..eb195bfd2a62db05ccab15b87e67ab81456dee63 100644 --- a/src/main/java/org/geant/lgservice/infrastructure/ssh/Config.java +++ b/src/main/java/org/geant/lgservice/infrastructure/ssh/SSHConfig.java @@ -7,7 +7,7 @@ import org.springframework.context.annotation.Configuration; import java.io.File; @Configuration -public class Config { +public class SSHConfig { @Bean public ConnectionFactory connectionFactory( diff --git a/src/main/java/org/geant/lgservice/infrastructure/ssh/SSHHost.java b/src/main/java/org/geant/lgservice/infrastructure/ssh/SSHHost.java index 8eba9473978cc53240b8b5a0b2ff08c198a9c688..98d7d29f0622cc5567c6671e31329d50f04f5099 100644 --- a/src/main/java/org/geant/lgservice/infrastructure/ssh/SSHHost.java +++ b/src/main/java/org/geant/lgservice/infrastructure/ssh/SSHHost.java @@ -12,10 +12,11 @@ import java.io.InputStream; import java.io.InputStreamReader; import java.util.Collection; import java.util.HashSet; +import java.util.Optional; import java.util.stream.Collectors; -import static ch.ethz.ssh2.ChannelCondition.EXIT_STATUS; import static org.geant.lgservice.infrastructure.ssh.SSHHost.ConnectionStateListener.State.DISCONNECTED; +import static org.geant.lgservice.infrastructure.ssh.SSHHost.ConnectionStateListener.State.ERROR; @Slf4j public class SSHHost implements ConnectionMonitor { @@ -38,25 +39,25 @@ public class SSHHost implements ConnectionMonitor { public String execute(String command) { Session session = null; try { + log.debug("opening session on connection [{}] with host [{}]", connection, hostname()); session = connection.openSession(); + log.debug("Session [{}] opened", session); log.debug("sending command [{}] to host [{}]", command, hostname()); session.execCommand(command); + log.debug("command [{}] sent to host [{}]", command, hostname()); - session.waitForCondition(EXIT_STATUS, 0); - int exitStatus = session.getExitStatus(); + int exitStatus = Optional.of(session).map(Session::getExitStatus).orElse(0); log.debug("Exit status [{}] recieved", exitStatus); - InputStream response = exitStatus == 0 ? session.getStdout() : session.getStderr(); + InputStream response = exitStatus != 0 ? session.getStderr() : session.getStdout(); try (BufferedReader reader = new BufferedReader(new InputStreamReader(response))) { return reader.lines().collect(Collectors.joining("\n")); } } catch (IOException e) { + triggerStateChangeListeners(ERROR); + connection.close(); throw new TechnicalException(e); - } finally { - if (session != null) { - session.close(); - } } } @@ -69,18 +70,23 @@ public class SSHHost implements ConnectionMonitor { return this; } + private void triggerStateChangeListeners(ConnectionStateListener.State state) { + connectionStateListeners + .forEach(connectionStateListener -> connectionStateListener.stateChanged(this, state)); + } + @Override public void connectionLost(Throwable reason) { log.warn("Host [{}] lost connection: ", connection.getHostname(), reason); - connectionStateListeners - .forEach(connectionStateListener -> connectionStateListener.stateChanged(this, DISCONNECTED)); + triggerStateChangeListeners(DISCONNECTED); } @FunctionalInterface public interface ConnectionStateListener { enum State { - DISCONNECTED + DISCONNECTED, + ERROR } void stateChanged(SSHHost host, State state); diff --git a/src/main/java/org/geant/lgservice/interfaces/rest/BusinessExceptionHandler.java b/src/main/java/org/geant/lgservice/interfaces/rest/BusinessExceptionHandler.java index a67f39942644af7bbedf38e2b830558a1d79af68..07fb9acb6696f814d69616b33ffbe63ca2f36836 100644 --- a/src/main/java/org/geant/lgservice/interfaces/rest/BusinessExceptionHandler.java +++ b/src/main/java/org/geant/lgservice/interfaces/rest/BusinessExceptionHandler.java @@ -1,5 +1,6 @@ package org.geant.lgservice.interfaces.rest; +import lombok.extern.slf4j.Slf4j; import org.geant.lgservice.exceptions.BusinessException; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; @@ -7,13 +8,13 @@ import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler; +@Slf4j @ControllerAdvice public class BusinessExceptionHandler extends ResponseEntityExceptionHandler { - - @ExceptionHandler(BusinessException.class) protected ResponseEntity<Object> handleBusinessException (BusinessException ex) { + log.info("Business error: {}", ex.getMessage()); return ResponseEntity.status(HttpStatus.valueOf(ex.getReason().name())).build(); } } diff --git a/src/main/java/org/geant/lgservice/pojos/Command.java b/src/main/java/org/geant/lgservice/pojos/Command.java index 265d84425583960b6155ec275484ea4a2cc02a3e..d6f806f2a0d077cc887b5c16070d8406d05ab566 100644 --- a/src/main/java/org/geant/lgservice/pojos/Command.java +++ b/src/main/java/org/geant/lgservice/pojos/Command.java @@ -6,11 +6,11 @@ import lombok.Setter; @NoArgsConstructor @Setter +@Getter public class Command { private String name; - @Getter private String value; private String description;