Skip to content

Commit

Permalink
#233 Initial Istio Awareness implementation (#286)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
salaboy authored and ryanjbaxter committed Dec 20, 2018
1 parent b3de20b commit ef51d11
Show file tree
Hide file tree
Showing 20 changed files with 504 additions and 5 deletions.
17 changes: 16 additions & 1 deletion .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ jobs:
# Allow intra-pod communication
sudo iptables -P FORWARD ACCEPT
/snap/bin/microk8s.enable dns registry
echo n |/snap/bin/microk8s.enable dns registry istio
# wait until the registry is up and running
sleep 10
Expand All @@ -85,6 +85,21 @@ jobs:
done
echo "Kubernetes Container Registry enabled"
# wait until istio is up and running
sleep 10
n=0
until [ $n -ge 10 ]
do
(/snap/bin/microk8s.kubectl get pod -l istio=sidecar-injector --namespace=istio-system | grep -z "Running") && break
n=$[$n+1]
sleep 10
done
echo "Istio enabled"
# create the namespace where the istio integration test will run
/snap/bin/microk8s.kubectl create -f .circleci/istio-test-namespace.yml
- run:
name: Run integration tests
command: |
Expand Down
6 changes: 6 additions & 0 deletions .circleci/istio-test-namespace.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
apiVersion: v1
kind: Namespace
metadata:
name: istio-test
labels:
istio-injection: enabled
13 changes: 11 additions & 2 deletions docs/src/main/asciidoc/kubernetes-awareness.adoc
Original file line number Diff line number Diff line change
@@ -1,15 +1,24 @@
== Kubernetes Awareness
== Kubernetes Ecosystem Awareness

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

=== Kubernetes Profile Autoconfiguration

When the application runs as a pod inside Kubernetes a Spring profile named `kubernetes` will automatically get activated.
This allows the developer to customize the configuration, to define beans that will be applied when the Spring Boot application is deployed
within the Kubernetes platform *(e.g. different dev and prod configuration)*.

=== Istio Awareness

When including the **spring-cloud-kubernetes-istio** module into the application classpath a new profile will be added to the application,
if the application is running inside a Kubernetes Cluster with http://istio.io[Istio] installed. Then you can use
spring **@Profile("istio")** annotations into your Beans and **@Configuration**'s.

The Istio awareness module uses the **me.snowdrop:istio-client** to interact with Istio APIs enabling us to discover traffic rules, circuit breakers, etc.
Making it easy for our Spring Boot applications to consume this data to dynamically configure themselves according the environment.
3 changes: 2 additions & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -88,13 +88,14 @@
<module>spring-cloud-starter-kubernetes-all</module>
<module>spring-cloud-kubernetes-examples</module>
<module>spring-cloud-kubernetes-leader</module>
<module>spring-cloud-kubernetes-istio</module>
<module>spring-cloud-kubernetes-integration-tests</module>
<module>docs</module>
</modules>

<dependencyManagement>
<dependencies>

<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-kubernetes-dependencies</artifactId>
Expand Down
12 changes: 12 additions & 0 deletions spring-cloud-kubernetes-dependencies/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
<arquillian.version>1.4.0.Final</arquillian.version>
<arquillian-cube.version>1.15.2</arquillian-cube.version>
<kubernetes-client.version>4.1.0</kubernetes-client.version>
<istio-client.version>1.0.0</istio-client.version>
<mockwebserver.version>0.1.0</mockwebserver.version>
</properties>
<dependencyManagement>
Expand All @@ -46,6 +47,12 @@
<scope>import</scope>
</dependency>

<dependency>
<groupId>me.snowdrop</groupId>
<artifactId>istio-client</artifactId>
<version>${istio-client.version}</version>
</dependency>

<!-- Own dependencies -->
<dependency>
<groupId>org.springframework.cloud</groupId>
Expand All @@ -65,6 +72,11 @@
<version>${project.version}</version>
</dependency>

<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-kubernetes-istio</artifactId>
<version>${project.version}</version>
</dependency>

<dependency>
<groupId>org.springframework.cloud</groupId>
Expand Down
9 changes: 8 additions & 1 deletion spring-cloud-kubernetes-integration-tests/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ tests against that microk8s cluster
```bash
sudo snap install microk8s --classic --channel=1.11/stable
sleep 10
microk8s.enable dns registry
echo n | microk8s.enable dns registry istio
```

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

For the istio tests to work, we need a namespace that is properly configured for Istio injection.
Such a namespace called `istio-test` can easily be created using:

```bash
microk8s.kubectl create -f .circleci/istio-test-namespace.yml
```


## Launch tests

