Skip to content

Commit 0f09078

Browse files
committed
use message broker to process server info
1 parent eff68d5 commit 0f09078

60 files changed

Lines changed: 1351 additions & 984 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@ Inventory manager for [Hetzner Robot](https://robot.your-server.de)
88
export HETZNER_IROBO_ROBOT_USERNAME="xxx"
99
export HETZNER_IROBO_ROBOT_PASSWORD="xxx"
1010
export HETZNER_IROBO_ROBOT_SSH_KEY="xxx"
11+
export HETZNER_IROBO_ZABBIX_URL="xxx"
1112
export HETZNER_IROBO_ZABBIX_USERNAME="xxx"
1213
export HETZNER_IROBO_ZABBIX_PASSWORD="xxx"
13-
export ZABBIX_API_URL="xxx"
1414
docker-compose --profile=complete up
1515
```
1616

build.gradle

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ dependencies {
2727
implementation 'org.springframework.boot:spring-boot-starter-validation'
2828
implementation 'org.springframework.cloud:spring-cloud-starter-sleuth'
2929
implementation 'org.springframework.cloud:spring-cloud-sleuth-zipkin'
30-
implementation 'org.springframework.retry:spring-retry'
30+
implementation 'io.nats:nats-spring-cloud-stream-binder:0.5.0'
3131
implementation 'ch.qos.logback:logback-access'
3232
compileOnly 'org.projectlombok:lombok'
3333
developmentOnly 'org.springframework.boot:spring-boot-devtools'
@@ -37,7 +37,6 @@ dependencies {
3737
implementation 'com.github.spotbugs:spotbugs-annotations:4.4.1'
3838
implementation 'com.google.guava:guava:31.0.1-jre'
3939
implementation 'com.google.mug:mug:5.5'
40-
implementation 'io.github.mlniang:spring-zabbix-api-client:0.1.1'
4140
implementation 'com.hierynomus:sshj:0.31.0'
4241
implementation 'org.postgresql:postgresql'
4342
runtimeOnly 'org.flywaydb:flyway-core'
@@ -53,4 +52,6 @@ dependencies {
5352
testCompileOnly 'org.projectlombok:lombok'
5453
testImplementation 'org.testcontainers:junit-jupiter'
5554
testImplementation 'org.testcontainers:cockroachdb'
55+
testImplementation group: 'org.springframework.cloud', name: 'spring-cloud-stream', classifier: 'test-binder'
56+
testRuntimeOnly 'com.h2database:h2'
5657
}

docker-compose.yaml

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
version: "3.8"
22
services:
3+
nats:
4+
image: nats:2.6.1-alpine3.14
5+
ports:
6+
- "4222:4222"
7+
- "8222:8222"
38
cockroachdb:
49
image: cockroachdb/cockroach:v21.1.9
510
command:
@@ -28,13 +33,14 @@ services:
2833
profiles: [ "complete" ]
2934
depends_on:
3035
- create-db
36+
- nats
3137
environment:
3238
- HETZNER_IROBO_ROBOT_USERNAME
3339
- HETZNER_IROBO_ROBOT_PASSWORD
3440
- HETZNER_IROBO_ROBOT_SSH_KEY
41+
- HETZNER_IROBO_ZABBIX_URL
3542
- HETZNER_IROBO_ZABBIX_USERNAME
3643
- HETZNER_IROBO_ZABBIX_PASSWORD
37-
- ZABBIX_API_URL
3844
- SPRING_PROFILES_ACTIVE=cloud
3945
deploy:
4046
restart_policy:

lombok.config

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
lombok.extern.findbugs.addSuppressFBWarnings=true
22
lombok.anyConstructor.addConstructorProperties=true
33
lombok.anyConstructor.suppressConstructorProperties=false
4+
lombok.copyableAnnotations += org.springframework.beans.factory.annotation.Qualifier

src/main/java/com/github/slamdev/hetzner/irobo/HetznerIroboApplication.java

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,13 @@
11
package com.github.slamdev.hetzner.irobo;
22

33
import com.github.slamdev.hetzner.irobo.integration.AppConfig;
4-
import net.javacrumbs.shedlock.spring.annotation.EnableSchedulerLock;
54
import org.springframework.boot.SpringApplication;
65
import org.springframework.boot.autoconfigure.SpringBootApplication;
76
import org.springframework.boot.context.properties.EnableConfigurationProperties;
87
import org.springframework.data.jdbc.repository.config.EnableJdbcAuditing;
9-
import org.springframework.retry.annotation.EnableRetry;
10-
import org.springframework.scheduling.annotation.EnableScheduling;
118

129
@SpringBootApplication
1310
@EnableJdbcAuditing
14-
@EnableScheduling
15-
@EnableRetry
16-
@EnableSchedulerLock(defaultLockAtMostFor = "1m")
1711
@EnableConfigurationProperties(AppConfig.class)
1812
public class HetznerIroboApplication {
1913

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
package com.github.slamdev.hetzner.irobo.business.boundary;
2+
3+
import com.github.slamdev.hetzner.irobo.business.control.HetznerServerUpdater;
4+
import com.github.slamdev.hetzner.irobo.business.control.UpdateHistoryRepository;
5+
import com.github.slamdev.hetzner.irobo.business.entity.UpdateHistoryModel;
6+
import com.github.slamdev.hetzner.irobo.integration.AppConfig;
7+
import lombok.RequiredArgsConstructor;
8+
import lombok.extern.slf4j.Slf4j;
9+
import net.javacrumbs.shedlock.spring.annotation.SchedulerLock;
10+
import org.springframework.scheduling.annotation.Scheduled;
11+
import org.springframework.stereotype.Component;
12+
13+
import java.time.Instant;
14+
import java.util.Optional;
15+
import java.util.concurrent.TimeUnit;
16+
17+
import static com.github.slamdev.hetzner.irobo.business.entity.UpdateHistoryModel.UpdateType.HETZNER_SERVER_LIST;
18+
19+
@Slf4j
20+
@Component
21+
@RequiredArgsConstructor
22+
public class HetznerScheduledUpdater {
23+
24+
private final HetznerServerUpdater hetznerServerUpdater;
25+
private final UpdateHistoryRepository updateHistoryRepository;
26+
private final AppConfig appConfig;
27+
28+
@Scheduled(fixedDelay = 5, timeUnit = TimeUnit.MINUTES, initialDelay = 1)
29+
@SchedulerLock(name = "hetzner-servers-list", lockAtLeastFor = "10s", lockAtMostFor = "30s")
30+
public void scheduleUpdateServersList() {
31+
Optional<UpdateHistoryModel> history = updateHistoryRepository.findFirstByUpdateTypeOrderByExecutedDateDesc(HETZNER_SERVER_LIST);
32+
boolean updateRequired = history
33+
.map(UpdateHistoryModel::getExecutedDate)
34+
.map(d -> d.plus(appConfig.getUpdateInterval().getHetznerServersList()).isBefore(Instant.now()))
35+
.orElse(true);
36+
37+
if (!updateRequired) {
38+
log.debug("last update {} is newer than {}; no action will be performed", history, appConfig.getUpdateInterval().getHetznerServersList());
39+
return;
40+
}
41+
42+
hetznerServerUpdater.updateServers();
43+
44+
UpdateHistoryModel updatedHistory = updateHistoryRepository.save(UpdateHistoryModel.builder()
45+
.updateType(HETZNER_SERVER_LIST)
46+
.executedDate(Instant.now())
47+
.build());
48+
49+
log.info("servers list updated: {}", updatedHistory);
50+
}
51+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package com.github.slamdev.hetzner.irobo.business.boundary;
2+
3+
import com.github.slamdev.hetzner.irobo.business.control.ServerFactory;
4+
import com.github.slamdev.hetzner.irobo.business.control.ServerRepository;
5+
import com.github.slamdev.hetzner.irobo.business.entity.HetznerServerMessage;
6+
import com.github.slamdev.hetzner.irobo.business.entity.ServerModel;
7+
import lombok.RequiredArgsConstructor;
8+
import lombok.extern.slf4j.Slf4j;
9+
import org.springframework.stereotype.Component;
10+
11+
import java.util.function.Consumer;
12+
13+
@Slf4j
14+
@RequiredArgsConstructor
15+
@Component("hetznerServerConsumer")
16+
public class HetznerServerConsumer implements Consumer<HetznerServerMessage> {
17+
18+
private final ServerRepository serverRepository;
19+
private final ServerFactory serverFactory;
20+
21+
@Override
22+
public void accept(HetznerServerMessage msg) {
23+
if (msg.isCancelled()) {
24+
serverRepository.deleteById(msg.getId());
25+
log.info("server {} is deleted because it is canceled", msg.getId());
26+
return;
27+
}
28+
29+
ServerModel server = serverFactory.create(msg);
30+
server = serverRepository.save(server);
31+
32+
log.info("server updated: {}", server);
33+
}
34+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package com.github.slamdev.hetzner.irobo.business.boundary;
2+
3+
import com.github.slamdev.hetzner.irobo.business.entity.HetznerServerMessage;
4+
import lombok.RequiredArgsConstructor;
5+
import org.springframework.cloud.stream.function.StreamBridge;
6+
import org.springframework.stereotype.Component;
7+
8+
@Component
9+
@RequiredArgsConstructor
10+
public class HetznerServerProducer {
11+
12+
private static final String DESTINATION = "hetznerServerProducer-out-0";
13+
private final StreamBridge streamBridge;
14+
15+
public void sendHetznerServer(HetznerServerMessage server) {
16+
streamBridge.send(DESTINATION, server);
17+
}
18+
}

src/main/java/com/github/slamdev/hetzner/irobo/business/boundary/IndexController.java

Lines changed: 10 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,9 @@
22

33
import com.fasterxml.jackson.databind.ObjectMapper;
44
import com.github.slamdev.hetzner.irobo.business.control.ServerRepository;
5-
import com.github.slamdev.hetzner.irobo.business.control.ServerSearchRepository;
65
import com.github.slamdev.hetzner.irobo.business.entity.ServerListViewModel;
76
import com.github.slamdev.hetzner.irobo.business.entity.ServerModel;
87
import com.github.slamdev.hetzner.irobo.integration.AppConfig;
9-
import com.github.slamdev.hetzner.irobo.integration.Streams;
108
import lombok.RequiredArgsConstructor;
119
import lombok.SneakyThrows;
1210
import org.springframework.data.domain.Sort;
@@ -27,18 +25,17 @@
2725
public class IndexController {
2826

2927
private final ServerRepository serverRepository;
30-
private final ServerSearchRepository serverSearchRepository;
3128
private final ObjectMapper objectMapper;
3229
private final AppConfig appConfig;
3330

3431
@GetMapping
35-
public ModelAndView indexPage(@RequestParam(defaultValue = "serverNumber") String sortField,
32+
public ModelAndView indexPage(@RequestParam(defaultValue = "id") String sortField,
3633
@RequestParam(defaultValue = "ASC") Sort.Direction sortDirection,
3734
@RequestParam(defaultValue = "") String filter) {
3835
String[] fields;
3936
switch (sortField) {
4037
case "externalIp":
41-
fields = new String[]{"serverIpV4", "serverIpV6"};
38+
fields = new String[]{"ipV4", "ipV6"};
4239
break;
4340
case "internalIp":
4441
fields = new String[]{"zabbixIp"};
@@ -51,19 +48,19 @@ public ModelAndView indexPage(@RequestParam(defaultValue = "serverNumber") Strin
5148
break;
5249
}
5350
Sort sort = Sort.by(sortDirection, fields);
54-
Iterable<ServerModel> results;
51+
List<ServerModel> results;
5552
if (filter.trim().isEmpty()) {
5653
results = serverRepository.findAll(sort);
5754
} else {
58-
List<Integer> ids = serverSearchRepository.findAllBySearchDataLike("%" + filter.toLowerCase(Locale.ROOT) + "%");
59-
results = serverRepository.findAllByServerNumberIsIn(ids, sort);
55+
String searchKeywords = "%" + filter.trim().toLowerCase(Locale.ROOT) + "%";
56+
results = serverRepository.findAllBySearchKeywordsLike(searchKeywords, sort);
6057
}
61-
List<ServerListViewModel> servers = Streams.stream(results)
58+
List<ServerListViewModel> servers = results.stream()
6259
.map(s -> ServerListViewModel.builder()
63-
.serverNumber(s.getServerNumber())
64-
.serverName(s.getServerName())
60+
.id(s.getId())
61+
.name(s.getName())
6562
.hostName(s.getZabbixHost())
66-
.externalIp(s.getServerIpV4() == null ? s.getServerIpV6() : s.getServerIpV4())
63+
.externalIp(s.getIpV4() == null ? s.getIpV6() : s.getIpV4())
6764
.internalIp(s.getZabbixIp())
6865
.product(s.getProduct())
6966
.dc(s.getDc())
@@ -83,7 +80,7 @@ private String buildZabbixUrl(ServerModel server) {
8380
}
8481

8582
private String buildHetznerUrl(ServerModel server) {
86-
return appConfig.getRobot().getWebUrl() + "/server?text=" + server.getServerNumber();
83+
return appConfig.getRobot().getWebUrl() + "/server?text=" + server.getId();
8784
}
8885

8986
@GetMapping("details")

src/main/java/com/github/slamdev/hetzner/irobo/business/control/HetznerRobotClient.java

Lines changed: 0 additions & 39 deletions
This file was deleted.

0 commit comments

Comments
 (0)