Skip to content

Commit 3e8cfc7

Browse files
authored
feat(#1510): implement grouping of services (#2797)
1 parent b2095db commit 3e8cfc7

29 files changed

+1219
-1274
lines changed

spring-boot-admin-docs/src/site/asciidoc/_server-discovery.adoc

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
11
[[spring-cloud-discovery-support]]
22
== Spring Cloud Discovery ==
33

4-
The Spring Boot Admin Server can use Spring Clouds `DiscoveryClient` to discover applications. The advantage is that the clients don't have to include the `spring-boot-admin-starter-client`. You just have to add a `DiscoveryClient` implementation to your admin server - everything else is done by AutoConfiguration.
4+
The Spring Boot Admin Server can use Spring Clouds `DiscoveryClient` to discover applications.
5+
The advantage is that the clients don't have to include the `spring-boot-admin-starter-client`.
6+
You just have to add a `DiscoveryClient` implementation to your admin server - everything else is done by AutoConfiguration.
57

68
[[spring-cloud-discovery-static-config]]
79
=== Static Configuration using SimpleDiscoveryClient ===
810

9-
Spring Cloud provides a `SimpleDiscoveryClient`. It allows you to specify client applications via static configuration:
11+
Spring Cloud provides a `SimpleDiscoveryClient`.
12+
It allows you to specify client applications via static configuration:
1013

1114
[source,xml]
1215
.pom.xml
@@ -36,14 +39,20 @@ spring:
3639
----
3740

3841
=== Other DiscoveryClients ===
39-
Spring Boot Admin supports all other implementations of Spring Cloud's `DiscoveryClient` (https://docs.spring.io/spring-cloud-netflix/docs/current/reference/html/#service-discovery-eureka-clients/[Eureka], https://docs.spring.io/spring-cloud-zookeeper/docs/current/reference/html/#spring-cloud-zookeeper-discovery[Zookeeper], https://docs.spring.io/spring-cloud-consul/docs/current/reference/html/#spring-cloud-consul-discovery[Consul], https://docs.spring.io/spring-cloud-kubernetes/docs/current/reference/html/#discoveryclient-for-kubernetes[Kubernetes], ...). You need to add it to the Spring Boot Admin Server and configure it properly.
42+
43+
Spring Boot Admin supports all other implementations of Spring Cloud's `DiscoveryClient` (https://docs.spring.io/spring-cloud-netflix/docs/current/reference/html/#service-discovery-eureka-clients/[Eureka], https://docs.spring.io/spring-cloud-zookeeper/docs/current/reference/html/#spring-cloud-zookeeper-discovery[Zookeeper], https://docs.spring.io/spring-cloud-consul/docs/current/reference/html/#spring-cloud-consul-discovery[Consul], https://docs.spring.io/spring-cloud-kubernetes/docs/current/reference/html/#discoveryclient-for-kubernetes[Kubernetes], ...).
44+
You need to add it to the Spring Boot Admin Server and configure it properly.
4045
An <<getting-started#discover-clients-via-spring-cloud-discovery,example setup using Eureka>> is shown above.
4146

4247
=== Converting ServiceInstances ===
4348

44-
The information from the service registry are converted by the `ServiceInstanceConverter`. Spring Boot Admin ships with a default and Eureka converter implementation. The correct one is selected by AutoConfiguration.
49+
The information from the service registry are converted by the `ServiceInstanceConverter`.
50+
Spring Boot Admin ships with a default and Eureka converter implementation.
51+
The correct one is selected by AutoConfiguration.
4552

46-
TIP: You can modify how the information from the registry is used to register the application by using SBA Server configuration options and instance metadata. The values from the metadata takes precedence over the server config. If the plenty of options don't fit your needs you can provide your own `ServiceInstanceConverter`.
53+
TIP: You can modify how the information from the registry is used to register the application by using SBA Server configuration options and instance metadata.
54+
The values from the metadata takes precedence over the server config.
55+
If the plenty of options don't fit your needs you can provide your own `ServiceInstanceConverter`.
4756

4857
NOTE: When using Eureka, the `healthCheckUrl` known to Eureka is used for health-checking, which can be set on your client using `eureka.instance.healthCheckUrl`.
4958

@@ -75,6 +84,10 @@ user.password
7584
| health.path
7685
| The path is appended to the service URL and will be used for the health-checking. Ignored by the `EurekaServiceInstanceConverter`.
7786
| `${spring.boot.admin.discovery.converter.health-endpoint}`
87+
88+
| group
89+
| The group is used to group services in the UI by the group name instead of application name.
90+
|
7891
|===
7992

8093
.Discovery configuration options
@@ -111,6 +124,7 @@ user.password
111124
|===
112125

113126
=== CloudFoundry ===
127+
114128
If you are deploying your applications to CloudFoundry then `vcap.application.application_id` and `vcap.application.instance_index` *_must_* be added to the metadata for proper registration of applications with Spring Boot Admin Server.
115129
Here is a sample configuration for Eureka:
116130

spring-boot-admin-server-ui/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
"private": true,
44
"description": "Spring Boot Admin UI",
55
"scripts": {
6-
"build": "vite build --emptyOutDir",
6+
"build": "vite build --emptyOutDir --sourcemap",
77
"build:dev": "NODE_ENV=development vite build --emptyOutDir --sourcemap --mode development",
88
"build:watch": "NODE_ENV=development vite build --emptyOutDir --watch --mode development",
99
"dev": "vite",

spring-boot-admin-server-ui/src/main/frontend/components/font-awesome-icon.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,8 @@ import { faStopCircle as farStopCircle } from '@fortawesome/free-regular-svg-ico
2121
import { faTimesCircle as farTimesCircle } from '@fortawesome/free-regular-svg-icons/faTimesCircle';
2222
import {
2323
faAngleDoubleLeft,
24-
faCogs,
25-
faEye,
24+
faCogs, faExpand,
25+
faEye, faList,
2626
faPowerOff,
2727
faUndoAlt,
2828
} from '@fortawesome/free-solid-svg-icons';
@@ -91,6 +91,8 @@ library.add(
9191
faInfoCircle,
9292
faExclamationCircle,
9393
faHome,
94+
faList,
95+
faExpand,
9496
faMapMarkerAlt,
9597
faCheckCircle,
9698
faMinusCircle,

spring-boot-admin-server-ui/src/main/frontend/components/sba-nav/sba-nav-item.vue

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,26 @@
11
<template>
22
<a
3-
v-if="href"
4-
:href="href"
5-
:target="target"
6-
class="sba-nav-item"
7-
rel="noopener noreferrer"
3+
v-if="href"
4+
:href="href"
5+
:target="target"
6+
class="sba-nav-item"
7+
rel="noopener noreferrer"
88
>
9-
<slot />
9+
<slot/>
1010
</a>
1111
<router-link v-else-if="to" :to="to" class="sba-nav-item">
12-
<slot />
12+
<slot/>
1313
</router-link>
1414

1515
<div v-else class="sba-nav-item">
16-
<slot />
16+
<slot/>
1717
</div>
1818
</template>
1919

2020
<script lang="ts" setup>
21+
import {RouteLocationRaw} from "vue-router";
22+
import {PropType} from "vue";
23+
2124
defineProps({
2225
href: {
2326
type: String,
@@ -28,7 +31,7 @@ defineProps({
2831
default: '_blank',
2932
},
3033
to: {
31-
type: String,
34+
type: Object as PropType<RouteLocationRaw>,
3235
default: null,
3336
},
3437
});

spring-boot-admin-server-ui/src/main/frontend/components/sba-panel.vue

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,12 @@
2323
class="rounded-t flex justify-between px-4 pt-5 pb-5 border-b sm:px-6 items-center bg-white transition-all"
2424
>
2525
<h3 class="text-lg leading-6 font-medium text-gray-900">
26-
<span v-text="title" />&nbsp;
27-
<span v-if="subtitle" class="text-sm text-gray-500" v-text="subtitle" />
28-
<slot v-if="'title' in $slots" name="title" />
26+
<button @click="$emit('title-click')" class="flex items-center">
27+
<slot v-if="'prefix' in $slots" name="prefix" />
28+
<span v-text="title" />
29+
<span v-if="subtitle" class="ml-2 text-sm text-gray-500 self-end" v-text="subtitle" />
30+
<slot v-if="'title' in $slots" name="title" />
31+
</button>
2932
</h3>
3033

3134
<div>
@@ -95,7 +98,7 @@ export default {
9598
default: false,
9699
},
97100
},
98-
emits: ['close'],
101+
emits: ['close', 'title-click'],
99102
data() {
100103
return {
101104
headerTopValue: 0,

spring-boot-admin-server-ui/src/main/frontend/components/sba-status-badge.vue

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,6 @@ export default {
1212
status: {
1313
type: [HealthStatus, String, Number],
1414
required: true,
15-
validator(value) {
16-
return Object.prototype.hasOwnProperty.call(
17-
HealthStatus,
18-
value.toUpperCase()
19-
);
20-
},
2115
},
2216
},
2317
computed: {
Lines changed: 31 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,47 @@
1-
import { ref } from 'vue';
1+
import {Ref, ref, UnwrapRef} from 'vue';
22

33
import ApplicationStore from '../store.js';
4+
import Application from "@/services/application";
45

56
let applicationStore: ApplicationStore | null = null;
6-
const applications = ref([]);
7+
const applications = ref([] as Application[]);
78
const applicationsInitialized = ref(false);
89
const error = ref(null);
910

1011
export function createApplicationStore() {
11-
if (applicationStore) throw new Error('ApplicationStore already created!');
12+
if (applicationStore) throw new Error('ApplicationStore already created!');
1213

13-
applicationStore = new ApplicationStore();
14-
return applicationStore;
14+
applicationStore = new ApplicationStore();
15+
return applicationStore;
1516
}
1617

17-
export function useApplicationStore() {
18-
applicationStore.addEventListener('connected', () => {
19-
applicationsInitialized.value = true;
20-
error.value = null;
21-
});
18+
type ApplicationStoreValue = {
19+
applications: Ref<UnwrapRef<Application[]>>;
20+
applicationsInitialized: Ref<boolean>;
21+
error: Ref<any>;
22+
applicationStore: ApplicationStore;
23+
}
24+
25+
export function useApplicationStore(): ApplicationStoreValue {
26+
applicationStore.addEventListener('connected', () => {
27+
applicationsInitialized.value = true;
28+
error.value = null;
29+
});
2230

23-
applicationStore.addEventListener('changed', (newApplications) => {
24-
applicationsInitialized.value = true;
25-
applications.value = newApplications;
26-
error.value = null;
27-
});
31+
applicationStore.addEventListener('changed', (newApplications) => {
32+
applicationsInitialized.value = true;
33+
applications.value = newApplications;
34+
error.value = null;
35+
});
2836

29-
applicationStore.addEventListener('error', (errorResponse) => {
30-
applicationsInitialized.value = true;
31-
error.value = errorResponse;
32-
});
37+
applicationStore.addEventListener('error', (errorResponse) => {
38+
applicationsInitialized.value = true;
39+
error.value = errorResponse;
40+
});
3341

34-
applicationStore.addEventListener('removed', () => {
35-
applicationsInitialized.value = false;
36-
});
42+
applicationStore.addEventListener('removed', () => {
43+
applicationsInitialized.value = false;
44+
});
3745

38-
return { applications, applicationsInitialized, error, applicationStore };
46+
return {applications, applicationsInitialized, error, applicationStore};
3947
}

spring-boot-admin-server-ui/src/main/frontend/global.d.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Component, Raw, RenderFunction } from 'vue';
1+
import {Component, Raw, RenderFunction} from 'vue';
22

33
import ViewRegistry from '@/viewRegistry';
44

@@ -8,6 +8,7 @@ declare global {
88
type ApplicationStream = {
99
data: any;
1010
} & MessageEvent;
11+
1112
interface Window {
1213
SBA: SBASettings;
1314
}

spring-boot-admin-server-ui/src/main/frontend/i18n/i18n.de.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
"hours": "{count} Stunde | {count} Stunden",
3030
"instance": "Instanz",
3131
"instances": "Instanzen",
32+
"instances_tc": "{count} Instanz | {count} Instanzen",
3233
"integer": "Integer",
3334
"menu": {
3435
"open": "Menü öffnen"
@@ -42,6 +43,7 @@
4243
"suppress": "Unterdrücken",
4344
"time": "Zeit",
4445
"unsuppress": "Reaktivieren",
46+
"no_group": "Keine Gruppe",
4547
"username": "Username",
4648
"go_to_previous_page": "Gehe zur vorherigen Seite",
4749
"go_to_page_n": "Gehe zu Seite {page}",

spring-boot-admin-server-ui/src/main/frontend/i18n/i18n.en.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@
3636
"hours": "{count} hour | {count} hours",
3737
"instance": "Instance",
3838
"instances": "Instances",
39-
"instances_tc": "{n} instance | {n} instances",
39+
"instances_tc": "{count} instance | {count} instances",
4040
"integer": "Integer",
4141
"menu": {
4242
"open": "Open menu"
@@ -50,6 +50,7 @@
5050
"suppress": "Suppress",
5151
"time": "Time",
5252
"unsuppress": "Unsuppress",
53+
"no_group": "No Group",
5354
"username": "Username",
5455
"go_to_previous_page": "Go to previous page",
5556
"go_to_page_n": "Go to page {page}",

0 commit comments

Comments
 (0)