Expand Down
64 changes: 64 additions & 0 deletions spring-cloud-kubernetes-integration-tests/istio/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
<?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">
<modelVersion>4.0.0</modelVersion>

<parent>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-kubernetes-integration-tests</artifactId>
<version>1.0.0.BUILD-SNAPSHOT</version>
</parent>

<name>Spring Cloud Kubernetes :: Integration Tests :: Istio</name>
<artifactId>istio</artifactId>
<packaging>jar</packaging>

<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-actuator-autoconfigure</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-kubernetes-istio</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.arquillian.cube</groupId>
<artifactId>arquillian-cube-kubernetes</artifactId>
<version>${arquillian-cube.version}</version>
<scope>test</scope>
<exclusions>
<exclusion>
<artifactId>undertow-core</artifactId>
<groupId>io.undertow</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.jboss.arquillian.junit</groupId>
<artifactId>arquillian-junit-standalone</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.rest-assured</groupId>
<artifactId>rest-assured</artifactId>
<scope>test</scope>
</dependency>
</dependencies>


</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# We need this fragment in order for the kubernetes client to talk to
# the Kubernetes API without caring about proper certificates
spec:
template:
spec:
containers:
- env:
- name: KUBERNETES_TRUST_CERTIFICATES
value: true

Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# we are using an FMP fragment to ensure that NodePort is used correctly
kind: Service
apiVersion: v1
metadata:
name: ${project.artifactId}
labels:
app: ${project.artifactId}
spec:
selector:
app: ${project.artifactId}
ports:
- protocol: TCP
port: 8080
nodePort: ${nodeport.value}
type: NodePort
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package org.springframework.cloud.kubernetes.it;

import me.snowdrop.istio.client.IstioClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.core.env.Environment;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.Arrays;
import java.util.List;

@SpringBootApplication
@RestController
public class IstioApplication {

@Autowired
private Environment environment;

// used just to ensure that the IstioClient is properly injected into the context
@Autowired
private IstioClient istioClient;

@GetMapping("/profiles")
public List<String> profiles() {
return Arrays.asList(environment.getActiveProfiles());
}

public static void main(String[] args) {
SpringApplication.run(IstioApplication.class, args);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
spring:
cloud:
istio:
client:
envoyPort: 15000
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package org.springframework.cloud.kubernetes.it;

import static io.restassured.RestAssured.given;

import org.arquillian.cube.kubernetes.impl.requirement.RequiresKubernetes;
import org.hamcrest.core.StringContains;
import org.jboss.arquillian.junit.Arquillian;
import org.junit.Test;
import org.junit.runner.RunWith;

@RequiresKubernetes
@RunWith(Arquillian.class)
public class ProfilesIT {

private static final String HOST = System.getProperty("service.host");
private static final Integer PORT = Integer.valueOf(System.getProperty("service.port"));

@Test
public void testProfileEndpoint() {
given()
.baseUri(String.format("http://%s:%d", HOST, PORT))
.get("profiles")
.then()
.statusCode(200)
.body(new StringContains("istio"));
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<arquillian xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://jboss.org/schema/arquillian"
xsi:schemaLocation="http://jboss.org/schema/arquillian http://jboss.org/schema/arquillian/arquillian_1_0.xsd">

<extension qualifier="kubernetes">
<property name="namespace.use.existing">istio-test</property>
</extension>

</arquillian>
1 change: 1 addition & 0 deletions spring-cloud-kubernetes-integration-tests/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@
<modules>
<module>simple-core</module>
<module>simple-configmap</module>
<module>istio</module>
<module>discovery</module>
</modules>

Expand Down
41 changes: 41 additions & 0 deletions spring-cloud-kubernetes-istio/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<?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>1.0.0.BUILD-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

<artifactId>spring-cloud-kubernetes-istio</artifactId>
<name>Spring Cloud Kubernetes :: Istio</name>

<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-kubernetes-core</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
</dependency>
<dependency>
<groupId>me.snowdrop</groupId>
<artifactId>istio-client</artifactId>
</dependency>
<!-- Testing Dependencies -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<scope>test</scope>
</dependency>

</dependencies>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*
* Copyright 2013-2018 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
*
* http://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.istio;

import io.fabric8.kubernetes.client.Config;
import me.snowdrop.istio.client.DefaultIstioClient;
import me.snowdrop.istio.client.IstioClient;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
@ConditionalOnProperty(value = "spring.cloud.istio.enabled", matchIfMissing = true)
public class IstioAutoConfiguration {

@Bean
@ConditionalOnMissingBean
public IstioClient istioClient(Config config) {
return new DefaultIstioClient(config);
}
}
Loading

0 comments on commit ef51d11

Please sign in to comment.