Skip to content

Commit ef51d11

Browse files
salaboyRyan Baxter
authored and
Ryan Baxter
committed
spring-cloud#233 Initial Istio Awareness implementation (spring-cloud#286)
* initial profile and check * initial istio aware profile impl * adding initial docs * fixing parent * using BootstrapConfiguration for istio profile check * Improve log messages * Add Istio integration tests * Remove unused IstioClient * Remove redundant parent * Revert "Remove redundant parent" This reverts commit c41271f * Fix startup error * Add name to istio module * Move IstioClient out of bootstrap configuration This is needed because the Kubernetes Config is not a bean in the bootstrap context. Furthermore the IstioClient is not used anywhere in the bootstrap context * Polish * Ensure this IstioClient is actually injected * Fix integration test setup * more refinements * aligning property name * adding headers and small refinements
1 parent b3de20b commit ef51d11

File tree

20 files changed

+504
-5
lines changed

20 files changed

+504
-5
lines changed

.circleci/config.yml

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ jobs:
7272
# Allow intra-pod communication
7373
sudo iptables -P FORWARD ACCEPT
7474
75-
/snap/bin/microk8s.enable dns registry
75+
echo n |/snap/bin/microk8s.enable dns registry istio
7676
7777
# wait until the registry is up and running
7878
sleep 10
@@ -85,6 +85,21 @@ jobs:
8585
done
8686
8787
echo "Kubernetes Container Registry enabled"
88+
89+
# wait until istio is up and running
90+
sleep 10
91+
n=0
92+
until [ $n -ge 10 ]
93+
do
94+
(/snap/bin/microk8s.kubectl get pod -l istio=sidecar-injector --namespace=istio-system | grep -z "Running") && break
95+
n=$[$n+1]
96+
sleep 10
97+
done
98+
99+
echo "Istio enabled"
100+
101+
# create the namespace where the istio integration test will run
102+
/snap/bin/microk8s.kubectl create -f .circleci/istio-test-namespace.yml
88103
- run:
89104
name: Run integration tests
90105
command: |

.circleci/istio-test-namespace.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
apiVersion: v1
2+
kind: Namespace
3+
metadata:
4+
name: istio-test
5+
labels:
6+
istio-injection: enabled
Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,24 @@
1-
== Kubernetes Awareness
1+
== Kubernetes Ecosystem Awareness
22

