Adds Discovery Server and Discovery Client (#886)
This commit is contained in:
227
README.adoc
227
README.adoc
@@ -114,6 +114,20 @@ access from a Spring Boot application running as a pod.
|
||||
|
||||
This is something that you get for free by adding the following dependency inside your project:
|
||||
|
||||
====
|
||||
HTTP Based `DiscoveryClient`
|
||||
[source,xml]
|
||||
----
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-kubernetes-discoveryclient</artifactId>
|
||||
</dependency>
|
||||
----
|
||||
====
|
||||
|
||||
NOTE: `spring-cloud-starter-kubernetes-discoveryclient` is designed to be used with the
|
||||
<<spring-cloud-kubernetes-discoveryserver, Spring Cloud Kubernetes DiscoveryServer>>.
|
||||
|
||||
====
|
||||
Fabric8 Kubernetes Client
|
||||
[source,xml]
|
||||
@@ -1514,6 +1528,219 @@ items:
|
||||
----
|
||||
====
|
||||
|
||||
[#spring-cloud-kubernetes-discoveryserver]
|
||||
## Spring Cloud Kubernetes Discovery Server
|
||||
|
||||
The Spring Cloud Kubernetes Discovery Server provides HTTP endpoints apps can use to gather information
|
||||
about services available within a Kubernetes cluster. The Spring Cloud Kubernetes Discovery Server
|
||||
can be used by apps using the `spring-cloud-starter-kubernetes-discoveryclient` to provide data to
|
||||
the `DiscoveryClient` implementation provided by that starter.
|
||||
|
||||
### Permissions
|
||||
The Spring Cloud Discovery server uses
|
||||
the Kubernetes API server to get data about Service and Endpoint resrouces so it needs list, watch, and
|
||||
get permissions to use those endpoints. See the below sample Kubernetes deployment YAML for an
|
||||
examlpe of how to configure the Service Account on Kubernetes.
|
||||
|
||||
|
||||
### Endpoints
|
||||
There are three endpoints exposed by the server.
|
||||
|
||||
#### `/apps`
|
||||
|
||||
A `GET` request sent to `/apps` will return a JSON array of available services. Each item contains
|
||||
the name of the Kubernetes service and service instance information. Below is a sample response.
|
||||
|
||||
====
|
||||
[source,json]
|
||||
----
|
||||
[
|
||||
{
|
||||
"name":"spring-cloud-kubernetes-discoveryserver",
|
||||
"serviceInstances":[
|
||||
{
|
||||
"instanceId":"836a2f25-daee-4af2-a1be-aab9ce2b938f",
|
||||
"serviceId":"spring-cloud-kubernetes-discoveryserver",
|
||||
"host":"10.244.1.6",
|
||||
"port":8761,
|
||||
"uri":"http://10.244.1.6:8761",
|
||||
"secure":false,
|
||||
"metadata":{
|
||||
"app":"spring-cloud-kubernetes-discoveryserver",
|
||||
"kubectl.kubernetes.io/last-applied-configuration":"{\"apiVersion\":\"v1\",\"kind\":\"Service\",\"metadata\":{\"annotations\":{},\"labels\":{\"app\":\"spring-cloud-kubernetes-discoveryserver\"},\"name\":\"spring-cloud-kubernetes-discoveryserver\",\"namespace\":\"default\"},\"spec\":{\"ports\":[{\"name\":\"http\",\"port\":80,\"targetPort\":8761}],\"selector\":{\"app\":\"spring-cloud-kubernetes-discoveryserver\"},\"type\":\"ClusterIP\"}}\n",
|
||||
"http":"8761"
|
||||
},
|
||||
"namespace":"default",
|
||||
"scheme":"http"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name":"kubernetes",
|
||||
"serviceInstances":[
|
||||
{
|
||||
"instanceId":"1234",
|
||||
"serviceId":"kubernetes",
|
||||
"host":"172.18.0.3",
|
||||
"port":6443,
|
||||
"uri":"http://172.18.0.3:6443",
|
||||
"secure":false,
|
||||
"metadata":{
|
||||
"provider":"kubernetes",
|
||||
"component":"apiserver",
|
||||
"https":"6443"
|
||||
},
|
||||
"namespace":"default",
|
||||
"scheme":"http"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
----
|
||||
====
|
||||
|
||||
#### `/app/{name}`
|
||||
|
||||
A `GET` request to `/app/{name}` can be used to get instance data for all instances of a given
|
||||
service. Below is a sample response when a `GET` request is made to `/app/kubernetes`.
|
||||
|
||||
====
|
||||
[source,json]
|
||||
----
|
||||
[
|
||||
{
|
||||
"instanceId":"1234",
|
||||
"serviceId":"kubernetes",
|
||||
"host":"172.18.0.3",
|
||||
"port":6443,
|
||||
"uri":"http://172.18.0.3:6443",
|
||||
"secure":false,
|
||||
"metadata":{
|
||||
"provider":"kubernetes",
|
||||
"component":"apiserver",
|
||||
"https":"6443"
|
||||
},
|
||||
"namespace":"default",
|
||||
"scheme":"http"
|
||||
}
|
||||
]
|
||||
----
|
||||
====
|
||||
|
||||
#### `/app/{name}/{instanceid}`
|
||||
|
||||
A `GET` request made to `/app/{name}/{instanceid}` will return the instance data for a specific
|
||||
instance of a given service. Below is a sample response when a `GET` request is made to `/app/kubernetes/1234`.
|
||||
|
||||
====
|
||||
[source,json]
|
||||
----
|
||||
{
|
||||
"instanceId":"1234",
|
||||
"serviceId":"kubernetes",
|
||||
"host":"172.18.0.3",
|
||||
"port":6443,
|
||||
"uri":"http://172.18.0.3:6443",
|
||||
"secure":false,
|
||||
"metadata":{
|
||||
"provider":"kubernetes",
|
||||
"component":"apiserver",
|
||||
"https":"6443"
|
||||
},
|
||||
"namespace":"default",
|
||||
"scheme":"http"
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
### Deployment YAML
|
||||
|
||||
An image of the Spring Cloud Discovery Server is hosted on Docker Hub.
|
||||
|
||||
Below is a sample deployment YAML you can use to deploy the Kubernetes Configuration Watcher to Kubernetes.
|
||||
|
||||
====
|
||||
[source,yaml]
|
||||
----
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: List
|
||||
items:
|
||||
- apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
labels:
|
||||
app: spring-cloud-kubernetes-discoveryserver
|
||||
name: spring-cloud-kubernetes-discoveryserver
|
||||
spec:
|
||||
ports:
|
||||
- name: http
|
||||
port: 80
|
||||
targetPort: 8761
|
||||
selector:
|
||||
app: spring-cloud-kubernetes-discoveryserver
|
||||
type: ClusterIP
|
||||
- apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
labels:
|
||||
app: spring-cloud-kubernetes-discoveryserver
|
||||
name: spring-cloud-kubernetes-discoveryserver
|
||||
- apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: RoleBinding
|
||||
metadata:
|
||||
labels:
|
||||
app: spring-cloud-kubernetes-discoveryserver
|
||||
name: spring-cloud-kubernetes-discoveryserver:view
|
||||
roleRef:
|
||||
kind: Role
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
name: namespace-reader
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: spring-cloud-kubernetes-discoveryserver
|
||||
- apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: Role
|
||||
metadata:
|
||||
namespace: default
|
||||
name: namespace-reader
|
||||
rules:
|
||||
- apiGroups: ["", "extensions", "apps"]
|
||||
resources: ["services", "endpoints"]
|
||||
verbs: ["get", "list", "watch"]
|
||||
- apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: spring-cloud-kubernetes-discoveryserver-deployment
|
||||
spec:
|
||||
selector:
|
||||
matchLabels:
|
||||
app: spring-cloud-kubernetes-discoveryserver
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: spring-cloud-kubernetes-discoveryserver
|
||||
spec:
|
||||
serviceAccount: spring-cloud-kubernetes-discoveryserver
|
||||
containers:
|
||||
- name: spring-cloud-kubernetes-discoveryserver
|
||||
image: springcloud/spring-cloud-kubernetes-discoveryserver:2.1.0-SNAPSHOT
|
||||
imagePullPolicy: IfNotPresent
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
port: 8761
|
||||
path: /actuator/health/readiness
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
port: 8761
|
||||
path: /actuator/health/liveness
|
||||
ports:
|
||||
- containerPort: 8761
|
||||
|
||||
|
||||
----
|
||||
====
|
||||
|
||||
== Examples
|
||||
|
||||
Spring Cloud Kubernetes tries to make it transparent for your applications to consume Kubernetes Native Services by
|
||||
|
||||
@@ -8,6 +8,20 @@ access from a Spring Boot application running as a pod.
|
||||
|
||||
This is something that you get for free by adding the following dependency inside your project:
|
||||
|
||||
====
|
||||
HTTP Based `DiscoveryClient`
|
||||
[source,xml]
|
||||
----
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-kubernetes-discoveryclient</artifactId>
|
||||
</dependency>
|
||||
----
|
||||
====
|
||||
|
||||
NOTE: `spring-cloud-starter-kubernetes-discoveryclient` is designed to be used with the
|
||||
<<spring-cloud-kubernetes-discoveryserver, Spring Cloud Kubernetes DiscoveryServer>>.
|
||||
|
||||
====
|
||||
Fabric8 Kubernetes Client
|
||||
[source,xml]
|
||||
|
||||
@@ -0,0 +1,212 @@
|
||||
[#spring-cloud-kubernetes-discoveryserver]
|
||||
## Spring Cloud Kubernetes Discovery Server
|
||||
|
||||
The Spring Cloud Kubernetes Discovery Server provides HTTP endpoints apps can use to gather information
|
||||
about services available within a Kubernetes cluster. The Spring Cloud Kubernetes Discovery Server
|
||||
can be used by apps using the `spring-cloud-starter-kubernetes-discoveryclient` to provide data to
|
||||
the `DiscoveryClient` implementation provided by that starter.
|
||||
|
||||
### Permissions
|
||||
The Spring Cloud Discovery server uses
|
||||
the Kubernetes API server to get data about Service and Endpoint resrouces so it needs list, watch, and
|
||||
get permissions to use those endpoints. See the below sample Kubernetes deployment YAML for an
|
||||
examlpe of how to configure the Service Account on Kubernetes.
|
||||
|
||||
|
||||
### Endpoints
|
||||
There are three endpoints exposed by the server.
|
||||
|
||||
#### `/apps`
|
||||
|
||||
A `GET` request sent to `/apps` will return a JSON array of available services. Each item contains
|
||||
the name of the Kubernetes service and service instance information. Below is a sample response.
|
||||
|
||||
====
|
||||
[source,json]
|
||||
----
|
||||
[
|
||||
{
|
||||
"name":"spring-cloud-kubernetes-discoveryserver",
|
||||
"serviceInstances":[
|
||||
{
|
||||
"instanceId":"836a2f25-daee-4af2-a1be-aab9ce2b938f",
|
||||
"serviceId":"spring-cloud-kubernetes-discoveryserver",
|
||||
"host":"10.244.1.6",
|
||||
"port":8761,
|
||||
"uri":"http://10.244.1.6:8761",
|
||||
"secure":false,
|
||||
"metadata":{
|
||||
"app":"spring-cloud-kubernetes-discoveryserver",
|
||||
"kubectl.kubernetes.io/last-applied-configuration":"{\"apiVersion\":\"v1\",\"kind\":\"Service\",\"metadata\":{\"annotations\":{},\"labels\":{\"app\":\"spring-cloud-kubernetes-discoveryserver\"},\"name\":\"spring-cloud-kubernetes-discoveryserver\",\"namespace\":\"default\"},\"spec\":{\"ports\":[{\"name\":\"http\",\"port\":80,\"targetPort\":8761}],\"selector\":{\"app\":\"spring-cloud-kubernetes-discoveryserver\"},\"type\":\"ClusterIP\"}}\n",
|
||||
"http":"8761"
|
||||
},
|
||||
"namespace":"default",
|
||||
"scheme":"http"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name":"kubernetes",
|
||||
"serviceInstances":[
|
||||
{
|
||||
"instanceId":"1234",
|
||||
"serviceId":"kubernetes",
|
||||
"host":"172.18.0.3",
|
||||
"port":6443,
|
||||
"uri":"http://172.18.0.3:6443",
|
||||
"secure":false,
|
||||
"metadata":{
|
||||
"provider":"kubernetes",
|
||||
"component":"apiserver",
|
||||
"https":"6443"
|
||||
},
|
||||
"namespace":"default",
|
||||
"scheme":"http"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
----
|
||||
====
|
||||
|
||||
#### `/app/{name}`
|
||||
|
||||
A `GET` request to `/app/{name}` can be used to get instance data for all instances of a given
|
||||
service. Below is a sample response when a `GET` request is made to `/app/kubernetes`.
|
||||
|
||||
====
|
||||
[source,json]
|
||||
----
|
||||
[
|
||||
{
|
||||
"instanceId":"1234",
|
||||
"serviceId":"kubernetes",
|
||||
"host":"172.18.0.3",
|
||||
"port":6443,
|
||||
"uri":"http://172.18.0.3:6443",
|
||||
"secure":false,
|
||||
"metadata":{
|
||||
"provider":"kubernetes",
|
||||
"component":"apiserver",
|
||||
"https":"6443"
|
||||
},
|
||||
"namespace":"default",
|
||||
"scheme":"http"
|
||||
}
|
||||
]
|
||||
----
|
||||
====
|
||||
|
||||
#### `/app/{name}/{instanceid}`
|
||||
|
||||
A `GET` request made to `/app/{name}/{instanceid}` will return the instance data for a specific
|
||||
instance of a given service. Below is a sample response when a `GET` request is made to `/app/kubernetes/1234`.
|
||||
|
||||
====
|
||||
[source,json]
|
||||
----
|
||||
{
|
||||
"instanceId":"1234",
|
||||
"serviceId":"kubernetes",
|
||||
"host":"172.18.0.3",
|
||||
"port":6443,
|
||||
"uri":"http://172.18.0.3:6443",
|
||||
"secure":false,
|
||||
"metadata":{
|
||||
"provider":"kubernetes",
|
||||
"component":"apiserver",
|
||||
"https":"6443"
|
||||
},
|
||||
"namespace":"default",
|
||||
"scheme":"http"
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
### Deployment YAML
|
||||
|
||||
An image of the Spring Cloud Discovery Server is hosted on Docker Hub.
|
||||
|
||||
Below is a sample deployment YAML you can use to deploy the Kubernetes Configuration Watcher to Kubernetes.
|
||||
|
||||
====
|
||||
[source,yaml]
|
||||
----
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: List
|
||||
items:
|
||||
- apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
labels:
|
||||
app: spring-cloud-kubernetes-discoveryserver
|
||||
name: spring-cloud-kubernetes-discoveryserver
|
||||
spec:
|
||||
ports:
|
||||
- name: http
|
||||
port: 80
|
||||
targetPort: 8761
|
||||
selector:
|
||||
app: spring-cloud-kubernetes-discoveryserver
|
||||
type: ClusterIP
|
||||
- apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
labels:
|
||||
app: spring-cloud-kubernetes-discoveryserver
|
||||
name: spring-cloud-kubernetes-discoveryserver
|
||||
- apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: RoleBinding
|
||||
metadata:
|
||||
labels:
|
||||
app: spring-cloud-kubernetes-discoveryserver
|
||||
name: spring-cloud-kubernetes-discoveryserver:view
|
||||
roleRef:
|
||||
kind: Role
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
name: namespace-reader
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: spring-cloud-kubernetes-discoveryserver
|
||||
- apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: Role
|
||||
metadata:
|
||||
namespace: default
|
||||
name: namespace-reader
|
||||
rules:
|
||||
- apiGroups: ["", "extensions", "apps"]
|
||||
resources: ["services", "endpoints"]
|
||||
verbs: ["get", "list", "watch"]
|
||||
- apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: spring-cloud-kubernetes-discoveryserver-deployment
|
||||
spec:
|
||||
selector:
|
||||
matchLabels:
|
||||
app: spring-cloud-kubernetes-discoveryserver
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: spring-cloud-kubernetes-discoveryserver
|
||||
spec:
|
||||
serviceAccount: spring-cloud-kubernetes-discoveryserver
|
||||
containers:
|
||||
- name: spring-cloud-kubernetes-discoveryserver
|
||||
image: springcloud/spring-cloud-kubernetes-discoveryserver:2.1.0-SNAPSHOT
|
||||
imagePullPolicy: IfNotPresent
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
port: 8761
|
||||
path: /actuator/health/readiness
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
port: 8761
|
||||
path: /actuator/health/liveness
|
||||
ports:
|
||||
- containerPort: 8761
|
||||
|
||||
|
||||
----
|
||||
====
|
||||
@@ -33,6 +33,8 @@ include::spring-cloud-kubernetes-configuration-watcher.adoc[]
|
||||
|
||||
include::spring-cloud-kubernetes-configserver.adoc[]
|
||||
|
||||
include::spring-cloud-kubernetes-discoveryserver.adoc[]
|
||||
|
||||
include::examples.adoc[]
|
||||
|
||||
include::other-resources.adoc[]
|
||||
|
||||
2
pom.xml
2
pom.xml
@@ -110,6 +110,8 @@
|
||||
<module>docs</module>
|
||||
<module>spring-cloud-kubernetes-fabric8-loadbalancer</module>
|
||||
<module>spring-cloud-starter-kubernetes-fabric8-loadbalancer</module>
|
||||
<module>spring-cloud-kubernetes-discovery</module>
|
||||
<module>spring-cloud-starter-kubernetes-discoveryclient</module>
|
||||
</modules>
|
||||
|
||||
<dependencyManagement>
|
||||
|
||||
@@ -3,4 +3,5 @@ set -e
|
||||
|
||||
./mvnw deploy -DskipTests -B -Pfast,deploy ${@}
|
||||
./mvnw dockerfile:push -pl :spring-cloud-kubernetes-configuration-watcher -Pdockerpush ${@}
|
||||
./mvnw dockerfile:push -pl :spring-cloud-kubernetes-discoveryserver -Pdockerpush ${@}
|
||||
./mvnw dockerfile:push -pl :spring-cloud-kubernetes-configserver -Pdockerpush ${@}
|
||||
|
||||
@@ -163,7 +163,8 @@ public class KubernetesInformerDiscoveryClient implements DiscoveryClient, Initi
|
||||
return addresses.stream()
|
||||
.map(addr -> new KubernetesServiceInstance(
|
||||
addr.getTargetRef() != null ? addr.getTargetRef().getUid() : "", serviceId,
|
||||
addr.getIp(), port, metadata, false));
|
||||
addr.getIp(), port, metadata, false, service.getMetadata().getNamespace(),
|
||||
service.getMetadata().getClusterName()));
|
||||
}).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
|
||||
@@ -151,8 +151,8 @@ public class KubernetesInformerDiscoveryClientTests {
|
||||
sharedInformerFactory, serviceLister, endpointsLister, null, null, kubernetesDiscoveryProperties);
|
||||
|
||||
assertThat(discoveryClient.getInstances("test-svc-1").toArray()).isEmpty();
|
||||
assertThat(discoveryClient.getInstances("test-svc-3").toArray())
|
||||
.containsOnly(new KubernetesServiceInstance("", "test-svc-3", "2.2.2.2", 8080, new HashMap<>(), false));
|
||||
assertThat(discoveryClient.getInstances("test-svc-3").toArray()).containsOnly(new KubernetesServiceInstance("",
|
||||
"test-svc-3", "2.2.2.2", 8080, new HashMap<>(), false, "namespace1", null));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -179,8 +179,8 @@ public class KubernetesInformerDiscoveryClientTests {
|
||||
KubernetesInformerDiscoveryClient discoveryClient = new KubernetesInformerDiscoveryClient("",
|
||||
sharedInformerFactory, serviceLister, endpointsLister, null, null, kubernetesDiscoveryProperties);
|
||||
|
||||
assertThat(discoveryClient.getInstances("test-svc-1"))
|
||||
.containsOnly(new KubernetesServiceInstance("", "test-svc-1", "2.2.2.2", 8080, new HashMap<>(), false));
|
||||
assertThat(discoveryClient.getInstances("test-svc-1")).containsOnly(new KubernetesServiceInstance("",
|
||||
"test-svc-1", "2.2.2.2", 8080, new HashMap<>(), false, "namespace1", null));
|
||||
|
||||
verify(kubernetesDiscoveryProperties, times(2)).isAllNamespaces();
|
||||
verify(kubernetesDiscoveryProperties, times(1)).getPrimaryPortName();
|
||||
@@ -196,8 +196,8 @@ public class KubernetesInformerDiscoveryClientTests {
|
||||
KubernetesInformerDiscoveryClient discoveryClient = new KubernetesInformerDiscoveryClient("namespace1",
|
||||
sharedInformerFactory, serviceLister, endpointsLister, null, null, kubernetesDiscoveryProperties);
|
||||
|
||||
assertThat(discoveryClient.getInstances("test-svc-1"))
|
||||
.containsOnly(new KubernetesServiceInstance("", "test-svc-1", "2.2.2.2", 8080, new HashMap<>(), false));
|
||||
assertThat(discoveryClient.getInstances("test-svc-1")).containsOnly(new KubernetesServiceInstance("",
|
||||
"test-svc-1", "2.2.2.2", 8080, new HashMap<>(), false, "namespace1", null));
|
||||
verify(kubernetesDiscoveryProperties, times(1)).isAllNamespaces();
|
||||
verify(kubernetesDiscoveryProperties, times(1)).getPrimaryPortName();
|
||||
}
|
||||
@@ -229,8 +229,8 @@ public class KubernetesInformerDiscoveryClientTests {
|
||||
KubernetesInformerDiscoveryClient discoveryClient = new KubernetesInformerDiscoveryClient("namespace1",
|
||||
sharedInformerFactory, serviceLister, endpointsLister, null, null, kubernetesDiscoveryProperties);
|
||||
|
||||
assertThat(discoveryClient.getInstances("test-svc-1"))
|
||||
.containsOnly(new KubernetesServiceInstance("", "test-svc-1", "2.2.2.2", 8080, new HashMap<>(), false));
|
||||
assertThat(discoveryClient.getInstances("test-svc-1")).containsOnly(new KubernetesServiceInstance("",
|
||||
"test-svc-1", "2.2.2.2", 8080, new HashMap<>(), false, "namespace1", null));
|
||||
verify(kubernetesDiscoveryProperties, times(1)).isAllNamespaces();
|
||||
verify(kubernetesDiscoveryProperties, times(1)).getPrimaryPortName();
|
||||
verify(kubernetesDiscoveryProperties, times(1)).isIncludeNotReadyAddresses();
|
||||
@@ -275,8 +275,8 @@ public class KubernetesInformerDiscoveryClientTests {
|
||||
KubernetesInformerDiscoveryClient discoveryClient = new KubernetesInformerDiscoveryClient("namespace1",
|
||||
sharedInformerFactory, serviceLister, endpointsLister, null, null, kubernetesDiscoveryProperties);
|
||||
|
||||
assertThat(discoveryClient.getInstances("test-svc-1"))
|
||||
.containsOnly(new KubernetesServiceInstance("", "test-svc-1", "1.1.1.1", 443, new HashMap<>(), false));
|
||||
assertThat(discoveryClient.getInstances("test-svc-1")).containsOnly(new KubernetesServiceInstance("",
|
||||
"test-svc-1", "1.1.1.1", 443, new HashMap<>(), false, "namespace1", null));
|
||||
verify(kubernetesDiscoveryProperties, times(1)).isAllNamespaces();
|
||||
verify(kubernetesDiscoveryProperties, times(1)).getPrimaryPortName();
|
||||
verify(kubernetesDiscoveryProperties, times(1)).isIncludeNotReadyAddresses();
|
||||
@@ -294,8 +294,8 @@ public class KubernetesInformerDiscoveryClientTests {
|
||||
KubernetesInformerDiscoveryClient discoveryClient = new KubernetesInformerDiscoveryClient("namespace1",
|
||||
sharedInformerFactory, serviceLister, endpointsLister, null, null, kubernetesDiscoveryProperties);
|
||||
|
||||
assertThat(discoveryClient.getInstances("test-svc-1"))
|
||||
.containsOnly(new KubernetesServiceInstance("", "test-svc-1", "1.1.1.1", 80, new HashMap<>(), false));
|
||||
assertThat(discoveryClient.getInstances("test-svc-1")).containsOnly(new KubernetesServiceInstance("",
|
||||
"test-svc-1", "1.1.1.1", 80, new HashMap<>(), false, "namespace1", null));
|
||||
verify(kubernetesDiscoveryProperties, times(1)).isAllNamespaces();
|
||||
verify(kubernetesDiscoveryProperties, times(1)).getPrimaryPortName();
|
||||
}
|
||||
@@ -311,8 +311,8 @@ public class KubernetesInformerDiscoveryClientTests {
|
||||
KubernetesInformerDiscoveryClient discoveryClient = new KubernetesInformerDiscoveryClient("namespace1",
|
||||
sharedInformerFactory, serviceLister, endpointsLister, null, null, kubernetesDiscoveryProperties);
|
||||
|
||||
assertThat(discoveryClient.getInstances("test-svc-1"))
|
||||
.containsOnly(new KubernetesServiceInstance("", "test-svc-1", "1.1.1.1", 443, new HashMap<>(), false));
|
||||
assertThat(discoveryClient.getInstances("test-svc-1")).containsOnly(new KubernetesServiceInstance("",
|
||||
"test-svc-1", "1.1.1.1", 443, new HashMap<>(), false, "namespace1", null));
|
||||
verify(kubernetesDiscoveryProperties, times(1)).getPrimaryPortName();
|
||||
verify(kubernetesDiscoveryProperties, times(1)).isAllNamespaces();
|
||||
verify(kubernetesDiscoveryProperties, times(1)).isIncludeNotReadyAddresses();
|
||||
@@ -330,8 +330,8 @@ public class KubernetesInformerDiscoveryClientTests {
|
||||
KubernetesInformerDiscoveryClient discoveryClient = new KubernetesInformerDiscoveryClient("namespace1",
|
||||
sharedInformerFactory, serviceLister, endpointsLister, null, null, kubernetesDiscoveryProperties);
|
||||
|
||||
assertThat(discoveryClient.getInstances("test-svc-1"))
|
||||
.containsOnly(new KubernetesServiceInstance("", "test-svc-1", "1.1.1.1", 80, new HashMap<>(), false));
|
||||
assertThat(discoveryClient.getInstances("test-svc-1")).containsOnly(new KubernetesServiceInstance("",
|
||||
"test-svc-1", "1.1.1.1", 80, new HashMap<>(), false, "namespace1", null));
|
||||
verify(kubernetesDiscoveryProperties, times(1)).isAllNamespaces();
|
||||
verify(kubernetesDiscoveryProperties, times(1)).getPrimaryPortName();
|
||||
}
|
||||
@@ -346,8 +346,8 @@ public class KubernetesInformerDiscoveryClientTests {
|
||||
KubernetesInformerDiscoveryClient discoveryClient = new KubernetesInformerDiscoveryClient("namespace1",
|
||||
sharedInformerFactory, serviceLister, endpointsLister, null, null, kubernetesDiscoveryProperties);
|
||||
|
||||
assertThat(discoveryClient.getInstances("test-svc-1"))
|
||||
.containsOnly(new KubernetesServiceInstance("", "test-svc-1", "1.1.1.1", 443, new HashMap<>(), false));
|
||||
assertThat(discoveryClient.getInstances("test-svc-1")).containsOnly(new KubernetesServiceInstance("",
|
||||
"test-svc-1", "1.1.1.1", 443, new HashMap<>(), false, "namespace1", null));
|
||||
verify(kubernetesDiscoveryProperties, times(1)).isAllNamespaces();
|
||||
verify(kubernetesDiscoveryProperties, times(1)).getPrimaryPortName();
|
||||
}
|
||||
@@ -362,8 +362,8 @@ public class KubernetesInformerDiscoveryClientTests {
|
||||
KubernetesInformerDiscoveryClient discoveryClient = new KubernetesInformerDiscoveryClient("namespace1",
|
||||
sharedInformerFactory, serviceLister, endpointsLister, null, null, kubernetesDiscoveryProperties);
|
||||
|
||||
assertThat(discoveryClient.getInstances("test-svc-1"))
|
||||
.containsOnly(new KubernetesServiceInstance("", "test-svc-1", "1.1.1.1", 80, new HashMap<>(), false));
|
||||
assertThat(discoveryClient.getInstances("test-svc-1")).containsOnly(new KubernetesServiceInstance("",
|
||||
"test-svc-1", "1.1.1.1", 80, new HashMap<>(), false, "namespace1", null));
|
||||
verify(kubernetesDiscoveryProperties, times(1)).isAllNamespaces();
|
||||
verify(kubernetesDiscoveryProperties, times(1)).getPrimaryPortName();
|
||||
}
|
||||
@@ -379,8 +379,8 @@ public class KubernetesInformerDiscoveryClientTests {
|
||||
KubernetesInformerDiscoveryClient discoveryClient = new KubernetesInformerDiscoveryClient("namespace1",
|
||||
sharedInformerFactory, serviceLister, endpointsLister, null, null, kubernetesDiscoveryProperties);
|
||||
|
||||
assertThat(discoveryClient.getInstances("test-svc-1"))
|
||||
.containsOnly(new KubernetesServiceInstance("", "test-svc-1", "1.1.1.1", 80, new HashMap<>(), false));
|
||||
assertThat(discoveryClient.getInstances("test-svc-1")).containsOnly(new KubernetesServiceInstance("",
|
||||
"test-svc-1", "1.1.1.1", 80, new HashMap<>(), false, "namespace1", null));
|
||||
verify(kubernetesDiscoveryProperties, times(1)).isAllNamespaces();
|
||||
verify(kubernetesDiscoveryProperties, times(1)).getPrimaryPortName();
|
||||
}
|
||||
|
||||
@@ -116,8 +116,9 @@ public class KubernetesInformerReactiveDiscoveryClientTests {
|
||||
new KubernetesNamespaceProvider(new MockEnvironment()), sharedInformerFactory, serviceLister,
|
||||
endpointsLister, null, null, kubernetesDiscoveryProperties);
|
||||
|
||||
StepVerifier.create(discoveryClient.getInstances("test-svc-1"))
|
||||
.expectNext(new KubernetesServiceInstance("", "test-svc-1", "2.2.2.2", 8080, new HashMap<>(), false))
|
||||
StepVerifier
|
||||
.create(discoveryClient.getInstances("test-svc-1")).expectNext(new KubernetesServiceInstance("",
|
||||
"test-svc-1", "2.2.2.2", 8080, new HashMap<>(), false, "namespace1", null))
|
||||
.expectComplete().verify();
|
||||
|
||||
verify(kubernetesDiscoveryProperties, times(2)).isAllNamespaces();
|
||||
@@ -135,8 +136,9 @@ public class KubernetesInformerReactiveDiscoveryClientTests {
|
||||
kubernetesNamespaceProvider, sharedInformerFactory, serviceLister, endpointsLister, null, null,
|
||||
kubernetesDiscoveryProperties);
|
||||
|
||||
StepVerifier.create(discoveryClient.getInstances("test-svc-1"))
|
||||
.expectNext(new KubernetesServiceInstance("", "test-svc-1", "2.2.2.2", 8080, new HashMap<>(), false))
|
||||
StepVerifier
|
||||
.create(discoveryClient.getInstances("test-svc-1")).expectNext(new KubernetesServiceInstance("",
|
||||
"test-svc-1", "2.2.2.2", 8080, new HashMap<>(), false, "namespace1", null))
|
||||
.expectComplete().verify();
|
||||
|
||||
verify(kubernetesDiscoveryProperties, times(1)).isAllNamespaces();
|
||||
|
||||
@@ -20,6 +20,10 @@
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-commons</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-logging</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-actuator-autoconfigure</artifactId>
|
||||
|
||||
@@ -37,19 +37,23 @@ public class KubernetesServiceInstance implements ServiceInstance {
|
||||
|
||||
private static final String COLON = ":";
|
||||
|
||||
private final String instanceId;
|
||||
private String instanceId;
|
||||
|
||||
private final String serviceId;
|
||||
private String serviceId;
|
||||
|
||||
private final String host;
|
||||
private String host;
|
||||
|
||||
private final int port;
|
||||
private int port;
|
||||
|
||||
private final URI uri;
|
||||
private URI uri;
|
||||
|
||||
private final Boolean secure;
|
||||
private Boolean secure;
|
||||
|
||||
private final Map<String, String> metadata;
|
||||
private Map<String, String> metadata;
|
||||
|
||||
private String namespace;
|
||||
|
||||
private String cluster;
|
||||
|
||||
/**
|
||||
* @param instanceId the id of the instance.
|
||||
@@ -68,6 +72,35 @@ public class KubernetesServiceInstance implements ServiceInstance {
|
||||
this.metadata = metadata;
|
||||
this.secure = secure;
|
||||
this.uri = createUri(secure ? HTTPS_PREFIX : HTTP_PREFIX, host, port);
|
||||
this.namespace = null;
|
||||
this.cluster = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param instanceId the id of the instance.
|
||||
* @param serviceId the id of the service.
|
||||
* @param host the address where the service instance can be found.
|
||||
* @param port the port on which the service is running.
|
||||
* @param metadata a map containing metadata.
|
||||
* @param secure indicates whether or not the connection needs to be secure.
|
||||
* @param namespace the namespace of the service.
|
||||
* @param cluster the clust the service resides in.
|
||||
*/
|
||||
public KubernetesServiceInstance(String instanceId, String serviceId, String host, int port,
|
||||
Map<String, String> metadata, Boolean secure, String namespace, String cluster) {
|
||||
this.instanceId = instanceId;
|
||||
this.serviceId = serviceId;
|
||||
this.host = host;
|
||||
this.port = port;
|
||||
this.metadata = metadata;
|
||||
this.secure = secure;
|
||||
this.uri = createUri(secure ? HTTPS_PREFIX : HTTP_PREFIX, host, port);
|
||||
this.namespace = namespace;
|
||||
this.cluster = cluster;
|
||||
}
|
||||
|
||||
// Allows for deserialization
|
||||
public KubernetesServiceInstance() {
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -114,7 +147,51 @@ public class KubernetesServiceInstance implements ServiceInstance {
|
||||
}
|
||||
|
||||
public String getNamespace() {
|
||||
return this.metadata != null ? this.metadata.get(NAMESPACE_METADATA_KEY) : null;
|
||||
return namespace != null ? namespace : this.metadata.get(NAMESPACE_METADATA_KEY);
|
||||
}
|
||||
|
||||
public String getCluster() {
|
||||
return this.cluster;
|
||||
}
|
||||
|
||||
public void setInstanceId(String instanceId) {
|
||||
this.instanceId = instanceId;
|
||||
}
|
||||
|
||||
public void setServiceId(String serviceId) {
|
||||
this.serviceId = serviceId;
|
||||
}
|
||||
|
||||
public void setHost(String host) {
|
||||
this.host = host;
|
||||
}
|
||||
|
||||
public void setPort(int port) {
|
||||
this.port = port;
|
||||
}
|
||||
|
||||
public void setUri(URI uri) {
|
||||
this.uri = uri;
|
||||
}
|
||||
|
||||
public void setSecure(Boolean secure) {
|
||||
this.secure = secure;
|
||||
}
|
||||
|
||||
public void setMetadata(Map<String, String> metadata) {
|
||||
this.metadata = metadata;
|
||||
}
|
||||
|
||||
public void setNamespace(String namespace) {
|
||||
this.namespace = namespace;
|
||||
}
|
||||
|
||||
public void setCluster(String cluster) {
|
||||
this.cluster = cluster;
|
||||
}
|
||||
|
||||
public Boolean getSecure() {
|
||||
return secure;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -129,19 +206,20 @@ public class KubernetesServiceInstance implements ServiceInstance {
|
||||
return port == that.port && Objects.equals(instanceId, that.instanceId)
|
||||
&& Objects.equals(serviceId, that.serviceId) && Objects.equals(host, that.host)
|
||||
&& Objects.equals(uri, that.uri) && Objects.equals(secure, that.secure)
|
||||
&& Objects.equals(metadata, that.metadata);
|
||||
&& Objects.equals(metadata, that.metadata) && Objects.equals(getNamespace(), that.getNamespace())
|
||||
&& Objects.equals(cluster, that.cluster);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "KubernetesServiceInstance{" + "instanceId='" + instanceId + '\'' + ", serviceId='" + serviceId + '\''
|
||||
+ ", host='" + host + '\'' + ", port=" + port + ", uri=" + uri + ", secure=" + secure + ", metadata="
|
||||
+ metadata + '}';
|
||||
+ ", host='" + host + '\'' + ", port=" + port + ", uri=" + uri + ", secure=" + secure + ", namespace="
|
||||
+ getNamespace() + ", cluster=" + cluster + ", metadata=" + metadata + '}';
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(instanceId, serviceId, host, port, uri, secure, metadata);
|
||||
return Objects.hash(instanceId, serviceId, host, port, uri, secure, getNamespace(), cluster, metadata);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
|
||||
<modules>
|
||||
<module>spring-cloud-kubernetes-configuration-watcher</module>
|
||||
<module>spring-cloud-kubernetes-discoveryserver</module>
|
||||
<module>spring-cloud-kubernetes-configserver</module>
|
||||
</modules>
|
||||
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: List
|
||||
items:
|
||||
- apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
labels:
|
||||
app: spring-cloud-kubernetes-discoveryserver
|
||||
name: spring-cloud-kubernetes-discoveryserver
|
||||
spec:
|
||||
ports:
|
||||
- name: http
|
||||
port: 80
|
||||
targetPort: 8761
|
||||
selector:
|
||||
app: spring-cloud-kubernetes-discoveryserver
|
||||
type: LoadBalancer
|
||||
- apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
labels:
|
||||
app: spring-cloud-kubernetes-discoveryserver
|
||||
name: spring-cloud-kubernetes-discoveryserver
|
||||
- apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: RoleBinding
|
||||
metadata:
|
||||
labels:
|
||||
app: spring-cloud-kubernetes-discoveryserver
|
||||
name: spring-cloud-kubernetes-discoveryserver:view
|
||||
roleRef:
|
||||
kind: Role
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
name: namespace-reader
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: spring-cloud-kubernetes-discoveryserver
|
||||
- apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: Role
|
||||
metadata:
|
||||
namespace: default
|
||||
name: namespace-reader
|
||||
rules:
|
||||
- apiGroups: ["", "extensions", "apps"]
|
||||
resources: ["configmaps", "pods", "services", "endpoints", "secrets"]
|
||||
verbs: ["get", "list", "watch"]
|
||||
- apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: spring-cloud-kubernetes-discoveryserver-deployment
|
||||
spec:
|
||||
selector:
|
||||
matchLabels:
|
||||
app: spring-cloud-kubernetes-discoveryserver
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: spring-cloud-kubernetes-discoveryserver
|
||||
spec:
|
||||
serviceAccount: spring-cloud-kubernetes-discoveryserver
|
||||
containers:
|
||||
- name: spring-cloud-kubernetes-discoveryserver
|
||||
image: springcloud/spring-cloud-kubernetes-discoveryserver:2.1.0-SNAPSHOT
|
||||
imagePullPolicy: IfNotPresent
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
port: 8761
|
||||
path: /actuator/health/readiness
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
port: 8761
|
||||
path: /actuator/health/liveness
|
||||
ports:
|
||||
- containerPort: 8761
|
||||
@@ -0,0 +1,149 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>spring-cloud-kubernetes-controllers</artifactId>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<version>2.1.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>spring-cloud-kubernetes-discoveryserver</artifactId>
|
||||
|
||||
<properties>
|
||||
<jib.version>1.8.0</jib.version>
|
||||
<base.image>openjdk:8u222-slim</base.image>
|
||||
<docker.registry.organization>springcloud</docker.registry.organization>
|
||||
<plexus-archiver.version>4.1.0</plexus-archiver.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-webflux</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-actuator</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-kubernetes-client</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.projectreactor</groupId>
|
||||
<artifactId>reactor-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.github.tomakehurst</groupId>
|
||||
<artifactId>wiremock-jre8</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
<configuration>
|
||||
<image>
|
||||
<name>${env.IMAGE}</name>
|
||||
</image>
|
||||
<goal>build-image</goal>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>build-image</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
<profiles>
|
||||
<profile>
|
||||
<id>dockerpush</id>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>com.spotify</groupId>
|
||||
<artifactId>dockerfile-maven-plugin</artifactId>
|
||||
<version>1.4.12</version>
|
||||
<configuration>
|
||||
<repository>${docker.registry.organization}/${artifactId}</repository>
|
||||
<tag>${project.version}</tag>
|
||||
<username>${env.DOCKER_HUB_USERNAME}</username>
|
||||
<password>${env.DOCKER_HUB_PASSWORD}</password>
|
||||
<build>
|
||||
<noCache>true</noCache>
|
||||
</build>
|
||||
</configuration>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.codehaus.plexus</groupId>
|
||||
<artifactId>plexus-archiver</artifactId>
|
||||
<version>${plexus-archiver.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</profile>
|
||||
<profile>
|
||||
<id>imagename</id>
|
||||
<activation>
|
||||
<property>
|
||||
<name>!env.IMAGE</name>
|
||||
</property>
|
||||
</activation>
|
||||
<properties>
|
||||
<env.IMAGE>springcloud/${project.artifactId}:${project.version}</env.IMAGE>
|
||||
</properties>
|
||||
</profile>
|
||||
<profile>
|
||||
<id>jib</id>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>com.google.cloud.tools</groupId>
|
||||
<artifactId>jib-maven-plugin</artifactId>
|
||||
<version>${jib.version}</version>
|
||||
<configuration>
|
||||
<from>
|
||||
<image>${base.image}</image>
|
||||
</from>
|
||||
<to>
|
||||
<image>spring-cloud/${project.artifactId}</image>
|
||||
</to>
|
||||
<container>
|
||||
<user>nobody:nogroup</user>
|
||||
<environment>
|
||||
</environment>
|
||||
</container>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>dockerBuild</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</profile>
|
||||
</profiles>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,20 @@
|
||||
apiVersion: skaffold/v2alpha3
|
||||
kind: Config
|
||||
metadata:
|
||||
name: spring-cloud-kubernetes-discoveryserver
|
||||
build:
|
||||
artifacts:
|
||||
- image: springcloud/spring-cloud-kubernetes-discoveryserver
|
||||
# custom:
|
||||
# buildCommand: "../../mvnw clean install"
|
||||
# dependencies:
|
||||
# paths:
|
||||
# - src
|
||||
# - pom.xml
|
||||
jib: {
|
||||
args: ["-Pjib"]
|
||||
}
|
||||
deploy:
|
||||
kubectl:
|
||||
manifests:
|
||||
- k8s/deployment.yaml
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright 2013-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframewok.cloud.kubernetes.discoveryserver;
|
||||
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.boot.builder.SpringApplicationBuilder;
|
||||
|
||||
/**
|
||||
* @author Ryan Baxter
|
||||
*/
|
||||
@SpringBootApplication
|
||||
public class DiscoveryServerApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
new SpringApplicationBuilder(DiscoveryServerApplication.class).run(args);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
* Copyright 2013-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframewok.cloud.kubernetes.discoveryserver;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.cloud.client.ServiceInstance;
|
||||
import org.springframework.cloud.kubernetes.client.discovery.reactive.KubernetesInformerReactiveDiscoveryClient;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
* @author Ryan Baxter
|
||||
*/
|
||||
@RestController
|
||||
public class DiscoveryServerController {
|
||||
|
||||
private KubernetesInformerReactiveDiscoveryClient reactiveDiscoveryClient;
|
||||
|
||||
public DiscoveryServerController(KubernetesInformerReactiveDiscoveryClient reactiveDiscoveryClient) {
|
||||
this.reactiveDiscoveryClient = reactiveDiscoveryClient;
|
||||
}
|
||||
|
||||
@GetMapping("/apps")
|
||||
public Flux<Service> apps() {
|
||||
return reactiveDiscoveryClient.getServices().flatMap(service -> reactiveDiscoveryClient.getInstances(service)
|
||||
.collectList().flatMap(serviceInstances -> Mono.just(new Service(service, serviceInstances))));
|
||||
}
|
||||
|
||||
@GetMapping("/apps/{name}")
|
||||
public Flux<ServiceInstance> appInstances(@PathVariable String name) {
|
||||
return reactiveDiscoveryClient.getInstances(name);
|
||||
}
|
||||
|
||||
@GetMapping("/app/{name}/{instanceId}")
|
||||
public Mono<ServiceInstance> appInstance(@PathVariable String name, @PathVariable String instanceId) {
|
||||
return reactiveDiscoveryClient.getInstances(name)
|
||||
.filter(serviceInstance -> serviceInstance.getInstanceId().equals(instanceId)).singleOrEmpty();
|
||||
}
|
||||
|
||||
public static class Service {
|
||||
|
||||
private String name;
|
||||
|
||||
private List<ServiceInstance> serviceInstances;
|
||||
|
||||
public Service() {
|
||||
}
|
||||
|
||||
public Service(String name, List<ServiceInstance> serviceInstances) {
|
||||
this.name = name;
|
||||
this.serviceInstances = serviceInstances;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public List<ServiceInstance> getServiceInstances() {
|
||||
return serviceInstances;
|
||||
}
|
||||
|
||||
public void setServiceInstances(List<ServiceInstance> serviceInstances) {
|
||||
this.serviceInstances = serviceInstances;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
Service service = (Service) o;
|
||||
return Objects.equals(getName(), service.getName())
|
||||
&& Objects.equals(getServiceInstances(), service.getServiceInstances());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(getName(), getServiceInstances());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
server:
|
||||
port: 8761
|
||||
@@ -0,0 +1,120 @@
|
||||
/*
|
||||
* Copyright 2013-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframewok.cloud.kubernetes.discoveryserver;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.test.StepVerifier;
|
||||
|
||||
import org.springframework.cloud.client.ServiceInstance;
|
||||
import org.springframework.cloud.kubernetes.client.discovery.reactive.KubernetesInformerReactiveDiscoveryClient;
|
||||
import org.springframework.cloud.kubernetes.commons.discovery.KubernetesServiceInstance;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
/**
|
||||
* @author Ryan Baxter
|
||||
*/
|
||||
class DiscoveryServerControllerTests {
|
||||
|
||||
private static final KubernetesServiceInstance serviceAInstance1 = new KubernetesServiceInstance("serviceAInstance1",
|
||||
"serviceAInstance1", "2.2.2.2", 8080, new HashMap<>(), false, "namespace1", null);
|
||||
|
||||
private static final KubernetesServiceInstance serviceAInstance2 = new KubernetesServiceInstance("serviceAInstance2",
|
||||
"serviceAInstance2", "2.2.2.2", 8080, new HashMap<>(), false, "namespace1", null);
|
||||
|
||||
private static final KubernetesServiceInstance serviceAInstance3 = new KubernetesServiceInstance("serviceAInstance3",
|
||||
"serviceAInstance3", "2.2.2.2", 8080, new HashMap<>(), false, "namespace2", null);
|
||||
|
||||
private static final KubernetesServiceInstance serviceBInstance1 = new KubernetesServiceInstance("serviceBInstance1",
|
||||
"serviceBInstance1", "2.2.2.2", 8080, new HashMap<>(), false, "namespace1", null);
|
||||
|
||||
private static final KubernetesServiceInstance serviceCInstance1 = new KubernetesServiceInstance("serviceCInstance1",
|
||||
"serviceCInstance1", "2.2.2.2", 8080, new HashMap<>(), false, "namespace2", null);
|
||||
|
||||
private static DiscoveryServerController.Service serviceA = new DiscoveryServerController.Service();
|
||||
private static DiscoveryServerController.Service serviceB = new DiscoveryServerController.Service();
|
||||
private static DiscoveryServerController.Service serviceC = new DiscoveryServerController.Service();
|
||||
|
||||
private static KubernetesInformerReactiveDiscoveryClient discoveryClient;
|
||||
|
||||
@BeforeAll
|
||||
static void beforeAll() {
|
||||
Flux<String> services = Flux.just("serviceA", "serviceB", "serviceC");
|
||||
|
||||
List<ServiceInstance> serviceAInstanceList = new ArrayList<>();
|
||||
serviceAInstanceList.add(serviceAInstance1);
|
||||
serviceAInstanceList.add(serviceAInstance2);
|
||||
serviceAInstanceList.add(serviceAInstance3);
|
||||
|
||||
Flux<ServiceInstance> serviceAInstances = Flux.fromIterable(serviceAInstanceList);
|
||||
|
||||
List<ServiceInstance> serviceBInstanceList = Collections.singletonList(serviceBInstance1);
|
||||
Flux<ServiceInstance> serviceBInstances = Flux.fromIterable(serviceBInstanceList);
|
||||
|
||||
List<ServiceInstance> serviceCInstanceList = Collections.singletonList(serviceCInstance1);
|
||||
Flux<ServiceInstance> serviceCInstances = Flux.fromIterable(serviceCInstanceList);
|
||||
|
||||
discoveryClient = mock(KubernetesInformerReactiveDiscoveryClient.class);
|
||||
when(discoveryClient.getServices()).thenReturn(services);
|
||||
when(discoveryClient.getInstances(eq("serviceA"))).thenReturn(serviceAInstances);
|
||||
when(discoveryClient.getInstances(eq("serviceB"))).thenReturn(serviceBInstances);
|
||||
when(discoveryClient.getInstances(eq("serviceC"))).thenReturn(serviceCInstances);
|
||||
when(discoveryClient.getInstances(eq("serviceD"))).thenReturn(Flux.empty());
|
||||
|
||||
serviceA.setName("serviceA");
|
||||
serviceA.setServiceInstances(serviceAInstanceList);
|
||||
|
||||
serviceB.setName("serviceB");
|
||||
serviceB.setServiceInstances(serviceBInstanceList);
|
||||
|
||||
serviceC.setName("serviceC");
|
||||
serviceC.setServiceInstances(serviceCInstanceList);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Test
|
||||
void apps() {
|
||||
DiscoveryServerController controller = new DiscoveryServerController(discoveryClient);
|
||||
StepVerifier.create(controller.apps()).expectNext(serviceA, serviceB, serviceC).verifyComplete();
|
||||
}
|
||||
|
||||
@Test
|
||||
void appInstances() {
|
||||
DiscoveryServerController controller = new DiscoveryServerController(discoveryClient);
|
||||
StepVerifier.create(controller.appInstances("serviceA")).expectNext(serviceAInstance1, serviceAInstance2, serviceAInstance3).verifyComplete();
|
||||
StepVerifier.create(controller.appInstances("serviceB")).expectNext(serviceBInstance1).verifyComplete();
|
||||
StepVerifier.create(controller.appInstances("serviceC")).expectNext(serviceCInstance1).verifyComplete();
|
||||
StepVerifier.create(controller.appInstances("serviceD")).expectNextCount(0).verifyComplete();
|
||||
}
|
||||
|
||||
@Test
|
||||
void appInstance() {
|
||||
DiscoveryServerController controller = new DiscoveryServerController(discoveryClient);
|
||||
StepVerifier.create(controller.appInstance("serviceA", "serviceAInstance2")).expectNext(serviceAInstance2).verifyComplete();
|
||||
StepVerifier.create(controller.appInstance("serviceB", "doesnotexist")).expectNextCount(0).verifyComplete();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,208 @@
|
||||
/*
|
||||
* Copyright 2013-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframewok.cloud.kubernetes.discoveryserver;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
import com.github.tomakehurst.wiremock.WireMockServer;
|
||||
import com.github.tomakehurst.wiremock.client.WireMock;
|
||||
import io.kubernetes.client.openapi.ApiClient;
|
||||
import io.kubernetes.client.openapi.JSON;
|
||||
import io.kubernetes.client.openapi.models.V1EndpointAddress;
|
||||
import io.kubernetes.client.openapi.models.V1EndpointPort;
|
||||
import io.kubernetes.client.openapi.models.V1EndpointSubset;
|
||||
import io.kubernetes.client.openapi.models.V1Endpoints;
|
||||
import io.kubernetes.client.openapi.models.V1EndpointsListBuilder;
|
||||
import io.kubernetes.client.openapi.models.V1ListMetaBuilder;
|
||||
import io.kubernetes.client.openapi.models.V1ObjectMeta;
|
||||
import io.kubernetes.client.openapi.models.V1ObjectReferenceBuilder;
|
||||
import io.kubernetes.client.openapi.models.V1Service;
|
||||
import io.kubernetes.client.openapi.models.V1ServiceListBuilder;
|
||||
import io.kubernetes.client.openapi.models.V1ServiceSpec;
|
||||
import io.kubernetes.client.openapi.models.V1ServiceStatus;
|
||||
import io.kubernetes.client.util.ClientBuilder;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.cloud.kubernetes.commons.KubernetesNamespaceProvider;
|
||||
import org.springframework.cloud.kubernetes.commons.discovery.KubernetesServiceInstance;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.test.web.reactive.server.WebTestClient;
|
||||
|
||||
import static com.github.tomakehurst.wiremock.client.WireMock.aResponse;
|
||||
import static com.github.tomakehurst.wiremock.client.WireMock.get;
|
||||
import static com.github.tomakehurst.wiremock.client.WireMock.stubFor;
|
||||
import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
/**
|
||||
* @author Ryan Baxter
|
||||
*/
|
||||
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = DiscoveryServerIntegrationTests.TestConfig.class,
|
||||
properties = {"debug=true"})
|
||||
public class DiscoveryServerIntegrationTests {
|
||||
|
||||
private static final V1Service testService1 = new V1Service()
|
||||
.metadata(new V1ObjectMeta().name("test-svc-1").namespace("namespace1"))
|
||||
.spec(new V1ServiceSpec().loadBalancerIP("1.1.1.1")).status(new V1ServiceStatus());
|
||||
private static final V1Endpoints testEndpoints1 = new V1Endpoints()
|
||||
.metadata(new V1ObjectMeta().name("test-svc-1").namespace("namespace1"))
|
||||
.addSubsetsItem(new V1EndpointSubset().addPortsItem(new V1EndpointPort().port(8080).name("http"))
|
||||
.addAddressesItem(new V1EndpointAddress().ip("2.2.2.2").targetRef(new V1ObjectReferenceBuilder().withUid("uid1").build())));
|
||||
|
||||
private static final V1Service testService2 = new V1Service()
|
||||
.metadata(new V1ObjectMeta().name("test-svc-1").namespace("namespace2"))
|
||||
.spec(new V1ServiceSpec().loadBalancerIP("1.1.1.1")).status(new V1ServiceStatus());
|
||||
|
||||
private static final V1Service testService3 = new V1Service()
|
||||
.metadata(new V1ObjectMeta().name("test-svc-3").namespace("namespace1").putLabelsItem("spring", "true")
|
||||
.putLabelsItem("k8s", "true"))
|
||||
.spec(new V1ServiceSpec().loadBalancerIP("1.1.1.1")).status(new V1ServiceStatus());
|
||||
private static final V1Endpoints testEndpoints3 = new V1Endpoints()
|
||||
.metadata(new V1ObjectMeta().name("test-svc-3").namespace("namespace1"))
|
||||
.addSubsetsItem(new V1EndpointSubset().addPortsItem(new V1EndpointPort().port(8080).name("http"))
|
||||
.addAddressesItem(new V1EndpointAddress().ip("2.2.2.2").targetRef(new V1ObjectReferenceBuilder().withUid("uid2").build())));
|
||||
|
||||
|
||||
private static WireMockServer wireMockServer;
|
||||
|
||||
@Autowired
|
||||
WebTestClient webTestClient;
|
||||
|
||||
@Test
|
||||
void apps(){
|
||||
Map<String, String> kubernetesServiceInstance1Metadata = new HashMap<>();
|
||||
kubernetesServiceInstance1Metadata.put(testEndpoints1.getSubsets().get(0).getPorts().get(0).getName(), testEndpoints1.getSubsets().get(0).getPorts().get(0).getPort().toString());
|
||||
|
||||
Map<String, String> kubernetesServiceInstance2Metadata = new HashMap<>();
|
||||
kubernetesServiceInstance2Metadata.put(testEndpoints3.getSubsets().get(0).getPorts().get(0).getName(), testEndpoints3.getSubsets().get(0).getPorts().get(0).getPort().toString());
|
||||
kubernetesServiceInstance2Metadata.putAll(testService3.getMetadata().getLabels());
|
||||
|
||||
KubernetesServiceInstance kubernetesServiceInstance1 = new KubernetesServiceInstance(testEndpoints1.getSubsets().get(0).getAddresses().get(0).getTargetRef().getUid(), testService1.getMetadata().getName(), testEndpoints1.getSubsets().get(0).getAddresses().get(0).getIp(), testEndpoints1.getSubsets().get(0).getPorts().get(0).getPort(), kubernetesServiceInstance1Metadata, false, testService1.getMetadata().getNamespace(), null);
|
||||
KubernetesServiceInstance kubernetesServiceInstance3 = new KubernetesServiceInstance(testEndpoints3.getSubsets().get(0).getAddresses().get(0).getTargetRef().getUid(), testService3.getMetadata().getName(), testEndpoints3.getSubsets().get(0).getAddresses().get(0).getIp(), testEndpoints3.getSubsets().get(0).getPorts().get(0).getPort(), kubernetesServiceInstance2Metadata, false, testService3.getMetadata().getNamespace(), null);
|
||||
|
||||
webTestClient.get().uri("/apps").exchange().expectBodyList(KubernetesService.class).hasSize(2).contains(new KubernetesService(testService1.getMetadata().getName(),
|
||||
Collections.singletonList(kubernetesServiceInstance1)), new KubernetesService(testService3.getMetadata().getName(), Collections.singletonList(kubernetesServiceInstance3)));
|
||||
}
|
||||
|
||||
@Test
|
||||
void appsName() {
|
||||
Map<String, String> kubernetesServiceInstance2Metadata = new HashMap<>();
|
||||
kubernetesServiceInstance2Metadata.put(testEndpoints3.getSubsets().get(0).getPorts().get(0).getName(), testEndpoints3.getSubsets().get(0).getPorts().get(0).getPort().toString());
|
||||
kubernetesServiceInstance2Metadata.putAll(testService3.getMetadata().getLabels());
|
||||
KubernetesServiceInstance kubernetesServiceInstance3 = new KubernetesServiceInstance(testEndpoints3.getSubsets().get(0).getAddresses().get(0).getTargetRef().getUid(), testService3.getMetadata().getName(), testEndpoints3.getSubsets().get(0).getAddresses().get(0).getIp(), testEndpoints3.getSubsets().get(0).getPorts().get(0).getPort(), kubernetesServiceInstance2Metadata, false, testService3.getMetadata().getNamespace(), null);
|
||||
webTestClient.get().uri("/apps/test-svc-3").exchange().expectBodyList(KubernetesServiceInstance.class).hasSize(1).contains(kubernetesServiceInstance3);
|
||||
}
|
||||
|
||||
@Test
|
||||
void instance() {
|
||||
Map<String, String> kubernetesServiceInstance2Metadata = new HashMap<>();
|
||||
kubernetesServiceInstance2Metadata.put(testEndpoints3.getSubsets().get(0).getPorts().get(0).getName(), testEndpoints3.getSubsets().get(0).getPorts().get(0).getPort().toString());
|
||||
kubernetesServiceInstance2Metadata.putAll(testService3.getMetadata().getLabels());
|
||||
KubernetesServiceInstance kubernetesServiceInstance3 = new KubernetesServiceInstance(testEndpoints3.getSubsets().get(0).getAddresses().get(0).getTargetRef().getUid(), testService3.getMetadata().getName(), testEndpoints3.getSubsets().get(0).getAddresses().get(0).getIp(), testEndpoints3.getSubsets().get(0).getPorts().get(0).getPort(), kubernetesServiceInstance2Metadata, false, testService3.getMetadata().getNamespace(), null);
|
||||
webTestClient.get().uri("/app/test-svc-3/uid2").exchange().expectBody(KubernetesServiceInstance.class).isEqualTo(kubernetesServiceInstance3);
|
||||
}
|
||||
|
||||
@SpringBootApplication
|
||||
protected static class TestConfig {
|
||||
|
||||
@Bean
|
||||
public KubernetesNamespaceProvider kubernetesNamespaceProvider() {
|
||||
KubernetesNamespaceProvider provider = mock(KubernetesNamespaceProvider.class);
|
||||
when(provider.getNamespace()).thenReturn("namespace1");
|
||||
return provider;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ApiClient apiClient() {
|
||||
wireMockServer = new WireMockServer(options().dynamicPort());
|
||||
wireMockServer.start();
|
||||
WireMock.configureFor(wireMockServer.port());
|
||||
stubFor(get("/api/v1/namespaces/namespace1/endpoints?resourceVersion=0&watch=false")
|
||||
.willReturn(aResponse().withStatus(200).withBody(new JSON().serialize(new V1EndpointsListBuilder()
|
||||
.withMetadata(new V1ListMetaBuilder().withNewResourceVersion("0").build()).addToItems(testEndpoints1, testEndpoints3).build()))));
|
||||
stubFor(get("/api/v1/namespaces/namespace1/services?resourceVersion=0&watch=false")
|
||||
.willReturn(aResponse().withStatus(200).withBody(new JSON().serialize(new V1ServiceListBuilder()
|
||||
.withMetadata(new V1ListMetaBuilder().withNewResourceVersion("0").build()).addToItems(testService1, testService2, testService3).build()))));
|
||||
stubFor(get("/api/v1/namespaces/namespace1/endpoints?watch=true")
|
||||
.willReturn(aResponse().withStatus(200)));
|
||||
stubFor(get("/api/v1/namespaces/namespace1/services?watch=true")
|
||||
.willReturn(aResponse().withStatus(200)));
|
||||
ApiClient apiClient = new ClientBuilder().setBasePath(wireMockServer.baseUrl()).build();
|
||||
return apiClient;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class KubernetesService {
|
||||
|
||||
private String name;
|
||||
|
||||
private List<KubernetesServiceInstance> serviceInstances;
|
||||
|
||||
public KubernetesService() { }
|
||||
|
||||
public KubernetesService(String name, List<KubernetesServiceInstance> serviceInstances) {
|
||||
this.name = name;
|
||||
this.serviceInstances = serviceInstances;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public List<KubernetesServiceInstance> getServiceInstances() {
|
||||
return serviceInstances;
|
||||
}
|
||||
|
||||
public void setServiceInstances(List<KubernetesServiceInstance> serviceInstances) {
|
||||
this.serviceInstances = serviceInstances;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
KubernetesService service = (KubernetesService) o;
|
||||
return Objects.equals(getName(), service.getName()) && Objects.equals(getServiceInstances(), service.getServiceInstances());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(getName(), getServiceInstances());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "KubernetesService{" +
|
||||
"name='" + name + '\'' +
|
||||
", serviceInstances=" + serviceInstances +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -140,6 +140,12 @@
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-kubernetes-discovery</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Own dependencies - Starters -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
@@ -189,6 +195,12 @@
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-kubernetes-discoveryclient</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Testing Dependencies -->
|
||||
<dependency>
|
||||
<groupId>org.jboss.arquillian.junit</groupId>
|
||||
|
||||
59
spring-cloud-kubernetes-discovery/pom.xml
Normal file
59
spring-cloud-kubernetes-discovery/pom.xml
Normal file
@@ -0,0 +1,59 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>spring-cloud-kubernetes</artifactId>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<version>2.1.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>spring-cloud-kubernetes-discovery</artifactId>
|
||||
|
||||
<properties>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-commons</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-actuator</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-autoconfigure</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-webflux</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.github.tomakehurst</groupId>
|
||||
<artifactId>wiremock-jre8</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.projectreactor</groupId>
|
||||
<artifactId>reactor-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright 2013-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.kubernetes.discovery;
|
||||
|
||||
/**
|
||||
* @author Ryan Baxter
|
||||
*/
|
||||
public class DiscoveryServerUrlInvalidException extends RuntimeException {
|
||||
|
||||
public DiscoveryServerUrlInvalidException() {
|
||||
super("spring.cloud.kubernetes.discovery-server-url must be specified and a valid URL.");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright 2013-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.kubernetes.discovery;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.springframework.cloud.client.ServiceInstance;
|
||||
import org.springframework.cloud.client.discovery.DiscoveryClient;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
/**
|
||||
* @author Ryan Baxter
|
||||
*/
|
||||
public class KubernetesDiscoveryClient implements DiscoveryClient {
|
||||
|
||||
private RestTemplate rest;
|
||||
|
||||
private KubernetesDiscoveryClientProperties properties;
|
||||
|
||||
public KubernetesDiscoveryClient(RestTemplate rest, KubernetesDiscoveryClientProperties properties) {
|
||||
if (!StringUtils.hasText(properties.getDiscoveryServerUrl())) {
|
||||
throw new DiscoveryServerUrlInvalidException();
|
||||
}
|
||||
this.rest = rest;
|
||||
this.properties = properties;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String description() {
|
||||
return "Kubernetes Discovery Client";
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ServiceInstance> getInstances(String serviceId) {
|
||||
List<ServiceInstance> response = Collections.emptyList();
|
||||
KubernetesServiceInstance[] responseBody = rest.getForEntity(
|
||||
properties.getDiscoveryServerUrl() + "/apps/" + serviceId, KubernetesServiceInstance[].class).getBody();
|
||||
if (responseBody != null && responseBody.length > 0) {
|
||||
response = Arrays.asList(responseBody);
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getServices() {
|
||||
List<String> response = Collections.emptyList();
|
||||
Service[] services = rest.getForEntity(properties.getDiscoveryServerUrl() + "/apps", Service[].class).getBody();
|
||||
if (services != null && services.length > 0) {
|
||||
response = Arrays.stream(services).map(service -> service.getName()).collect(Collectors.toList());
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
/*
|
||||
* Copyright 2013-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.kubernetes.discovery;
|
||||
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.boot.actuate.health.HealthIndicator;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.boot.web.client.RestTemplateBuilder;
|
||||
import org.springframework.cloud.client.ConditionalOnDiscoveryEnabled;
|
||||
import org.springframework.cloud.client.ConditionalOnDiscoveryHealthIndicatorEnabled;
|
||||
import org.springframework.cloud.client.ConditionalOnReactiveDiscoveryEnabled;
|
||||
import org.springframework.cloud.client.discovery.DiscoveryClient;
|
||||
import org.springframework.cloud.client.discovery.ReactiveDiscoveryClient;
|
||||
import org.springframework.cloud.client.discovery.event.InstanceRegisteredEvent;
|
||||
import org.springframework.cloud.client.discovery.health.DiscoveryClientHealthIndicatorProperties;
|
||||
import org.springframework.cloud.client.discovery.health.reactive.ReactiveDiscoveryClientHealthIndicator;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationEventPublisher;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
import org.springframework.web.reactive.function.client.WebClient;
|
||||
|
||||
/**
|
||||
* @author Ryan Baxter
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnDiscoveryEnabled
|
||||
@ConditionalOnProperty(value = { "spring.cloud.kubernetes.enabled", "spring.cloud.kubernetes.discovery.enabled" },
|
||||
matchIfMissing = true)
|
||||
@EnableConfigurationProperties({ DiscoveryClientHealthIndicatorProperties.class,
|
||||
KubernetesDiscoveryClientProperties.class })
|
||||
public class KubernetesDiscoveryClientAutoConfiguration {
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
public static class Servlet {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingClass("org.springframework.web.reactive.function.client.WebClient")
|
||||
public RestTemplate restTemplate() {
|
||||
return new RestTemplateBuilder().build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingClass("org.springframework.web.reactive.function.client.WebClient")
|
||||
public DiscoveryClient kubernetesDiscoveryClient(RestTemplate restTemplate,
|
||||
KubernetesDiscoveryClientProperties properties) {
|
||||
return new KubernetesDiscoveryClient(restTemplate, properties);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnClass({ HealthIndicator.class })
|
||||
@ConditionalOnDiscoveryHealthIndicatorEnabled
|
||||
public InitializingBean indicatorInitializer(ApplicationEventPublisher applicationEventPublisher,
|
||||
ApplicationContext applicationContext) {
|
||||
return () -> applicationEventPublisher
|
||||
.publishEvent(new InstanceRegisteredEvent<>(applicationContext.getId(), null));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnReactiveDiscoveryEnabled
|
||||
public static class Reactive {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnClass(name = { "org.springframework.web.reactive.function.client.WebClient" })
|
||||
@ConditionalOnMissingBean(WebClient.Builder.class)
|
||||
public WebClient.Builder webClientBuilder() {
|
||||
return WebClient.builder();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnClass(name = { "org.springframework.web.reactive.function.client.WebClient" })
|
||||
public ReactiveDiscoveryClient kubernetesReactiveDiscoveryClient(WebClient.Builder webClientBuilder,
|
||||
KubernetesDiscoveryClientProperties properties) {
|
||||
return new KubernetesReactiveDiscoveryClient(webClientBuilder, properties);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnClass(name = "org.springframework.boot.actuate.health.ReactiveHealthIndicator")
|
||||
@ConditionalOnDiscoveryHealthIndicatorEnabled
|
||||
public ReactiveDiscoveryClientHealthIndicator kubernetesReactiveDiscoveryClientHealthIndicator(
|
||||
KubernetesReactiveDiscoveryClient client, DiscoveryClientHealthIndicatorProperties properties,
|
||||
ApplicationContext applicationContext) {
|
||||
ReactiveDiscoveryClientHealthIndicator healthIndicator = new ReactiveDiscoveryClientHealthIndicator(client,
|
||||
properties);
|
||||
InstanceRegisteredEvent event = new InstanceRegisteredEvent(applicationContext.getId(), null);
|
||||
healthIndicator.onApplicationEvent(event);
|
||||
return healthIndicator;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright 2013-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.kubernetes.discovery;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
/**
|
||||
* @author Ryan Baxter
|
||||
*/
|
||||
@ConfigurationProperties("spring.cloud.kubernetes.discovery")
|
||||
public class KubernetesDiscoveryClientProperties {
|
||||
|
||||
private String discoveryServerUrl;
|
||||
|
||||
private boolean enabled = true;
|
||||
|
||||
public String getDiscoveryServerUrl() {
|
||||
return discoveryServerUrl;
|
||||
}
|
||||
|
||||
public void setDiscoveryServerUrl(String discoveryServerUrl) {
|
||||
this.discoveryServerUrl = discoveryServerUrl;
|
||||
}
|
||||
|
||||
public boolean isEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
public void setEnabled(boolean enabled) {
|
||||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright 2013-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.kubernetes.discovery;
|
||||
|
||||
import reactor.core.publisher.Flux;
|
||||
|
||||
import org.springframework.cache.annotation.Cacheable;
|
||||
import org.springframework.cloud.client.ServiceInstance;
|
||||
import org.springframework.cloud.client.discovery.ReactiveDiscoveryClient;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.reactive.function.client.WebClient;
|
||||
|
||||
/**
|
||||
* @author Ryan Baxter
|
||||
*/
|
||||
public class KubernetesReactiveDiscoveryClient implements ReactiveDiscoveryClient {
|
||||
|
||||
private WebClient webClient;
|
||||
|
||||
public KubernetesReactiveDiscoveryClient(WebClient.Builder webClientBuilder,
|
||||
KubernetesDiscoveryClientProperties properties) {
|
||||
if (!StringUtils.hasText(properties.getDiscoveryServerUrl())) {
|
||||
throw new DiscoveryServerUrlInvalidException();
|
||||
}
|
||||
this.webClient = webClientBuilder.baseUrl(properties.getDiscoveryServerUrl()).build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String description() {
|
||||
return "Reactive Kubernetes Discovery Client";
|
||||
}
|
||||
|
||||
@Override
|
||||
@Cacheable("serviceinstances")
|
||||
public Flux<ServiceInstance> getInstances(String serviceId) {
|
||||
return webClient.get().uri("/apps/" + serviceId)
|
||||
.exchangeToFlux(clientResponse -> clientResponse.bodyToFlux(KubernetesServiceInstance.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
@Cacheable("services")
|
||||
public Flux<String> getServices() {
|
||||
return webClient.get().uri("/apps").exchangeToFlux(
|
||||
clientResponse -> clientResponse.bodyToFlux(Service.class).map(service -> service.getName()));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,166 @@
|
||||
/*
|
||||
* Copyright 2013-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.kubernetes.discovery;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
import org.springframework.cloud.client.ServiceInstance;
|
||||
|
||||
/**
|
||||
* @author Ryan Baxter
|
||||
*/
|
||||
public class KubernetesServiceInstance implements ServiceInstance {
|
||||
|
||||
private String instanceId;
|
||||
|
||||
private String serviceId;
|
||||
|
||||
private String host;
|
||||
|
||||
private int port;
|
||||
|
||||
private boolean secure;
|
||||
|
||||
private URI uri;
|
||||
|
||||
private Map<String, String> metadata;
|
||||
|
||||
private String scheme;
|
||||
|
||||
private String namespace;
|
||||
|
||||
public KubernetesServiceInstance() {
|
||||
}
|
||||
|
||||
public KubernetesServiceInstance(String instanceId, String serviceId, String host, int port, boolean secure,
|
||||
URI uri, Map<String, String> metadata, String scheme, String namespace) {
|
||||
this.instanceId = instanceId;
|
||||
this.serviceId = serviceId;
|
||||
this.host = host;
|
||||
this.port = port;
|
||||
this.secure = secure;
|
||||
this.uri = uri;
|
||||
this.metadata = metadata;
|
||||
this.scheme = scheme;
|
||||
this.namespace = namespace;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getInstanceId() {
|
||||
return instanceId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getServiceId() {
|
||||
return serviceId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHost() {
|
||||
return host;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPort() {
|
||||
return port;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSecure() {
|
||||
return secure;
|
||||
}
|
||||
|
||||
@Override
|
||||
public URI getUri() {
|
||||
return uri;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String> getMetadata() {
|
||||
return metadata;
|
||||
}
|
||||
|
||||
public void setInstanceId(String instanceId) {
|
||||
this.instanceId = instanceId;
|
||||
}
|
||||
|
||||
public void setServiceId(String serviceId) {
|
||||
this.serviceId = serviceId;
|
||||
}
|
||||
|
||||
public void setHost(String host) {
|
||||
this.host = host;
|
||||
}
|
||||
|
||||
public void setPort(int port) {
|
||||
this.port = port;
|
||||
}
|
||||
|
||||
public void setSecure(boolean secure) {
|
||||
this.secure = secure;
|
||||
}
|
||||
|
||||
public void setUri(URI uri) {
|
||||
this.uri = uri;
|
||||
}
|
||||
|
||||
public void setMetadata(Map<String, String> metadata) {
|
||||
this.metadata = metadata;
|
||||
}
|
||||
|
||||
public void setScheme(String scheme) {
|
||||
this.scheme = scheme;
|
||||
}
|
||||
|
||||
public String getNamespace() {
|
||||
return namespace;
|
||||
}
|
||||
|
||||
public void setNamespace(String namespace) {
|
||||
this.namespace = namespace;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getScheme() {
|
||||
return scheme;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
KubernetesServiceInstance that = (KubernetesServiceInstance) o;
|
||||
return getPort() == that.getPort() && isSecure() == that.isSecure()
|
||||
&& Objects.equals(getInstanceId(), that.getInstanceId())
|
||||
&& Objects.equals(getServiceId(), that.getServiceId()) && Objects.equals(getHost(), that.getHost())
|
||||
&& Objects.equals(getUri(), that.getUri()) && Objects.equals(getMetadata(), that.getMetadata())
|
||||
&& Objects.equals(getScheme(), that.getScheme()) && Objects.equals(getNamespace(), that.getNamespace());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(getInstanceId(), getServiceId(), getHost(), getPort(), isSecure(), getUri(), getMetadata(),
|
||||
getScheme(), getNamespace());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright 2013-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.kubernetes.discovery;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author Ryan Baxter
|
||||
*/
|
||||
public class Service {
|
||||
|
||||
private String name;
|
||||
|
||||
private List<KubernetesServiceInstance> serviceInstances;
|
||||
|
||||
public Service() {
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public List<KubernetesServiceInstance> getServiceInstances() {
|
||||
return serviceInstances;
|
||||
}
|
||||
|
||||
public void setServiceInstances(List<KubernetesServiceInstance> serviceInstances) {
|
||||
this.serviceInstances = serviceInstances;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
|
||||
org.springframework.cloud.kubernetes.discovery.KubernetesDiscoveryClientAutoConfiguration
|
||||
@@ -0,0 +1,113 @@
|
||||
/*
|
||||
* Copyright 2013-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.kubernetes.discovery;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.boot.autoconfigure.AutoConfigurations;
|
||||
import org.springframework.boot.test.context.FilteredClassLoader;
|
||||
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
|
||||
import org.springframework.cloud.client.ReactiveCommonsClientAutoConfiguration;
|
||||
import org.springframework.cloud.client.discovery.DiscoveryClient;
|
||||
import org.springframework.cloud.client.discovery.ReactiveDiscoveryClient;
|
||||
import org.springframework.cloud.client.discovery.health.reactive.ReactiveDiscoveryClientHealthIndicator;
|
||||
import org.springframework.cloud.commons.util.UtilAutoConfiguration;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* @author Ryan Baxter
|
||||
*/
|
||||
class KubernetesDiscoveryClientAutoConfigurationTests {
|
||||
|
||||
private ApplicationContextRunner contextRunner = new ApplicationContextRunner()
|
||||
.withConfiguration(AutoConfigurations.of(UtilAutoConfiguration.class,
|
||||
ReactiveCommonsClientAutoConfiguration.class, KubernetesDiscoveryClientAutoConfiguration.class));
|
||||
|
||||
@Test
|
||||
public void shouldWorkWithDefaults() {
|
||||
contextRunner
|
||||
.withPropertyValues("spring.cloud.kubernetes.discovery.discovery-server-url=http://k8sdiscoveryserver")
|
||||
.withClassLoader(new FilteredClassLoader("org.springframework.web.reactive")).run(context -> {
|
||||
assertThat(context).hasSingleBean(DiscoveryClient.class);
|
||||
assertThat(context).doesNotHaveBean(ReactiveDiscoveryClient.class);
|
||||
assertThat(context).doesNotHaveBean(ReactiveDiscoveryClientHealthIndicator.class);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldNotHaveDiscoveryClientWhenDiscoveryDisabled() {
|
||||
contextRunner
|
||||
.withPropertyValues("spring.cloud.discovery.enabled=false",
|
||||
"spring.cloud.kubernetes.discovery.discovery-server-url=http://k8sdiscoveryserver")
|
||||
.run(context -> {
|
||||
assertThat(context).doesNotHaveBean(ReactiveDiscoveryClient.class);
|
||||
assertThat(context).doesNotHaveBean(DiscoveryClient.class);
|
||||
assertThat(context).doesNotHaveBean(ReactiveDiscoveryClientHealthIndicator.class);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldNotHaveDiscoveryClientWhenKubernetesDiscoveryDisabled() {
|
||||
contextRunner
|
||||
.withPropertyValues("spring.cloud.kubernetes.discovery.enabled=false",
|
||||
"spring.cloud.kubernetes.discovery.discovery-server-url=http://k8sdiscoveryserver")
|
||||
.run(context -> {
|
||||
assertThat(context).doesNotHaveBean(ReactiveDiscoveryClient.class);
|
||||
assertThat(context).doesNotHaveBean(DiscoveryClient.class);
|
||||
assertThat(context).doesNotHaveBean(ReactiveDiscoveryClientHealthIndicator.class);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldHaveReactiveDiscoveryClient() {
|
||||
contextRunner
|
||||
.withPropertyValues("spring.cloud.kubernetes.discovery.discovery-server-url=http://k8sdiscoveryserver")
|
||||
.run(context -> {
|
||||
assertThat(context).hasSingleBean(ReactiveDiscoveryClient.class);
|
||||
assertThat(context).doesNotHaveBean(DiscoveryClient.class);
|
||||
assertThat(context).hasSingleBean(ReactiveDiscoveryClientHealthIndicator.class);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldNotHaveDiscoveryClientWhenReactiveDiscoveryDisabled() {
|
||||
contextRunner.withPropertyValues("spring.cloud.discovery.reactive.enabled=false").run(context -> {
|
||||
assertThat(context).doesNotHaveBean(ReactiveDiscoveryClient.class);
|
||||
assertThat(context).doesNotHaveBean(ReactiveDiscoveryClientHealthIndicator.class);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldNotHaveDiscoveryClientWhenKubernetesDisabled() {
|
||||
contextRunner.withPropertyValues("spring.cloud.kubernetes.enabled=false").run(context -> {
|
||||
assertThat(context).doesNotHaveBean(ReactiveDiscoveryClient.class);
|
||||
assertThat(context).doesNotHaveBean(ReactiveDiscoveryClientHealthIndicator.class);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void worksWithoutActuator() {
|
||||
contextRunner
|
||||
.withPropertyValues("spring.cloud.kubernetes.discovery.discovery-server-url=http://k8sdiscoveryserver")
|
||||
.withClassLoader(new FilteredClassLoader("org.springframework.boot.actuate")).run(context -> {
|
||||
assertThat(context).hasSingleBean(ReactiveDiscoveryClient.class);
|
||||
assertThat(context).doesNotHaveBean(ReactiveDiscoveryClientHealthIndicator.class);
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
* Copyright 2013-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.kubernetes.discovery;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import com.github.tomakehurst.wiremock.WireMockServer;
|
||||
import com.github.tomakehurst.wiremock.client.WireMock;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.boot.web.client.RestTemplateBuilder;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
import static com.github.tomakehurst.wiremock.client.WireMock.aResponse;
|
||||
import static com.github.tomakehurst.wiremock.client.WireMock.get;
|
||||
import static com.github.tomakehurst.wiremock.client.WireMock.stubFor;
|
||||
import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* @author Ryan Baxter
|
||||
*/
|
||||
class KubernetesDiscoveryClientTests {
|
||||
|
||||
private static final String APPS = "[{\"name\":\"test-svc-1\",\"serviceInstances\":[{\"instanceId\":\"uid1\",\"serviceId\":\"test-svc-1\",\"host\":\"2.2.2.2\",\"port\":8080,\"uri\":\"http://2.2.2.2:8080\",\"secure\":false,\"metadata\":{\"http\":\"8080\"},\"namespace\":\"namespace1\",\"cluster\":null,\"scheme\":\"http\"}]},{\"name\":\"test-svc-3\",\"serviceInstances\":[{\"instanceId\":\"uid2\",\"serviceId\":\"test-svc-3\",\"host\":\"2.2.2.2\",\"port\":8080,\"uri\":\"http://2.2.2.2:8080\",\"secure\":false,\"metadata\":{\"spring\":\"true\",\"http\":\"8080\",\"k8s\":\"true\"},\"namespace\":\"namespace1\",\"cluster\":null,\"scheme\":\"http\"}]}]";
|
||||
|
||||
private static final String APPS_NAME = "[{\"instanceId\":\"uid2\",\"serviceId\":\"test-svc-3\",\"host\":\"2.2.2.2\",\"port\":8080,\"uri\":\"http://2.2.2.2:8080\",\"secure\":false,\"metadata\":{\"spring\":\"true\",\"http\":\"8080\",\"k8s\":\"true\"},\"namespace\":\"namespace1\",\"cluster\":null,\"scheme\":\"http\"}]";
|
||||
|
||||
private static WireMockServer wireMockServer;
|
||||
|
||||
@BeforeAll
|
||||
static void beforeAll() {
|
||||
wireMockServer = new WireMockServer(options().dynamicPort());
|
||||
wireMockServer.start();
|
||||
WireMock.configureFor(wireMockServer.port());
|
||||
stubFor(get("/apps")
|
||||
.willReturn(aResponse().withStatus(200).withBody(APPS).withHeader("content-type", "application/json")));
|
||||
stubFor(get("/apps/test-svc-3").willReturn(
|
||||
aResponse().withStatus(200).withBody(APPS_NAME).withHeader("content-type", "application/json")));
|
||||
stubFor(get("/apps/does-not-exist")
|
||||
.willReturn(aResponse().withStatus(200).withBody("").withHeader("content-type", "application/json")));
|
||||
}
|
||||
|
||||
@Test
|
||||
void getInstances() {
|
||||
RestTemplate rest = new RestTemplateBuilder().build();
|
||||
KubernetesDiscoveryClientProperties properties = new KubernetesDiscoveryClientProperties();
|
||||
properties.setDiscoveryServerUrl(wireMockServer.baseUrl());
|
||||
KubernetesDiscoveryClient discoveryClient = new KubernetesDiscoveryClient(rest, properties);
|
||||
assertThat(discoveryClient.getServices()).contains("test-svc-1", "test-svc-3");
|
||||
}
|
||||
|
||||
@Test
|
||||
void getServices() {
|
||||
RestTemplate rest = new RestTemplateBuilder().build();
|
||||
KubernetesDiscoveryClientProperties properties = new KubernetesDiscoveryClientProperties();
|
||||
properties.setDiscoveryServerUrl(wireMockServer.baseUrl());
|
||||
KubernetesDiscoveryClient discoveryClient = new KubernetesDiscoveryClient(rest, properties);
|
||||
Map<String, String> metadata = new HashMap<>();
|
||||
metadata.put("spring", "true");
|
||||
metadata.put("http", "8080");
|
||||
metadata.put("k8s", "true");
|
||||
assertThat(discoveryClient.getInstances("test-svc-3"))
|
||||
.contains(new KubernetesServiceInstance("uid2", "test-svc-3", "2.2.2.2", 8080, false,
|
||||
URI.create("http://2.2.2.2:8080"), metadata, "http", "namespace1"));
|
||||
assertThat(discoveryClient.getInstances("does-not-exist")).isEmpty();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
* Copyright 2013-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.kubernetes.discovery;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import com.github.tomakehurst.wiremock.WireMockServer;
|
||||
import com.github.tomakehurst.wiremock.client.WireMock;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import reactor.test.StepVerifier;
|
||||
|
||||
import org.springframework.web.reactive.function.client.WebClient;
|
||||
|
||||
import static com.github.tomakehurst.wiremock.client.WireMock.aResponse;
|
||||
import static com.github.tomakehurst.wiremock.client.WireMock.get;
|
||||
import static com.github.tomakehurst.wiremock.client.WireMock.stubFor;
|
||||
import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options;
|
||||
|
||||
/**
|
||||
* @author Ryan Baxter
|
||||
*/
|
||||
class KubernetesReactiveDiscoveryClientTests {
|
||||
|
||||
private static final String APPS = "[{\"name\":\"test-svc-1\",\"serviceInstances\":[{\"instanceId\":\"uid1\",\"serviceId\":\"test-svc-1\",\"host\":\"2.2.2.2\",\"port\":8080,\"uri\":\"http://2.2.2.2:8080\",\"secure\":false,\"metadata\":{\"http\":\"8080\"},\"namespace\":\"namespace1\",\"cluster\":null,\"scheme\":\"http\"}]},{\"name\":\"test-svc-3\",\"serviceInstances\":[{\"instanceId\":\"uid2\",\"serviceId\":\"test-svc-3\",\"host\":\"2.2.2.2\",\"port\":8080,\"uri\":\"http://2.2.2.2:8080\",\"secure\":false,\"metadata\":{\"spring\":\"true\",\"http\":\"8080\",\"k8s\":\"true\"},\"namespace\":\"namespace1\",\"cluster\":null,\"scheme\":\"http\"}]}]";
|
||||
|
||||
private static final String APPS_NAME = "[{\"instanceId\":\"uid2\",\"serviceId\":\"test-svc-3\",\"host\":\"2.2.2.2\",\"port\":8080,\"uri\":\"http://2.2.2.2:8080\",\"secure\":false,\"metadata\":{\"spring\":\"true\",\"http\":\"8080\",\"k8s\":\"true\"},\"namespace\":\"namespace1\",\"cluster\":null,\"scheme\":\"http\"}]";
|
||||
|
||||
private static WireMockServer wireMockServer;
|
||||
|
||||
@BeforeAll
|
||||
static void beforeAll() {
|
||||
wireMockServer = new WireMockServer(options().dynamicPort());
|
||||
wireMockServer.start();
|
||||
WireMock.configureFor(wireMockServer.port());
|
||||
stubFor(get("/apps")
|
||||
.willReturn(aResponse().withStatus(200).withBody(APPS).withHeader("content-type", "application/json")));
|
||||
stubFor(get("/apps/test-svc-3").willReturn(
|
||||
aResponse().withStatus(200).withBody(APPS_NAME).withHeader("content-type", "application/json")));
|
||||
stubFor(get("/apps/does-not-exist")
|
||||
.willReturn(aResponse().withStatus(200).withBody("").withHeader("content-type", "application/json")));
|
||||
}
|
||||
|
||||
@Test
|
||||
void getInstances() {
|
||||
KubernetesDiscoveryClientProperties properties = new KubernetesDiscoveryClientProperties();
|
||||
properties.setDiscoveryServerUrl(wireMockServer.baseUrl());
|
||||
KubernetesReactiveDiscoveryClient discoveryClient = new KubernetesReactiveDiscoveryClient(WebClient.builder(),
|
||||
properties);
|
||||
StepVerifier.create(discoveryClient.getServices()).expectNext("test-svc-1", "test-svc-3").verifyComplete();
|
||||
}
|
||||
|
||||
@Test
|
||||
void getServices() {
|
||||
KubernetesDiscoveryClientProperties properties = new KubernetesDiscoveryClientProperties();
|
||||
properties.setDiscoveryServerUrl(wireMockServer.baseUrl());
|
||||
KubernetesReactiveDiscoveryClient discoveryClient = new KubernetesReactiveDiscoveryClient(WebClient.builder(),
|
||||
properties);
|
||||
Map<String, String> metadata = new HashMap<>();
|
||||
metadata.put("spring", "true");
|
||||
metadata.put("http", "8080");
|
||||
metadata.put("k8s", "true");
|
||||
StepVerifier.create(discoveryClient.getInstances("test-svc-3"))
|
||||
.expectNext(new KubernetesServiceInstance("uid2", "test-svc-3", "2.2.2.2", 8080, false,
|
||||
URI.create("http://2.2.2.2:8080"), metadata, "http", "namespace1"))
|
||||
.verifyComplete();
|
||||
StepVerifier.create(discoveryClient.getInstances("test-svc-3")).expectNextCount(0);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -30,6 +30,8 @@ ALL_INTEGRATION_PROJECTS=(
|
||||
"spring-cloud-kubernetes-configuration-watcher-it"
|
||||
"spring-cloud-kubernetes-client-loadbalancer-it"
|
||||
"spring-cloud-kubernetes-client-reactive-discovery-client-it"
|
||||
"spring-cloud-kubernetes-discoverclient-it"
|
||||
"spring-cloud-kubernetes-reactive-discoveryclient-it"
|
||||
)
|
||||
INTEGRATION_PROJECTS=(${INTEGRATION_PROJECTS:-${ALL_INTEGRATION_PROJECTS[@]}})
|
||||
|
||||
@@ -42,7 +44,8 @@ DEFAULT_PULLING_IMAGES=(
|
||||
)
|
||||
PULLING_IMAGES=(${PULLING_IMAGES:-${DEFAULT_PULLING_IMAGES[@]}})
|
||||
|
||||
LOADING_IMAGES=(${LOADING_IMAGES:-${DEFAULT_PULLING_IMAGES[@]}} "docker.io/springcloud/spring-cloud-kubernetes-configuration-watcher:${MVN_VERSION}")
|
||||
LOADING_IMAGES=(${LOADING_IMAGES:-${DEFAULT_PULLING_IMAGES[@]}} "docker.io/springcloud/spring-cloud-kubernetes-configuration-watcher:${MVN_VERSION}"
|
||||
"docker.io/springcloud/spring-cloud-kubernetes-discoveryserver:${MVN_VERSION}")
|
||||
# cleanup on exit (useful for running locally)
|
||||
cleanup() {
|
||||
"${KIND}" delete cluster || true
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
creationTimestamp: null
|
||||
labels:
|
||||
app: spring-cloud-kubernetes-discoveryclient-it
|
||||
name: spring-cloud-kubernetes-discoveryclient-it-deployment
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: spring-cloud-kubernetes-discoveryclient-it
|
||||
strategy: {}
|
||||
template:
|
||||
metadata:
|
||||
creationTimestamp: null
|
||||
labels:
|
||||
app: spring-cloud-kubernetes-discoveryclient-it
|
||||
spec:
|
||||
serviceAccountName: spring-cloud-kubernetes-serviceaccount
|
||||
containers:
|
||||
- image: springcloud/spring-cloud-kubernetes-discoveryclient-it:2.1.0-SNAPSHOT
|
||||
imagePullPolicy: IfNotPresent
|
||||
name: spring-cloud-kubernetes-discoveryclient-it
|
||||
resources: {}
|
||||
status: {}
|
||||
@@ -0,0 +1,18 @@
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
creationTimestamp: null
|
||||
labels:
|
||||
app: spring-cloud-kubernetes-discoveryclient-it
|
||||
name: spring-cloud-kubernetes-discoveryclient-it
|
||||
spec:
|
||||
ports:
|
||||
- name: 80-8080
|
||||
port: 80
|
||||
protocol: TCP
|
||||
targetPort: 8080
|
||||
selector:
|
||||
app: spring-cloud-kubernetes-discoveryclient-it
|
||||
type: ClusterIP
|
||||
status:
|
||||
loadBalancer: {}
|
||||
@@ -0,0 +1,143 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>spring-cloud-kubernetes-integration-tests</artifactId>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<version>2.1.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>spring-cloud-kubernetes-discoverclient-it</artifactId>
|
||||
|
||||
<properties>
|
||||
<jib.version>1.8.0</jib.version>
|
||||
<base.image>openjdk:8u222-slim</base.image>
|
||||
</properties>
|
||||
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-kubernetes-discoveryclient</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-actuator</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-kubernetes-test-support</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.kubernetes</groupId>
|
||||
<artifactId>client-java</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.kubernetes</groupId>
|
||||
<artifactId>client-java-extended</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.github.docker-java</groupId>
|
||||
<artifactId>docker-java-core</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.github.docker-java</groupId>
|
||||
<artifactId>docker-java-transport-httpclient5</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<resources>
|
||||
<resource>
|
||||
<directory>../src/main/resources</directory>
|
||||
<filtering>true</filtering>
|
||||
</resource>
|
||||
<resource>
|
||||
<directory>src/main/resources</directory>
|
||||
<filtering>true</filtering>
|
||||
</resource>
|
||||
</resources>
|
||||
</build>
|
||||
|
||||
<profiles>
|
||||
<profile>
|
||||
<id>skaffold</id>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
<configuration>
|
||||
<image>
|
||||
<name>${env.IMAGE}</name>
|
||||
</image>
|
||||
<goal>build-image</goal>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>build-image</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</profile>
|
||||
<profile>
|
||||
<id>imagename</id>
|
||||
<activation>
|
||||
<property>
|
||||
<name>!env.IMAGE</name>
|
||||
</property>
|
||||
</activation>
|
||||
<properties>
|
||||
<env.IMAGE>springcloud/${project.artifactId}:${project.version}</env.IMAGE>
|
||||
</properties>
|
||||
</profile>
|
||||
<profile>
|
||||
<id>jib</id>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>com.google.cloud.tools</groupId>
|
||||
<artifactId>jib-maven-plugin</artifactId>
|
||||
<version>${jib.version}</version>
|
||||
<configuration>
|
||||
<from>
|
||||
<image>${base.image}</image>
|
||||
</from>
|
||||
<to>
|
||||
<image>spring-cloud/${project.artifactId}</image>
|
||||
</to>
|
||||
<container>
|
||||
<user>nobody:nogroup</user>
|
||||
<environment>
|
||||
</environment>
|
||||
</container>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>dockerBuild</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</profile>
|
||||
</profiles>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,22 @@
|
||||
apiVersion: skaffold/v2alpha3
|
||||
kind: Config
|
||||
metadata:
|
||||
name: spring-cloud-kubernetes-discoveryclient-it
|
||||
build:
|
||||
artifacts:
|
||||
- image: springcloud/spring-cloud-kubernetes-discoveryclient-it
|
||||
jib: {
|
||||
args: [ "-Pjib" ]
|
||||
}
|
||||
# custom:
|
||||
# buildCommand: "../../mvnw clean install -Pskaffold"
|
||||
# dependencies:
|
||||
# paths:
|
||||
# - src
|
||||
# - pom.xml
|
||||
deploy:
|
||||
kubectl:
|
||||
manifests:
|
||||
- k8s/deployment-it.yaml
|
||||
- k8s/service-it.yaml
|
||||
- ../permissions.yaml
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright 2013-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.kubernetes.discoveryclient.it;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.cloud.client.ServiceInstance;
|
||||
import org.springframework.cloud.client.discovery.DiscoveryClient;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
* @author Ryan Baxter
|
||||
*/
|
||||
@SpringBootApplication
|
||||
@RestController
|
||||
public class KubernetesDiscoveryClientApplicationIt {
|
||||
|
||||
@Autowired
|
||||
DiscoveryClient discoveryClient;
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(KubernetesDiscoveryClientApplicationIt.class, args);
|
||||
}
|
||||
|
||||
@GetMapping("/services")
|
||||
public List<String> services() {
|
||||
return discoveryClient.getServices();
|
||||
}
|
||||
|
||||
@GetMapping("/service/{serviceId}")
|
||||
public List<ServiceInstance> service(@PathVariable String serviceId) {
|
||||
return discoveryClient.getInstances(serviceId);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
spring:
|
||||
cloud:
|
||||
kubernetes:
|
||||
discovery:
|
||||
discoveryServerUrl: http://spring-cloud-kubernetes-discoveryserver
|
||||
management:
|
||||
endpoint:
|
||||
health:
|
||||
show-details: always
|
||||
endpoints:
|
||||
web:
|
||||
exposure:
|
||||
include: "*"
|
||||
@@ -0,0 +1,237 @@
|
||||
/*
|
||||
* Copyright 2013-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.kubernetes.discoveryclient.it;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.time.Duration;
|
||||
import java.util.Arrays;
|
||||
import java.util.Map;
|
||||
|
||||
import io.kubernetes.client.openapi.ApiClient;
|
||||
import io.kubernetes.client.openapi.ApiException;
|
||||
import io.kubernetes.client.openapi.apis.AppsV1Api;
|
||||
import io.kubernetes.client.openapi.apis.CoreV1Api;
|
||||
import io.kubernetes.client.openapi.apis.NetworkingV1Api;
|
||||
import io.kubernetes.client.openapi.models.V1Deployment;
|
||||
import io.kubernetes.client.openapi.models.V1Ingress;
|
||||
import io.kubernetes.client.openapi.models.V1Service;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.junit.jupiter.api.AfterAll;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.boot.web.client.RestTemplateBuilder;
|
||||
import org.springframework.cloud.kubernetes.integration.tests.commons.K8SUtils;
|
||||
import org.springframework.http.client.ClientHttpResponse;
|
||||
import org.springframework.web.client.ResponseErrorHandler;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.awaitility.Awaitility.await;
|
||||
import static org.springframework.cloud.kubernetes.integration.tests.commons.K8SUtils.createApiClient;
|
||||
import static org.springframework.cloud.kubernetes.integration.tests.commons.K8SUtils.getPomVersion;
|
||||
|
||||
/**
|
||||
* @author Ryan Baxter
|
||||
*/
|
||||
public class DiscoveryClientIT {
|
||||
|
||||
private static final Log LOG = LogFactory.getLog(DiscoveryClientIT.class);
|
||||
|
||||
private static final String DISCOVERYSERVER_DEPLOYMENT_NAME = "spring-cloud-kubernetes-discoveryserver-deployment";
|
||||
|
||||
private static final String DISCOVERYSERVER_APP_NAME = "spring-cloud-kubernetes-discoveryserver";
|
||||
|
||||
private static final String SPRING_CLOUD_K8S_DISCOVERYCLIENT_DEPLOYMENT_NAME = "spring-cloud-kubernetes-discoveryclient-it-deployment";
|
||||
|
||||
private static final String SPRING_CLOUD_K8S_DISCOVERYCLIENT_APP_NAME = "spring-cloud-kubernetes-discoveryclient-it";
|
||||
|
||||
private static final String NAMESPACE = "default";
|
||||
|
||||
private static ApiClient client;
|
||||
|
||||
private static CoreV1Api api;
|
||||
|
||||
private static AppsV1Api appsApi;
|
||||
|
||||
private static NetworkingV1Api networkingApi;
|
||||
|
||||
private static K8SUtils k8SUtils;
|
||||
|
||||
@BeforeAll
|
||||
public static void setup() throws Exception {
|
||||
client = createApiClient();
|
||||
api = new CoreV1Api();
|
||||
appsApi = new AppsV1Api();
|
||||
networkingApi = new NetworkingV1Api();
|
||||
k8SUtils = new K8SUtils(api, appsApi);
|
||||
|
||||
deployDiscoveryServer();
|
||||
|
||||
// Check to make sure the discovery server deployment is ready
|
||||
k8SUtils.waitForDeployment(DISCOVERYSERVER_DEPLOYMENT_NAME, NAMESPACE);
|
||||
|
||||
// Check to see if endpoint is ready
|
||||
k8SUtils.waitForEndpointReady(DISCOVERYSERVER_APP_NAME, NAMESPACE);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDiscoveryClient() throws Exception {
|
||||
try {
|
||||
deployDiscoveryIt();
|
||||
testLoadBalancer();
|
||||
testHealth();
|
||||
}
|
||||
catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
finally {
|
||||
cleanup();
|
||||
}
|
||||
}
|
||||
|
||||
private void cleanup() throws ApiException {
|
||||
appsApi.deleteCollectionNamespacedDeployment(NAMESPACE, null, null, null,
|
||||
"metadata.name=" + SPRING_CLOUD_K8S_DISCOVERYCLIENT_DEPLOYMENT_NAME, null, null, null, null, null, null,
|
||||
null, null, null);
|
||||
api.deleteNamespacedService(SPRING_CLOUD_K8S_DISCOVERYCLIENT_APP_NAME, NAMESPACE, null, null, null, null, null,
|
||||
null);
|
||||
networkingApi.deleteNamespacedIngress("it-ingress", NAMESPACE, null, null, null, null, null, null);
|
||||
}
|
||||
|
||||
private void testLoadBalancer() throws Exception {
|
||||
// Check to make sure the controller deployment is ready
|
||||
k8SUtils.waitForDeployment(SPRING_CLOUD_K8S_DISCOVERYCLIENT_DEPLOYMENT_NAME, NAMESPACE);
|
||||
RestTemplate rest = createRestTemplate();
|
||||
// Sometimes the NGINX ingress takes a bit to catch up and realize the service is
|
||||
// available and we get a 503, we just need to wait a bit
|
||||
await().timeout(Duration.ofSeconds(60))
|
||||
.until(() -> rest.getForEntity("http://localhost:80/discoveryclient-it/services", String.class)
|
||||
.getStatusCode().is2xxSuccessful());
|
||||
String[] result = rest.getForObject("http://localhost:80/discoveryclient-it/services", String[].class);
|
||||
LOG.info("Services: " + result);
|
||||
assertThat(Arrays.stream(result)
|
||||
.anyMatch(s -> "spring-cloud-kubernetes-discoveryserver".equalsIgnoreCase(s))).isTrue();
|
||||
|
||||
}
|
||||
|
||||
private RestTemplate createRestTemplate() {
|
||||
RestTemplate rest = new RestTemplateBuilder().build();
|
||||
|
||||
rest.setErrorHandler(new ResponseErrorHandler() {
|
||||
@Override
|
||||
public boolean hasError(ClientHttpResponse clientHttpResponse) throws IOException {
|
||||
LOG.warn("Received response status code: " + clientHttpResponse.getRawStatusCode());
|
||||
if (clientHttpResponse.getRawStatusCode() == 503) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleError(ClientHttpResponse clientHttpResponse) throws IOException {
|
||||
|
||||
}
|
||||
});
|
||||
return rest;
|
||||
}
|
||||
|
||||
public void testHealth() {
|
||||
RestTemplate rest = createRestTemplate();
|
||||
|
||||
// Sometimes the NGINX ingress takes a bit to catch up and realize the service is
|
||||
// available and we get a 503, we just need to wait a bit
|
||||
await().timeout(Duration.ofSeconds(60))
|
||||
.until(() -> rest.getForEntity("http://localhost:80/discoveryclient-it/actuator/health", String.class)
|
||||
.getStatusCode().is2xxSuccessful());
|
||||
|
||||
Map<String, Object> health = rest.getForObject("http://localhost:80/discoveryclient-it/actuator/health",
|
||||
Map.class);
|
||||
Map<String, Object> components = (Map) health.get("components");
|
||||
|
||||
Map<String, Object> discoveryComposite = (Map) components.get("discoveryComposite");
|
||||
assertThat(discoveryComposite.get("status")).isEqualTo("UP");
|
||||
}
|
||||
|
||||
@AfterAll
|
||||
public static void after() throws Exception {
|
||||
appsApi.deleteCollectionNamespacedDeployment(NAMESPACE, null, null, null,
|
||||
"metadata.name=" + DISCOVERYSERVER_DEPLOYMENT_NAME, null, null, null, null, null, null, null, null,
|
||||
null);
|
||||
|
||||
api.deleteNamespacedService(DISCOVERYSERVER_APP_NAME, NAMESPACE, null, null, null, null, null, null);
|
||||
networkingApi.deleteNamespacedIngress("discoveryserver-ingress", NAMESPACE, null, null, null, null, null, null);
|
||||
|
||||
}
|
||||
|
||||
private void deployDiscoveryIt() throws Exception {
|
||||
appsApi.createNamespacedDeployment(NAMESPACE, getDiscoveryItDeployment(), null, null, null);
|
||||
api.createNamespacedService(NAMESPACE, getDiscoveryService(), null, null, null);
|
||||
networkingApi.createNamespacedIngress(NAMESPACE, getDiscoveryItIngress(), null, null, null);
|
||||
}
|
||||
|
||||
private V1Service getDiscoveryService() throws Exception {
|
||||
V1Service service = (V1Service) k8SUtils
|
||||
.readYamlFromClasspath("spring-cloud-kubernetes-discoveryclient-it-service.yaml");
|
||||
return service;
|
||||
}
|
||||
|
||||
private V1Deployment getDiscoveryItDeployment() throws Exception {
|
||||
V1Deployment deployment = (V1Deployment) k8SUtils
|
||||
.readYamlFromClasspath("spring-cloud-kubernetes-discoveryclient-it-deployment.yaml");
|
||||
String image = deployment.getSpec().getTemplate().getSpec().getContainers().get(0).getImage() + ":"
|
||||
+ getPomVersion();
|
||||
deployment.getSpec().getTemplate().getSpec().getContainers().get(0).setImage(image);
|
||||
return deployment;
|
||||
}
|
||||
|
||||
private V1Ingress getDiscoveryItIngress() throws Exception {
|
||||
V1Ingress ingress = (V1Ingress) k8SUtils
|
||||
.readYamlFromClasspath("spring-cloud-kubernetes-discoveryclient-it-ingress.yaml");
|
||||
return ingress;
|
||||
}
|
||||
|
||||
private static void deployDiscoveryServer() throws Exception {
|
||||
appsApi.createNamespacedDeployment(NAMESPACE, getDiscoveryServerDeployment(), null, null, null);
|
||||
api.createNamespacedService(NAMESPACE, getDiscoveryServerService(), null, null, null);
|
||||
networkingApi.createNamespacedIngress(NAMESPACE, getDiscoveryServerIngress(), null, null, null);
|
||||
}
|
||||
|
||||
private static V1Ingress getDiscoveryServerIngress() throws Exception {
|
||||
V1Ingress ingress = (V1Ingress) k8SUtils
|
||||
.readYamlFromClasspath("spring-cloud-kubernetes-discoveryserver-ingress.yaml");
|
||||
return ingress;
|
||||
}
|
||||
|
||||
private static V1Service getDiscoveryServerService() throws Exception {
|
||||
V1Service service = (V1Service) k8SUtils
|
||||
.readYamlFromClasspath("spring-cloud-kubernetes-discoveryserver-service.yaml");
|
||||
return service;
|
||||
}
|
||||
|
||||
private static V1Deployment getDiscoveryServerDeployment() throws Exception {
|
||||
V1Deployment deployment = (V1Deployment) k8SUtils
|
||||
.readYamlFromClasspath("spring-cloud-kubernetes-discoveryserver-deployment.yaml");
|
||||
String image = deployment.getSpec().getTemplate().getSpec().getContainers().get(0).getImage() + ":"
|
||||
+ getPomVersion();
|
||||
deployment.getSpec().getTemplate().getSpec().getContainers().get(0).setImage(image);
|
||||
return deployment;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: spring-cloud-kubernetes-discoveryclient-it-deployment
|
||||
spec:
|
||||
selector:
|
||||
matchLabels:
|
||||
app: spring-cloud-kubernetes-discoveryclient-it
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: spring-cloud-kubernetes-discoveryclient-it
|
||||
spec:
|
||||
serviceAccountName: spring-cloud-kubernetes-serviceaccount
|
||||
containers:
|
||||
- name: spring-cloud-kubernetes-discoveryclient-it
|
||||
image: docker.io/springcloud/spring-cloud-kubernetes-discoveryclient-it
|
||||
imagePullPolicy: IfNotPresent
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
port: 8080
|
||||
path: /actuator/health/readiness
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
port: 8080
|
||||
path: /actuator/health/liveness
|
||||
ports:
|
||||
- containerPort: 8080
|
||||
@@ -0,0 +1,18 @@
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: it-ingress
|
||||
namespace: default
|
||||
annotations:
|
||||
nginx.ingress.kubernetes.io/rewrite-target: /$2
|
||||
spec:
|
||||
rules:
|
||||
- http:
|
||||
paths:
|
||||
- path: /discoveryclient-it(/|$)(.*)
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
name: spring-cloud-kubernetes-discoveryclient-it
|
||||
port:
|
||||
number: 8080
|
||||
@@ -0,0 +1,14 @@
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
labels:
|
||||
app: spring-cloud-kubernetes-discoveryclient-it
|
||||
name: spring-cloud-kubernetes-discoveryclient-it
|
||||
spec:
|
||||
ports:
|
||||
- name: http
|
||||
port: 8080
|
||||
targetPort: 8080
|
||||
selector:
|
||||
app: spring-cloud-kubernetes-discoveryclient-it
|
||||
type: ClusterIP
|
||||
@@ -0,0 +1,28 @@
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: spring-cloud-kubernetes-discoveryserver-deployment
|
||||
spec:
|
||||
selector:
|
||||
matchLabels:
|
||||
app: spring-cloud-kubernetes-discoveryserver
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: spring-cloud-kubernetes-discoveryserver
|
||||
spec:
|
||||
serviceAccountName: spring-cloud-kubernetes-serviceaccount
|
||||
containers:
|
||||
- name: spring-cloud-kubernetes-discoveryserver
|
||||
image: docker.io/springcloud/spring-cloud-kubernetes-discoveryserver
|
||||
imagePullPolicy: IfNotPresent
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
port: 8761
|
||||
path: /actuator/health/readiness
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
port: 8761
|
||||
path: /actuator/health/liveness
|
||||
ports:
|
||||
- containerPort: 8761
|
||||
@@ -0,0 +1,18 @@
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: discoveryserver-ingress
|
||||
namespace: default
|
||||
annotations:
|
||||
nginx.ingress.kubernetes.io/rewrite-target: /$2
|
||||
spec:
|
||||
rules:
|
||||
- http:
|
||||
paths:
|
||||
- path: /discoveryserver(/|$)(.*)
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
name: spring-cloud-kubernetes-discoveryserver
|
||||
port:
|
||||
number: 80
|
||||
@@ -0,0 +1,14 @@
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
labels:
|
||||
app: spring-cloud-kubernetes-discoveryserver
|
||||
name: spring-cloud-kubernetes-discoveryserver
|
||||
spec:
|
||||
ports:
|
||||
- name: http
|
||||
port: 80
|
||||
targetPort: 8761
|
||||
selector:
|
||||
app: spring-cloud-kubernetes-discoveryserver
|
||||
type: ClusterIP
|
||||
@@ -0,0 +1,26 @@
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
creationTimestamp: null
|
||||
labels:
|
||||
app: spring-cloud-kubernetes-reactive-discoveryclient-it
|
||||
name: spring-cloud-kubernetes-reactive-discoveryclient-it-deployment
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: spring-cloud-kubernetes-reactive-discoveryclient-it
|
||||
strategy: {}
|
||||
template:
|
||||
metadata:
|
||||
creationTimestamp: null
|
||||
labels:
|
||||
app: spring-cloud-kubernetes-reactive-discoveryclient-it
|
||||
spec:
|
||||
serviceAccountName: spring-cloud-kubernetes-serviceaccount
|
||||
containers:
|
||||
- image: springcloud/spring-cloud-kubernetes-reactive-discoveryclient-it:2.0.4-SNAPSHOT
|
||||
imagePullPolicy: IfNotPresent
|
||||
name: spring-cloud-kubernetes-reactive-discoveryclient-it
|
||||
resources: {}
|
||||
status: {}
|
||||
@@ -0,0 +1,18 @@
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
creationTimestamp: null
|
||||
labels:
|
||||
app: spring-cloud-kubernetes-reactive-discoveryclient-it
|
||||
name: spring-cloud-kubernetes-reactive-discoveryclient-it
|
||||
spec:
|
||||
ports:
|
||||
- name: 80-8080
|
||||
port: 80
|
||||
protocol: TCP
|
||||
targetPort: 8080
|
||||
selector:
|
||||
app: spring-cloud-kubernetes-reactive-discoveryclient-it
|
||||
type: ClusterIP
|
||||
status:
|
||||
loadBalancer: {}
|
||||
@@ -0,0 +1,141 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>spring-cloud-kubernetes-integration-tests</artifactId>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<version>2.1.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>spring-cloud-kubernetes-reactive-discoveryclient-it</artifactId>
|
||||
|
||||
<properties>
|
||||
<jib.version>1.8.0</jib.version>
|
||||
<base.image>openjdk:8u222-slim</base.image>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-webflux</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-kubernetes-discoveryclient</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-actuator</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-kubernetes-test-support</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.kubernetes</groupId>
|
||||
<artifactId>client-java</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.kubernetes</groupId>
|
||||
<artifactId>client-java-extended</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.github.docker-java</groupId>
|
||||
<artifactId>docker-java-core</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.github.docker-java</groupId>
|
||||
<artifactId>docker-java-transport-httpclient5</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<resources>
|
||||
<resource>
|
||||
<directory>../src/main/resources</directory>
|
||||
<filtering>true</filtering>
|
||||
</resource>
|
||||
<resource>
|
||||
<directory>src/main/resources</directory>
|
||||
<filtering>true</filtering>
|
||||
</resource>
|
||||
</resources>
|
||||
</build>
|
||||
|
||||
<profiles>
|
||||
<profile>
|
||||
<id>skaffold</id>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
<configuration>
|
||||
<image>
|
||||
<name>${env.IMAGE}</name>
|
||||
</image>
|
||||
<goal>build-image</goal>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>build-image</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</profile>
|
||||
<profile>
|
||||
<id>imagename</id>
|
||||
<activation>
|
||||
<property>
|
||||
<name>!env.IMAGE</name>
|
||||
</property>
|
||||
</activation>
|
||||
<properties>
|
||||
<env.IMAGE>springcloud/${project.artifactId}:${project.version}</env.IMAGE>
|
||||
</properties>
|
||||
</profile>
|
||||
<profile>
|
||||
<id>jib</id>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>com.google.cloud.tools</groupId>
|
||||
<artifactId>jib-maven-plugin</artifactId>
|
||||
<version>${jib.version}</version>
|
||||
<configuration>
|
||||
<from>
|
||||
<image>${base.image}</image>
|
||||
</from>
|
||||
<to>
|
||||
<image>spring-cloud/${project.artifactId}</image>
|
||||
</to>
|
||||
<container>
|
||||
<user>nobody:nogroup</user>
|
||||
<environment>
|
||||
</environment>
|
||||
</container>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>dockerBuild</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</profile>
|
||||
</profiles>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,22 @@
|
||||
apiVersion: skaffold/v2alpha3
|
||||
kind: Config
|
||||
metadata:
|
||||
name: spring-cloud-kubernetes-reactive-discoveryclient-it
|
||||
build:
|
||||
artifacts:
|
||||
- image: springcloud/spring-cloud-kubernetes-reactive-discoveryclient-it
|
||||
jib: {
|
||||
args: [ "-Pjib" ]
|
||||
}
|
||||
# custom:
|
||||
# buildCommand: "../../mvnw clean install -Pskaffold"
|
||||
# dependencies:
|
||||
# paths:
|
||||
# - src
|
||||
# - pom.xml
|
||||
deploy:
|
||||
kubectl:
|
||||
manifests:
|
||||
- k8s/deployment-it.yaml
|
||||
- k8s/service-it.yaml
|
||||
- ../permissions.yaml
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright 2013-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.kubernetes.reactive.discoveryclient.it;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.cloud.client.ServiceInstance;
|
||||
import org.springframework.cloud.client.discovery.ReactiveDiscoveryClient;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
* @author Ryan Baxter
|
||||
*/
|
||||
@SpringBootApplication
|
||||
@RestController
|
||||
public class KubernetesReactiveDiscoveryClientApplicationIt {
|
||||
|
||||
@Autowired
|
||||
ReactiveDiscoveryClient discoveryClient;
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(KubernetesReactiveDiscoveryClientApplicationIt.class, args);
|
||||
}
|
||||
|
||||
@GetMapping("/services")
|
||||
public Mono<List<String>> services() {
|
||||
return discoveryClient.getServices().collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@GetMapping("/service/{serviceId}")
|
||||
public Flux<ServiceInstance> service(@PathVariable String serviceId) {
|
||||
return discoveryClient.getInstances(serviceId);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
spring:
|
||||
cloud:
|
||||
kubernetes:
|
||||
discovery:
|
||||
discoveryServerUrl: http://spring-cloud-kubernetes-discoveryserver
|
||||
management:
|
||||
endpoint:
|
||||
health:
|
||||
show-details: always
|
||||
endpoints:
|
||||
web:
|
||||
exposure:
|
||||
include: "*"
|
||||
@@ -0,0 +1,237 @@
|
||||
/*
|
||||
* Copyright 2013-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.kubernetes.reactive.discoveryclient.it;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.time.Duration;
|
||||
import java.util.Arrays;
|
||||
import java.util.Map;
|
||||
|
||||
import io.kubernetes.client.openapi.ApiClient;
|
||||
import io.kubernetes.client.openapi.ApiException;
|
||||
import io.kubernetes.client.openapi.apis.AppsV1Api;
|
||||
import io.kubernetes.client.openapi.apis.CoreV1Api;
|
||||
import io.kubernetes.client.openapi.apis.NetworkingV1Api;
|
||||
import io.kubernetes.client.openapi.models.V1Deployment;
|
||||
import io.kubernetes.client.openapi.models.V1Ingress;
|
||||
import io.kubernetes.client.openapi.models.V1Service;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.junit.jupiter.api.AfterAll;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.boot.web.client.RestTemplateBuilder;
|
||||
import org.springframework.cloud.kubernetes.integration.tests.commons.K8SUtils;
|
||||
import org.springframework.http.client.ClientHttpResponse;
|
||||
import org.springframework.web.client.ResponseErrorHandler;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.awaitility.Awaitility.await;
|
||||
import static org.springframework.cloud.kubernetes.integration.tests.commons.K8SUtils.createApiClient;
|
||||
import static org.springframework.cloud.kubernetes.integration.tests.commons.K8SUtils.getPomVersion;
|
||||
|
||||
/**
|
||||
* @author Ryan Baxter
|
||||
*/
|
||||
class ReactiveDiscoveryClientIT {
|
||||
|
||||
private static final Log LOG = LogFactory.getLog(ReactiveDiscoveryClientIT.class);
|
||||
|
||||
private static final String DISCOVERYSERVER_DEPLOYMENT_NAME = "spring-cloud-kubernetes-discoveryserver-deployment";
|
||||
|
||||
private static final String DISCOVERYSERVER_APP_NAME = "spring-cloud-kubernetes-discoveryserver";
|
||||
|
||||
private static final String SPRING_CLOUD_K8S_DISCOVERYCLIENT_DEPLOYMENT_NAME = "spring-cloud-kubernetes-discoveryclient-it-deployment";
|
||||
|
||||
private static final String SPRING_CLOUD_K8S_DISCOVERYCLIENT_APP_NAME = "spring-cloud-kubernetes-discoveryclient-it";
|
||||
|
||||
private static final String NAMESPACE = "default";
|
||||
|
||||
private static ApiClient client;
|
||||
|
||||
private static CoreV1Api api;
|
||||
|
||||
private static AppsV1Api appsApi;
|
||||
|
||||
private static NetworkingV1Api networkingApi;
|
||||
|
||||
private static K8SUtils k8SUtils;
|
||||
|
||||
@BeforeAll
|
||||
public static void setup() throws Exception {
|
||||
client = createApiClient();
|
||||
api = new CoreV1Api();
|
||||
appsApi = new AppsV1Api();
|
||||
networkingApi = new NetworkingV1Api();
|
||||
k8SUtils = new K8SUtils(api, appsApi);
|
||||
|
||||
deployDiscoveryServer();
|
||||
|
||||
// Check to make sure the discovery server deployment is ready
|
||||
k8SUtils.waitForDeployment(DISCOVERYSERVER_DEPLOYMENT_NAME, NAMESPACE);
|
||||
|
||||
// Check to see if endpoint is ready
|
||||
k8SUtils.waitForEndpointReady(DISCOVERYSERVER_APP_NAME, NAMESPACE);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDiscoveryClient() throws Exception {
|
||||
try {
|
||||
deployDiscoveryIt();
|
||||
testLoadBalancer();
|
||||
testHealth();
|
||||
}
|
||||
catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
finally {
|
||||
cleanup();
|
||||
}
|
||||
}
|
||||
|
||||
private void cleanup() throws ApiException {
|
||||
appsApi.deleteCollectionNamespacedDeployment(NAMESPACE, null, null, null,
|
||||
"metadata.name=" + SPRING_CLOUD_K8S_DISCOVERYCLIENT_DEPLOYMENT_NAME, null, null, null, null, null, null,
|
||||
null, null, null);
|
||||
api.deleteNamespacedService(SPRING_CLOUD_K8S_DISCOVERYCLIENT_APP_NAME, NAMESPACE, null, null, null, null, null,
|
||||
null);
|
||||
networkingApi.deleteNamespacedIngress("it-ingress", NAMESPACE, null, null, null, null, null, null);
|
||||
}
|
||||
|
||||
private void testLoadBalancer() throws Exception {
|
||||
// Check to make sure the controller deployment is ready
|
||||
k8SUtils.waitForDeployment(SPRING_CLOUD_K8S_DISCOVERYCLIENT_DEPLOYMENT_NAME, NAMESPACE);
|
||||
RestTemplate rest = createRestTemplate();
|
||||
// Sometimes the NGINX ingress takes a bit to catch up and realize the service is
|
||||
// available and we get a 503, we just need to wait a bit
|
||||
await().timeout(Duration.ofSeconds(60))
|
||||
.until(() -> rest.getForEntity("http://localhost:80/discoveryclient-it/services", String.class)
|
||||
.getStatusCode().is2xxSuccessful());
|
||||
String[] result = rest.getForObject("http://localhost:80/discoveryclient-it/services", String[].class);
|
||||
LOG.info("Services: " + result);
|
||||
assertThat(Arrays.stream(result).anyMatch(s -> "spring-cloud-kubernetes-discoveryserver".equalsIgnoreCase(s)))
|
||||
.isTrue();
|
||||
|
||||
}
|
||||
|
||||
private RestTemplate createRestTemplate() {
|
||||
RestTemplate rest = new RestTemplateBuilder().build();
|
||||
|
||||
rest.setErrorHandler(new ResponseErrorHandler() {
|
||||
@Override
|
||||
public boolean hasError(ClientHttpResponse clientHttpResponse) throws IOException {
|
||||
LOG.warn("Received response status code: " + clientHttpResponse.getRawStatusCode());
|
||||
if (clientHttpResponse.getRawStatusCode() == 503) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleError(ClientHttpResponse clientHttpResponse) throws IOException {
|
||||
|
||||
}
|
||||
});
|
||||
return rest;
|
||||
}
|
||||
|
||||
public void testHealth() {
|
||||
RestTemplate rest = createRestTemplate();
|
||||
|
||||
// Sometimes the NGINX ingress takes a bit to catch up and realize the service is
|
||||
// available and we get a 503, we just need to wait a bit
|
||||
await().timeout(Duration.ofSeconds(60))
|
||||
.until(() -> rest.getForEntity("http://localhost:80/discoveryclient-it/actuator/health", String.class)
|
||||
.getStatusCode().is2xxSuccessful());
|
||||
|
||||
Map<String, Object> health = rest.getForObject("http://localhost:80/discoveryclient-it/actuator/health",
|
||||
Map.class);
|
||||
Map<String, Object> components = (Map) health.get("components");
|
||||
|
||||
Map<String, Object> discoveryComposite = (Map) components.get("discoveryComposite");
|
||||
assertThat(discoveryComposite.get("status")).isEqualTo("UP");
|
||||
}
|
||||
|
||||
@AfterAll
|
||||
public static void after() throws Exception {
|
||||
appsApi.deleteCollectionNamespacedDeployment(NAMESPACE, null, null, null,
|
||||
"metadata.name=" + DISCOVERYSERVER_DEPLOYMENT_NAME, null, null, null, null, null, null, null, null,
|
||||
null);
|
||||
|
||||
api.deleteNamespacedService(DISCOVERYSERVER_APP_NAME, NAMESPACE, null, null, null, null, null, null);
|
||||
networkingApi.deleteNamespacedIngress("discoveryserver-ingress", NAMESPACE, null, null, null, null, null, null);
|
||||
|
||||
}
|
||||
|
||||
private void deployDiscoveryIt() throws Exception {
|
||||
appsApi.createNamespacedDeployment(NAMESPACE, getDiscoveryItDeployment(), null, null, null);
|
||||
api.createNamespacedService(NAMESPACE, getDiscoveryService(), null, null, null);
|
||||
networkingApi.createNamespacedIngress(NAMESPACE, getDiscoveryItIngress(), null, null, null);
|
||||
}
|
||||
|
||||
private V1Service getDiscoveryService() throws Exception {
|
||||
V1Service service = (V1Service) k8SUtils
|
||||
.readYamlFromClasspath("spring-cloud-kubernetes-discoveryclient-it-service.yaml");
|
||||
return service;
|
||||
}
|
||||
|
||||
private V1Deployment getDiscoveryItDeployment() throws Exception {
|
||||
V1Deployment deployment = (V1Deployment) k8SUtils
|
||||
.readYamlFromClasspath("spring-cloud-kubernetes-discoveryclient-it-deployment.yaml");
|
||||
String image = deployment.getSpec().getTemplate().getSpec().getContainers().get(0).getImage() + ":"
|
||||
+ getPomVersion();
|
||||
deployment.getSpec().getTemplate().getSpec().getContainers().get(0).setImage(image);
|
||||
return deployment;
|
||||
}
|
||||
|
||||
private V1Ingress getDiscoveryItIngress() throws Exception {
|
||||
V1Ingress ingress = (V1Ingress) k8SUtils
|
||||
.readYamlFromClasspath("spring-cloud-kubernetes-discoveryclient-it-ingress.yaml");
|
||||
return ingress;
|
||||
}
|
||||
|
||||
private static void deployDiscoveryServer() throws Exception {
|
||||
appsApi.createNamespacedDeployment(NAMESPACE, getDiscoveryServerDeployment(), null, null, null);
|
||||
api.createNamespacedService(NAMESPACE, getDiscoveryServerService(), null, null, null);
|
||||
networkingApi.createNamespacedIngress(NAMESPACE, getDiscoveryServerIngress(), null, null, null);
|
||||
}
|
||||
|
||||
private static V1Ingress getDiscoveryServerIngress() throws Exception {
|
||||
V1Ingress ingress = (V1Ingress) k8SUtils
|
||||
.readYamlFromClasspath("spring-cloud-kubernetes-discoveryserver-ingress.yaml");
|
||||
return ingress;
|
||||
}
|
||||
|
||||
private static V1Service getDiscoveryServerService() throws Exception {
|
||||
V1Service service = (V1Service) k8SUtils
|
||||
.readYamlFromClasspath("spring-cloud-kubernetes-discoveryserver-service.yaml");
|
||||
return service;
|
||||
}
|
||||
|
||||
private static V1Deployment getDiscoveryServerDeployment() throws Exception {
|
||||
V1Deployment deployment = (V1Deployment) k8SUtils
|
||||
.readYamlFromClasspath("spring-cloud-kubernetes-discoveryserver-deployment.yaml");
|
||||
String image = deployment.getSpec().getTemplate().getSpec().getContainers().get(0).getImage() + ":"
|
||||
+ getPomVersion();
|
||||
deployment.getSpec().getTemplate().getSpec().getContainers().get(0).setImage(image);
|
||||
return deployment;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: spring-cloud-kubernetes-discoveryclient-it-deployment
|
||||
spec:
|
||||
selector:
|
||||
matchLabels:
|
||||
app: spring-cloud-kubernetes-discoveryclient-it
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: spring-cloud-kubernetes-discoveryclient-it
|
||||
spec:
|
||||
serviceAccountName: spring-cloud-kubernetes-serviceaccount
|
||||
containers:
|
||||
- name: spring-cloud-kubernetes-discoveryclient-it
|
||||
image: docker.io/springcloud/spring-cloud-kubernetes-reactive-discoveryclient-it
|
||||
imagePullPolicy: IfNotPresent
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
port: 8080
|
||||
path: /actuator/health/readiness
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
port: 8080
|
||||
path: /actuator/health/liveness
|
||||
ports:
|
||||
- containerPort: 8080
|
||||
@@ -0,0 +1,18 @@
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: it-ingress
|
||||
namespace: default
|
||||
annotations:
|
||||
nginx.ingress.kubernetes.io/rewrite-target: /$2
|
||||
spec:
|
||||
rules:
|
||||
- http:
|
||||
paths:
|
||||
- path: /discoveryclient-it(/|$)(.*)
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
name: spring-cloud-kubernetes-discoveryclient-it
|
||||
port:
|
||||
number: 8080
|
||||
@@ -0,0 +1,14 @@
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
labels:
|
||||
app: spring-cloud-kubernetes-discoveryclient-it
|
||||
name: spring-cloud-kubernetes-discoveryclient-it
|
||||
spec:
|
||||
ports:
|
||||
- name: http
|
||||
port: 8080
|
||||
targetPort: 8080
|
||||
selector:
|
||||
app: spring-cloud-kubernetes-discoveryclient-it
|
||||
type: ClusterIP
|
||||
@@ -0,0 +1,28 @@
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: spring-cloud-kubernetes-discoveryserver-deployment
|
||||
spec:
|
||||
selector:
|
||||
matchLabels:
|
||||
app: spring-cloud-kubernetes-discoveryserver
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: spring-cloud-kubernetes-discoveryserver
|
||||
spec:
|
||||
serviceAccountName: spring-cloud-kubernetes-serviceaccount
|
||||
containers:
|
||||
- name: spring-cloud-kubernetes-discoveryserver
|
||||
image: docker.io/springcloud/spring-cloud-kubernetes-discoveryserver
|
||||
imagePullPolicy: IfNotPresent
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
port: 8761
|
||||
path: /actuator/health/readiness
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
port: 8761
|
||||
path: /actuator/health/liveness
|
||||
ports:
|
||||
- containerPort: 8761
|
||||
@@ -0,0 +1,18 @@
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: discoveryserver-ingress
|
||||
namespace: default
|
||||
annotations:
|
||||
nginx.ingress.kubernetes.io/rewrite-target: /$2
|
||||
spec:
|
||||
rules:
|
||||
- http:
|
||||
paths:
|
||||
- path: /discoveryserver(/|$)(.*)
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
name: spring-cloud-kubernetes-discoveryserver
|
||||
port:
|
||||
number: 80
|
||||
@@ -0,0 +1,14 @@
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
labels:
|
||||
app: spring-cloud-kubernetes-discoveryserver
|
||||
name: spring-cloud-kubernetes-discoveryserver
|
||||
spec:
|
||||
ports:
|
||||
- name: http
|
||||
port: 80
|
||||
targetPort: 8761
|
||||
selector:
|
||||
app: spring-cloud-kubernetes-discoveryserver
|
||||
type: ClusterIP
|
||||
25
spring-cloud-starter-kubernetes-discoveryclient/pom.xml
Normal file
25
spring-cloud-starter-kubernetes-discoveryclient/pom.xml
Normal file
@@ -0,0 +1,25 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>spring-cloud-kubernetes</artifactId>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<version>2.1.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>spring-cloud-starter-kubernetes-discoveryclient</artifactId>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-commons</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-kubernetes-discovery</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -13,5 +13,6 @@
|
||||
<suppress files=".*PollingConfigurationChangeDetector.*" checks="LineLength*"/>
|
||||
<suppress files=".*LeaderRecordWatcherTest.*" checks="LineLength*"/>
|
||||
<suppress files=".*ConfigurationWatcherApplication\.java" checks="HideUtilityClassConstructor"/>
|
||||
<suppress files=".*DiscoveryServerApplication\.java" checks="HideUtilityClassConstructor"/>
|
||||
<suppress files=".*KubernetesConfigServerApplication\.java" checks="HideUtilityClassConstructor"/>
|
||||
</suppressions>
|
||||
|
||||
Reference in New Issue
Block a user