33
All of the features described above will work equally well regardless of whether your application is running inside
44
Kubernetes or not. This is really helpful for development and troubleshooting.
55
From a development point of view, this is really helpful as you can start your Spring Boot application and debug one
66
of the modules part of this project. It is not required to deploy it in Kubernetes
77
as the code of the project relies on the
8-
[Fabric8 Kubernetes Java client](https://github.com/fabric8io/kubernetes-client) which is a fluent DSL able to
8+
https://github.com/fabric8io/kubernetes-client[Fabric8 Kubernetes Java client] which is a fluent DSL able to
99
communicate using `http` protocol to the REST API of Kubernetes Server.
1010

1111
=== Kubernetes Profile Autoconfiguration
1212

1313
When the application runs as a pod inside Kubernetes a Spring profile named `kubernetes` will automatically get activated.
1414
This allows the developer to customize the configuration, to define beans that will be applied when the Spring Boot application is deployed
1515
within the Kubernetes platform *(e.g. different dev and prod configuration)*.
16+
17+
=== Istio Awareness
18+
19+
When including the **spring-cloud-kubernetes-istio** module into the application classpath a new profile will be added to the application,
20+
if the application is running inside a Kubernetes Cluster with http://istio.io[Istio] installed. Then you can use
21+
spring **@Profile("istio")** annotations into your Beans and **@Configuration**'s.
22+
23+
The Istio awareness module uses the **me.snowdrop:istio-client** to interact with Istio APIs enabling us to discover traffic rules, circuit breakers, etc.
24+
Making it easy for our Spring Boot applications to consume this data to dynamically configure themselves according the environment.

pom.xml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,13 +88,14 @@
8888
<module>spring-cloud-starter-kubernetes-all</module>
8989
<module>spring-cloud-kubernetes-examples</module>
9090
<module>spring-cloud-kubernetes-leader</module>
91+
<module>spring-cloud-kubernetes-istio</module>
9192
<module>spring-cloud-kubernetes-integration-tests</module>
9293
<module>docs</module>
9394
</modules>
9495

9596
<dependencyManagement>
9697
<dependencies>
97-
98+
9899
<dependency>
99100
<groupId>org.springframework.cloud</groupId>
100101
<artifactId>spring-cloud-kubernetes-dependencies</artifactId>

spring-cloud-kubernetes-dependencies/pom.xml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
<arquillian.version>1.4.0.Final</arquillian.version>
3535
<arquillian-cube.version>1.15.2</arquillian-cube.version>
3636
<kubernetes-client.version>4.1.0</kubernetes-client.version>
37+
<istio-client.version>1.0.0</istio-client.version>
3738
<mockwebserver.version>0.1.0</mockwebserver.version>
3839
</properties>
3940
<dependencyManagement>
@@ -46,6 +47,12 @@
4647
<scope>import</scope>
4748
</dependency>
4849

50+
<dependency>
51+
<groupId>me.snowdrop</groupId>
52+
<artifactId>istio-client</artifactId>
53+
<version>${istio-client.version}</version>
54+
</dependency>
55+
4956
<!-- Own dependencies -->
5057
<dependency>
5158
<groupId>org.springframework.cloud</groupId>
@@ -65,6 +72,11 @@
6572
<version>${project.version}</version>
6673
</dependency>
6774

75+
<dependency>
76+
<groupId>org.springframework.cloud</groupId>
77+
<artifactId>spring-cloud-kubernetes-istio</artifactId>
78+
<version>${project.version}</version>
79+
</dependency>
6880

6981
<dependency>
7082
<groupId>org.springframework.cloud</groupId>

spring-cloud-kubernetes-integration-tests/README.md

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ tests against that microk8s cluster
2727
```bash
2828
sudo snap install microk8s --classic --channel=1.11/stable
2929
sleep 10
30-
microk8s.enable dns registry
30+
echo n | microk8s.enable dns registry istio
3131
```
3232

3333
Ensure everything is running by inspecting the output of:
@@ -66,6 +66,13 @@ Export the kube config file that will be used to access this cluster
6666
microk8s.kubectl config view --raw > /tmp/kubeconfig
6767
```
6868

69+
For the istio tests to work, we need a namespace that is properly configured for Istio injection.
70+
Such a namespace called `istio-test` can easily be created using:
71+
72+
```bash
73+
microk8s.kubectl create -f .circleci/istio-test-namespace.yml
74+
```
75+
6976

7077
## Launch tests
7178

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project xmlns="http://maven.apache.org/POM/4.0.0"
3+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
5+
<modelVersion>4.0.0</modelVersion>
6+
7+
<parent>
8+
<groupId>org.springframework.cloud</groupId>
9+
<artifactId>spring-cloud-kubernetes-integration-tests</artifactId>
10+
<version>1.0.0.BUILD-SNAPSHOT</version>
11+
</parent>
12+
13+
<name>Spring Cloud Kubernetes :: Integration Tests :: Istio</name>
14+
<artifactId>istio</artifactId>
15+
<packaging>jar</packaging>
16+
17+
<dependencies>
18+
<dependency>
19+
<groupId>org.springframework.boot</groupId>
20+
<artifactId>spring-boot-starter-web</artifactId>
21+
</dependency>
22+
<dependency>
23+
<groupId>org.springframework.boot</groupId>
24+
<artifactId>spring-boot-actuator</artifactId>
25+
</dependency>
26+
<dependency>
27+
<groupId>org.springframework.boot</groupId>
28+
<artifactId>spring-boot-actuator-autoconfigure</artifactId>
29+
</dependency>
30+
<dependency>
31+
<groupId>org.springframework.cloud</groupId>
32+
<artifactId>spring-cloud-kubernetes-istio</artifactId>
33+
</dependency>
34+
<dependency>
35+
<groupId>org.springframework.boot</groupId>
36+
<artifactId>spring-boot-starter-test</artifactId>
37+
<scope>test</scope>
38+
</dependency>
39+
<dependency>
40+
<groupId>org.arquillian.cube</groupId>
41+
<artifactId>arquillian-cube-kubernetes</artifactId>
42+
<version>${arquillian-cube.version}</version>
43+
<scope>test</scope>
44+
<exclusions>
45+
<exclusion>
46+
<artifactId>undertow-core</artifactId>
47+
<groupId>io.undertow</groupId>
48+
</exclusion>
49+
</exclusions>
50+
</dependency>
51+
<dependency>
52+
<groupId>org.jboss.arquillian.junit</groupId>
53+
<artifactId>arquillian-junit-standalone</artifactId>
54+
<scope>test</scope>
55+
</dependency>
56+
<dependency>
57+
<groupId>io.rest-assured</groupId>
58+
<artifactId>rest-assured</artifactId>
59+
<scope>test</scope>
60+
</dependency>
61+
</dependencies>
62+
63+
64+
</project>
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# We need this fragment in order for the kubernetes client to talk to
2+
# the Kubernetes API without caring about proper certificates
3+
spec:
4+
template:
5+
spec:
6+
containers:
7+
- env:
8+
- name: KUBERNETES_TRUST_CERTIFICATES
9+
value: true
10+
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# we are using an FMP fragment to ensure that NodePort is used correctly
2+
kind: Service
3+
apiVersion: v1
4+
metadata:
5+
name: ${project.artifactId}
6+
labels:
7+
app: ${project.artifactId}
8+
spec:
9+
selector:
10+
app: ${project.artifactId}
11+
ports:
12+
- protocol: TCP
13+
port: 8080
14+
nodePort: ${nodeport.value}
15+
type: NodePort
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package org.springframework.cloud.kubernetes.it;
2+
3+
import me.snowdrop.istio.client.IstioClient;
4+
import org.springframework.beans.factory.annotation.Autowired;
5+
import org.springframework.boot.SpringApplication;
6+
import org.springframework.boot.autoconfigure.SpringBootApplication;
7+
import org.springframework.core.env.Environment;
8+
import org.springframework.web.bind.annotation.GetMapping;
9+
import org.springframework.web.bind.annotation.RestController;
10+
11+
import java.util.Arrays;
12+
import java.util.List;
13+
14+
@SpringBootApplication
15+
@RestController
16+
public class IstioApplication {
17+
18+
@Autowired
19+
private Environment environment;
20+
21+
// used just to ensure that the IstioClient is properly injected into the context
22+
@Autowired
23+
private IstioClient istioClient;
24+
25+
@GetMapping("/profiles")
26+
public List<String> profiles() {
27+
return Arrays.asList(environment.getActiveProfiles());
28+
}
29+
30+
public static void main(String[] args) {
31+
SpringApplication.run(IstioApplication.class, args);
32+
}
33+
34+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
spring:
2+
cloud:
3+
istio:
4+
client:
5+
envoyPort: 15000
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package org.springframework.cloud.kubernetes.it;
2+
3+
import static io.restassured.RestAssured.given;
4+
5+
import org.arquillian.cube.kubernetes.impl.requirement.RequiresKubernetes;
6+
import org.hamcrest.core.StringContains;
7+
import org.jboss.arquillian.junit.Arquillian;
8+
import org.junit.Test;
9+
import org.junit.runner.RunWith;
10+
11+
@RequiresKubernetes
12+
@RunWith(Arquillian.class)
13+
public class ProfilesIT {
14+
15+
private static final String HOST = System.getProperty("service.host");
16+
private static final Integer PORT = Integer.valueOf(System.getProperty("service.port"));
17+
18+
@Test
19+
public void testProfileEndpoint() {
20+
given()
21+
.baseUri(String.format("http://%s:%d", HOST, PORT))
22+
.get("profiles")
23+
.then()
24+
.statusCode(200)
25+
.body(new StringContains("istio"));
26+
}
27+
28+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<arquillian xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
2+
xmlns="http://jboss.org/schema/arquillian"
3+
xsi:schemaLocation="http://jboss.org/schema/arquillian http://jboss.org/schema/arquillian/arquillian_1_0.xsd">
4+
5+
<extension qualifier="kubernetes">
6+
<property name="namespace.use.existing">istio-test</property>
7+
</extension>
8+
9+
</arquillian>

spring-cloud-kubernetes-integration-tests/pom.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,7 @@
128128
<modules>
129129
<module>simple-core</module>
130130
<module>simple-configmap</module>
131+
<module>istio</module>
131132
<module>discovery</module>
132133
</modules>
133134

spring-cloud-kubernetes-istio/pom.xml

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project xmlns="http://maven.apache.org/POM/4.0.0"
3+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
5+
<parent>
6+
<artifactId>spring-cloud-kubernetes</artifactId>
7+
<groupId>org.springframework.cloud</groupId>
8+
<version>1.0.0.BUILD-SNAPSHOT</version>
9+
</parent>
10+
<modelVersion>4.0.0</modelVersion>
11+
12+
<artifactId>spring-cloud-kubernetes-istio</artifactId>
13+
<name>Spring Cloud Kubernetes :: Istio</name>
14+
15+
<dependencies>
16+
<dependency>
17+
<groupId>org.springframework.cloud</groupId>
18+
<artifactId>spring-cloud-kubernetes-core</artifactId>
19+
</dependency>
20+
<dependency>
21+
<groupId>org.springframework</groupId>
22+
<artifactId>spring-web</artifactId>
23+
</dependency>
24+
<dependency>
25+
<groupId>me.snowdrop</groupId>
26+
<artifactId>istio-client</artifactId>
27+
</dependency>
28+
<!-- Testing Dependencies -->
29+
<dependency>
30+
<groupId>org.springframework.boot</groupId>
31+
<artifactId>spring-boot-starter-test</artifactId>
32+
<scope>test</scope>
33+
</dependency>
34+
<dependency>
35+
<groupId>org.springframework.boot</groupId>
36+
<artifactId>spring-boot-starter-web</artifactId>
37+
<scope>test</scope>
38+
</dependency>
39+
40+
</dependencies>
41+
</project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/*
2+
* Copyright 2013-2018 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*
16+
*/
17+
package org.springframework.cloud.kubernetes.istio;
18+
19+
import io.fabric8.kubernetes.client.Config;
20+
import me.snowdrop.istio.client.DefaultIstioClient;
21+
import me.snowdrop.istio.client.IstioClient;
22+
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
23+
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
24+
import org.springframework.context.annotation.Bean;
25+
import org.springframework.context.annotation.Configuration;
26+
27+
@Configuration
28+
@ConditionalOnProperty(value = "spring.cloud.istio.enabled", matchIfMissing = true)
29+
public class IstioAutoConfiguration {
30+
31+
@Bean
32+
@ConditionalOnMissingBean
33+
public IstioClient istioClient(Config config) {
34+
return new DefaultIstioClient(config);
35+
}
36+
}

0 commit comments

Comments
 (0)